PureScript 编译器安装全指南:预编译二进制、包管理器与 Stack 源码构建
PureScript 编译器安装全指南:预编译二进制、包管理器与 Stack 源码构建
导读:本文基于仓库根目录的 INSTALL.md 编写,系统梳理 PureScript 编译器(
purs)在当前仓库(0.15.16,由 GHC 9.8.4 构建)下的全部安装途径——官方预编译二进制、NPM / Homebrew / FreeBSD / GNU Guix 等社区分发,以及使用 Stack 从源码自行编译的完整流程;同时针对 Linux 上libtinfo依赖缺失、npm 全局安装EACCES权限报错这两类高频安装故障给出定位与解决方案。读完本文,你可以根据自己的操作系统、架构与偏好,选择最合适的方式在几分钟内获得可用的purs可执行文件,并理解其版本输出与发布机制的底层细节。
一、安装前的系统要求:GHC 9.8.4 的支持范围
PureScript 编译器是用 Haskell 编写的(仓库 cabal 描述为 "A small strongly, statically typed programming language with expressive types, inspired by Haskell and compiling to JavaScript"),因此其运行平台的边界取决于构建所用 GHC 版本的支持范围。
按 INSTALL.md 的说明,当前编译器由 GHC 9.8.4 构建,可在 GHC 9.8.4 支持的任何操作系统上运行。具体而言:
- Windows:早于 Vista 的版本不受官方支持;
- macOS / OS X:早于 Mac OS X 10.7(Lion)的版本不受官方支持。
仓库中的构建配置与该要求一致:stack.yaml 使用 resolver: lts-23.18(该 snapshot 对应 GHC 9.8.x 系列),而 purescript.cabal 中 version: 0.15.16、base >=4.19.2.0 && <4.20 的依赖上界同样与 GHC 9.8.4 的 base 版本(4.19.x)吻合,可以作为“当前仓库实际由 GHC 9.8.4 构建”的实现层证据。
注意:这里描述的是“安装/运行”的支持范围,不涉及各平台下的语言特性差异;如果你所在平台不在 GHC 9.8.4 支持列表中,建议参考下文“从源码构建”一节,在其他环境(或交叉工具链)中自行产出可执行文件。
二、官方预编译二进制:解压即用的最快路径
每个 PureScript 官方 release 都会附带 Linux、macOS、Windows 三种平台的 x86-64 预编译二进制包。对绝大多数用户而言,这是最快、最不容易出错的安装方式:
- 在官方 release 页面下载对应平台与架构的压缩包;
- 解压后得到一个
purs可执行文件; - 将
purs放入PATH环境变量中的任意目录(例如~/.local/bin、/usr/local/bin)。
仓库中 bundle/README 对二进制包的说明与此一致:
This bundle contains the
pursexecutable; copy this file anywhere on your PATH. For information about how to use thepursexecutable, runpurs --help.
也就是说,二进制包的内容就是一个自包含的 purs 可执行文件,无需任何额外运行库(详见下文“curses 库”一节,v0.14.2 之后预编译二进制已不再依赖 ncurses)。
适用范围与限制:官方预编译包仅提供 x86-64 架构。如果你使用的是其他操作系统或架构(如 ARM、RISC-V、PowerPC 等),通常需要自行从源码构建编译器——这正是下文“从源码构建”一节的适用场景。
三、包管理器分发:NPM、Homebrew、FreeBSD 与 Guix
INSTALL.md 列出了若干由社区维护的安装渠道,方便在不同生态中接入。需要注意:这些分发“并非官方发布流程的直接产物”,可能滞后于最新 release。
| 渠道 | 命令 | 适用场景 |
|---|---|---|
| NPM | npm install -g purescript |
Node.js 生态,可被前端构建工具链调用 |
| Homebrew | brew install purescript |
macOS |
| FreeBSD 二进制包 | pkg install hs-purescript |
FreeBSD |
| GNU Guix | guix install purescript |
GNU/Linux(Guix 发行版) |
NPM 分发的实现机制
当前仓库的 npm-package/package.json 揭示了 npm 分发的工作方式,这对理解 npm install -g purescript 很有帮助:
- 包名
purescript,版本0.15.16(与 purescript.cabal 中的编译器版本保持一致); bin字段将命令purs映射到purs.bin(发布时由prepublishOnly脚本从purs.bin.placeholder复制生成);postinstall脚本执行install-purescript --purs-ver=0.15.16,即借助purescript-installer依赖,在安装时拉取并放置对应版本的purs二进制。
从源码结构看,npm 包本质上是一个“下载器 + 包装器”:它并不包含编译器源码,而是在安装时获取与 --purs-ver 指定版本匹配的预编译二进制。这也解释了为什么 npm 安装失败时通常表现为网络或权限问题(见下文“EACCES 错误”一节),而非编译器自身的问题。发布流程细节可参见 RELEASE_GUIDE.md(其中维护者发布后需验证 npm i purescript 与 npm i purescript@next 两种安装方式均可工作)。
四、从源码构建:Stack 方式与工程化细节
当没有对应平台的预编译二进制时,从源码构建是唯一途径。官方推荐的方式是使用 Stack,完整步骤如下:
$ stack update
$ stack unpack purescript
$ cd purescript-x.y.z # (将 x.y.z 替换为你下载到的版本号)
$ stack install --flag purescript:RELEASE
执行完毕后,编译得到的 purs 可执行文件会被复制到 ~/.local/bin。如果尚未安装 Stack 本身,请参考 Stack 官方文档的安装与升级说明(INSTALL.md 中给出了对应链接)。
构建配置的源码级解读
仓库中的构建配置文件可以帮你理解上述命令背后的行为:
--flag purescript:RELEASE的含义:该 flag 对应 purescript.cabal 中定义的flag release(描述为 "Mark this build as a release build: prevents inclusion of extra info e.g. commit SHA in --version output")。从 app/Version.hs 的实现可以看到:定义了RELEASE时,purs --version只输出版本号;未定义时,版本字符串会附加上[development build; commit: <git hash> DIRTY?]之类的开发构建信息。因此发布版二进制请务必带RELEASEflag 构建,这样--version输出才会干净、可被工具解析。- 构建优化:stack.yaml 中对本地包统一启用了
-O2 -Werror -fspecialize-aggressively -fexpose-all-unfoldings,并固定了language-javascript-0.7.0.0等 extra-deps(原因是新版对async关键字作为对象键的解析存在回归)。 - 静态链接选项:purescript.cabal 还提供了
flag static(manual: True),开启后通过ld-options: -static -pthread产出静态链接的可执行文件,适合需要部署到无 Haskell 运行时环境的场景。 - Makefile 快捷目标:仓库根目录 Makefile 提供了等价的开发目标:
make build(stack build purescript)、make install(stack install)、make run(stack build --fast && stack exec -- purs)、make test(stack test --fast purescript)、make ghci(打开 GHCi 交互环境)等。日常调试迭代可优先使用这些目标,它们封装了与 stack.yaml 一致的参数。
构建产物验证
构建成功后,可以用以下命令确认安装是否正常:
$ purs --version
$ purs --help
purs --help 会列出全部子命令(compile、repl、docs、graph、ide、publish 等,对应 app 目录下的各 Command 模块);purs --version 的输出行为则如前所述取决于是否定义了 RELEASE 宏。
五、历史遗留问题:curses 库与 libtinfo 报错
这是 PureScript 安装史上一个著名的坑,INSTALL.md 专门用一节解释:
- 问题背景:在 v0.14.2 之前,PureScript REPL 默认依赖
curses库(通过 Haskell 的terminfo包)。如果系统缺少该动态库,运行purs时会报:error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory - Linux 上的修复:手动安装
ncurses。以 Ubuntu 为例:$ sudo apt install libtinfo5 libncurses5-dev - 现状(v0.14.2 起):使用官方预编译二进制或以默认配置从源码构建时,不再需要
ncurses。如果仍然希望启用curses(例如获得更好的长行输入编辑体验),可以在构建时把 Haskeline 的terminfoflag 设为true来“opt in”。
这条信息与仓库现状是吻合的:purescript.cabal 中 Haskeline 的版本约束为 haskeline >=0.8.2.1 && <0.9,但并未强制开启 terminfo 特性,因此默认构建产物不再绑定系统级 curses 动态库。
排查建议:如果你的 purs 恰好报出 libtinfo 相关错误,请先确认二进制来源(是否 v0.14.2 之前的旧版),再按上述 apt 方式补齐系统库,或直接改用新版预编译二进制。
六、npm 安装报 EACCES: permission denied
使用 npm install -g purescript 时,如果出现:
Error: EACCES: permission denied
其根因是 npm 全局安装目录的写入权限不足(例如默认全局目录位于系统级路径、当前用户无写权限)。INSTALL.md 给出的最佳解决方案是:
- 通过 Node 版本管理器(node version manager) 安装 Node.js 和 npm;
- 这样全局包会被安装到用户目录下的受控位置,天然规避权限问题;
- npm 官方文档中关于 "resolving EACCES permissions errors when installing packages globally" 一节对此错误有更系统的说明。
这一建议与 npm 包的工作机制一致(见上文第三节):purescript 的 npm 包在 postinstall 阶段要通过 purescript-installer 将二进制写入安装位置,全局安装路径无写权限时,该阶段就会直接失败。
七、安装后快速自检清单
完成任一种安装后,建议按以下顺序确认环境就绪:
purs --version:能打印版本号(官方预编译二进制或带RELEASEflag 构建的输出不含 commit 后缀);purs --help:能看到完整的子命令列表;purs repl:能进入 REPL 交互环境(验证 REPL 相关依赖正常);which purs:确认purs位于PATH中你预期的目录。
如果以上任何一步失败,回到对应章节排查:版本与平台要求(第一节)、二进制来源(第二、三节)、源码构建参数(第四节)、系统库缺失(第五节)、权限问题(第六节)。
八、维护视角:INSTALL.md 与发布流程的联动
最后补充一个对镜像维护者有用的视角:RELEASE_GUIDE.md 在每次发布前明确要求“检查 INSTALL.md 是否保持最新”,包括:GHC 版本是否与当前构建一致、基于 GHC 版本的支持操作系统列表是否正确、是否有已停止维护的非官方安装渠道需要移除、源码构建步骤是否仍然可用。同时,INSTALL.md 本身也被列入 purescript.cabal 的 extra-source-files,随源码发行包一起分发。
这说明本文所有安装途径(预编译二进制、npm、Homebrew、FreeBSD、Guix、Stack 源码构建)都是发布流程中被主动维护和验证的“第一等公民”,你在镜像中看到的本文件与仓库代码、构建配置保持同一版本节奏。若发现安装文档与某个新 release 的实际情况不符,多半是发布前的同步遗漏,可对照 CHANGELOG.md 与 RELEASE_GUIDE.md 进一步核实。