Merge pull request #6426 from edolstra/respect-outputs-to-install

nix: Respect meta.outputsToInstall, and use all outputs by default
This commit is contained in:
Eelco Dolstra 2022-05-03 13:43:22 +02:00 committed by GitHub
commit 404c222444
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 105 additions and 31 deletions

View file

@ -440,10 +440,8 @@ DerivedPaths InstallableValue::toDerivedPaths()
// Group by derivation, helps with .all in particular // Group by derivation, helps with .all in particular
for (auto & drv : toDerivations()) { for (auto & drv : toDerivations()) {
auto outputName = drv.outputName; for (auto & outputName : drv.outputsToInstall)
if (outputName == "") drvsToOutputs[drv.drvPath].insert(outputName);
throw Error("derivation '%s' lacks an 'outputName' attribute", state->store->printStorePath(drv.drvPath));
drvsToOutputs[drv.drvPath].insert(outputName);
drvsToCopy.insert(drv.drvPath); drvsToCopy.insert(drv.drvPath);
} }
@ -497,7 +495,13 @@ std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations
auto drvPath = drvInfo.queryDrvPath(); auto drvPath = drvInfo.queryDrvPath();
if (!drvPath) if (!drvPath)
throw Error("'%s' is not a derivation", what()); throw Error("'%s' is not a derivation", what());
res.push_back({ *drvPath, drvInfo.queryOutputName() }); std::set<std::string> outputsToInstall;
for (auto & output : drvInfo.queryOutputs(false, true))
outputsToInstall.insert(output.first);
res.push_back(DerivationInfo {
.drvPath = *drvPath,
.outputsToInstall = std::move(outputsToInstall)
});
} }
return res; return res;
@ -598,9 +602,24 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
auto drvPath = attr->forceDerivation(); auto drvPath = attr->forceDerivation();
std::set<std::string> outputsToInstall;
if (auto aMeta = attr->maybeGetAttr(state->sMeta))
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
if (outputsToInstall.empty())
if (auto aOutputs = attr->maybeGetAttr(state->sOutputs))
for (auto & s : aOutputs->getListOfStrings())
outputsToInstall.insert(s);
if (outputsToInstall.empty())
outputsToInstall.insert("out");
auto drvInfo = DerivationInfo { auto drvInfo = DerivationInfo {
std::move(drvPath), .drvPath = std::move(drvPath),
attr->getAttr(state->sOutputName)->getString() .outputsToInstall = std::move(outputsToInstall),
}; };
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)}; return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};

View file

@ -141,7 +141,7 @@ struct InstallableValue : Installable
struct DerivationInfo struct DerivationInfo
{ {
StorePath drvPath; StorePath drvPath;
std::string outputName; std::set<std::string> outputsToInstall;
}; };
virtual std::vector<DerivationInfo> toDerivations() = 0; virtual std::vector<DerivationInfo> toDerivations() = 0;

View file

