Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mips64el support #161158

Merged
merged 9 commits into from Mar 17, 2022
3 changes: 2 additions & 1 deletion lib/systems/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ rec {
else if final.isAarch64 then "arm64"
else if final.isx86_32 then "i386"
else if final.isx86_64 then "x86_64"
else if final.isMips then "mips"
else if final.isMips32 then "mips"
else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64
else if final.isPower then "powerpc"
else if final.isRiscV then "riscv"
else if final.isS390 then "s390"
Expand Down
8 changes: 6 additions & 2 deletions lib/systems/doubles.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let

# Linux
"aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux"
"armv7l-linux" "i686-linux" "m68k-linux" "mipsel-linux"
"armv7l-linux" "i686-linux" "m68k-linux" "mipsel-linux" "mips64el-linux"
"powerpc64-linux" "powerpc64le-linux" "riscv32-linux"
"riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux"

Expand Down Expand Up @@ -87,7 +87,11 @@ in {
darwin = filterDoubles predicates.isDarwin;
freebsd = filterDoubles predicates.isFreeBSD;
# Should be better, but MinGW is unclear.
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; });
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabin32; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabi64; });
illumos = filterDoubles predicates.isSunOS;
linux = filterDoubles predicates.isLinux;
netbsd = filterDoubles predicates.isNetBSD;
Expand Down
20 changes: 20 additions & 0 deletions lib/systems/examples.nix
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,26 @@ rec {
config = "mipsel-unknown-linux-gnu";
} // platforms.fuloong2f_n32;

# MIPS ABI table transcribed from here: https://wiki.debian.org/Multiarch/Tuples

# can execute on 32bit chip
mips-linux-gnu = { config = "mips-linux-gnu"; } // platforms.gcc_mips32r2_o32;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This this been tested at all? This does not even build stdenv: https://hercules-ci.com/github/nix-community/cross-toolchains.nix/jobs/76

Copy link
Author

@ghost ghost Mar 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not yet supported, nor are they expected to evaluate. From the commit message,

MIPS has a large space of {architecture,abi,endianness}; this commit
adds all of them to lib/systems/platforms.nix so we can be done with
it.

As the one-line comment at the top of the block you quoted says, it is just a transcription of the table which enumerates all the possible {cpumode,kernel,libc,abi} combinations, since mips has a lot of them -- probably more than any other architecture. I transcribed the table to (a) discourage future platform support from cooking up custom nonstandard platform names and (b) show people that no, there isn't an infinite neverending list of combinations; there is a limit to how many of these we will ever care about.

Right now only mips64el-linux-{gnu,musl}abi64 works 100% (after all my PRs get merged). Support for gnuabin32 is very close; as soon as boost-context adds support for it we will have that too.

https://hercules-ci.com/github/nix-community/cross-toolchains.nix/jobs/76

It seems that you've disabled JavaScript on this page. The Hercules CI dashboard needs that though.

😝

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the point of having a tuple that cannot be tested in any way. This sounds like deadcode to me. It should have at least a comment stating that it cannot be used yet.

Copy link
Author

@ghost ghost Mar 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are approximately 182 licenses in licenses.nix, none of which can be "tested in an way". This is okay, because they aren't code; they are us teaching Nix about a list of things that exists outside of Nix, in the real world.

The mips 5-tuple table is the same thing, but for chips instead of legal documents. Don't think of it as executable code.

Sorry to see that you got frustrated so quickly with trying to add a new platform to nixpkgs. I found the experience very frustrating too.

I have also found the nixpkgs process for merging complex, multi-commit changes (like the mips64el support) to be frustrating. Reviewers very sensibly ask that large and complex changes be broken up into multiple PRs, but GitHub totally fails to provide any kind of dependency tracking between bugs (kerneldev workflow handles this easily with the notion of a "patch series"). Since PRs that do not evaluate correctly tend to not attract any reviews, the person submitting a complex multi-PR changeset basically has to wait for each one to be merged before submitting the next. Technically you can submit them all at once, like I did, but there is no benefit to doing this when you have a long dependency chain: only the first PR in the chain will evaluate correctly; the rest will not evaluate (due to the earlier one not yet being merged), so those will fail CI and not be reviewed until the early one gets merged and then the submitter goes back and manually rebases all the later PRs. So really there is no benefit to submitting them all at once. The submitter has to manually submit them one at a time, waiting for each to merge before submitting the next.

