Skip to content

Commit 4984a5a

Browse files
committed
Script command commercialhaskell#2805
This adds a basic script command. Most of the work involved has to do with preventing config files from being loaded. Originally we planned on creating an alternative set of config types for with and without a local config. That turned out to be prohibitively invasive. Instead, we now just create a dummy config file instead ~/.stack/script/lts-x.y (or /nightly-...). While this addresses reproducibility, it doesn't help with the speed concerns: script is now about 100ms faster than runghc on my system for the case where --package is provided, but it's still over a second for Hello World. The slowdown is inherent right now in checking if the relevant packages are installed. It would be nice to figure out a way to optimize the package check. Also, this should include some integration tests. It should be a simple matter of a test that includes a bogus stack.yaml and proving that stack script ignores it.
1 parent ff2f502 commit 4984a5a

17 files changed

Lines changed: 348 additions & 111 deletions

ChangeLog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ Release notes:
66

77
Major changes:
88

9+
* A new command, `script`, has been added, intended to make the script
10+
interpreter workflow more reliable, easier to use, and more
11+
efficient. This command forces the user to provide a `--resolver`
12+
value, ignores all config files for more reproducible results, and
13+
optimizes the existing package check to make the common case of all
14+
packages already being present much faster. This mode does require
15+
that all packages be present in a snapshot, however.
16+
[#2805](https://github.com/commercialhaskell/stack/issues/2805)
17+
918
Behavior changes:
1019

1120
* The default package metadata backend has been changed from Git to

doc/GUIDE.md

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,23 +1646,17 @@ running the file.
16461646
An example will be easiest to understand:
16471647
16481648
```
1649-
michael@d30748af6d3d:~$ cat turtle.hs
1649+
michael@d30748af6d3d:~$ cat turtle-example.hs
16501650
#!/usr/bin/env stack
1651-
-- stack --resolver lts-3.2 --install-ghc runghc --package turtle
1651+
-- stack --resolver lts-6.25 script --package turtle
16521652
{-# LANGUAGE OverloadedStrings #-}
16531653
import Turtle
16541654
main = echo "Hello World!"
1655-
michael@d30748af6d3d:~$ chmod +x turtle.hs
1656-
michael@d30748af6d3d:~$ ./turtle.hs
1657-
Run from outside a project, using implicit global project config
1658-
Using resolver: lts-3.2 specified on command line
1659-
hashable-1.2.3.3: configure
1660-
# installs some more dependencies
1661-
Completed all 22 actions.
1655+
michael@d30748af6d3d:~$ chmod +x turtle-example.hs
1656+
michael@d30748af6d3d:~$ ./turtle-example.hs
1657+
Completed 5 action(s).
16621658
Hello World!
1663-
michael@d30748af6d3d:~$ ./turtle.hs
1664-
Run from outside a project, using implicit global project config
1665-
Using resolver: lts-3.2 specified on command line
1659+
michael@d30748af6d3d:~$ ./turtle-example.hs
16661660
Hello World!
16671661
```
16681662
@@ -1680,11 +1674,30 @@ ensure the turtle package is available.
16801674
If you're on Windows: you can run `stack turtle.hs` instead of `./turtle.hs`.
16811675
The shebang line is not required in that case.
16821676
1677+
### Using multiple packages
1678+
1679+
You can also specify multiple packages, either with multiple `--package`
1680+
arguments, or by providing a comma or space separated list. For example:
1681+
1682+
```
1683+
#!/usr/bin/env stack
1684+
{- stack
1685+
script
1686+
--resolver lts-6.25
1687+
--package turtle
1688+
--package "stm async"
1689+
--package http-client,http-conduit
1690+
-}
1691+
```
1692+
16831693
### Stack configuration for scripts
16841694
1685-
If the current working directory is inside a project then that project's stack
1686-
configuration is effective when running the script. Otherwise the script uses
1687-
the global project configuration specified in
1695+
With the `script` command, all Stack configuration files are ignored to provide a
1696+
completely reliable script running experience. However, see the example below
1697+
with `runghc` for an approach to scripts which will respect your configuration
1698+
files. When using `runghc`, if the current working directory is inside a
1699+
project then that project's stack configuration is effective when running the
1700+
script. Otherwise the script uses the global project configuration specified in
16881701
`~/.stack/global-project/stack.yaml`.
16891702
16901703
### Specifying interpreter options
@@ -1703,50 +1716,61 @@ separating the stack options and ghc options with a `--`. Here is an example of
17031716
a multi line block comment with ghc options:
17041717
17051718
```
1706-
#!/usr/bin/env stack
1707-
{- stack
1708-
--resolver lts-3.2
1709-
--install-ghc
1710-
runghc
1711-
--package turtle
1712-
--
1713-
-hide-all-packages
1714-
-}
1719+
#!/usr/bin/env stack
1720+
{- stack
1721+
script
1722+
--resolver lts-6.25
1723+
--package turtle
1724+
--
1725+
+RTS -s -RTS
1726+
-}
17151727
```
17161728
17171729
### Writing independent and reliable scripts
17181730
1719-
Independent means that the script is independent of any prior deployment
1720-
specific configuration. If required, the script will install everything it
1721-
needs automatically on any machine that it runs on. To make a script always
1722-
work irrespective of any specific environment configuration you can do the
1723-
following:
1731+
With the release of Stack 1.2.1, there is a new command, `script`, which will
1732+
automatically:
1733+
1734+
* Install GHC and libraries if missing
1735+
* Require that all packages used be explicitly stated on the command line
1736+
1737+
This ensures that your scripts are _independent_ of any prior deployment
1738+
specific configuration, and are _reliable_ by using exactly the same version of
1739+
all packages every time it runs so that the script does not break by
1740+
accidentally using incompatible package versions.
1741+
1742+
In previous versions of Stack, the `runghc` command was used for scripts
1743+
instead. In order to achieve the same effect with the `runghc` command, you can
1744+
do the following:
17241745
17251746
1. Use the `--install-ghc` option to install the compiler automatically
17261747
2. Explicitly specify all packages required by the script using the
17271748
`--package` option. Use `-hide-all-packages` ghc option to force
17281749
explicit specification of all packages.
1750+
3. Use the `--resolver` Stack option to ensure a specific GHC version and
1751+
package set is used.
17291752
1730-
Reliable means the script will use exactly the same version of all packages
1731-
every time it runs so that the script does not break by accidentally using
1732-
incompatible package versions. To achieve that use an explicit `--resolver`
1733-
stack option.
1734-
1735-
Here is an interpreter comment for a completely self-contained and reproducible
1736-
version of our toy example:
1737-
```
1738-
#!/usr/bin/env stack
1739-
{- stack
1740-
--resolver lts-3.2
1741-
--install-ghc
1742-
runghc
1743-
--package base
1744-
--package turtle
1745-
--
1746-
-hide-all-packages
1753+
Even with this configuration, it is still possible for configuration
1754+
files to impact `stack runghc`, which is why `stack script` is strongly
1755+
recommended in general. For those curious, here is an example with `runghc`:
1756+
1757+
```
1758+
#!/usr/bin/env stack
1759+
{- stack
1760+
--resolver lts-6.25
1761+
--install-ghc
1762+
runghc
1763+
--package base
1764+
--package turtle
1765+
--
1766+
-hide-all-packages
17471767
-}
17481768
```
17491769
1770+
The `runghc` command is still very useful, especially when you're working on a
1771+
project and want to access the package databases and configurations used by
1772+
that project. See the next section for more information on configuration files.
1773+
17501774
## Finding project configs, and the implicit global
17511775
17521776
Whenever you run something with stack, it needs a `stack.yaml` project file. The

src/Stack/Build.hs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ build setLocalFiles mbuildLk boptsCli = fixCodePage $ do
120120
plan <- withLoadPackage menv $ \loadPackage ->
121121
constructPlan mbp baseConfigOpts locals extraToBuild localDumpPkgs loadPackage sourceMap installedMap
122122

123+
allowLocals <- view $ configL.to configAllowLocals
124+
unless allowLocals $ case justLocals plan of
125+
[] -> return ()
126+
localsIdents -> throwM $ LocalPackagesPresent localsIdents
127+
123128
-- If our work to do is all local, let someone else have a turn with the snapshot.
124129
-- They won't damage what's already in there.
125130
case (mbuildLk, allLocal plan) of
@@ -155,6 +160,13 @@ allLocal =
155160
Map.elems .
156161
planTasks
157162

163+
justLocals :: Plan -> [PackageIdentifier]
164+
justLocals =
165+
map taskProvides .
166+
filter ((== Local) . taskLocation) .
167+
Map.elems .
168+
planTasks
169+
158170
checkCabalVersion :: (StackM env m, HasEnvConfig env) => m ()
159171
checkCabalVersion = do
160172
allowNewer <- view $ configL.to configAllowNewer

0 commit comments

Comments
 (0)