@ -47,7 +47,7 @@ struct AttrDb
{ {
auto state(_state->lock()); auto state(_state->lock());
Path cacheDir = getCacheDir() + "/nix/eval-cache-v2"; Path cacheDir = getCacheDir() + "/nix/eval-cache-v3";
createDirs(cacheDir); createDirs(cacheDir);
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
@ -175,6 +175,24 @@ struct AttrDb
}); });
} }
AttrId setListOfStrings(
AttrKey key,
const std::vector<std::string> & l)
{
return doSQLite([&]()
{
auto state(_state->lock());
state->insertAttribute.use()
(key.first)
(symbols[key.second])
(AttrType::ListOfStrings)
(concatStringsSep("\t", l)).exec();
return state->db.getLastInsertedRowId();
});
}
AttrId setPlaceholder(AttrKey key) AttrId setPlaceholder(AttrKey key)
{ {
return doSQLite([&]() return doSQLite([&]()
@ -269,6 +287,8 @@ struct AttrDb
} }
case AttrType::Bool: case AttrType::Bool:
return {{rowId, queryAttribute.getInt(2) != 0}}; return {{rowId, queryAttribute.getInt(2) != 0}};
case AttrType::ListOfStrings:
return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}};
case AttrType::Missing: case AttrType::Missing:
return {{rowId, missing_t()}}; return {{rowId, missing_t()}};
case AttrType::Misc: case AttrType::Misc:
@ -385,7 +405,7 @@ std::string AttrCursor::getAttrPathStr(Symbol name) const
Value & AttrCursor::forceValue() Value & AttrCursor::forceValue()
{ {
debug("evaluating uncached attribute %s", getAttrPathStr()); debug("evaluating uncached attribute '%s'", getAttrPathStr());
auto & v = getValue(); auto & v = getValue();
@ -601,6 +621,38 @@ bool AttrCursor::getBool()
return v.boolean; return v.boolean;
} }
std::vector<std::string> AttrCursor::getListOfStrings()
{
if (root->db) {
if (!cachedValue)
cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto l = std::get_if<std::vector<std::string>>(&cachedValue->second)) {
debug("using cached list of strings attribute '%s'", getAttrPathStr());
return *l;
} else
throw TypeError("'%s' is not a list of strings", getAttrPathStr());
}
}
debug("evaluating uncached attribute '%s'", getAttrPathStr());
auto & v = getValue();
root->state.forceValue(v, noPos);
if (v.type() != nList)
throw TypeError("'%s' is not a list", getAttrPathStr());
std::vector<std::string> res;
for (auto & elem : v.listItems())
res.push_back(std::string(root->state.forceStringNoCtx(*elem)));
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
return res;
}
std::vector<Symbol> AttrCursor::getAttrs() std::vector<Symbol> AttrCursor::getAttrs()
{ {
if (root->db) { if (root->db) {

View file

@ -44,6 +44,7 @@ enum AttrType {
Misc = 4, Misc = 4,
Failed = 5, Failed = 5,
Bool = 6, Bool = 6,
ListOfStrings = 7,
}; };
struct placeholder_t {}; struct placeholder_t {};
@ -61,7 +62,8 @@ typedef std::variant<
missing_t, missing_t,
misc_t, misc_t,
failed_t, failed_t,
bool bool,
std::vector<std::string>
> AttrValue; > AttrValue;
class AttrCursor : public std::enable_shared_from_this<AttrCursor> class AttrCursor : public std::enable_shared_from_this<AttrCursor>
@ -114,6 +116,8 @@ public:
bool getBool(); bool getBool();
std::vector<std::string> getListOfStrings();
std::vector<Symbol> getAttrs(); std::vector<Symbol> getAttrs();
bool isDerivation(); bool isDerivation();

View file

@ -89,7 +89,7 @@ UnresolvedApp Installable::toApp(EvalState & state)
auto outputName = cursor->getAttr(state.sOutputName)->getString(); auto outputName = cursor->getAttr(state.sOutputName)->getString();
auto name = cursor->getAttr(state.sName)->getString(); auto name = cursor->getAttr(state.sName)->getString();
auto aPname = cursor->maybeGetAttr("pname"); auto aPname = cursor->maybeGetAttr("pname");
auto aMeta = cursor->maybeGetAttr("meta"); auto aMeta = cursor->maybeGetAttr(state.sMeta);
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr; auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
auto mainProgram = auto mainProgram =
aMainProgram aMainProgram

View file

@ -1015,8 +1015,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
auto name = visitor.getAttr(state->sName)->getString(); auto name = visitor.getAttr(state->sName)->getString();
if (json) { if (json) {
std::optional<std::string> description; std::optional<std::string> description;
if (auto aMeta = visitor.maybeGetAttr("meta")) { if (auto aMeta = visitor.maybeGetAttr(state->sMeta)) {
if (auto aDescription = aMeta->maybeGetAttr("description")) if (auto aDescription = aMeta->maybeGetAttr(state->sDescription))
description = aDescription->getString(); description = aDescription->getString();
} }
j.emplace("type", "derivation"); j.emplace("type", "derivation");

View file

@ -67,7 +67,6 @@ struct ProfileElement
ref<Store> store, ref<Store> store,
const BuiltPaths & builtPaths) const BuiltPaths & builtPaths)
{ {
// FIXME: respect meta.outputsToInstall
storePaths.clear(); storePaths.clear();
for (auto & buildable : builtPaths) { for (auto & buildable : builtPaths) {
std::visit(overloaded { std::visit(overloaded {

View file

@ -93,10 +93,10 @@ struct CmdSearch : InstallableCommand, MixJSON
}; };
if (cursor.isDerivation()) { if (cursor.isDerivation()) {
DrvName name(cursor.getAttr("name")->getString()); DrvName name(cursor.getAttr(state->sName)->getString());
auto aMeta = cursor.maybeGetAttr("meta"); auto aMeta = cursor.maybeGetAttr(state->sMeta);
auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr; auto aDescription = aMeta ? aMeta->maybeGetAttr(state->sDescription) : nullptr;
auto description = aDescription ? aDescription->getString() : ""; auto description = aDescription ? aDescription->getString() : "";
std::replace(description.begin(), description.end(), '\n', ' '); std::replace(description.begin(), description.end(), '\n', ' ');
auto attrPath2 = concatStringsSep(".", attrPathS); auto attrPath2 = concatStringsSep(".", attrPathS);

View file

@ -2,15 +2,8 @@ source common.sh
clearStore clearStore
# Make sure that 'nix build' only returns the outputs we asked for. # Make sure that 'nix build' returns all outputs by default.
nix build -f multiple-outputs.nix --json a --no-link | jq --exit-status ' nix build -f multiple-outputs.nix --json a b --no-link | jq --exit-status '
(.[0] |
(.drvPath | match(".*multiple-outputs-a.drv")) and
(.outputs | keys | length == 1) and
(.outputs.first | match(".*multiple-outputs-a-first")))
'
nix build -f multiple-outputs.nix --json a.all b.all --no-link | jq --exit-status '
(.[0] | (.[0] |
(.drvPath | match(".*multiple-outputs-a.drv")) and (.drvPath | match(".*multiple-outputs-a.drv")) and
(.outputs | keys | length == 2) and (.outputs | keys | length == 2) and

View file

@ -23,7 +23,7 @@ rec {
}; };
rootCA = mkCADerivation { rootCA = mkCADerivation {
name = "rootCA"; name = "rootCA";
outputs = [ "out" "dev" "foo"]; outputs = [ "out" "dev" "foo" ];
buildCommand = '' buildCommand = ''
echo "building a CA derivation" echo "building a CA derivation"
echo "The seed is ${toString seed}" echo "The seed is ${toString seed}"

View file

@ -25,7 +25,8 @@ buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 transi
# Check that the thing weve just substituted has its realisation stored # Check that the thing weve just substituted has its realisation stored
nix realisation info --file ./content-addressed.nix transitivelyDependentCA nix realisation info --file ./content-addressed.nix transitivelyDependentCA
# Check that its dependencies have it too # Check that its dependencies have it too
nix realisation info --file ./content-addressed.nix dependentCA rootCA nix realisation info --file ./content-addressed.nix dependentCA
# nix realisation info --file ./content-addressed.nix rootCA --outputs out
# Same thing, but # Same thing, but
# 1. With non-ca derivations # 1. With non-ca derivations

View file

@ -17,6 +17,7 @@ cat > $flake1Dir/flake.nix <<EOF
outputs = { self }: with import ./config.nix; rec { outputs = { self }: with import ./config.nix; rec {
packages.$system.default = mkDerivation { packages.$system.default = mkDerivation {
name = "profile-test-\${builtins.readFile ./version}"; name = "profile-test-\${builtins.readFile ./version}";
outputs = [ "out" "man" "dev" ];
builder = builtins.toFile "builder.sh" builder = builtins.toFile "builder.sh"
'' ''
mkdir -p \$out/bin mkdir -p \$out/bin
@ -26,10 +27,13 @@ cat > $flake1Dir/flake.nix <<EOF
EOF EOF
chmod +x \$out/bin/hello chmod +x \$out/bin/hello
echo DONE echo DONE
mkdir -p \$man/share/man
mkdir -p \$dev/include
''; '';
__contentAddressed = import ./ca.nix; __contentAddressed = import ./ca.nix;
outputHashMode = "recursive"; outputHashMode = "recursive";
outputHashAlgo = "sha256"; outputHashAlgo = "sha256";
meta.outputsToInstall = [ "out" "man" ];
}; };
}; };
} }
@ -46,6 +50,8 @@ nix-env -f ./user-envs.nix -i foo-1.0
nix profile list | grep '0 - - .*-foo-1.0' nix profile list | grep '0 - - .*-foo-1.0'
nix profile install $flake1Dir -L nix profile install $flake1Dir -L
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
[ -e $TEST_HOME/.nix-profile/share/man ]
(! [ -e $TEST_HOME/.nix-profile/include ])
nix profile history nix profile history
nix profile history | grep "packages.$system.default: ∅ -> 1.0" nix profile history | grep "packages.$system.default: ∅ -> 1.0"
nix profile diff-closures | grep 'env-manifest.nix: ε → ∅' nix profile diff-closures | grep 'env-manifest.nix: ε → ∅'
@ -55,7 +61,7 @@ printf NixOS > $flake1Dir/who
printf 2.0 > $flake1Dir/version printf 2.0 > $flake1Dir/version
nix profile upgrade 1 nix profile upgrade 1
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]] [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]]
nix profile history | grep "packages.$system.default: 1.0 -> 2.0" nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man"
# Test 'history', 'diff-closures'. # Test 'history', 'diff-closures'.
nix profile diff-closures nix profile diff-closures
@ -86,7 +92,7 @@ nix profile wipe-history
printf true > $flake1Dir/ca.nix printf true > $flake1Dir/ca.nix
printf 3.0 > $flake1Dir/version printf 3.0 > $flake1Dir/version
nix profile upgrade 0 nix profile upgrade 0
nix profile history | grep "packages.$system.default: 1.0 -> 3.0" nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 3.0, 3.0-man"
# Test new install of CA package. # Test new install of CA package.
nix profile remove 0 nix profile remove 0