Stack#

备注

本章节选、整理并翻译自 Stack 文档

简介#

  • Stack:跨平台的 Haskell 项目管理工具,包括 GHC 安装、项目编译、依赖安装等;
  • Stack 在 Cabal 的基础上做了改进,是 Cabal 的增强版;
  • Cabal 过于复杂,更推荐使用 Stack 进行包和项目管理;

命令#

$ stack [options] COMMAND|FILE
  • 运行 Stack 命令行;

参数

COMMAND#

子命令。

FILE#

指定文件名。

选项

--resolver <resolver>#

覆盖项目指定的解析器,使用该选项指定的解析器,解析器格式与配置文件一致。

setup#

$ stack setup [GHC_VERSION] [options]
  • 下载指定 GHC;

参数

GHC_VERSION#

GHC 版本,默认与当前解析器一致。

init#

$ stack init [DIR] [options]
  • 创建 Stack 项目配置文件stack.yaml
  • 快照:
    • init命令会寻找当前目录及子目录中的.cabal文件,并计算依赖和版本;
    • init命令会找到最佳快照,使编译需要的外部依赖最少;
    • init命令会以最新 LTS、最新 Nightly、其他 LTS 的顺序寻找最佳快照;

参数

DIR#

生成配置文件的目录名,默认当前目录;

选项

--force#

强制覆盖已有的stack.yaml

--ignore-subdirs#

忽略指定子目录下的.cabal文件。

--omit-packages#

排除冲突的包。

$ stack init
Looking for .cabal or package.yaml files to use to init the project.
We didn't find any local package directories
You may want to create a package with "stack new" instead
Create an empty project for now
...

new#

$ stack new PACKAGE_NAME [option] [TEMPLATE_NAME] [options]
  • 创建 Haskell 项目;

参数

PACKAGE_NAME#

Haskell 包名,由字母数字和短横杠-组成。

TEMPLATE_NAME#

包结构的模板,可以是 URL、本地文件名或[[service]:username/]templateservice可以为githubgitlabbitbucket)形式,默认为new-template

$ stack new helloworld new-template
Downloading template "new-template" to create project "helloworld"
in helloworld/ ...

Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- helloworld/

...

build#

$ stack build [TARGET] [options]
  • 编译 Haskell 项目;
  • 编译:
    • 编译后会生成stack.yaml.lock文件和.stack-work/目录;
    • 编译后产生的中间文件和可执行文件都在.stack-work/目录下;
    • build命令产生的可执行文件默认名为PACKAGE_NAME-exe

参数

TARGET#

编译对象,默认为所有本地的包。可以为以下格式:

  • PACKAGE:可指定包名;

    $ stack build helloworld
    
  • PACKAGE-VERSION:可指定包标识符,即包名加版本号;

    $ stack build helloworld-1.4.14
    
  • [PACKAGE[:TYPE]]:COMPONENT:可指定单个构成部分;

    $ stack build helloworld:test:helloworld-test
    $ stack build helloworld:helloworld-test
    $ stack build :helloworld-test
    
    • 构成部分:可通过stack ide targets命令查看;
      • :lib:库;
      • [:exe]:PACKAGE-exe:可执行文件;
      • [:test]:PACKAGE-test:测试组;
    • 指定其中一个部分不会编译其他部分;
  • DIR:可指定单个目录,编译该目录下的所有包和子包;

选项

--dry-run#

显示编译后产生的影响,但不编译。

--ghc-options#

指定 GHC 编译选项。

--[no-]haddock#

禁用/启用当前目录的文档生成,默认禁用。

--[no-]test#

禁用/启用当前目录的测试,默认禁用。

--[no-]copy-bins#

禁用/启用将当前目录的可执行文件复制到指定二进制目录,默认禁用。

--[no-]bench#

禁用/启用当前目录的标杆分析,默认禁用。

--[no-]test#

禁用/启用当前目录的测试,默认禁用。

--no-run-tests#

编译测试组后禁止运行测试组。

--no-run-benchmarks#

编译标杆分析组后禁止运行标杆分析。

--skip <ARG>#

编译时跳过指定构成部分。

--coverage#

生成代码覆盖报告。

