diff --git a/lix/libcmd/cmd-profiles.hh b/lix/libcmd/cmd-profiles.hh index 322154803..ef8c74156 100644 --- a/lix/libcmd/cmd-profiles.hh +++ b/lix/libcmd/cmd-profiles.hh @@ -3,7 +3,7 @@ #include "lix/libcmd/built-path.hh" #include "lix/libexpr/eval.hh" -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libflakes/expr/flakeref.hh" #include "lix/libexpr/get-drvs.hh" #include "lix/libutil/types.hh" #include "lix/libutil/json-fwd.hh" diff --git a/lix/libcmd/command.cc b/lix/libcmd/command.cc index 812f8b446..6836c024f 100644 --- a/lix/libcmd/command.cc +++ b/lix/libcmd/command.cc @@ -1,4 +1,5 @@ #include "lix/libcmd/command.hh" +#include "lix/libflakes/init.hh" #include "lix/libstore/store-api.hh" #include "lix/libstore/local-fs-store.hh" #include "lix/libstore/derivations.hh" @@ -119,6 +120,8 @@ ref EvalCommand::getEvaluator() startReplOnEvalErrors ? AbstractNixRepl::runSimple : nullptr ); + flake::initFlakes(evalState.get()); + evalState->repair = repair; } return ref::unsafeFromPtr(evalState); diff --git a/lix/libcmd/command.hh b/lix/libcmd/command.hh index da8cc4934..8add3435d 100644 --- a/lix/libcmd/command.hh +++ b/lix/libcmd/command.hh @@ -6,7 +6,7 @@ #include "lix/libutil/args.hh" #include "lix/libcmd/common-eval-args.hh" #include "lix/libstore/path.hh" -#include "lix/libexpr/flake/lockfile.hh" +#include "lix/libflakes/expr/lockfile.hh" #include "lix/libutil/async.hh" #include diff --git a/lix/libcmd/common-eval-args.cc b/lix/libcmd/common-eval-args.cc index eee525ce6..28744d2a1 100644 --- a/lix/libcmd/common-eval-args.cc +++ b/lix/libcmd/common-eval-args.cc @@ -5,7 +5,7 @@ #include "lix/libexpr/eval.hh" #include "lix/libfetchers/fetchers.hh" #include "lix/libfetchers/registry.hh" -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libflakes/expr/flakeref.hh" #include "lix/libstore/store-api.hh" #include "lix/libcmd/command.hh" #include "lix/libutil/async.hh" diff --git a/lix/libcmd/installable-attr-path.cc b/lix/libcmd/installable-attr-path.cc index 72db441e9..d7d1a95fc 100644 --- a/lix/libcmd/installable-attr-path.cc +++ b/lix/libcmd/installable-attr-path.cc @@ -5,7 +5,7 @@ #include "lix/libcmd/common-eval-args.hh" #include "lix/libexpr/eval.hh" #include "lix/libexpr/get-drvs.hh" -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" namespace nix { diff --git a/lix/libcmd/installable-flake.cc b/lix/libcmd/installable-flake.cc index 988248d93..91c025758 100644 --- a/lix/libcmd/installable-flake.cc +++ b/lix/libcmd/installable-flake.cc @@ -4,7 +4,7 @@ #include "lix/libexpr/attr-path.hh" #include "lix/libcmd/common-eval-args.hh" #include "lix/libexpr/eval.hh" -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" #include "lix/libexpr/eval-cache.hh" #include diff --git a/lix/libcmd/installable-value.hh b/lix/libcmd/installable-value.hh index 04f630650..7e8bd3862 100644 --- a/lix/libcmd/installable-value.hh +++ b/lix/libcmd/installable-value.hh @@ -4,7 +4,7 @@ #include "lix/libcmd/installables.hh" #include "lix/libexpr/eval-cache.hh" #include "lix/libexpr/eval.hh" -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" namespace nix { diff --git a/lix/libcmd/installables.cc b/lix/libcmd/installables.cc index a18323f69..2954c2582 100644 --- a/lix/libcmd/installables.cc +++ b/lix/libcmd/installables.cc @@ -14,7 +14,7 @@ #include "lix/libexpr/eval-settings.hh" #include "lix/libstore/store-api.hh" #include "lix/libmain/shared.hh" -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" #include "lix/libexpr/eval-cache.hh" #include "lix/libfetchers/registry.hh" #include "lix/libstore/build-result.hh" diff --git a/lix/libcmd/meson.build b/lix/libcmd/meson.build index e76faed89..70c1e4903 100644 --- a/lix/libcmd/meson.build +++ b/lix/libcmd/meson.build @@ -45,6 +45,7 @@ libcmd = library( liblixstore, liblixfetchers, liblixexpr, + liblixflakes, liblixmain, liblix_doc, boehm, @@ -76,6 +77,7 @@ liblixcmd = declare_dependency( dependencies : [ liblixutil, liblixstore, + liblixflakes, kj, ], link_with : libcmd, diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index b737e5521..887ffa457 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -22,8 +22,8 @@ #include "lix/libexpr/get-drvs.hh" #include "lix/libstore/derivations.hh" #include "lix/libstore/globals.hh" -#include "lix/libexpr/flake/flake.hh" -#include "lix/libexpr/flake/lockfile.hh" +#include "lix/libflakes/expr/flake.hh" +#include "lix/libflakes/expr/lockfile.hh" #include "lix/libcmd/editor-for.hh" #include "lix/libutil/finally.hh" #include "lix/libcmd/markdown.hh" diff --git a/lix/libexpr/builtins/import.md b/lix/libexpr/builtins/import.md index ec43e9c2c..32be2a2ff 100644 --- a/lix/libexpr/builtins/import.md +++ b/lix/libexpr/builtins/import.md @@ -6,8 +6,7 @@ renameInGlobalScope: false Load, parse and return the Nix expression in the file *path*. The value *path* can be a path, a string, or an attribute set with an -`__toString` attribute or a `outPath` attribute (as derivations or flake -inputs typically have). +`__toString` attribute or a `outPath` attribute (as derivations typically have). If *path* is a directory, the file `default.nix` in that directory is loaded. diff --git a/lix/libexpr/eval-settings.cc b/lix/libexpr/eval-settings.cc index 6cddff707..59c9536b2 100644 --- a/lix/libexpr/eval-settings.cc +++ b/lix/libexpr/eval-settings.cc @@ -6,48 +6,12 @@ namespace nix { -/* Very hacky way to parse $NIX_PATH, which is colon-separated, but - can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ -static Strings parseNixPath(const std::string & s) -{ - Strings res; - - auto p = s.begin(); - - while (p != s.end()) { - auto start = p; - auto start2 = p; - - while (p != s.end() && *p != ':') { - if (*p == '=') start2 = p + 1; - ++p; - } - - if (p == s.end()) { - if (p != start) res.push_back(std::string(start, p)); - break; - } - - if (*p == ':') { - auto prefix = std::string(start2, s.end()); - if (EvalSettings::isPseudoUrl(prefix) || prefix.starts_with("flake:")) { - ++p; - while (p != s.end() && *p != ':') ++p; - } - res.push_back(std::string(start, p)); - if (p == s.end()) break; - } - - ++p; - } - - return res; -} EvalSettings::EvalSettings() { - auto var = getEnv("NIX_PATH"); - if (var) nixPath.setDefault(parseNixPath(*var)); + //auto var = getEnv("NIX_PATH"); + // TODO(flakes): move that allowed prefix and parse it from eval plugins. + //if (var) nixPath.setDefault(parseNixPath(*var, )); } Strings EvalSettings::getDefaultNixPath() diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index d532d5da9..1d534cb04 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -21,7 +21,7 @@ #include "lix/libexpr/print.hh" #include "lix/libexpr/gc-small-vector.hh" #include "lix/libfetchers/fetch-to-store.hh" -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libfetchers/fetchers.hh" #include "lix/libutil/exit.hh" #include "lix/libutil/json.hh" #include "symbol-table.hh" @@ -318,14 +318,16 @@ EvalBuiltins::EvalBuiltins( EvalPaths::EvalPaths( AsyncIoRoot & aio, const ref & store, - SearchPath searchPath, + SearchPath initialSearchPath, + bool pureEval, EvalErrorContext & errors ) : store(store) - , searchPath_(std::move(searchPath)) + , searchPath_(std::move(initialSearchPath)) + , pureEval(pureEval) , errors(errors) { - if (evalSettings.restrictEval || evalSettings.pureEval) { + if (evalSettings.restrictEval || pureEval) { allowedPaths = AllowedPath{.allowAllChildren = false}; for (auto & i : searchPath_.elements) { @@ -349,6 +351,70 @@ EvalPaths::EvalPaths( } } +/* Very hacky way to parse $NIX_PATH, which is colon-separated, but + can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ +static Strings parseNixPath(const std::string & s, const std::set & allowedPrefixes) +{ + Strings res; + + auto p = s.begin(); + + while (p != s.end()) { + auto start = p; + auto start2 = p; + + while (p != s.end() && *p != ':') { + if (*p == '=') start2 = p + 1; + ++p; + } + + if (p == s.end()) { + if (p != start) res.push_back(std::string(start, p)); + break; + } + + if (*p == ':') { + auto prefix = std::string(start2, s.end()); + + auto matchesAllowedPrefixes = std::any_of( + allowedPrefixes.begin(), + allowedPrefixes.end(), + [&](const std::string & allowedPrefix) { + return prefix.starts_with(allowedPrefix); + } + ); + if (EvalSettings::isPseudoUrl(prefix) || matchesAllowedPrefixes) { + ++p; + while (p != s.end() && *p != ':') ++p; + } + res.push_back(std::string(start, p)); + if (p == s.end()) break; + } + + ++p; + } + + return res; +} + +void EvalPaths::parseAndConfigureEnvironmentNixPath(std::string const & value) +{ + if (!pureEval) { + for (auto & elem : parseNixPath(value, allowedNixPathPrefixes)) { + searchPath_.elements.emplace_back(SearchPath::Elem::parse(elem)); + } + } +} + +void EvalPaths::configureSettingNixPath(std::list const & values) +{ + if (!pureEval) { + for (auto & elem : values) { + searchPath_.elements.emplace_back(SearchPath::Elem::parse(elem)); + } + } +} + Evaluator::Evaluator( AsyncIoRoot & aio, const SearchPath & _searchPath, @@ -357,16 +423,7 @@ Evaluator::Evaluator( std::function debugRepl ) : s(symbols) - , paths(aio, store, [&] { - SearchPath searchPath; - if (!evalSettings.pureEval) { - for (auto & i : _searchPath.elements) - searchPath.elements.emplace_back(SearchPath::Elem {i}); - for (auto & i : evalSettings.nixPath.get()) - searchPath.elements.emplace_back(SearchPath::Elem::parse(i)); - } - return searchPath; - }(), errors) + , paths(aio, store, _searchPath, evalSettings.pureEval, errors) , builtins(mem, symbols, paths.searchPath(), store->config().storeDir) , repair(NoRepair) , store(store) @@ -392,6 +449,11 @@ Evaluator::Evaluator( box_ptr Evaluator::begin(AsyncIoRoot & aio) { assert(!activeEval); + if (auto envNixPath = getEnvNonEmpty("NIX_PATH")) { + paths.parseAndConfigureEnvironmentNixPath(*envNixPath); + } else { + paths.configureSettingNixPath(evalSettings.nixPath); + } return box_ptr::unsafeFromNonnull( std::unique_ptr(new EvalState(aio, *this)) ); @@ -2972,7 +3034,12 @@ try { std::optional res; - if (EvalSettings::isPseudoUrl(value)) { + for (auto & responder : searchPathResponders) { + if (responder.canResolve(value)) + res = TRY_AWAIT(responder.resolve(value, store)); + } + + if (!res && EvalSettings::isPseudoUrl(value)) { try { auto storePath = TRY_AWAIT(fetchers::downloadTarball( store, EvalSettings::resolvePseudoUrl(value), "source", false)).tree.storePath; @@ -2984,16 +3051,7 @@ try { } } - else if (value.starts_with("flake:")) { - experimentalFeatureSettings.require(Xp::Flakes); - auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false); - debug("fetching flake search path element '%s''", value); - auto storePath = - TRY_AWAIT(TRY_AWAIT(flakeRef.resolve(store)).fetchTree(store)).first.storePath; - res = {store->toRealPath(storePath)}; - } - - else { + else if (!res) { auto path = absPath(value); if (pathExists(path)) res = { path }; diff --git a/lix/libexpr/eval.hh b/lix/libexpr/eval.hh index 22fa12723..c77a5ec99 100644 --- a/lix/libexpr/eval.hh +++ b/lix/libexpr/eval.hh @@ -320,20 +320,47 @@ struct EvalErrorContext class EvalPaths { + struct Responder { + std::function canResolve; + std::function>> (std::string const &, ref)> resolve; + }; + ref store; + std::set allowedNixPathPrefixes; + std::vector searchPathResponders; SearchPath searchPath_; + bool pureEval; EvalErrorContext & errors; public: EvalPaths( AsyncIoRoot & aio, const ref & store, - SearchPath searchPath, + SearchPath initialSearchPath, + bool pureEval, EvalErrorContext & errors ); + void parseAndConfigureEnvironmentNixPath(std::string const & value); + void configureSettingNixPath(std::list const & value); + const SearchPath & searchPath() const { return searchPath_; } + std::string resolvePseudoUrl(std::string_view url) { + if (url.starts_with("channel:")) + return "https://channels.nixos.org/" + std::string(url.substr(8)) + "/nixexprs.tar.xz"; + else + return std::string(url); + } + + bool registerAllowedNixPathPrefix(const std::string & allowedPrefix) { + auto [_, added] = allowedNixPathPrefixes.insert(allowedPrefix); + return added; + } + void registerSearchPathResolver(std::function eligible, std::function>> (std::string const & value, ref store)> resolver) { + searchPathResponders.push_back({.canResolve=eligible,.resolve=resolver}); + } + private: struct AllowedPath { diff --git a/lix/libexpr/extra-primops.hh b/lix/libexpr/extra-primops.hh index 745acbc94..a77335258 100644 --- a/lix/libexpr/extra-primops.hh +++ b/lix/libexpr/extra-primops.hh @@ -19,12 +19,4 @@ void prim_getContext(EvalState & state, Value * * args, Value & v); void prim_hasContext(EvalState & state, Value * * args, Value & v); void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v); -namespace flake { - -void prim_flakeRefToString(EvalState & state, Value * * args, Value & v); -void prim_getFlake(EvalState & state, Value * * args, Value & v); -void prim_parseFlakeRef(EvalState & state, Value * * args, Value & v); - -} - } diff --git a/lix/libexpr/meson.build b/lix/libexpr/meson.build index 199b12de4..e1cac8609 100644 --- a/lix/libexpr/meson.build +++ b/lix/libexpr/meson.build @@ -11,7 +11,6 @@ foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix' ] install_dir : includedir / 'lix/libexpr', ) endforeach -subdir('flake') libexpr_setting_definitions = files( # keep-sorted start @@ -79,7 +78,6 @@ builtin_definitions = files( 'builtins/filter.md', 'builtins/filterSource.md', 'builtins/findFile.md', - 'builtins/flakeRefToString.md', 'builtins/floor.md', 'builtins/foldlStrict.md', 'builtins/fromJSON.md', @@ -90,7 +88,6 @@ builtin_definitions = files( 'builtins/getAttr.md', 'builtins/getContext.md', 'builtins/getEnv.md', - 'builtins/getFlake.md', 'builtins/groupBy.md', 'builtins/hasAttr.md', 'builtins/hasContext.md', @@ -116,7 +113,6 @@ builtin_definitions = files( 'builtins/match.md', 'builtins/mul.md', 'builtins/parseDrvName.md', - 'builtins/parseFlakeRef.md', 'builtins/partition.md', 'builtins/path.md', 'builtins/pathExists.md', @@ -207,10 +203,6 @@ libexpr_sources = files( 'eval-error.cc', 'eval-settings.cc', 'eval.cc', - 'flake/config.cc', - 'flake/flake.cc', - 'flake/flakeref.cc', - 'flake/lockfile.cc', 'function-trace.cc', 'gc-alloc.cc', 'get-drvs.cc', @@ -242,9 +234,6 @@ libexpr_headers = files( 'eval-inline.hh', 'eval-settings.hh', 'eval.hh', - 'flake/flake.hh', - 'flake/flakeref.hh', - 'flake/lockfile.hh', 'function-trace.hh', 'gc-alloc.hh', 'gc-small-vector.hh', diff --git a/lix/libexpr/settings/pure-eval.md b/lix/libexpr/settings/pure-eval.md index a87f7c63c..06241d4b1 100644 --- a/lix/libexpr/settings/pure-eval.md +++ b/lix/libexpr/settings/pure-eval.md @@ -12,7 +12,6 @@ Pure evaluation mode ensures that the result of Nix expressions is fully determi Access is nonetheless allowed to (absolute) paths in the Nix store that are returned by builtins like [`builtins.filterSource`](@docroot@/language/builtins.md#builtins-filterSource), [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball) and similar. - Impure fetches such as not specifying a commit ID for `builtins.fetchGit` or not specifying a hash for `builtins.fetchTarball` are rejected. - - In flakes, access to relative paths outside of the root of the flake's source tree (often, a git repository) is rejected. - The evaluator ignores `NIX_PATH`, `-I` and the `nix-path` setting. Thus, [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) is an empty list. - The builtins [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) are absent from `builtins`. - [`builtins.getEnv`](@docroot@/language/builtin-constants.md#builtins-currentSystem) always returns empty string for any variable. diff --git a/lix/libexpr/builtins/flakeRefToString.md b/lix/libflakes/builtins/flakeRefToString.md similarity index 100% rename from lix/libexpr/builtins/flakeRefToString.md rename to lix/libflakes/builtins/flakeRefToString.md diff --git a/lix/libexpr/builtins/getFlake.md b/lix/libflakes/builtins/getFlake.md similarity index 100% rename from lix/libexpr/builtins/getFlake.md rename to lix/libflakes/builtins/getFlake.md diff --git a/lix/libexpr/builtins/parseFlakeRef.md b/lix/libflakes/builtins/parseFlakeRef.md similarity index 100% rename from lix/libexpr/builtins/parseFlakeRef.md rename to lix/libflakes/builtins/parseFlakeRef.md diff --git a/lix/libexpr/flake/call-flake.nix b/lix/libflakes/expr/call-flake.nix similarity index 100% rename from lix/libexpr/flake/call-flake.nix rename to lix/libflakes/expr/call-flake.nix diff --git a/lix/libexpr/flake/config.cc b/lix/libflakes/expr/config.cc similarity index 99% rename from lix/libexpr/flake/config.cc rename to lix/libflakes/expr/config.cc index 6e72e8514..11eedecf6 100644 --- a/lix/libexpr/flake/config.cc +++ b/lix/libflakes/expr/config.cc @@ -1,4 +1,4 @@ -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" #include "lix/libutil/logging.hh" #include "lix/libutil/json.hh" #include "lix/libutil/users.hh" diff --git a/lix/libexpr/flake/flake.cc b/lix/libflakes/expr/flake.cc similarity index 98% rename from lix/libexpr/flake/flake.cc rename to lix/libflakes/expr/flake.cc index 044940abc..68ac4924f 100644 --- a/lix/libexpr/flake/flake.cc +++ b/lix/libflakes/expr/flake.cc @@ -1,8 +1,9 @@ -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" #include "lix/libexpr/eval.hh" #include "lix/libexpr/eval-settings.hh" #include "lix/libexpr/extra-primops.hh" -#include "lix/libexpr/flake/lockfile.hh" +#include "lix/libexpr/primops.hh" +#include "lix/libflakes/expr/lockfile.hh" #include "lix/libstore/store-api.hh" #include "lix/libfetchers/fetchers.hh" #include "lix/libutil/async.hh" @@ -994,6 +995,12 @@ void prim_getFlake(EvalState & state, Value * * args, Value & v) v); } +static RegisterPrimOp primop_getFlake(PrimOp{{ + .name = "getFlake", + .arity = 1, + .fun = prim_getFlake, +}}); + void prim_parseFlakeRef( EvalState & state, Value * * args, @@ -1015,6 +1022,12 @@ void prim_parseFlakeRef( v.mkAttrs(binds); } +static RegisterPrimOp primop_parseFlakeRef(PrimOp{{ + .name = "parseFlakeRef", + .arity = 1, + .fun = prim_parseFlakeRef, +}}); + void prim_flakeRefToString( EvalState & state, Value * * args, @@ -1053,6 +1066,12 @@ void prim_flakeRefToString( v.mkString(flakeRef.to_string()); } +static RegisterPrimOp primop_flakeRefToString(PrimOp{{ + .name = "flakeRefToString", + .arity = 1, + .fun = prim_flakeRefToString, +}}); + } Fingerprint LockedFlake::getFingerprint() const diff --git a/lix/libexpr/flake/flake.hh b/lix/libflakes/expr/flake.hh similarity index 98% rename from lix/libexpr/flake/flake.hh rename to lix/libflakes/expr/flake.hh index 007b5e037..ccdfe5ebf 100644 --- a/lix/libexpr/flake/flake.hh +++ b/lix/libflakes/expr/flake.hh @@ -3,8 +3,8 @@ #include "lix/libexpr/eval.hh" #include "lix/libutil/types.hh" -#include "lix/libexpr/flake/flakeref.hh" -#include "lix/libexpr/flake/lockfile.hh" +#include "lix/libflakes/expr/flakeref.hh" +#include "lix/libflakes/expr/lockfile.hh" #include "lix/libexpr/value.hh" namespace nix { diff --git a/lix/libexpr/flake/flakeref.cc b/lix/libflakes/expr/flakeref.cc similarity index 99% rename from lix/libexpr/flake/flakeref.cc rename to lix/libflakes/expr/flakeref.cc index 1e1a789d5..a813d5366 100644 --- a/lix/libexpr/flake/flakeref.cc +++ b/lix/libflakes/expr/flakeref.cc @@ -1,4 +1,4 @@ -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libflakes/expr/flakeref.hh" #include "lix/libstore/store-api.hh" #include "lix/libutil/async.hh" #include "lix/libutil/regex.hh" diff --git a/lix/libexpr/flake/flakeref.hh b/lix/libflakes/expr/flakeref.hh similarity index 100% rename from lix/libexpr/flake/flakeref.hh rename to lix/libflakes/expr/flakeref.hh diff --git a/lix/libexpr/flake/lockfile.cc b/lix/libflakes/expr/lockfile.cc similarity index 99% rename from lix/libexpr/flake/lockfile.cc rename to lix/libflakes/expr/lockfile.cc index 6998d6656..c6f4fdf09 100644 --- a/lix/libexpr/flake/lockfile.cc +++ b/lix/libflakes/expr/lockfile.cc @@ -1,4 +1,4 @@ -#include "lix/libexpr/flake/lockfile.hh" +#include "lix/libflakes/expr/lockfile.hh" #include "lix/libstore/store-api.hh" #include "lix/libutil/error.hh" #include "lix/libutil/json.hh" diff --git a/lix/libexpr/flake/lockfile.hh b/lix/libflakes/expr/lockfile.hh similarity index 98% rename from lix/libexpr/flake/lockfile.hh rename to lix/libflakes/expr/lockfile.hh index b67e68cd0..ab2d42438 100644 --- a/lix/libexpr/flake/lockfile.hh +++ b/lix/libflakes/expr/lockfile.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libflakes/expr/flakeref.hh" #include "lix/libutil/json-fwd.hh" namespace nix { diff --git a/lix/libexpr/flake/meson.build b/lix/libflakes/expr/meson.build similarity index 66% rename from lix/libexpr/flake/meson.build rename to lix/libflakes/expr/meson.build index cce1b0c75..e5652dda3 100644 --- a/lix/libexpr/flake/meson.build +++ b/lix/libflakes/expr/meson.build @@ -1,8 +1,8 @@ -libexpr_generated_headers += custom_target( +libflakes_generated_headers += custom_target( command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ], input : 'call-flake.nix', output : '@PLAINNAME@.gen.hh', capture : true, install : true, - install_dir : includedir / 'lix/libexpr/flake', + install_dir : includedir / 'lix/libflakes/expr', ) diff --git a/lix/libflakes/extra-primops.hh b/lix/libflakes/extra-primops.hh new file mode 100644 index 000000000..03438ee78 --- /dev/null +++ b/lix/libflakes/extra-primops.hh @@ -0,0 +1,13 @@ +#pragma once +///@file + +namespace nix { +class EvalState; +struct Value; + +namespace flake { +void prim_flakeRefToString(EvalState & state, Value * * args, Value & v); +void prim_getFlake(EvalState & state, Value * * args, Value & v); +void prim_parseFlakeRef(EvalState & state, Value * * args, Value & v); +} +} diff --git a/lix/libflakes/init.cc b/lix/libflakes/init.cc new file mode 100644 index 000000000..8da07424a --- /dev/null +++ b/lix/libflakes/init.cc @@ -0,0 +1,25 @@ +#include "lix/libflakes/init.hh" +#include "lix/libflakes/expr/flakeref.hh" +#include "lix/libexpr/eval.hh" +#include "lix/libstore/store-api.hh" + +// TODO: register the builtins +// register the setting for flakes (eval-cache)? + +namespace nix::flake { +void initFlakes(Evaluator * eval) +{ + // We inform the evaluator about Flakes during NIX_PATH processing. + eval->paths.registerAllowedNixPathPrefix("flake:"); + eval->paths.registerSearchPathResolver([&](std::string const & value) { + return value.starts_with("flake:"); + }, [&](std::string const & value, ref store) -> kj::Promise>> { + auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false); + debug("fetching flake search path element '%s'", value); + auto storePath = + TRY_AWAIT(TRY_AWAIT(flakeRef.resolve(store)).fetchTree(store)).first.storePath; + + co_return {store->toRealPath(storePath)}; + }); +} +} diff --git a/lix/libflakes/init.hh b/lix/libflakes/init.hh new file mode 100644 index 000000000..2e9ce9f89 --- /dev/null +++ b/lix/libflakes/init.hh @@ -0,0 +1,10 @@ +#pragma once +///@file + +namespace nix { +class Evaluator; + +namespace flake { + void initFlakes(Evaluator * eval); +} +} diff --git a/lix/libflakes/lix-flakes.pc.in b/lix/libflakes/lix-flakes.pc.in new file mode 100644 index 000000000..f1a0e457c --- /dev/null +++ b/lix/libflakes/lix-flakes.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Lix libflakes +Description: Lix Package Manager (libflakes) +Version: @PACKAGE_VERSION@ +# dependencies on boost is omitted since it is optional (only required by some headers) +Requires: lix-base lix-util lix-store lix-fetchers @BOEHM_IF_FOUND@ +Libs: -L${libdir} -llixflakes diff --git a/lix/libflakes/meson.build b/lix/libflakes/meson.build new file mode 100644 index 000000000..a41260375 --- /dev/null +++ b/lix/libflakes/meson.build @@ -0,0 +1,157 @@ +libflakes_generated_headers = [] + +subdir('expr') + +# libflakes_setting_definitions = files( +# # keep-sorted start +# # keep-sorted end +# ) +# libflakes_settings_header = custom_target( +# command : [ +# python.full_path(), +# '@SOURCE_ROOT@/lix/code-generation/build_settings.py', +# '--kernel', host_machine.system(), +# '--header', '@OUTPUT@', +# '--experimental-features', '@SOURCE_ROOT@/lix/libutil/experimental-features', +# '@INPUT@', +# ], +# input : libflakes_setting_definitions, +# output : 'libflakes-settings.gen.inc', +# install : true, +# install_dir : includedir / 'lix/libflakes', +# ) + +builtin_definitions = files( + # keep-sorted start + 'builtins/flakeRefToString.md', + 'builtins/getFlake.md', + 'builtins/parseFlakeRef.md', + # keep-sorted end +) +builtins_gen = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/lix/code-generation/build_builtins.py', + '--header', '@OUTPUT0@', + '--docs', '@OUTPUT1@', + '--experimental-features', '@SOURCE_ROOT@/lix/libutil/experimental-features', + '@INPUT@' + ], + input : builtin_definitions, + output : [ + 'register-builtins.gen.inc', + 'builtins.md', + ], +) +register_builtins_header = builtins_gen[0] +builtins_md = builtins_gen[1] + +libflakes_sources = files( + # keep-sorted start + 'expr/config.cc', + 'expr/flake.cc', + 'expr/flakeref.cc', + 'expr/lockfile.cc', + 'init.cc', + # keep-sorted end +) + +libflakes_headers = files( + # keep-sorted start + 'expr/flake.hh', + 'expr/flakeref.hh', + 'expr/lockfile.hh', + 'init.hh' + # keep-sorted end +) + +dependencies = [ + liblixutil, + liblixstore, + liblixfetchers, + liblixexpr, + boehm, + boost, + kj, + nlohmann_json, + toml11, +] + +libflakes_temp = library( + is_static ? 'lixflakes_temp' : 'lixflakes', + libflakes_sources, + # libflakes_settings_header, + libflakes_generated_headers, + register_builtins_header, + dependencies : dependencies, + # for shared.hh + include_directories : [ + '../libmain', + ], + cpp_pch : cpp_pch, + install : not is_static, + # FIXME(Qyriad): is this right? + install_rpath : libdir, +) +# FIXME: remove when https://git.lix.systems/lix-project/lix/issues/359 is fixed. +# FIXME: replace by prelink when https://github.com/mesonbuild/meson/pull/14846 is widely available. +if is_static + libflakes_prelink = custom_target( + 'lixflakes-prelink', + output : 'lixflakes-prelink.o', + input : libflakes_temp, + command : [ + cxx.cmd_array(), + '-r', + '-o', + '@OUTPUT@', + is_darwin ? '-Wl,-force_load' : '-Wl,--whole-archive', + '@INPUT@', + ], + ) + libflakes = library( + 'lixflakes', + [libflakes_prelink], + dependencies : dependencies, + install : true, + ) +else + libflakes = libflakes_temp +endif + +install_headers( + libflakes_headers, + subdir : 'lix/libflakes', + preserve_path : true, +) + +liblixflakes = declare_dependency( + include_directories : include_directories('../..'), + # sources : libflakes_settings_header, + dependencies : [ + liblixutil, + liblixexpr, + liblixfetchers, + boehm, + boost, + kj, + ], + link_with : libflakes, +) + +meson.override_dependency('lix-flakes', liblixflakes) + +# FIXME: not using the pkg-config module because it creates way too many deps +# while meson migration is in progress, and we want to not include boost here +configure_file( + input : 'lix-flakes.pc.in', + output : 'lix-flakes.pc', + install_dir : libdir / 'pkgconfig', + configuration : { + 'prefix' : prefix, + 'libdir' : libdir, + 'includedir' : includedir, + 'PACKAGE_VERSION' : meson.project_version(), + 'BOEHM_IF_FOUND' : boehm.found() ? 'bdw-gc' : '', + }, +) diff --git a/lix/libflakes/plugin.cc b/lix/libflakes/plugin.cc new file mode 100644 index 000000000..83384b13e --- /dev/null +++ b/lix/libflakes/plugin.cc @@ -0,0 +1,5 @@ +extern "C" void nix_plugin_entry() +{ + // 1. register an allowed prefix for NIX_PATH parsing + // 2. register a search path resolver +} diff --git a/lix/meson.build b/lix/meson.build index 40960c486..c01d6a86d 100644 --- a/lix/meson.build +++ b/lix/meson.build @@ -22,6 +22,8 @@ subdir('libstore') subdir('libfetchers') # libexpr depends on all of the above subdir('libexpr') +# libflakes depends on all of the above +subdir('libflakes') # libmain depends on libutil and libstore subdir('libmain') # libcmd depends on everything diff --git a/lix/nix/flake.cc b/lix/nix/flake.cc index 757d22258..827a4daa9 100644 --- a/lix/nix/flake.cc +++ b/lix/nix/flake.cc @@ -5,7 +5,7 @@ #include "lix/libexpr/eval.hh" #include "lix/libexpr/eval-inline.hh" // IWYU pragma: keep #include "lix/libexpr/eval-settings.hh" -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" #include "lix/libexpr/get-drvs.hh" #include "lix/libstore/store-api.hh" #include "lix/libstore/derivations.hh" diff --git a/lix/nix/meson.build b/lix/nix/meson.build index 35f1f9fb7..6747f8ceb 100644 --- a/lix/nix/meson.build +++ b/lix/nix/meson.build @@ -172,6 +172,7 @@ nix = executable( liblixstore, liblixfetchers, liblixexpr, + liblixflakes, liblixmain, liblixcmd, boehm, diff --git a/lix/nix/profile.cc b/lix/nix/profile.cc index cbfdb5cb3..1d2afab79 100644 --- a/lix/nix/profile.cc +++ b/lix/nix/profile.cc @@ -7,7 +7,7 @@ #include "lix/libstore/derivations.hh" #include "lix/libutil/archive.hh" #include "lix/libstore/builtins/buildenv.hh" -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libflakes/expr/flakeref.hh" #include "lix/libutil/regex.hh" #include "user-env.hh" #include "lix/libstore/profiles.hh" diff --git a/lix/nix/registry.cc b/lix/nix/registry.cc index acf852c87..743a69cbc 100644 --- a/lix/nix/registry.cc +++ b/lix/nix/registry.cc @@ -2,7 +2,7 @@ #include "lix/libmain/common-args.hh" #include "lix/libmain/shared.hh" #include "lix/libexpr/eval.hh" -#include "lix/libexpr/flake/flake.hh" +#include "lix/libflakes/expr/flake.hh" #include "lix/libstore/store-api.hh" #include "lix/libfetchers/fetchers.hh" #include "lix/libutil/url-parts.hh" diff --git a/tests/unit/libexpr/flakeref.cc b/tests/unit/libflakes/flakeref.cc similarity index 78% rename from tests/unit/libexpr/flakeref.cc rename to tests/unit/libflakes/flakeref.cc index e2ffd3239..c9a584c80 100644 --- a/tests/unit/libexpr/flakeref.cc +++ b/tests/unit/libflakes/flakeref.cc @@ -1,16 +1,20 @@ #include -#include "lix/libexpr/flake/flakeref.hh" +#include "lix/libflakes/expr/flakeref.hh" +#include "tests/libexpr.hh" namespace nix { +class FlakeRefTest : public LibExprTest {}; + + /* ----------- tests for flake/flakeref.hh --------------------------------------------------*/ /* ---------------------------------------------------------------------------- * to_string * --------------------------------------------------------------------------*/ - TEST(to_string, doesntReencodeUrl) { + TEST_F(FlakeRefTest, doesntReencodeUrl) { auto s = "http://localhost:8181/test/+3d.tar.gz"; auto flakeref = parseFlakeRef(s); auto parsed = flakeref.to_string(); diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 04fa7b0a4..c1969bd80 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -207,7 +207,6 @@ libexpr_tests_sources = files( 'libexpr/attr-path.cc', 'libexpr/derived-path.cc', 'libexpr/error_traces.cc', - 'libexpr/flakeref.cc', 'libexpr/json.cc', 'libexpr/primops.cc', 'libexpr/search-path.cc', @@ -247,6 +246,42 @@ test( verbose : false, ) +libflakes_tests_sources = files( + 'libflakes/flakeref.cc', +) + +libflakes_tester = executable( + 'liblixflakes-tests', + libflakes_tests_sources, + dependencies : [ + libasanoptions, + liblixexpr_test_support, + liblixstore_test_support, + liblixstore, + liblixutil, + liblixexpr, + liblixfetchers, + liblixflakes, + rapidcheck, + gtest, + nlohmann_json, + kj + ], + cpp_pch : cpp_pch +) + +test( + 'libflakes-unit-tests', + libflakes_tester, + args : tests_args, + env : default_test_env + { + '_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libflakes/data', + }, + suite : 'check', + protocol : 'gtest', + verbose : false, +) + libcmd_tester = executable( 'liblixcmd-tests', files('libcmd/args.cc'),