on master help sign in
ChangeLog.md−10
since splitsnapshots
unifiedsplit
@@ -2,16 +2,6 @@
2
2
3This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released.
3This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released.
4
4
5−## Unreleased
6−
7−* Use TemplateHaskell to determine asset file paths
8− * Migration: All uses of `static @"some/path"` become `$(static "some/path")`. Instead of requiring `TypeApplications` and `DataKinds`, modules calling `static` must now enable `TemplateHaskell`.
9− * Deprecation: Deprecate static asset modules generated via 'obelisk-asset-manifest-generate' in favor of modules generated via 'obelisk-asset-th-generate'. The new executable takes the same arguments as the old and should be a drop-in replacement. To preserve the old behavior, set `__deprecated.useObeliskAssetManifestGenerate = true;` in your obelisk project configuration.
10− * Feature: Files added to the static directory while `ob run` is active no longer require `ob run` to be restarted
11−* Feature: When `staticFiles` is a derivation, as opposed to a regular directory, produce a symlink to the result of that derivation at `static.out` and have `ob run` serve static assets from that symlink. This makes it possible for the static asset derivation to be rebuilt and the new results served without restarting `ob run`.
12−* Feature: Rebuild static asset derivations while `ob run` is active as long as the change to the derivation is within the project folder. `ob run` now displays a message ("Static assets rebuilt and symlinked to static.out") whenever static assets have been rebuilt and the new static assets are being served.
13−* Feature: Add `staticFilePath` to `Obelisk.Generated.Static`. Like `static`, this uses TH to generate a reference to a file. Unlike `static`, this `staticFilePath` generates a path on the filesystem instead of URL path.
14−
15## v0.9.1.0
5## v0.9.1.0
16
6
17* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
7* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
default.nix+4−10
@@ -65,7 +65,7 @@ in rec {
65 '';
65 '';
66 nullIfAbsent = p: if lib.pathExists p then p else null;
66 nullIfAbsent = p: if lib.pathExists p then p else null;
67 #TODO: Avoid copying files within the nix store. Right now, obelisk-asset-manifest-generate copies files into a big blob so that the android/ios static assets can be imported from there; instead, we should get everything lined up right before turning it into an APK, so that copies, if necessary, only exist temporarily.
67 #TODO: Avoid copying files within the nix store. Right now, obelisk-asset-manifest-generate copies files into a big blob so that the android/ios static assets can be imported from there; instead, we should get everything lined up right before turning it into an APK, so that copies, if necessary, only exist temporarily.
68− processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static", exe ? "obelisk-asset-th-generate" }: pkgs.runCommand "asset-manifest" {
68+ processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static" }: pkgs.runCommand "asset-manifest" {
69 inherit src;
69 inherit src;
70 outputs = [ "out" "haskellManifest" "symlinked" ];
70 outputs = [ "out" "haskellManifest" "symlinked" ];
71 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
71 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
@@ -73,7 +73,7 @@ in rec {
73 set -euo pipefail
73 set -euo pipefail
74 touch "$out"
74 touch "$out"
75 mkdir -p "$symlinked"
75 mkdir -p "$symlinked"
76− ${exe} "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
76+ obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
77 '';
77 '';
78
78
79 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" {} ''
79 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" {} ''
@@ -237,14 +237,13 @@ in rec {
237 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
237 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
238 , __closureCompilerOptimizationLevel ? "ADVANCED" # Set this to `null` to skip the closure-compiler step
238 , __closureCompilerOptimizationLevel ? "ADVANCED" # Set this to `null` to skip the closure-compiler step
239 , __withGhcide ? false
239 , __withGhcide ? false
240− , __deprecated ? {}
241 }:
240 }:
242 let
241 let
243 allConfig = nixpkgs.lib.makeExtensible (self: {
242 allConfig = nixpkgs.lib.makeExtensible (self: {
244 base = base';
243 base = base';
245 inherit args;
244 inherit args;
246 userSettings = {
245 userSettings = {
247− inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide __deprecated;
246+ inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide;
248 staticFiles = if staticFiles == null then self.base + /static else staticFiles;
247 staticFiles = if staticFiles == null then self.base + /static else staticFiles;
249 };
248 };
250 frontendName = "frontend";
249 frontendName = "frontend";
@@ -252,12 +251,7 @@ in rec {
252 commonName = "common";
251 commonName = "common";
253 staticName = "obelisk-generated-static";
252 staticName = "obelisk-generated-static";
254 staticFilesImpure = let fs = self.userSettings.staticFiles; in if lib.isDerivation fs then fs else toString fs;
253 staticFilesImpure = let fs = self.userSettings.staticFiles; in if lib.isDerivation fs then fs else toString fs;
255− processedStatic = processAssets {
256− src = self.userSettings.staticFiles;
254+ processedStatic = processAssets { src = self.userSettings.staticFiles; };
257− exe = if lib.attrByPath ["userSettings" "__deprecated" "useObeliskAssetManifestGenerate"] false self
258− then builtins.trace "obelisk-asset-manifest-generate is deprecated. Use obelisk-asset-th-generate instead." "obelisk-asset-manifest-generate"
259− else "obelisk-asset-th-generate";
260− };
261 # The packages whose names and roles are defined by this package
255 # The packages whose names and roles are defined by this package
262 predefinedPackages = lib.filterAttrs (_: x: x != null) {
256 predefinedPackages = lib.filterAttrs (_: x: x != null) {
263 ${self.frontendName} = nullIfAbsent (self.base + "/frontend");
257 ${self.frontendName} = nullIfAbsent (self.base + "/frontend");
lib/asset/README.md+2−2
@@ -5,7 +5,7 @@
5
5
6**Efficiently served, aggressively cached static web resources.**
6**Efficiently served, aggressively cached static web resources.**
7
7
8−The `assets.nix` file contains nix expressions that are a mutually recursive set of attributes used to create static asset directories with hashable file encodings. This file should be incorporated within your project's `default.nix` file.
8+The `assets.nix` file contains nix expressions that are a mutually recursive set of attributes used to create static asset directories with hashable file encodings. This file should be incorporated within your project's `default.nix` file. (See `example/default.nix`, line 2)
9
9
10```nix
10```nix
11{ assets ? import ./../assets.nix { inherit nixpkgs; } }:
11{ assets ? import ./../assets.nix { inherit nixpkgs; } }:
@@ -19,7 +19,7 @@ In this example, `mkAssets` is used on a directory that has a `.png` file inside
19myAssets = assets.mkAssets ./static;
19myAssets = assets.mkAssets ./static;
20```
20```
21
21
22−Once you have successfully incorporated `assets.nix` into your project's nix file(s), use `nix-build` to generate a symlink of your `mkAssets` expression. The following command will generate an immutable symlink that holds hashed static assets.
22+Once you have successfully incorporated `assets.nix` into your project's nix file(s), use `nix-build` to generate a symlink of your `mkAssets` expression. The following command from `example/run-example` (line 3) will generate an immutable symlink that holds hashed static assets.
23
23
24```bash
24```bash
25nix-build -o static.assets -A myAssets
25nix-build -o static.assets -A myAssets
lib/asset/manifest/obelisk-asset-manifest.cabal+1−11
@@ -27,7 +27,6 @@ library
27 , unix-compat
27 , unix-compat
28 , vector
28 , vector
29 exposed-modules:
29 exposed-modules:
30− Obelisk.Asset.Cabal
31 Obelisk.Asset.Gather
30 Obelisk.Asset.Gather
32 Obelisk.Asset.Promoted
31 Obelisk.Asset.Promoted
33 Obelisk.Asset.Symlink
32 Obelisk.Asset.Symlink
@@ -36,7 +35,7 @@ library
36 other-extensions: TemplateHaskell
35 other-extensions: TemplateHaskell
37 ghc-options:
36 ghc-options:
38 -Wall -Werror -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O2
37 -Wall -Werror -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O2
39− -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
38+ -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
40
39
41executable obelisk-asset-manifest-generate
40executable obelisk-asset-manifest-generate
42 hs-source-dirs: src-bin
41 hs-source-dirs: src-bin
@@ -45,12 +44,3 @@ executable obelisk-asset-manifest-generate
45 base
44 base
46 , obelisk-asset-manifest
45 , obelisk-asset-manifest
47 , text
46 , text
48−
49−executable obelisk-asset-th-generate
50− hs-source-dirs: src-bin
51− main-is: static-th.hs
52− build-depends:
53− base
54− , obelisk-asset-manifest
55− , filepath
56− , text
lib/asset/manifest/src-bin/static-th.hsdeleted−43
@@ -1,43 +0,0 @@
1−import qualified Data.Text as T
2−import Obelisk.Asset.Cabal
3−import Obelisk.Asset.Gather
4−import Obelisk.Asset.Symlink
5−import System.Environment
6−import System.FilePath
7−
8−main :: IO ()
9−main = do
10− --TODO: Usage
11− [root, haskellTarget, packageName, moduleName, fileTarget] <- getArgs
12− paths <- gatherHashedPaths root
13− writeCabalProject haskellTarget $ SimplePkg
14− { _simplePkg_name = T.pack packageName
15− , _simplePkg_moduleName = T.pack moduleName
16− , _simplePkg_dependencies = map T.pack
17− [ "base"
18− , "obelisk-asset-manifest"
19− , "template-haskell"
20− ]
21− , _simplePkg_moduleContents = T.pack $ unlines
22− [ "{-# Language CPP #-}"
23− , "{-|"
24− , " Description:"
25− , " Automatically generated module that provides the 'static' TH function"
26− , " to generate paths to static assets."
27− , "-}"
28− , "module " <> moduleName <> " ( static, staticFilePath ) where"
29− , ""
30− , "import Obelisk.Asset.TH"
31− , "import Language.Haskell.TH"
32− , ""
33− , "static, staticFilePath :: FilePath -> Q Exp"
34− , "#ifdef OBELISK_ASSET_PASSTHRU"
35− , "static = staticAssetRaw"
36− , "staticFilePath = staticAssetFilePathRaw \"static.out\""
37− , "#else"
38− , "static = staticAssetHashed " <> show root
39− , "staticFilePath = staticAssetFilePathRaw " <> show root
40− , "#endif"
41− ]
42− }
43− copyAndSymlink paths root fileTarget
lib/asset/manifest/src/Obelisk/Asset/Cabal.hsdeleted−42
@@ -1,42 +0,0 @@
1−{-# Language OverloadedStrings #-}
2−module Obelisk.Asset.Cabal where
3−
4−import qualified Data.List as L
5−import Data.Text (Text)
6−import qualified Data.Text as T
7−import qualified Data.Text.IO as T
8−import System.Directory
9−import System.FilePath
10−
11−-- | A generated single-module package
12−data SimplePkg = SimplePkg
13− { _simplePkg_name :: Text
14− , _simplePkg_moduleName :: Text
15− , _simplePkg_moduleContents :: Text
16− , _simplePkg_dependencies :: [Text]
17− }
18−
19−cabalFile :: SimplePkg -> Text
20−cabalFile (SimplePkg packageName moduleName _ deps) = T.unlines
21− [ "name: " <> packageName
22− , "version: 0"
23− , "cabal-version: >= 1.2"
24− , "build-type: Simple"
25− , ""
26− , "library"
27− , " hs-source-dirs: src"
28− , " build-depends:"
29− , " " <> T.intercalate ", " deps
30− , " exposed-modules: " <> moduleName
31− ]
32−
33−writeCabalProject :: FilePath -> SimplePkg -> IO ()
34−writeCabalProject target pkg = do
35− createDirectoryIfMissing True target
36− T.writeFile (target </> T.unpack (_simplePkg_name pkg) <.> "cabal") $ cabalFile pkg
37− let moduleName = _simplePkg_moduleName pkg
38− (modName', moduleDirPath) = case L.uncons (reverse $ T.splitOn "." moduleName) of
39− Nothing -> error $ "writeStaticProject: invalid module name " <> T.unpack moduleName
40− Just (name, parents) -> (name, target </> "src" </> T.unpack (T.intercalate "/" $ reverse parents))
41− createDirectoryIfMissing True moduleDirPath
42− T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") $ _simplePkg_moduleContents pkg
lib/asset/manifest/src/Obelisk/Asset/Gather.hs+13−13
@@ -6,21 +6,21 @@ module Obelisk.Asset.Gather
6 , toHashedPath
6 , toHashedPath
7 ) where
7 ) where
8
8
9−import Control.DeepSeq (force)
9+import Control.DeepSeq
10import Control.Monad (forM)
10import Control.Monad (forM)
11−import Data.Bits (shift, (.|.), (.&.))
11+import Data.Bits
12−import qualified Data.ByteString.Lazy as LBS (readFile, toStrict, ByteString, length, index)
12+import qualified Data.ByteString.Lazy as LBS
13−import qualified Data.ByteString.Lazy.Builder as LBS (toLazyByteString, word8)
13+import qualified Data.ByteString.Lazy.Builder as LBS
14−import Data.Char (ord)
14+import Data.Char
15−import Data.Digest.Pure.SHA (bytestringDigest, sha256)
15+import Data.Digest.Pure.SHA
16import Data.Map (Map)
16import Data.Map (Map)
17−import qualified Data.Map as Map (singleton)
17+import qualified Data.Map as Map
18−import qualified Data.Text as T (unpack)
18+import qualified Data.Text as T
19−import Data.Text.Encoding (decodeUtf8)
19+import Data.Text.Encoding
20−import qualified Data.Vector.Unboxed as UV ((!), fromList)
20+import qualified Data.Vector.Unboxed as UV
21−import Data.Word (Word8)
21+import Data.Word
22−import System.FilePath.Posix ((</>), splitFileName, normalise)
22+import System.FilePath.Posix
23−import System.Directory (listDirectory, doesFileExist)
23+import System.Directory
24
24
25-- | Given a path, recursively explore it, creating hashed paths for all files found
25-- | Given a path, recursively explore it, creating hashed paths for all files found
26gatherHashedPaths
26gatherHashedPaths
lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+27−8
@@ -10,6 +10,7 @@ module Obelisk.Asset.Promoted
10import Obelisk.Asset.Gather
10import Obelisk.Asset.Gather
11
11
12import Data.Foldable
12import Data.Foldable
13+import qualified Data.List as L
13import Language.Haskell.TH (runQ, pprint)
14import Language.Haskell.TH (runQ, pprint)
14import Language.Haskell.TH.Syntax hiding (lift)
15import Language.Haskell.TH.Syntax hiding (lift)
15import GHC.TypeLits
16import GHC.TypeLits
@@ -17,11 +18,12 @@ import Data.Sequence (Seq)
17import qualified Data.Sequence as Seq
18import qualified Data.Sequence as Seq
18import Control.Monad.Trans.Writer
19import Control.Monad.Trans.Writer
19import System.FilePath
20import System.FilePath
21+import System.Directory
20import Data.Text (Text)
22import Data.Text (Text)
21import qualified Data.Text as T
23import qualified Data.Text as T
24+import qualified Data.Text.IO as T
22import Data.Map (Map)
25import Data.Map (Map)
23import qualified Data.Map as Map
26import qualified Data.Map as Map
24−import Obelisk.Asset.Cabal (writeCabalProject, SimplePkg(..))
25
27
26data StaticConfig = StaticConfig
28data StaticConfig = StaticConfig
27 { _staticConfig_packageName :: Text --TODO: Better type
29 { _staticConfig_packageName :: Text --TODO: Better type
@@ -30,14 +32,31 @@ data StaticConfig = StaticConfig
30
32
31writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
33writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
32writeStaticProject paths target cfg = do
34writeStaticProject paths target cfg = do
35+ createDirectoryIfMissing True target
36+ T.writeFile (target </> T.unpack (_staticConfig_packageName cfg) <.> "cabal") $ staticCabalFile cfg
33 let modName = _staticConfig_moduleName cfg
37 let modName = _staticConfig_moduleName cfg
38+ (modName', moduleDirPath) = case L.uncons (reverse $ T.splitOn "." modName) of
39+ Nothing -> error $ "writeStaticProject: invalid module name " <> T.unpack modName
40+ Just (name, parents) -> (name, target </> "src" </> T.unpack (T.intercalate "/" $ reverse parents))
41+ createDirectoryIfMissing True moduleDirPath
34 modContents <- staticModuleFile modName paths
42 modContents <- staticModuleFile modName paths
35− writeCabalProject target $ SimplePkg
43+ T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") modContents
36− { _simplePkg_name = _staticConfig_packageName cfg
44+
37− , _simplePkg_moduleName = _staticConfig_moduleName cfg
45+staticCabalFile :: StaticConfig -> Text
38− , _simplePkg_moduleContents = modContents
46+staticCabalFile cfg = T.unlines
39− , _simplePkg_dependencies = ["base", "ghc-prim", "text"]
47+ [ "name: " <> _staticConfig_packageName cfg
40− }
48+ , "version: 0"
49+ , "cabal-version: >= 1.2"
50+ , "build-type: Simple"
51+ , ""
52+ , "library"
53+ , " hs-source-dirs: src"
54+ , " build-depends:"
55+ , " base,"
56+ , " ghc-prim,"
57+ , " text"
58+ , " exposed-modules: " <> _staticConfig_moduleName cfg
59+ ]
41
60
42staticModuleFile :: Text -> Map FilePath FilePath -> IO Text
61staticModuleFile :: Text -> Map FilePath FilePath -> IO Text
43staticModuleFile moduleName paths = do
62staticModuleFile moduleName paths = do
@@ -50,7 +69,7 @@ staticModuleFile moduleName paths = do
50 , "{-# LANGUAGE OverloadedStrings #-}"
69 , "{-# LANGUAGE OverloadedStrings #-}"
51 , "{-# LANGUAGE ScopedTypeVariables #-}"
70 , "{-# LANGUAGE ScopedTypeVariables #-}"
52 , "{-# LANGUAGE TypeApplications #-}"
71 , "{-# LANGUAGE TypeApplications #-}"
53− , "module " <> moduleName <> " {-# DEPRECATED \"Generate this module with the 'obelisk-asset-th-generate' executable instead.\" #-} where"
72+ , "module " <> moduleName <> " where"
54 , ""
73 , ""
55 , "import qualified GHC.Types"
74 , "import qualified GHC.Types"
56 , "import Data.Text (Text)"
75 , "import Data.Text (Text)"
lib/asset/manifest/src/Obelisk/Asset/TH.hs+3−34
@@ -1,45 +1,14 @@
1−{-|
2−Description:
3− Template Haskell for generating asset paths.
4−-}
5module Obelisk.Asset.TH
1module Obelisk.Asset.TH
6 ( assetPath
2 ( assetPath
7− , staticAssetRaw
8− , staticAssetHashed
9− , staticAssetFilePath
10− , staticAssetFilePathRaw
11 ) where
3 ) where
12
4
13import Obelisk.Asset.Gather
5import Obelisk.Asset.Gather
14
6
15−import Language.Haskell.TH
16import Language.Haskell.TH.Syntax
7import Language.Haskell.TH.Syntax
17import System.FilePath.Posix
8import System.FilePath.Posix
18
9
19−-- | Produces the hashed path of a file
20−hashedAssetFilePath :: FilePath -> FilePath -> Q FilePath
21−hashedAssetFilePath root relativePath = do
22− qAddDependentFile $ root </> relativePath
23− runIO (toHashedPath root relativePath)
24−
25-- | Produces a string literal with the hashed path of the file
10-- | Produces a string literal with the hashed path of the file
26assetPath :: FilePath -> FilePath -> Q Exp
11assetPath :: FilePath -> FilePath -> Q Exp
27−assetPath root relativePath =
12+assetPath root relativePath = do
28− LitE . StringL <$> hashedAssetFilePath root relativePath
13+ qAddDependentFile $ root </> relativePath
29−
14+ LitE . StringL <$> runIO (toHashedPath root relativePath)
30−staticPrefix :: FilePath
31−staticPrefix = "/static"
32−
33−staticAssetRaw :: FilePath -> Q Exp
34−staticAssetRaw fp = returnQ $ LitE $ StringL $ staticPrefix </> fp
35−
36−staticAssetHashed :: FilePath -> FilePath -> Q Exp
37−staticAssetHashed root fp = do
38− LitE . StringL . (staticPrefix </>) <$> hashedAssetFilePath root fp
39−
40−staticAssetFilePathRaw :: FilePath -> FilePath -> Q Exp
41−staticAssetFilePathRaw root fp = returnQ $ LitE $ StringL $ root </> fp
42−
43−staticAssetFilePath :: FilePath -> FilePath -> Q Exp
44−staticAssetFilePath root fp = do
45− LitE . StringL . (root </>) <$> hashedAssetFilePath root fp
lib/cliapp/src/Obelisk/CliApp/Logging.hs+1−5
@@ -44,7 +44,7 @@ import Data.Text (Text)
44import qualified Data.Text as T
44import qualified Data.Text as T
45import qualified Data.Text.IO as T
45import qualified Data.Text.IO as T
46import GHC.IO.Encoding.Types
46import GHC.IO.Encoding.Types
47−import System.Console.ANSI (Color (..), ColorIntensity (Vivid),
47+import System.Console.ANSI (Color (Red, Yellow), ColorIntensity (Vivid),
48 ConsoleIntensity (FaintIntensity), ConsoleLayer (Foreground),
48 ConsoleIntensity (FaintIntensity), ConsoleLayer (Foreground),
49 SGR (SetColor, SetConsoleIntensity), clearLine)
49 SGR (SetColor, SetConsoleIntensity), clearLine)
50import System.Exit (ExitCode (..))
50import System.Exit (ExitCode (..))
@@ -184,8 +184,6 @@ writeLog withNewLine noColor (WithSeverity severity s) = if T.null s then pure (
184 | noColor && severity <= Warning = liftIO $ putFn $ T.pack (show severity) <> ": " <> s
184 | noColor && severity <= Warning = liftIO $ putFn $ T.pack (show severity) <> ": " <> s
185 | not noColor && severity <= Error = TS.putStrWithSGR errorColors h withNewLine s
185 | not noColor && severity <= Error = TS.putStrWithSGR errorColors h withNewLine s
186 | not noColor && severity <= Warning = TS.putStrWithSGR warningColors h withNewLine s
186 | not noColor && severity <= Warning = TS.putStrWithSGR warningColors h withNewLine s
187− | not noColor && severity == Notice = TS.putStrWithSGR noticeColors h withNewLine s
188− | not noColor && severity == Informational = TS.putStrWithSGR infoColors h withNewLine s
189 | not noColor && severity >= Debug = TS.putStrWithSGR debugColors h withNewLine s
187 | not noColor && severity >= Debug = TS.putStrWithSGR debugColors h withNewLine s
190 | otherwise = liftIO $ putFn s
188 | otherwise = liftIO $ putFn s
191
189
@@ -193,8 +191,6 @@ writeLog withNewLine noColor (WithSeverity severity s) = if T.null s then pure (
193 h = if severity <= Error then stderr else stdout
191 h = if severity <= Error then stderr else stdout
194 errorColors = [SetColor Foreground Vivid Red]
192 errorColors = [SetColor Foreground Vivid Red]
195 warningColors = [SetColor Foreground Vivid Yellow]
193 warningColors = [SetColor Foreground Vivid Yellow]
196− infoColors = [SetColor Foreground Vivid Green]
197− noticeColors = [SetColor Foreground Vivid Blue]
198 debugColors = [SetConsoleIntensity FaintIntensity]
194 debugColors = [SetConsoleIntensity FaintIntensity]
199
195
200-- | Allow the user to immediately switch to verbose logging upon pressing a particular key.
196-- | Allow the user to immediately switch to verbose logging upon pressing a particular key.
lib/command/obelisk-command.cabal−3
@@ -20,7 +20,6 @@ library
20 , exceptions
20 , exceptions
21 , extra
21 , extra
22 , filepath
22 , filepath
23− , fsnotify
24 , git
23 , git
25 , github
24 , github
26 , here
25 , here
@@ -40,8 +39,6 @@ library
40 , optparse-applicative
39 , optparse-applicative
41 , placeholders
40 , placeholders
42 , process
41 , process
43− , reflex
44− , reflex-fsnotify
45 , shell-escape
42 , shell-escape
46 , temporary
43 , temporary
47 , terminal-size
44 , terminal-size
lib/command/src/Obelisk/Command/Preprocessor.hs−1
@@ -59,7 +59,6 @@ writeOutput packageInfo' origPath outPath = withFile outPath WriteMode $ \hOut -
59 where
59 where
60 hPutTextBuilder h = BU.hPutBuilder h . TL.encodeUtf8Builder . TL.toLazyText
60 hPutTextBuilder h = BU.hPutBuilder h . TL.encodeUtf8Builder . TL.toLazyText
61
61
62−--NOTE: We cannot restrict the package set by adding '-package' flags to OPTIONS_GHC, because GHC rejects them there. It seems that we won't be able to properly handle that situation until GHC itself supports loading multiple packages officially in GHCi
63generateHeader :: FilePath -> CabalPackageInfo -> TL.Builder
62generateHeader :: FilePath -> CabalPackageInfo -> TL.Builder
64generateHeader origPath packageInfo =
63generateHeader origPath packageInfo =
65 hsExtensions <> ghcOptions <> lineNumberPragma origPath
64 hsExtensions <> ghcOptions <> lineNumberPragma origPath
lib/command/src/Obelisk/Command/Project.hs+9−129
@@ -1,9 +1,6 @@
1{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TupleSections #-}
6−
7module Obelisk.Command.Project
4module Obelisk.Command.Project
8 ( InitSource (..)
5 ( InitSource (..)
9 , findProjectObeliskCommand
6 , findProjectObeliskCommand
@@ -12,7 +9,6 @@ module Obelisk.Command.Project
12 , initProject
9 , initProject
13 , nixShellRunConfig
10 , nixShellRunConfig
14 , nixShellRunProc
11 , nixShellRunProc
15− , obShellRunProc
16 , nixShellWithHoogle
12 , nixShellWithHoogle
17 , nixShellWithoutPkgs
13 , nixShellWithoutPkgs
18 , obeliskDirName
14 , obeliskDirName
@@ -20,18 +16,12 @@ module Obelisk.Command.Project
20 , toObeliskDir
16 , toObeliskDir
21 , withProjectRoot
17 , withProjectRoot
22 , bashEscape
18 , bashEscape
23− , getHaskellManifestProjectPath
24− , AssetSource(..)
25− , describeImpureAssetSource
26− , watchStaticFilesDerivation
27 ) where
19 ) where
28
20
29import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
21import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
30−import Control.Lens ((.~), (?~), (<&>), (^.), _2, _3)
22+import Control.Lens ((.~), (?~), (<&>))
31import Control.Monad
23import Control.Monad
32−import Control.Monad.Except
33import Control.Monad.IO.Class (liftIO)
24import Control.Monad.IO.Class (liftIO)
34−import Control.Monad.Log
35import Control.Monad.State
25import Control.Monad.State
36import qualified Data.Aeson as Json
26import qualified Data.Aeson as Json
37import qualified Data.ByteString.UTF8 as BSU
27import qualified Data.ByteString.UTF8 as BSU
@@ -40,36 +30,28 @@ import qualified Data.ByteString.Lazy as BSL
40import Data.Default (def)
30import Data.Default (def)
41import Data.Function ((&), on)
31import Data.Function ((&), on)
42import Data.Map (Map)
32import Data.Map (Map)
43−import Data.Maybe (isJust)
44import Data.Text (Text)
33import Data.Text (Text)
45import qualified Data.Text as T
34import qualified Data.Text as T
46import Data.Text.Encoding (decodeUtf8, encodeUtf8)
35import Data.Text.Encoding (decodeUtf8, encodeUtf8)
47import Data.Traversable (for)
36import Data.Traversable (for)
48−import Reflex
49−import Reflex.FSNotify
50−import Reflex.Host.Headless
51import System.Directory
37import System.Directory
52import System.Environment (lookupEnv)
38import System.Environment (lookupEnv)
53−import System.Exit (ExitCode(..))
54import System.FilePath
39import System.FilePath
55−import System.FSNotify (defaultConfig, eventPath, WatchConfig(..))
56−import qualified System.Info as SysInfo
57import System.IO.Temp
40import System.IO.Temp
58import System.IO.Unsafe (unsafePerformIO)
41import System.IO.Unsafe (unsafePerformIO)
59import System.PosixCompat.Files
42import System.PosixCompat.Files
60import System.PosixCompat.Types
43import System.PosixCompat.Types
61import System.PosixCompat.User
44import System.PosixCompat.User
62−import qualified System.Process as Proc
63import Text.ShellEscape (bash, bytes)
45import Text.ShellEscape (bash, bytes)
64
46
65import GitHub.Data.GitData (Branch)
47import GitHub.Data.GitData (Branch)
66import GitHub.Data.Name (Name)
48import GitHub.Data.Name (Name)
67
49
68−import Obelisk.App (MonadObelisk, runObelisk, getObelisk)
50+import Obelisk.App (MonadObelisk)
69import Obelisk.CliApp
51import Obelisk.CliApp
70import Obelisk.Command.Nix
52import Obelisk.Command.Nix
71import Obelisk.Command.Thunk
53import Obelisk.Command.Thunk
72−import Obelisk.Command.Utils (nixBuildExePath, nixExePath, toNixPath, cp, nixShellPath, lnPath)
54+import Obelisk.Command.Utils (nixBuildExePath, nixExePath, toNixPath, cp, nixShellPath)
73
55
74--TODO: Make this module resilient to random exceptions
56--TODO: Make this module resilient to random exceptions
75
57
@@ -318,9 +300,6 @@ bashEscape = BSU.toString . bytes . bash . BSU.fromString
318nixShellRunProc :: NixShellConfig -> ProcessSpec
300nixShellRunProc :: NixShellConfig -> ProcessSpec
319nixShellRunProc cfg = setDelegateCtlc True $ proc nixShellPath $ runNixShellConfig cfg
301nixShellRunProc cfg = setDelegateCtlc True $ proc nixShellPath $ runNixShellConfig cfg
320
302
321−obShellRunProc :: FilePath -> Text -> ProcessSpec
322−obShellRunProc root cmd = setDelegateCtlc True $ setCwd (Just root) $ proc "ob" ["shell", T.unpack cmd]
323−
324nixShellWithoutPkgs
303nixShellWithoutPkgs
325 :: MonadObelisk m
304 :: MonadObelisk m
326 => FilePath -- ^ Path to project root
305 => FilePath -- ^ Path to project root
@@ -355,21 +334,7 @@ nixShellWithHoogle root isPure shell' command = do
355 \})).project.shells.${shell}"
334 \})).project.shells.${shell}"
356 & nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
335 & nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
357
336
358−-- | Describes the provenance of static assets (i.e., are they the result of a derivation
359−-- that was built, or just a folder full of files.
360−data AssetSource = AssetSource_Derivation
361− | AssetSource_Files
362− deriving (Eq)
363−
364−-- | Some log messages to make it easier to tell where static files are coming from
365−describeImpureAssetSource :: AssetSource -> Text -> Text
366−describeImpureAssetSource src path = case src of
367− AssetSource_Files -> "Assets impurely loaded from: " <> path
368− AssetSource_Derivation -> "Assets derivation built and impurely loaded from: " <> path
369−
370−-- | Determine where the static files of a project are and whether they're plain files or a derivation.
371−-- If they are a derivation, that derivation will be built.
372−findProjectAssets :: MonadObelisk m => FilePath -> m (AssetSource, Text)
337+findProjectAssets :: MonadObelisk m => FilePath -> m Text
373findProjectAssets root = do
338findProjectAssets root = do
374 isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
339 isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
375 proc nixExePath
340 proc nixExePath
@@ -381,95 +346,10 @@ findProjectAssets root = do
381 ]
346 ]
382 -- Check whether the impure static files are a derivation (and so must be built)
347 -- Check whether the impure static files are a derivation (and so must be built)
383 if isDerivation == "1"
348 if isDerivation == "1"
384− then do
385− _ <- buildStaticFilesDerivationAndSymlink
386− (readProcessAndLogStderr Debug)
387− root
388− pure (AssetSource_Derivation, T.pack $ root </> "static.out")
389− else fmap (AssetSource_Files,) $ do
390− path <- readProcessAndLogStderr Debug $ setCwd (Just root) $
391− proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
392− _ <- readProcessAndLogStderr Debug $ setCwd (Just root) $
393− proc lnPath ["-sfT", T.unpack path, "./static.out"]
394− pure path
395−
396−-- | Get the nix store path to the generated static asset manifest module (e.g., "obelisk-generated-static")
397−getHaskellManifestProjectPath :: MonadObelisk m => FilePath -> m Text
398−getHaskellManifestProjectPath root = fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $
349+ then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
399− proc nixBuildExePath
400− [ "--no-out-link"
401− , "-E"
402− , "(let a = import ./. {}; in a.passthru.processedStatic.haskellManifest)"
403− ]
404−
405−-- | Watch the project directory for file changes and check whether those file changes
406−-- cause changes in the static files nix derivation. If so, rebuild it.
407−watchStaticFilesDerivation
408− :: (MonadIO m, MonadObelisk m)
409− => FilePath
410− -> m ()
411−watchStaticFilesDerivation root = do
412− ob <- getObelisk
413− drv0 <- showDerivation
414− liftIO $ runHeadlessApp $ do
415− pb <- getPostBuild
416− checkForChanges <- batchOccurrences 0.25 =<< watchDirectoryTree
417− -- On macOS, use the polling backend due to https://github.com/luite/hfsevents/issues/13
418− (defaultConfig { confUsePolling = SysInfo.os == "darwin", confPollInterval = 250000 })
419− (root <$ pb)
420− ((/="static.out") . takeFileName . eventPath)
421− drv <- performEvent $ ffor checkForChanges $ \_ ->
422− liftIO $ runObelisk ob showDerivation
423− drvs <- foldDyn (\new (_, old, _) -> (old, new, old /= new)) (drv0, drv0, False) drv
424− void $ throttleBatchWithLag
425− (\e -> performEvent $ ffor e $ \_ -> liftIO $ runObelisk ob $ do
426− putLog Notice "Static assets being built..."
427− buildStaticCatchErrors >>= \case
428− Nothing -> pure ()
429− Just _ -> putLog Notice "Static assets built and symlinked to static.out"
430− )
431− ((() <$) . ffilter (\x -> isJust (x ^._2) && x ^._3) $ updated drvs)
432− pure never
433− where
434− handleBuildFailure
435− :: MonadObelisk m
436− => (ExitCode, String, String)
437− -> m (Maybe Text)
438− handleBuildFailure (ex, out, err) = case ex of
439− ExitSuccess -> pure $ Just $ T.pack out
440− _ -> do
441− putLog Error $
442− ("Static assets build failed: " <>) $
443− T.unlines $ reverse $ take 10 $ reverse $ T.lines $ T.pack err
444− pure Nothing
445− showDerivation :: MonadObelisk m => m (Maybe Text)
446− showDerivation =
447− handleBuildFailure <=< readCreateProcessWithExitCode $
448− setCwd (Just root) $ ProcessSpec
449− { _processSpec_createProcess = Proc.proc nixExePath
450− [ "show-derivation"
451− , "-f", "."
452− , "passthru.staticFilesImpure"
453− ]
454− , _processSpec_overrideEnv = Nothing
455− }
456− buildStaticCatchErrors :: MonadObelisk m => m (Maybe Text)
457− buildStaticCatchErrors = handleBuildFailure =<<
458− buildStaticFilesDerivationAndSymlink
459− readCreateProcessWithExitCode
460− root
461−
462−buildStaticFilesDerivationAndSymlink
463− :: MonadObelisk m
464− => (ProcessSpec -> m a)
465− -> FilePath
466− -> m a
467−buildStaticFilesDerivationAndSymlink f root = f $
468− setCwd (Just root) $ ProcessSpec
469− { _processSpec_createProcess = Proc.proc
470− nixBuildExePath
350+ proc nixBuildExePath
471− [ "-o", "static.out"
351+ [ "--no-out-link"
472 , "-E", "(import ./. {}).passthru.staticFilesImpure"
352 , "-E", "(import ./. {}).passthru.staticFilesImpure"
473 ]
353 ]
474− , _processSpec_overrideEnv = Nothing
354+ else readProcessAndLogStderr Debug $ setCwd (Just root) $
475− }
355+ proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
lib/command/src/Obelisk/Command/Run.hs+11−78
@@ -14,8 +14,7 @@ module Obelisk.Command.Run where
14import Control.Arrow ((&&&))
14import Control.Arrow ((&&&))
15import Control.Exception (Exception, bracket)
15import Control.Exception (Exception, bracket)
16import Control.Lens (ifor, (.~), (&))
16import Control.Lens (ifor, (.~), (&))
17−import Control.Concurrent (forkIO)
17+import Control.Monad (filterM)
18−import Control.Monad (filterM, void)
19import Control.Monad.Except (runExceptT, throwError)
18import Control.Monad.Except (runExceptT, throwError)
20import Control.Monad.IO.Class (liftIO)
19import Control.Monad.IO.Class (liftIO)
21import Control.Monad.Reader (MonadIO)
20import Control.Monad.Reader (MonadIO)
@@ -46,23 +45,13 @@ import Debug.Trace (trace)
46import Distribution.Compiler (CompilerFlavor(..))
45import Distribution.Compiler (CompilerFlavor(..))
47import Distribution.PackageDescription.Parsec (parseGenericPackageDescription)
46import Distribution.PackageDescription.Parsec (parseGenericPackageDescription)
48import Distribution.Parsec.ParseResult (runParseResult)
47import Distribution.Parsec.ParseResult (runParseResult)
49−import Distribution.Pretty (prettyShow)
50−import Distribution.Simple.Compiler (PackageDB (GlobalPackageDB))
51−import Distribution.Simple.Configure (configCompilerEx, getInstalledPackages)
52−import Distribution.Simple.PackageIndex (InstalledPackageIndex, lookupDependency)
53−import Distribution.Simple.Program.Db (defaultProgramDb)
54import qualified Distribution.System as Dist
48import qualified Distribution.System as Dist
55−import Distribution.Types.BuildInfo (buildable, cppOptions, defaultExtensions, defaultLanguage, hsSourceDirs, options, targetBuildDepends)
49+import Distribution.Types.BuildInfo (buildable, cppOptions, defaultExtensions, defaultLanguage, hsSourceDirs, options)
56import Distribution.Types.CondTree (simplifyCondTree)
50import Distribution.Types.CondTree (simplifyCondTree)
57−import Distribution.Types.Dependency (Dependency (..), depPkgName)
58import Distribution.Types.GenericPackageDescription (ConfVar (Arch, Impl, OS), condLibrary)
51import Distribution.Types.GenericPackageDescription (ConfVar (Arch, Impl, OS), condLibrary)
59−import Distribution.Types.InstalledPackageInfo (compatPackageKey)
60import Distribution.Types.Library (libBuildInfo)
52import Distribution.Types.Library (libBuildInfo)
61−import Distribution.Types.PackageName (mkPackageName)
62−import Distribution.Types.VersionRange (anyVersion)
63import Distribution.Utils.Generic (toUTF8BS, readUTF8File)
53import Distribution.Utils.Generic (toUTF8BS, readUTF8File)
64import qualified Distribution.Parsec.Common as Dist
54import qualified Distribution.Parsec.Common as Dist
65−import qualified Distribution.Verbosity as Verbosity (silent)
66import qualified Hpack.Config as Hpack
55import qualified Hpack.Config as Hpack
67import qualified Hpack.Render as Hpack
56import qualified Hpack.Render as Hpack
68import qualified Hpack.Yaml as Hpack
57import qualified Hpack.Yaml as Hpack
@@ -72,16 +61,12 @@ import System.Directory
72import System.Environment (getExecutablePath)
61import System.Environment (getExecutablePath)
73import System.FilePath
62import System.FilePath
74import qualified System.Info
63import qualified System.Info
75−import System.IO (hClose, hGetLine)
76import System.IO.Temp (withSystemTempDirectory)
64import System.IO.Temp (withSystemTempDirectory)
77−import System.Process (CreateProcess (..), StdStream (..))
78
65
79−import Obelisk.App (MonadObelisk, getObelisk, runObelisk)
66+import Obelisk.App (MonadObelisk)
80import Obelisk.CliApp (
67import Obelisk.CliApp (
81 Severity (..),
68 Severity (..),
82− createProcess_,
83 failWith,
69 failWith,
84− overCreateProcess,
85 proc,
70 proc,
86 putLog,
71 putLog,
87 readCreateProcessWithExitCode,
72 readCreateProcessWithExitCode,
@@ -92,7 +77,7 @@ import Obelisk.CliApp (
92 withSpinner,
77 withSpinner,
93 )
78 )
94import Obelisk.Command.Nix
79import Obelisk.Command.Nix
95−import Obelisk.Command.Project
80+import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape)
96import Obelisk.Command.Thunk (attrCacheFileName)
81import Obelisk.Command.Thunk (attrCacheFileName)
97import Obelisk.Command.Utils (findExePath, ghcidExePath)
82import Obelisk.Command.Utils (findExePath, ghcidExePath)
98
83
@@ -111,8 +96,6 @@ data CabalPackageInfo = CabalPackageInfo
111 -- ^ List of compiler-specific options (e.g., the "ghc-options" field of the cabal file)
96 -- ^ List of compiler-specific options (e.g., the "ghc-options" field of the cabal file)
112 , _cabalPackageInfo_cppOptions :: [String]
97 , _cabalPackageInfo_cppOptions :: [String]
113 -- ^ List of CPP (C Preprocessor) options (e.g. the "cpp-options" field of the cabal file)
98 -- ^ List of CPP (C Preprocessor) options (e.g. the "cpp-options" field of the cabal file)
114− , _cabalPackageInfo_buildDepends :: [Dependency]
115− -- ^ List of build dependencies listed in the cabal file
116 }
99 }
117
100
118-- | 'Bool' with a better name for its purpose.
101-- | 'Bool' with a better name for its purpose.
@@ -161,8 +144,8 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
161 , _target_attr = Just "__unstable__.profiledObRun"
144 , _target_attr = Just "__unstable__.profiledObRun"
162 , _target_expr = Nothing
145 , _target_expr = Nothing
163 }
146 }
164− (assetType, assets) <- findProjectAssets root
147+ assets <- findProjectAssets root
165− putLog Debug $ describeImpureAssetSource assetType assets
148+ putLog Debug $ "Assets impurely loaded from: " <> assets
166 time <- liftIO getCurrentTime
149 time <- liftIO getCurrentTime
167 let profileBaseName = formatTime defaultTimeLocale profileBasePattern time
150 let profileBaseName = formatTime defaultTimeLocale profileBasePattern time
168 liftIO $ createDirectoryIfMissing True $ takeDirectory $ root </> profileBaseName
151 liftIO $ createDirectoryIfMissing True $ takeDirectory $ root </> profileBaseName
@@ -180,16 +163,9 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
180run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
163run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
181run root interpretPaths = do
164run root interpretPaths = do
182 pkgs <- getParsedLocalPkgs root interpretPaths
165 pkgs <- getParsedLocalPkgs root interpretPaths
183− (assetType, assets) <- findProjectAssets root
166+ assets <- findProjectAssets root
184− manifestPkg <- parsePackagesOrFail . (:[]) . T.unpack =<< getHaskellManifestProjectPath root
167+ putLog Debug $ "Assets impurely loaded from: " <> assets
185− putLog Debug $ describeImpureAssetSource assetType assets
186− case assetType of
187− AssetSource_Derivation -> do
188− ob <- getObelisk
189− putLog Debug "Starting static file derivation watcher..."
190− void $ liftIO $ forkIO $ runObelisk ob $ watchStaticFilesDerivation root
191− _ -> pure ()
192− ghciArgs <- getGhciSessionSettings (pkgs <> manifestPkg) root True
168+ ghciArgs <- getGhciSessionSettings pkgs root True
193 freePort <- getFreePort
169 freePort <- getFreePort
194 withGhciScriptArgs pkgs $ \dotGhciArgs -> do
170 withGhciScriptArgs pkgs $ \dotGhciArgs -> do
195 runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
171 runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
@@ -246,12 +222,7 @@ getLocalPkgs root interpretPaths = do
246 let rootsAndExclusions = calcIntepretFinds "" interpretPaths
222 let rootsAndExclusions = calcIntepretFinds "" interpretPaths
247
223
248 fmap fold $ for (MMap.toAscList rootsAndExclusions) $ \(interpretPathRoot, exclusions) ->
224 fmap fold $ for (MMap.toAscList rootsAndExclusions) $ \(interpretPathRoot, exclusions) ->
249− let allExclusions = obeliskPackageExclusions
225+ let allExclusions = obeliskPackageExclusions <> exclusions <> Set.singleton ("*" </> attrCacheFileName)
250− <> exclusions
251− <> Set.singleton ("*" </> attrCacheFileName)
252− <> Set.singleton ("*" </> "lib/asset/manifest") -- NB: obelisk-asset-manifest is excluded because it generates
253− -- a module that in turn imports it. This will cause ob run to
254− -- fail in its current implementation.
255 in fmap (Set.fromList . map normalise) $ runFind $
226 in fmap (Set.fromList . map normalise) $ runFind $
256 ["-L", interpretPathRoot, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
227 ["-L", interpretPathRoot, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
257 <> concat [["-not", "-path", p </> "*"] | p <- toList allExclusions]
228 <> concat [["-not", "-path", p </> "*"] | p <- toList allExclusions]
@@ -383,7 +354,6 @@ parseCabalPackage' pkg = runExceptT $ do
383 defaultLanguage $ libBuildInfo lib
354 defaultLanguage $ libBuildInfo lib
384 , _cabalPackageInfo_compilerOptions = options $ libBuildInfo lib
355 , _cabalPackageInfo_compilerOptions = options $ libBuildInfo lib
385 , _cabalPackageInfo_cppOptions = cppOptions $ libBuildInfo lib
356 , _cabalPackageInfo_cppOptions = cppOptions $ libBuildInfo lib
386− , _cabalPackageInfo_buildDepends = targetBuildDepends $ libBuildInfo lib
387 }
357 }
388 Right Nothing -> pure Nothing
358 Right Nothing -> pure Nothing
389 Left (_, errors) ->
359 Left (_, errors) ->
@@ -430,8 +400,7 @@ withGhciScriptArgs
430 => f CabalPackageInfo -- ^ List of packages to load into ghci
400 => f CabalPackageInfo -- ^ List of packages to load into ghci
431 -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
401 -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
432 -> m ()
402 -> m ()
433−withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp ->
403+withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp -> f ["-XNoImplicitPrelude", "-ghci-script", fp]
434− f ["-XNoImplicitPrelude", "-ghci-script", fp]
435 where
404 where
436 -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
405 -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
437 -- is passed to the ghci session.
406 -- is passed to the ghci session.
@@ -481,7 +450,6 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
481 -- all paths to 'pathBase'.
450 -- all paths to 'pathBase'.
482 selfExe <- liftIO $ canonicalizePath =<< getExecutablePath
451 selfExe <- liftIO $ canonicalizePath =<< getExecutablePath
483 canonicalPathBase <- liftIO $ canonicalizePath pathBase
452 canonicalPathBase <- liftIO $ canonicalizePath pathBase
484− installedPackageIndex <- loadPackageIndex pathBase
485
453
486 (pkgFiles, pkgSrcPaths :: [NonEmpty FilePath]) <- fmap unzip $ liftIO $ for packageInfos $ \pkg -> do
454 (pkgFiles, pkgSrcPaths :: [NonEmpty FilePath]) <- fmap unzip $ liftIO $ for packageInfos $ \pkg -> do
487 canonicalSrcDirs <- traverse canonicalizePath $ (_cabalPackageInfo_packageRoot pkg </>) <$> _cabalPackageInfo_sourceDirs pkg
455 canonicalSrcDirs <- traverse canonicalizePath $ (_cabalPackageInfo_packageRoot pkg </>) <$> _cabalPackageInfo_sourceDirs pkg
@@ -490,51 +458,16 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
490
458
491 pure
459 pure
492 $ baseGhciOptions
460 $ baseGhciOptions
493− <> ["-DOBELISK_ASSET_PASSTHRU"] -- For passthrough static assets
494 <> ["-F", "-pgmF", selfExe, "-optF", preprocessorIdentifier]
461 <> ["-F", "-pgmF", selfExe, "-optF", preprocessorIdentifier]
495 <> concatMap (\p -> ["-optF", p]) pkgFiles
462 <> concatMap (\p -> ["-optF", p]) pkgFiles
496 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
463 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
497− <> concatMap (\packageId -> ["-package-id", packageId ])
498− (packageIds installedPackageIndex)
499 where
464 where
500 relativeTo' = if useRelativePaths then relativeTo else const
465 relativeTo' = if useRelativePaths then relativeTo else const
501− -- Package names we're building and not needed from the package DB
502− packageNames =
503− map (mkPackageName . T.unpack . _cabalPackageInfo_packageName)
504− packageInfos
505− packageIds installedPackageIndex =
506− map (dependencyPackageId installedPackageIndex) $
507− filter ((`notElem` packageNames) . depPkgName) $
508− concatMap _cabalPackageInfo_buildDepends packageInfos <>
509− [Dependency (mkPackageName "obelisk-run") anyVersion]
510− dependencyPackageId installedPackageIndex dep =
511− case lookupDependency installedPackageIndex dep of
512− ((_version,installedPackageInfo:_) :_) ->
513− compatPackageKey installedPackageInfo
514− _ -> error $ "Couldn't resolve dependency for " <> prettyShow dep
515−
516−-- Load the package index used by the GHC in this path's nix project
517−loadPackageIndex :: (MonadObelisk m) => FilePath -> m InstalledPackageIndex
518−loadPackageIndex root = do
519− ghcPath <- getPathInNixEnvironment "bash -c 'type -p ghc'"
520− ghcPkgPath <- getPathInNixEnvironment "bash -c 'type -p ghc-pkg'"
521− (compiler, _platform, programDb) <- liftIO
522− $ configCompilerEx (Just GHC) (Just ghcPath) (Just ghcPkgPath) defaultProgramDb Verbosity.silent
523− liftIO $ getInstalledPackages Verbosity.silent compiler [GlobalPackageDB] programDb
524− where
525− getPathInNixEnvironment cmd = do
526− (_,Just h,_,_) <- createProcess_ "loadPackageIndex" $ procSpec cmd
527− path <- liftIO $ hGetLine h >>= canonicalizePath
528− liftIO $ hClose h
529− pure path
530− procSpec =
531− overCreateProcess (\cs -> cs { std_out = CreatePipe }) . obShellRunProc root
532
466
533baseGhciOptions :: [String]
467baseGhciOptions :: [String]
534baseGhciOptions =
468baseGhciOptions =
535 [ "-ignore-dot-ghci"
469 [ "-ignore-dot-ghci"
536 , "-no-user-package-db"
470 , "-no-user-package-db"
537− , "-hide-all-packages"
538 , "-package-env", "-"
471 , "-package-env", "-"
539 ]
472 ]
540
473
lib/command/src/Obelisk/Command/Utils.hs−3
@@ -75,9 +75,6 @@ gitPath = $(staticWhich "git")
75whichPath :: FilePath
75whichPath :: FilePath
76whichPath = $(staticWhich "which")
76whichPath = $(staticWhich "which")
77
77
78−lnPath :: FilePath
79−lnPath = $(staticWhich "ln")
80−
81sshKeygenPath :: FilePath
78sshKeygenPath :: FilePath
82sshKeygenPath = $(staticWhich "ssh-keygen")
79sshKeygenPath = $(staticWhich "ssh-keygen")
83
80
skeleton/.gitignore−1
@@ -13,4 +13,3 @@ result
13result-*
13result-*
14tags
14tags
15TAGS
15TAGS
16−static.out
skeleton/frontend/src/Frontend.hs+4−3
@@ -1,5 +1,6 @@
1+{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE TemplateHaskell #-}
3+{-# LANGUAGE TypeApplications #-}
3
4
4module Frontend where
5module Frontend where
5
6
@@ -26,7 +27,7 @@ frontend :: Frontend (R FrontendRoute)
26frontend = Frontend
27frontend = Frontend
27 { _frontend_head = do
28 { _frontend_head = do
28 el "title" $ text "Obelisk Minimal Example"
29 el "title" $ text "Obelisk Minimal Example"
29− elAttr "link" ("href" =: $(static "main.css") <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
30+ elAttr "link" ("href" =: static @"main.css" <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
30 , _frontend_body = do
31 , _frontend_body = do
31 el "h1" $ text "Welcome to Obelisk!"
32 el "h1" $ text "Welcome to Obelisk!"
32 el "p" $ text $ T.pack commonStuff
33 el "p" $ text $ T.pack commonStuff
@@ -37,7 +38,7 @@ frontend = Frontend
37 -- print "Hello, World!" on the client.
38 -- print "Hello, World!" on the client.
38 prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
39 prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
39
40
40− elAttr "img" ("src" =: $(static "obelisk.jpg")) blank
41+ elAttr "img" ("src" =: static @"obelisk.jpg") blank
41 el "div" $ do
42 el "div" $ do
42 exampleConfig <- getConfig "common/example"
43 exampleConfig <- getConfig "common/example"
43 case exampleConfig of
44 case exampleConfig of