Skip to content

Commit

Permalink
Support non-x86_64-linux system types in flakes
Browse files Browse the repository at this point in the history
A command like

  $ nix run nixpkgs#hello

will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:

  packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
    hello = ...;
  });

The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.

Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)

The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
  • Loading branch information
edolstra committed Oct 15, 2019
1 parent 0bc8f16 commit 7d38060
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 61 deletions.
26 changes: 14 additions & 12 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@

systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ];

forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);

# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor = nixpkgs.lib.genAttrs systems (system:
nixpkgsFor = forAllSystems (system:
import nixpkgs {
inherit system;
overlays = [ self.overlay ];
Expand Down Expand Up @@ -427,19 +429,19 @@

};

checks = {
binaryTarball = self.hydraJobs.binaryTarball.x86_64-linux;
perlBindings = self.hydraJobs.perlBindings.x86_64-linux;
};
checks = forAllSystems (system: {
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
});

packages = {
inherit (nixpkgsFor.x86_64-linux) nix;
};
packages = forAllSystems (system: {
inherit (nixpkgsFor.${system}) nix;
});

defaultPackage = self.packages.nix;
defaultPackage = forAllSystems (system: self.packages.${system}.nix);

devShell =
with nixpkgsFor.x86_64-linux;
devShell = forAllSystems (system:
with nixpkgsFor.${system};
with commonDeps pkgs;

stdenv.mkDerivation {
Expand All @@ -461,7 +463,7 @@
PATH=$prefix/bin:$PATH
unset PYTHONPATH
'';
};
});

};
}
17 changes: 2 additions & 15 deletions src/nix/command.hh
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,9 @@ struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions
std::shared_ptr<Installable> parseInstallable(
ref<Store> store, const std::string & installable);

virtual Strings getDefaultFlakeAttrPaths()
{
return {"defaultPackage"};
}
virtual Strings getDefaultFlakeAttrPaths();

virtual Strings getDefaultFlakeAttrPathPrefixes()
{
return {
// As a convenience, look for the attribute in
// 'outputs.packages'.
"packages.",
// As a temporary hack until Nixpkgs is properly converted
// to provide a clean 'packages' set, look in 'legacyPackages'.
"legacyPackages."
};
}
virtual Strings getDefaultFlakeAttrPathPrefixes();
};

enum RealiseMode { Build, NoBuild, DryRun };
Expand Down
80 changes: 61 additions & 19 deletions src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
auto state = getEvalState();
auto flake = resolveFlake();

auto checkSystemName = [&](const std::string & system, const Pos & pos) {
// FIXME: what's the format of "system"?
if (system.find('-') == std::string::npos)
throw Error("'%s' is not a valid system type, at %s", system, pos);
};

auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
auto drvInfo = getDerivation(*state, v, false);
Expand Down Expand Up @@ -374,42 +380,78 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON

if (name == "checks") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
drvPaths.insert(checkDerivation(
name + "." + (std::string) attr.name, *attr.value, *attr.pos));
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos);
for (auto & attr2 : *attr.value->attrs) {
auto drvPath = checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos);
if ((std::string) attr.name == settings.thisSystem.get())
drvPaths.insert(drvPath);
}
}
}

else if (name == "packages") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
checkDerivation(
name + "." + (std::string) attr.name, *attr.value, *attr.pos);
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos);
for (auto & attr2 : *attr.value->attrs)
checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos);
}
}

else if (name == "apps") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
checkApp(
name + "." + (std::string) attr.name, *attr.value, *attr.pos);
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos);
for (auto & attr2 : *attr.value->attrs)
checkApp(
fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos);
}
}

else if (name == "defaultPackage" || name == "devShell")
checkDerivation(name, vOutput, pos);
else if (name == "defaultPackage" || name == "devShell") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
checkDerivation(
fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos);
}
}

else if (name == "defaultApp")
checkApp(name, vOutput, pos);
else if (name == "defaultApp") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
checkApp(
fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos);
}
}

else if (name == "legacyPackages")
// FIXME: do getDerivations?
;
else if (name == "legacyPackages") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
// FIXME: do getDerivations?
}
}

else if (name == "overlay")
checkOverlay(name, vOutput, pos);

else if (name == "overlays") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
checkOverlay(name + "." + (std::string) attr.name,
checkOverlay(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos);
}

