在 macOS 上从源码编译 Hasura GraphQL Engine:基于 brew 与 GHC 9.4.5 的完整实战指南
2026/9/20 2:58:02 网站建设 项目流程

在 macOS 上从源码编译 Hasura GraphQL Engine:基于 brew 与 GHC 9.4.5 的完整实战指南

【免费下载链接】graphql-engineBlazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.项目地址: https://gitcode.com/gh_mirrors/gr/graphql-engine

本篇指南完整复现 Hasura GraphQL Engine 开源仓库(graphql-engine)在 macOS 上的源码编译流程:从 ghcup 安装 Haskell 工具链、brew 安装全部 C 语言依赖、构建 Console 前端资源、配置 Cabal 项目文件,到最终产出graphql-engine可执行文件。读完本文,你将能在一台全新的 macOS 上独立完成整个构建链路,并理解版本号烘焙、前端资源加载等关键机制在源码中的实现方式。

本文以仓库文档 server/COMPILING-ON-MACOS.md 为骨架,并结合仓库内相关源码与配置文件进行深度印证。

一、构建前置说明:Homebrew 前缀路径

整个编译流程的难点在于:graphql-engine 依赖大量通过 brew 安装的 C 库(libpq、unixodbc、openssl、libffi 等),而 Haskell 的 Cabal 构建系统默认无法找到它们,需要显式地通过路径配置让 Cabal 感知这些依赖。

文档中的命令统一使用/opt/homebrew作为 Homebrew 安装前缀,但你的机器上前缀可能不同:较老版本的 Homebrew 通常安装到/usr/local。请先执行下面命令确认:

brew --prefix

如果输出不是/opt/homebrew,请将本文(以及下文所有命令与配置)中的/opt/homebrew一律替换为brew --prefix的实际输出路径。这一点若不注意,后续的extra-include-dirsextra-lib-dirs配置将指向不存在的目录,Cabal 解析阶段就会直接失败。

二、第一步:安装 GHC 9.4.5 与 cabal-install(ghcup)

首先通过 ghcup 安装指定版本的 Haskell 工具链:

  1. 使用 ghcup 安装ghc-9.4.5
  2. 使用 ghcup 安装cabal-install(建议安装 3.x 较新版本,以便支持后续构建所需的语法特性)。

安装完成后可用以下命令校验:

ghc --version cabal --version

需要注意:当前仓库根目录的 cabal.project(以及 cabal/dev-sh.project)中with-compiler已指向更新的编译器版本(如ghc-9.14.1)。因此本地编译前应核对所选 project 文件中的with-compiler与你实际安装的 GHC 版本是否一致,避免 Cabal 报 "cannot find compiler" 类错误。

三、第二步:用 brew 安装全部系统依赖

执行以下命令一次性安装构建所需的全部依赖:

brew install google-cloud-sdk \ node@16 \ openssl \ unixodbc \ libpq \ libffi \ microsoft/mssql-release/mssql-tools18 \ direnv \ coreutils

各依赖在构建链中的作用大致如下:

依赖用途
google-cloud-sdkBigQuery 数据源相关的 SDK(部分测试与功能需要)
node@16构建 Console 前端资源(nx/webpack 工具链)
opensslpostgresql-libpq等包的 TLS 依赖,配置中需要其 include/lib 目录
unixodbcODBC 数据源支持(odbc包)
libpqPostgreSQL 客户端库,postgresql-libpqpg-client的核心依赖
libffiPythoncffi及部分 Haskell 包的 C 依赖,Python 构建时需其 pkgconfig
mssql-tools18SQL Server 数据源的连接工具
direnv开发环境变量管理工具
coreutilsGNU coreutils,部分构建脚本依赖其行为

3.1 将依赖加入 PATH

随后将这些工具加入 shell 环境(以 zsh 为例):

echo 'export PATH="/opt/homebrew/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/bin:$PATH"' >> ~/.zshrc echo 'export PATH="/opt/homebrew/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc echo 'export PATH="/opt/homebrew/opt/node@16/bin:$PATH"' >> ~/.zshrc echo 'export PATH="/opt/homebrew/opt/libpq/bin:$PATH"' >> ~/.zshrc

同样地,如果brew --prefix输出不是/opt/homebrew,请同步替换以上路径。执行后source ~/.zshrc(或新开终端)使其生效。

提示:如果你是在已有环境上重新执行这些步骤以更新 Mac,构建可能因旧缓存失效而失败,此时需先执行cabal clean清理后再继续。

四、第三步:构建 Console 前端资源(server-build:ce)

graphql-engine 的可执行文件默认会将 Console(控制台)前端资源打包进服务,因此编译前必须先在 frontend 目录下构建前端产物:

cd frontend npm ci npm run server-build:ce cd ..