To summarize: I think the nixpkgs-github process is optimized to attract "drive-by" one-liner patches from casual contributors (like my systemdSupport patches), at the cost of not being able to cope well with large/complex changes (like my mips64el patches) and refactorings (like cleaning up lib/systems/, which is... tangled) other than simple treewide search-and-replace. I'm sure that optimizing for casual contributions helped nixpkgs grow fast early on, but it doesn't seem to be a good way to limit the growth of "technical debt".

Anyways, because of the headaches of managing not-yet-fully-merged multi-PR submissions, I'm going to wait for all of my mips64el work (#161162, #163401, #161159, #164835, #161925, #161163) to get fully merged all the way to master, and bootstrap tarballs to be built, before I start working on any other mips tuples. When I do, the little-endian ones will be first; in order to test big-endian I'll need to set up another machine with a big-endian kernel.

PS, an orthogonal problem is that PRs that deal with changes to non-package nix files (like lib/*) don't have a meta.maintainer who is obligated to review the PR, or at least give an idea of when they might get around to doing that. So they kinda just sit around, with the massively-overburdened "default code owners" (edolstra, alyssais, etc) assigned. I bet they get hundreds of review auto-requests per day, which is totally not scalable.

mipsel-linux-gnu = { config = "mipsel-linux-gnu"; } // platforms.gcc_mips32r2_o32;
mipsisa32r6-linux-gnu = { config = "mipsisa32r6-linux-gnu"; } // platforms.gcc_mips32r6_o32;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also does not evaluate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

Copy link
Author

@ghost ghost Mar 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an aside, it would be really nice if nixpkgs had a template "here's what you need to fill in to add support for a new platform", all in one file. Right now it's scattered all over the place in lib/systems/ (and elsewhere too). There are a ton of other support fragments (like 8-bit AVR!!!)

Having everything needed to support each new platform in one place for each platform would make it obvious whether things like this are checked in because we were transcribing some table for the benefit of future platforms-to-be-added, or whether full platform support is intended. In the former case there would be missing entries (attrsets with a null or abort value, and probably a leftover comment from the template that says FIXME).

A nixpkgs "platform" is really just a gnu 5-tuple (cpumode, vendor, kernel, libc, abi). Everything else (kernel flags, compiler optimization flags, etc) are site-specific customizations which don't affect interoperatbility between different derivations.

I would advise eliminating any distinction between "platform" and "gnu 5-tuple".

Then, for a given product that can be bought (say, a Raspberry Pi), all you need to know is which kernel to use. Kernel configuration is much more complex than just a 5-tuple, and will vary among different devices which belong to the same 5-tuple. Any given kernel will support some set of one or more 5-tuples (for example, amd64 machines can run i386-linux-gnu userspaces as well as x86_64-linux-musl userspaces), and a nixpkgs kernel expression ought to be able to enumerate (or at least predicate) which 5-tuples it supports.

Once you've picked your hardware and picked your kernel, you pick your platform from those which your chosen kernel supports, then you're done.

mipsisa32r6el-linux-gnu = { config = "mipsisa32r6el-linux-gnu"; } // platforms.gcc_mips32r6_o32;

# require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
mips64-linux-gnuabin32 = { config = "mips64-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
mips64el-linux-gnuabin32 = { config = "mips64el-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
mipsisa64r6-linux-gnuabin32 = { config = "mipsisa64r6-linux-gnuabin32"; } // platforms.gcc_mips64r6_n32;
mipsisa64r6el-linux-gnuabin32 = { config = "mipsisa64r6el-linux-gnuabin32"; } // platforms.gcc_mips64r6_n32;

# 64bit pointers
mips64-linux-gnuabi64 = { config = "mips64-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
mips64el-linux-gnuabi64 = { config = "mips64el-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
mipsisa64r6-linux-gnuabi64 = { config = "mipsisa64r6-linux-gnuabi64"; } // platforms.gcc_mips64r6_64;
mipsisa64r6el-linux-gnuabi64 = { config = "mipsisa64r6el-linux-gnuabi64"; } // platforms.gcc_mips64r6_64;

muslpi = raspberryPi // {
config = "armv6l-unknown-linux-musleabihf";
};
Expand Down
6 changes: 5 additions & 1 deletion lib/systems/inspect.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ rec {
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
isMips = { cpu = { family = "mips"; }; };
isMips32 = { cpu = { family = "mips"; bits = 32; }; };
isMips64 = { cpu = { family = "mips"; bits = 64; }; };
isMips64n32 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
isMmix = { cpu = { family = "mmix"; }; };
isRiscV = { cpu = { family = "riscv"; }; };
isSparc = { cpu = { family = "sparc"; }; };
Expand Down Expand Up @@ -57,7 +61,7 @@ rec {

isAndroid = [ { abi = abis.android; } { abi = abis.androideabi; } ];
isGnu = with abis; map (a: { abi = a; }) [ gnuabi64 gnu gnueabi gnueabihf ];
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf ];
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];

isEfi = map (family: { cpu.family = family; })
Expand Down
7 changes: 7 additions & 0 deletions lib/systems/parse.nix
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,13 @@ rec {
];
};
gnuabi64 = { abi = "64"; };
muslabi64 = { abi = "64"; };

# NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
# It is basically the 64-bit abi with 32-bit pointers. Details:
# https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
gnuabin32 = { abi = "n32"; };
muslabin32 = { abi = "n32"; };

musleabi = { float = "soft"; };
musleabihf = { float = "hard"; };
Expand Down
47 changes: 47 additions & 0 deletions lib/systems/platforms.nix
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Note: lib/systems/default.nix takes care of producing valid,
# fully-formed "platform" values (e.g. hostPlatform, buildPlatform,
# targetPlatform, etc) containing at least the minimal set of attrs
# required (see types.parsedPlatform in lib/systems/parse.nix). This
# file takes an already-valid platform and further elaborates it with
# optional fields such as linux-kernel, gcc, etc.

{ lib }:
rec {
pc = {
Expand Down Expand Up @@ -482,6 +489,43 @@ rec {
};
};

# can execute on 32bit chip
gcc_mips32r2_o32 = { gcc = { arch = "mips32r2"; abi = "o32"; }; };
gcc_mips32r6_o32 = { gcc = { arch = "mips32r6"; abi = "o32"; }; };
gcc_mips64r2_n32 = { gcc = { arch = "mips64r2"; abi = "n32"; }; };
gcc_mips64r6_n32 = { gcc = { arch = "mips64r6"; abi = "n32"; }; };
gcc_mips64r2_64 = { gcc = { arch = "mips64r2"; abi = "64"; }; };
gcc_mips64r6_64 = { gcc = { arch = "mips64r6"; abi = "64"; }; };

# based on:
# https://www.mail-archive.com/qemu-discuss@nongnu.org/msg05179.html
# https://gmplib.org/~tege/qemu.html#mips64-debian
mips64el-qemu-linux-gnuabi64 = (import ./examples).mips64el-linux-gnuabi64 // {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import ./examples should be import ./examples.nix { inherit lib; }
#168784

linux-kernel = {
name = "mips64el";
baseConfig = "64r2el_defconfig";
target = "vmlinuz";
autoModules = false;
DTB = true;
# for qemu 9p passthrough filesystem
extraConfig = ''
MIPS_MALTA y
PAGE_SIZE_4KB y
CPU_LITTLE_ENDIAN y
CPU_MIPS64_R2 y
64BIT y
CPU_MIPS64_R2 y

NET_9P y
NET_9P_VIRTIO y
9P_FS y
9P_FS_POSIX_ACL y
PCI y
VIRTIO_PCI y
'';
};
};

##
## Other
##
Expand All @@ -499,6 +543,9 @@ rec {
};
};

# This function takes a minimally-valid "platform" and returns an
# attrset containing zero or more additional attrs which should be
# included in the platform in order to further elaborate it.
select = platform:
# x86
/**/ if platform.isx86 then pc
Expand Down
4 changes: 2 additions & 2 deletions lib/tests/systems.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ with lib.systems.doubles; lib.runTests {

testarm = mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv6l-netbsd" "armv6l-none" "armv7a-linux" "armv7a-netbsd" "armv7l-linux" "armv7l-netbsd" "arm-none" "armv7a-darwin" ];
testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-genode" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
testmips = mseteq mips [ "mipsel-linux" "mipsel-netbsd" ];
testmips = mseteq mips [ "mips64el-linux" "mipsel-linux" "mipsel-netbsd" ];
testmmix = mseteq mmix [ "mmix-mmixware" ];
testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-genode" "x86_64-redox" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" "x86_64-none" ];

Expand All @@ -28,7 +28,7 @@ with lib.systems.doubles; lib.runTests {
testredox = mseteq redox [ "x86_64-redox" ];
testgnu = mseteq gnu (linux /* ++ kfreebsd ++ ... */);
testillumos = mseteq illumos [ "x86_64-solaris" ];
testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux" "armv7l-linux" "i686-linux" "mipsel-linux" "riscv32-linux" "riscv64-linux" "x86_64-linux" "powerpc64-linux" "powerpc64le-linux" "m68k-linux" "s390-linux" "s390x-linux" ];
testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux" "armv7l-linux" "i686-linux" "mips64el-linux" "mipsel-linux" "riscv32-linux" "riscv64-linux" "x86_64-linux" "powerpc64-linux" "powerpc64le-linux" "m68k-linux" "s390-linux" "s390x-linux" ];
testnetbsd = mseteq netbsd [ "aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd" "i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd" "riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd" ];
testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ];
testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "i686-windows" "x86_64-windows" ];
Expand Down
1 change: 1 addition & 0 deletions pkgs/stdenv/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ in
armv8m-linux = stagesLinux;
aarch64-linux = stagesLinux;
mipsel-linux = stagesLinux;
mips64el-linux = stagesLinux;
powerpc-linux = /* stagesLinux */ stagesNative;
powerpc64-linux = stagesLinux;
powerpc64le-linux = stagesLinux;
Expand Down
2 changes: 2 additions & 0 deletions pkgs/stdenv/linux/make-bootstrap-tools-cross.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ in lib.mapAttrs (n: make) (with lib.systems.examples; {
armv6l-musl = muslpi;
aarch64-musl = aarch64-multiplatform-musl;
riscv64 = riscv64;
mips64el-linux-gnuabin32 = lib.systems.platforms.mips64el-linux-gnuabin32;
mips64el-linux-gnuabi64 = lib.systems.platforms.mips64el-linux-gnuabi64;
powerpc64 = ppc64;
powerpc64-musl = ppc64-musl;
powerpc64le = powernv;
Expand Down
44 changes: 26 additions & 18 deletions pkgs/top-level/stage.nix
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,30 @@
} @args:

let
# This is a function from parsed platforms (like
# stdenv.hostPlatform.parsed) to parsed platforms.
makeMuslParsedPlatform = parsed:
# The following line guarantees that the output of this function
# is a well-formed platform with no missing fields. It will be
# uncommented in a separate PR, in case it breaks the build.
#(x: lib.trivial.pipe x [ (x: builtins.removeAttrs x [ "_type" ]) lib.systems.parse.mkSystem ])
(parsed // {
abi = {
gnu = lib.systems.parse.abis.musl;
gnueabi = lib.systems.parse.abis.musleabi;
gnueabihf = lib.systems.parse.abis.musleabihf;
gnuabin32 = lib.systems.parse.abis.muslabin32;
gnuabi64 = lib.systems.parse.abis.muslabi64;
# The following two entries ensure that this function is idempotent.
musleabi = lib.systems.parse.abis.musleabi;
musleabihf = lib.systems.parse.abis.musleabihf;
muslabin32 = lib.systems.parse.abis.muslabin32;
muslabi64 = lib.systems.parse.abis.muslabi64;
}.${parsed.abi.name}
or lib.systems.parse.abis.musl;
});


