@@ -31,13 +31,14 @@ import System.Directory
3131import System.FilePath
3232import System.Process (proc)
3333import System.IO.Temp (withSystemTempDirectory)
34+import Text.Read (readMaybe)
3435import Data.ByteString (ByteString)
3536
3637import Obelisk.App (MonadObelisk, ObeliskT)
3738import Obelisk.CliApp
3839 ( CliT (..), HasCliConfig, Severity (..)
3940 , callCommand, failWith, getCliConfig, putLog
40− , readProcessAndLogStderr, runCli)
41+ , readProcessAndLogStderr, runCli, shellQuoteAndEscapeDouble)
4142import Obelisk.Command.Project (inProjectShell, withProjectRoot)
4243
4344data CabalPackageInfo = CabalPackageInfo
@@ -57,15 +58,12 @@ run = do
5758 withGhciScript pkgs $ \dotGhciPath -> do
5859 freePort <- getFreePort
5960 assets <- withProjectRoot "." $ \root -> do
60− let importableRoot = if "/" `isInfixOf` root
61− then root
62− else "./" <> root
6361 isDerivation <- readProcessAndLogStderr Debug $
6462 proc "nix"
6563 [ "eval"
6664 , "-f"
6765 , root
68− , "(let a = import " <> importableRoot <> " {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
66+ , "(let a = import " <> importablePath root <> " {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
6967 , "--raw"
7068 -- `--raw` is not available with old nix-instantiate. It drops quotation
7169 -- marks and trailing newline, so is very convenient for shelling out.
@@ -73,11 +71,15 @@ run = do
7371 -- Check whether the impure static files are a derivation (and so must be built)
7472 if isDerivation == "1"
7573 then fmap T.strip $ readProcessAndLogStderr Debug $ -- Strip whitespace here because nix-build has no --raw option
76− proc "nix-build" ["-E", "(import " <> importableRoot <> "{}).passthru.staticFilesImpure"]
74+ proc "nix-build"
75+ [ "--no-out-link"
76+ , "-E", "(import " <> importablePath root <> "{}).passthru.staticFilesImpure"
77+ ]
7778 else readProcessAndLogStderr Debug $
7879 proc "nix" ["eval", "-f", root, "passthru.staticFilesImpure", "--raw"]
7980 putLog Debug $ "Assets impurely loaded from: " <> assets
80− runGhcid dotGhciPath $ Just $ unwords
81+ extraGhciArgs <- getExtraGhciArgs
82+ runGhcid dotGhciPath extraGhciArgs $ Just $ unwords
8183 [ "Obelisk.Run.run"
8284 , show freePort
8385 , "(runServeAsset " ++ show assets ++ ")"
@@ -89,12 +91,14 @@ runRepl :: MonadObelisk m => m ()
8991runRepl = do
9092 pkgs <- getLocalPkgs
9193 withGhciScript pkgs $ \dotGhciPath -> do
92− runGhciRepl dotGhciPath
94+ runGhciRepl dotGhciPath =<< getExtraGhciArgs
9395
9496runWatch :: MonadObelisk m => m ()
9597runWatch = do
9698 pkgs <- getLocalPkgs
97− withGhciScript pkgs $ \dotGhciPath -> runGhcid dotGhciPath Nothing
99+ withGhciScript pkgs $ \dotGhciPath -> do
100+ ghciArgs <- getExtraGhciArgs
101+ runGhcid dotGhciPath ghciArgs Nothing
98102
99103-- | Relative paths to local packages of an obelisk project
100104-- TODO a way to query this
@@ -202,21 +206,24 @@ warnDifferentLanguages _ = return ()
202206runGhciRepl
203207 :: MonadObelisk m
204208 => FilePath -- ^ Path to .ghci
209+ -> [String] -- ^ Extra ghci arguments
205210 -> m ()
206−runGhciRepl dotGhci = inProjectShell "ghc" $ unwords $ "ghci" : ["-no-user-package-db", "-ghci-script", dotGhci]
211+runGhciRepl dotGhci ghciArgs = inProjectShell "ghc" $ unwords $ "ghci" : ["-no-user-package-db", "-ghci-script", dotGhci] ++ ghciArgs
207212
208213-- | Run ghcid
209214runGhcid
210215 :: MonadObelisk m
211216 => FilePath -- ^ Path to .ghci
217+ -> [String] -- ^ Extra ghci arguments
212218 -> Maybe String -- ^ Optional command to run at every reload
213219 -> m ()
214−runGhcid dotGhci mcmd = callCommand $ unwords $ "ghcid" : opts
220+runGhcid dotGhci ghciArgs mcmd = callCommand $ unwords $ "ghcid" : opts
215221 where
216222 opts =
217223 [ "-W"
218224 --TODO: The decision of whether to use -fwarn-redundant-constraints should probably be made by the user
219− , "--command='ghci -Wall -ignore-dot-ghci -fwarn-redundant-constraints -no-user-package-db -ghci-script " <> dotGhci <> "' "
225+ , "--command='ghci -Wall -ignore-dot-ghci -fwarn-redundant-constraints -no-user-package-db -ghci-script " <> dotGhci
226+ <> " " <> T.unpack (T.unwords $ fmap (shellQuoteAndEscapeDouble . T.pack) ghciArgs) <> "' "
220227 , "--reload=config"
221228 , "--outputfile=ghcid-output.txt"
222229 ] <> testCmd
@@ -231,3 +238,19 @@ getFreePort = liftIO $ withSocketsDo $ do
231238 sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
232239 bind sock (addrAddress addr)
233240 return sock
241+
242+getExtraGhciArgs :: MonadObelisk m => m [String]
243+getExtraGhciArgs = withProjectRoot "." $ \root -> do
244+ let attr = "dev.extraGhciArgs"
245+ value <- readProcessAndLogStderr Error (
246+ proc "nix"
247+ [ "eval"
248+ , "--json"
249+ , "(import " <> importablePath root <> " {})." <> attr <> " or []"
250+ ])
251+ case readMaybe (T.unpack value) of
252+ Nothing -> [] <$ putLog Error ("Unable to parse " <> T.pack attr <> ": " <> value)
253+ Just v -> return v
254+
255+importablePath :: String -> String
256+importablePath x = if "/" `isInfixOf` x then x else "./" <> x