使用 Nix Flake 搭建基础的 Rust 跨平台构建环境

  1. 新增 flake.nix 文件,并执行 git add flake.nix

    {
      inputs = {
        utils.url = "github:numtide/flake-utils";
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
      };
    
      outputs = {
        nixpkgs,
        utils,
        ...
      }:
        utils.lib.eachDefaultSystem
        (
          system: let
            pkgs = import nixpkgs {inherit system;};
            lib = pkgs.lib;
          in {
            devShells =
              {
                default = pkgs.mkShell {
                  nativeBuildInputs = with pkgs; [
                    pkg-config
                    gcc
                  ];
                };
              }
              // builtins.listToAttrs (map (target: {
                name = target;
                value = let
                  cross = import nixpkgs {
                    inherit system;
                    crossSystem = {config = target;};
                  };
                  cpkgs = cross.pkgsMusl;
                in (cross.mkShell {
                  nativeBuildInputs = with pkgs; [
                    pkg-config
                    gcc # proc macro 等编译过程,需要原架构 toolchians
                  ];
                  env = let
                    normalized = lib.strings.toUpper (builtins.replaceStrings ["-"] ["_"] target);
                  in
                    with cpkgs; {
                      # https://doc.rust-lang.org/cargo/reference/environment-variables.html#configuration-environment-variables
                      "CARGO_TARGET_${normalized}_LINKER" = "${stdenv.cc.targetPrefix}cc";
                    };
                });
              }) ["x86_64-unknown-linux-musl" "aarch64-unknown-linux-musl"]);
          }
        );
    }
    
  2. 执行构建

    # 本地构建
    nix develop .#default --command \
     cargo build
    
    # 跨平台构建
    #
    # 别忘了先安装对应的 rustup 组件
    # rustup target add aarch64-unknown-linux-musl
    # rustup target add x86_64-unknown-linux-musl
    
    TARGET=x86_64-unknown-linux-musl nix develop .#$TARGET --command \
     cargo build --target $TARGET
    

openssl, pq 等 C/C++ 依赖处理

使用静态编译,并使用有 vendor (打包 C/C++ 源码 )版本的 bindings 项目,把这些加入依赖管理,确保能稳定的可重复构建。

  1. 在 Cargo.toml 文件中增加对应 target 的依赖。

    [target.x86_64-unknown-linux-musl.dependencies]
    openssl-sys = { version = "0.9", features = ["vendored"] }
    pq-sys = { version = "0.7", features = ["bundled"] }
    
    [target.aarch64-unknown-linux-musl.dependencies]
    openssl-sys = { version = "0.9", features = ["vendored"] }
    pq-sys = { version = "0.7", features = ["bundled"] }
    
  2. 执行构建

    # 本地静态编译则需要指定 RUSTFLAGS。经实测,有些架构该配置无效
    RUSTFLAGS='-C target-feature=+crt-static' cargo build
    
    # 而 x86_64-unknown-linux-musl 则不需要指定,默认启用
    TARGET=x86_64-unknown-linux-musl nix develop .#$TARGET --command \
      cargo build --target $TARGET
    

CRT means standard C runtime. The default of crt-static will be different depending on the target. For example x86_64-unknown-linux-musl will have it on by default, whereas arm-unknown-linux-musleabi will have it turned off by default. 1