DBCDK / morph

NixOS deployment tool
MIT License
839 stars 63 forks source link

New per-machine nixpkgs merge causes pkgs.pkgsi686Linux to be cross-compiled #68

Closed delroth closed 5 years ago

delroth commented 5 years ago

Test case: have a morph deployment with hardware.pulseaudio.support32Bit = true;

Current behavior: morph build cross-builds half of the world to build an i686 alsa-plugins package.

Expected behavior: morph build does not do that, which is what happens with e.g. nixops or nixos-rebuild on the same config.

Can be easily repro'd by trying to build anything from pkgsi686Linux:

nix-build eval-machines.nix -A 'nodes.mynode.pkgs.pkgsi686Linux.alsaPlugins' --arg networkExpr mynet.nix

cc @Shados

Shados commented 5 years ago

Ugh, I dug into this one and figured it out, it's a result of the definition of pkgsi686Linux (and also at least pkgsMusl) implicitly relying on nixpkgs being imported impurely (via pkgs/top-level/impure.nix rather than pkgs/top-level/default.nix). morph does import it impurely, but explicitly setting crossSystem breaks a subtle assumption later in nixpkgs' eval machinery.

Details: In the definition of pkgsi686Linux, it uses a check on stdenv.hostPlatform and stdenv.buildPlatform being equivalent (which is typically the case), in order to determine whether to override localSystem or crossSystem in its call to nixpkgsFun (which hackily re-evaluates nixpkgs). https://github.com/NixOS/nixpkgs/blob/e82b8fa7f7890e819bb4de6ce1f8e20d5a5a4c53/pkgs/top-level/stage.nix#L183-L193

The issue here is that it leaves the other localSystem/crossSystem argument unset, and if you look at the definition of that here you can see that means it defaults to whatever arguments were given to nixpkgs originally and captured by the args @-pattern.

Normally, crossSystem is defaulted to null when evaluating nixpkgs impurely, then that null is passed on to the pure eval machinery, within which it is defaulted to localSystem if it is null.

See the problem? If we let the impure null crossSystem default be passed on to the pure eval machinery, then nixpkgsFun captures that null, and i686PkgsLinux later re-evaluates nixpkgs with a modifed localSystem and a null crossSystem, which then results in crossSystem within that re-eval being defaulted to the modified localSystem.

If instead we explicitly pass crossSystem originally, then crossSystem within the i686PkgsLinux re-eval is not defaulted to the i686 localSystem, and cross-compiling occurs.

We could "fix" this in morph by changing the nixpkgs.pkgs default to only set crossSystem if localSystem != crossSystem, and we probably should... but I also think this represents an upstream bug in nixpkgs. pkgsi686Linux shouldn't be reliant on a detail of how the default impure nixpkgs eval expression functions =/.