on master help sign in
Merge pull request #654 from obsidiansystems/add-obelisk-profiling-feature

Add obelisk profiling feature

commita3ccae62b538dfa13a699f4059acd3c60a6ebd0c authorElliot Cameron <3noch@users.noreply.github.com> dateFri 13 Mar 2020, 19:43 (7 years ago) parentd8fd799b09c540
11 files changed+179−56
ChangeLog.md+1 all-builds.nix+1 default.nix+34−7 dep/reflex-platform/github.json+2−2 lib/cliapp/src/Obelisk/CliApp.hs+1 lib/cliapp/src/Obelisk/CliApp/Process.hs+8−3 lib/command/src/Obelisk/Command.hs+23−1 lib/command/src/Obelisk/Command/Nix.hs+21−11 lib/command/src/Obelisk/Command/Project.hs+25−5 lib/command/src/Obelisk/Command/Run.hs+62−27 skeleton/.gitignore+1
ChangeLog.md+1
@@ -9,6 +9,7 @@ This project's release branch is `master`. This log is written from the perspect
99* Fix `ob deploy test android` to work. ([#645](https://github.com/obsidiansystems/obelisk/pull/645))
1010* Fix vulnerability where Android deployments would leave signing keys in the nix store which is world readable. ([#645](https://github.com/obsidiansystems/obelisk/pull/645)) (Thanks to [kmicklas](https://github.com/kmicklas) for the report.)
1111* Add `Obelisk.Backend.runBackendWith` to allow customization of how GHCJS resources are loaded in the page. ([#668](https://github.com/obsidiansystems/obelisk/pull/668))
12+* Add `ob profile` command to run Obelisk projects with profiling. `ob profile` works like `ob run`, but instead of using `ghci`, it builds an executable that is built with profiling enabled. ([#654](https://github.com/obsidiansystems/obelisk/pull/654))
1213
1314## v0.6.0.0 - 2020-02-21
1415
all-builds.nix+1
@@ -68,6 +68,7 @@ let
6868 iosSkeleton = (import ./skeleton { inherit obelisk; }).ios.frontend;
6969 nameSuffix = if profiling then "profiled" else "unprofiled";
7070 packages = {
71+ skeletonProfiledObRun = skeleton.__unstable__.profiledObRun;
7172 inherit
7273 command
7374 serverSkeletonShell
default.nix+34−7
@@ -5,7 +5,7 @@
55, reflex-platform-func ? import ./dep/reflex-platform
66}:
77let
8− reflex-platform = getReflexPlatform system;
8+ reflex-platform = getReflexPlatform { inherit system; };
99 inherit (reflex-platform) hackGet nixpkgs;
1010 pkgs = nixpkgs;
1111
@@ -25,10 +25,8 @@ let
2525 openssh
2626 ];
2727
28− getReflexPlatform = sys: reflex-platform-func {
29− inherit iosSdkVersion config;
30− system = sys;
31− enableLibraryProfiling = profiling;
28+ getReflexPlatform = { system, enableLibraryProfiling ? profiling }: reflex-platform-func {
29+ inherit iosSdkVersion config system enableLibraryProfiling;
3230
3331 nixpkgsOverlays = [
3432 (self: super: {
@@ -272,7 +270,7 @@ in rec {
272270 # An Obelisk project is a reflex-platform project with a predefined layout and role for each component
273271 project = base': projectDefinition:
274272 let
275− projectOut = sys: let reflexPlatformProject = (getReflexPlatform sys).project; in reflexPlatformProject (args@{ nixpkgs, ... }:
273+ projectOut = { system, enableLibraryProfiling ? profiling }: let reflexPlatformProject = (getReflexPlatform { inherit system enableLibraryProfiling; }).project; in reflexPlatformProject (args@{ nixpkgs, ... }:
276274 let
277275 inherit (lib.strings) hasPrefix;
278276 mkProject =
@@ -377,7 +375,7 @@ in rec {
377375 });
378376 in allConfig;
379377 in (mkProject (projectDefinition args)).projectConfig);
380− mainProjectOut = projectOut system;
378+ mainProjectOut = projectOut { inherit system; };
381379 serverOn = projectInst: version: serverExe
382380 projectInst.ghc.backend
383381 mainProjectOut.ghcjs.frontend
@@ -387,6 +385,35 @@ in rec {
387385 linuxExe = serverOn (projectOut "x86_64-linux");
388386 dummyVersion = "Version number is only available for deployments";
389387 in mainProjectOut // {
388+
389+ __unstable__.profiledObRun = let
390+ profiled = projectOut { inherit system; enableLibraryProfiling = true; };
391+ exeSource = builtins.toFile "ob-run.hs" ''
392+ module Main where
393+
394+ import Control.Exception
395+ import Reflex.Profiled
396+ import System.Environment
397+
398+ import qualified Obelisk.Run
399+ import qualified Frontend
400+ import qualified Backend
401+
402+ main :: IO ()
403+ main = do
404+ args <- getArgs
405+ let port = read $ args !! 0
406+ assets = args !! 1
407+ profileFile = (args !! 2) <> ".rprof"
408+ Obelisk.Run.run port (Obelisk.Run.runServeAsset assets) Backend.backend Frontend.frontend `finally` writeProfilingData profileFile
409+ '';
410+ in nixpkgs.runCommand "ob-run" {
411+ buildInputs = [ (profiled.ghc.ghcWithPackages (p: [ p.backend p.frontend])) ];
412+ } ''
413+ mkdir -p $out/bin/
414+ ghc -x hs -prof -fno-prof-auto -threaded ${exeSource} -o $out/bin/ob-run
415+ '';
416+
390417 linuxExeConfigurable = linuxExe;
391418 linuxExe = linuxExe dummyVersion;
392419 exe = serverOn mainProjectOut dummyVersion;
dep/reflex-platform/github.json+2−2
@@ -3,6 +3,6 @@
33 "repo": "reflex-platform",
44 "branch": "develop",
55 "private": false,
6− "rev": "2df9a830193f551ff222ac6b0c739c622578ddc0",
7− "sha256": "17084xqhixk79av0gnjgwkhdigf9zwkd4axb6qp89dyyffy5s3hi"
6+ "rev": "156ebb7c391ec909d19e794e591f107462862543",
7+ "sha256": "0rvbaimcvcica80538i6vxckv8si1gbd1ynvgxv7zr5jd1gk6vi5"
88}
lib/cliapp/src/Obelisk/CliApp.hs+1
@@ -51,6 +51,7 @@ module Obelisk.CliApp
5151 , setDelegateCtlc
5252 , setEnvOverride
5353 , shell
54+ , waitForProcess
5455 ) where
5556
5657import Control.Monad.Log (Severity (..))
lib/cliapp/src/Obelisk/CliApp/Process.hs+8−3
@@ -28,6 +28,7 @@ module Obelisk.CliApp.Process
2828 , setDelegateCtlc
2929 , setEnvOverride
3030 , shell
31+ , waitForProcess
3132 ) where
3233
3334import Control.Monad ((<=<), join, void)
@@ -51,7 +52,7 @@ import System.IO (Handle)
5152import System.IO.Streams (InputStream, handleToInputStream)
5253import qualified System.IO.Streams as Streams
5354import System.IO.Streams.Concurrent (concurrentMerge)
54−import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out, waitForProcess)
55+import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out)
5556import qualified System.Process as Process
5657import qualified Data.Aeson as Aeson
5758
@@ -143,7 +144,7 @@ readProcessAndLogOutput (sev_out, sev_err) process = do
143144 outText <- liftIO $ T.decodeUtf8With lenientDecode <$> BS.hGetContents out
144145 putLogRaw sev_out outText
145146
146− liftIO (waitForProcess p) >>= \case
147+ waitForProcess p >>= \case
147148 ExitSuccess -> pure outText
148149 ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (Process.cmdspec $ _processSpec_createProcess process) code
149150
@@ -218,7 +219,7 @@ withProcess process f = do -- TODO: Use bracket.
218219
219220 f out err -- Pass the handles to the passed function
220221
221− liftIO (waitForProcess p) >>= \case
222+ waitForProcess p >>= \case
222223 ExitSuccess -> return (out, err)
223224 ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (Process.cmdspec $ _processSpec_createProcess process) code
224225
@@ -235,6 +236,10 @@ streamToLog stream = fix $ \loop -> do
235236 Nothing -> return ()
236237 Just (sev, line) -> putLogRaw sev (T.decodeUtf8With lenientDecode line) >> loop
237238
239+-- | Wrapper around `System.Process.waitForProcess`
240+waitForProcess :: MonadIO m => ProcessHandle -> m ExitCode
241+waitForProcess = liftIO . Process.waitForProcess
242+
238243-- | Pretty print a 'CmdSpec'
239244reconstructCommand :: Process.CmdSpec -> Text
240245reconstructCommand p = case p of
lib/command/src/Obelisk/Command.hs+23−1
@@ -87,6 +87,7 @@ data ObCommand
8787 = ObCommand_Init InitSource Bool
8888 | ObCommand_Deploy DeployCommand
8989 | ObCommand_Run
90+ | ObCommand_Profile String [String]
9091 | ObCommand_Thunk ThunkCommand
9192 | ObCommand_Repl
9293 | ObCommand_Watch
@@ -108,6 +109,7 @@ obCommand cfg = hsubparser
108109 [ command "init" $ info (ObCommand_Init <$> initSource <*> initForce) $ progDesc "Initialize an Obelisk project"
109110 , command "deploy" $ info (ObCommand_Deploy <$> deployCommand cfg) $ progDesc "Prepare a deployment for an Obelisk project"
110111 , command "run" $ info (pure ObCommand_Run) $ progDesc "Run current project in development mode"
112+ , command "profile" $ info (uncurry ObCommand_Profile <$> profileCommand) $ progDesc "Run current project with profiling enabled"
111113 , command "thunk" $ info (ObCommand_Thunk <$> thunkCommand) $ progDesc "Manipulate thunk directories"
112114 , command "repl" $ info (pure ObCommand_Repl) $ progDesc "Open an interactive interpreter"
113115 , command "watch" $ info (pure ObCommand_Watch) $ progDesc "Watch current project for errors and warnings"
@@ -133,7 +135,7 @@ deployCommand cfg = hsubparser $ mconcat
133135 where
134136 platformP = hsubparser $ mconcat
135137 [ command "android" $ info (pure (Android, [])) mempty
136− , command "ios" $ info ((,) <$> pure IOS <*> (fmap pure $ strArgument (metavar "TEAMID" <> help "Your Team ID - found in the Apple developer portal"))) mempty
138+ , command "ios" $ info ((,) <$> pure IOS <*> fmap pure (strArgument (metavar "TEAMID" <> help "Your Team ID - found in the Apple developer portal"))) mempty
137139 ]
138140
139141 remoteBuilderParser :: Parser (Maybe RemoteBuilder)
@@ -183,6 +185,25 @@ data DeployInitOpts = DeployInitOpts
183185 }
184186 deriving Show
185187
188+profileCommand :: Parser (String, [String])
189+profileCommand = (,)
190+ <$> strOption
191+ ( long "output"
192+ <> short 'o'
193+ <> help "Base output to use for profiling output. Suffixes are added to this based on the profiling type. Defaults to a timestamped path in the profile/ directory in the project's root."
194+ <> metavar "PATH"
195+ <> value "profile/%Y-%m-%dT%H:%M:%S"
196+ <> showDefault
197+ )
198+ <*> (words <$> strOption
199+ ( long "rts-flags"
200+ <> help "RTS Flags to pass to the executable."
201+ <> value "-p -hc"
202+ <> metavar "FLAGS"
203+ <> showDefault
204+ ))
205+
206+
186207--TODO: Result should provide normalised path and also original user input for error reporting.
187208thunkDirectoryParser :: Parser FilePath
188209thunkDirectoryParser = fmap (dropTrailingPathSeparator . normalise) . strArgument $ mconcat
@@ -366,6 +387,7 @@ ob = \case
366387 DeployCommand_Update -> deployUpdate "."
367388 DeployCommand_Test (platform, extraArgs) -> deployMobile platform extraArgs
368389 ObCommand_Run -> run
390+ ObCommand_Profile basePath rtsFlags -> profile basePath rtsFlags
369391 ObCommand_Thunk tc -> case tc of
370392 ThunkCommand_Update thunks config -> for_ thunks (updateThunkToLatest config)
371393 ThunkCommand_Unpack thunks -> for_ thunks unpackThunk
lib/command/src/Obelisk/Command/Nix.hs+21−11
@@ -32,6 +32,8 @@ module Obelisk.Command.Nix
3232
3333 , boolArg
3434 , nixCmd
35+ , nixCmdProc
36+ , nixCmdProc'
3537 , rawArg
3638 , runNixShellConfig
3739 , strArg
@@ -188,27 +190,35 @@ runNixShellConfig cfg = mconcat
188190 ["--run", run] | run <- maybeToList $ cfg ^. nixShellConfig_run
189191 ]
190192
191−nixCmd :: MonadObelisk m => NixCmd -> m FilePath
192−nixCmd cmdCfg = withSpinner' ("Running " <> cmd <> desc) (Just $ const $ "Built " <> desc) $ do
193− output <- readProcessAndLogStderr Debug $ proc (T.unpack cmd) options
194− -- Remove final newline that Nix appends
195− Just (outPath, '\n') <- pure $ T.unsnoc output
196− pure $ T.unpack outPath
193+nixCmdProc :: NixCmd -> ProcessSpec
194+nixCmdProc = fst . nixCmdProc'
195+
196+nixCmdProc' :: NixCmd -> (ProcessSpec, T.Text)
197+nixCmdProc' cmdCfg = (proc (T.unpack cmd) options, cmd)
197198 where
198− (cmd, options, commonCfg) = case cmdCfg of
199+ (cmd, options) = case cmdCfg of
199200 NixCmd_Build cfg' ->
200201 ( "nix-build"
201202 , runNixBuildConfig cfg'
202− , cfg' ^. nixCommonConfig
203203 )
204204 NixCmd_Instantiate cfg' ->
205205 ( "nix-instantiate"
206206 , runNixInstantiateConfig cfg'
207− , cfg' ^. nixCommonConfig
208207 )
208+
209+nixCmd :: MonadObelisk m => NixCmd -> m FilePath
210+nixCmd cmdCfg = withSpinner' ("Running " <> cmd <> desc) (Just $ const $ "Built " <> desc) $ do
211+ output <- readProcessAndLogStderr Debug cmdProc
212+ -- Remove final newline that Nix appends
213+ Just (outPath, '\n') <- pure $ T.unsnoc output
214+ pure $ T.unpack outPath
215+ where
216+ (cmdProc, cmd) = nixCmdProc' cmdCfg
217+ commonCfg = case cmdCfg of
218+ NixCmd_Build cfg' -> cfg' ^. nixCommonConfig
219+ NixCmd_Instantiate cfg' -> cfg' ^. nixCommonConfig
209220 path = commonCfg ^. nixCmdConfig_target . target_path
210221 desc = T.pack $ mconcat $ catMaybes
211− [ (" on " <>) <$> path
222+ [ ("on " <>) <$> path
212223 , (\a -> " [" <> a <> "]") <$> (commonCfg ^. nixCmdConfig_target . target_attr)
213224 ]
214−
lib/command/src/Obelisk/Command/Project.hs+25−5
@@ -5,6 +5,7 @@ module Obelisk.Command.Project
55 ( InitSource (..)
66 , findProjectObeliskCommand
77 , findProjectRoot
8+ , findProjectAssets
89 , initProject
910 , nixShellRunConfig
1011 , nixShellRunProc
@@ -41,7 +42,6 @@ import System.IO.Temp
4142import System.IO.Unsafe (unsafePerformIO)
4243import System.Posix (FileStatus, FileMode, CMode (..), UserID, deviceID, fileID, fileMode, fileOwner, getFileStatus, getRealUserID)
4344import System.Posix.Files
44−import System.Process (waitForProcess)
4545
4646import GitHub.Data.GitData (Branch)
4747import GitHub.Data.Name (Name)
@@ -50,7 +50,7 @@ import Obelisk.App (MonadObelisk)
5050import Obelisk.CliApp
5151import Obelisk.Command.Nix
5252import Obelisk.Command.Thunk
53−import Obelisk.Command.Utils (nixExePath)
53+import Obelisk.Command.Utils (nixExePath, nixBuildExePath)
5454
5555--TODO: Make this module resilient to random exceptions
5656
@@ -287,7 +287,7 @@ nixShellWithPkgs root isPure chdirToRoot packageNamesAndPaths command = do
287287 [ rawArg "root" $ toNixPath $ if chdirToRoot then "." else root
288288 , strArg "pkgs" (T.unpack $ decodeUtf8 $ BSL.toStrict $ Json.encode packageNamesAndAbsPaths)
289289 ]
290− void $ liftIO $ waitForProcess ph
290+ void $ waitForProcess ph
291291
292292nixShellWithHoogle :: MonadObelisk m => FilePath -> Bool -> String -> Maybe String -> m ()
293293nixShellWithHoogle root isPure shell' command = do
@@ -298,7 +298,7 @@ nixShellWithHoogle root isPure shell' command = do
298298 \userSettings = super.userSettings // { withHoogle = true; };\
299299 \})).project.shells.${shell}"
300300 & nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
301− void $ liftIO $ waitForProcess ph
301+ void $ waitForProcess ph
302302
303303projectShell :: MonadObelisk m => FilePath -> Bool -> String -> Maybe String -> m ()
304304projectShell root isPure shellName command = do
@@ -306,4 +306,24 @@ projectShell root isPure shellName command = do
306306 (_, _, _, ph) <- createProcess_ "runNixShellAttr" $ setCwd (Just root) $ nixShellRunProc $ defShellConfig
307307 & nixShellConfig_common . nixCmdConfig_target . target_path ?~ "default.nix"
308308 & nixShellConfig_common . nixCmdConfig_target . target_attr ?~ ("shells." <> shellName)
309− void $ liftIO $ waitForProcess ph
309+ void $ waitForProcess ph
310+
311+findProjectAssets :: MonadObelisk m => FilePath -> m Text
312+findProjectAssets root = do
313+ isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
314+ proc nixExePath
315+ [ "eval"
316+ , "(let a = import ./. {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
317+ , "--raw"
318+ -- `--raw` is not available with old nix-instantiate. It drops quotation
319+ -- marks and trailing newline, so is very convenient for shelling out.
320+ ]
321+ -- Check whether the impure static files are a derivation (and so must be built)
322+ if isDerivation == "1"
323+ then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
324+ proc nixBuildExePath
325+ [ "--no-out-link"
326+ , "-E", "(import ./. {}).passthru.staticFilesImpure"
327+ ]
328+ else readProcessAndLogStderr Debug $ setCwd (Just root) $
329+ proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
lib/command/src/Obelisk/Command/Run.hs+62−27
@@ -4,16 +4,18 @@
44{-# LANGUAGE OverloadedStrings #-}
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE TupleSections #-}
7+{-# LANGUAGE QuasiQuotes #-}
78module Obelisk.Command.Run where
89
910import Control.Arrow ((&&&))
1011import Control.Exception (Exception, bracket)
11−import Control.Lens (ifor)
12−import Control.Monad (filterM, unless)
12+import Control.Lens (ifor, (.~), (&))
13+import Control.Monad (filterM, unless, void)
1314import Control.Monad.Except (runExceptT, throwError)
1415import Control.Monad.IO.Class (liftIO)
1516import Control.Monad.Reader (MonadIO)
1617import Data.Coerce (coerce)
18+import Data.Default (def)
1719import Data.Either
1820import Data.Foldable (for_, toList)
1921import qualified Data.List.NonEmpty as NE
@@ -23,6 +25,8 @@ import Data.Maybe
2325import qualified Data.Set as Set
2426import Data.Text (Text)
2527import qualified Data.Text as T
28+import Data.Time.Clock (getCurrentTime)
29+import Data.Time.Format (formatTime, defaultTimeLocale)
2630import Data.Traversable (for)
2731import Debug.Trace (trace)
2832import Distribution.Compiler (CompilerFlavor(..))
@@ -47,10 +51,23 @@ import qualified System.Info
4751import System.IO.Temp (withSystemTempDirectory)
4852
4953import Obelisk.App (MonadObelisk)
50−import Obelisk.CliApp (Severity (..) , failWith, putLog, proc, readCreateProcessWithExitCode, readProcessAndLogStderr, setCwd)
51−import Obelisk.Command.Project (nixShellWithPkgs, toImplDir, withProjectRoot)
54+import Obelisk.CliApp (
55+ Severity (..),
56+ createProcess_,
57+ failWith,
58+ proc,
59+ putLog,
60+ readCreateProcessWithExitCode,
61+ readProcessAndLogStderr,
62+ setCwd,
63+ setDelegateCtlc,
64+ waitForProcess,
65+ withSpinner,
66+ )
67+import Obelisk.Command.Nix
68+import Obelisk.Command.Project (nixShellWithPkgs, toImplDir, withProjectRoot, findProjectAssets)
5269import Obelisk.Command.Thunk (attrCacheFileName)
53−import Obelisk.Command.Utils (findExePath, ghcidExePath, nixBuildExePath, nixExePath)
70+import Obelisk.Command.Utils (findExePath, ghcidExePath)
5471
5572data CabalPackageInfo = CabalPackageInfo
5673 { _cabalPackageInfo_packageFile :: FilePath
@@ -71,30 +88,49 @@ data CabalPackageInfo = CabalPackageInfo
7188preprocessorIdentifier :: String
7289preprocessorIdentifier = "__preprocessor-apply-packages"
7390
74−run :: MonadObelisk m => m ()
91+profile
92+ :: MonadObelisk m
93+ => String
94+ -> [String]
95+ -> m ()
96+profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
97+ putLog Debug "Using profiled build of project."
98+
99+ outPath <- withSpinner "Building profiled executable" $
100+ fmap (T.unpack . T.strip) $ readProcessAndLogStderr Debug $ setCwd (Just root) $ nixCmdProc $
101+ NixCmd_Build $ def
102+ & nixBuildConfig_outLink .~ OutLink_None
103+ & nixCmdConfig_target .~ Target
104+ { _target_path = Just "."
105+ , _target_attr = Just "__unstable__.profiledObRun"
106+ , _target_expr = Nothing
107+ }
108+ assets <- findProjectAssets root
109+ putLog Debug $ "Assets impurely loaded from: " <> assets
110+ time <- liftIO getCurrentTime
111+ let profileBaseName = formatTime defaultTimeLocale profileBasePattern time
112+ liftIO $ createDirectoryIfMissing True $ takeDirectory $ root </> profileBaseName
113+ putLog Debug $ "Storing profiled data under base name of " <> T.pack (root </> profileBaseName)
114+ freePort <- getFreePort
115+ (_, _, _, ph) <- createProcess_ "runProfExe" $ setCwd (Just root) $ setDelegateCtlc True $ proc (outPath </> "bin" </> "ob-run") $
116+ [ show freePort
117+ , T.unpack assets
118+ , profileBaseName
119+ , "+RTS"
120+ , "-po" <> profileBaseName
121+ ] <> rtsFlags
122+ <> [ "-RTS" ]
123+ void $ waitForProcess ph
124+
125+run
126+ :: MonadObelisk m
127+ => m ()
75128run = withProjectRoot "." $ \root -> do
76129 pkgs <- fmap toList . parsePackagesOrFail =<< getLocalPkgs root
77130 withGhciScript pkgs root $ \dotGhciPath -> do
78− assets <- do
79− isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
80− proc nixExePath
81− [ "eval"
82− , "(let a = import ./. {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
83− , "--raw"
84− -- `--raw` is not available with old nix-instantiate. It drops quotation
85− -- marks and trailing newline, so is very convenient for shelling out.
86− ]
87− -- Check whether the impure static files are a derivation (and so must be built)
88− if isDerivation == "1"
89− then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
90− proc nixBuildExePath
91− [ "--no-out-link"
92− , "-E", "(import ./. {}).passthru.staticFilesImpure"
93− ]
94− else readProcessAndLogStderr Debug $ setCwd (Just root) $
95− proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
96− putLog Debug $ "Assets impurely loaded from: " <> assets
97131 freePort <- getFreePort
132+ assets <- findProjectAssets root
133+ putLog Debug $ "Assets impurely loaded from: " <> assets
98134 runGhcid root True dotGhciPath pkgs $ Just $ unwords
99135 [ "Obelisk.Run.run"
100136 , show freePort
@@ -289,8 +325,7 @@ withGhciScript packageInfos pathBase f = do
289325 , if null modulesToLoad then "" else ":load " <> unwords modulesToLoad
290326 , "import qualified Obelisk.Run"
291327 , "import qualified Frontend"
292− , "import qualified Backend"
293− ]
328+ , "import qualified Backend" ]
294329 withSystemTempDirectory "ob-ghci" $ \fp -> do
295330 let dotGhciPath = fp </> ".ghci"
296331 liftIO $ writeFile dotGhciPath dotGhci
skeleton/.gitignore+1
@@ -6,3 +6,4 @@ result-ios
66result-exe
77.attr-cache
88ghcid-output.txt
9+profile/