stdenvAdapters = self: super:
let
res = import ../stdenv/adapters.nix {
Expand Down Expand Up @@ -188,14 +212,7 @@ let
})] ++ overlays;
${if stdenv.hostPlatform == stdenv.buildPlatform
then "localSystem" else "crossSystem"} = {
parsed = stdenv.hostPlatform.parsed // {
abi = {
gnu = lib.systems.parse.abis.musl;
gnueabi = lib.systems.parse.abis.musleabi;
gnueabihf = lib.systems.parse.abis.musleabihf;
}.${stdenv.hostPlatform.parsed.abi.name}
or lib.systems.parse.abis.musl;
};
parsed = makeMuslParsedPlatform stdenv.hostPlatform.parsed;
};
} else throw "Musl libc only supports Linux systems.";

Expand Down Expand Up @@ -239,16 +256,7 @@ let
} // lib.optionalAttrs stdenv.hostPlatform.isLinux {
crossSystem = {
isStatic = true;
parsed = stdenv.hostPlatform.parsed // {
abi = {
gnu = lib.systems.parse.abis.musl;
gnueabi = lib.systems.parse.abis.musleabi;
gnueabihf = lib.systems.parse.abis.musleabihf;
musleabi = lib.systems.parse.abis.musleabi;
musleabihf = lib.systems.parse.abis.musleabihf;
}.${stdenv.hostPlatform.parsed.abi.name}
or lib.systems.parse.abis.musl;
};
parsed = makeMuslParsedPlatform stdenv.hostPlatform.parsed;
} // lib.optionalAttrs (stdenv.hostPlatform.system == "powerpc64-linux") {
gcc.abi = "elfv2";
};
Expand Down