on master help sign in
Merge branch 'develop' into ae@static-which-prefetch
commit666a73b5c9871627072ccf477ea4307adfa1767a authorAlexandre Esteves <alexandre.fmp.esteves@gmail.com> dateFri 22 May 2020, 07:07 (6 years ago) parent35662c63894795
22 files changed+156−99
ChangeLog.md+7−1 default.nix+15−12 dep/reflex-platform/default.nix+1−7 dep/reflex-platform/github.json+3−3 dep/reflex-platform/thunk.nix+9 guides/app-deploy/README.md+14−8 lib/asset/manifest/obelisk-asset-manifest.cabal+1−1 lib/asset/manifest/src/Obelisk/Asset/Symlink.hs+1−1 lib/asset/serve-snap/obelisk-asset-serve-snap.cabal+1−1 lib/asset/serve-snap/src/Obelisk/Asset/Serve/Snap.hs+1−1 lib/cliapp/obelisk-cliapp.cabal+2 lib/cliapp/src/Obelisk/CliApp/Process.hs+4−2 lib/command/obelisk-command.cabal+3−1 lib/command/src/Obelisk/Command.hs+10−6 lib/command/src/Obelisk/Command/Deploy.hs+1−1 lib/command/src/Obelisk/Command/Project.hs+14−7 lib/command/src/Obelisk/Command/Run.hs+57−42 lib/command/src/Obelisk/Command/Thunk.hs+1−1 lib/selftest/src/Obelisk/SelfTest.hs+6−1 lib/snap-extras/obelisk-snap-extras.cabal−1 skeleton/common/common.cabal+1 skeleton/frontend/frontend.cabal+4−2
ChangeLog.md+7−1
@@ -5,10 +5,16 @@ This project's release branch is `master`. This log is written from the perspect
55## Unreleased
66
77* ([#715](https://github.com/obsidiansystems/obelisk/pull/715)) In `Obelisk.Route` deprecate `isoEncoder` and `prismEncoder` in favor of more precisely named `viewEncoder` and `reviewEncoder` (respectively) and improve documentation regarding contravariance of `reviewEncoder`.
8+* ([#739](https://github.com/obsidiansystems/obelisk/pull/739)) Improve `ob shell` by allowing commands to be passed verbatim after a `--` argument. For example, `ob shell 'run command'` can now be written `ob shell -- run command`.
9+* ([#735](https://github.com/obsidiansystems/obelisk/pull/735)) Fix regression causing custom `Prelude`s to break `ob run`/`ob watch`/`ob repl`.
10+* ([#737](https://github.com/obsidiansystems/obelisk/pull/737)) Fix bug causing custom `Prelude`s to break `ob profile`.
11+* ([#742](https://github.com/obsidiansystems/obelisk/pull/742), [#57](https://github.com/obsidiansystems/obelisk/pull/57), [#406](https://github.com/obsidiansystems/obelisk/pull/406)) Enable `-dedupe` and `-DGHCJS_BROWSER` in GHCJS builds to make JavaScript output considerably smaller leading to faster load/parse times and faster build times.
12+* ([#742](https://github.com/obsidiansystems/obelisk/pull/742)) Update `reflex-platform` which includes:
13+ * A new version of GHCJS where `-dedupe` is fixed.
814
915## v0.8.0.0
1016
11−* ([#674](https://github.com/obsidiansystems/obelisk/pull/674), [#711](https://github.com/obsidiansystems/obelisk/pull/711)) Introduce a new thunk format to support accessing the thunk's source directly when packed. When packed, thunks have an additional file called `thunk.nix` and `default.nix` is now a think wrapper around that.
17+* ([#674](https://github.com/obsidiansystems/obelisk/pull/674), [#711](https://github.com/obsidiansystems/obelisk/pull/711)) Introduce a new thunk format to support accessing the thunk's source directly when packed. When packed, thunks have an additional file called `thunk.nix` and `default.nix` is now a thin wrapper around that.
1218* ([#665](https://github.com/obsidiansystems/obelisk/pull/665)) Add `--interpret` and `--no-interpret` options to `ob run`/`ob watch`/`ob repl`/`ob shell`. These options allow you to pick which paths will be pre-compiled by `nix` when entering the shell/session and which won't. For example `ob run --no-interpret dep` will ensure that any dependencies found in `./dep` will be built by `nix` before loading the rest of the project into the `ghci` session. The same configuration for `ob shell` will ensure that those packages are built and available in the `ghc` package database inside the shell.
1319
1420 **NOTE:** `ob shell`'s default behavior is now different. By default it now behaves like `ob run`/`ob watch`/`ob repl` in that it does *not* pre-build any packages whose `.cabal` or `package.yaml` files are found in the project. To regain the previous behavior, use `ob shell --no-interpret . --interpret backend --interpret common --interpret frontend` from the project root.
default.nix+15−12
@@ -321,29 +321,32 @@ in rec {
321321 __unstable__.profiledObRun = let
322322 profiled = projectOut { inherit system; enableLibraryProfiling = true; };
323323 exeSource = builtins.toFile "ob-run.hs" ''
324+ {-# LANGUAGE NoImplicitPrelude #-}
325+ {-# LANGUAGE PackageImports #-}
324326 module Main where
325327
326− import Control.Exception
327− import Reflex.Profiled
328− import System.Environment
328+ -- Explicitly import Prelude from base lest there be multiple modules called Prelude
329+ import "base" Prelude (IO, (++), read)
329330
330− import qualified Obelisk.Run
331+ import "base" Control.Exception (finally)
332+ import "reflex" Reflex.Profiled (writeProfilingData)
333+ import "base" System.Environment (getArgs)
334+
335+ import qualified "obelisk-run" Obelisk.Run
331336 import qualified Frontend
332337 import qualified Backend
333338
334339 main :: IO ()
335340 main = do
336− args <- getArgs
337− let port = read $ args !! 0
338− assets = args !! 1
339− profileFile = (args !! 2) <> ".rprof"
340− Obelisk.Run.run port (Obelisk.Run.runServeAsset assets) Backend.backend Frontend.frontend `finally` writeProfilingData profileFile
341+ [portStr, assets, profFileName] <- getArgs
342+ Obelisk.Run.run (read portStr) (Obelisk.Run.runServeAsset assets) Backend.backend Frontend.frontend
343+ `finally` writeProfilingData (profFileName ++ ".rprof")
341344 '';
342345 in nixpkgs.runCommand "ob-run" {
343− buildInputs = [ (profiled.ghc.ghcWithPackages (p: [ p.backend p.frontend])) ];
346+ buildInputs = [ (profiled.ghc.ghcWithPackages (p: [p.backend p.frontend])) ];
344347 } ''
345− cp ${exeSource} $PWD/ob-run.hs
346− mkdir -p $out/bin/
348+ cp ${exeSource} ob-run.hs
349+ mkdir -p $out/bin
347350 ghc -x hs -prof -fno-prof-auto -threaded ob-run.hs -o $out/bin/ob-run
348351 '';
349352
dep/reflex-platform/default.nix+1−7
@@ -1,8 +1,2 @@
11# DO NOT HAND-EDIT THIS FILE
2−let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
3− if !fetchSubmodules && !private then builtins.fetchTarball {
4− url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
5− } else (import <nixpkgs> {}).fetchFromGitHub {
6− inherit owner repo rev sha256 fetchSubmodules private;
7− };
8−in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))
2+import (import ./thunk.nix)
dep/reflex-platform/github.json+3−3
@@ -1,8 +1,8 @@
11{
22 "owner": "reflex-frp",
33 "repo": "reflex-platform",
4− "branch": "release/0.5.3.0",
4+ "branch": "develop",
55 "private": false,
6− "rev": "9779ea3b4c87fdc971057c561262696d5e9b1847",
7− "sha256": "0v87ilal9355xwz8y9m0zh14pm9c0f7pqch0854kkj92ybc5l62q"
6+ "rev": "c21ec7036c8351185cd2feef9ce577b6a61c4f22",
7+ "sha256": "0m4sdpz6qvngralpyprrbvsyi2zmfh2l3q5vhihwfp3842blvbfg"
88}
dep/reflex-platform/thunk.nixnew file+9
@@ -0,0 +1,9 @@
1+# DO NOT HAND-EDIT THIS FILE
2+let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
3+ if !fetchSubmodules && !private then builtins.fetchTarball {
4+ url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
5+ } else (import <nixpkgs> {}).fetchFromGitHub {
6+ inherit owner repo rev sha256 fetchSubmodules private;
7+ };
8+ json = builtins.fromJSON (builtins.readFile ./github.json);
9+in fetch json
guides/app-deploy/README.md+14−8
@@ -1,11 +1,12 @@
11# Obelisk Application Deployment Guide
2+
23This document serves two purposes, the first one is to guide new users that want to deploy Obelisk/Reflex apps and the second one to provide a walk-through for test driving Obelisk releases.
34
4−> **Note:** To complete this *entire* guide you need access to a macOS machine running the latest OSX release, a Linux machine (can be a VM on that mac) with Nix installed, an iPhone or iPad, and a recent Android device. It also assumes that you have Nix correctly set up and that your system is set up to fetch from binary caches instead of building everything on your machine. If you notice any of the commands below takes more than 5 minutes, then caches are not likely enabled.
5+> **Note:** This guide can be completed on a Linux host with Nix installed or a macOS host with Nix installed. Each section is labeled regarding which operating system hosts it supports. When going through the guide, start at the beginning and go through all sections that are supported on your host and merely skip sections that are not supported. The sections for iOS deployments require an iPhone or iPad device and the sections for Android deployments require an Android device. This guide assumes that you have Nix correctly set up and that your system is configured to fetch from binary caches instead of building everything on your machine. If you notice any of the commands below takes more than 5 minutes, then caches are not likely enabled.
56>
67> Refer to the [Obelisk Installation Documentation](https://github.com/obsidiansystems/obelisk#installing-obelisk) for instructions on configuring the binary caches for any Nix-compatible setup.
78
8−## a) Get Obelisk
9+## a) Get Obelisk (Linux, Mac)
910
1011There are several ways to install Obelisk, but the one we are going to use here allows us to test a single version of Obelisk in different ways. The approach is to clone Obelisk from it's GitHub repository and start a project that uses that local check out.
1112
@@ -39,7 +40,7 @@ ob init --branch "$OBELISK_BRANCH"
3940
4041> **Note:** If the `--branch` parameter is not used, then Obelisk sets your project up to look at the `master` branch from GitHub. This is quite handy for real life projects but distracting for our test drive since we want to make sure any changes we do to the Obelisk codebase (like testing a pull request) are immediately picked up.
4142
42−## b) Run the Obelisk app on localhost
43+## b) Run the Obelisk app on localhost (Linux, Mac)
4344
4445Let's test that we can run a server on localhost. This is quite easy:
4546
@@ -52,9 +53,10 @@ Now open a browser and point it to http://localhost:8000 (or just click on this
5253
5354![](assets/app-deploy.png)
5455
55−## c) Deploy the web app on a remote machine
56+## c) Prepare a VirtualBox deploy target (Linux, Mac)
5657
5758### Import the NixOS VirtualBox appliance
59+
5860Install [VirtualBox](https://www.virtualbox.org/) on your machine.
5961
6062On NixOS, do this by adding the following line to your `/etc/nixos/configuration.nix` before the last line containing `}`:
@@ -201,7 +203,7 @@ git remote add origin "$WORKDIR/myapp-git-remote"
201203git push -u origin master
202204```
203205
204−### Deploy
206+### Initialize deployment directory
205207
206208With that, we can come back to the Obelisk app and initialize the deployment:
207209
@@ -216,7 +218,11 @@ Then configure the deployment for VirtualBox:
216218echo '{nixosPkgs, ...}: {...}: { imports = [ (nixosPkgs.path + /nixos/modules/virtualisation/virtualbox-image.nix) ]; }' > "$WORKDIR/myappdeploy/module.nix"
217219```
218220
219−And deploy:
221+## d) Deploy to VirtualBox target (Linux only)
222+
223+> Deploying requires a Linux host in order to build the Linux binaries.
224+
225+Now we can deploy to the VirtualBox VM.
220226
221227```bash
222228cd "$WORKDIR/myappdeploy"
@@ -243,7 +249,7 @@ It should look like this:
243249
244250Congratulations! You have deployed an Obelisk application to a remote server via SSH.
245251
246−## d) Deploy an Android app (Linux host only)
252+## e) Deploy an Android app (Linux only)
247253
248254> **Note:** This section requires that you be running a Linux *host*.
249255
@@ -286,7 +292,7 @@ When the deployment is complete opening the app should look something like
286292
287293Congratulations! You have deployed an Obelisk Android app via USB.
288294
289−## e) Deploy an iOS app (macOS host only)
295+## f) Deploy an iOS app (Mac only)
290296
291297> **Note:** This section requires that you be running a macOS *host*.
292298
lib/asset/manifest/obelisk-asset-manifest.cabal+1−1
@@ -24,7 +24,7 @@ library
2424 , template-haskell
2525 , text
2626 , transformers
27− , unix
27+ , unix-compat
2828 , vector
2929 exposed-modules:
3030 Obelisk.Asset.Gather
lib/asset/manifest/src/Obelisk/Asset/Symlink.hs+1−1
@@ -7,7 +7,7 @@ import Data.Map (Map)
77import qualified Data.Map as Map
88import System.Directory
99import System.FilePath.Posix
10−import System.Posix.Files
10+import System.PosixCompat.Files
1111
1212-- | Copy the given hashed files from one location to hashed paths in another
1313-- location, and symlink the unhashed paths to the hashed ones
lib/asset/serve-snap/obelisk-asset-serve-snap.cabal+1−1
@@ -23,7 +23,7 @@ library
2323 , obelisk-snap-extras
2424 , text
2525 , transformers
26− , unix
26+ , unix-compat
2727 exposed-modules:
2828 Obelisk.Asset.Accept
2929 Obelisk.Asset.Serve.Snap
lib/asset/serve-snap/src/Obelisk/Asset/Serve/Snap.hs+1−1
@@ -31,7 +31,7 @@ import Data.Text.Encoding (decodeUtf8, encodeUtf8)
3131import System.Directory (getDirectoryContents)
3232import System.FilePath ((</>), splitFileName, takeDirectory)
3333import System.IO.Error (isDoesNotExistError)
34−import System.Posix (getFileStatus, fileSize)
34+import System.PosixCompat.Files (getFileStatus, fileSize)
3535
3636-- | Serve static assets from an asset directory generated via @assets.nix@ or, failing that, from a regular directory.
3737--
lib/cliapp/obelisk-cliapp.cabal+2
@@ -19,10 +19,12 @@ library
1919 , monad-loops
2020 , mtl
2121 , process
22+ , shell-escape
2223 , terminal-size
2324 , text
2425 , time
2526 , transformers
27+ , utf8-string
2628 exposed-modules:
2729 Obelisk.CliApp
2830 other-modules:
lib/cliapp/src/Obelisk/CliApp/Process.hs+4−2
@@ -38,6 +38,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
3838import Control.Lens (Prism', review)
3939import qualified Data.ByteString as BS
4040import qualified Data.ByteString.Char8 as BSC
41+import qualified Data.ByteString.UTF8 as BSU
4142import Data.Function (fix)
4243import Data.Map (Map)
4344import qualified Data.Map as Map
@@ -54,6 +55,7 @@ import qualified System.IO.Streams as Streams
5455import System.IO.Streams.Concurrent (concurrentMerge)
5556import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out)
5657import qualified System.Process as Process
58+import Text.ShellEscape (bash, bytes)
5759import qualified Data.Aeson as Aeson
5860
5961import Control.Monad.Log (Severity (..))
@@ -246,8 +248,8 @@ reconstructCommand p = case p of
246248 Process.ShellCommand str -> T.pack str
247249 Process.RawCommand c as -> processToShellString c as
248250 where
249− processToShellString cmd args = T.unwords $ map quoteAndEscape (cmd : args)
250− quoteAndEscape x = "'" <> T.replace "'" "'\''" (T.pack x) <> "'"
251+ processToShellString cmd args = T.pack $ unwords $
252+ map (BSU.toString . bytes . bash . BSU.fromString) (cmd : args)
251253
252254reconstructProcSpec :: ProcessSpec -> Text
253255reconstructProcSpec = reconstructCommand . Process.cmdspec . _processSpec_createProcess
lib/command/obelisk-command.cabal+3−1
@@ -39,13 +39,15 @@ library
3939 , optparse-applicative
4040 , placeholders
4141 , process
42+ , shell-escape
4243 , temporary
4344 , terminal-size
4445 , text
4546 , time
4647 , transformers
47− , unix
48+ , unix-compat
4849 , unordered-containers
50+ , utf8-string
4951 , which
5052 , yaml
5153 exposed-modules:
lib/command/src/Obelisk/Command.hs+10−6
@@ -22,7 +22,7 @@ import System.Environment
2222import System.FilePath
2323import qualified System.Info
2424import System.IO (hIsTerminalDevice, stdout)
25−import System.Posix.Process (executeFile)
25+import System.Process (rawSystem)
2626
2727import Obelisk.App
2828import Obelisk.CliApp
@@ -114,7 +114,7 @@ obCommand cfg = hsubparser
114114 , command "thunk" $ info (ObCommand_Thunk <$> thunkOption) $ progDesc "Manipulate thunk directories"
115115 , command "repl" $ info (ObCommand_Repl <$> interpretOpts) $ progDesc "Open an interactive interpreter"
116116 , command "watch" $ info (ObCommand_Watch <$> interpretOpts) $ progDesc "Watch current project for errors and warnings"
117− , command "shell" $ info (ObCommand_Shell <$> shellOpts) $ progDesc "Enter a shell with project dependencies"
117+ , command "shell" $ info (ObCommand_Shell <$> shellOpts) $ progDesc "Enter a shell with project dependencies or run a command in such a shell. E.g. ob shell -- ghc-pkg list"
118118 , command "doc" $ info (ObCommand_Doc <$> shellFlags <*> packageNames) $
119119 progDesc "List paths to haddock documentation for specified packages"
120120 <> footerDoc (Just $
@@ -285,7 +285,10 @@ shellOpts :: Parser ShellOpts
285285shellOpts = ShellOpts
286286 <$> shellFlags
287287 <*> interpretOpts
288− <*> optional (strArgument (metavar "COMMAND"))
288+ -- This funny construction is used to support optparse-applicative's @--@ parsing.
289+ -- All arguments after @--@ are left unparsed and instead provided to the last positional parser
290+ -- which must therefore be 'many' in order to consume the rest of the input.
291+ <*> ((\xs -> if null xs then Nothing else Just $ unwords xs) <$> many (strArgument (metavar "COMMAND")))
289292
290293portOpt :: Int -> Parser Int
291294portOpt dfault = option auto (long "port" <> short 'p' <> help "Port number for server" <> showDefault <> value dfault <> metavar "INT")
@@ -363,7 +366,8 @@ main' argsCfg = do
363366 Just impl -> do
364367 -- Invoke the real implementation, using --no-handoff to prevent infinite recursion
365368 putLog Debug $ "Handing off to " <> T.pack impl
366− liftIO $ executeFile impl False ("--no-handoff" : myArgs) Nothing
369+ _ <- liftIO $ rawSystem impl ("--no-handoff" : myArgs)
370+ return ()
367371 case myArgs of
368372 "--no-handoff" : as -> go as -- If we've been told not to hand off, don't hand off
369373 origPath:inPath:outPath:preprocessorName:packagePaths
@@ -398,11 +402,11 @@ ob = \case
398402 ObCommand_Repl interpretPathsList -> withInterpretPaths interpretPathsList runRepl
399403 ObCommand_Watch interpretPathsList -> withInterpretPaths interpretPathsList runWatch
400404 ObCommand_Shell (ShellOpts shellAttr interpretPathsList cmd) -> withInterpretPaths interpretPathsList $ \root interpretPaths ->
401− nixShellForInterpretPaths False shellAttr root interpretPaths cmd
405+ nixShellForInterpretPaths False shellAttr root interpretPaths cmd -- N.B. We do NOT bash escape here; we want to run the command as-is
402406 ObCommand_Doc shellAttr pkgs -> withInterpretPaths [] $ \root interpretPaths ->
403407 nixShellForInterpretPaths True shellAttr root interpretPaths $ Just $ haddockCommand pkgs
404408 ObCommand_Hoogle shell' port -> withProjectRoot "." $ \root -> do
405− nixShellWithHoogle root True shell' $ Just $ "hoogle server -p " <> show port <> " --local"
409+ nixShellWithHoogle root True shell' $ Just $ "hoogle server -p" <> show port <> " --local"
406410 ObCommand_Internal icmd -> case icmd of
407411 ObInternal_ApplyPackages origPath inPath outPath packagePaths -> do
408412 liftIO $ Preprocessor.applyPackages origPath inPath outPath packagePaths
lib/command/src/Obelisk/Command/Deploy.hs+1−1
@@ -25,7 +25,7 @@ import GHC.Generics
2525import System.Directory
2626import System.FilePath
2727import System.IO
28−import System.Posix.Files
28+import System.PosixCompat.Files
2929import Text.URI (URI)
3030import qualified Text.URI as URI
3131import Text.URI.Lens
lib/command/src/Obelisk/Command/Project.hs+14−7
@@ -15,6 +15,7 @@ module Obelisk.Command.Project
1515 , toImplDir
1616 , toObeliskDir
1717 , withProjectRoot
18+ , bashEscape
1819 ) where
1920
2021import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
@@ -23,6 +24,7 @@ import Control.Monad
2324import Control.Monad.IO.Class (liftIO)
2425import Control.Monad.State
2526import qualified Data.Aeson as Json
27+import qualified Data.ByteString.UTF8 as BSU
2628import Data.Bits
2729import qualified Data.ByteString.Lazy as BSL
2830import Data.Default (def)
@@ -30,15 +32,17 @@ import Data.Function ((&), on)
3032import Data.Map (Map)
3133import Data.Text (Text)
3234import qualified Data.Text as T
33−import Data.Text.Encoding (decodeUtf8)
35+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
3436import Data.Traversable (for)
3537import System.Directory
3638import System.Environment (lookupEnv)
3739import System.FilePath
3840import System.IO.Temp
3941import System.IO.Unsafe (unsafePerformIO)
40−import System.Posix (FileStatus, FileMode, CMode (..), UserID, deviceID, fileID, fileMode, fileOwner, getFileStatus, getRealUserID)
41−import System.Posix.Files
42+import System.PosixCompat.Files
43+import System.PosixCompat.Types
44+import System.PosixCompat.User
45+import Text.ShellEscape (bash, bytes)
4246
4347import GitHub.Data.GitData (Branch)
4448import GitHub.Data.Name (Name)
@@ -278,12 +282,15 @@ nixShellRunConfig root isPure command = do
278282 pure $ def
279283 & nixShellConfig_pure .~ isPure
280284 & nixShellConfig_common . nixCmdConfig_target .~ (def & target_path .~ Nothing)
281− & nixShellConfig_run .~ (command <&> \c -> mconcat
282− [ "export NIX_PATH=nixpkgs=", T.unpack nixpkgsPath, "; "
283− , maybe "" (\v -> "export NIX_REMOTE=" <> v <> "; ") nixRemote
284− , c
285+ & nixShellConfig_run .~ (command <&> \cs -> unwords $ concat
286+ [ ["export", BSU.toString . bytes . bash $ "NIX_PATH=nixpkgs=" <> encodeUtf8 nixpkgsPath, ";"]
287+ , maybe [] (\v -> ["export", BSU.toString . bytes . bash $ "NIX_REMOTE=" <> encodeUtf8 (T.pack v), ";"]) nixRemote
288+ , [cs]
285289 ])
286290
291+bashEscape :: String -> String
292+bashEscape = BSU.toString . bytes . bash . BSU.fromString
293+
287294nixShellRunProc :: NixShellConfig -> ProcessSpec
288295nixShellRunProc cfg = setDelegateCtlc True $ proc "nix-shell" $ runNixShellConfig cfg
289296
lib/command/src/Obelisk/Command/Run.hs+57−42
@@ -25,12 +25,14 @@ import Data.Default (def)
2525import Data.Either (partitionEithers)
2626import Data.Foldable (fold, for_, toList)
2727import Data.Functor.Identity (runIdentity)
28+import Data.List (intercalate)
2829import Data.List.NonEmpty (NonEmpty)
2930import qualified Data.List.NonEmpty as NE
3031import Data.Map (Map)
3132import qualified Data.Map as Map
3233import qualified Data.Map.Monoidal as MMap
33−import Data.Maybe
34+import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
35+import Data.Ord (comparing)
3436import Data.Set (Set)
3537import qualified Data.Set as Set
3638import Data.String.Here.Interpolated (i)
@@ -44,17 +46,17 @@ import Distribution.Compiler (CompilerFlavor(..))
4446import Distribution.PackageDescription.Parsec (parseGenericPackageDescription)
4547import Distribution.Parsec.ParseResult (runParseResult)
4648import qualified Distribution.System as Dist
47−import Distribution.Types.BuildInfo
48−import Distribution.Types.CondTree
49−import Distribution.Types.GenericPackageDescription
50−import Distribution.Types.Library
51−import Distribution.Utils.Generic
49+import Distribution.Types.BuildInfo (buildable, defaultExtensions, defaultLanguage, hsSourceDirs, options)
50+import Distribution.Types.CondTree (simplifyCondTree)
51+import Distribution.Types.GenericPackageDescription (ConfVar (Arch, Impl, OS), condLibrary)
52+import Distribution.Types.Library (libBuildInfo)
53+import Distribution.Utils.Generic (toUTF8BS, readUTF8File)
5254import qualified Distribution.Parsec.Common as Dist
5355import qualified Hpack.Config as Hpack
5456import qualified Hpack.Render as Hpack
5557import qualified Hpack.Yaml as Hpack
56−import Language.Haskell.Extension
57−import Network.Socket hiding (Debug)
58+import Language.Haskell.Extension (Extension, Language)
59+import qualified Network.Socket as Socket
5860import System.Directory
5961import System.Environment (getExecutablePath)
6062import System.FilePath
@@ -76,7 +78,7 @@ import Obelisk.CliApp (
7678 withSpinner,
7779 )
7880import Obelisk.Command.Nix
79−import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets)
81+import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape)
8082import Obelisk.Command.Thunk (attrCacheFileName)
8183import Obelisk.Command.Utils (findExePath, ghcidExePath)
8284
@@ -161,12 +163,12 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
161163run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
162164run root interpretPaths = do
163165 pkgs <- getParsedLocalPkgs root interpretPaths
164− withGhciScript pkgs $ \dotGhciPath -> do
165− assets <- findProjectAssets root
166− putLog Debug $ "Assets impurely loaded from: " <> assets
167− ghciArgs <- getGhciSessionSettings pkgs root True
168− freePort <- getFreePort
169− runGhcid root True (ghciArgs <> mkGhciScriptArg dotGhciPath) pkgs $ Just $ unwords
166+ assets <- findProjectAssets root
167+ putLog Debug $ "Assets impurely loaded from: " <> assets
168+ ghciArgs <- getGhciSessionSettings pkgs root True
169+ freePort <- getFreePort
170+ withGhciScriptArgs pkgs $ \dotGhciArgs -> do
171+ runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
170172 [ "Obelisk.Run.run"
171173 , show freePort
172174 , "(Obelisk.Run.runServeAsset " ++ show assets ++ ")"
@@ -178,15 +180,15 @@ runRepl :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
178180runRepl root interpretPaths = do
179181 pkgs <- getParsedLocalPkgs root interpretPaths
180182 ghciArgs <- getGhciSessionSettings pkgs "." True
181− withGhciScript pkgs $ \dotGhciPath ->
182− runGhciRepl root pkgs (ghciArgs <> mkGhciScriptArg dotGhciPath)
183+ withGhciScriptArgs pkgs $ \dotGhciArgs ->
184+ runGhciRepl root pkgs (ghciArgs <> dotGhciArgs)
183185
184186runWatch :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
185187runWatch root interpretPaths = do
186188 pkgs <- getParsedLocalPkgs root interpretPaths
187189 ghciArgs <- getGhciSessionSettings pkgs root True
188− withGhciScript pkgs $ \dotGhciPath ->
189− runGhcid root True (ghciArgs <> mkGhciScriptArg dotGhciPath) pkgs Nothing
190+ withGhciScriptArgs pkgs $ \dotGhciArgs ->
191+ runGhcid root True (ghciArgs <> dotGhciArgs) pkgs Nothing
190192
191193exportGhciConfig :: MonadObelisk m => Bool -> FilePath -> PathTree Interpret -> m [String]
192194exportGhciConfig useRelativePaths root interpretPaths = do
@@ -393,13 +395,29 @@ parsePackagesOrFail dirs' = do
393395packageInfoToNamePathMap :: Foldable f => f CabalPackageInfo -> Map Text FilePath
394396packageInfoToNamePathMap = Map.fromList . map (_cabalPackageInfo_packageName &&& _cabalPackageInfo_packageRoot) . toList
395397
398+-- Like 'withGhciScript' but provides the precise ghci arguments to add to a ghci session
399+withGhciScriptArgs
400+ :: (MonadObelisk m, Foldable f)
401+ => f CabalPackageInfo -- ^ List of packages to load into ghci
402+ -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
403+ -> m ()
404+withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp -> f ["-XNoImplicitPrelude", "-ghci-script", fp]
405+ where
406+ -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
407+ -- is passed to the ghci session.
408+ loadPreludeManually =
409+ [ ":add Prelude" -- @:add@ is used because it's less noisy when there is no custom Prelude
410+ , ":set -XImplicitPrelude" -- Turn the default setting on
411+ ]
412+
396413-- | Create ghci configuration to load the given packages
397414withGhciScript
398415 :: (MonadObelisk m, Foldable f)
399− => f CabalPackageInfo -- ^ List of packages to load into ghci
416+ => [String] -- ^ Commands to prefix to file
417+ -> f CabalPackageInfo -- ^ List of packages to load into ghci
400418 -> (FilePath -> m ()) -- ^ Action to run with the path to generated temporary .ghci
401419 -> m ()
402−withGhciScript (toList -> packageInfos) f =
420+withGhciScript preCommands (toList -> packageInfos) f =
403421 withSystemTempDirectory "ob-ghci" $ \fp -> do
404422 let dotGhciPath = fp </> ".ghci"
405423 liftIO $ writeFile dotGhciPath dotGhci
@@ -411,7 +429,8 @@ withGhciScript (toList -> packageInfos) f =
411429 , [ "Backend" | "backend" `Set.member` packageNames ]
412430 , [ "Frontend" | "frontend" `Set.member` packageNames ]
413431 ]
414− dotGhci = unlines
432+ dotGhci = unlines $
433+ preCommands <>
415434 [ if null modulesToLoad then "" else ":load " <> unwords modulesToLoad
416435 , "import qualified Obelisk.Run"
417436 , "import qualified Frontend"
@@ -464,7 +483,7 @@ runGhciRepl root (toList -> packages) ghciArgs =
464483 -- NOTE: We do *not* want to use $(staticWhich "ghci") here because we need the
465484 -- ghc that is provided by the shell in the user's project.
466485 nixShellWithoutPkgs root True False (packageInfoToNamePathMap packages) "ghc" $
467− Just $ unwords $ "ghci" : ghciArgs -- TODO: Shell escape
486+ Just $ unwords $ fmap bashEscape $ "ghci" : ghciArgs
468487
469488-- | Run ghcid
470489runGhcid
@@ -477,32 +496,28 @@ runGhcid
477496 -> m ()
478497runGhcid root chdirToRoot ghciArgs (toList -> packages) mcmd =
479498 nixShellWithoutPkgs root True chdirToRoot (packageInfoToNamePathMap packages) "ghc" $
480− Just $ unwords $ ghcidExePath : opts -- TODO: Shell escape
499+ Just $ unwords $ fmap bashEscape $ ghcidExePath : opts
481500 where
482− opts =
483− [ "-W"
484− , "--outputfile=ghcid-output.txt"
485− ] <> map (\x -> "--reload='" <> x <> "'") reloadFiles
486− <> map (\x -> "--restart='" <> x <> "'") restartFiles
487− <> testCmd
488− <> ["--command='" <> unwords ("ghci" : ghciArgs) <> "'"] -- TODO: Shell escape
489− testCmd = maybeToList (flip fmap mcmd $ \cmd -> "--test='" <> cmd <> "'") -- TODO: Shell escape
490−
501+ opts = concat
502+ [ ["-W"]
503+ , ["--outputfile=ghcid-output.txt"]
504+ , map (\x -> "--reload=" <> x) reloadFiles
505+ , map (\x -> "--restart=" <> x) restartFiles
506+ , maybe [] (\cmd -> ["--test=" <> cmd]) mcmd
507+ , ["--command=" <> unwords ("ghci" : ghciArgs)]
508+ ]
491509 adjustRoot x = if chdirToRoot then makeRelative root x else x
492510 reloadFiles = map adjustRoot [root </> "config"]
493511 restartFiles = map (adjustRoot . _cabalPackageInfo_packageFile) packages
494512
495−mkGhciScriptArg :: FilePath -> [String]
496−mkGhciScriptArg dotGhci = ["-ghci-script", dotGhci]
497−
498−getFreePort :: MonadIO m => m PortNumber
499−getFreePort = liftIO $ withSocketsDo $ do
500− addr:_ <- getAddrInfo (Just defaultHints) (Just "127.0.0.1") (Just "0")
501− bracket (open addr) close socketPort
513+getFreePort :: MonadIO m => m Socket.PortNumber
514+getFreePort = liftIO $ Socket.withSocketsDo $ do
515+ addr:_ <- Socket.getAddrInfo (Just Socket.defaultHints) (Just "127.0.0.1") (Just "0")
516+ bracket (open addr) Socket.close Socket.socketPort
502517 where
503518 open addr = do
504− sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
505− bind sock (addrAddress addr)
519+ sock <- Socket.socket (Socket.addrFamily addr) (Socket.addrSocketType addr) (Socket.addrProtocol addr)
520+ Socket.bind sock (Socket.addrAddress addr)
506521 return sock
507522
508523
lib/command/src/Obelisk/Command/Thunk.hs+1−1
@@ -57,7 +57,7 @@ import System.Exit
5757import System.FilePath
5858import System.IO.Error
5959import System.IO.Temp
60−import System.Posix (getSymbolicLinkStatus, modificationTime)
60+import System.PosixCompat.Files (getSymbolicLinkStatus, modificationTime)
6161import System.Which (staticWhich)
6262import qualified Text.URI as URI
6363
lib/selftest/src/Obelisk/SelfTest.hs+6−1
@@ -243,7 +243,7 @@ main' isVerbose httpManager obeliskRepoReadOnly = withInitCache $ \initCache ->
243243 uu <- update
244244 assertRevEQ u uu
245245
246− it "can run 'ob doc'" $ inTmpObInit $ \_ -> runOb ["doc", "reflex"]
246+ it "can run 'ob doc'" $ inTmpObInit $ \_ -> runOb_ ["doc", "reflex"]
247247
248248 describe "ob thunk pack/unpack" $ parallel $ do
249249 it "has thunk pack and unpack inverses" $ inTmpObInitWithImplCopy $ \_ -> do
@@ -306,6 +306,11 @@ main' isVerbose httpManager obeliskRepoReadOnly = withInitCache $ \initCache ->
306306 void $ errExit False $ runOb ["thunk", "update", toTextIgnore dir, "--branch", "dumble-palooza"]
307307 checkDir dir >>= liftIO . assertEqual "" startingContents
308308
309+ describe "ob shell" $ parallel $ do
310+ it "works with --" $ inTmpObInit $ \_ -> do
311+ output <- runOb ["shell", "--", "ghc-pkg", "list"]
312+ liftIO $ assertBool "Unexpected output from ob shell" $ ("Cabal-" `T.isInfixOf` output) && ("ghc-" `T.isInfixOf` output)
313+
309314 describe "ob hoogle" $ {- NOT parallel -} do
310315 it "starts a hoogle server on the given port" $ inTmpObInit $ \_ -> do
311316 [p0] <- liftIO $ getFreePorts 1
lib/snap-extras/obelisk-snap-extras.cabal−1
@@ -12,7 +12,6 @@ library
1212 , base
1313 , bytestring
1414 , snap-core
15− , unix
1615 , directory
1716 , filepath
1817 , text
skeleton/common/common.cabal+1
@@ -12,3 +12,4 @@ library
1212 exposed-modules:
1313 Common.Api
1414 Common.Route
15+ ghc-options: -Wall
skeleton/frontend/frontend.cabal+4−2
@@ -28,7 +28,9 @@ executable frontend
2828 , reflex-dom
2929 , obelisk-generated-static
3030 , frontend
31− --TODO: Make these ghc-options optional
3231 ghc-options: -threaded
32+ if impl(ghcjs)
33+ ghc-options: -dedupe
34+ cpp-options: -DGHCJS_BROWSER
3335 if os(darwin)
34− ghc-options: -dynamic
36+ ghc-options: -dynamic