实战记录:使用 Haskell.nix 交叉编译 Haskell 项目至 Windows

在 Haskell 开发中,使用 Nix 进行构建能带来极大的便利,尤其是涉及跨平台交叉编译时。本文记录了将一个基于 haskell.nix 的项目(haskell-periodic)配置为支持 Windows (mingwW64) 交叉编译的全过程。

1. 调整构建脚本 (Makefile)

原始的 Makefile 1 主要是针对 Linux (musl) 环境设计的,缺少对 Windows 可执行文件后缀(.exe)和特定工具链的支持。

为了支持 Windows 交叉编译,我们需要在 Makefile 中增加对 mingwW64 平台的分支判断,主要修改点如下:

  1. 定义 Strip 工具 :Linux 下通常使用 standard strip,但在交叉编译 Windows 版本时,必须使用对应的 x86_64-w64-mingw32-strip
  2. 处理文件后缀 :Windows 可执行文件需要 .exe 后缀,而 Linux 不需要。
  3. 修改输出目标:构建目标路径需要动态追加这个后缀。

修改后的 Makefile 片段:

Makefile

makefile 复制代码
# ... 之前的定义 ...

ifeq ($(PLATFORM),mingwW64)
# Windows 平台特定配置
STRIP = x86_64-w64-mingw32-strip
COMPILER ?= ghc9122
EXT = .exe
else
# ... 其他平台配置 ...
endif

# ...

# 针对每个组件,确保输出包含后缀 $(EXT)
periodic:
	PKG=periodic-client-exe make dist/$(PLATFORM)/$@$(EXT)

# ... 其他组件类似 ...

通过运行 make PLATFORM=mingwW64,构建系统现在可以正确识别目标平台并尝试生成 .exe 文件。

2. 遭遇链接错误:UCRT 与 MSVCRT 冲突

在调整完 Makefile 并尝试构建后,我们在链接阶段(Linking)遇到了如下错误:

Plaintext

vbnet 复制代码
multiple definition of `__getmainargs'
multiple definition of `_onexit'
multiple definition of `_amsg_exit'
...

问题分析:

这是一个经典的 Windows C 运行时冲突问题。现代的 GHC 和 Nix 工具链倾向于使用 UCRT (Universal C Runtime),但某些依赖或 GHC 内部组件可能引入了旧版的 MSVCRT (Microsoft Visual C Runtime)。当链接器同时遇到这两套库的符号定义时,就会报"多重定义"错误。

3. 修正 Nix 配置 (default.nix)

为了解决这个问题,我们需要通过 GHC 传递参数给链接器,告诉它允许符号的多重定义(让链接器自动选择其中一个)。

我们需要修改 default.nix 2222,利用 haskell.nix 提供的模块系统 (modules) 注入特定的 configureFlags

修改后的 default.nix 关键部分:

Nix

ini 复制代码
# ... 头部引入保持不变 ...

in pkgs.haskell-nix.cabalProject {
    # ... src, index-state 等配置 ...
    
    modules = [(
       {pkgs, ...}: {
         enableProfiling = enableProfiling;

         # --- Windows 交叉编译修复 ---
         # 针对 Windows 平台,添加链接器参数以解决 UCRT/MSVCRT 冲突
         packages.periodic-client-exe.configureFlags = pkgs.lib.optionals pkgs.stdenv.hostPlatform.isWindows [
           "--ghc-option=-optl-Wl,--allow-multiple-definition"
         ];

         # 如果 Server 端也需要在 Windows 构建,同样应用该修复
         packages.periodic-server.configureFlags = pkgs.lib.optionals pkgs.stdenv.hostPlatform.isMusl [
           # ... 保留原有的 Linux/Musl 链接库配置 ...
           "--ghc-option=-optl=-lssl"
           # ...
         ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isWindows [
           "--ghc-option=-optl-Wl,--allow-multiple-definition"
         ];
      })];
}

这里使用了 pkgs.lib.optionals pkgs.stdenv.hostPlatform.isWindows 来确保这些标志仅在编译 Windows 版本时生效,不会影响 Linux 版本的构建。

4. 最终结果

完成上述修改后,再次执行构建命令:

Bash

ini 复制代码
make PLATFORM=mingwW64

构建过程顺利通过链接阶段,成功在 dist/mingwW64/ 目录下生成了 Windows 可执行文件(如 periodic.exe)。通过这种方式,我们利用 Nix 强大的交叉编译能力,在不离开 Linux 开发环境的情况下,优雅地交付了 Windows 二进制文件。

相关推荐
玉梅小洋10 小时前
Windows 10 Android 构建配置指南
android·windows
Anita_Sun14 小时前
一看就懂的 Haskell 教程 - 类型推断机制
后端·haskell
Anita_Sun15 小时前
一看就懂的 Haskell 教程 - 类型签名
后端·haskell
雨中风华16 小时前
Linux, macOS系统实现远程目录访问(等同于windows平台xFsRedir软件的目录重定向)
linux·windows·macos
yuuki23323318 小时前
【C++】继承
开发语言·c++·windows
非凡ghost19 小时前
PowerDirector安卓版(威力导演安卓版)
android·windows·学习·软件需求
疯狂敲代码的老刘21 小时前
JDK 1.6到25 全版本网盘合集 (Windows + Mac + Linux)
java·linux·windows·macos·jdk
love530love1 天前
Windows 下 GCC 编译器安装与排错实录
人工智能·windows·python·gcc·msys2·gtk·msys2 mingw 64
猫头虎1 天前
OpenClaw开源汉化发行版:介绍、下载、安装、配置教程
运维·windows·开源·aigc·ai编程·agi·csdn
luffy54591 天前
windows下通过docker-desktop创建redis实例
windows·redis·docker·容器