on master help sign in
Merge branch 'develop' into reduce-nix-closure
commit955b7bb0e418eaa7a21123ae9f0579a99b0e78fd authorAli Abrar <aliabrar@gmail.com> dateFri 16 Apr 2021, 08:54 (6 years ago) parent655f93960d56bc
21 files changed+410−87
ChangeLog.md+12−1 README.md+1−1 all-builds.nix+1−1 default.nix+11−5 dep/reflex-platform/github.json+3−3 lib/asset/README.md+2−2 lib/asset/manifest/obelisk-asset-manifest.cabal+11−1 lib/asset/manifest/src-bin/static-th.hs+43 lib/asset/manifest/src/Obelisk/Asset/Cabal.hs+42 lib/asset/manifest/src/Obelisk/Asset/Gather.hs+13−13 lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+8−27 lib/asset/manifest/src/Obelisk/Asset/TH.hs+34−3 lib/cliapp/src/Obelisk/CliApp/Logging.hs+5−1 lib/command/obelisk-command.cabal+3 lib/command/src/Obelisk/Command/Preprocessor.hs+1 lib/command/src/Obelisk/Command/Project.hs+142−13 lib/command/src/Obelisk/Command/Run.hs+70−11 lib/command/src/Obelisk/Command/Utils.hs+3 skeleton/.gitignore+1 skeleton/default.nix+1−1 skeleton/frontend/src/Frontend.hs+3−4
ChangeLog.md+12−1
@@ -4,12 +4,23 @@ This project's release branch is `master`. This log is written from the perspect
44
55## Unreleased
66
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
16+
717* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
818* [#812](https://github.com/obsidiansystems/obelisk/pull/812): Add support for `NoImplicitPrelude` and other extensions disabled via `No`
919* Pinned version bumps:
10− * reflex-platform [0.6.2.0](https://github.com/reflex-frp/reflex-platform/releases/tag/v0.6.2.0)
20+ * reflex-platform [0.7.0.0](https://github.com/reflex-frp/reflex-platform/releases/tag/v0.7.0.0)
1121 * hnix 0.8.0
1222* [#787](https://github.com/obsidiansystems/obelisk/pull/787): Set `immutable` cache control directive when serving content-addressed static assets
23+* Use iOS SDK 13.2
1324
1425
1526## v0.9.0.1
README.md+1−1
@@ -221,7 +221,7 @@ Your account must also belong to an Apple Developer Team, if you want to access
2212211. Click accept to authorize on both the computer and the iPhone.
222222
223223##### Xcode
224−Install Xcode 8.2 (contains iOS SDK 10.2) and open it so that it runs its post install tool setup.
224+Install Xcode 11.2 (contains iOS SDK 13.2) and open it so that it runs its post install tool setup.
225225
226226These versions will work out of the box but iOS SDKs prior to 11.3 should also work. You can choose another installed version in `default.nix`
227227
all-builds.nix+1−1
@@ -1,6 +1,6 @@
11{ self-args ? {
22 config.android_sdk.accept_license = true;
3− iosSdkVersion = "10.2";
3+ iosSdkVersion = "13.2";
44 }
55, local-self ? import ./. self-args
66, supportedSystems ? [ builtins.currentSystem ]
default.nix+11−5
@@ -1,6 +1,6 @@
11{ system ? builtins.currentSystem
22, profiling ? false
3−, iosSdkVersion ? "10.2"
3+, iosSdkVersion ? "13.2"
44, config ? {}
55, terms ? { # Accepted terms, conditions, and licenses
66 security.acme.acceptTerms = false;
@@ -65,7 +65,7 @@ in rec {
6565 '';
6666 nullIfAbsent = p: if lib.pathExists p then p else null;
6767 #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" }: pkgs.runCommand "asset-manifest" {
68+ processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static", exe ? "obelisk-asset-th-generate" }: pkgs.runCommand "asset-manifest" {
6969 inherit src;
7070 outputs = [ "out" "haskellManifest" "symlinked" ];
7171 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
@@ -73,7 +73,7 @@ in rec {
7373 set -euo pipefail
7474 touch "$out"
7575 mkdir -p "$symlinked"
76− obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
76+ ${exe} "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
7777 '';
7878
7979 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" {} ''
@@ -237,13 +237,14 @@ in rec {
237237 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
238238 , __closureCompilerOptimizationLevel ? "ADVANCED" # Set this to `null` to skip the closure-compiler step
239239 , __withGhcide ? false
240+ , __deprecated ? {}
240241 }:
241242 let
242243 allConfig = nixpkgs.lib.makeExtensible (self: {
243244 base = base';
244245 inherit args;
245246 userSettings = {
246− inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide;
247+ inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide __deprecated;
247248 staticFiles = if staticFiles == null then self.base + /static else staticFiles;
248249 };
249250 frontendName = "frontend";
@@ -251,7 +252,12 @@ in rec {
251252 commonName = "common";
252253 staticName = "obelisk-generated-static";
253254 staticFilesImpure = let fs = self.userSettings.staticFiles; in if lib.isDerivation fs then fs else toString fs;
254− processedStatic = processAssets { src = self.userSettings.staticFiles; };
255+ processedStatic = processAssets {
256+ 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+ };
255261 # The packages whose names and roles are defined by this package
256262 predefinedPackages = lib.filterAttrs (_: x: x != null) {
257263 ${self.frontendName} = nullIfAbsent (self.base + "/frontend");
dep/reflex-platform/github.json+3−3
@@ -1,8 +1,8 @@
11{
22 "owner": "reflex-frp",
33 "repo": "reflex-platform",
4− "branch": "release/0.6.2.0",
4+ "branch": "release/0.7.0.0",
55 "private": false,
6− "rev": "efc6d923c633207d18bd4d8cae3e20110a377864",
7− "sha256": "121rmnkx8nwiy96ipfyyv6vrgysv0zpr2br46y70zf4d0y1h1lz5"
6+ "rev": "27a7851a780ff21472d85f9a02fb84635ef9027a",
7+ "sha256": "1m4zlw91dpjlbdchjyxhvhv3k7lilm0qa17dhhww868xxc27sann"
88}
lib/asset/README.md+2−2
@@ -5,7 +5,7 @@
55
66**Efficiently served, aggressively cached static web resources.**
77
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)
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.
99
1010```nix
1111{ 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
1919myAssets = assets.mkAssets ./static;
2020```
2121
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.
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.
2323
2424```bash
2525nix-build -o static.assets -A myAssets
lib/asset/manifest/obelisk-asset-manifest.cabal+11−1
@@ -27,6 +27,7 @@ library
2727 , unix-compat
2828 , vector
2929 exposed-modules:
30+ Obelisk.Asset.Cabal
3031 Obelisk.Asset.Gather
3132 Obelisk.Asset.Promoted
3233 Obelisk.Asset.Symlink
@@ -35,7 +36,7 @@ library
3536 other-extensions: TemplateHaskell
3637 ghc-options:
3738 -Wall -Werror -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O2
38− -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
39+ -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
3940
4041executable obelisk-asset-manifest-generate
4142 hs-source-dirs: src-bin
@@ -44,3 +45,12 @@ executable obelisk-asset-manifest-generate
4445 base
4546 , obelisk-asset-manifest
4647 , 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.hsnew file+43
@@ -0,0 +1,43 @@
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.hsnew file+42
@@ -0,0 +1,42 @@
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
66 , toHashedPath
77 ) where
88
9−import Control.DeepSeq
9+import Control.DeepSeq (force)
1010import Control.Monad (forM)
11−import Data.Bits
12−import qualified Data.ByteString.Lazy as LBS
13−import qualified Data.ByteString.Lazy.Builder as LBS
14−import Data.Char
15−import Data.Digest.Pure.SHA
11+import Data.Bits (shift, (.|.), (.&.))
12+import qualified Data.ByteString.Lazy as LBS (readFile, toStrict, ByteString, length, index)
13+import qualified Data.ByteString.Lazy.Builder as LBS (toLazyByteString, word8)
14+import Data.Char (ord)
15+import Data.Digest.Pure.SHA (bytestringDigest, sha256)
1616import Data.Map (Map)
17−import qualified Data.Map as Map
18−import qualified Data.Text as T
19−import Data.Text.Encoding
20−import qualified Data.Vector.Unboxed as UV
21−import Data.Word
22−import System.FilePath.Posix
23−import System.Directory
17+import qualified Data.Map as Map (singleton)
18+import qualified Data.Text as T (unpack)
19+import Data.Text.Encoding (decodeUtf8)
20+import qualified Data.Vector.Unboxed as UV ((!), fromList)
21+import Data.Word (Word8)
22+import System.FilePath.Posix ((</>), splitFileName, normalise)
23+import System.Directory (listDirectory, doesFileExist)
2424
2525-- | Given a path, recursively explore it, creating hashed paths for all files found
2626gatherHashedPaths
lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+8−27
@@ -10,7 +10,6 @@ module Obelisk.Asset.Promoted
1010import Obelisk.Asset.Gather
1111
1212import Data.Foldable
13−import qualified Data.List as L
1413import Language.Haskell.TH (runQ, pprint)
1514import Language.Haskell.TH.Syntax hiding (lift)
1615import GHC.TypeLits
@@ -18,12 +17,11 @@ import Data.Sequence (Seq)
1817import qualified Data.Sequence as Seq
1918import Control.Monad.Trans.Writer
2019import System.FilePath
21−import System.Directory
2220import Data.Text (Text)
2321import qualified Data.Text as T
24−import qualified Data.Text.IO as T
2522import Data.Map (Map)
2623import qualified Data.Map as Map
24+import Obelisk.Asset.Cabal (writeCabalProject, SimplePkg(..))
2725
2826data StaticConfig = StaticConfig
2927 { _staticConfig_packageName :: Text --TODO: Better type
@@ -32,31 +30,14 @@ data StaticConfig = StaticConfig
3230
3331writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
3432writeStaticProject paths target cfg = do
35− createDirectoryIfMissing True target
36− T.writeFile (target </> T.unpack (_staticConfig_packageName cfg) <.> "cabal") $ staticCabalFile cfg
3733 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
4234 modContents <- staticModuleFile modName paths
43− T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") modContents
44−
45−staticCabalFile :: StaticConfig -> Text
46−staticCabalFile cfg = T.unlines
47− [ "name: " <> _staticConfig_packageName cfg
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− ]
35+ writeCabalProject target $ SimplePkg
36+ { _simplePkg_name = _staticConfig_packageName cfg
37+ , _simplePkg_moduleName = _staticConfig_moduleName cfg
38+ , _simplePkg_moduleContents = modContents
39+ , _simplePkg_dependencies = ["base", "ghc-prim", "text"]
40+ }
6041
6142staticModuleFile :: Text -> Map FilePath FilePath -> IO Text
6243staticModuleFile moduleName paths = do
@@ -69,7 +50,7 @@ staticModuleFile moduleName paths = do
6950 , "{-# LANGUAGE OverloadedStrings #-}"
7051 , "{-# LANGUAGE ScopedTypeVariables #-}"
7152 , "{-# LANGUAGE TypeApplications #-}"
72− , "module " <> moduleName <> " where"
53+ , "module " <> moduleName <> " {-# DEPRECATED \"Generate this module with the 'obelisk-asset-th-generate' executable instead.\" #-} where"
7354 , ""
7455 , "import qualified GHC.Types"
7556 , "import Data.Text (Text)"
lib/asset/manifest/src/Obelisk/Asset/TH.hs+34−3
@@ -1,14 +1,45 @@
1+{-|
2+Description:
3+ Template Haskell for generating asset paths.
4+-}
15module Obelisk.Asset.TH
26 ( assetPath
7+ , staticAssetRaw
8+ , staticAssetHashed
9+ , staticAssetFilePath
10+ , staticAssetFilePathRaw
311 ) where
412
513import Obelisk.Asset.Gather
614
15+import Language.Haskell.TH
716import Language.Haskell.TH.Syntax
817import System.FilePath.Posix
918
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+
1025-- | Produces a string literal with the hashed path of the file
1126assetPath :: FilePath -> FilePath -> Q Exp
12−assetPath root relativePath = do
13− qAddDependentFile $ root </> relativePath
14− LitE . StringL <$> runIO (toHashedPath root relativePath)
27+assetPath root relativePath =
28+ LitE . StringL <$> hashedAssetFilePath root relativePath
29+
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+5−1
@@ -44,7 +44,7 @@ import Data.Text (Text)
4444import qualified Data.Text as T
4545import qualified Data.Text.IO as T
4646import GHC.IO.Encoding.Types
47−import System.Console.ANSI (Color (Red, Yellow), ColorIntensity (Vivid),
47+import System.Console.ANSI (Color (..), ColorIntensity (Vivid),
4848 ConsoleIntensity (FaintIntensity), ConsoleLayer (Foreground),
4949 SGR (SetColor, SetConsoleIntensity), clearLine)
5050import System.Exit (ExitCode (..))
@@ -184,6 +184,8 @@ writeLog withNewLine noColor (WithSeverity severity s) = if T.null s then pure (
184184 | noColor && severity <= Warning = liftIO $ putFn $ T.pack (show severity) <> ": " <> s
185185 | not noColor && severity <= Error = TS.putStrWithSGR errorColors h withNewLine s
186186 | 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
187189 | not noColor && severity >= Debug = TS.putStrWithSGR debugColors h withNewLine s
188190 | otherwise = liftIO $ putFn s
189191
@@ -191,6 +193,8 @@ writeLog withNewLine noColor (WithSeverity severity s) = if T.null s then pure (
191193 h = if severity <= Error then stderr else stdout
192194 errorColors = [SetColor Foreground Vivid Red]
193195 warningColors = [SetColor Foreground Vivid Yellow]
196+ infoColors = [SetColor Foreground Vivid Green]
197+ noticeColors = [SetColor Foreground Vivid Blue]
194198 debugColors = [SetConsoleIntensity FaintIntensity]
195199
196200-- | Allow the user to immediately switch to verbose logging upon pressing a particular key.
lib/command/obelisk-command.cabal+3
@@ -20,6 +20,7 @@ library
2020 , exceptions
2121 , extra
2222 , filepath
23+ , fsnotify
2324 , git
2425 , github
2526 , here
@@ -39,6 +40,8 @@ library
3940 , optparse-applicative
4041 , placeholders
4142 , process
43+ , reflex
44+ , reflex-fsnotify
4245 , shell-escape
4346 , temporary
4447 , terminal-size
lib/command/src/Obelisk/Command/Preprocessor.hs+1
@@ -59,6 +59,7 @@ writeOutput packageInfo' origPath outPath = withFile outPath WriteMode $ \hOut -
5959 where
6060 hPutTextBuilder h = BU.hPutBuilder h . TL.encodeUtf8Builder . TL.toLazyText
6161
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
6263generateHeader :: FilePath -> CabalPackageInfo -> TL.Builder
6364generateHeader origPath packageInfo =
6465 hsExtensions <> ghcOptions <> lineNumberPragma origPath
lib/command/src/Obelisk/Command/Project.hs+142−13
@@ -1,6 +1,9 @@
11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE LambdaCase #-}
33{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE TupleSections #-}
6+
47module Obelisk.Command.Project
58 ( InitSource (..)
69 , findProjectObeliskCommand
@@ -11,17 +14,24 @@ module Obelisk.Command.Project
1114 , nixShellRunProc
1215 , nixShellWithHoogle
1316 , nixShellWithoutPkgs
17+ , mkObNixShellProc
1418 , obeliskDirName
1519 , toImplDir
1620 , toObeliskDir
1721 , withProjectRoot
1822 , bashEscape
23+ , getHaskellManifestProjectPath
24+ , AssetSource(..)
25+ , describeImpureAssetSource
26+ , watchStaticFilesDerivation
1927 ) where
2028
2129import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
22−import Control.Lens ((.~), (?~), (<&>))
30+import Control.Lens ((.~), (?~), (<&>), (^.), _2, _3)
2331import Control.Monad
32+import Control.Monad.Except
2433import Control.Monad.IO.Class (liftIO)
34+import Control.Monad.Log
2535import Control.Monad.State
2636import qualified Data.Aeson as Json
2737import qualified Data.ByteString.UTF8 as BSU
@@ -30,28 +40,36 @@ import qualified Data.ByteString.Lazy as BSL
3040import Data.Default (def)
3141import Data.Function ((&), on)
3242import Data.Map (Map)
43+import Data.Maybe (isJust)
3344import Data.Text (Text)
3445import qualified Data.Text as T
3546import Data.Text.Encoding (decodeUtf8, encodeUtf8)
3647import Data.Traversable (for)
48+import Reflex
49+import Reflex.FSNotify
50+import Reflex.Host.Headless
3751import System.Directory
3852import System.Environment (lookupEnv)
53+import System.Exit (ExitCode(..))
3954import System.FilePath
55+import System.FSNotify (defaultConfig, eventPath, WatchConfig(..))
56+import qualified System.Info as SysInfo
4057import System.IO.Temp
4158import System.IO.Unsafe (unsafePerformIO)
4259import System.PosixCompat.Files
4360import System.PosixCompat.Types
4461import System.PosixCompat.User
62+import qualified System.Process as Proc
4563import Text.ShellEscape (bash, bytes)
4664
4765import GitHub.Data.GitData (Branch)
4866import GitHub.Data.Name (Name)
4967
50−import Obelisk.App (MonadObelisk)
68+import Obelisk.App (MonadObelisk, runObelisk, getObelisk)
5169import Obelisk.CliApp
5270import Obelisk.Command.Nix
5371import Obelisk.Command.Thunk
54−import Obelisk.Command.Utils (nixBuildExePath, nixExePath, toNixPath, cp, nixShellPath)
72+import Obelisk.Command.Utils (nixBuildExePath, nixExePath, toNixPath, cp, nixShellPath, lnPath)
5573
5674--TODO: Make this module resilient to random exceptions
5775
@@ -300,7 +318,7 @@ bashEscape = BSU.toString . bytes . bash . BSU.fromString
300318nixShellRunProc :: NixShellConfig -> ProcessSpec
301319nixShellRunProc cfg = setDelegateCtlc True $ proc nixShellPath $ runNixShellConfig cfg
302320
303−nixShellWithoutPkgs
321+mkObNixShellProc
304322 :: MonadObelisk m
305323 => FilePath -- ^ Path to project root
306324 -> Bool -- ^ Should this be a pure shell?
@@ -308,12 +326,12 @@ nixShellWithoutPkgs
308326 -> Map Text FilePath -- ^ Package names mapped to their paths
309327 -> String -- ^ Shell attribute to use (e.g. @"ghc"@, @"ghcjs"@, etc.)
310328 -> Maybe String -- ^ If 'Just' run the given command; otherwise just open the interactive shell
311− -> m ()
312−nixShellWithoutPkgs root isPure chdirToRoot packageNamesAndPaths shellAttr command = do
329+ -> m ProcessSpec
330+mkObNixShellProc root isPure chdirToRoot packageNamesAndPaths shellAttr command = do
313331 packageNamesAndAbsPaths <- liftIO $ for packageNamesAndPaths makeAbsolute
314332 defShellConfig <- nixShellRunConfig root isPure command
315333 let setCwd_ = if chdirToRoot then setCwd (Just root) else id
316− runProcess_ $ setCwd_ $ nixShellRunProc $ defShellConfig
334+ pure $ setCwd_ $ nixShellRunProc $ defShellConfig
317335 & nixShellConfig_common . nixCmdConfig_target . target_expr ?~
318336 "{root, pkgs, shell}: ((import root {}).passthru.__unstable__.self.extend (_: _: {\
319337 \shellPackages = builtins.fromJSON pkgs;\
@@ -324,6 +342,18 @@ nixShellWithoutPkgs root isPure chdirToRoot packageNamesAndPaths shellAttr comma
324342 , strArg "shell" shellAttr
325343 ]
326344
345+nixShellWithoutPkgs
346+ :: MonadObelisk m
347+ => FilePath -- ^ Path to project root
348+ -> Bool -- ^ Should this be a pure shell?
349+ -> Bool -- ^ Should we chdir to the package root in the shell?
350+ -> Map Text FilePath -- ^ Package names mapped to their paths
351+ -> String -- ^ Shell attribute to use (e.g. @"ghc"@, @"ghcjs"@, etc.)
352+ -> Maybe String -- ^ If 'Just' run the given command; otherwise just open the interactive shell
353+ -> m ()
354+nixShellWithoutPkgs root isPure chdirToRoot packageNamesAndPaths shellAttr command = do
355+ runProcess_ =<< mkObNixShellProc root isPure chdirToRoot packageNamesAndPaths shellAttr command
356+
327357nixShellWithHoogle :: MonadObelisk m => FilePath -> Bool -> String -> Maybe String -> m ()
328358nixShellWithHoogle root isPure shell' command = do
329359 defShellConfig <- nixShellRunConfig root isPure command
@@ -334,7 +364,21 @@ nixShellWithHoogle root isPure shell' command = do
334364 \})).project.shells.${shell}"
335365 & nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
336366
337−findProjectAssets :: MonadObelisk m => FilePath -> m Text
367+-- | Describes the provenance of static assets (i.e., are they the result of a derivation
368+-- that was built, or just a folder full of files.
369+data AssetSource = AssetSource_Derivation
370+ | AssetSource_Files
371+ deriving (Eq)
372+
373+-- | Some log messages to make it easier to tell where static files are coming from
374+describeImpureAssetSource :: AssetSource -> Text -> Text
375+describeImpureAssetSource src path = case src of
376+ AssetSource_Files -> "Assets impurely loaded from: " <> path
377+ AssetSource_Derivation -> "Assets derivation built and impurely loaded from: " <> path
378+
379+-- | Determine where the static files of a project are and whether they're plain files or a derivation.
380+-- If they are a derivation, that derivation will be built.
381+findProjectAssets :: MonadObelisk m => FilePath -> m (AssetSource, Text)
338382findProjectAssets root = do
339383 isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
340384 proc nixExePath
@@ -346,10 +390,95 @@ findProjectAssets root = do
346390 ]
347391 -- Check whether the impure static files are a derivation (and so must be built)
348392 if isDerivation == "1"
349− then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
350− proc nixBuildExePath
351− [ "--no-out-link"
393+ then do
394+ _ <- buildStaticFilesDerivationAndSymlink
395+ (readProcessAndLogStderr Debug)
396+ root
397+ pure (AssetSource_Derivation, T.pack $ root </> "static.out")
398+ else fmap (AssetSource_Files,) $ do
399+ path <- readProcessAndLogStderr Debug $ setCwd (Just root) $
400+ proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
401+ _ <- readProcessAndLogStderr Debug $ setCwd (Just root) $
402+ proc lnPath ["-sfT", T.unpack path, "./static.out"]
403+ pure path
404+
405+-- | Get the nix store path to the generated static asset manifest module (e.g., "obelisk-generated-static")
406+getHaskellManifestProjectPath :: MonadObelisk m => FilePath -> m Text
407+getHaskellManifestProjectPath root = fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $
408+ proc nixBuildExePath
409+ [ "--no-out-link"
410+ , "-E"
411+ , "(let a = import ./. {}; in a.passthru.processedStatic.haskellManifest)"
412+ ]
413+
414+-- | Watch the project directory for file changes and check whether those file changes
415+-- cause changes in the static files nix derivation. If so, rebuild it.
416+watchStaticFilesDerivation
417+ :: (MonadIO m, MonadObelisk m)
418+ => FilePath
419+ -> m ()
420+watchStaticFilesDerivation root = do
421+ ob <- getObelisk
422+ drv0 <- showDerivation
423+ liftIO $ runHeadlessApp $ do
424+ pb <- getPostBuild
425+ checkForChanges <- batchOccurrences 0.25 =<< watchDirectoryTree
426+ -- On macOS, use the polling backend due to https://github.com/luite/hfsevents/issues/13
427+ (defaultConfig { confUsePolling = SysInfo.os == "darwin", confPollInterval = 250000 })
428+ (root <$ pb)
429+ ((/="static.out") . takeFileName . eventPath)
430+ drv <- performEvent $ ffor checkForChanges $ \_ ->
431+ liftIO $ runObelisk ob showDerivation
432+ drvs <- foldDyn (\new (_, old, _) -> (old, new, old /= new)) (drv0, drv0, False) drv
433+ void $ throttleBatchWithLag
434+ (\e -> performEvent $ ffor e $ \_ -> liftIO $ runObelisk ob $ do
435+ putLog Notice "Static assets being built..."
436+ buildStaticCatchErrors >>= \case
437+ Nothing -> pure ()
438+ Just _ -> putLog Notice "Static assets built and symlinked to static.out"
439+ )
440+ ((() <$) . ffilter (\x -> isJust (x ^._2) && x ^._3) $ updated drvs)
441+ pure never
442+ where
443+ handleBuildFailure
444+ :: MonadObelisk m
445+ => (ExitCode, String, String)
446+ -> m (Maybe Text)
447+ handleBuildFailure (ex, out, err) = case ex of
448+ ExitSuccess -> pure $ Just $ T.pack out
449+ _ -> do
450+ putLog Error $
451+ ("Static assets build failed: " <>) $
452+ T.unlines $ reverse $ take 10 $ reverse $ T.lines $ T.pack err
453+ pure Nothing
454+ showDerivation :: MonadObelisk m => m (Maybe Text)
455+ showDerivation =
456+ handleBuildFailure <=< readCreateProcessWithExitCode $
457+ setCwd (Just root) $ ProcessSpec
458+ { _processSpec_createProcess = Proc.proc nixExePath
459+ [ "show-derivation"
460+ , "-f", "."
461+ , "passthru.staticFilesImpure"
462+ ]
463+ , _processSpec_overrideEnv = Nothing
464+ }
465+ buildStaticCatchErrors :: MonadObelisk m => m (Maybe Text)
466+ buildStaticCatchErrors = handleBuildFailure =<<
467+ buildStaticFilesDerivationAndSymlink
468+ readCreateProcessWithExitCode
469+ root
470+
471+buildStaticFilesDerivationAndSymlink
472+ :: MonadObelisk m
473+ => (ProcessSpec -> m a)
474+ -> FilePath
475+ -> m a
476+buildStaticFilesDerivationAndSymlink f root = f $
477+ setCwd (Just root) $ ProcessSpec
478+ { _processSpec_createProcess = Proc.proc
479+ nixBuildExePath
480+ [ "-o", "static.out"
352481 , "-E", "(import ./. {}).passthru.staticFilesImpure"
353482 ]
354− else readProcessAndLogStderr Debug $ setCwd (Just root) $
355− proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
483+ , _processSpec_overrideEnv = Nothing
484+ }
lib/command/src/Obelisk/Command/Run.hs+70−11
@@ -14,7 +14,8 @@ module Obelisk.Command.Run where
1414import Control.Arrow ((&&&))
1515import Control.Exception (Exception, bracket)
1616import Control.Lens (ifor, (.~), (&))
17−import Control.Monad (filterM)
17+import Control.Concurrent (forkIO)
18+import Control.Monad (filterM, void)
1819import Control.Monad.Except (runExceptT, throwError)
1920import Control.Monad.IO.Class (liftIO)
2021import Control.Monad.Reader (MonadIO)
@@ -45,13 +46,23 @@ import Debug.Trace (trace)
4546import Distribution.Compiler (CompilerFlavor(..))
4647import Distribution.PackageDescription.Parsec (parseGenericPackageDescription)
4748import 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)
4854import qualified Distribution.System as Dist
49−import Distribution.Types.BuildInfo (buildable, cppOptions, defaultExtensions, defaultLanguage, hsSourceDirs, options)
55+import Distribution.Types.BuildInfo (buildable, cppOptions, defaultExtensions, defaultLanguage, hsSourceDirs, options, targetBuildDepends)
5056import Distribution.Types.CondTree (simplifyCondTree)
57+import Distribution.Types.Dependency (Dependency (..), depPkgName)
5158import Distribution.Types.GenericPackageDescription (ConfVar (Arch, Impl, OS), condLibrary)
59+import Distribution.Types.InstalledPackageInfo (compatPackageKey)
5260import Distribution.Types.Library (libBuildInfo)
61+import Distribution.Types.PackageName (mkPackageName)
62+import Distribution.Types.VersionRange (anyVersion)
5363import Distribution.Utils.Generic (toUTF8BS, readUTF8File)
5464import qualified Distribution.Parsec.Common as Dist
65+import qualified Distribution.Verbosity as Verbosity (silent)
5566import qualified Hpack.Config as Hpack
5667import qualified Hpack.Render as Hpack
5768import qualified Hpack.Yaml as Hpack
@@ -63,7 +74,7 @@ import System.FilePath
6374import qualified System.Info
6475import System.IO.Temp (withSystemTempDirectory)
6576
66−import Obelisk.App (MonadObelisk)
77+import Obelisk.App (MonadObelisk, getObelisk, runObelisk)
6778import Obelisk.CliApp (
6879 Severity (..),
6980 failWith,
@@ -77,7 +88,7 @@ import Obelisk.CliApp (
7788 withSpinner,
7889 )
7990import Obelisk.Command.Nix
80−import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape)
91+import Obelisk.Command.Project
8192import Obelisk.Command.Thunk (attrCacheFileName)
8293import Obelisk.Command.Utils (findExePath, ghcidExePath)
8394
@@ -96,6 +107,8 @@ data CabalPackageInfo = CabalPackageInfo
96107 -- ^ List of compiler-specific options (e.g., the "ghc-options" field of the cabal file)
97108 , _cabalPackageInfo_cppOptions :: [String]
98109 -- ^ List of CPP (C Preprocessor) options (e.g. the "cpp-options" field of the cabal file)
110+ , _cabalPackageInfo_buildDepends :: [Dependency]
111+ -- ^ List of build dependencies listed in the cabal file
99112 }
100113
101114-- | 'Bool' with a better name for its purpose.
@@ -144,8 +157,8 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
144157 , _target_attr = Just "__unstable__.profiledObRun"
145158 , _target_expr = Nothing
146159 }
147− assets <- findProjectAssets root
148− putLog Debug $ "Assets impurely loaded from: " <> assets
160+ (assetType, assets) <- findProjectAssets root
161+ putLog Debug $ describeImpureAssetSource assetType assets
149162 time <- liftIO getCurrentTime
150163 let profileBaseName = formatTime defaultTimeLocale profileBasePattern time
151164 liftIO $ createDirectoryIfMissing True $ takeDirectory $ root </> profileBaseName
@@ -163,9 +176,16 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
163176run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
164177run root interpretPaths = do
165178 pkgs <- getParsedLocalPkgs root interpretPaths
166− assets <- findProjectAssets root
167− putLog Debug $ "Assets impurely loaded from: " <> assets
168− ghciArgs <- getGhciSessionSettings pkgs root True
179+ (assetType, assets) <- findProjectAssets root
180+ manifestPkg <- parsePackagesOrFail . (:[]) . T.unpack =<< getHaskellManifestProjectPath root
181+ putLog Debug $ describeImpureAssetSource assetType assets
182+ case assetType of
183+ AssetSource_Derivation -> do
184+ ob <- getObelisk
185+ putLog Debug "Starting static file derivation watcher..."
186+ void $ liftIO $ forkIO $ runObelisk ob $ watchStaticFilesDerivation root
187+ _ -> pure ()
188+ ghciArgs <- getGhciSessionSettings (pkgs <> manifestPkg) root True
169189 freePort <- getFreePort
170190 withGhciScriptArgs pkgs $ \dotGhciArgs -> do
171191 runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
@@ -222,7 +242,12 @@ getLocalPkgs root interpretPaths = do
222242 let rootsAndExclusions = calcIntepretFinds "" interpretPaths
223243
224244 fmap fold $ for (MMap.toAscList rootsAndExclusions) $ \(interpretPathRoot, exclusions) ->
225− let allExclusions = obeliskPackageExclusions <> exclusions <> Set.singleton ("*" </> attrCacheFileName)
245+ let allExclusions = obeliskPackageExclusions
246+ <> exclusions
247+ <> Set.singleton ("*" </> attrCacheFileName)
248+ <> Set.singleton ("*" </> "lib/asset/manifest") -- NB: obelisk-asset-manifest is excluded because it generates
249+ -- a module that in turn imports it. This will cause ob run to
250+ -- fail in its current implementation.
226251 in fmap (Set.fromList . map normalise) $ runFind $
227252 ["-L", interpretPathRoot, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
228253 <> concat [["-not", "-path", p </> "*"] | p <- toList allExclusions]
@@ -354,6 +379,7 @@ parseCabalPackage' pkg = runExceptT $ do
354379 defaultLanguage $ libBuildInfo lib
355380 , _cabalPackageInfo_compilerOptions = options $ libBuildInfo lib
356381 , _cabalPackageInfo_cppOptions = cppOptions $ libBuildInfo lib
382+ , _cabalPackageInfo_buildDepends = targetBuildDepends $ libBuildInfo lib
357383 }
358384 Right Nothing -> pure Nothing
359385 Left (_, errors) ->
@@ -400,7 +426,8 @@ withGhciScriptArgs
400426 => f CabalPackageInfo -- ^ List of packages to load into ghci
401427 -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
402428 -> m ()
403−withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp -> f ["-XNoImplicitPrelude", "-ghci-script", fp]
429+withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp ->
430+ f ["-XNoImplicitPrelude", "-ghci-script", fp]
404431 where
405432 -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
406433 -- is passed to the ghci session.
@@ -450,6 +477,7 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
450477 -- all paths to 'pathBase'.
451478 selfExe <- liftIO $ canonicalizePath =<< getExecutablePath
452479 canonicalPathBase <- liftIO $ canonicalizePath pathBase
480+ installedPackageIndex <- loadPackageIndex packageInfos pathBase
453481
454482 (pkgFiles, pkgSrcPaths :: [NonEmpty FilePath]) <- fmap unzip $ liftIO $ for packageInfos $ \pkg -> do
455483 canonicalSrcDirs <- traverse canonicalizePath $ (_cabalPackageInfo_packageRoot pkg </>) <$> _cabalPackageInfo_sourceDirs pkg
@@ -458,16 +486,47 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
458486
459487 pure
460488 $ baseGhciOptions
489+ <> ["-DOBELISK_ASSET_PASSTHRU"] -- For passthrough static assets
461490 <> ["-F", "-pgmF", selfExe, "-optF", preprocessorIdentifier]
462491 <> concatMap (\p -> ["-optF", p]) pkgFiles
463492 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
493+ <> concatMap (\packageId -> ["-package-id", packageId ])
494+ (packageIds installedPackageIndex)
464495 where
465496 relativeTo' = if useRelativePaths then relativeTo else const
497+ -- Package names we're building and not needed from the package DB
498+ packageNames =
499+ map (mkPackageName . T.unpack . _cabalPackageInfo_packageName)
500+ packageInfos
501+ packageIds installedPackageIndex =
502+ map (dependencyPackageId installedPackageIndex) $
503+ filter ((`notElem` packageNames) . depPkgName) $
504+ concatMap _cabalPackageInfo_buildDepends packageInfos <>
505+ [Dependency (mkPackageName "obelisk-run") anyVersion]
506+ dependencyPackageId installedPackageIndex dep =
507+ case lookupDependency installedPackageIndex dep of
508+ ((_version,installedPackageInfo:_) :_) ->
509+ compatPackageKey installedPackageInfo
510+ _ -> error $ "Couldn't resolve dependency for " <> prettyShow dep
511+
512+-- Load the package index used by the GHC in this path's nix project
513+loadPackageIndex :: MonadObelisk m => [CabalPackageInfo] -> FilePath -> m InstalledPackageIndex
514+loadPackageIndex packageInfos root = do
515+ ghcPath <- getPathInNixEnvironment "bash -c 'type -p ghc'"
516+ ghcPkgPath <- getPathInNixEnvironment "bash -c 'type -p ghc-pkg'"
517+ (compiler, _platform, programDb) <- liftIO
518+ $ configCompilerEx (Just GHC) (Just ghcPath) (Just ghcPkgPath) defaultProgramDb Verbosity.silent
519+ liftIO $ getInstalledPackages Verbosity.silent compiler [GlobalPackageDB] programDb
520+ where
521+ getPathInNixEnvironment cmd = do
522+ path <- readProcessAndLogStderr Debug =<< mkObNixShellProc root False True (packageInfoToNamePathMap packageInfos) "ghc" (Just cmd)
523+ liftIO $ canonicalizePath $ T.unpack $ T.strip path
466524
467525baseGhciOptions :: [String]
468526baseGhciOptions =
469527 [ "-ignore-dot-ghci"
470528 , "-no-user-package-db"
529+ , "-hide-all-packages"
471530 , "-package-env", "-"
472531 ]
473532
lib/command/src/Obelisk/Command/Utils.hs+3
@@ -75,6 +75,9 @@ gitPath = $(staticWhich "git")
7575whichPath :: FilePath
7676whichPath = $(staticWhich "which")
7777
78+lnPath :: FilePath
79+lnPath = $(staticWhich "ln")
80+
7881sshKeygenPath :: FilePath
7982sshKeygenPath = $(staticWhich "ssh-keygen")
8083
skeleton/.gitignore+1
@@ -13,3 +13,4 @@ result
1313result-*
1414tags
1515TAGS
16+static.out
skeleton/default.nix+1−1
@@ -1,7 +1,7 @@
11{ system ? builtins.currentSystem
22, obelisk ? import ./.obelisk/impl {
33 inherit system;
4− iosSdkVersion = "10.2";
4+ iosSdkVersion = "13.2";
55
66 # You must accept the Android Software Development Kit License Agreement at
77 # https://developer.android.com/studio/terms in order to build Android apps.
skeleton/frontend/src/Frontend.hs+3−4
@@ -1,6 +1,5 @@
1−{-# LANGUAGE DataKinds #-}
21{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE TypeApplications #-}
2+{-# LANGUAGE TemplateHaskell #-}
43
54module Frontend where
65
@@ -27,7 +26,7 @@ frontend :: Frontend (R FrontendRoute)
2726frontend = Frontend
2827 { _frontend_head = do
2928 el "title" $ text "Obelisk Minimal Example"
30− elAttr "link" ("href" =: static @"main.css" <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
29+ elAttr "link" ("href" =: $(static "main.css") <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
3130 , _frontend_body = do
3231 el "h1" $ text "Welcome to Obelisk!"
3332 el "p" $ text $ T.pack commonStuff
@@ -38,7 +37,7 @@ frontend = Frontend
3837 -- print "Hello, World!" on the client.
3938 prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
4039
41− elAttr "img" ("src" =: static @"obelisk.jpg") blank
40+ elAttr "img" ("src" =: $(static "obelisk.jpg")) blank
4241 el "div" $ do
4342 exampleConfig <- getConfig "common/example"
4443 case exampleConfig of