@@ -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
@@ -55,15 +56,12 @@ run = do
5556 withGhciScript pkgs $ \dotGhciPath -> do
5657 freePort <- getFreePort
5758 assets <- withProjectRoot "." $ \root -> do
58− let importableRoot = if "/" `isInfixOf` root
59− then root
60− else "./" <> root
6159 isDerivation <- readProcessAndLogStderr Debug $
6260 proc "nix"
6361 [ "eval"
6462 , "-f"
6563 , root
66− , "(let a = import " <> importableRoot <> " {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
64+ , "(let a = import " <> importablePath root <> " {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
6765 , "--raw"
6866 -- `--raw` is not available with old nix-instantiate. It drops quotation
6967 -- marks and trailing newline, so is very convenient for shelling out.
@@ -71,11 +69,15 @@ run = do
7169 -- Check whether the impure static files are a derivation (and so must be built)
7270 if isDerivation == "1"
7371 then fmap T.strip $ readProcessAndLogStderr Debug $ -- Strip whitespace here because nix-build has no --raw option
74− proc "nix-build" ["-E", "(import " <> importableRoot <> "{}).passthru.staticFilesImpure"]
72+ proc "nix-build"
73+ [ "--no-out-link"
74+ , "-E", "(import " <> importablePath root <> "{}).passthru.staticFilesImpure"
75+ ]
7576 else readProcessAndLogStderr Debug $
7677 proc "nix" ["eval", "-f", root, "passthru.staticFilesImpure", "--raw"]
7778 putLog Debug $ "Assets impurely loaded from: " <> assets
78− runGhcid dotGhciPath $ Just $ unwords
79+ extraGhciArgs <- getExtraGhciArgs
80+ runGhcid dotGhciPath extraGhciArgs $ Just $ unwords
7981 [ "Obelisk.Run.run"
8082 , show freePort
8183 , "(runServeAsset " ++ show assets ++ ")"
@@ -87,12 +89,14 @@ runRepl :: MonadObelisk m => m ()
8789runRepl = do
8890 pkgs <- getLocalPkgs
8991 withGhciScript pkgs $ \dotGhciPath -> do
90− runGhciRepl dotGhciPath
92+ runGhciRepl dotGhciPath =<< getExtraGhciArgs
9193
9294runWatch :: MonadObelisk m => m ()
9395runWatch = do
9496 pkgs <- getLocalPkgs
95− withGhciScript pkgs $ \dotGhciPath -> runGhcid dotGhciPath Nothing
97+ withGhciScript pkgs $ \dotGhciPath -> do
98+ ghciArgs <- getExtraGhciArgs
99+ runGhcid dotGhciPath ghciArgs Nothing
96100
97101-- | Relative paths to local packages of an obelisk project
98102-- TODO a way to query this
@@ -189,21 +193,24 @@ withGhciScript pkgs f = do
189193runGhciRepl
190194 :: MonadObelisk m
191195 => FilePath -- ^ Path to .ghci
196+ -> [String] -- ^ Extra ghci arguments
192197 -> m ()
193−runGhciRepl dotGhci = inProjectShell "ghc" $ unwords $ "ghci" : ["-no-user-package-db", "-ghci-script", dotGhci]
198+runGhciRepl dotGhci ghciArgs = inProjectShell "ghc" $ unwords $ "ghci" : ["-no-user-package-db", "-ghci-script", dotGhci] ++ ghciArgs
194199
195200-- | Run ghcid
196201runGhcid
197202 :: MonadObelisk m
198203 => FilePath -- ^ Path to .ghci
204+ -> [String] -- ^ Extra ghci arguments
199205 -> Maybe String -- ^ Optional command to run at every reload
200206 -> m ()
201−runGhcid dotGhci mcmd = callCommand $ unwords $ "ghcid" : opts
207+runGhcid dotGhci ghciArgs mcmd = callCommand $ unwords $ "ghcid" : opts
202208 where
203209 opts =
204210 [ "-W"
205211 --TODO: The decision of whether to use -fwarn-redundant-constraints should probably be made by the user
206− , "--command='ghci -Wall -ignore-dot-ghci -fwarn-redundant-constraints -no-user-package-db -ghci-script " <> dotGhci <> "' "
212+ , "--command='ghci -Wall -ignore-dot-ghci -fwarn-redundant-constraints -no-user-package-db -ghci-script " <> dotGhci
213+ <> " " <> T.unpack (T.unwords $ fmap (shellQuoteAndEscapeDouble . T.pack) ghciArgs) <> "' "
207214 , "--reload=config"
208215 , "--outputfile=ghcid-output.txt"
209216 ] <> testCmd
@@ -218,3 +225,19 @@ getFreePort = liftIO $ withSocketsDo $ do
218225 sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
219226 bind sock (addrAddress addr)
220227 return sock
228+
229+getExtraGhciArgs :: MonadObelisk m => m [String]
230+getExtraGhciArgs = withProjectRoot "." $ \root -> do
231+ let attr = "dev.extraGhciArgs"
232+ value <- readProcessAndLogStderr Error (
233+ proc "nix"
234+ [ "eval"
235+ , "--json"
236+ , "(import " <> importablePath root <> " {})." <> attr <> " or []"
237+ ])
238+ case readMaybe (T.unpack value) of
239+ Nothing -> [] <$ putLog Error ("Unable to parse " <> T.pack attr <> ": " <> value)
240+ Just v -> return v
241+
242+importablePath :: String -> String
243+importablePath x = if "/" `isInfixOf` x then x else "./" <> x