From ada434032b59593d617cc75b2ef2d0584d0b9f17 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sun, 12 Nov 2023 10:57:02 +0100 Subject: [PATCH] Got windows cross-compilation working The resulting binary is still untested, but it _does_ compile! This required re-jiggering basically everything about the nix flake, including replacing the toolchain management to fenix. --- flake.lock | 128 +++++++++--------------------- flake.nix | 189 +++++++++++++++++++++++++++----------------- rust-toolchain.toml | 3 +- 3 files changed, 156 insertions(+), 164 deletions(-) diff --git a/flake.lock b/flake.lock index 12bf0f7..0ce9e76 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,27 @@ { "nodes": { - "flake-utils": { + "fenix": { "inputs": { - "systems": "systems" + "nixpkgs": "nixpkgs", + "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "lastModified": 1699770036, + "narHash": "sha256-bZmI7ytPAYLpyFNgj5xirDkKuAniOkj1xHdv5aIJ5GM=", + "owner": "nix-community", + "repo": "fenix", + "rev": "81ab0b4f7ae9ebb57daa0edf119c4891806e4d3a", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "nix-community", + "repo": "fenix", "type": "github" } }, "naersk": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs_2" }, "locked": { "lastModified": 1690373729, @@ -38,6 +39,22 @@ } }, "nixpkgs": { + "locked": { + "lastModified": 1699099776, + "narHash": "sha256-X09iKJ27mGsGambGfkKzqvw5esP1L/Rf8H3u3fCqIiU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "85f1ba3e51676fa8cc604a3d863d729026a6b8eb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1691003216, "narHash": "sha256-Qq/MPkhS12Bl0X060pPvX3v9ac3f2rRQfHjjozPh/Qs=", @@ -51,7 +68,7 @@ "type": "indirect" } }, - "nixpkgs_2": { + "nixpkgs_3": { "locked": { "lastModified": 1688392541, "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", @@ -67,94 +84,27 @@ "type": "github" } }, - "nixpkgs_3": { - "locked": { - "lastModified": 1681358109, - "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "root": { "inputs": { + "fenix": "fenix", "naersk": "naersk", - "nixpkgs": "nixpkgs_2", - "rust-overlay": "rust-overlay", - "utils": "utils" - } - }, - "rust-overlay": { - "inputs": { - "flake-utils": "flake-utils", "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1691029059, - "narHash": "sha256-QwVeE9YTgH3LmL7yw2V/hgswL6yorIvYSp4YGI8lZYM=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "99df4908445be37ddb2d332580365fce512a7dcf", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" } }, - "systems": { + "rust-analyzer-src": { + "flake": false, "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "lastModified": 1699715108, + "narHash": "sha256-yPozsobJU55gj+szgo4Lpcg1lHvGQYAT6Y4MrC80mWE=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "5fcf5289e726785d20d3aa4d13d90a43ed248e83", "type": "github" }, "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "utils": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", "type": "github" } } diff --git a/flake.nix b/flake.nix index f605050..5512ee1 100644 --- a/flake.nix +++ b/flake.nix @@ -1,102 +1,143 @@ { inputs = { + fenix.url = "github:nix-community/fenix"; naersk.url = "github:nix-community/naersk/master"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11"; - utils.url = "github:numtide/flake-utils"; - - rust-overlay.url = "github:oxalica/rust-overlay"; }; - outputs = { self, nixpkgs, utils, naersk, rust-overlay }: + outputs = { self, nixpkgs, naersk, fenix }: let - buildTargetsBySystem = { - "x86_64-linux" = "x86_64-unknown-linux-musl"; - "i686-linux" = "i686-unknown-linux-musl"; - "aarch64-linux" = "aarch64-unknown-linux-musl"; + buildTargets = { + "x86_64-linux" = { + crossSystemConfig = "x86_64-unknown-linux-musl"; + rustTarget = "x86_64-unknown-linux-musl"; + }; + + "i686-linux" = { + crossSystemConfig = "i686-unknown-linux-musl"; + rustTarget = "i686-unknown-linux-musl"; + }; + + "aarch64-linux" = { + crossSystemConfig = "aarch64-unknown-linux-musl"; + rustTarget = "aarch64-unknown-linux-musl"; + }; + + "x86_64-windows" = { + crossSystemConfig = "x86_64-w64-mingw32"; + rustTarget = "x86_64-pc-windows-gnu"; + makeBuildPackageAttrs = pkgsCross: { + depsBuildBuild = [ + pkgsCross.stdenv.cc + pkgsCross.windows.pthreads + ]; + }; + }; }; - supportedSystems = builtins.attrNames buildTargetsBySystem; + # eachSystem [system] (system: ...) + # + # Returns an attrset with a key for every system in the given array, with + # the key's value being the result of calling the callback with that key. + eachSystem = supportedSystems: callback: builtins.foldl' + (overall: system: overall // { ${system} = callback system; }) + {} + supportedSystems; - eachCrossSystem = callback: (builtins.foldl' - (overall: buildSystem: overall // { - ${buildSystem} = (builtins.foldl' - (inner: hostSystem: inner // { - "cross-${hostSystem}" = callback buildSystem hostSystem; + # eachCrossSystem [system] (buildSystem: targetSystem: ...) + # + # Returns an attrset with a key "$buildSystem.cross-$targetSystem" for + # every combination of the elements of the array of system strings. The + # value of the attrs will be the result of calling the callback with each + # combination. + # + # There will also be keys "$system.default", which are aliases of + # "$system.cross-$system" for every system. + # + eachCrossSystem = supportedSystems: callback: + eachSystem supportedSystems (buildSystem: builtins.foldl' + (inner: targetSystem: inner // { + "cross-${targetSystem}" = callback buildSystem targetSystem; }) { default = callback buildSystem buildSystem; } supportedSystems - ); - }) - {} - supportedSystems - ); + ); - mkPkgs = buildSystem: hostSystem: import nixpkgs ({ + mkPkgs = buildSystem: targetSystem: import nixpkgs ({ system = buildSystem; - overlays = [ - (import rust-overlay) - (final: prev: { - rust-toolchain = prev.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; - }) - ]; - } // (if buildSystem == hostSystem then {} else { + } // (if targetSystem == null then {} else { # The nixpkgs cache doesn't have any packages where cross-compiling has # been enabled, even if the target platform is actually the same as the # build platform (and therefore it's not really cross-compiling). So we # only set up the cross-compiling config if the target platform is # different. - crossSystem.config = hostSystem; + crossSystem.config = buildTargets.${targetSystem}.crossSystemConfig; })); - mkRustEnv = crossPkgs: hostSystem: { - OPENSSL_STATIC = "1"; - OPENSSL_LIB_DIR = "${crossPkgs.pkgsStatic.openssl.out}/lib"; - OPENSSL_INCLUDE_DIR = "${crossPkgs.pkgsStatic.openssl.dev}/include"; - CARGO_BUILD_TARGET = buildTargetsBySystem.${hostSystem}; - CARGO_BUILD_RUSTFLAGS = [ - "-C" "target-feature=+crt-static" - "-C" "linker=${crossPkgs.stdenv.cc}/bin/${crossPkgs.stdenv.cc.targetPrefix}cc" - "-C" "link-arg=-static" - ]; - }; + in { + packages = eachCrossSystem + (builtins.attrNames buildTargets) + (buildSystem: targetSystem: let + pkgs = mkPkgs buildSystem null; + pkgsCross = mkPkgs buildSystem targetSystem; + rustTarget = buildTargets.${targetSystem}.rustTarget; - in - { - packages = eachCrossSystem (buildSystem: hostSystem: let - pkgs = mkPkgs buildSystem buildSystem; - crossPkgs = mkPkgs buildSystem hostSystem; - naersk-lib = pkgs.callPackage naersk { - cargo = pkgs.rust-toolchain; - rustc = pkgs.rust-toolchain; - }; - rustEnv = mkRustEnv crossPkgs hostSystem; - in - naersk-lib.buildPackage ({ - src = ./.; - doCheck = false; - nativeBuildInputs = [ crossPkgs.pkgsStatic.stdenv.cc ]; - } // rustEnv) - ); + # TODO I'd prefer to use the toolchain file + # https://github.com/nix-community/fenix/issues/123 + #toolchain = fenix.packages.${buildSystem}.fromToolchainFile { + # file = ./rust-toolchain.toml; + # sha256 = "sha256-LU4C/i+maIOqBZagUaXpFyWZyOVfQ3Ah5/JTz7v6CG4="; + #}; - devShells = eachCrossSystem (buildSystem: hostSystem: let - pkgs = mkPkgs buildSystem buildSystem; - crossPkgs = mkPkgs buildSystem hostSystem; - rustEnv = mkRustEnv crossPkgs hostSystem; - in - pkgs.mkShell ({ - nativeBuildInputs = [ - crossPkgs.stdenv.cc - crossPkgs.openssl - pkgs.nmap # ncat - pkgs.rust-toolchain + fenixPkgs = fenix.packages.${buildSystem}; + + mkToolchain = fenixPkgs: fenixPkgs.toolchainOf { + channel = "nightly"; + date = "2023-07-23"; + sha256 = "sha256-LU4C/i+maIOqBZagUaXpFyWZyOVfQ3Ah5/JTz7v6CG4="; + }; + + toolchain = fenixPkgs.combine [ + (mkToolchain fenixPkgs).rustc + (mkToolchain fenixPkgs).cargo + (mkToolchain fenixPkgs.targets.${rustTarget}).rust-std ]; - shellHook = '' - export CARGO_HOME=$(pwd)/.cargo - if [ -f "config.yml" ]; then - export DOMANI_CONFIG_PATH=config-dev.yml - fi - ''; - } // rustEnv)); + buildPackageAttrs = if + builtins.hasAttr "makeBuildPackageAttrs" buildTargets.${targetSystem} + then + buildTargets.${targetSystem}.makeBuildPackageAttrs pkgsCross + else + {}; + + naersk-lib = pkgs.callPackage naersk { + cargo = toolchain; + rustc = toolchain; + }; + + in + naersk-lib.buildPackage (buildPackageAttrs // rec { + src = ./.; + strictDeps = true; + doCheck = false; + + OPENSSL_STATIC = "1"; + OPENSSL_LIB_DIR = "${pkgsCross.pkgsStatic.openssl.out}/lib"; + OPENSSL_INCLUDE_DIR = "${pkgsCross.pkgsStatic.openssl.dev}/include"; + + # Required because ring crate is special. This also seems to have + # fixed some issues with the x86_64-windows cross-compile :shrug: + TARGET_CC="${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc"; + + CARGO_BUILD_TARGET = rustTarget; + CARGO_BUILD_RUSTFLAGS = [ + "-C" "target-feature=+crt-static" + "-C" "link-arg=-static" + + # https://github.com/rust-lang/cargo/issues/4133 + "-C" "linker=${TARGET_CC}" + ]; + }) + ); }; } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 89137ef..e01adca 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -4,5 +4,6 @@ components = [ "rustfmt", "rustc-dev", "clippy", "cargo" ] targets = [ "x86_64-unknown-linux-musl", "i686-unknown-linux-musl", - "aarch64-unknown-linux-musl" + "aarch64-unknown-linux-musl", + "x86_64-pc-windows-gnu" ]