motivation
I have a set of steps to build a cross-compiled project (https://github.com/n64decomp/sm64). These steps require e.g. binutils targeting a MIPS platform like e.g. mips-linux-gnu. (Others like mips64-elf or mips64-linux-gnu are allegedly also supported by this project.)
-> I want to make a shell.nix file that, under nix-shell (or nix shell), generates an environment with cross-targeting binutils executables.
One more note: I am compiling on an M1 mac. However the project doesn't support building from aarm64-darwin, so I'm using x86_64-darwin as the build platform under the Rosetta2 compatibility layer.
attempt
I wrote a shell.nix like so:
{ pkgs ? import <nixpkgs> {
# https://nixos.org/manual/nixpkgs/stable/#sec-cross-usage
localSystem = (import <nixpkgs/lib>).systems.examples.x86_64-darwin;
crossSystem = (import <nixpkgs/lib>).systems.examples.mips-linux-gnu;
}
}:
pkgs.mkShell {
# stuff I want at build-time
# -> package host = project build
# -> package target = project host
# pulled from https://github.com/n64decomp/sm64#step-1-install-dependencies-1
nativeBuildInputs = [
# doesn't compile code -> no target
pkgs.gnumake42
pkgs.coreutils
pkgs.pkg-config
# compiles -> target = mips-linux-gnu
pkgs.binutils
];
shellHook = ''
# some other setup commands...
'';
}
Running nix-shell and trying to use any of the dependencies (e.g., make) generates an error like cannot execute binary file: Exec format error. It seems like all the binaries are built to run on mips, not to build for mips.
I also tried to put these dependencies in buildInputs instead. Running the project build then generates the error
ld: archive has no table of contents file 'audiofile/libaudiofile.a' for architecture x86_64
Imo, this conversely makes it sound like the target for mips-unknown-linux-gnu-ld is x86_64, and NOT mips.
question
How do I rewrite this shell.nix file to specify packages that run on x86_64-darwin, and build for the target mips-linux-gnu?
EDIT: more info
I opened up a nix repl to check what build/host/target my shell would generate:
nix-repl> pkgs = import <nixpkgs> { localSystem = (import <nixpkgs/lib>).systems.examples.x86_64-darwin; crossSystem = (import <nixpkgs/lib>).systems.examples.mips-linux-gnu; }
nix-repl> pkgs.stdenv.buildPlatform.config
"x86_64-apple-darwin"
nix-repl> pkgs.stdenv.hostPlatform.config
"mips-unknown-linux-gnu"
nix-repl> pkgs.stdenv.targetPlatform.config
"mips-unknown-linux-gnu"
It looks like my build platform is x86_64-darwin, and my host and target platforms are both mips-linux. This sounds right; I am using x86_64-darwin to build this project, and the resulting ROM runs on mips. (It doesn't generate other code, so the target is irrelevant.)
But nix-shell is used for development; when you run nix-shell derivation.nix, it drops you into the build sequence for the derivation. Thus the environment is on the build platform, right? But it seems here like nix-shell shell.nix is generating a host-platform environment. Why is that?
Solved it! There's a couple things that were going on here, as I now understand it:
audiofile/libaudiofile.aerror was an issue with the project's makefile (which doesn't pose issues when building with other package management tools for some reason...?). I modified the project itself to fix thisgnumake42, because the version of the defaultmakeis incompatible with this projectgnumake42dependency needed to be indepsBuildBuild.depsBuildHost(a.k.a.nativeBuildInputs) didn't work, not sure whypkgs {crossSystem = ...}. the "solution" is to usecallPackage, though I'm not exactly sure why this fixes the issuex86_64-darwinfor project compatibility, but it turns out removing this to use the defaultaarm64-darwinplatform works fine too (lesson: sometimes the community doesn't know best, and there's no harm in experimenting to validate what they say!)Here's the resulting
shell.nixfile, which I can use to correctly build my desired project: