CrepeGoat
CrepeGoat

Reputation: 2515

`shell.nix` to manually cross-compile a project for `mips-linux-gnu`

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?

Upvotes: 1

Views: 431

Answers (1)

CrepeGoat
CrepeGoat

Reputation: 2515

Solved it! There's a couple things that were going on here, as I now understand it:

  • the audiofile/libaudiofile.a error 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 this
  • basically all of these dependencies (minus one) are provided by default. the only dependency I really needed was gnumake42, because the version of the default make is incompatible with this project
  • the gnumake42 dependency needed to be in depsBuildBuild. depsBuildHost (a.k.a. nativeBuildInputs) didn't work, not sure why
  • there's allegedly a nix issue when cross-compiling via specifying pkgs {crossSystem = ...}. the "solution" is to use callPackage, though I'm not exactly sure why this fixes the issue 🤔
  • I thought I needed to set the local system to x86_64-darwin for project compatibility, but it turns out removing this to use the default aarm64-darwin platform 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.nix file, which I can use to correctly build my desired project:

{ pkgs ? import <nixpkgs> {
    # https://nixos.org/manual/nixpkgs/stable/#sec-cross-usage
    crossSystem = (import <nixpkgs/lib>).systems.examples.mips-linux-gnu;
  }
}:

pkgs.callPackage
  ({ mkShell
   , gnumake42
   }: mkShell {
    # pulled from https://github.com/n64decomp/sm64#step-1-install-dependencies-1
    depsBuildBuild = [
      gnumake42 # v4.4 breaks the build!
    ];

    shellHook = ''
      # same setup code as before
      # ...
    '';
  })
{ }

Upvotes: 1

Related Questions