Merge pull request #4543 from obsidiansystems/indexed-store-path-outputs
Low level `<drvPath>^<outputName>` installable syntax to match existing `<highLevelInstallable>^<outputNames>` syntax
This commit is contained in:
commit
e2a4e7aecd
6 changed files with 137 additions and 38 deletions
|
@ -14,3 +14,14 @@
|
||||||
'<nixpkgs>' -A hello
|
'<nixpkgs>' -A hello
|
||||||
# NIX_PATH=nixpkgs=flake:nixpkgs nix-build '<nixpkgs>' -A hello
|
# NIX_PATH=nixpkgs=flake:nixpkgs nix-build '<nixpkgs>' -A hello
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables.
|
||||||
|
For example,
|
||||||
|
```shell-session
|
||||||
|
$ nix-build /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^dev`
|
||||||
|
```
|
||||||
|
now works just as
|
||||||
|
```shell-session
|
||||||
|
$ nix-build glibc^dev`
|
||||||
|
```
|
||||||
|
does already.
|
||||||
|
|
|
@ -399,44 +399,56 @@ static StorePath getDeriver(
|
||||||
struct InstallableStorePath : Installable
|
struct InstallableStorePath : Installable
|
||||||
{
|
{
|
||||||
ref<Store> store;
|
ref<Store> store;
|
||||||
StorePath storePath;
|
DerivedPath req;
|
||||||
|
|
||||||
InstallableStorePath(ref<Store> store, StorePath && storePath)
|
InstallableStorePath(ref<Store> store, StorePath && storePath)
|
||||||
: store(store), storePath(std::move(storePath)) { }
|
: store(store),
|
||||||
|
req(storePath.isDerivation()
|
||||||
|
? (DerivedPath) DerivedPath::Built {
|
||||||
|
.drvPath = std::move(storePath),
|
||||||
|
.outputs = {},
|
||||||
|
}
|
||||||
|
: (DerivedPath) DerivedPath::Opaque {
|
||||||
|
.path = std::move(storePath),
|
||||||
|
})
|
||||||
|
{ }
|
||||||
|
|
||||||
std::string what() const override { return store->printStorePath(storePath); }
|
InstallableStorePath(ref<Store> store, DerivedPath && req)
|
||||||
|
: store(store), req(std::move(req))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::string what() const override
|
||||||
|
{
|
||||||
|
return req.to_string(*store);
|
||||||
|
}
|
||||||
|
|
||||||
DerivedPaths toDerivedPaths() override
|
DerivedPaths toDerivedPaths() override
|
||||||
{
|
{
|
||||||
if (storePath.isDerivation()) {
|
return { req };
|
||||||
auto drv = store->readDerivation(storePath);
|
|
||||||
return {
|
|
||||||
DerivedPath::Built {
|
|
||||||
.drvPath = storePath,
|
|
||||||
.outputs = drv.outputNames(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
DerivedPath::Opaque {
|
|
||||||
.path = storePath,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePathSet toDrvPaths(ref<Store> store) override
|
StorePathSet toDrvPaths(ref<Store> store) override
|
||||||
{
|
{
|
||||||
if (storePath.isDerivation()) {
|
return std::visit(overloaded {
|
||||||
return {storePath};
|
[&](const DerivedPath::Built & bfd) -> StorePathSet {
|
||||||
} else {
|
return { bfd.drvPath };
|
||||||
return {getDeriver(store, *this, storePath)};
|
},
|
||||||
}
|
[&](const DerivedPath::Opaque & bo) -> StorePathSet {
|
||||||
|
return { getDeriver(store, *this, bo.path) };
|
||||||
|
},
|
||||||
|
}, req.raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<StorePath> getStorePath() override
|
std::optional<StorePath> getStorePath() override
|
||||||
{
|
{
|
||||||
return storePath;
|
return std::visit(overloaded {
|
||||||
|
[&](const DerivedPath::Built & bfd) {
|
||||||
|
return bfd.drvPath;
|
||||||
|
},
|
||||||
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
|
return bo.path;
|
||||||
|
},
|
||||||
|
}, req.raw());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -803,7 +815,22 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
for (auto & s : ss) {
|
for (auto & s : ss) {
|
||||||
std::exception_ptr ex;
|
std::exception_ptr ex;
|
||||||
|
|
||||||
if (s.find('/') != std::string::npos) {
|
auto found = s.rfind('^');
|
||||||
|
if (found != std::string::npos) {
|
||||||
|
try {
|
||||||
|
result.push_back(std::make_shared<InstallableStorePath>(
|
||||||
|
store,
|
||||||
|
DerivedPath::Built::parse(*store, s.substr(0, found), s.substr(found + 1))));
|
||||||
|
continue;
|
||||||
|
} catch (BadStorePath &) {
|
||||||
|
} catch (...) {
|
||||||
|
if (!ex)
|
||||||
|
ex = std::current_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found = s.find('/');
|
||||||
|
if (found != std::string::npos) {
|
||||||
try {
|
try {
|
||||||
result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
|
result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -78,15 +78,16 @@ DerivedPath::Opaque DerivedPath::Opaque::parse(const Store & store, std::string_
|
||||||
return {store.parseStorePath(s)};
|
return {store.parseStorePath(s)};
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view s)
|
DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view drvS, std::string_view outputsS)
|
||||||
{
|
{
|
||||||
size_t n = s.find("!");
|
auto drvPath = store.parseStorePath(drvS);
|
||||||
assert(n != s.npos);
|
|
||||||
auto drvPath = store.parseStorePath(s.substr(0, n));
|
|
||||||
auto outputsS = s.substr(n + 1);
|
|
||||||
std::set<std::string> outputs;
|
std::set<std::string> outputs;
|
||||||
if (outputsS != "*")
|
if (outputsS != "*") {
|
||||||
outputs = tokenizeString<std::set<std::string>>(outputsS, ",");
|
outputs = tokenizeString<std::set<std::string>>(outputsS, ",");
|
||||||
|
if (outputs.empty())
|
||||||
|
throw Error(
|
||||||
|
"Explicit list of wanted outputs '%s' must not be empty. Consider using '*' as a wildcard meaning all outputs if no output in particular is wanted.", outputsS);
|
||||||
|
}
|
||||||
return {drvPath, outputs};
|
return {drvPath, outputs};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
||||||
size_t n = s.find("!");
|
size_t n = s.find("!");
|
||||||
return n == s.npos
|
return n == s.npos
|
||||||
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
|
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
|
||||||
: (DerivedPath) DerivedPath::Built::parse(store, s);
|
: (DerivedPath) DerivedPath::Built::parse(store, s.substr(0, n), s.substr(n + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||||
|
|
|
@ -47,7 +47,7 @@ struct DerivedPathBuilt {
|
||||||
std::set<std::string> outputs;
|
std::set<std::string> outputs;
|
||||||
|
|
||||||
std::string to_string(const Store & store) const;
|
std::string to_string(const Store & store) const;
|
||||||
static DerivedPathBuilt parse(const Store & store, std::string_view);
|
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
||||||
nlohmann::json toJSON(ref<Store> store) const;
|
nlohmann::json toJSON(ref<Store> store) const;
|
||||||
|
|
||||||
bool operator < (const DerivedPathBuilt & b) const
|
bool operator < (const DerivedPathBuilt & b) const
|
||||||
|
|
|
@ -164,6 +164,13 @@ operate are determined as follows:
|
||||||
…
|
…
|
||||||
```
|
```
|
||||||
|
|
||||||
|
and likewise, using a store path to a "drv" file to specify the derivation:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix build '/nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^dev,static'
|
||||||
|
…
|
||||||
|
```
|
||||||
|
|
||||||
* You can also specify that *all* outputs should be used using the
|
* You can also specify that *all* outputs should be used using the
|
||||||
syntax *installable*`^*`. For example, the following shows the size
|
syntax *installable*`^*`. For example, the following shows the size
|
||||||
of all outputs of the `glibc` package in the binary cache:
|
of all outputs of the `glibc` package in the binary cache:
|
||||||
|
@ -177,6 +184,12 @@ operate are determined as follows:
|
||||||
/nix/store/q6580lr01jpcsqs4r5arlh4ki2c1m9rv-glibc-2.33-123-dev 44200560
|
/nix/store/q6580lr01jpcsqs4r5arlh4ki2c1m9rv-glibc-2.33-123-dev 44200560
|
||||||
```
|
```
|
||||||
|
|
||||||
|
and likewise, using a store path to a "drv" file to specify the derivation:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix path-info -S '/nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^*'
|
||||||
|
…
|
||||||
|
```
|
||||||
* If you didn't specify the desired outputs, but the derivation has an
|
* If you didn't specify the desired outputs, but the derivation has an
|
||||||
attribute `meta.outputsToInstall`, Nix will use those outputs. For
|
attribute `meta.outputsToInstall`, Nix will use those outputs. For
|
||||||
example, since the package `nixpkgs#libxml2` has this attribute:
|
example, since the package `nixpkgs#libxml2` has this attribute:
|
||||||
|
@ -189,6 +202,9 @@ operate are determined as follows:
|
||||||
a command like `nix shell nixpkgs#libxml2` will provide only those
|
a command like `nix shell nixpkgs#libxml2` will provide only those
|
||||||
two outputs by default.
|
two outputs by default.
|
||||||
|
|
||||||
|
Note that a store derivation (given by `.drv` file store path) doesn't have
|
||||||
|
any attributes like `meta`, and thus this case doesn't apply to it.
|
||||||
|
|
||||||
* Otherwise, Nix will use all outputs of the derivation.
|
* Otherwise, Nix will use all outputs of the derivation.
|
||||||
|
|
||||||
# Nix stores
|
# Nix stores
|
||||||
|
|
|
@ -8,13 +8,15 @@ set -o pipefail
|
||||||
nix build -f multiple-outputs.nix --json a b --no-link | jq --exit-status '
|
nix build -f multiple-outputs.nix --json a b --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 |
|
||||||
(.outputs.first | match(".*multiple-outputs-a-first")) and
|
(keys | length == 2) and
|
||||||
(.outputs.second | match(".*multiple-outputs-a-second")))
|
(.first | match(".*multiple-outputs-a-first")) and
|
||||||
|
(.second | match(".*multiple-outputs-a-second"))))
|
||||||
and (.[1] |
|
and (.[1] |
|
||||||
(.drvPath | match(".*multiple-outputs-b.drv")) and
|
(.drvPath | match(".*multiple-outputs-b.drv")) and
|
||||||
(.outputs | keys | length == 1) and
|
(.outputs |
|
||||||
(.outputs.out | match(".*multiple-outputs-b")))
|
(keys | length == 1) and
|
||||||
|
(.out | match(".*multiple-outputs-b"))))
|
||||||
'
|
'
|
||||||
|
|
||||||
# Test output selection using the '^' syntax.
|
# Test output selection using the '^' syntax.
|
||||||
|
@ -56,6 +58,48 @@ nix build -f multiple-outputs.nix --json 'e^*' --no-link | jq --exit-status '
|
||||||
(.outputs | keys == ["a", "b", "c"]))
|
(.outputs | keys == ["a", "b", "c"]))
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# Test building from raw store path to drv not expression.
|
||||||
|
|
||||||
|
drv=$(nix eval -f multiple-outputs.nix --raw a.drvPath)
|
||||||
|
if nix build "$drv^not-an-output" --no-link --json; then
|
||||||
|
fail "'not-an-output' should fail to build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if nix build "$drv^" --no-link --json; then
|
||||||
|
fail "'empty outputs list' should fail to build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if nix build "$drv^*nope" --no-link --json; then
|
||||||
|
fail "'* must be entire string' should fail to build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
nix build "$drv^first" --no-link --json | jq --exit-status '
|
||||||
|
(.[0] |
|
||||||
|
(.drvPath | match(".*multiple-outputs-a.drv")) and
|
||||||
|
(.outputs |
|
||||||
|
(keys | length == 1) and
|
||||||
|
(.first | match(".*multiple-outputs-a-first")) and
|
||||||
|
(has("second") | not)))
|
||||||
|
'
|
||||||
|
|
||||||
|
nix build "$drv^first,second" --no-link --json | jq --exit-status '
|
||||||
|
(.[0] |
|
||||||
|
(.drvPath | match(".*multiple-outputs-a.drv")) and
|
||||||
|
(.outputs |
|
||||||
|
(keys | length == 2) and
|
||||||
|
(.first | match(".*multiple-outputs-a-first")) and
|
||||||
|
(.second | match(".*multiple-outputs-a-second"))))
|
||||||
|
'
|
||||||
|
|
||||||
|
nix build "$drv^*" --no-link --json | jq --exit-status '
|
||||||
|
(.[0] |
|
||||||
|
(.drvPath | match(".*multiple-outputs-a.drv")) and
|
||||||
|
(.outputs |
|
||||||
|
(keys | length == 2) and
|
||||||
|
(.first | match(".*multiple-outputs-a-first")) and
|
||||||
|
(.second | match(".*multiple-outputs-a-second"))))
|
||||||
|
'
|
||||||
|
|
||||||
# Make sure that `--impure` works (regression test for https://github.com/NixOS/nix/issues/6488)
|
# Make sure that `--impure` works (regression test for https://github.com/NixOS/nix/issues/6488)
|
||||||
nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status '
|
nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status '
|
||||||
(.[0] |
|
(.[0] |
|
||||||
|
|
Loading…
Reference in a new issue