其中:

  • npm ci依据 frontend/package.json 的锁文件安装精确版本的依赖;
  • server-build:ce对应 package.json 中的"server-build:ce": "nx run console-ce:build-server-assets",即通过 Nx 构建 CE(Community Edition)控制台的 server assets。

文档特别提醒:这一步可能需要 python2 已安装且位于$PATH(部分较老的前端构建工具链依赖 python2)。如果你的机器上缺少 python2,可考虑用pyenv安装后加入 PATH,再重试。

若你构建的是 Enterprise/Pro 版本,可对照 scripts/make/frontend.mk 中的目标,使用server-build:ee对应命令。

五、第四步:安装 Python 环境(测试依赖)

源码编译本身不依赖 Python,但仓库的集成测试体系(pytest)需要一套 Python 环境。按文档执行:

export PKG_CONFIG_PATH="/opt/homebrew/opt/libffi/lib/pkgconfig" export LDFLAGS="-L/opt/homebrew/opt/openssl@1.1/lib" export CPPFLAGS="-I/opt/homebrew/opt/openssl@1.1/include" cd server python3 -m venv .python-venv source .python-venv/bin/activate pip3 install -r tests-py/requirements.txt (cd tests-py/remote_schemas/nodejs && npm ci)

说明:

  • PKG_CONFIG_PATH指向 libffi 的 pkgconfig 目录,让 Python 生态中的 cffi 等包能正确找到 libffi;
  • LDFLAGS/CPPFLAGS指向 openssl,供需要链接 OpenSSL 的 Python 包使用;
  • 依赖清单位于 server/tests-py/requirements.txt;
  • 集成测试使用的 Node.js remote schema 示例依赖在 server/tests-py/remote_schemas/nodejs,需要单独npm ci

六、第五步:配置 Cabal 以定位 C 依赖

这是整个流程中最容易出错的一步。Cabal 默认不会自动搜索 brew 安装的 C 库,必须通过 project 文件中的package段为相关 Haskell 包指定额外的 include 与 lib 搜索目录。

6.1 追加 package 配置

将以下内容追加到cabal/dev-sh.project.local文件末尾(同样记得替换/opt/homebrew):

package odbc extra-include-dirs: /opt/homebrew/opt/unixodbc/include extra-lib-dirs: /opt/homebrew/opt/unixodbc/lib package postgresql-libpq extra-include-dirs: /opt/homebrew/opt/libpq/include /opt/homebrew/opt/openssl/include extra-lib-dirs: /opt/homebrew/opt/libpq/lib /opt/homebrew/opt/openssl/lib package pg-client extra-include-dirs: /opt/homebrew/opt/libpq/include /opt/homebrew/opt/openssl/include extra-lib-dirs: /opt/homebrew/opt/libpq/lib /opt/homebrew/opt/openssl/lib

这三个 package 分别是:

  • odbc:ODBC 数据库驱动封装,依赖 unixodbc 头文件与动态库;
  • postgresql-libpq:PostgreSQL libpq 的 Haskell 绑定,依赖 libpq 与 openssl;
  • pg-client:graphql-engine 仓库内部的 PostgreSQL 客户端封装库,同样直接依赖 libpq 与 openssl。

你可以直接在仓库 cabal/dev-sh.project.local 中看到该文件的原貌:它同时包含debug-infoexecutable-dynamic: Truelibrary-vanilla: False等针对本地开发优化的全局配置,末尾三段正是上述 C 依赖路径配置——这也印证了文档描述与仓库现状的一致性(若你的 brew 前缀不同,仍需手动改写这几段)。

6.2 启用 project.local 配置

将整个cabal/dev-sh.project.local的内容复制粘贴到cabal.project.local,或者直接创建符号链接:

ln -s cabal/dev-sh.project.local cabal.project.local

两种方式的取舍:

  • 复制粘贴:可以在cabal.project.local中继续叠加本地项目的package graphql-engine覆盖配置——如果你打算修改 graphql-engine 源码本身,推荐这种方式;
  • 符号链接:简单省事,适合"按原样编译"的场景,不需要对代码做任何改动。

注意:cabal.project.local是 Cabal 默认读取的本地覆盖文件,会被 Cabal 自动加载;若你通过--project-file指定了其他 project 文件(例如 scripts/dev.sh 使用的cabal/dev-sh.project),则对应的.local文件规则需按该工具的既有约定来放置。

七、第六步:写入版本号到 server/CURRENT_VERSION

graphql-engine 会在编译期把版本号烘焙进二进制。这一步是很多新手容易跳过的,但跳过会直接导致编译失败

echo '2.13.0' > server/CURRENT_VERSION

2.13.0仅为示例,请替换为你要构建的实际版本号,例如仓库 releases 目录中存在的版本。)

7.1 源码级原理:版本号如何被读取

在 server/src-lib/Hasura/Server/Version.hs 中,currentVersion通过 Template Haskell 在编译期读取该文件:

