<aside> 🚧 这个页面上的信息会不断更新。 我会按照别人经常问我什么问题来决定优先更新哪些内容,欢迎向我提问! 如果发现哪里有错误或者说得不够清楚,也请告诉我,很可能我顺手就改了。
</aside>
<aside> 🚧 正在施工
</aside>
推荐使用 Stack + Stackage 管理项目。Stack 是一个命令行程序,Stackage 是一个软件仓库。
Stack 每次运行时一定有一个“当前项目”,它首先从当前目录开始向上逐级检查每层目录中是否有 stack.yaml
文件,如果有,这就是当前项目的根目录。如果都没找到,当前项目的根目录将会是“global-project”:~/.stack/global-project
。如果这个目录不存在或里面没有 stack.yaml
文件,会自动生成默认内容。
项目之间不会互相影响,global-project 仅仅是在找不到当前项目时采用的默认当前项目,并不会影响任何其他项目。
每个项目必须有一个 stack.yaml
文件,它主要做两件事:指定项目使用的编译器版本,和描述项目包含的外部 package 和本地 package。所有 package 在一个名字空间中,不能有同名。对于外部 package,必须精确指定一个版本,以确保编译是可复现的(Stack 还会进一步将下载到的源码哈希锁定在 stack.yaml.lock
文件中)。对于本地 package,需要指定源代码所在的目录。
最简单的项目一般由一个 Stackage snapshot 和一个本地 package 构成。在 stack.yaml
中写 snapshot: lts-22.33
这样一行,表示使用 lts-22.33
这个 Stackage snapshot,它指定了一个编译器版本和几千个外部 package 分别的精确版本,它们被测试过可以兼容工作。Stackage snapshot 有点像 Debian,里面的版本可能会有一点旧,但省去了自己解决不同库之间兼容性问题的麻烦。除此之外什么都不写的话,默认包含一个本地 package,目录和项目目录相同。
每个 package 应当有一个 package.yaml
文件(或者也可以有一个 <package-name>.cabal
文件,前者会自动被翻译成后者),描述这个 package 依赖哪些 package,有哪些额外编译产物(例如可执行文件、测试、benchmark 等),默认的语言扩展和编译参数(但每份源代码文件可以分别覆盖这个默认值)。
Package 的依赖关系不能有环。
用 stack build
命令编译项目的所有本地 package,加 package 名作为参数则可以指定编译特定 package,无论它是外部 package 还是本地 package。编译过程是按需的,不被用到的 package 不会被下载和编译。
每个 package 包含若干 module,module 的名字一般类似这样:
Control.Monad
Data.List.NonEmpty
Prelude