$ stack build
Building all executables for `helloworld' once. After a successful
build of all of them, only specified executables will be rebuilt.
helloworld> configure (lib + exe)
Configuring helloworld-0.1.0.0...
helloworld> build (lib + exe)
Preprocessing library for helloworld-0.1.0.0..
Building library for helloworld-0.1.0.0..
[1 of 2] Compiling Lib
[2 of 2] Compiling Paths_helloworld
...

ghc#

$ stack ghc [options]
  • 运行 GHC 编译器;

选项

-- <argument(s)>#

指定 GHC 编译器的参数。

--cwd <dir>#

运行前指定当前工作目录。

--package <package(s)>#

指定额外的包。

$ stack ghc -- app/Main.hs
[1 of 1] Compiling Main             ( app/Main.hs, app/Main.o )
Linking app/Main ...

runghcrunhaskell#

$ stack runghc [options]
  • 运行runghc命令;
  • stack runhaskellstack runghc命令的别名;
  • runghc命令可用于脚本解释

选项

-- <argument(s)>#

指定runghc命令的参数。

--cwd <dir>#

运行前指定当前工作目录。

--package <package(s)>#

指定额外的包。

ghci#

$ stack ghci [TARGET/FILE] [options]
  • 运行 GHCi;

参数

TARGET/FILE#

指定加载的包或文件,默认为所有本地包。

选项

--ghci-options <options>#

指定 GHCi 选项。

--ghc-options <options>#

指定 GHC 选项。

--with-ghc <ghc>#

指定使用的 GHC 编译器版本。

--[no-test]#

禁用/启用测试组,默认禁用。

--[no-bench]#

禁用/启用标杆分析,默认禁用。

exec#

$ stack exec COMMAND [-- ARGUMENT(S)] [options]
  • 添加、修改环境变量并执行命令;
  • 命令执行:
    • build命令后产生的可执行文件可通过exec命令执行,exec命令会自动解析可执行文件的地址,不用手动指定;
    • exec ghci命令可用于脚本解释

参数

COMMAND#

要执行的命令。

$ stack exec helloworld-exe
someFunc

script#

$ stack script [option] FILE [options]
  • 解释 Haskell 脚本;

  • 解释器:

    • Stack 可用作 Haskell 源文件的解释器;

      $ stack Main.hs
      Hello World
      
    • script命令会忽略所有配置文件;

    • 使用script命令时,必须指定全局选项--resolver

    • Shebang:

      • 可以为 Haskell 源文件指定 Shebang,Shebang 后跟特殊注释以指定编译行为;

        #!/usr/bin/env stack
        -- stack --resolver lts-18.23 script --package random
        
      • 特殊注释可以为多行注释;

        #!/usr/bin/env stack
        {- stack
           --resolver lts-18.23
           script
           --package random
         -}
        
      • 若不指定 Shebang 和特殊注释,则 Stack 默认使用runghc命令执行文件;

    • 其他命令:某些命令同样可用于特殊注释中;

      • runghcrunhaskell命令:不推荐使用,更推荐script命令;

        #!/usr/bin/env stack
        {- stack
           --resolver lts-18.23
           --install-ghc
           runghc
           --package random
           --
           -hide-all-packages
         -}
        
      • exec ghci命令:将文件加载到 GHCi 中;

        #!/usr/bin/env stack
        {- stack
           --resolver lts-18.23
           --install-ghc
           exec ghci
           --package random
         -}
        

参数

FILE#

Haskell 源文件。

选项

--ghc-options <options>#

GHC 选项。

--package <package(s)>#

安装指定的包。

--extra-dep <package-version>#

指定不在快照中的依赖。

--compile#

编译文件时不作优化,并运行。

--optimize#

编译文件时优化,并运行。

--no-run#

编译后不运行。

test#

$ stack test [TARGET] [options]

参数

  • build命令相同
$ stack test
helloworld-0.1.0.0: unregistering (components added: test:helloworld-test)
helloworld> configure (lib + exe + test)
Configuring helloworld-0.1.0.0...
helloworld> build (lib + exe + test)
Preprocessing library for helloworld-0.1.0.0..
Building library for helloworld-0.1.0.0..
...
helloworld> test (suite: helloworld-test)

Test suite not yet implemented

helloworld> Test suite helloworld-test passed
Completed 2 action(s).

install#

$ stack install [TARGET] [options]
  • 编译项目并将可执行文件复制到指定二进制目录下,实际是stack build --copy-bins命令的别名;

参数

  • build命令相同
$ stack install
Building all executables for `helloworld' once. After a successful build
of all of them, only specified executables will be rebuilt.
...

Copied executables to /Users/chattille/.local/bin:
- helloworld-exe

haddock#

$ stack haddock [TARGET] [options]

参数

  • build命令相同

bench#

$ stack bench [TARGET] [options]
  • 编译项目并进行标杆分析,实际是stack build --bench命令的别名;
  • 参数:与build命令相同;

clean#

$ stack clean [PACKAGE] [options]
  • 清除指定包下产生的各种编译文件,即.stack-work/dist/目录;

参数

PACKAGE#

包名,默认为所有包。

选项

--full#

清除整个.stack-work/目录。

$ tree -L 1 .stack-work
.stack-work
├── dist
├── install
├── stack.sqlite3
└── stack.sqlite3.pantry-write-lock
$ stack clean
$ tree -L 1 .stack-work
.stack-work
├── install
├── stack.sqlite3
└── stack.sqlite3.pantry-write-lock

purge#

$ stack purge [options]
  • 清除整个.stack-work/目录,但不包括install命令复制的可执行文件,可将项目还原到未编译状态,实际是stack clean --full命令的别名;
$ ls -A
.gitignore    LICENSE   app              src             test
.stack-work   README.md helloworld.cabal stack.yaml
ChangeLog.md  Setup.hs  package.yaml     stack.yaml.lock
$ stack purge
$ ls -A
.gitignore    README.md helloworld.cabal stack.yaml
ChangeLog.md  Setup.hs  package.yaml     stack.yaml.lock
LICENSE       app       src              test

ls#

$ stack ls COMMAND [SUBCOMMAND] [options]
  • 列出指定要素;

参数

COMMAND#

命令。可以为:

  • snapshots:显示快照;

    参数

    COMMAND#

    子命令。可以为:

    • remote:显示远程快照;
    • local:显示本地快照,默认命令;

    选项

    -l, --lts#

    只显示 LTS 快照。

    -n, --nightly#

    只显示 Nightly 快照。

  • dependencies:列出依赖包;

    参数

    COMMAND#

    子命令。可以为:

    • text:以文本格式输出,默认命令;
    • tree:以树状图格式输出;
    • json:以 JSON 格式输出;

    选项

    --depth <depth>#

    指定依赖深度。

    --prune <packages>#

    排除指定包,多个包用逗号分隔。

  • stack-colorsstack-colours:列出 Stack 输出时使用的颜色;

list#

$ stack list [PACKAGE] [options]
  • 列出包在快照中的 ID;

参数

PACKAGE#

包名,默认为本地所有包。

unpack#

$ stack unpack PACKAGE [options]
  • 下载指定包;

参数

PACKAGE#

要下载的包名。

选项

--to <arg>#

下载到该目录的子目录中。

path#

$ stack path [options]
  • 打印 Stack 的路径信息;

选项

--bin-path#

PATH环境变量。

--programs#

Stack 安装的 GHC 编译器路径。

--stack-root#

Stack 的根目录。

--project-root#

当前项目的根目录。

--local-hoogle-root#

本地 Hoogle 的根目录。

--local-doc-root#

当前项目的文档根目录。

--local-bin#

二进制目录,install命令会将可执行文件安装到此目录。

--snapshot-pkg-db#

快照数据库路径;

--local-pkg-db#

本地数据库路径;

--global-pkg-db#

全局数据库路径;

ide#

$ stack ide [options] COMMAND
  • IDE 相关命令;

参数

COMMAND#

子命令。可以为:

  • targets:打印所有可编译 Stack 对象;
  • packages:打印所有可加载的本地包;

templates#

$ stack templates [options]
  • 显示模板帮助信息;

dot#

$ stack dot [options] [TARGET] [options]
  • Graphviz 格式显示依赖关系;

参数

TARGET#

显示该目标的依赖关系,默认为所有本地包。

选项

--[no-]external#

禁用/启用外部依赖,默认禁用。

--depth <depth>#

依赖关系的深度,默认无限。

--prune <packages>#

去除指定包,多个包用逗号分隔。

--test#

包括测试组的依赖。

--bench#

包括标杆分析组的依赖。

