1{ pkgs, lib }:
2
3let
4
5 testedSystems = lib.filterAttrs (
6 name: value:
7 let
8 platform = lib.systems.elaborate value;
9 in
10 platform.isLinux || platform.isWindows
11 ) lib.systems.examples;
12
13 getExecutable =
14 pkgs: pkgFun: exec:
15 "${pkgFun pkgs}${exec}${pkgs.stdenv.hostPlatform.extensions.executable}";
16
17 compareTest =
18 {
19 emulator,
20 pkgFun,
21 hostPkgs,
22 crossPkgs,
23 exec,
24 args ? [ ],
25 }:
26 let
27 pkgName = (pkgFun hostPkgs).name;
28 args' = lib.concatStringsSep " " args;
29 in
30 crossPkgs.runCommand "test-${pkgName}-${crossPkgs.stdenv.hostPlatform.config}"
31 {
32 nativeBuildInputs = [ pkgs.dos2unix ];
33 }
34 ''
35 # Just in case we are using wine, get rid of that annoying extra
36 # stuff.
37 export WINEDEBUG=-all
38
39 HOME=$(pwd)
40 mkdir -p $out
41
42 # We need to remove whitespace, unfortunately
43 # Windows programs use \r but Unix programs use \n
44
45 echo Running native-built program natively
46
47 # find expected value natively
48 ${getExecutable hostPkgs pkgFun exec} ${args'} \
49 | dos2unix > $out/expected
50
51 echo Running cross-built program in emulator
52
53 # run emulator to get actual value
54 ${emulator} ${getExecutable crossPkgs pkgFun exec} ${args'} \
55 | dos2unix > $out/actual
56
57 echo Comparing results...
58
59 if [ "$(cat $out/actual)" != "$(cat $out/expected)" ]; then
60 echo "${pkgName} did not output expected value:"
61 cat $out/expected
62 echo "instead it output:"
63 cat $out/actual
64 exit 1
65 else
66 echo "${pkgName} test passed"
67 echo "both produced output:"
68 cat $out/actual
69 fi
70 '';
71
72 mapMultiPlatformTest =
73 crossSystemFun: test:
74 lib.dontRecurseIntoAttrs (
75 lib.mapAttrs (
76 name: system:
77 lib.recurseIntoAttrs (test rec {
78 crossPkgs = import pkgs.path {
79 localSystem = { inherit (pkgs.stdenv.hostPlatform) config; };
80 crossSystem = crossSystemFun system;
81 };
82
83 emulator = crossPkgs.stdenv.hostPlatform.emulator pkgs;
84
85 # Apply some transformation on windows to get dlls in the right
86 # place. Unfortunately mingw doesn’t seem to be able to do linking
87 # properly.
88 platformFun =
89 pkg:
90 if crossPkgs.stdenv.hostPlatform.isWindows then
91 pkgs.buildEnv {
92 name = "${pkg.name}-winlinks";
93 paths = [ pkg ] ++ pkg.buildInputs;
94 }
95 else
96 pkg;
97 })
98 ) testedSystems
99 );
100
101 tests = {
102
103 file =
104 {
105 platformFun,
106 crossPkgs,
107 emulator,
108 }:
109 compareTest {
110 inherit emulator crossPkgs;
111 hostPkgs = pkgs;
112 exec = "/bin/file";
113 args = [
114 "${pkgs.file}/share/man/man1/file.1.gz"
115 "${pkgs.dejavu_fonts}/share/fonts/truetype/DejaVuMathTeXGyre.ttf"
116 ];
117 pkgFun = pkgs: platformFun pkgs.file;
118 };
119
120 hello =
121 {
122 platformFun,
123 crossPkgs,
124 emulator,
125 }:
126 compareTest {
127 inherit emulator crossPkgs;
128 hostPkgs = pkgs;
129 exec = "/bin/hello";
130 pkgFun = pkgs: pkgs.hello;
131 };
132
133 pkg-config =
134 {
135 platformFun,
136 crossPkgs,
137 emulator,
138 }:
139 crossPkgs.runCommand "test-pkg-config-${crossPkgs.stdenv.hostPlatform.config}"
140 {
141 depsBuildBuild = [ crossPkgs.pkgsBuildBuild.pkg-config ];
142 nativeBuildInputs = [
143 crossPkgs.pkgsBuildHost.pkg-config
144 crossPkgs.buildPackages.zlib
145 ];
146 depsBuildTarget = [ crossPkgs.pkgsBuildTarget.pkg-config ];
147 buildInputs = [ crossPkgs.zlib ];
148 NIX_DEBUG = 7;
149 }
150 ''
151 mkdir $out
152 ${crossPkgs.pkgsBuildBuild.pkg-config.targetPrefix}pkg-config --cflags zlib > "$out/for-build"
153 ${crossPkgs.pkgsBuildHost.pkg-config.targetPrefix}pkg-config --cflags zlib > "$out/for-host"
154 ! diff "$out/for-build" "$out/for-host"
155 '';
156 };
157
158 # see https://github.com/NixOS/nixpkgs/issues/213453
159 # this is a good test of a lot of tricky glibc/libgcc corner cases
160 mbuffer =
161 let
162 mbuffer = pkgs.pkgsCross.aarch64-multiplatform.mbuffer;
163 emulator = with lib.systems; (elaborate examples.aarch64-multiplatform).emulator pkgs;
164 in
165 pkgs.runCommand "test-mbuffer" { } ''
166 echo hello | ${emulator} ${mbuffer}/bin/mbuffer
167 touch $out
168 '';
169
170 # This is meant to be a carefully curated list of builds/packages
171 # that tend to break when refactoring our cross-compilation
172 # infrastructure.
173 #
174 # It should strike a balance between being small enough to fit in
175 # a single eval (i.e. not so large that hydra-eval-jobs is needed)
176 # so we can ask @ofborg to check it, yet should have good examples
177 # of things that often break. So, no buckshot `mapTestOnCross`
178 # calls here.
179 sanity = [
180 mbuffer
181 #pkgs.pkgsCross.gnu64.bash # https://github.com/NixOS/nixpkgs/issues/243164
182 pkgs.gcc_multi.cc
183 pkgs.pkgsMusl.stdenv
184 pkgs.pkgsLLVM.stdenv
185 pkgs.pkgsStatic.bash
186 #pkgs.pkgsCross.gnu64_simplekernel.bash # https://github.com/NixOS/nixpkgs/issues/264989
187 pkgs.pkgsCross.arm-embedded.stdenv
188 pkgs.pkgsCross.sheevaplug.stdenv # for armv5tel
189 pkgs.pkgsCross.raspberryPi.stdenv # for armv6l
190 pkgs.pkgsCross.armv7l-hf-multiplatform.stdenv
191 pkgs.pkgsCross.m68k.stdenv
192 pkgs.pkgsCross.aarch64-multiplatform.pkgsBuildTarget.gcc
193 pkgs.pkgsCross.powernv.pkgsBuildTarget.gcc
194 pkgs.pkgsCross.s390.stdenv
195 pkgs.pkgsCross.mips64el-linux-gnuabi64.stdenv
196 pkgs.pkgsCross.mips64el-linux-gnuabin32.stdenv
197 pkgs.pkgsCross.mingwW64.stdenv
198 # Uses the expression that is used by the most cross-compil_ed_ GHCs
199 pkgs.pkgsCross.riscv64.haskell.compiler.native-bignum.ghc948
200
201 ]
202 ++ lib.optionals (with pkgs.stdenv.buildPlatform; isx86_64 && isLinux) [
203 # Musl-to-glibc cross on the same architecture tends to turn up
204 # lots of interesting corner cases. Only expected to work for
205 # x86_64-linux buildPlatform.
206 pkgs.pkgsMusl.pkgsCross.gnu64.hello
207
208 # Two web browsers -- exercises almost the entire packageset
209 pkgs.pkgsCross.aarch64-multiplatform.qutebrowser-qt5
210 pkgs.pkgsCross.aarch64-multiplatform.firefox
211
212 # Uses pkgsCross.riscv64-embedded; see https://github.com/NixOS/nixpkgs/issues/267859
213 pkgs.spike
214 ];
215
216in
217{
218 gcc = lib.recurseIntoAttrs (
219 lib.mapAttrs (_: mapMultiPlatformTest (system: system // { useLLVM = false; })) tests
220 );
221 llvm = lib.recurseIntoAttrs (
222 lib.mapAttrs (_: mapMultiPlatformTest (system: system // { useLLVM = true; })) tests
223 );
224
225 inherit mbuffer sanity;
226}