forked from lix-project/lix
Merge remote-tracking branch 'upstream/master' into path-info
This commit is contained in:
commit
43414738a0
20 changed files with 205 additions and 66 deletions
2
Makefile
2
Makefile
|
@ -36,4 +36,4 @@ endif
|
||||||
|
|
||||||
include mk/lib.mk
|
include mk/lib.mk
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++17 -I src
|
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++20 -I src
|
||||||
|
|
|
@ -276,8 +276,11 @@ PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||||
|
|
||||||
# Look for rapidcheck.
|
# Look for rapidcheck.
|
||||||
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
|
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
|
||||||
|
AC_LANG_PUSH(C++)
|
||||||
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
|
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
|
||||||
AC_CHECK_LIB([rapidcheck], [])
|
dnl No good for C++ libs with mangled symbols
|
||||||
|
dnl AC_CHECK_LIB([rapidcheck], [])
|
||||||
|
AC_LANG_POP(C++)
|
||||||
|
|
||||||
|
|
||||||
# Look for nlohmann/json.
|
# Look for nlohmann/json.
|
||||||
|
|
|
@ -633,7 +633,7 @@ written to standard output.
|
||||||
|
|
||||||
A NAR archive is like a TAR or Zip archive, but it contains only the
|
A NAR archive is like a TAR or Zip archive, but it contains only the
|
||||||
information that Nix considers important. For instance, timestamps are
|
information that Nix considers important. For instance, timestamps are
|
||||||
elided because all files in the Nix store have their timestamp set to 0
|
elided because all files in the Nix store have their timestamp set to 1
|
||||||
anyway. Likewise, all permissions are left out except for the execute
|
anyway. Likewise, all permissions are left out except for the execute
|
||||||
bit, because all files in the Nix store have 444 or 555 permission.
|
bit, because all files in the Nix store have 444 or 555 permission.
|
||||||
|
|
||||||
|
|
|
@ -255,3 +255,67 @@ Derivations can declare some infrequently used optional attributes.
|
||||||
> substituted. Thus it is usually a good idea to align `system` with
|
> substituted. Thus it is usually a good idea to align `system` with
|
||||||
> `builtins.currentSystem` when setting `allowSubstitutes` to
|
> `builtins.currentSystem` when setting `allowSubstitutes` to
|
||||||
> `false`. For most trivial derivations this should be the case.
|
> `false`. For most trivial derivations this should be the case.
|
||||||
|
|
||||||
|
- [`__structuredAttrs`]{#adv-attr-structuredAttrs}\
|
||||||
|
If the special attribute `__structuredAttrs` is set to `true`, the other derivation
|
||||||
|
attributes are serialised in JSON format and made available to the
|
||||||
|
builder via the file `.attrs.json` in the builder’s temporary
|
||||||
|
directory. This obviates the need for `passAsFile` since JSON files
|
||||||
|
have no size restrictions, unlike process environments.
|
||||||
|
|
||||||
|
It also makes it possible to tweak derivation settings in a structured way; see
|
||||||
|
[`outputChecks`](#adv-attr-outputChecks) for example.
|
||||||
|
|
||||||
|
As a convenience to Bash builders,
|
||||||
|
Nix writes a script named `.attrs.sh` to the builder’s directory
|
||||||
|
that initialises shell variables corresponding to all attributes
|
||||||
|
that are representable in Bash. This includes non-nested
|
||||||
|
(associative) arrays. For example, the attribute `hardening.format = true`
|
||||||
|
ends up as the Bash associative array element `${hardening[format]}`.
|
||||||
|
|
||||||
|
- [`outputChecks`]{#adv-attr-outputChecks}\
|
||||||
|
When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks`
|
||||||
|
attribute allows defining checks per-output.
|
||||||
|
|
||||||
|
In addition to
|
||||||
|
[`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites),
|
||||||
|
[`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites),
|
||||||
|
the following attributes are available:
|
||||||
|
|
||||||
|
- `maxSize` defines the maximum size of the output path.
|
||||||
|
- `maxClosureSize` defines the maximum size of the output's closure.
|
||||||
|
- `ignoreSelfRefs` controls whether self-references should be considered when
|
||||||
|
checking for allowed references/requisites.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
__structuredAttrs = true;
|
||||||
|
|
||||||
|
outputChecks.out = {
|
||||||
|
# The closure of 'out' must not be larger than 256 MiB.
|
||||||
|
maxClosureSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
|
# It must not refer to the C compiler or to the 'dev' output.
|
||||||
|
disallowedRequisites = [ stdenv.cc "dev" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
outputChecks.dev = {
|
||||||
|
# The 'dev' output must not be larger than 128 KiB.
|
||||||
|
maxSize = 128 * 1024;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
|
||||||
|
When using [structured attributes](#adv-attr-structuredAttrs), the **experimental**
|
||||||
|
attribute `unsafeDiscardReferences` is a per-output boolean which, if set to `true`,
|
||||||
|
disables scanning the build output for runtime dependencies altogether.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
__structuredAttrs = true;
|
||||||
|
unsafeDiscardReferences.out = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful, for example, when generating self-contained filesystem images with
|
||||||
|
their own embedded Nix store: hashes found inside such an image refer
|
||||||
|
to the embedded store and not to the host's Nix store.
|
||||||
|
|
||||||
|
This is only allowed if the `discard-references` experimental feature is enabled.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
makefiles = local.mk
|
makefiles = local.mk
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++17 -I ../src
|
GLOBAL_CXXFLAGS += -g -Wall -std=c++20 -I ../src
|
||||||
|
|
||||||
-include Makefile.config
|
-include Makefile.config
|
||||||
|
|
||||||
|
|
|
@ -379,10 +379,9 @@ Installable::getCursors(EvalState & state)
|
||||||
ref<eval_cache::AttrCursor>
|
ref<eval_cache::AttrCursor>
|
||||||
Installable::getCursor(EvalState & state)
|
Installable::getCursor(EvalState & state)
|
||||||
{
|
{
|
||||||
auto cursors = getCursors(state);
|
/* Although getCursors should return at least one element, in case it doesn't,
|
||||||
if (cursors.empty())
|
bound check to avoid an undefined behavior for vector[0] */
|
||||||
throw Error("cannot find flake attribute '%s'", what());
|
return getCursors(state).at(0);
|
||||||
return cursors[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static StorePath getDeriver(
|
static StorePath getDeriver(
|
||||||
|
@ -696,46 +695,28 @@ InstallableFlake::getCursors(EvalState & state)
|
||||||
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>> res;
|
std::vector<ref<eval_cache::AttrCursor>> res;
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
|
||||||
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
|
||||||
if (attr) res.push_back(ref(*attr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
|
|
||||||
{
|
|
||||||
auto lockedFlake = getLockedFlake();
|
|
||||||
|
|
||||||
auto cache = openEvalCache(state, lockedFlake);
|
|
||||||
auto root = cache->getRoot();
|
|
||||||
|
|
||||||
Suggestions suggestions;
|
Suggestions suggestions;
|
||||||
|
|
||||||
auto attrPaths = getActualAttrPaths();
|
auto attrPaths = getActualAttrPaths();
|
||||||
|
|
||||||
for (auto & attrPath : attrPaths) {
|
for (auto & attrPath : attrPaths) {
|
||||||
debug("trying flake output attribute '%s'", attrPath);
|
debug("trying flake output attribute '%s'", attrPath);
|
||||||
|
|
||||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||||
parseAttrPath(state, attrPath),
|
if (attr) {
|
||||||
true
|
res.push_back(ref(*attr));
|
||||||
);
|
} else {
|
||||||
|
suggestions += attr.getSuggestions();
|
||||||
if (!attrOrSuggestions) {
|
|
||||||
suggestions += attrOrSuggestions.getSuggestions();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return *attrOrSuggestions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Error(
|
if (res.size() == 0)
|
||||||
suggestions,
|
throw Error(
|
||||||
"flake '%s' does not provide attribute %s",
|
suggestions,
|
||||||
flakeRef,
|
"flake '%s' does not provide attribute %s",
|
||||||
showAttrPaths(attrPaths));
|
flakeRef,
|
||||||
|
showAttrPaths(attrPaths));
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||||
|
|
|
@ -103,9 +103,13 @@ struct Installable
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a cursor to each value this Installable could refer to. However
|
||||||
|
if none exists, throw exception instead of returning empty vector. */
|
||||||
virtual std::vector<ref<eval_cache::AttrCursor>>
|
virtual std::vector<ref<eval_cache::AttrCursor>>
|
||||||
getCursors(EvalState & state);
|
getCursors(EvalState & state);
|
||||||
|
|
||||||
|
/* Get the first and most preferred cursor this Installable could refer
|
||||||
|
to, or throw an exception if none exists. */
|
||||||
virtual ref<eval_cache::AttrCursor>
|
virtual ref<eval_cache::AttrCursor>
|
||||||
getCursor(EvalState & state);
|
getCursor(EvalState & state);
|
||||||
|
|
||||||
|
@ -193,15 +197,11 @@ struct InstallableFlake : InstallableValue
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
/* Get a cursor to every attrpath in getActualAttrPaths()
|
||||||
exists. */
|
that exists. However if none exists, throw an exception. */
|
||||||
std::vector<ref<eval_cache::AttrCursor>>
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
getCursors(EvalState & state) override;
|
getCursors(EvalState & state) override;
|
||||||
|
|
||||||
/* Get a cursor to the first attrpath in getActualAttrPaths() that
|
|
||||||
exists, or throw an exception with suggestions if none exists. */
|
|
||||||
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
|
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
||||||
|
|
||||||
FlakeRef nixpkgsFlakeRef() const override;
|
FlakeRef nixpkgsFlakeRef() const override;
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixcmd
|
Libs: -L${libdir} -lnixcmd
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++20
|
||||||
|
|
|
@ -7,4 +7,4 @@ Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Requires: nix-store bdw-gc
|
Requires: nix-store bdw-gc
|
||||||
Libs: -L${libdir} -lnixexpr
|
Libs: -L${libdir} -lnixexpr
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++20
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixmain
|
Libs: -L${libdir} -lnixmain
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++20
|
||||||
|
|
|
@ -380,7 +380,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
getFile(narInfoFile,
|
getFile(narInfoFile,
|
||||||
{[=](std::future<std::optional<std::string>> fut) {
|
{[=,this](std::future<std::optional<std::string>> fut) {
|
||||||
try {
|
try {
|
||||||
auto data = fut.get();
|
auto data = fut.get();
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ void Worker::run(const Goals & _topGoals)
|
||||||
if (!children.empty() || !waitingForAWhile.empty())
|
if (!children.empty() || !waitingForAWhile.empty())
|
||||||
waitForInput();
|
waitForInput();
|
||||||
else {
|
else {
|
||||||
if (awake.empty() && 0 == settings.maxBuildJobs)
|
if (awake.empty() && 0U == settings.maxBuildJobs)
|
||||||
{
|
{
|
||||||
if (getMachines().empty())
|
if (getMachines().empty())
|
||||||
throw Error("unable to start any build; either increase '--max-jobs' "
|
throw Error("unable to start any build; either increase '--max-jobs' "
|
||||||
|
|
|
@ -222,19 +222,19 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
|
||||||
.longName = name,
|
.longName = name,
|
||||||
.description = "Enable sandboxing.",
|
.description = "Enable sandboxing.",
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(smEnabled); }}
|
.handler = {[this]() { override(smEnabled); }}
|
||||||
});
|
});
|
||||||
args.addFlag({
|
args.addFlag({
|
||||||
.longName = "no-" + name,
|
.longName = "no-" + name,
|
||||||
.description = "Disable sandboxing.",
|
.description = "Disable sandboxing.",
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(smDisabled); }}
|
.handler = {[this]() { override(smDisabled); }}
|
||||||
});
|
});
|
||||||
args.addFlag({
|
args.addFlag({
|
||||||
.longName = "relaxed-" + name,
|
.longName = "relaxed-" + name,
|
||||||
.description = "Enable sandboxing, but allow builds to disable it.",
|
.description = "Enable sandboxing, but allow builds to disable it.",
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(smRelaxed); }}
|
.handler = {[this]() { override(smRelaxed); }}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixstore -lnixutil
|
Libs: -L${libdir} -lnixstore -lnixutil
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++20
|
||||||
|
|
|
@ -324,7 +324,7 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
||||||
expectArgs({
|
expectArgs({
|
||||||
.label = "subcommand",
|
.label = "subcommand",
|
||||||
.optional = true,
|
.optional = true,
|
||||||
.handler = {[=](std::string s) {
|
.handler = {[=,this](std::string s) {
|
||||||
assert(!command);
|
assert(!command);
|
||||||
auto i = commands.find(s);
|
auto i = commands.find(s);
|
||||||
if (i == commands.end()) {
|
if (i == commands.end()) {
|
||||||
|
|
|
@ -209,7 +209,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||||
.description = fmt("Set the `%s` setting.", name),
|
.description = fmt("Set the `%s` setting.", name),
|
||||||
.category = category,
|
.category = category,
|
||||||
.labels = {"value"},
|
.labels = {"value"},
|
||||||
.handler = {[=](std::string s) { overridden = true; set(s); }},
|
.handler = {[this](std::string s) { overridden = true; set(s); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isAppendable())
|
if (isAppendable())
|
||||||
|
@ -218,7 +218,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||||
.description = fmt("Append to the `%s` setting.", name),
|
.description = fmt("Append to the `%s` setting.", name),
|
||||||
.category = category,
|
.category = category,
|
||||||
.labels = {"value"},
|
.labels = {"value"},
|
||||||
.handler = {[=](std::string s) { overridden = true; set(s, true); }},
|
.handler = {[this](std::string s) { overridden = true; set(s, true); }},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,13 +270,13 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string &
|
||||||
.longName = name,
|
.longName = name,
|
||||||
.description = fmt("Enable the `%s` setting.", name),
|
.description = fmt("Enable the `%s` setting.", name),
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(true); }}
|
.handler = {[this]() { override(true); }}
|
||||||
});
|
});
|
||||||
args.addFlag({
|
args.addFlag({
|
||||||
.longName = "no-" + name,
|
.longName = "no-" + name,
|
||||||
.description = fmt("Disable the `%s` setting.", name),
|
.description = fmt("Disable the `%s` setting.", name),
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(false); }}
|
.handler = {[this]() { override(false); }}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,11 +250,15 @@ public:
|
||||||
operator const T &() const { return value; }
|
operator const T &() const { return value; }
|
||||||
operator T &() { return value; }
|
operator T &() { return value; }
|
||||||
const T & get() const { return value; }
|
const T & get() const { return value; }
|
||||||
bool operator ==(const T & v2) const { return value == v2; }
|
template<typename U>
|
||||||
bool operator !=(const T & v2) const { return value != v2; }
|
bool operator ==(const U & v2) const { return value == v2; }
|
||||||
void operator =(const T & v) { assign(v); }
|
template<typename U>
|
||||||
|
bool operator !=(const U & v2) const { return value != v2; }
|
||||||
|
template<typename U>
|
||||||
|
void operator =(const U & v) { assign(v); }
|
||||||
virtual void assign(const T & v) { value = v; }
|
virtual void assign(const T & v) { value = v; }
|
||||||
void setDefault(const T & v) { if (!overridden) value = v; }
|
template<typename U>
|
||||||
|
void setDefault(const U & v) { if (!overridden) value = v; }
|
||||||
|
|
||||||
void set(const std::string & str, bool append = false) override;
|
void set(const std::string & str, bool append = false) override;
|
||||||
|
|
||||||
|
|
|
@ -1002,6 +1002,61 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
auto flake = std::make_shared<LockedFlake>(lockFlake());
|
auto flake = std::make_shared<LockedFlake>(lockFlake());
|
||||||
auto localSystem = std::string(settings.thisSystem.get());
|
auto localSystem = std::string(settings.thisSystem.get());
|
||||||
|
|
||||||
|
std::function<bool(
|
||||||
|
eval_cache::AttrCursor & visitor,
|
||||||
|
const std::vector<Symbol> &attrPath,
|
||||||
|
const Symbol &attr)> hasContent;
|
||||||
|
|
||||||
|
// For frameworks it's important that structures are as lazy as possible
|
||||||
|
// to prevent infinite recursions, performance issues and errors that
|
||||||
|
// aren't related to the thing to evaluate. As a consequence, they have
|
||||||
|
// to emit more attributes than strictly (sic) necessary.
|
||||||
|
// However, these attributes with empty values are not useful to the user
|
||||||
|
// so we omit them.
|
||||||
|
hasContent = [&](
|
||||||
|
eval_cache::AttrCursor & visitor,
|
||||||
|
const std::vector<Symbol> &attrPath,
|
||||||
|
const Symbol &attr) -> bool
|
||||||
|
{
|
||||||
|
auto attrPath2(attrPath);
|
||||||
|
attrPath2.push_back(attr);
|
||||||
|
auto attrPathS = state->symbols.resolve(attrPath2);
|
||||||
|
const auto & attrName = state->symbols[attr];
|
||||||
|
|
||||||
|
auto visitor2 = visitor.getAttr(attrName);
|
||||||
|
|
||||||
|
if ((attrPathS[0] == "apps"
|
||||||
|
|| attrPathS[0] == "checks"
|
||||||
|
|| attrPathS[0] == "devShells"
|
||||||
|
|| attrPathS[0] == "legacyPackages"
|
||||||
|
|| attrPathS[0] == "packages")
|
||||||
|
&& (attrPathS.size() == 1 || attrPathS.size() == 2)) {
|
||||||
|
for (const auto &subAttr : visitor2->getAttrs()) {
|
||||||
|
if (hasContent(*visitor2, attrPath2, subAttr)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrPathS.size() == 1)
|
||||||
|
&& (attrPathS[0] == "formatter"
|
||||||
|
|| attrPathS[0] == "nixosConfigurations"
|
||||||
|
|| attrPathS[0] == "nixosModules"
|
||||||
|
|| attrPathS[0] == "overlays"
|
||||||
|
)) {
|
||||||
|
for (const auto &subAttr : visitor2->getAttrs()) {
|
||||||
|
if (hasContent(*visitor2, attrPath2, subAttr)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't recognize it, it's probably content
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
std::function<nlohmann::json(
|
std::function<nlohmann::json(
|
||||||
eval_cache::AttrCursor & visitor,
|
eval_cache::AttrCursor & visitor,
|
||||||
const std::vector<Symbol> & attrPath,
|
const std::vector<Symbol> & attrPath,
|
||||||
|
@ -1027,7 +1082,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
{
|
{
|
||||||
if (!json)
|
if (!json)
|
||||||
logger->cout("%s", headerPrefix);
|
logger->cout("%s", headerPrefix);
|
||||||
auto attrs = visitor.getAttrs();
|
std::vector<Symbol> attrs;
|
||||||
|
for (const auto &attr : visitor.getAttrs()) {
|
||||||
|
if (hasContent(visitor, attrPath, attr))
|
||||||
|
attrs.push_back(attr);
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto & [i, attr] : enumerate(attrs)) {
|
for (const auto & [i, attr] : enumerate(attrs)) {
|
||||||
const auto & attrName = state->symbols[attr];
|
const auto & attrName = state->symbols[attr];
|
||||||
bool last = i + 1 == attrs.size();
|
bool last = i + 1 == attrs.size();
|
||||||
|
|
|
@ -56,8 +56,8 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
Strings getDefaultFlakeAttrPaths() override
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"packages." + settings.thisSystem.get() + ".",
|
"packages." + settings.thisSystem.get(),
|
||||||
"legacyPackages." + settings.thisSystem.get() + "."
|
"legacyPackages." + settings.thisSystem.get()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,3 +37,30 @@ in
|
||||||
assert show_output.legacyPackages.${builtins.currentSystem}.hello.name == "simple";
|
assert show_output.legacyPackages.${builtins.currentSystem}.hello.name == "simple";
|
||||||
true
|
true
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# Test that attributes are only reported when they have actual content
|
||||||
|
cat >flake.nix <<EOF
|
||||||
|
{
|
||||||
|
description = "Bla bla";
|
||||||
|
|
||||||
|
outputs = inputs: rec {
|
||||||
|
apps.$system = { };
|
||||||
|
checks.$system = { };
|
||||||
|
devShells.$system = { };
|
||||||
|
legacyPackages.$system = { };
|
||||||
|
packages.$system = { };
|
||||||
|
packages.someOtherSystem = { };
|
||||||
|
|
||||||
|
formatter = { };
|
||||||
|
nixosConfigurations = { };
|
||||||
|
nixosModules = { };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
nix flake show --json --all-systems > show-output.json
|
||||||
|
nix eval --impure --expr '
|
||||||
|
let show_output = builtins.fromJSON (builtins.readFile ./show-output.json);
|
||||||
|
in
|
||||||
|
assert show_output == { };
|
||||||
|
true
|
||||||
|
'
|
||||||
|
|
Loading…
Reference in a new issue