sdist#

$ stack sdist [DIR] [options]
  • 生成包的.tar文件以供上传分享;

参数

DIR#

生成.tarball文件的目录,默认为当前目录。

选项

--tar-dir <arg>#

将所有.tar文件复制到该目录。

upload#

$ stack upload [DIR] [options]

参数

DIR#

要上传的目录,默认为当前目录。

update#

$ stack update [options]
  • 下载并更新包索引;

upgrade#

$ stack upgrade [options]
  • 升级 Stack 到最新版本;

项目结构#

  • stack new命令默认使用模板new-template
  • 目录结构:
    • .gitignore:指定 Git 版本管理时要忽略的文件;
    • ChangeLog.md:项目历史;
    • LICENSE:项目使用的许可证;
    • README.md:项目简介;
    • Setup.hs:Cabal 编译系统的一部分,虽然技术上来说 Stack 不需要该文件,但在 Haskell 世界里推荐包含该文件;
    • PACKAGE_NAME.cabal:Cabal 编译使用的文件,由stack build命令自动更新,不应做修改;
    • app/:生成可执行文件的目录;
      • Main.hs:主模块,程序的入口;
    • package.yaml:包的配置文件;
    • src/:主模块使用的各种辅助模块;
    • stack.yaml:Stack 编译的配置文件,规定编译行为;
    • test/:测试组代码;

Stackage#

  • Hackage:Cabal 下载包时会从 Haskell 社区的远程库下载,该远程库为 Hackage;

  • Stackage

    • Stack 下载包时会从自己的远程库下载,该远程库为 Stackage,是一套稳定的从 Hackage 精选的 Haskell 包的集合;
    • Stack 同样支持 Hackage;
  • 快照

    • Stackage 将多个包打包成一个集合,称为快照,供特定版本的 GHC 使用;

    • 可通过全局选项--resolver或键resolver指定快照或 GHC 版本;

    • 快照分为两种:

      GHC version and corresponding LTS version.
      • LTS:稳定的精选 Haskell 包集合,每个 GHC 版本都有对应的 LTS
      • Nightly:更新的包集合,对应最新 GHC 版本,稳定性不如 LTS;

数据库#

  • 数据库:Haskell 包的数据库,包含包的各种信息,包括编译库、可执行文件、文档和其他文件;

    $ ls .stack-work/install/x86_64-osx/.../8.10.7/
    bin   doc   lib   pkgdb
    
  • 数据库结构:Stack 的数据库是分层的,分为全局数据库、快照数据库和本地数据库,可用ghc-pkg list命令查看;

    $ stack exec -- ghc-pkg list
    /Users/chattille/.ghcup/ghc/.../package.conf.d
        Cabal-3.2.1.0
        array-0.5.4.0
        base-4.14.3.0
        ...
        transformers-0.5.6.2
        unix-2.7.2.2
        xhtml-3000.2.2.1
    /Users/chattille/.stack/snapshots/.../pkgdb
        random-1.2.0
        splitmix-0.1.0.4
    /Users/chattille/.../Test/.stack-work/install/.../pkgdb
        Test-0.1.0.0
    
    • 全局数据库:GHC 编译器自带的包的数据库,由所有项目共享;
    • 快照数据库:来自快照的包的数据库,储存于~/.stack/snapshots/目录下,使用相同快照的项目可共享,不同快照不共享;
    • 本地数据库:当前项目的数据库,储存于./.stack-work/install/目录下,不与其他项目共享;
  • 多层数据库有利于在不同项目中复用相同的包,也能防止不同项目间的包污染;

package.yaml#

  • dependencies:指定当前项目的依赖,可用ls命令查看依赖;

    dependencies:
      - base >= 4.7 && < 5
      - text  # 在这里添加
    
  • extra-deps:指定不存在于当前快照的依赖;

    extra-deps:
      - acme-missiles-0.3  # 不在 LTS 内
    
  • resolver:指定解析器,若本地不存在对应版本的 GHC 编译器,则自动下载;

    • ghc-X.Y.Z:指定具体 GHC 编译器版本;
    • lts-X.Y:指定 LTS 快照版本,省略Y则指定最新lts-X版本;
    • nightly-YYYY-MM-DD:指定 Nightly 快照日期;
    resolver: lts-18.23