currentVersion :: Version currentVersion = fromText $ T.dropWhileEnd (== '\n') $ T.pack $( do versionFileName <- makeRelativeToProject "CURRENT_VERSION" addDependentFile versionFileName ... runIO (readFile versionFileName `onException` error noFileErr) >>= stringE )

关键细节:

  • makeRelativeToProject "CURRENT_VERSION"定位仓库server/目录下的版本文件;
  • addDependentFile把该文件登记为编译依赖,使缓存场景下文件变化也能触发正确重编译(这也是 server/graphql-engine.cabal 第 13-21 行将CURRENT_VERSION列入extra-source-files的原因,注释明确引用了 cabal 的 issue #4746);
  • 文件不存在时,会抛出提示信息,指引开发者先执行echo 12345 > .../server/CURRENT_VERSION

版本字符串会被解析为三种形态之一(Version.hs):

形态判定示例输出
VersionRelease能按 SemVer 解析(如2.13.0v2.13.0
VersionCE-ce结尾原样输出
VersionDev其余无法解析的字符串(如12345原样输出

7.2 版本号的两种用途

版本号在编译出的二进制中承担两个作用:

  1. graphql-engine --version的输出内容
  2. Console 前端资源的 CDN 地址生成:如果运行时未通过--console-assets-dir指定本地编译的前端资源目录,服务器将依据版本号映射到 CDN 上的 Console 资源。

映射逻辑见 server/src-lib/Hasura/Server/Version.hs 的versionToAssetsVersion:正式发布版本会映射为versioned/v2.13这类路径(并依据预发布标识推断stable/beta等发布通道),开发版本则映射为versioned/<文本>。因此,若你构建的是未发布版本却希望使用 CDN Console,版本号的形态会直接影响资源能否命中;想要加载本地构建的 Console 资源,则应使用--console-assets-dir指向第四步生成的 assets 目录。

7.3 本地开发使用的"魔术版本号"

值得一提的是,仓库的 scripts/dev.sh 在本地开发时统一写入12345作为版本号,其注释说明这是有意为之:该数字保证不触发无谓重编译,并且在集成测试的版本测试中被显式忽略。如果你仅想编译一个可运行的服务而不关心版本号,写入12345与写入真实版本号在构建层面同样有效。

八、第七步:正式编译

完成以上全部准备后,开始构建:

cabal update cabal build exe:graphql-engine -j4
  • cabal update拉取 Hackage 包索引(首次构建必须);
  • cabal build exe:graphql-engine -j4以 4 路并行编译 server 主程序,产物为graphql-engine可执行文件。

构建产物默认位于 Cabal 的 dist-newstyle 目录下(可通过cabal list-bin exe:graphql-engine查看确切路径)。这是首次构建,Haskell 依赖树庞大,请预留充足时间与磁盘空间。

九、构建后的验证与运行

编译完成后,可先验证版本输出:

$(cabal list-bin exe:graphql-engine) --version

启动服务(以本地 PostgreSQL 为例,仓库根目录 docker-compose.yaml 提供了配套数据库编排):

$(cabal list-bin exe:graphql-engine) serve

如需加载本地编译的 Console 资源,可添加--console-assets-dir指向 frontend 构建产物目录;否则服务器会依据CURRENT_VERSION从 CDN 拉取对应版本的 Console 资源(详见 server/src-lib/Hasura/Server/App.hs 附近的静态资源服务逻辑)。

十、常见问题排查速查

现象原因与处理
cabal报找不到 GHC未安装 ghc-9.4.5,或 project 文件with-compiler与实际安装版本不一致
链接阶段找不到libpq/odbc未正确配置 cabal/dev-sh.project.local 中的extra-include-dirs/extra-lib-dirs,或/opt/homebrew前缀与实际不符
TH 阶段报DEAR HASURIAN错误未创建server/CURRENT_VERSION文件,按提示写入版本号即可(参见 Version.hs 中的错误信息)
前端构建失败提示 python2按文档第四步说明安装 python2 并加入$PATH
更新环境后构建异常先执行cabal clean清理旧缓存再重新构建

结语

通过 ghcup + brew + Cabal 的三段式配合,你可以在 macOS 上完整构建 Hasura GraphQL Engine:ghcup 提供 Haskell 工具链,brew 补齐全部 C 依赖,而cabal/dev-sh.project.local则充当连接两者的"桥梁"。理解CURRENT_VERSION的编译期烘焙机制(server/src-lib/Hasura/Server/Version.hs)与 Console 资源的加载策略,能帮助你在构建自有版本、调试 Console 时少走弯路。建议以本文配合仓库文档 server/COMPILING-ON-MACOS.md 与 scripts/dev.sh 一起阅读,后者封装了本地开发所需的完整环境编排。

【免费下载链接】graphql-engineBlazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.项目地址: https://gitcode.com/gh_mirrors/gr/graphql-engine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询