forked from lix-project/lix
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:
commit
404c222444
|
@ -440,9 +440,7 @@ 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 == "")
|
|
||||||
throw Error("derivation '%s' lacks an 'outputName' attribute", state->store->printStorePath(drv.drvPath));
|
|
||||||
drvsToOutputs[drv.drvPath].insert(outputName);
|
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)};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -25,7 +25,8 @@ buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 transi
|
||||||
# Check that the thing we’ve just substituted has its realisation stored
|
# Check that the thing we’ve 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue