diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index c1f4de76f..f9a0063ee 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -2,3 +2,15 @@ * The `repeat` and `enforce-determinism` options have been removed since they had been broken under many circumstances for a long time. + +* You can now use [flake references] in the [old command line interface], e.g. + + [flake references]: ../command-ref/new-cli/nix3-flake.md#flake-references + [old command line interface]: ../command-ref/main-commands.md + + ``` + # nix-build flake:nixpkgs -A hello + # nix-build -I nixpkgs=flake:github:NixOS/nixpkgs/nixos-22.05 \ + '' -A hello + # NIX_PATH=nixpkgs=flake:nixpkgs nix-build '' -A hello + ``` diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index dc0de4b9f..67fc501c9 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -85,6 +85,23 @@ MixEvalArgs::MixEvalArgs() -I nixpkgs=channel:nixos-21.05 -I nixpkgs=https://nixos.org/channels/nixos-21.05/nixexprs.tar.xz ``` + + You can also fetch source trees using flake URLs and add them to the + search path. For instance, + + ``` + -I nixpkgs=flake:nixpkgs + ``` + + specifies that the prefix `nixpkgs` shall refer to the source tree + downloaded from the `nixpkgs` entry in the flake registry. Similarly, + + ``` + -I nixpkgs=flake:github:NixOS/nixpkgs/nixos-22.05 + ``` + + makes `` refer to a particular branch of the + `NixOS/nixpkgs` repository on GitHub. )", .category = category, .labels = {"path"}, @@ -142,14 +159,25 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) Path lookupFileArg(EvalState & state, std::string_view s) { - if (isUri(s)) { - return state.store->toRealPath( - fetchers::downloadTarball( - state.store, resolveUri(s), "source", false).first.storePath); - } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { + if (EvalSettings::isPseudoUrl(s)) { + auto storePath = fetchers::downloadTarball( + state.store, EvalSettings::resolvePseudoUrl(s), "source", false).first.storePath; + return state.store->toRealPath(storePath); + } + + else if (hasPrefix(s, "flake:")) { + settings.requireExperimentalFeature(Xp::Flakes); + auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); + auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath; + return state.store->toRealPath(storePath); + } + + else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { Path p(s.substr(1, s.size() - 2)); return state.findFile(p); - } else + } + + else return absPath(std::string(s)); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b67ed853e..6955aacbf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -402,7 +402,8 @@ static Strings parseNixPath(const std::string & s) } if (*p == ':') { - if (isUri(std::string(start2, s.end()))) { + auto prefix = std::string(start2, s.end()); + if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) { ++p; while (p != s.end() && *p != ':') ++p; } @@ -2583,6 +2584,23 @@ Strings EvalSettings::getDefaultNixPath() return res; } +bool EvalSettings::isPseudoUrl(std::string_view s) +{ + if (s.compare(0, 8, "channel:") == 0) return true; + size_t pos = s.find("://"); + if (pos == std::string::npos) return false; + std::string scheme(s, 0, pos); + return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3" || scheme == "ssh"; +} + +std::string EvalSettings::resolvePseudoUrl(std::string_view url) +{ + if (hasPrefix(url, "channel:")) + return "https://nixos.org/channels/" + std::string(url.substr(8)) + "/nixexprs.tar.xz"; + else + return std::string(url); +} + EvalSettings evalSettings; static GlobalConfig::Register rEvalSettings(&evalSettings); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f07f15d43..cf307d820 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -590,6 +590,10 @@ struct EvalSettings : Config static Strings getDefaultNixPath(); + static bool isPseudoUrl(std::string_view s); + + static std::string resolvePseudoUrl(std::string_view url); + Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", "Whether builtin functions that allow executing native code should be enabled."}; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 7c9b5a2db..fbf865719 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -643,6 +643,7 @@ formal #include "filetransfer.hh" #include "fetchers.hh" #include "store-api.hh" +#include "flake/flake.hh" namespace nix { @@ -805,17 +806,28 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl std::pair res; - if (isUri(elem.second)) { + if (EvalSettings::isPseudoUrl(elem.second)) { try { - res = { true, store->toRealPath(fetchers::downloadTarball( - store, resolveUri(elem.second), "source", false).first.storePath) }; + auto storePath = fetchers::downloadTarball( + store, EvalSettings::resolvePseudoUrl(elem.second), "source", false).first.storePath; + res = { true, store->toRealPath(storePath) }; } catch (FileTransferError & e) { logWarning({ .msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second) }); res = { false, "" }; } - } else { + } + + else if (hasPrefix(elem.second, "flake:")) { + settings.requireExperimentalFeature(Xp::Flakes); + auto flakeRef = parseFlakeRef(elem.second.substr(6), {}, true, false); + debug("fetching flake search path element '%s''", elem.second); + auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath; + res = { true, store->toRealPath(storePath) }; + } + + else { auto path = absPath(elem.second); if (pathExists(path)) res = { true, path }; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 84e7f5c02..680446787 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -220,8 +220,6 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v } else url = state.forceStringNoCtx(*args[0], pos); - url = resolveUri(*url); - state.checkURI(*url); if (name == "") diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 2ff411e18..756bd4423 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -33,14 +33,6 @@ FileTransferSettings fileTransferSettings; static GlobalConfig::Register rFileTransferSettings(&fileTransferSettings); -std::string resolveUri(std::string_view uri) -{ - if (uri.compare(0, 8, "channel:") == 0) - return "https://nixos.org/channels/" + std::string(uri.substr(8)) + "/nixexprs.tar.xz"; - else - return std::string(uri); -} - struct curlFileTransfer : public FileTransfer { CURLM * curlm = 0; @@ -873,14 +865,4 @@ FileTransferError::FileTransferError(FileTransfer::Error error, std::optional response, const Args & ... args); }; -bool isUri(std::string_view s); - -/* Resolve deprecated 'channel:' URLs. */ -std::string resolveUri(std::string_view uri); - } diff --git a/tests/flakes/flakes.sh b/tests/flakes/flakes.sh index 8cdc320fb..5ef4d0a7a 100644 --- a/tests/flakes/flakes.sh +++ b/tests/flakes/flakes.sh @@ -473,3 +473,9 @@ nix store delete $(nix store add-path $badFlakeDir) [[ $(nix path-info $(nix store add-path $flake1Dir)) =~ flake1 ]] [[ $(nix path-info path:$(nix store add-path $flake1Dir)) =~ simple ]] + +# Test fetching flakerefs in the legacy CLI. +[[ $(nix-instantiate --eval flake:flake3 -A x) = 123 ]] +[[ $(nix-instantiate --eval flake:git+file://$flake3Dir -A x) = 123 ]] +[[ $(nix-instantiate -I flake3=flake:flake3 --eval '' -A x) = 123 ]] +[[ $(NIX_PATH=flake3=flake:flake3 nix-instantiate --eval '' -A x) = 123 ]]