diff --git a/doc/manual/rl-next/c-api-flake-init.md b/doc/manual/rl-next/c-api-flake-init.md new file mode 100644 index 000000000..d6e7c3890 --- /dev/null +++ b/doc/manual/rl-next/c-api-flake-init.md @@ -0,0 +1,20 @@ +--- +synopsis: C API `nix_flake_init_global` removed +prs: 12759 +issues: 5638 +--- + +In order to improve the modularity of the code base, we are removing a use of global state, and therefore the `nix_flake_init_global` function. + +Instead, use `nix_flake_settings_add_to_eval_state_builder`. For example: + +```diff +- nix_flake_init_global(ctx, settings); +- HANDLE_ERROR(ctx); +- + nix_eval_state_builder * builder = nix_eval_state_builder_new(ctx, store); + HANDLE_ERROR(ctx); + ++ nix_flake_settings_add_to_eval_state_builder(ctx, settings, builder); ++ HANDLE_ERROR(ctx); +``` diff --git a/src/libflake-c/nix_api_flake.cc b/src/libflake-c/nix_api_flake.cc index 17cf6572d..2479bf2e0 100644 --- a/src/libflake-c/nix_api_flake.cc +++ b/src/libflake-c/nix_api_flake.cc @@ -1,6 +1,7 @@ #include "nix_api_flake.h" #include "nix_api_flake_internal.hh" #include "nix_api_util_internal.h" +#include "nix_api_expr_internal.h" #include "flake/flake.hh" @@ -18,15 +19,11 @@ void nix_flake_settings_free(nix_flake_settings * settings) delete settings; } -nix_err nix_flake_init_global(nix_c_context * context, nix_flake_settings * settings) +nix_err nix_flake_settings_add_to_eval_state_builder( + nix_c_context * context, nix_flake_settings * settings, nix_eval_state_builder * builder) { - static std::shared_ptr registeredSettings; try { - if (registeredSettings) - throw nix::Error("nix_flake_init_global already initialized"); - - registeredSettings = settings->settings; - nix::flake::initLib(*registeredSettings); + settings->settings->configureEvalSettings(builder->settings); } NIXC_CATCH_ERRS } diff --git a/src/libflake-c/nix_api_flake.h b/src/libflake-c/nix_api_flake.h index 80051298d..75675835e 100644 --- a/src/libflake-c/nix_api_flake.h +++ b/src/libflake-c/nix_api_flake.h @@ -35,9 +35,15 @@ nix_flake_settings * nix_flake_settings_new(nix_c_context * context); void nix_flake_settings_free(nix_flake_settings * settings); /** - * @brief Register Flakes support process-wide. + * @brief Initialize a `nix_flake_settings` to contain `builtins.getFlake` and + * potentially more. + * + * @param[out] context Optional, stores error information + * @param[in] settings The settings to use for e.g. `builtins.getFlake` + * @param[in] builder The builder to modify */ -nix_err nix_flake_init_global(nix_c_context * context, nix_flake_settings * settings); +nix_err nix_flake_settings_add_to_eval_state_builder( + nix_c_context * context, nix_flake_settings * settings, nix_eval_state_builder * builder); #ifdef __cplusplus } // extern "C" diff --git a/src/libflake-tests/nix_api_flake.cc b/src/libflake-tests/nix_api_flake.cc index 1678e3e55..9aa7773a0 100644 --- a/src/libflake-tests/nix_api_flake.cc +++ b/src/libflake-tests/nix_api_flake.cc @@ -25,13 +25,13 @@ TEST_F(nix_api_store_test, nix_api_init_global_getFlake_exists) assert_ctx_ok(); ASSERT_NE(nullptr, settings); - nix_flake_init_global(ctx, settings); - assert_ctx_ok(); - nix_eval_state_builder * builder = nix_eval_state_builder_new(ctx, store); ASSERT_NE(nullptr, builder); assert_ctx_ok(); + nix_flake_settings_add_to_eval_state_builder(ctx, settings, builder); + assert_ctx_ok(); + auto state = nix_eval_state_build(ctx, builder); assert_ctx_ok(); ASSERT_NE(nullptr, state); diff --git a/src/libflake/flake/flake-primops.cc b/src/libflake/flake/flake-primops.cc new file mode 100644 index 000000000..f04887e85 --- /dev/null +++ b/src/libflake/flake/flake-primops.cc @@ -0,0 +1,59 @@ +#include "flake-primops.hh" +#include "eval.hh" +#include "flake.hh" +#include "flakeref.hh" +#include "settings.hh" + +namespace nix::flake::primops { + +PrimOp getFlake(const Settings & settings) +{ + auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + std::string flakeRefS( + state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); + auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true); + if (state.settings.pureEval && !flakeRef.input.isLocked()) + throw Error( + "cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", + flakeRefS, + state.positions[pos]); + + callFlake( + state, + lockFlake( + settings, + state, + flakeRef, + LockFlags{ + .updateLockFile = false, + .writeLockFile = false, + .useRegistries = !state.settings.pureEval && settings.useRegistries, + .allowUnlocked = !state.settings.pureEval, + }), + v); + }; + + return PrimOp{ + .name = "__getFlake", + .args = {"args"}, + .doc = R"( + Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: + + ```nix + (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix + ``` + + Unless impure evaluation is allowed (`--impure`), the flake reference + must be "locked", e.g. contain a Git revision or content hash. An + example of an unlocked usage is: + + ```nix + (builtins.getFlake "github:edolstra/dwarffs").rev + ``` + )", + .fun = prim_getFlake, + .experimentalFeature = Xp::Flakes, + }; +} + +} // namespace nix::flake::primops diff --git a/src/libflake/flake/flake-primops.hh b/src/libflake/flake/flake-primops.hh new file mode 100644 index 000000000..662761c4e --- /dev/null +++ b/src/libflake/flake/flake-primops.hh @@ -0,0 +1,13 @@ +#pragma once + +#include "eval.hh" +#include "flake/settings.hh" + +namespace nix::flake::primops { + +/** + * Returns a `builtins.getFlake` primop with the given nix::flake::Settings. + */ +nix::PrimOp getFlake(const Settings & settings); + +} // namespace nix::flake \ No newline at end of file diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 475e159cd..1427b5818 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -973,49 +973,6 @@ void callFlake(EvalState & state, state.callFunction(*vCallFlake, args, vRes, noPos); } -void initLib(const Settings & settings) -{ - auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); - auto flakeRef = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true); - if (state.settings.pureEval && !flakeRef.input.isLocked()) - throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); - - callFlake(state, - lockFlake(settings, state, flakeRef, - LockFlags { - .updateLockFile = false, - .writeLockFile = false, - .useRegistries = !state.settings.pureEval && settings.useRegistries, - .allowUnlocked = !state.settings.pureEval, - }), - v); - }; - - RegisterPrimOp::primOps->push_back({ - .name = "__getFlake", - .args = {"args"}, - .doc = R"( - Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: - - ```nix - (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix - ``` - - Unless impure evaluation is allowed (`--impure`), the flake reference - must be "locked", e.g. contain a Git revision or content hash. An - example of an unlocked usage is: - - ```nix - (builtins.getFlake "github:edolstra/dwarffs").rev - ``` - )", - .fun = prim_getFlake, - .experimentalFeature = Xp::Flakes, - }); -} - static void prim_parseFlakeRef( EvalState & state, const PosIdx pos, diff --git a/src/libflake/flake/flake.hh b/src/libflake/flake/flake.hh index d8cd9aac0..d7a151587 100644 --- a/src/libflake/flake/flake.hh +++ b/src/libflake/flake/flake.hh @@ -14,14 +14,6 @@ namespace flake { struct Settings; -/** - * Initialize `libnixflake` - * - * So far, this registers the `builtins.getFlake` primop, which depends - * on the choice of `flake:Settings`. - */ -void initLib(const Settings & settings); - struct FlakeInput; typedef std::map FlakeInputs; diff --git a/src/libflake/flake/settings.cc b/src/libflake/flake/settings.cc index 6a0294e62..f5f9f96d0 100644 --- a/src/libflake/flake/settings.cc +++ b/src/libflake/flake/settings.cc @@ -1,7 +1,13 @@ #include "flake/settings.hh" +#include "flake/flake-primops.hh" namespace nix::flake { Settings::Settings() {} +void Settings::configureEvalSettings(nix::EvalSettings & evalSettings) +{ + evalSettings.addPrimOp(primops::getFlake(*this)); } + +} // namespace nix diff --git a/src/libflake/flake/settings.hh b/src/libflake/flake/settings.hh index 991eaca1f..f629f3e74 100644 --- a/src/libflake/flake/settings.hh +++ b/src/libflake/flake/settings.hh @@ -1,21 +1,24 @@ #pragma once ///@file -#include "types.hh" #include "config.hh" -#include "util.hh" - -#include -#include #include +namespace nix { +// Forward declarations +struct EvalSettings; + +} // namespace nix + namespace nix::flake { struct Settings : public Config { Settings(); + void configureEvalSettings(nix::EvalSettings & evalSettings); + Setting useRegistries{ this, true, diff --git a/src/libflake/meson.build b/src/libflake/meson.build index b757d0d76..7dee31574 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -44,6 +44,7 @@ sources = files( 'flake/flake.cc', 'flake/flakeref.cc', 'flake/lockfile.cc', + 'flake/flake-primops.cc', 'flake/settings.cc', 'flake/url-name.cc', ) diff --git a/src/nix/main.cc b/src/nix/main.cc index 0a6b77e9e..188d424bc 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -18,6 +18,7 @@ #include "network-proxy.hh" #include "eval-cache.hh" #include "flake/flake.hh" +#include "flake/settings.hh" #include "self-exe.hh" #include "json-utils.hh" #include "crash-handler.hh" @@ -368,7 +369,7 @@ void mainWrapped(int argc, char * * argv) initNix(); initGC(); - flake::initLib(flakeSettings); + flakeSettings.configureEvalSettings(evalSettings); /* Set the build hook location