Expand All @@ -419,14 +461,14 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
else if (name == "nixosModules") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
checkModule(name + "." + (std::string) attr.name,
checkModule(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos);
}

else if (name == "nixosConfigurations") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
checkNixOSConfiguration(name + "." + (std::string) attr.name,
checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos);
}

Expand Down
17 changes: 17 additions & 0 deletions src/nix/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ SourceExprCommand::SourceExprCommand()
.dest(&file);
}

Strings SourceExprCommand::getDefaultFlakeAttrPaths()
{
return {"defaultPackage." + settings.thisSystem.get()};
}

Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
{
return {
// As a convenience, look for the attribute in
// 'outputs.packages'.
"packages." + settings.thisSystem.get() + ".",
// As a temporary hack until Nixpkgs is properly converted
// to provide a clean 'packages' set, look in 'legacyPackages'.
"legacyPackages." + settings.thisSystem.get() + "."
};
}

ref<EvalState> EvalCommand::getEvalState()
{
if (!evalState) {
Expand Down
4 changes: 2 additions & 2 deletions src/nix/run.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,12 @@ struct CmdApp : InstallableCommand, RunCommon

Strings getDefaultFlakeAttrPaths() override
{
return {"defaultApp"};
return {"defaultApp." + settings.thisSystem.get()};
}

Strings getDefaultFlakeAttrPathPrefixes() override
{
return {"apps."};
return {"apps." + settings.thisSystem.get() + "."};
}

void run(ref<Store> store) override
Expand Down
2 changes: 1 addition & 1 deletion src/nix/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ struct Common : InstallableCommand, MixProfile

Strings getDefaultFlakeAttrPaths() override
{
return {"devShell", "defaultPackage"};
return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()};
}

Path getShellOutPath(ref<Store> store)
Expand Down
24 changes: 12 additions & 12 deletions tests/flakes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ cat > $flake1Dir/flake.nix <<EOF
description = "Bla bla";
outputs = inputs: rec {
packages.foo = import ./simple.nix;
defaultPackage = packages.foo;
packages.$system.foo = import ./simple.nix;
defaultPackage.$system = packages.$system.foo;
};
}
EOF
Expand All @@ -51,7 +51,7 @@ cat > $flake2Dir/flake.nix <<EOF
description = "Fnord";
outputs = { self, flake1 }: rec {
packages.bar = flake1.packages.foo;
packages.$system.bar = flake1.packages.$system.foo;
};
}
EOF
Expand All @@ -66,10 +66,10 @@ cat > $flake3Dir/flake.nix <<EOF
description = "Fnord";
outputs = { self, flake2 }: rec {
packages.xyzzy = flake2.packages.bar;
packages.$system.xyzzy = flake2.packages.$system.bar;
checks = {
xyzzy = packages.xyzzy;
xyzzy = packages.$system.xyzzy;
};
};
}
Expand Down Expand Up @@ -182,8 +182,8 @@ cat > $flake3Dir/flake.nix <<EOF
description = "Fnord";
outputs = { self, flake1, flake2 }: rec {
packages.xyzzy = flake2.packages.bar;
packages."sth sth" = flake1.packages.foo;
packages.$system.xyzzy = flake2.packages.$system.bar;
packages.$system."sth sth" = flake1.packages.$system.foo;
};
}
EOF
Expand Down Expand Up @@ -242,9 +242,9 @@ cat > $flake3Dir/flake.nix <<EOF
description = "Fnord";
outputs = inputs: rec {
packages.xyzzy = inputs.flake2.packages.bar;
packages.sth = inputs.flake1.packages.foo;
packages.fnord =
packages.$system.xyzzy = inputs.flake2.packages.$system.bar;
packages.$system.sth = inputs.flake1.packages.$system.foo;
packages.$system.fnord =
with import ./config.nix;
mkDerivation {
inherit system;
Expand Down Expand Up @@ -307,8 +307,8 @@ cat > $flake3Dir/flake.nix <<EOF
description = "Fnord";
outputs = { self, flake1, flake2, nonFlake }: rec {
packages.sth = flake1.packages.foo;
packages.fnord =
packages.$system.sth = flake1.packages.$system.foo;
packages.$system.fnord =
with import ./config.nix;
mkDerivation {
inherit system;
Expand Down

0 comments on commit 7d38060

Please sign in to comment.