From 9e8f2090365a509656dead69bc91fb6615cf9d05 Mon Sep 17 00:00:00 2001 From: Raphael Robatsch Date: Thu, 13 Apr 2023 16:22:45 +0200 Subject: [PATCH 1/2] Display valid installable in InstallableDerivedPath::parse warning The warning message should produce an installable name that can be passed to `nix build`, `nix path-info`, etc. again. Since the CLI expects that the .drv path and the output names are separated by a caret, the warning message must also separate the .drv path and output names with a caret. However, `DerivedPath::Built.to_string()` uses an exclamation point as the separator instead. This commit adds a `separator` argument to the to_string method. This changes the warning message from: If this command is now failing try again with '/nix/store/foo.drv!*' to: If this command is now failing try again with '/nix/store/foo.drv^*' --- src/libcmd/installable-derived-path.cc | 2 +- src/libstore/derived-path.cc | 13 +++++++------ src/libstore/derived-path.hh | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libcmd/installable-derived-path.cc b/src/libcmd/installable-derived-path.cc index 6ecf54b7c..35f6c5bfe 100644 --- a/src/libcmd/installable-derived-path.cc +++ b/src/libcmd/installable-derived-path.cc @@ -47,7 +47,7 @@ InstallableDerivedPath InstallableDerivedPath::parse( }; warn( "The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '%s'", - oldDerivedPath.to_string(*store)); + oldDerivedPath.to_string(*store, '^')); }; return DerivedPath::Opaque { .path = std::move(storePath), diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index e5f0f1b33..6baca70a3 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -59,18 +59,19 @@ std::string DerivedPath::Opaque::to_string(const Store & store) const return store.printStorePath(path); } -std::string DerivedPath::Built::to_string(const Store & store) const +std::string DerivedPath::Built::to_string(const Store & store, char separator) const { return store.printStorePath(drvPath) - + "!" + + separator + outputs.to_string(); } -std::string DerivedPath::to_string(const Store & store) const +std::string DerivedPath::to_string(const Store & store, char separator) const { - return std::visit( - [&](const auto & req) { return req.to_string(store); }, - this->raw()); + return std::visit(overloaded { + [&](const DerivedPath::Built & req) { return req.to_string(store, separator); }, + [&](const DerivedPath::Opaque & req) { return req.to_string(store); }, + }, this->raw()); } diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index 2155776b1..9fc64e2f2 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -48,7 +48,7 @@ struct DerivedPathBuilt { StorePath drvPath; OutputsSpec outputs; - std::string to_string(const Store & store) const; + std::string to_string(const Store & store, char separator = '!') const; static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view); nlohmann::json toJSON(ref store) const; @@ -81,7 +81,7 @@ struct DerivedPath : _DerivedPathRaw { return static_cast(*this); } - std::string to_string(const Store & store) const; + std::string to_string(const Store & store, char separator = '!') const; static DerivedPath parse(const Store & store, std::string_view); }; From ee420ac64e7d1f51f5abcb069dbe84cd6ff707ce Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 14 Apr 2023 20:45:11 -0400 Subject: [PATCH 2/2] Legacy vs non-legacy `to_string`/`parse` for `DerivedPath` As requested by @roberth, it is good to call out the specific instances we care about, which is `!` for the RPC protocols, and `^` for humans. This doesn't take advantage of parametricity as much, but since the human and computer interfaces are good to decouple anyways (we don't care if they drift further apart over time in the slightest) some separation and slight duplication is fine. Also, unit test both round trips. --- src/libcmd/installable-derived-path.cc | 2 +- src/libstore/derived-path.cc | 37 +++++++++++++++++++++----- src/libstore/derived-path.hh | 30 ++++++++++++++++++--- src/libstore/remote-store.cc | 4 +-- src/libstore/tests/derived-path.cc | 8 ++++++ 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/libcmd/installable-derived-path.cc b/src/libcmd/installable-derived-path.cc index 35f6c5bfe..6ecf54b7c 100644 --- a/src/libcmd/installable-derived-path.cc +++ b/src/libcmd/installable-derived-path.cc @@ -47,7 +47,7 @@ InstallableDerivedPath InstallableDerivedPath::parse( }; warn( "The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '%s'", - oldDerivedPath.to_string(*store, '^')); + oldDerivedPath.to_string(*store)); }; return DerivedPath::Opaque { .path = std::move(storePath), diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index 6baca70a3..9a2ffda39 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -59,17 +59,32 @@ std::string DerivedPath::Opaque::to_string(const Store & store) const return store.printStorePath(path); } -std::string DerivedPath::Built::to_string(const Store & store, char separator) const +std::string DerivedPath::Built::to_string(const Store & store) const { return store.printStorePath(drvPath) - + separator + + '^' + outputs.to_string(); } -std::string DerivedPath::to_string(const Store & store, char separator) const +std::string DerivedPath::Built::to_string_legacy(const Store & store) const +{ + return store.printStorePath(drvPath) + + '!' + + outputs.to_string(); +} + +std::string DerivedPath::to_string(const Store & store) const { return std::visit(overloaded { - [&](const DerivedPath::Built & req) { return req.to_string(store, separator); }, + [&](const DerivedPath::Built & req) { return req.to_string(store); }, + [&](const DerivedPath::Opaque & req) { return req.to_string(store); }, + }, this->raw()); +} + +std::string DerivedPath::to_string_legacy(const Store & store) const +{ + return std::visit(overloaded { + [&](const DerivedPath::Built & req) { return req.to_string_legacy(store); }, [&](const DerivedPath::Opaque & req) { return req.to_string(store); }, }, this->raw()); } @@ -88,14 +103,24 @@ DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_vi }; } -DerivedPath DerivedPath::parse(const Store & store, std::string_view s) +static inline DerivedPath parseWith(const Store & store, std::string_view s, std::string_view separator) { - size_t n = s.find("!"); + size_t n = s.find(separator); return n == s.npos ? (DerivedPath) DerivedPath::Opaque::parse(store, s) : (DerivedPath) DerivedPath::Built::parse(store, s.substr(0, n), s.substr(n + 1)); } +DerivedPath DerivedPath::parse(const Store & store, std::string_view s) +{ + return parseWith(store, s, "^"); +} + +DerivedPath DerivedPath::parseLegacy(const Store & store, std::string_view s) +{ + return parseWith(store, s, "!"); +} + RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const { RealisedPath::Set res; diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index 9fc64e2f2..5f7acbebc 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -48,8 +48,18 @@ struct DerivedPathBuilt { StorePath drvPath; OutputsSpec outputs; - std::string to_string(const Store & store, char separator = '!') const; - static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view); + /** + * Uses `^` as the separator + */ + std::string to_string(const Store & store) const; + /** + * Uses `!` as the separator + */ + std::string to_string_legacy(const Store & store) const; + /** + * The caller splits on the separator, so it works for both variants. + */ + static DerivedPathBuilt parse(const Store & store, std::string_view drvPath, std::string_view outputs); nlohmann::json toJSON(ref store) const; GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs); @@ -81,8 +91,22 @@ struct DerivedPath : _DerivedPathRaw { return static_cast(*this); } - std::string to_string(const Store & store, char separator = '!') const; + /** + * Uses `^` as the separator + */ + std::string to_string(const Store & store) const; + /** + * Uses `!` as the separator + */ + std::string to_string_legacy(const Store & store) const; + /** + * Uses `^` as the separator + */ static DerivedPath parse(const Store & store, std::string_view); + /** + * Uses `!` as the separator + */ + static DerivedPath parseLegacy(const Store & store, std::string_view); }; /** diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index e128c3a29..b862902d1 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -90,12 +90,12 @@ void write(const Store & store, Sink & out, const ContentAddress & ca) DerivedPath read(const Store & store, Source & from, Phantom _) { auto s = readString(from); - return DerivedPath::parse(store, s); + return DerivedPath::parseLegacy(store, s); } void write(const Store & store, Sink & out, const DerivedPath & req) { - out << req.to_string(store); + out << req.to_string_legacy(store); } diff --git a/src/libstore/tests/derived-path.cc b/src/libstore/tests/derived-path.cc index d1ac2c5e7..e6d32dbd0 100644 --- a/src/libstore/tests/derived-path.cc +++ b/src/libstore/tests/derived-path.cc @@ -51,6 +51,14 @@ TEST_F(DerivedPathTest, force_init) { } +RC_GTEST_FIXTURE_PROP( + DerivedPathTest, + prop_legacy_round_rip, + (const DerivedPath & o)) +{ + RC_ASSERT(o == DerivedPath::parseLegacy(*store, o.to_string_legacy(*store))); +} + RC_GTEST_FIXTURE_PROP( DerivedPathTest, prop_round_rip,