aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/labeler.yml2
-rw-r--r--src/libcmd/installable-flake.cc2
-rw-r--r--src/libcmd/installable-flake.hh2
-rw-r--r--src/libexpr/eval.cc20
-rw-r--r--src/libexpr/lexer.l2
-rw-r--r--src/libexpr/tests/value/print.cc236
-rw-r--r--src/libexpr/value.hh5
-rw-r--r--src/nix/flake.md19
-rw-r--r--src/nix/profile-list.md46
-rw-r--r--src/nix/profile.cc42
-rw-r--r--tests/nix-profile.sh3
-rw-r--r--tests/test-libstoreconsumer/local.mk3
12 files changed, 337 insertions, 45 deletions
diff --git a/.github/labeler.yml b/.github/labeler.yml
index fce0d3aeb..12120bdb3 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -16,7 +16,7 @@
"new-cli":
- src/nix/**/*
-"tests":
+"with-tests":
# Unit tests
- src/*/tests/**/*
# Functional and integration tests
diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc
index eb944240b..4da9b131b 100644
--- a/src/libcmd/installable-flake.cc
+++ b/src/libcmd/installable-flake.cc
@@ -151,7 +151,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
},
ExtraPathInfoFlake::Flake {
.originalRef = flakeRef,
- .resolvedRef = getLockedFlake()->flake.lockedRef,
+ .lockedRef = getLockedFlake()->flake.lockedRef,
}),
}};
}
diff --git a/src/libcmd/installable-flake.hh b/src/libcmd/installable-flake.hh
index 7ac4358d2..314918c14 100644
--- a/src/libcmd/installable-flake.hh
+++ b/src/libcmd/installable-flake.hh
@@ -19,7 +19,7 @@ struct ExtraPathInfoFlake : ExtraPathInfoValue
*/
struct Flake {
FlakeRef originalRef;
- FlakeRef resolvedRef;
+ FlakeRef lockedRef;
};
Flake flake;
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index aaeb419ad..706a19024 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -95,11 +95,16 @@ RootValue allocRootValue(Value * v)
#endif
}
-void Value::print(const SymbolTable & symbols, std::ostream & str,
- std::set<const void *> * seen) const
+void Value::print(const SymbolTable &symbols, std::ostream &str,
+ std::set<const void *> *seen, int depth) const
+
{
checkInterrupt();
+ if (depth <= 0) {
+ str << "«too deep»";
+ return;
+ }
switch (internalType) {
case tInt:
str << integer;
@@ -123,7 +128,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << "{ ";
for (auto & i : attrs->lexicographicOrder(symbols)) {
str << symbols[i->name] << " = ";
- i->value->print(symbols, str, seen);
+ i->value->print(symbols, str, seen, depth - 1);
str << "; ";
}
str << "}";
@@ -139,7 +144,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << "[ ";
for (auto v2 : listItems()) {
if (v2)
- v2->print(symbols, str, seen);
+ v2->print(symbols, str, seen, depth - 1);
else
str << "(nullptr)";
str << " ";
@@ -181,11 +186,10 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
}
}
-
-void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
-{
+void Value::print(const SymbolTable &symbols, std::ostream &str,
+ bool showRepeated, int depth) const {
std::set<const void *> seen;
- print(symbols, str, showRepeated ? nullptr : &seen);
+ print(symbols, str, showRepeated ? nullptr : &seen, depth);
}
// Pretty print types for assertion errors
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 462b3b602..a3a8608d9 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -36,7 +36,7 @@ static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
#define CUR_POS makeCurPos(*yylloc, data)
// backup to recover from yyless(0)
-YYLTYPE prev_yylloc;
+thread_local YYLTYPE prev_yylloc;
static void initLoc(YYLTYPE * loc)
{
diff --git a/src/libexpr/tests/value/print.cc b/src/libexpr/tests/value/print.cc
new file mode 100644
index 000000000..5e96e12ec
--- /dev/null
+++ b/src/libexpr/tests/value/print.cc
@@ -0,0 +1,236 @@
+#include "tests/libexpr.hh"
+
+#include "value.hh"
+
+namespace nix {
+
+using namespace testing;
+
+struct ValuePrintingTests : LibExprTest
+{
+ template<class... A>
+ void test(Value v, std::string_view expected, A... args)
+ {
+ std::stringstream out;
+ v.print(state.symbols, out, args...);
+ ASSERT_EQ(out.str(), expected);
+ }
+};
+
+TEST_F(ValuePrintingTests, tInt)
+{
+ Value vInt;
+ vInt.mkInt(10);
+ test(vInt, "10");
+}
+
+TEST_F(ValuePrintingTests, tBool)
+{
+ Value vBool;
+ vBool.mkBool(true);
+ test(vBool, "true");
+}
+
+TEST_F(ValuePrintingTests, tString)
+{
+ Value vString;
+ vString.mkString("some-string");
+ test(vString, "\"some-string\"");
+}
+
+TEST_F(ValuePrintingTests, tPath)
+{
+ Value vPath;
+ vPath.mkString("/foo");
+ test(vPath, "\"/foo\"");
+}
+
+TEST_F(ValuePrintingTests, tNull)
+{
+ Value vNull;
+ vNull.mkNull();
+ test(vNull, "null");
+}
+
+TEST_F(ValuePrintingTests, tAttrs)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ BindingsBuilder builder(state, state.allocBindings(10));
+ builder.insert(state.symbols.create("one"), &vOne);
+ builder.insert(state.symbols.create("two"), &vTwo);
+
+ Value vAttrs;
+ vAttrs.mkAttrs(builder.finish());
+
+ test(vAttrs, "{ one = 1; two = 2; }");
+}
+
+TEST_F(ValuePrintingTests, tList)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ Value vList;
+ state.mkList(vList, 5);
+ vList.bigList.elems[0] = &vOne;
+ vList.bigList.elems[1] = &vTwo;
+ vList.bigList.size = 3;
+
+ test(vList, "[ 1 2 (nullptr) ]");
+}
+
+TEST_F(ValuePrintingTests, vThunk)
+{
+ Value vThunk;
+ vThunk.mkThunk(nullptr, nullptr);
+
+ test(vThunk, "<CODE>");
+}
+
+TEST_F(ValuePrintingTests, vApp)
+{
+ Value vApp;
+ vApp.mkApp(nullptr, nullptr);
+
+ test(vApp, "<CODE>");
+}
+
+TEST_F(ValuePrintingTests, vLambda)
+{
+ Value vLambda;
+ vLambda.mkLambda(nullptr, nullptr);
+
+ test(vLambda, "<LAMBDA>");
+}
+
+TEST_F(ValuePrintingTests, vPrimOp)
+{
+ Value vPrimOp;
+ vPrimOp.mkPrimOp(nullptr);
+
+ test(vPrimOp, "<PRIMOP>");
+}
+
+TEST_F(ValuePrintingTests, vPrimOpApp)
+{
+ Value vPrimOpApp;
+ vPrimOpApp.mkPrimOpApp(nullptr, nullptr);
+
+ test(vPrimOpApp, "<PRIMOP-APP>");
+}
+
+TEST_F(ValuePrintingTests, vExternal)
+{
+ struct MyExternal : ExternalValueBase
+ {
+ public:
+ std::string showType() const override
+ {
+ return "";
+ }
+ std::string typeOf() const override
+ {
+ return "";
+ }
+ virtual std::ostream & print(std::ostream & str) const override
+ {
+ str << "testing-external!";
+ return str;
+ }
+ } myExternal;
+ Value vExternal;
+ vExternal.mkExternal(&myExternal);
+
+ test(vExternal, "testing-external!");
+}
+
+TEST_F(ValuePrintingTests, vFloat)
+{
+ Value vFloat;
+ vFloat.mkFloat(2.0);
+
+ test(vFloat, "2");
+}
+
+TEST_F(ValuePrintingTests, vBlackhole)
+{
+ Value vBlackhole;
+ vBlackhole.mkBlackhole();
+ test(vBlackhole, "«potential infinite recursion»");
+}
+
+TEST_F(ValuePrintingTests, depthAttrs)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ BindingsBuilder builder(state, state.allocBindings(10));
+ builder.insert(state.symbols.create("one"), &vOne);
+ builder.insert(state.symbols.create("two"), &vTwo);
+
+ Value vAttrs;
+ vAttrs.mkAttrs(builder.finish());
+
+ BindingsBuilder builder2(state, state.allocBindings(10));
+ builder2.insert(state.symbols.create("one"), &vOne);
+ builder2.insert(state.symbols.create("two"), &vTwo);
+ builder2.insert(state.symbols.create("nested"), &vAttrs);
+
+ Value vNested;
+ vNested.mkAttrs(builder2.finish());
+
+ test(vNested, "{ nested = «too deep»; one = «too deep»; two = «too deep»; }", false, 1);
+ test(vNested, "{ nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; }", false, 2);
+ test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 3);
+ test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 4);
+}
+
+TEST_F(ValuePrintingTests, depthList)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ BindingsBuilder builder(state, state.allocBindings(10));
+ builder.insert(state.symbols.create("one"), &vOne);
+ builder.insert(state.symbols.create("two"), &vTwo);
+
+ Value vAttrs;
+ vAttrs.mkAttrs(builder.finish());
+
+ BindingsBuilder builder2(state, state.allocBindings(10));
+ builder2.insert(state.symbols.create("one"), &vOne);
+ builder2.insert(state.symbols.create("two"), &vTwo);
+ builder2.insert(state.symbols.create("nested"), &vAttrs);
+
+ Value vNested;
+ vNested.mkAttrs(builder2.finish());
+
+ Value vList;
+ state.mkList(vList, 5);
+ vList.bigList.elems[0] = &vOne;
+ vList.bigList.elems[1] = &vTwo;
+ vList.bigList.elems[2] = &vNested;
+ vList.bigList.size = 3;
+
+ test(vList, "[ «too deep» «too deep» «too deep» ]", false, 1);
+ test(vList, "[ 1 2 { nested = «too deep»; one = «too deep»; two = «too deep»; } ]", false, 2);
+ test(vList, "[ 1 2 { nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; } ]", false, 3);
+ test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 4);
+ test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 5);
+}
+
+} // namespace nix
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 44c8071c2..c44683e50 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -2,6 +2,7 @@
///@file
#include <cassert>
+#include <climits>
#include "symbol-table.hh"
#include "value/context.hh"
@@ -137,11 +138,11 @@ private:
friend std::string showType(const Value & v);
- void print(const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen) const;
+ void print(const SymbolTable &symbols, std::ostream &str, std::set<const void *> *seen, int depth) const;
public:
- void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const;
+ void print(const SymbolTable &symbols, std::ostream &str, bool showRepeated = false, int depth = INT_MAX) const;
// Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's
diff --git a/src/nix/flake.md b/src/nix/flake.md
index 456fd0ea1..92f477917 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -71,8 +71,6 @@ inputs.nixpkgs = {
Here are some examples of flake references in their URL-like representation:
-* `.`: The flake in the current directory.
-* `/home/alice/src/patchelf`: A flake in some other directory.
* `nixpkgs`: The `nixpkgs` entry in the flake registry.
* `nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293`: The `nixpkgs`
entry in the flake registry, with its Git revision overridden to a
@@ -93,6 +91,23 @@ Here are some examples of flake references in their URL-like representation:
* `https://github.com/NixOS/patchelf/archive/master.tar.gz`: A tarball
flake.
+## Path-like syntax
+
+Flakes corresponding to a local path can also be referred to by a direct path reference, either `/absolute/path/to/the/flake` or `./relative/path/to/the/flake` (note that the leading `./` is mandatory for relative paths to avoid any ambiguity).
+
+The semantic of such a path is as follows:
+
+* If the directory is part of a Git repository, then the input will be treated as a `git+file:` URL, otherwise it will be treated as a `path:` url;
+* If the directory doesn't contain a `flake.nix` file, then Nix will search for such a file upwards in the file system hierarchy until it finds any of:
+ 1. The Git repository root, or
+ 2. The filesystem root (/), or
+ 3. A folder on a different mount point.
+
+### Examples
+
+* `.`: The flake to which the current directory belongs to.
+* `/home/alice/src/patchelf`: A flake in some other directory.
+
## Flake reference attributes
The following generic flake reference attributes are supported:
diff --git a/src/nix/profile-list.md b/src/nix/profile-list.md
index fa786162f..5d7fcc0ec 100644
--- a/src/nix/profile-list.md
+++ b/src/nix/profile-list.md
@@ -6,26 +6,48 @@ R""(
```console
# nix profile list
- 0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify github:NixOS/nixpkgs/c23db78bbd474c4d0c5c3c551877523b4a50db06#legacyPackages.x86_64-linux.spotify /nix/store/akpdsid105phbbvknjsdh7hl4v3fhjkr-spotify-1.1.46.916.g416cacf1
- 1 flake:nixpkgs#legacyPackages.x86_64-linux.zoom-us github:NixOS/nixpkgs/c23db78bbd474c4d0c5c3c551877523b4a50db06#legacyPackages.x86_64-linux.zoom-us /nix/store/89pmjmbih5qpi7accgacd17ybpgp4xfm-zoom-us-5.4.53350.1027
- 2 flake:blender-bin#packages.x86_64-linux.default github:edolstra/nix-warez/d09d7eea893dcb162e89bc67f6dc1ced14abfc27?dir=blender#packages.x86_64-linux.default /nix/store/zfgralhqjnam662kqsgq6isjw8lhrflz-blender-bin-2.91.0
+ Index: 0
+ Flake attribute: legacyPackages.x86_64-linux.gdb
+ Original flake URL: flake:nixpkgs
+ Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
+ Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
+
+ Index: 1
+ Flake attribute: packages.x86_64-linux.default
+ Original flake URL: flake:blender-bin
+ Locked flake URL: github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender
+ Store paths: /nix/store/i798sxl3j40wpdi1rgf391id1b5klw7g-blender-bin-3.1.2
```
+ Note that you can unambiguously rebuild a package from a profile
+ through its locked flake URL and flake attribute, e.g.
+
+ ```console
+ # nix build github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender#packages.x86_64-linux.default
+ ```
+
+ will build the package with index 1 shown above.
+
# Description
This command shows what packages are currently installed in a
-profile. The output consists of one line per package, with the
-following fields:
+profile. For each installed package, it shows the following
+information:
+
+* `Index`: An integer that can be used to unambiguously identify the
+ package in invocations of `nix profile remove` and `nix profile
+ upgrade`.
-* An integer that can be used to unambiguously identify the package in
- invocations of `nix profile remove` and `nix profile upgrade`.
+* `Flake attribute`: The flake output attribute path that provides the
+ package (e.g. `packages.x86_64-linux.hello`).
-* The original ("unlocked") flake reference and output attribute path
- used at installation time.
+* `Original flake URL`: The original ("unlocked") flake reference
+ specified by the user when the package was first installed via `nix
+ profile install`.
-* The locked flake reference to which the unlocked flake reference was
- resolved.
+* `Locked flake URL`: The locked flake reference to which the original
+ flake reference was resolved.
-* The store path(s) of the package.
+* `Store paths`: The store path(s) of the package.
)""
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index f3b73f10d..b833b5192 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -21,7 +21,7 @@ struct ProfileElementSource
{
FlakeRef originalRef;
// FIXME: record original attrpath.
- FlakeRef resolvedRef;
+ FlakeRef lockedRef;
std::string attrPath;
ExtendedOutputsSpec outputs;
@@ -168,7 +168,7 @@ struct ProfileManifest
}
}
- std::string toJSON(Store & store) const
+ nlohmann::json toJSON(Store & store) const
{
auto array = nlohmann::json::array();
for (auto & element : elements) {
@@ -181,7 +181,7 @@ struct ProfileManifest
obj["priority"] = element.priority;
if (element.source) {
obj["originalUrl"] = element.source->originalRef.to_string();
- obj["url"] = element.source->resolvedRef.to_string();
+ obj["url"] = element.source->lockedRef.to_string();
obj["attrPath"] = element.source->attrPath;
obj["outputs"] = element.source->outputs;
}
@@ -190,7 +190,7 @@ struct ProfileManifest
nlohmann::json json;
json["version"] = 2;
json["elements"] = array;
- return json.dump();
+ return json;
}
StorePath build(ref<Store> store)
@@ -210,7 +210,7 @@ struct ProfileManifest
buildProfile(tempDir, std::move(pkgs));
- writeFile(tempDir + "/manifest.json", toJSON(*store));
+ writeFile(tempDir + "/manifest.json", toJSON(*store).dump());
/* Add the symlink tree to the store. */
StringSink sink;
@@ -349,7 +349,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
if (auto * info2 = dynamic_cast<ExtraPathInfoFlake *>(&*info)) {
element.source = ProfileElementSource {
.originalRef = info2->flake.originalRef,
- .resolvedRef = info2->flake.resolvedRef,
+ .lockedRef = info2->flake.lockedRef,
.attrPath = info2->value.attrPath,
.outputs = info2->value.extendedOutputsSpec,
};
@@ -588,14 +588,14 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
assert(infop);
auto & info = *infop;
- if (element.source->resolvedRef == info.flake.resolvedRef) continue;
+ if (element.source->lockedRef == info.flake.lockedRef) continue;
printInfo("upgrading '%s' from flake '%s' to '%s'",
- element.source->attrPath, element.source->resolvedRef, info.flake.resolvedRef);
+ element.source->attrPath, element.source->lockedRef, info.flake.lockedRef);
element.source = ProfileElementSource {
.originalRef = installable->flakeRef,
- .resolvedRef = info.flake.resolvedRef,
+ .lockedRef = info.flake.lockedRef,
.attrPath = info.value.attrPath,
.outputs = installable->extendedOutputsSpec,
};
@@ -635,7 +635,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
}
};
-struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile
+struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile, MixJSON
{
std::string description() override
{
@@ -653,12 +653,22 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
{
ProfileManifest manifest(*getEvalState(), *profile);
- for (size_t i = 0; i < manifest.elements.size(); ++i) {
- auto & element(manifest.elements[i]);
- logger->cout("%d %s %s %s", i,
- element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath + element.source->outputs.to_string() : "-",
- element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath + element.source->outputs.to_string() : "-",
- concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
+ if (json) {
+ std::cout << manifest.toJSON(*store).dump() << "\n";
+ } else {
+ for (size_t i = 0; i < manifest.elements.size(); ++i) {
+ auto & element(manifest.elements[i]);
+ if (i) logger->cout("");
+ logger->cout("Index: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
+ i,
+ element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
+ if (element.source) {
+ logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
+ logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
+ logger->cout("Locked flake URL: %s", element.source->lockedRef.to_string());
+ }
+ logger->cout("Store paths: %s", concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
+ }
}
}
};
diff --git a/tests/nix-profile.sh b/tests/nix-profile.sh
index 9da3f802b..7c478a0cd 100644
--- a/tests/nix-profile.sh
+++ b/tests/nix-profile.sh
@@ -47,8 +47,9 @@ cp ./config.nix $flake1Dir/
# Test upgrading from nix-env.
nix-env -f ./user-envs.nix -i foo-1.0
-nix profile list | grep '0 - - .*-foo-1.0'
+nix profile list | grep -A2 'Index:.*0' | grep 'Store paths:.*foo-1.0'
nix profile install $flake1Dir -L
+nix profile list | grep -A4 'Index:.*1' | grep 'Locked flake URL:.*narHash'
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
[ -e $TEST_HOME/.nix-profile/share/man ]
(! [ -e $TEST_HOME/.nix-profile/include ])
diff --git a/tests/test-libstoreconsumer/local.mk b/tests/test-libstoreconsumer/local.mk
index cd2d0c7f8..edc140723 100644
--- a/tests/test-libstoreconsumer/local.mk
+++ b/tests/test-libstoreconsumer/local.mk
@@ -2,6 +2,9 @@ programs += test-libstoreconsumer
test-libstoreconsumer_DIR := $(d)
+# do not install
+test-libstoreconsumer_INSTALL_DIR :=
+
test-libstoreconsumer_SOURCES := \
$(wildcard $(d)/*.cc) \