From 21fc0ddce55a3caa4cc920c8394d1f9f218661ed Mon Sep 17 00:00:00 2001 From: Alois Wohlschlager Date: Sun, 8 Sep 2024 19:01:46 +0200 Subject: [PATCH 1/3] libutil: generate experimental and deprecated features from data Currently, a bunch of documentation is generated by embedding parts of it in the nix executable, getting it out again by running it, and then postprocessing the output. This is bad, since it creates a pointless dependency of the documentation on the executable, and also makes documentation generation impossible when cross-compiling. Instead, both the code and the documentation should be generated from data, see https://git.lix.systems/lix-project/lix/issues/292 . Here we start applying this approach to the experimental and deprecated features, which are done in one go since the technical implementation is very similar. Of course, the actual benefits are not realised yet, since the offending pattern is used in several more places. These will be fixed later. Change-Id: I4c802052cc7e865c61119a34b8f1063c4decc9cb --- .gitignore | 2 +- doc/manual/generate-features-shortlist.nix | 14 -- doc/manual/generate-features.nix | 18 -- doc/manual/meson.build | 12 +- doc/manual/src/command-ref/meson.build | 34 --- .../src/contributing/deprecated-features.md | 2 +- .../src/contributing/experimental-features.md | 2 +- doc/manual/src/contributing/hacking.md | 14 ++ doc/manual/src/contributing/meson.build | 28 --- meson/clang-tidy/build_required_targets.py | 3 +- package.nix | 2 + .../build_experimental_features.py | 58 +++++ src/code-generation/common.py | 50 ++++ src/libutil/config.hh | 4 +- src/libutil/deprecated-features.cc | 33 +-- src/libutil/deprecated-features.hh | 9 +- .../deprecated-features/ancient-let.md | 7 + .../deprecated-features/rec-set-overrides.md | 7 + .../deprecated-features/url-literals.md | 5 + src/libutil/experimental-features.cc | 231 +----------------- src/libutil/experimental-features.hh | 22 +- .../auto-allocate-uids.md | 6 + .../experimental-features/ca-derivations.md | 9 + src/libutil/experimental-features/cgroups.md | 6 + .../daemon-trust-override.md | 8 + .../dynamic-derivations.md | 11 + .../experimental-features/fetch-closure.md | 5 + src/libutil/experimental-features/flakes.md | 6 + .../impure-derivations.md | 28 +++ .../experimental-features/nix-command.md | 6 + .../experimental-features/no-url-literals.md | 39 +++ .../parse-toml-timestamps.md | 5 + .../experimental-features/pipe-operator.md | 8 + .../read-only-local-store.md | 5 + .../experimental-features/recursive-nix.md | 39 +++ .../experimental-features/repl-automation.md | 5 + .../experimental-features/repl-flake.md | 5 + src/libutil/meson.build | 98 ++++++++ 38 files changed, 452 insertions(+), 394 deletions(-) delete mode 100644 doc/manual/generate-features-shortlist.nix delete mode 100644 doc/manual/generate-features.nix delete mode 100644 doc/manual/src/contributing/meson.build create mode 100644 src/code-generation/build_experimental_features.py create mode 100644 src/code-generation/common.py create mode 100644 src/libutil/deprecated-features/ancient-let.md create mode 100644 src/libutil/deprecated-features/rec-set-overrides.md create mode 100644 src/libutil/deprecated-features/url-literals.md create mode 100644 src/libutil/experimental-features/auto-allocate-uids.md create mode 100644 src/libutil/experimental-features/ca-derivations.md create mode 100644 src/libutil/experimental-features/cgroups.md create mode 100644 src/libutil/experimental-features/daemon-trust-override.md create mode 100644 src/libutil/experimental-features/dynamic-derivations.md create mode 100644 src/libutil/experimental-features/fetch-closure.md create mode 100644 src/libutil/experimental-features/flakes.md create mode 100644 src/libutil/experimental-features/impure-derivations.md create mode 100644 src/libutil/experimental-features/nix-command.md create mode 100644 src/libutil/experimental-features/no-url-literals.md create mode 100644 src/libutil/experimental-features/parse-toml-timestamps.md create mode 100644 src/libutil/experimental-features/pipe-operator.md create mode 100644 src/libutil/experimental-features/read-only-local-store.md create mode 100644 src/libutil/experimental-features/recursive-nix.md create mode 100644 src/libutil/experimental-features/repl-automation.md create mode 100644 src/libutil/experimental-features/repl-flake.md diff --git a/.gitignore b/.gitignore index 15c871218..146d433a1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,5 +37,5 @@ buildtime.bin # Rust build files when using Cargo (not actually supported for building but it spews the files anyway) /target/ -# Python compiled files from the test suite +# Python compiled files from the code generators and test suite *.pyc diff --git a/doc/manual/generate-features-shortlist.nix b/doc/manual/generate-features-shortlist.nix deleted file mode 100644 index 055698d64..000000000 --- a/doc/manual/generate-features-shortlist.nix +++ /dev/null @@ -1,14 +0,0 @@ -# Usually "experimental" or "deprecated" -kind: -# "xp" or "dp" -kindShort: - -with builtins; -with import ./utils.nix; - -let - showExperimentalFeature = name: doc: '' - - [`${name}`](@docroot@/contributing/${kind}-features.md#${kindShort}-feature-${name}) - ''; -in -xps: indent " " (concatStrings (attrValues (mapAttrs showExperimentalFeature xps))) diff --git a/doc/manual/generate-features.nix b/doc/manual/generate-features.nix deleted file mode 100644 index 4a12ccdce..000000000 --- a/doc/manual/generate-features.nix +++ /dev/null @@ -1,18 +0,0 @@ -# Usually "experimental" or "deprecated" -_kind: -# "xp" or "dp" -kindShort: - -with builtins; -with import ./utils.nix; - -let - showFeature = - name: doc: - squash '' - ## [`${name}`]{#${kindShort}-feature-${name}} - - ${doc} - ''; -in -xps: (concatStringsSep "\n" (attrValues (mapAttrs showFeature xps))) diff --git a/doc/manual/meson.build b/doc/manual/meson.build index 35d94740c..02383f6bb 100644 --- a/doc/manual/meson.build +++ b/doc/manual/meson.build @@ -48,14 +48,6 @@ nix_conf_file_md = custom_target( output : 'conf-file.md', ) -nix_exp_features_json = custom_target( - command : [ nix, '__dump-xp-features' ], - capture : true, - output : 'xp-features.json', - # FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though. - build_always_stale : true, -) - language_json = custom_target( command: [nix, '__dump-language'], output : 'language.json', @@ -80,10 +72,8 @@ generate_manual_deps = files( # Generates builtins.md and builtin-constants.md. subdir('src/language') -# Generates new-cli pages, {experimental,deprecated}-features-shortlist.md, and conf-file.md. +# Generates new-cli pages and conf-file.md. subdir('src/command-ref') -# Generates {experimental,deprecated}-feature-descriptions.md. -subdir('src/contributing') # Generates rl-next-generated.md. subdir('src/release-notes') diff --git a/doc/manual/src/command-ref/meson.build b/doc/manual/src/command-ref/meson.build index f66ee0d39..6acc79f4e 100644 --- a/doc/manual/src/command-ref/meson.build +++ b/doc/manual/src/command-ref/meson.build @@ -1,37 +1,3 @@ -experimental_features_shortlist_md = custom_target( - command : nix_eval_for_docs + [ - '--expr', - 'import @INPUT0@ "experimental" "xp" (builtins.fromJSON (builtins.readFile @INPUT1@))', - ], - input : [ - '../../generate-features-shortlist.nix', - nix_exp_features_json, - ], - capture : true, - output : 'experimental-features-shortlist.md', - env : nix_env_for_docs, -) - -dp_features_json = custom_target( - command : [nix, '__dump-dp-features'], - capture : true, - output : 'dp-features.json', -) - -deprecated_features_shortlist_md = custom_target( - command : nix_eval_for_docs + [ - '--expr', - 'import @INPUT0@ "deprecated" "dp" (builtins.fromJSON (builtins.readFile @INPUT1@))', - ], - input : [ - '../../generate-features-shortlist.nix', - dp_features_json, - ], - capture : true, - output : 'deprecated-features-shortlist.md', - env : nix_env_for_docs, -) - # Intermediate step for manpage generation. # This splorks the output of generate-manpage.nix as JSON, # which gets written as a directory tree below. diff --git a/doc/manual/src/contributing/deprecated-features.md b/doc/manual/src/contributing/deprecated-features.md index 7536944f7..ef37a1d98 100644 --- a/doc/manual/src/contributing/deprecated-features.md +++ b/doc/manual/src/contributing/deprecated-features.md @@ -34,4 +34,4 @@ However, we do not live in such an ideal world, and currently this goal is so fa # Currently available deprecated features -{{#include @generated@/contributing/deprecated-feature-descriptions.md}} +{{#include @generated@/../../../src/libutil/deprecated-feature-descriptions.md}} diff --git a/doc/manual/src/contributing/experimental-features.md b/doc/manual/src/contributing/experimental-features.md index c27ba5da0..d5f6f2526 100644 --- a/doc/manual/src/contributing/experimental-features.md +++ b/doc/manual/src/contributing/experimental-features.md @@ -99,4 +99,4 @@ This means that experimental features and RFCs are orthogonal mechanisms, and ca # Currently available experimental features -{{#include @generated@/contributing/experimental-feature-descriptions.md}} +{{#include @generated@/../../../src/libutil/experimental-feature-descriptions.md}} diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 37c96a5c0..d245e20d1 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -399,3 +399,17 @@ The following properties are supported: Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. Set `buildUnreleasedNotes = true;` in `flake.nix` to build the release notes on the fly. + +## Adding experimental or deprecated features + +Experimental and deprecated features are generally referenced both in the code and in the documentation. +To prevent duplication or divergence, they are defined in data files, and a script generates the necessary glue. + +The data file format is similar to the release notes: it consists of a YAML metadata header, followed by the documentation in Markdown format. +The following metadata properties are supported: +* `name` (required): user-facing name of the feature, to be used in `nix.conf` options and on the command line. + This should also be the stem of the file name (with extension `md`). +* `internalName` (required): identifier used to refer to the feature inside the C++ code. + +Experimental feature data files should live in `src/libutil/experimental-features`, and deprecated features in `src/libutil/deprecated-features`. +They must be listed in the `experimental_feature_definitions` or `deprecated_feature_definitions` lists in `src/libutil/meson.build` respectively to be considered by the build system. diff --git a/doc/manual/src/contributing/meson.build b/doc/manual/src/contributing/meson.build deleted file mode 100644 index da88ca146..000000000 --- a/doc/manual/src/contributing/meson.build +++ /dev/null @@ -1,28 +0,0 @@ -# Intermediate step for experimental-feature-descriptions.md. -# This splorks the output of generate-xp-features.nix as JSON, -# which gets written as a directory tree below. -experimental_feature_descriptions_md = custom_target( - command : nix_eval_for_docs + [ - '--expr', - 'import @INPUT0@ "experimental" "xp" (builtins.fromJSON (builtins.readFile @INPUT1@))', - ], - input : [ - '../../generate-features.nix', - nix_exp_features_json, - ], - capture : true, - output : 'experimental-feature-descriptions.md', -) - -deprecated_feature_descriptions_md = custom_target( - command : nix_eval_for_docs + [ - '--expr', - 'import @INPUT0@ "deprecated" "dp" (builtins.fromJSON (builtins.readFile @INPUT1@))', - ], - input : [ - '../../generate-features.nix', - dp_features_json, - ], - capture : true, - output : 'deprecated-feature-descriptions.md', -) diff --git a/meson/clang-tidy/build_required_targets.py b/meson/clang-tidy/build_required_targets.py index 5c0e9641e..f26aa24a5 100755 --- a/meson/clang-tidy/build_required_targets.py +++ b/meson/clang-tidy/build_required_targets.py @@ -14,7 +14,8 @@ def main(): args = ap.parse_args() - targets = [t for t in get_targets_of_rule(args.build_root, 'CUSTOM_COMMAND') if t.endswith('gen.hh')] + targets = [t for t in get_targets_of_rule(args.build_root, 'CUSTOM_COMMAND') if t.endswith('.gen.hh')] + targets += [t for t in get_targets_of_rule(args.build_root, 'CUSTOM_COMMAND') if t.endswith('.gen.inc')] ninja_build(args.build_root, targets) if __name__ == '__main__': diff --git a/package.nix b/package.nix index 5c66b8446..297bc01fc 100644 --- a/package.nix +++ b/package.nix @@ -245,9 +245,11 @@ stdenv.mkDerivation (finalAttrs: { nativeBuildInputs = [ + # python3.withPackages does not splice properly, see https://github.com/NixOS/nixpkgs/issues/305858 (python3.pythonOnBuildForHost.withPackages (p: [ p.pytest p.pytest-xdist + p.python-frontmatter ])) meson ninja diff --git a/src/code-generation/build_experimental_features.py b/src/code-generation/build_experimental_features.py new file mode 100644 index 000000000..8768d826e --- /dev/null +++ b/src/code-generation/build_experimental_features.py @@ -0,0 +1,58 @@ +from typing import NamedTuple + +from common import cxx_literal, generate_file, load_data + +KNOWN_KEYS = set([ + 'name', + 'internalName', +]) + +class ExperimentalFeature(NamedTuple): + name: str + internal_name: str + description: str + + def parse(datum): + unknown_keys = set(datum.keys()) - KNOWN_KEYS + if unknown_keys: + raise ValueError('unknown keys', unknown_keys) + return ExperimentalFeature( + name = datum['name'], + internal_name = datum['internalName'], + description = datum.content, + ) + +def main(): + import argparse + + ap = argparse.ArgumentParser() + ap.add_argument('--deprecated', action='store_true', help='Generate deprecated features') + ap.add_argument('--header', help='Path of the declaration header to generate') + ap.add_argument('--impl-header', help='Path of the implementation header to generate') + ap.add_argument('--descriptions', help='Path of the description file to generate') + ap.add_argument('--shortlist', help='Path of the shortlist file to generate') + ap.add_argument('defs', help='Experimental feature definition files', nargs='+') + args = ap.parse_args() + + features = load_data(args.defs, ExperimentalFeature.parse) + + generate_file(args.header, features, lambda feature: feature.name, lambda feature: + f' {feature.internal_name},\n') + generate_file(args.impl_header, features, lambda feature: feature.name, lambda feature: + f''' {{ + .tag = {"Dep" if args.deprecated else "Xp"}::{feature.internal_name}, + .name = {cxx_literal(feature.name)}, + .description = {cxx_literal(feature.description)}, + }}, +''') + generate_file(args.descriptions, features, lambda feature: feature.name, lambda feature: + f'''## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}} + +{feature.description} + +''') + generate_file(args.shortlist, features, lambda feature: feature.name, lambda feature: + f' - [`{feature.name}`](@docroot@/contributing/{"deprecated" if args.deprecated else "experimental"}-features.md#{"dp" if args.deprecated else "xp"}-feature-{feature.name})\n') + +if __name__ == '__main__': + main() diff --git a/src/code-generation/common.py b/src/code-generation/common.py new file mode 100644 index 000000000..228359c78 --- /dev/null +++ b/src/code-generation/common.py @@ -0,0 +1,50 @@ +import frontmatter +import pathlib +from collections import defaultdict + +def cxx_escape_character(c): + if ord(c) >= 0x20 and ord(c) < 0x7f and c != '"' and c != '?' and c != '\\': + return c + elif c == '\t': + return r'\t' + elif c == '\n': + return r'\n' + elif c == '\r': + return r'\r' + elif c == '"': + return r'\"' + elif c == '?': + return r'\?' + elif c == '\\': + return r'\\' + elif ord(c) <= 0xffff: + return str.format(r'\u{:04x}', ord(c)) + else: + return str.format(r'\U{:08x}', ord(c)) + +def cxx_literal(v): + if isinstance(v, str): + return ''.join(['"', *(cxx_escape_character(c) for c in v), '"']) + else: + raise NotImplementedError(f'cannot represent {repr(v)} in C++') + +def load_data(defs, parse_function): + data = [] + for path in defs: + try: + datum = frontmatter.load(path) + data.append((path, parse_function(datum))) + except Exception as e: + e.add_note(f'in {path}') + raise + return data + +def generate_file(path, data, sort_key_function, generate_function): + if path is not None: + with open(path, 'w') as out: + for path, datum in sorted(data, key=lambda pathAndDatum: sort_key_function(pathAndDatum[1])): + try: + out.write(generate_function(datum)) + except Exception as e: + e.add_note(f'in {path}') + raise diff --git a/src/libutil/config.hh b/src/libutil/config.hh index b3dcc122f..eadc71926 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -410,7 +410,7 @@ struct FeatureSettings : Config { The following experimental features are available: - {{#include @generated@/command-ref/experimental-features-shortlist.md}} + {{#include @generated@/../../../src/libutil/experimental-features-shortlist.md}} Experimental features are [further documented in the manual](@docroot@/contributing/experimental-features.md). )"}; @@ -450,7 +450,7 @@ struct FeatureSettings : Config { The following deprecated feature features can be re-activated: - {{#include @generated@/command-ref/deprecated-features-shortlist.md}} + {{#include @generated@/../../../src/libutil/deprecated-features-shortlist.md}} Deprecated features are [further documented in the manual](@docroot@/contributing/deprecated-features.md). )"}; diff --git a/src/libutil/deprecated-features.cc b/src/libutil/deprecated-features.cc index 4de4c8ec7..65a003cd4 100644 --- a/src/libutil/deprecated-features.cc +++ b/src/libutil/deprecated-features.cc @@ -20,38 +20,13 @@ struct DeprecatedFeatureDetails * counter will be incremented once instead of twice, causing a build * failure. * - * By instead defining this instead as 1 + the bottom deprecated - * feature, we either have no issue at all if few features are not added - * at the end of the list, or a proper merge conflict if they are. + * By instead defining this instead as a dummy bottom deprecated + * feature, we do not have this issue. */ -constexpr size_t numDepFeatures = 1 + static_cast(Dep::UrlLiterals); +constexpr size_t numDepFeatures = static_cast(Dep::NumDepFeatures); constexpr std::array depFeatureDetails = {{ - { - .tag = Dep::RecSetOverrides, - .name = "rec-set-overrides", - .description = R"( - Allow `__overrides` in recursive attribute sets. - - Use fix point functions (e.g. `lib.fix` in Nixpkgs) instead. - )", - }, - { - .tag = Dep::AncientLet, - .name = "ancient-let", - .description = R"( - Allow the ancient `let { body = …; … }` syntax. - - Use the `let … in` syntax instead. - )", - }, - { - .tag = Dep::UrlLiterals, - .name = "url-literals", - .description = R"( - Allow unquoted URLs as part of the Nix language syntax. - )", - }, + #include "deprecated-features-impl.gen.inc" }}; static_assert( diff --git a/src/libutil/deprecated-features.hh b/src/libutil/deprecated-features.hh index bdff1bcdb..cd425e8f7 100644 --- a/src/libutil/deprecated-features.hh +++ b/src/libutil/deprecated-features.hh @@ -9,18 +9,13 @@ namespace nix { /** * The list of available deprecated features. * - * If you update this, don’t forget to also change the map defining - * their string representation and documentation in the corresponding - * `.cc` file as well. - * * Reminder: New deprecated features should start out with a warning without throwing an error. * See the developer documentation for details. */ enum struct DeprecatedFeature { - RecSetOverrides, - AncientLet, - UrlLiterals, + #include "deprecated-features.gen.inc" + NumDepFeatures, // number of available deprecated features, do not use }; enum struct DeprecatedFeatures {}; diff --git a/src/libutil/deprecated-features/ancient-let.md b/src/libutil/deprecated-features/ancient-let.md new file mode 100644 index 000000000..ced9934bb --- /dev/null +++ b/src/libutil/deprecated-features/ancient-let.md @@ -0,0 +1,7 @@ +--- +name: ancient-let +internalName: AncientLet +--- +Allow the ancient `let { body = …; … }` syntax. + +Use the `let … in` syntax instead. diff --git a/src/libutil/deprecated-features/rec-set-overrides.md b/src/libutil/deprecated-features/rec-set-overrides.md new file mode 100644 index 000000000..37d7003b1 --- /dev/null +++ b/src/libutil/deprecated-features/rec-set-overrides.md @@ -0,0 +1,7 @@ +--- +name: rec-set-overrides +internalName: RecSetOverrides +--- +Allow `__overrides` in recursive attribute sets. + +Use fix point functions (e.g. `lib.fix` in Nixpkgs) instead. diff --git a/src/libutil/deprecated-features/url-literals.md b/src/libutil/deprecated-features/url-literals.md new file mode 100644 index 000000000..99232913f --- /dev/null +++ b/src/libutil/deprecated-features/url-literals.md @@ -0,0 +1,5 @@ +--- +name: url-literals +internalName: UrlLiterals +--- +Allow unquoted URLs as part of the Nix language syntax. diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index bb7a95a24..da3e6c2cd 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -20,236 +20,13 @@ struct ExperimentalFeatureDetails * counter will be incremented once instead of twice, causing a build * failure. * - * By instead defining this instead as 1 + the bottom experimental - * feature, we either have no issue at all if few features are not added - * at the end of the list, or a proper merge conflict if they are. + * By instead defining this instead as a dummy bottom experimental + * feature, we do not get this issue. */ -constexpr size_t numXpFeatures = 1 + static_cast(Xp::ReplAutomation); +constexpr size_t numXpFeatures = static_cast(Xp::NumXpFeatures); constexpr std::array xpFeatureDetails = {{ - { - .tag = Xp::CaDerivations, - .name = "ca-derivations", - .description = R"( - Allow derivations to be content-addressed in order to prevent - rebuilds when changes to the derivation do not result in changes to - the derivation's output. See - [__contentAddressed](@docroot@/language/advanced-attributes.md#adv-attr-__contentAddressed) - for details. - )", - }, - { - .tag = Xp::ImpureDerivations, - .name = "impure-derivations", - .description = R"( - Allow derivations to produce non-fixed outputs by setting the - `__impure` derivation attribute to `true`. An impure derivation can - have differing outputs each time it is built. - - Example: - - ``` - derivation { - name = "impure"; - builder = /bin/sh; - __impure = true; # mark this derivation as impure - args = [ "-c" "read -n 10 random < /dev/random; echo $random > $out" ]; - system = builtins.currentSystem; - } - ``` - - Each time this derivation is built, it can produce a different - output (as the builder outputs random bytes to `$out`). Impure - derivations also have access to the network, and only fixed-output - or other impure derivations can rely on impure derivations. Finally, - an impure derivation cannot also be - [content-addressed](#xp-feature-ca-derivations). - - This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime). - )", - }, - { - .tag = Xp::Flakes, - .name = "flakes", - .description = R"( - Enable flakes. See the manual entry for [`nix - flake`](@docroot@/command-ref/new-cli/nix3-flake.md) for details. - )", - }, - { - .tag = Xp::NixCommand, - .name = "nix-command", - .description = R"( - Enable the new `nix` subcommands. See the manual on - [`nix`](@docroot@/command-ref/new-cli/nix.md) for details. - )", - }, - { - .tag = Xp::RecursiveNix, - .name = "recursive-nix", - .description = R"( - Allow derivation builders to call Nix, and thus build derivations - recursively. - - Example: - - ``` - with import {}; - - runCommand "foo" - { - buildInputs = [ nix jq ]; - NIX_PATH = "nixpkgs=${}"; - } - '' - hello=$(nix-build -E '(import {}).hello.overrideDerivation (args: { name = "recursive-hello"; })') - - mkdir -p $out/bin - ln -s $hello/bin/hello $out/bin/hello - '' - ``` - - An important restriction on recursive builders is disallowing - arbitrary substitutions. For example, running - - ``` - nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10 - ``` - - in the above `runCommand` script would be disallowed, as this could - lead to derivations with hidden dependencies or breaking - reproducibility by relying on the current state of the Nix store. An - exception would be if - `/nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10` were - already in the build inputs or built by a previous recursive Nix - call. - )", - }, - { - .tag = Xp::NoUrlLiterals, - .name = "no-url-literals", - .description = R"( - Disallow unquoted URLs as part of the Nix language syntax. The Nix - language allows for URL literals, like so: - - ``` - $ nix repl - Welcome to Nix 2.15.0. Type :? for help. - - nix-repl> http://foo - "http://foo" - ``` - - But enabling this experimental feature will cause the Nix parser to - throw an error when encountering a URL literal: - - ``` - $ nix repl --extra-experimental-features 'no-url-literals' - Welcome to Nix 2.15.0. Type :? for help. - - nix-repl> http://foo - error: URL literals are disabled - - at «string»:1:1: - - 1| http://foo - | ^ - - ``` - - While this is currently an experimental feature, unquoted URLs are - being deprecated and their usage is discouraged. - - The reason is that, as opposed to path literals, URLs have no - special properties that distinguish them from regular strings, URLs - containing parameters have to be quoted anyway, and unquoted URLs - may confuse external tooling. - )", - }, - { - .tag = Xp::PipeOperator, - .name = "pipe-operator", - .description = R"( - Enable new operators for function application to "pipe" arguments through a chain of functions similar to `lib.pipe`. - This implementation is based on Nix [RFC 148](https://github.com/NixOS/rfcs/pull/148). - - Tracking issue: https://git.lix.systems/lix-project/lix/issues/438 - )", - }, - { - .tag = Xp::FetchClosure, - .name = "fetch-closure", - .description = R"( - Enable the use of the [`fetchClosure`](@docroot@/language/builtins.md#builtins-fetchClosure) built-in function in the Nix language. - )", - }, - { - .tag = Xp::ReplFlake, - .name = "repl-flake", - .description = R"( - Allow passing [installables](@docroot@/command-ref/new-cli/nix.md#installables) to `nix repl`, making its interface consistent with the other experimental commands. - )", - }, - { - .tag = Xp::AutoAllocateUids, - .name = "auto-allocate-uids", - .description = R"( - Allows Nix to automatically pick UIDs for builds, rather than creating - `nixbld*` user accounts. See the [`auto-allocate-uids`](#conf-auto-allocate-uids) setting for details. - )", - }, - { - .tag = Xp::Cgroups, - .name = "cgroups", - .description = R"( - Allows Nix to execute builds inside cgroups. See - the [`use-cgroups`](#conf-use-cgroups) setting for details. - )", - }, - { - .tag = Xp::DaemonTrustOverride, - .name = "daemon-trust-override", - .description = R"( - Allow forcing trusting or not trusting clients with - `nix-daemon`. This is useful for testing, but possibly also - useful for various experiments with `nix-daemon --stdio` - networking. - )", - }, - { - .tag = Xp::DynamicDerivations, - .name = "dynamic-derivations", - .description = R"( - Allow the use of a few things related to dynamic derivations: - - - "text hashing" derivation outputs, so we can build .drv - files. - - - dependencies in derivations on the outputs of - derivations that are themselves derivations outputs. - )", - }, - { - .tag = Xp::ParseTomlTimestamps, - .name = "parse-toml-timestamps", - .description = R"( - Allow parsing of timestamps in builtins.fromTOML. - )", - }, - { - .tag = Xp::ReadOnlyLocalStore, - .name = "read-only-local-store", - .description = R"( - Allow the use of the `read-only` parameter in [local store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) URIs. - )", - }, - { - .tag = Xp::ReplAutomation, - .name = "repl-automation", - .description = R"( - Makes the repl not use editline, print ENQ (U+0005) when ready for a command, and take commands followed by newline. - )", - }, + #include "experimental-features-impl.gen.inc" }}; static_assert( diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index c45f05d78..3241ed955 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -8,29 +8,11 @@ namespace nix { /** * The list of available experimental features. - * - * If you update this, don’t forget to also change the map defining - * their string representation and documentation in the corresponding - * `.cc` file as well. */ enum struct ExperimentalFeature { - CaDerivations, - ImpureDerivations, - Flakes, - NixCommand, - RecursiveNix, - NoUrlLiterals, - PipeOperator, - FetchClosure, - ReplFlake, - AutoAllocateUids, - Cgroups, - DaemonTrustOverride, - DynamicDerivations, - ParseTomlTimestamps, - ReadOnlyLocalStore, - ReplAutomation, + #include "experimental-features.gen.inc" + NumXpFeatures, // number of available experimental features, do not use }; enum struct ExperimentalFeatures {}; diff --git a/src/libutil/experimental-features/auto-allocate-uids.md b/src/libutil/experimental-features/auto-allocate-uids.md new file mode 100644 index 000000000..af7a2b68d --- /dev/null +++ b/src/libutil/experimental-features/auto-allocate-uids.md @@ -0,0 +1,6 @@ +--- +name: auto-allocate-uids +internalName: AutoAllocateUids +--- +Allows Nix to automatically pick UIDs for builds, rather than creating +`nixbld*` user accounts. See the [`auto-allocate-uids`](#conf-auto-allocate-uids) setting for details. diff --git a/src/libutil/experimental-features/ca-derivations.md b/src/libutil/experimental-features/ca-derivations.md new file mode 100644 index 000000000..423666c54 --- /dev/null +++ b/src/libutil/experimental-features/ca-derivations.md @@ -0,0 +1,9 @@ +--- +name: ca-derivations +internalName: CaDerivations +--- +Allow derivations to be content-addressed in order to prevent +rebuilds when changes to the derivation do not result in changes to +the derivation's output. See +[__contentAddressed](@docroot@/language/advanced-attributes.md#adv-attr-__contentAddressed) +for details. diff --git a/src/libutil/experimental-features/cgroups.md b/src/libutil/experimental-features/cgroups.md new file mode 100644 index 000000000..66e64fde9 --- /dev/null +++ b/src/libutil/experimental-features/cgroups.md @@ -0,0 +1,6 @@ +--- +name: cgroups +internalName: Cgroups +--- +Allows Nix to execute builds inside cgroups. See +the [`use-cgroups`](#conf-use-cgroups) setting for details. diff --git a/src/libutil/experimental-features/daemon-trust-override.md b/src/libutil/experimental-features/daemon-trust-override.md new file mode 100644 index 000000000..e87f31dd3 --- /dev/null +++ b/src/libutil/experimental-features/daemon-trust-override.md @@ -0,0 +1,8 @@ +--- +name: daemon-trust-override +internalName: DaemonTrustOverride +--- +Allow forcing trusting or not trusting clients with +`nix-daemon`. This is useful for testing, but possibly also +useful for various experiments with `nix-daemon --stdio` +networking. diff --git a/src/libutil/experimental-features/dynamic-derivations.md b/src/libutil/experimental-features/dynamic-derivations.md new file mode 100644 index 000000000..79201bcc3 --- /dev/null +++ b/src/libutil/experimental-features/dynamic-derivations.md @@ -0,0 +1,11 @@ +--- +name: dynamic-derivations +internalName: DynamicDerivations +--- +Allow the use of a few things related to dynamic derivations: + + - "text hashing" derivation outputs, so we can build .drv + files. + + - dependencies in derivations on the outputs of + derivations that are themselves derivations outputs. diff --git a/src/libutil/experimental-features/fetch-closure.md b/src/libutil/experimental-features/fetch-closure.md new file mode 100644 index 000000000..45692f030 --- /dev/null +++ b/src/libutil/experimental-features/fetch-closure.md @@ -0,0 +1,5 @@ +--- +name: fetch-closure +internalName: FetchClosure +--- +Enable the use of the [`fetchClosure`](@docroot@/language/builtins.md#builtins-fetchClosure) built-in function in the Nix language. diff --git a/src/libutil/experimental-features/flakes.md b/src/libutil/experimental-features/flakes.md new file mode 100644 index 000000000..3cb1c74a0 --- /dev/null +++ b/src/libutil/experimental-features/flakes.md @@ -0,0 +1,6 @@ +--- +name: flakes +internalName: Flakes +--- +Enable flakes. See the manual entry for [`nix +flake`](@docroot@/command-ref/new-cli/nix3-flake.md) for details. diff --git a/src/libutil/experimental-features/impure-derivations.md b/src/libutil/experimental-features/impure-derivations.md new file mode 100644 index 000000000..f2ab9f5f2 --- /dev/null +++ b/src/libutil/experimental-features/impure-derivations.md @@ -0,0 +1,28 @@ +--- +name: impure-derivations +internalName: ImpureDerivations +--- +Allow derivations to produce non-fixed outputs by setting the +`__impure` derivation attribute to `true`. An impure derivation can +have differing outputs each time it is built. + +Example: + +``` +derivation { + name = "impure"; + builder = /bin/sh; + __impure = true; # mark this derivation as impure + args = [ "-c" "read -n 10 random < /dev/random; echo $random > $out" ]; + system = builtins.currentSystem; +} +``` + +Each time this derivation is built, it can produce a different +output (as the builder outputs random bytes to `$out`). Impure +derivations also have access to the network, and only fixed-output +or other impure derivations can rely on impure derivations. Finally, +an impure derivation cannot also be +[content-addressed](#xp-feature-ca-derivations). + +This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime). diff --git a/src/libutil/experimental-features/nix-command.md b/src/libutil/experimental-features/nix-command.md new file mode 100644 index 000000000..7cfe6a97f --- /dev/null +++ b/src/libutil/experimental-features/nix-command.md @@ -0,0 +1,6 @@ +--- +name: nix-command +internalName: NixCommand +--- +Enable the new `nix` subcommands. See the manual on +[`nix`](@docroot@/command-ref/new-cli/nix.md) for details. diff --git a/src/libutil/experimental-features/no-url-literals.md b/src/libutil/experimental-features/no-url-literals.md new file mode 100644 index 000000000..2c183900e --- /dev/null +++ b/src/libutil/experimental-features/no-url-literals.md @@ -0,0 +1,39 @@ +--- +name: no-url-literals +internalName: NoUrlLiterals +--- +Disallow unquoted URLs as part of the Nix language syntax. The Nix +language allows for URL literals, like so: + +``` +$ nix repl +Welcome to Nix 2.15.0. Type :? for help. + +nix-repl> http://foo +"http://foo" +``` + +But enabling this experimental feature will cause the Nix parser to +throw an error when encountering a URL literal: + +``` +$ nix repl --extra-experimental-features 'no-url-literals' +Welcome to Nix 2.15.0. Type :? for help. + +nix-repl> http://foo +error: URL literals are disabled + +at «string»:1:1: + +1| http://foo + | ^ + +``` + +While this is currently an experimental feature, unquoted URLs are +being deprecated and their usage is discouraged. + +The reason is that, as opposed to path literals, URLs have no +special properties that distinguish them from regular strings, URLs +containing parameters have to be quoted anyway, and unquoted URLs +may confuse external tooling. diff --git a/src/libutil/experimental-features/parse-toml-timestamps.md b/src/libutil/experimental-features/parse-toml-timestamps.md new file mode 100644 index 000000000..9dbfb70d0 --- /dev/null +++ b/src/libutil/experimental-features/parse-toml-timestamps.md @@ -0,0 +1,5 @@ +--- +name: parse-toml-timestamps +internalName: ParseTomlTimestamps +--- +Allow parsing of timestamps in builtins.fromTOML. diff --git a/src/libutil/experimental-features/pipe-operator.md b/src/libutil/experimental-features/pipe-operator.md new file mode 100644 index 000000000..c9f26ffa3 --- /dev/null +++ b/src/libutil/experimental-features/pipe-operator.md @@ -0,0 +1,8 @@ +--- +name: pipe-operator +internalName: PipeOperator +--- +Enable new operators for function application to "pipe" arguments through a chain of functions similar to `lib.pipe`. +This implementation is based on Nix [RFC 148](https://github.com/NixOS/rfcs/pull/148). + +Tracking issue: https://git.lix.systems/lix-project/lix/issues/438 diff --git a/src/libutil/experimental-features/read-only-local-store.md b/src/libutil/experimental-features/read-only-local-store.md new file mode 100644 index 000000000..700618765 --- /dev/null +++ b/src/libutil/experimental-features/read-only-local-store.md @@ -0,0 +1,5 @@ +--- +name: read-only-local-store +internalName: ReadOnlyLocalStore +--- +Allow the use of the `read-only` parameter in [local store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) URIs. diff --git a/src/libutil/experimental-features/recursive-nix.md b/src/libutil/experimental-features/recursive-nix.md new file mode 100644 index 000000000..fd67ed5aa --- /dev/null +++ b/src/libutil/experimental-features/recursive-nix.md @@ -0,0 +1,39 @@ +--- +name: recursive-nix +internalName: RecursiveNix +--- +Allow derivation builders to call Nix, and thus build derivations +recursively. + +Example: + +``` +with import {}; + +runCommand "foo" + { + buildInputs = [ nix jq ]; + NIX_PATH = "nixpkgs=${}"; + } + '' + hello=$(nix-build -E '(import {}).hello.overrideDerivation (args: { name = "recursive-hello"; })') + + mkdir -p $out/bin + ln -s $hello/bin/hello $out/bin/hello + '' +``` + +An important restriction on recursive builders is disallowing +arbitrary substitutions. For example, running + +``` +nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10 +``` + +in the above `runCommand` script would be disallowed, as this could +lead to derivations with hidden dependencies or breaking +reproducibility by relying on the current state of the Nix store. An +exception would be if +`/nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10` were +already in the build inputs or built by a previous recursive Nix +call. diff --git a/src/libutil/experimental-features/repl-automation.md b/src/libutil/experimental-features/repl-automation.md new file mode 100644 index 000000000..c21cd4478 --- /dev/null +++ b/src/libutil/experimental-features/repl-automation.md @@ -0,0 +1,5 @@ +--- +name: repl-automation +internalName: ReplAutomation +--- +Makes the repl not use editline, print ENQ (U+0005) when ready for a command, and take commands followed by newline. diff --git a/src/libutil/experimental-features/repl-flake.md b/src/libutil/experimental-features/repl-flake.md new file mode 100644 index 000000000..76d910bbf --- /dev/null +++ b/src/libutil/experimental-features/repl-flake.md @@ -0,0 +1,5 @@ +--- +name: repl-flake +internalName: ReplFlake +--- +Allow passing [installables](@docroot@/command-ref/new-cli/nix.md#installables) to `nix repl`, making its interface consistent with the other experimental commands. diff --git a/src/libutil/meson.build b/src/libutil/meson.build index afca4e021..ca75df2d5 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -130,9 +130,99 @@ libutil_headers = files( 'xml-writer.hh', ) +experimental_feature_definitions = files( + 'experimental-features/auto-allocate-uids.md', + 'experimental-features/ca-derivations.md', + 'experimental-features/cgroups.md', + 'experimental-features/daemon-trust-override.md', + 'experimental-features/dynamic-derivations.md', + 'experimental-features/fetch-closure.md', + 'experimental-features/flakes.md', + 'experimental-features/impure-derivations.md', + 'experimental-features/nix-command.md', + 'experimental-features/no-url-literals.md', + 'experimental-features/parse-toml-timestamps.md', + 'experimental-features/pipe-operator.md', + 'experimental-features/read-only-local-store.md', + 'experimental-features/recursive-nix.md', + 'experimental-features/repl-automation.md', + 'experimental-features/repl-flake.md', +) + +deprecated_feature_definitions = files( + 'deprecated-features/ancient-let.md', + 'deprecated-features/rec-set-overrides.md', + 'deprecated-features/url-literals.md', +) + +experimental_features_gen = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_experimental_features.py', + '--header', '@OUTPUT0@', + '--impl-header', '@OUTPUT1@', + '--shortlist', '@OUTPUT2@', + '--descriptions', '@OUTPUT3@', + '@INPUT@', + ], + input : experimental_feature_definitions, + output : [ + 'experimental-features.gen.inc', + 'experimental-features-impl.gen.inc', + 'experimental-features-shortlist.md', + 'experimental-feature-descriptions.md', + ], + install : true, + install_dir: [ + includedir / 'lix/libutil', + false, + false, + false, + ], +) +experimental_features_header = experimental_features_gen[0] +experimental_features_impl_header = experimental_features_gen[1] +experimental_features_shortlist_md = experimental_features_gen[2] +experimental_feature_descriptions_md = experimental_features_gen[3] + +deprecated_features_gen = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_experimental_features.py', + '--deprecated', + '--header', '@OUTPUT0@', + '--impl-header', '@OUTPUT1@', + '--shortlist', '@OUTPUT2@', + '--descriptions', '@OUTPUT3@', + '@INPUT@', + ], + input : deprecated_feature_definitions, + output : [ + 'deprecated-features.gen.inc', + 'deprecated-features-impl.gen.inc', + 'deprecated-features-shortlist.md', + 'deprecated-feature-descriptions.md', + ], + install : true, + install_dir: [ + includedir / 'lix/libutil', + false, + false, + false + ], +) +deprecated_features_header = deprecated_features_gen[0] +deprecated_features_impl_header = deprecated_features_gen[1] +deprecated_features_shortlist_md = deprecated_features_gen[2] +deprecated_feature_descriptions_md = deprecated_features_gen[3] + libutil = library( 'lixutil', libutil_sources, + experimental_features_header, + experimental_features_impl_header, + deprecated_features_header, + deprecated_features_impl_header, dependencies : [ aws_sdk, aws_s3, @@ -169,6 +259,10 @@ configure_file( # Used by libstore and libfetchers. liblixutil = declare_dependency( include_directories : include_directories('.'), + sources : [ + experimental_features_header, + deprecated_features_header, + ], link_with : libutil ) @@ -176,6 +270,10 @@ liblixutil = declare_dependency( if is_static liblixutil_mstatic = declare_dependency( include_directories : include_directories('.'), + sources : [ + experimental_features_header, + deprecated_features_header, + ], link_whole : libutil, ) else From 2e0c1a5ea993f17d0907831d1b63845d41e065ee Mon Sep 17 00:00:00 2001 From: Alois Wohlschlager Date: Mon, 5 Aug 2024 19:18:43 +0200 Subject: [PATCH 2/3] treewide: generate global settings from data Change-Id: If048b73a55d42522827eb9c140a066ba061e957c --- doc/manual/meson.build | 33 - doc/manual/src/command-ref/meson.build | 24 +- doc/manual/src/contributing/hacking.md | 38 +- src/code-generation/build_settings.py | 141 +++ src/code-generation/common.py | 12 +- src/libexpr/eval-settings.hh | 215 +--- src/libexpr/meson.build | 34 + .../settings/allow-import-from-derivation.md | 11 + ...ow-unsafe-native-code-during-evaluation.md | 21 + src/libexpr/settings/allowed-uris.md | 10 + src/libexpr/settings/debugger-on-trace.md | 12 + src/libexpr/settings/eval-cache.md | 7 + src/libexpr/settings/eval-system.md | 15 + src/libexpr/settings/ignore-try.md | 8 + src/libexpr/settings/max-call-depth.md | 7 + src/libexpr/settings/nix-path.md | 11 + src/libexpr/settings/pure-eval.md | 19 + src/libexpr/settings/repl-overlays.md | 86 ++ src/libexpr/settings/restrict-eval.md | 11 + src/libexpr/settings/trace-function-calls.md | 20 + src/libexpr/settings/trace-verbose.md | 7 + src/libfetchers/fetch-settings.hh | 99 +- src/libfetchers/meson.build | 27 + .../settings/accept-flake-config.md | 22 + src/libfetchers/settings/access-tokens.md | 51 + src/libfetchers/settings/allow-dirty.md | 7 + .../settings/commit-lockfile-summary.md | 9 + src/libfetchers/settings/flake-registry.md | 13 + src/libfetchers/settings/use-registries.md | 8 + src/libfetchers/settings/warn-dirty.md | 7 + .../file-transfer-settings/connect-timeout.md | 9 + .../download-attempts.md | 7 + .../http-connections.md | 10 + src/libstore/file-transfer-settings/http2.md | 7 + .../stalled-download-timeout.md | 9 + .../user-agent-suffix.md | 7 + src/libstore/filetransfer.hh | 34 +- src/libstore/globals.hh | 982 +----------------- src/libstore/meson.build | 119 ++- .../settings/allow-symlinked-store.md | 14 + .../settings/allowed-impure-host-deps.md | 7 + .../settings/always-allow-substitutes.md | 9 + src/libstore/settings/auto-allocate-uids.md | 11 + src/libstore/settings/auto-optimise-store.md | 11 + src/libstore/settings/build-dir.md | 14 + src/libstore/settings/build-hook.md | 14 + src/libstore/settings/build-poll-interval.md | 7 + src/libstore/settings/build-users-group.md | 43 + .../settings/builders-use-substitutes.md | 13 + src/libstore/settings/builders.md | 12 + src/libstore/settings/compress-build-log.md | 10 + src/libstore/settings/cores.md | 16 + .../settings/darwin-log-sandbox-violations.md | 8 + src/libstore/settings/diff-hook.md | 32 + src/libstore/settings/download-speed.md | 8 + src/libstore/settings/enable-core-dumps.md | 10 + src/libstore/settings/extra-platforms.md | 21 + src/libstore/settings/fallback.md | 10 + src/libstore/settings/fsync-metadata.md | 10 + src/libstore/settings/gc-reserved-space.md | 7 + src/libstore/settings/hashed-mirrors.md | 23 + src/libstore/settings/id-count.md | 14 + src/libstore/settings/ignored-acls.md | 11 + src/libstore/settings/impersonate-linux-26.md | 9 + src/libstore/settings/keep-build-log.md | 11 + src/libstore/settings/keep-derivations.md | 17 + src/libstore/settings/keep-env-derivations.md | 22 + src/libstore/settings/keep-failed.md | 7 + src/libstore/settings/keep-going.md | 7 + src/libstore/settings/keep-outputs.md | 17 + src/libstore/settings/log-lines.md | 7 + src/libstore/settings/max-build-log-size.md | 10 + src/libstore/settings/max-free.md | 13 + src/libstore/settings/max-jobs.md | 14 + src/libstore/settings/max-silent-time.md | 16 + .../settings/max-substitution-jobs.md | 10 + .../settings/min-free-check-interval.md | 7 + src/libstore/settings/min-free.md | 10 + src/libstore/settings/nar-buffer-size.md | 7 + .../settings/narinfo-cache-negative-ttl.md | 10 + .../settings/narinfo-cache-positive-ttl.md | 14 + src/libstore/settings/netrc-file.md | 29 + src/libstore/settings/plugin-files.md | 27 + src/libstore/settings/post-build-hook.md | 46 + src/libstore/settings/pre-build-hook.md | 20 + src/libstore/settings/print-missing.md | 7 + .../require-drop-supplementary-groups.md | 18 + src/libstore/settings/require-sigs.md | 17 + src/libstore/settings/run-diff-hook.md | 11 + src/libstore/settings/sandbox-build-dir.md | 10 + src/libstore/settings/sandbox-dev-shm-size.md | 11 + src/libstore/settings/sandbox-fallback.md | 7 + src/libstore/settings/sandbox-paths.md | 20 + src/libstore/settings/sandbox.md | 36 + src/libstore/settings/secret-key-files.md | 11 + src/libstore/settings/ssl-cert-file.md | 19 + src/libstore/settings/start-id.md | 14 + src/libstore/settings/store.md | 11 + src/libstore/settings/substitute.md | 10 + src/libstore/settings/substituters.md | 20 + .../settings/sync-before-registering.md | 7 + src/libstore/settings/system-features.md | 21 + src/libstore/settings/system.md | 29 + src/libstore/settings/tarball-ttl.md | 18 + src/libstore/settings/temp-dir.md | 14 + src/libstore/settings/timeout.md | 15 + src/libstore/settings/trusted-public-keys.md | 16 + src/libstore/settings/trusted-substituters.md | 11 + src/libstore/settings/use-cgroups.md | 12 + src/libstore/settings/use-sqlite-wal.md | 8 + .../settings/use-xdg-base-directories.md | 33 + .../archive-settings/preallocate-contents.md | 7 + src/libutil/archive-settings/use-case-hack.md | 13 + src/libutil/archive.cc | 11 +- src/libutil/config.hh | 53 +- .../feature-settings/deprecated-features.md | 19 + .../feature-settings/experimental-features.md | 19 + src/libutil/logging-settings/show-trace.md | 8 + src/libutil/logging.hh | 7 +- src/libutil/meson.build | 59 ++ src/nix/daemon-settings/allowed-users.md | 16 + src/nix/daemon-settings/trusted-users.md | 16 + src/nix/daemon.cc | 34 +- .../develop-settings/bash-prompt-prefix.md | 8 + .../develop-settings/bash-prompt-suffix.md | 8 + src/nix/develop-settings/bash-prompt.md | 8 + src/nix/develop.cc | 12 +- src/nix/meson.build | 38 + 128 files changed, 2103 insertions(+), 1473 deletions(-) create mode 100644 src/code-generation/build_settings.py create mode 100644 src/libexpr/settings/allow-import-from-derivation.md create mode 100644 src/libexpr/settings/allow-unsafe-native-code-during-evaluation.md create mode 100644 src/libexpr/settings/allowed-uris.md create mode 100644 src/libexpr/settings/debugger-on-trace.md create mode 100644 src/libexpr/settings/eval-cache.md create mode 100644 src/libexpr/settings/eval-system.md create mode 100644 src/libexpr/settings/ignore-try.md create mode 100644 src/libexpr/settings/max-call-depth.md create mode 100644 src/libexpr/settings/nix-path.md create mode 100644 src/libexpr/settings/pure-eval.md create mode 100644 src/libexpr/settings/repl-overlays.md create mode 100644 src/libexpr/settings/restrict-eval.md create mode 100644 src/libexpr/settings/trace-function-calls.md create mode 100644 src/libexpr/settings/trace-verbose.md create mode 100644 src/libfetchers/settings/accept-flake-config.md create mode 100644 src/libfetchers/settings/access-tokens.md create mode 100644 src/libfetchers/settings/allow-dirty.md create mode 100644 src/libfetchers/settings/commit-lockfile-summary.md create mode 100644 src/libfetchers/settings/flake-registry.md create mode 100644 src/libfetchers/settings/use-registries.md create mode 100644 src/libfetchers/settings/warn-dirty.md create mode 100644 src/libstore/file-transfer-settings/connect-timeout.md create mode 100644 src/libstore/file-transfer-settings/download-attempts.md create mode 100644 src/libstore/file-transfer-settings/http-connections.md create mode 100644 src/libstore/file-transfer-settings/http2.md create mode 100644 src/libstore/file-transfer-settings/stalled-download-timeout.md create mode 100644 src/libstore/file-transfer-settings/user-agent-suffix.md create mode 100644 src/libstore/settings/allow-symlinked-store.md create mode 100644 src/libstore/settings/allowed-impure-host-deps.md create mode 100644 src/libstore/settings/always-allow-substitutes.md create mode 100644 src/libstore/settings/auto-allocate-uids.md create mode 100644 src/libstore/settings/auto-optimise-store.md create mode 100644 src/libstore/settings/build-dir.md create mode 100644 src/libstore/settings/build-hook.md create mode 100644 src/libstore/settings/build-poll-interval.md create mode 100644 src/libstore/settings/build-users-group.md create mode 100644 src/libstore/settings/builders-use-substitutes.md create mode 100644 src/libstore/settings/builders.md create mode 100644 src/libstore/settings/compress-build-log.md create mode 100644 src/libstore/settings/cores.md create mode 100644 src/libstore/settings/darwin-log-sandbox-violations.md create mode 100644 src/libstore/settings/diff-hook.md create mode 100644 src/libstore/settings/download-speed.md create mode 100644 src/libstore/settings/enable-core-dumps.md create mode 100644 src/libstore/settings/extra-platforms.md create mode 100644 src/libstore/settings/fallback.md create mode 100644 src/libstore/settings/fsync-metadata.md create mode 100644 src/libstore/settings/gc-reserved-space.md create mode 100644 src/libstore/settings/hashed-mirrors.md create mode 100644 src/libstore/settings/id-count.md create mode 100644 src/libstore/settings/ignored-acls.md create mode 100644 src/libstore/settings/impersonate-linux-26.md create mode 100644 src/libstore/settings/keep-build-log.md create mode 100644 src/libstore/settings/keep-derivations.md create mode 100644 src/libstore/settings/keep-env-derivations.md create mode 100644 src/libstore/settings/keep-failed.md create mode 100644 src/libstore/settings/keep-going.md create mode 100644 src/libstore/settings/keep-outputs.md create mode 100644 src/libstore/settings/log-lines.md create mode 100644 src/libstore/settings/max-build-log-size.md create mode 100644 src/libstore/settings/max-free.md create mode 100644 src/libstore/settings/max-jobs.md create mode 100644 src/libstore/settings/max-silent-time.md create mode 100644 src/libstore/settings/max-substitution-jobs.md create mode 100644 src/libstore/settings/min-free-check-interval.md create mode 100644 src/libstore/settings/min-free.md create mode 100644 src/libstore/settings/nar-buffer-size.md create mode 100644 src/libstore/settings/narinfo-cache-negative-ttl.md create mode 100644 src/libstore/settings/narinfo-cache-positive-ttl.md create mode 100644 src/libstore/settings/netrc-file.md create mode 100644 src/libstore/settings/plugin-files.md create mode 100644 src/libstore/settings/post-build-hook.md create mode 100644 src/libstore/settings/pre-build-hook.md create mode 100644 src/libstore/settings/print-missing.md create mode 100644 src/libstore/settings/require-drop-supplementary-groups.md create mode 100644 src/libstore/settings/require-sigs.md create mode 100644 src/libstore/settings/run-diff-hook.md create mode 100644 src/libstore/settings/sandbox-build-dir.md create mode 100644 src/libstore/settings/sandbox-dev-shm-size.md create mode 100644 src/libstore/settings/sandbox-fallback.md create mode 100644 src/libstore/settings/sandbox-paths.md create mode 100644 src/libstore/settings/sandbox.md create mode 100644 src/libstore/settings/secret-key-files.md create mode 100644 src/libstore/settings/ssl-cert-file.md create mode 100644 src/libstore/settings/start-id.md create mode 100644 src/libstore/settings/store.md create mode 100644 src/libstore/settings/substitute.md create mode 100644 src/libstore/settings/substituters.md create mode 100644 src/libstore/settings/sync-before-registering.md create mode 100644 src/libstore/settings/system-features.md create mode 100644 src/libstore/settings/system.md create mode 100644 src/libstore/settings/tarball-ttl.md create mode 100644 src/libstore/settings/temp-dir.md create mode 100644 src/libstore/settings/timeout.md create mode 100644 src/libstore/settings/trusted-public-keys.md create mode 100644 src/libstore/settings/trusted-substituters.md create mode 100644 src/libstore/settings/use-cgroups.md create mode 100644 src/libstore/settings/use-sqlite-wal.md create mode 100644 src/libstore/settings/use-xdg-base-directories.md create mode 100644 src/libutil/archive-settings/preallocate-contents.md create mode 100644 src/libutil/archive-settings/use-case-hack.md create mode 100644 src/libutil/feature-settings/deprecated-features.md create mode 100644 src/libutil/feature-settings/experimental-features.md create mode 100644 src/libutil/logging-settings/show-trace.md create mode 100644 src/nix/daemon-settings/allowed-users.md create mode 100644 src/nix/daemon-settings/trusted-users.md create mode 100644 src/nix/develop-settings/bash-prompt-prefix.md create mode 100644 src/nix/develop-settings/bash-prompt-suffix.md create mode 100644 src/nix/develop-settings/bash-prompt.md diff --git a/doc/manual/meson.build b/doc/manual/meson.build index 02383f6bb..c34a0e3ca 100644 --- a/doc/manual/meson.build +++ b/doc/manual/meson.build @@ -15,39 +15,6 @@ nix_eval_for_docs_common = nix_for_docs + [ ] nix_eval_for_docs = nix_eval_for_docs_common + '--raw' -conf_file_json = custom_target( - command : nix_for_docs + [ 'show-config', '--json' ], - capture : true, - output : 'conf-file.json', - env : nix_env_for_docs, - # FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though. - build_always_stale : true, -) - -nix_conf_file_md_body = custom_target( - command : nix_eval_for_docs + [ - '--expr', - '(import @INPUT0@).showSettings { inlineHTML = true; } (builtins.fromJSON (builtins.readFile @INPUT1@))', - ], - capture : true, - input : [ - 'utils.nix', - conf_file_json, - ], - output : 'conf-file.md.body', - env : nix_env_for_docs, -) - -nix_conf_file_md = custom_target( - command : [ 'cat', '@INPUT@' ], - capture : true, - input : [ - 'src/command-ref/conf-file.md', - nix_conf_file_md_body, - ], - output : 'conf-file.md', -) - language_json = custom_target( command: [nix, '__dump-language'], output : 'language.json', diff --git a/doc/manual/src/command-ref/meson.build b/doc/manual/src/command-ref/meson.build index 6acc79f4e..67f7261ae 100644 --- a/doc/manual/src/command-ref/meson.build +++ b/doc/manual/src/command-ref/meson.build @@ -31,17 +31,23 @@ nix3_cli_files = custom_target( conf_file_md = custom_target( command : [ - nix_eval_for_docs, - '--expr', - '(import @INPUT0@).showSettings { inlineHTML = true; } (builtins.fromJSON (builtins.readFile @INPUT1@))', + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--docs', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', ], - capture : true, input : [ - '../../utils.nix', - conf_file_json, - experimental_features_shortlist_md, - deprecated_features_shortlist_md, + libexpr_setting_definitions, + libfetchers_setting_definitions, + file_transfer_setting_definitions, + libstore_setting_definitions, + archive_setting_definitions, + feature_setting_definitions, + logging_setting_definitions, + daemon_setting_definitions, + develop_settings_definitions, ], output : 'conf-file.md', - env : nix_env_for_docs, ) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index d245e20d1..4798e4b7f 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -400,16 +400,46 @@ The following properties are supported: Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. Set `buildUnreleasedNotes = true;` in `flake.nix` to build the release notes on the fly. -## Adding experimental or deprecated features +## Adding experimental or deprecated features, or global settings -Experimental and deprecated features are generally referenced both in the code and in the documentation. +Experimental and deprecated features, and global settings are generally referenced both in the code and in the documentation. To prevent duplication or divergence, they are defined in data files, and a script generates the necessary glue. - The data file format is similar to the release notes: it consists of a YAML metadata header, followed by the documentation in Markdown format. -The following metadata properties are supported: + +### Experimental or deprecated features + +Experimental and deprecated features support the following metadata properties: * `name` (required): user-facing name of the feature, to be used in `nix.conf` options and on the command line. This should also be the stem of the file name (with extension `md`). * `internalName` (required): identifier used to refer to the feature inside the C++ code. Experimental feature data files should live in `src/libutil/experimental-features`, and deprecated features in `src/libutil/deprecated-features`. They must be listed in the `experimental_feature_definitions` or `deprecated_feature_definitions` lists in `src/libutil/meson.build` respectively to be considered by the build system. + +### Global settings + +Global settings support the following metadata properties: +* `name` (required): user-facing name of the setting, to be used as key in `nix.conf` and in the `--option` command line argument. +* `internalName` (required): identifier used to refer to the setting inside the C++ code. +* `platforms` (optional): a list specifying the platforms on which this setting is available. + If not specified, it is available on all platforms. + Valid platform names are `darwin`, `linux`. +* `type` (optional): C++ type of the setting value. + This specifies the setting object type as `Setting`; if more control is required, use `settingType` instead. +* `settingType` (required if `type` is not specified): C++ type of the setting object. +* `default` (optional): default value of the setting. + `null`, truth values, integers, strings and lists are supported as long as the correct YAML type is used, `type` is not taken into account). + Other types, machine-dependent values or non-standard representations must be handled using `defaultExpr` and `defaultText` instead. +* `defaultExpr` (required if `default` is not specified): a string containing the C++ expression representing the default value. +* `defaultText` (required if `default` is not specified): a string containing the Markdown expression representing the default value in the documentation. + Literal values are conventionally surrounded by backticks, and a system-dependent value is signaled by `*machine-specific*`. +* `aliases` (optional): a list of secondary user-facing names under which the setting is available. + Defaults to empty if not specified. +* `experimentalFeature` (optional): the user-facing name of the experimental feature which needs to be enabled to change the setting. + If not specified, no experimental feature is required. +* `deprecated` (optional): whether the setting is deprecated and shown as such in the documentation for `nix.conf`. + Defaults to false if not specified. + +Settings are not collected in a single place in the source tree, so an appropriate place needs to be found for the setting to live. +Look for related setting definition files under second-level subdirectories of `src` whose name includes `settings`. +Then add the new file there, and don't forget to register it in the appropriate `meson.build` file. diff --git a/src/code-generation/build_settings.py b/src/code-generation/build_settings.py new file mode 100644 index 000000000..f9e993795 --- /dev/null +++ b/src/code-generation/build_settings.py @@ -0,0 +1,141 @@ +from typing import List, NamedTuple, Optional + +from build_experimental_features import ExperimentalFeature +from common import cxx_literal, generate_file, load_data + +KNOWN_KEYS = set([ + 'name', + 'internalName', + 'platforms', + 'type', + 'settingType', + 'default', + 'defaultExpr', + 'defaultText', + 'aliases', + 'experimentalFeature', + 'deprecated', +]) + +class Setting(NamedTuple): + name: str + internal_name: str + description: str + platforms: Optional[List[str]] + setting_type: str + default_expr: str + default_text: str + aliases: List[str] + experimental_feature: Optional[str] + deprecated: bool + + def parse(datum): + unknown_keys = set(datum.keys()) - KNOWN_KEYS + if unknown_keys: + raise ValueError('unknown keys', unknown_keys) + default_text = f'`{nix_conf_literal(datum["default"])}`' if 'default' in datum else datum['defaultText'] + if default_text == '``': + default_text = '*empty*' + return Setting( + name = datum['name'], + internal_name = datum['internalName'], + description = datum.content, + platforms = datum.get('platforms', None), + setting_type = f'Setting<{datum["type"]}>' if 'type' in datum else datum['settingType'], + default_expr = cxx_literal(datum['default']) if 'default' in datum else datum['defaultExpr'], + default_text = default_text, + aliases = datum.get('aliases', []), + experimental_feature = datum.get('experimentalFeature', None), + deprecated = datum.get('deprecated', False), + ) + +platform_names = { + 'darwin': 'Darwin', + 'linux': 'Linux', +} + +def nix_conf_literal(v): + if v is None: + return '' + elif isinstance(v, bool) and v == False: # 0 == False + return 'false' + elif isinstance(v, bool) and v == True: # 1 == True + return 'true' + elif isinstance(v, int): + return str(v) + elif isinstance(v, str): + return v + elif isinstance(v, list): + return ' '.join([nix_conf_literal(item) for item in v]) + else: + raise NotImplementedError(f'Cannot represent {repr(v)} in nix.conf') + +def indent(prefix, body): + return ''.join(['\n' if line == '' else f'{prefix}{line}\n' for line in body.split('\n')]) + +def main(): + import argparse + + ap = argparse.ArgumentParser() + ap.add_argument('--kernel', help='Name of the kernel Lix will run on') + ap.add_argument('--header', help='Path of the header to generate') + ap.add_argument('--docs', help='Path of the documentation file to generate') + ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions') + ap.add_argument('defs', help='Setting definition files', nargs='+') + args = ap.parse_args() + + settings = load_data(args.defs, Setting.parse) + + experimental_feature_names = set([setting.experimental_feature for (_, setting) in settings]) + experimental_feature_names.discard(None) + experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names] + experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse) + experimental_features = dict(map(lambda path_and_feature: + (path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features)) + experimental_features[None] = 'std::nullopt' + + generate_file(args.header, settings, lambda setting: setting.name, lambda setting: + f'''{setting.setting_type} {setting.internal_name} {{ + this, + {setting.default_expr}, + {cxx_literal(setting.name)}, + {cxx_literal(setting.description)}, + {cxx_literal(setting.aliases)}, + true, + {experimental_features[setting.experimental_feature]}, + {cxx_literal(setting.deprecated)} +}}; + +''' if setting.platforms is None or args.kernel in setting.platforms else '') + generate_file(args.docs, settings, lambda setting: setting.name, lambda setting: + f'''- [`{setting.name}`](#conf-{setting.name}) + +{indent(" ", setting.description)} +''' + (f''' > **Note** + > This setting is only available on {', '.join([platform_names[platform] for platform in setting.platforms])} systems. + +''' if setting.platforms is not None else '') + (f''' > **Warning** + > This setting is part of an + > [experimental feature](@docroot@/contributing/experimental-features.md). + + To change this setting, you need to make sure the corresponding experimental feature, + [`{setting.experimental_feature}`](@docroot@/contributing/experimental-features.md#xp-feature-{setting.experimental_feature}), + is enabled. + For example, include the following in [`nix.conf`](#): + + ``` + extra-experimental-features = {setting.experimental_feature} + {setting.name} = ... + ``` + +''' if setting.experimental_feature is not None else '') + (''' > **Warning** + > This setting is deprecated and will be removed in a future version of Lix. + +''' if setting.deprecated else '') + f''' **Default:** {setting.default_text} + +''' + (f''' **Deprecated alias:** {', '.join([f'`{item}`' for item in setting.aliases])} + +''' if setting.aliases != [] else '')) + +if __name__ == '__main__': + main() diff --git a/src/code-generation/common.py b/src/code-generation/common.py index 228359c78..38e8122a0 100644 --- a/src/code-generation/common.py +++ b/src/code-generation/common.py @@ -23,8 +23,18 @@ def cxx_escape_character(c): return str.format(r'\U{:08x}', ord(c)) def cxx_literal(v): - if isinstance(v, str): + if v is None: + return 'std::nullopt' + elif isinstance(v, bool) and v == False: # 0 == False + return 'false' + elif isinstance(v, bool) and v == True: # 1 == True + return 'true' + elif isinstance(v, int): + return str(v) + elif isinstance(v, str): return ''.join(['"', *(cxx_escape_character(c) for c in v), '"']) + elif isinstance(v, list): + return f'{{{", ".join([cxx_literal(item) for item in v])}}}' else: raise NotImplementedError(f'cannot represent {repr(v)} in C++') diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index f7ad2d786..a53eec38b 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -14,226 +14,13 @@ struct EvalSettings : Config static std::string resolvePseudoUrl(std::string_view url); - Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"( - Enable built-in functions that allow executing native code. - - In particular, this adds: - - `builtins.importNative` *path* *symbol* - - Runs function with *symbol* from a dynamic shared object (DSO) at *path*. - This may be used to add new builtins to the Nix language. - The procedure must have the following signature: - ```cpp - extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); - ``` - - - `builtins.exec` *arguments* - - Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression. - )"}; - - Setting nixPath{ - this, getDefaultNixPath(), "nix-path", - R"( - List of directories to be searched for `<...>` file references - - In particular, outside of [pure evaluation mode](#conf-pure-eval), this determines the value of - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). - )"}; - - Setting currentSystem{ - this, "", "eval-system", - R"( - This option defines - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) - in the Nix language if it is set as a non-empty string. - Otherwise, if it is defined as the empty string (the default), the value of the - [`system` ](#conf-system) - configuration setting is used instead. - - Unlike `system`, this setting does not change what kind of derivations can be built locally. - This is useful for evaluating Nix code on one system to produce derivations to be built on another type of system. - )"}; + #include "libexpr-settings.gen.inc" /** * Implements the `eval-system` vs `system` defaulting logic * described for `eval-system`. */ const std::string & getCurrentSystem(); - - Setting restrictEval{ - this, false, "restrict-eval", - R"( - If set to `true`, the Nix evaluator will not allow access to any - files outside of the Nix search path (as set via the `NIX_PATH` - environment variable or the `-I` option), or to URIs outside of - [`allowed-uris`](../command-ref/conf-file.md#conf-allowed-uris). - The default is `false`. - )"}; - - Setting pureEval{this, false, "pure-eval", - R"( - Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state: - - - File system and network access is restricted to accesses to immutable data only: - - Path literals relative to the home directory like `~/lix` are rejected at parse time. - - Access to absolute paths that did not result from Nix language evaluation is rejected when such paths are given as parameters to builtins like, for example, [`builtins.readFile`](@docroot@/language/builtins.md#builtins-readFile). - - 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. - - [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) throws an error (Lix may change this, tracking issue: ) - )" - }; - - Setting enableImportFromDerivation{ - this, true, "allow-import-from-derivation", - R"( - By default, Lix allows you to `import` from a derivation, allowing - building at evaluation time. With this option set to false, Lix will - throw an error when evaluating an expression that uses this feature, - allowing users to ensure their evaluation will not require any - builds to take place. - )"}; - - Setting allowedUris{this, {}, "allowed-uris", - R"( - A list of URI prefixes to which access is allowed in restricted - evaluation mode. For example, when set to - `https://github.com/NixOS`, builtin functions such as `fetchGit` are - allowed to access `https://github.com/NixOS/patchelf.git`. - )"}; - - - Setting traceFunctionCalls{this, false, "trace-function-calls", - R"( - If set to `true`, the Nix evaluator will trace every function call. - Nix will print a log message at the "vomit" level for every function - entrance and function exit. - - function-trace entered undefined position at 1565795816999559622 - function-trace exited undefined position at 1565795816999581277 - function-trace entered /nix/store/.../example.nix:226:41 at 1565795253249935150 - function-trace exited /nix/store/.../example.nix:226:41 at 1565795253249941684 - - The `undefined position` means the function call is a builtin. - - Use the `contrib/stack-collapse.py` script distributed with the Nix - source code to convert the trace logs in to a format suitable for - `flamegraph.pl`. - )"}; - - Setting useEvalCache{this, true, "eval-cache", - "Whether to use the flake evaluation cache."}; - - Setting ignoreExceptionsDuringTry{this, false, "ignore-try", - R"( - If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in - debug mode (using the --debugger flag). By default the debugger will pause on all exceptions. - )"}; - - Setting traceVerbose{this, false, "trace-verbose", - "Whether `builtins.traceVerbose` should trace its first argument when evaluated."}; - - Setting maxCallDepth{this, 10000, "max-call-depth", - "The maximum function call depth to allow before erroring."}; - - Setting builtinsTraceDebugger{this, false, "debugger-on-trace", - R"( - If set to true and the `--debugger` flag is given, - [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will - enter the debugger like - [`builtins.break`](@docroot@/language/builtins.md#builtins-break). - - This is useful for debugging warnings in third-party Nix code. - )"}; - - PathsSetting replOverlays{this, Paths(), "repl-overlays", - R"( - A list of files containing Nix expressions that can be used to add - default bindings to [`nix - repl`](@docroot@/command-ref/new-cli/nix3-repl.md) sessions. - - Each file is called with three arguments: - 1. An [attribute set](@docroot@/language/values.html#attribute-set) - containing at least a - [`currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) - attribute (this is identical to - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem), - except that it's available in - [`pure-eval`](@docroot@/command-ref/conf-file.html#conf-pure-eval) - mode). - 2. The top-level bindings produced by the previous `repl-overlays` - value (or the default top-level bindings). - 3. The final top-level bindings produced by calling all - `repl-overlays`. - - For example, the following file would alias `pkgs` to - `legacyPackages.${info.currentSystem}` (if that attribute is defined): - - ```nix - info: final: prev: - if prev ? legacyPackages - && prev.legacyPackages ? ${info.currentSystem} - then - { - pkgs = prev.legacyPackages.${info.currentSystem}; - } - else - { } - ``` - - Here's a more elaborate `repl-overlay`, which provides the following - variables: - - The original, unmodified variables are aliased to `original`. - - `legacyPackages.${system}` (if it exists) or `packages.${system}` - (otherwise) is aliased to `pkgs`. - - All attribute set variables with a `${system}` attribute are - abbreviated in the same manner; e.g. `devShells.${system}` is - shortened to `devShells`. - - For example, the following attribute set: - - ```nix - info: final: attrs: let - # Equivalent to nixpkgs `lib.optionalAttrs`. - optionalAttrs = predicate: attrs: - if predicate - then attrs - else {}; - - # If `attrs.${oldName}.${info.currentSystem}` exists, alias `${newName}` to - # it. - collapseRenamed = oldName: newName: - optionalAttrs (builtins.hasAttr oldName attrs - && builtins.hasAttr info.currentSystem attrs.${oldName}) - { - ${newName} = attrs.${oldName}.${info.currentSystem}; - }; - - # Alias `attrs.${oldName}.${info.currentSystem} to `${newName}`. - collapse = name: collapseRenamed name name; - - # Alias all `attrs` keys with an `${info.currentSystem}` attribute. - collapseAll = - builtins.foldl' - (prev: name: prev // collapse name) - {} - (builtins.attrNames attrs); - in - # Preserve the original bindings as `original`. - (optionalAttrs (! attrs ? original) - { - original = attrs; - }) - // (collapseRenamed "packages" "pkgs") - // (collapseRenamed "legacyPackages" "pkgs") - // collapseAll - ``` - )"}; }; extern EvalSettings evalSettings; diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index af72bd549..f3ea93c71 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -13,6 +13,37 @@ foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix' ] endforeach subdir('flake') +libexpr_setting_definitions = files( + 'settings/allow-import-from-derivation.md', + 'settings/allow-unsafe-native-code-during-evaluation.md', + 'settings/allowed-uris.md', + 'settings/debugger-on-trace.md', + 'settings/eval-cache.md', + 'settings/eval-system.md', + 'settings/ignore-try.md', + 'settings/max-call-depth.md', + 'settings/nix-path.md', + 'settings/pure-eval.md', + 'settings/repl-overlays.md', + 'settings/restrict-eval.md', + 'settings/trace-function-calls.md', + 'settings/trace-verbose.md', +) +libexpr_settings_header = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : libexpr_setting_definitions, + output : 'libexpr-settings.gen.inc', + install : true, + install_dir : includedir / 'lib/libexpr', +) + libexpr_sources = files( 'attr-path.cc', 'attr-set.cc', @@ -84,6 +115,7 @@ libexpr_headers = files( libexpr = library( 'lixexpr', libexpr_sources, + libexpr_settings_header, libexpr_generated_headers, dependencies : [ liblixutil, @@ -112,6 +144,7 @@ install_headers( liblixexpr = declare_dependency( include_directories : include_directories('.'), + sources : libexpr_settings_header, link_with : libexpr, ) @@ -119,6 +152,7 @@ liblixexpr = declare_dependency( if is_static liblixexpr_mstatic = declare_dependency( include_directories : include_directories('.'), + sources : libexpr_settings_header, link_whole : libexpr, ) else diff --git a/src/libexpr/settings/allow-import-from-derivation.md b/src/libexpr/settings/allow-import-from-derivation.md new file mode 100644 index 000000000..2be92b999 --- /dev/null +++ b/src/libexpr/settings/allow-import-from-derivation.md @@ -0,0 +1,11 @@ +--- +name: allow-import-from-derivation +internalName: enableImportFromDerivation +type: bool +default: true +--- +By default, Lix allows you to `import` from a derivation, allowing +building at evaluation time. With this option set to false, Lix will +throw an error when evaluating an expression that uses this feature, +allowing users to ensure their evaluation will not require any +builds to take place. diff --git a/src/libexpr/settings/allow-unsafe-native-code-during-evaluation.md b/src/libexpr/settings/allow-unsafe-native-code-during-evaluation.md new file mode 100644 index 000000000..c378083e4 --- /dev/null +++ b/src/libexpr/settings/allow-unsafe-native-code-during-evaluation.md @@ -0,0 +1,21 @@ +--- +name: allow-unsafe-native-code-during-evaluation +internalName: enableNativeCode +type: bool +default: false +--- +Enable built-in functions that allow executing native code. + +In particular, this adds: +- `builtins.importNative` *path* *symbol* + + Runs function with *symbol* from a dynamic shared object (DSO) at *path*. + This may be used to add new builtins to the Nix language. + The procedure must have the following signature: + ```cpp + extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); + ``` + +- `builtins.exec` *arguments* + + Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression. diff --git a/src/libexpr/settings/allowed-uris.md b/src/libexpr/settings/allowed-uris.md new file mode 100644 index 000000000..d86afaa51 --- /dev/null +++ b/src/libexpr/settings/allowed-uris.md @@ -0,0 +1,10 @@ +--- +name: allowed-uris +internalName: allowedUris +type: Strings +default: [] +--- +A list of URI prefixes to which access is allowed in restricted +evaluation mode. For example, when set to +`https://github.com/NixOS`, builtin functions such as `fetchGit` are +allowed to access `https://github.com/NixOS/patchelf.git`. diff --git a/src/libexpr/settings/debugger-on-trace.md b/src/libexpr/settings/debugger-on-trace.md new file mode 100644 index 000000000..afecabfec --- /dev/null +++ b/src/libexpr/settings/debugger-on-trace.md @@ -0,0 +1,12 @@ +--- +name: debugger-on-trace +internalName: builtinsTraceDebugger +type: bool +default: false +--- +If set to true and the `--debugger` flag is given, +[`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will +enter the debugger like +[`builtins.break`](@docroot@/language/builtins.md#builtins-break). + +This is useful for debugging warnings in third-party Nix code. diff --git a/src/libexpr/settings/eval-cache.md b/src/libexpr/settings/eval-cache.md new file mode 100644 index 000000000..3602bca8d --- /dev/null +++ b/src/libexpr/settings/eval-cache.md @@ -0,0 +1,7 @@ +--- +name: eval-cache +internalName: useEvalCache +type: bool +default: true +--- +Whether to use the flake evaluation cache. diff --git a/src/libexpr/settings/eval-system.md b/src/libexpr/settings/eval-system.md new file mode 100644 index 000000000..a1cbc7d18 --- /dev/null +++ b/src/libexpr/settings/eval-system.md @@ -0,0 +1,15 @@ +--- +name: eval-system +internalName: currentSystem +type: std::string +default: '' +--- +This option defines +[`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) +in the Nix language if it is set as a non-empty string. +Otherwise, if it is defined as the empty string (the default), the value of the +[`system` ](#conf-system) +configuration setting is used instead. + +Unlike `system`, this setting does not change what kind of derivations can be built locally. +This is useful for evaluating Nix code on one system to produce derivations to be built on another type of system. diff --git a/src/libexpr/settings/ignore-try.md b/src/libexpr/settings/ignore-try.md new file mode 100644 index 000000000..4fd5fac07 --- /dev/null +++ b/src/libexpr/settings/ignore-try.md @@ -0,0 +1,8 @@ +--- +name: ignore-try +internalName: ignoreExceptionsDuringTry +type: bool +default: false +--- +If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in +debug mode (using the --debugger flag). By default the debugger will pause on all exceptions. diff --git a/src/libexpr/settings/max-call-depth.md b/src/libexpr/settings/max-call-depth.md new file mode 100644 index 000000000..454be5f9b --- /dev/null +++ b/src/libexpr/settings/max-call-depth.md @@ -0,0 +1,7 @@ +--- +name: max-call-depth +internalName: maxCallDepth +type: unsigned int +default: 10000 +--- +The maximum function call depth to allow before erroring. diff --git a/src/libexpr/settings/nix-path.md b/src/libexpr/settings/nix-path.md new file mode 100644 index 000000000..8382db260 --- /dev/null +++ b/src/libexpr/settings/nix-path.md @@ -0,0 +1,11 @@ +--- +name: nix-path +internalName: nixPath +type: Strings +defaultExpr: 'getDefaultNixPath()' +defaultText: '*machine-specific*' +--- +List of directories to be searched for `<...>` file references + +In particular, outside of [pure evaluation mode](#conf-pure-eval), this determines the value of +[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). diff --git a/src/libexpr/settings/pure-eval.md b/src/libexpr/settings/pure-eval.md new file mode 100644 index 000000000..a87f7c63c --- /dev/null +++ b/src/libexpr/settings/pure-eval.md @@ -0,0 +1,19 @@ +--- +name: pure-eval +internalName: pureEval +type: bool +default: false +--- +Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state: + +- File system and network access is restricted to accesses to immutable data only: + - Path literals relative to the home directory like `~/lix` are rejected at parse time. + - Access to absolute paths that did not result from Nix language evaluation is rejected when such paths are given as parameters to builtins like, for example, [`builtins.readFile`](@docroot@/language/builtins.md#builtins-readFile). + + 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. +- [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) throws an error (Lix may change this, tracking issue: ) diff --git a/src/libexpr/settings/repl-overlays.md b/src/libexpr/settings/repl-overlays.md new file mode 100644 index 000000000..e18594e3e --- /dev/null +++ b/src/libexpr/settings/repl-overlays.md @@ -0,0 +1,86 @@ +--- +name: repl-overlays +internalName: replOverlays +settingType: PathsSetting +default: [] +--- +A list of files containing Nix expressions that can be used to add +default bindings to [`nix +repl`](@docroot@/command-ref/new-cli/nix3-repl.md) sessions. + +Each file is called with three arguments: +1. An [attribute set](@docroot@/language/values.html#attribute-set) + containing at least a + [`currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + attribute (this is identical to + [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem), + except that it's available in + [`pure-eval`](@docroot@/command-ref/conf-file.html#conf-pure-eval) + mode). +2. The top-level bindings produced by the previous `repl-overlays` + value (or the default top-level bindings). +3. The final top-level bindings produced by calling all + `repl-overlays`. + +For example, the following file would alias `pkgs` to +`legacyPackages.${info.currentSystem}` (if that attribute is defined): + +```nix +info: final: prev: +if prev ? legacyPackages + && prev.legacyPackages ? ${info.currentSystem} +then +{ + pkgs = prev.legacyPackages.${info.currentSystem}; +} +else +{ } +``` + +Here's a more elaborate `repl-overlay`, which provides the following +variables: +- The original, unmodified variables are aliased to `original`. +- `legacyPackages.${system}` (if it exists) or `packages.${system}` + (otherwise) is aliased to `pkgs`. +- All attribute set variables with a `${system}` attribute are + abbreviated in the same manner; e.g. `devShells.${system}` is + shortened to `devShells`. + +For example, the following attribute set: + +```nix +info: final: attrs: let + # Equivalent to nixpkgs `lib.optionalAttrs`. + optionalAttrs = predicate: attrs: + if predicate + then attrs + else {}; + + # If `attrs.${oldName}.${info.currentSystem}` exists, alias `${newName}` to + # it. + collapseRenamed = oldName: newName: + optionalAttrs (builtins.hasAttr oldName attrs + && builtins.hasAttr info.currentSystem attrs.${oldName}) + { + ${newName} = attrs.${oldName}.${info.currentSystem}; + }; + + # Alias `attrs.${oldName}.${info.currentSystem} to `${newName}`. + collapse = name: collapseRenamed name name; + + # Alias all `attrs` keys with an `${info.currentSystem}` attribute. + collapseAll = + builtins.foldl' + (prev: name: prev // collapse name) + {} + (builtins.attrNames attrs); +in + # Preserve the original bindings as `original`. + (optionalAttrs (! attrs ? original) + { + original = attrs; + }) + // (collapseRenamed "packages" "pkgs") + // (collapseRenamed "legacyPackages" "pkgs") + // collapseAll +``` diff --git a/src/libexpr/settings/restrict-eval.md b/src/libexpr/settings/restrict-eval.md new file mode 100644 index 000000000..22c5689da --- /dev/null +++ b/src/libexpr/settings/restrict-eval.md @@ -0,0 +1,11 @@ +--- +name: restrict-eval +internalName: restrictEval +type: bool +default: false +--- +If set to `true`, the Nix evaluator will not allow access to any +files outside of the Nix search path (as set via the `NIX_PATH` +environment variable or the `-I` option), or to URIs outside of +[`allowed-uris`](../command-ref/conf-file.md#conf-allowed-uris). +The default is `false`. diff --git a/src/libexpr/settings/trace-function-calls.md b/src/libexpr/settings/trace-function-calls.md new file mode 100644 index 000000000..eb264d969 --- /dev/null +++ b/src/libexpr/settings/trace-function-calls.md @@ -0,0 +1,20 @@ +--- +name: trace-function-calls +internalName: traceFunctionCalls +type: bool +default: false +--- +If set to `true`, the Nix evaluator will trace every function call. +Nix will print a log message at the "vomit" level for every function +entrance and function exit. + + function-trace entered undefined position at 1565795816999559622 + function-trace exited undefined position at 1565795816999581277 + function-trace entered /nix/store/.../example.nix:226:41 at 1565795253249935150 + function-trace exited /nix/store/.../example.nix:226:41 at 1565795253249941684 + +The `undefined position` means the function call is a builtin. + +Use the `contrib/stack-collapse.py` script distributed with the Nix +source code to convert the trace logs in to a format suitable for +`flamegraph.pl`. diff --git a/src/libexpr/settings/trace-verbose.md b/src/libexpr/settings/trace-verbose.md new file mode 100644 index 000000000..5669cc4c1 --- /dev/null +++ b/src/libexpr/settings/trace-verbose.md @@ -0,0 +1,7 @@ +--- +name: trace-verbose +internalName: traceVerbose +type: bool +default: false +--- +Whether `builtins.traceVerbose` should trace its first argument when evaluated. diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 0bdc707ec..0f24d83dc 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -20,104 +20,7 @@ struct FetchSettings : public Config { FetchSettings(); - Setting accessTokens{this, {}, "access-tokens", - R"( - Access tokens used to access protected GitHub, GitLab, or - other locations requiring token-based authentication. - - Access tokens are specified as a string made up of - space-separated `host=token` values. The specific token - used is selected by matching the `host` portion against the - "host" specification of the input. The actual use of the - `token` value is determined by the type of resource being - accessed: - - * Github: the token value is the OAUTH-TOKEN string obtained - as the Personal Access Token from the Github server (see - https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps). - - * Gitlab: the token value is either the OAuth2 token or the - Personal Access Token (these are different types tokens - for gitlab, see - https://docs.gitlab.com/12.10/ee/api/README.html#authentication). - The `token` value should be `type:tokenstring` where - `type` is either `OAuth2` or `PAT` to indicate which type - of token is being specified. - - Example `~/.config/nix/nix.conf`: - - ``` - access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk - ``` - - Example `~/code/flake.nix`: - - ```nix - input.foo = { - type = "gitlab"; - host = "gitlab.mycompany.com"; - owner = "mycompany"; - repo = "pro"; - }; - ``` - - This example specifies three tokens, one each for accessing - github.com, gitlab.mycompany.com, and gitlab.com. - - The `input.foo` uses the "gitlab" fetcher, which might - requires specifying the token type along with the token - value. - )"}; - - Setting allowDirty{this, true, "allow-dirty", - "Whether to allow dirty Git/Mercurial trees."}; - - Setting warnDirty{this, true, "warn-dirty", - "Whether to warn about dirty Git/Mercurial trees."}; - - Setting flakeRegistry{this, "vendored", "flake-registry", - R"( - Path or URI of the global flake registry. - - URIs are deprecated. When set to 'vendored', defaults to a vendored - copy of https://channels.nixos.org/flake-registry.json. - - When empty, disables the global flake registry. - )", - {}, true, Xp::Flakes}; - - - Setting useRegistries{this, true, "use-registries", - "Whether to use flake registries to resolve flake references.", - {}, true, Xp::Flakes}; - - Setting acceptFlakeConfig{ - this, AcceptFlakeConfig::Ask, "accept-flake-config", - R"( - Whether to accept Lix configuration from the `nixConfig` attribute of - a flake. Doing so as a trusted user allows Nix flakes to gain root - access on your machine if they set one of the several - trusted-user-only settings that execute commands as root. - - If set to `true`, such configuration will be accepted without asking; - this is almost always a very bad idea. Setting this to `ask` will - prompt the user each time whether to allow a certain configuration - option set this way, and offer to optionally remember their choice. - When set to `false`, the configuration will be automatically - declined. - - See [multi-user installations](@docroot@/installation/multi-user.md) - for more details on the Lix security model. - )", - {}, true, Xp::Flakes}; - - Setting commitLockFileSummary{ - this, "", "commit-lockfile-summary", - R"( - The commit summary to use when committing changed flake lock files. If - empty, the summary is generated based on the action performed. - )", - {}, true, Xp::Flakes}; + #include "libfetchers-settings.gen.inc" }; // FIXME: don't use a global variable. diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index b66d0b9f9..e388e5498 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -22,8 +22,33 @@ libfetchers_headers = files( 'registry.hh', ) +libfetchers_setting_definitions = files( + 'settings/accept-flake-config.md', + 'settings/access-tokens.md', + 'settings/allow-dirty.md', + 'settings/commit-lockfile-summary.md', + 'settings/flake-registry.md', + 'settings/use-registries.md', + 'settings/warn-dirty.md', +) +libfetchers_settings_header = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : libfetchers_setting_definitions, + output : 'libfetchers-settings.gen.inc', + install : true, + install_dir : includedir / 'lix/libfetchers', +) + libfetchers = library( 'lixfetchers', + libfetchers_settings_header, libfetchers_sources, dependencies : [ liblixstore, @@ -54,6 +79,7 @@ configure_file( liblixfetchers = declare_dependency( include_directories : include_directories('.'), + sources : libfetchers_settings_header, link_with : libfetchers, ) @@ -61,6 +87,7 @@ liblixfetchers = declare_dependency( if is_static liblixfetchers_mstatic = declare_dependency( include_directories : include_directories('.'), + sources : libfetchers_settings_header, link_whole : libfetchers, ) else diff --git a/src/libfetchers/settings/accept-flake-config.md b/src/libfetchers/settings/accept-flake-config.md new file mode 100644 index 000000000..fd4113b6c --- /dev/null +++ b/src/libfetchers/settings/accept-flake-config.md @@ -0,0 +1,22 @@ +--- +name: accept-flake-config +internalName: acceptFlakeConfig +type: AcceptFlakeConfig +defaultExpr: AcceptFlakeConfig::Ask +defaultText: '`ask`' +experimentalFeature: flakes +--- +Whether to accept Lix configuration from the `nixConfig` attribute of +a flake. Doing so as a trusted user allows Nix flakes to gain root +access on your machine if they set one of the several +trusted-user-only settings that execute commands as root. + +If set to `true`, such configuration will be accepted without asking; +this is almost always a very bad idea. Setting this to `ask` will +prompt the user each time whether to allow a certain configuration +option set this way, and offer to optionally remember their choice. +When set to `false`, the configuration will be automatically +declined. + +See [multi-user installations](@docroot@/installation/multi-user.md) +for more details on the Lix security model. diff --git a/src/libfetchers/settings/access-tokens.md b/src/libfetchers/settings/access-tokens.md new file mode 100644 index 000000000..b985a3f47 --- /dev/null +++ b/src/libfetchers/settings/access-tokens.md @@ -0,0 +1,51 @@ +--- +name: access-tokens +internalName: accessTokens +type: StringMap +default: [] +--- +Access tokens used to access protected GitHub, GitLab, or +other locations requiring token-based authentication. + +Access tokens are specified as a string made up of +space-separated `host=token` values. The specific token +used is selected by matching the `host` portion against the +"host" specification of the input. The actual use of the +`token` value is determined by the type of resource being +accessed: + +* Github: the token value is the OAUTH-TOKEN string obtained + as the Personal Access Token from the Github server (see + https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps). + +* Gitlab: the token value is either the OAuth2 token or the + Personal Access Token (these are different types tokens + for gitlab, see + https://docs.gitlab.com/12.10/ee/api/README.html#authentication). + The `token` value should be `type:tokenstring` where + `type` is either `OAuth2` or `PAT` to indicate which type + of token is being specified. + +Example `~/.config/nix/nix.conf`: + +``` +access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk +``` + +Example `~/code/flake.nix`: + +```nix +input.foo = { + type = "gitlab"; + host = "gitlab.mycompany.com"; + owner = "mycompany"; + repo = "pro"; +}; +``` + +This example specifies three tokens, one each for accessing +github.com, gitlab.mycompany.com, and gitlab.com. + +The `input.foo` uses the "gitlab" fetcher, which might +requires specifying the token type along with the token +value. diff --git a/src/libfetchers/settings/allow-dirty.md b/src/libfetchers/settings/allow-dirty.md new file mode 100644 index 000000000..18252ca3c --- /dev/null +++ b/src/libfetchers/settings/allow-dirty.md @@ -0,0 +1,7 @@ +--- +name: allow-dirty +internalName: allowDirty +type: bool +default: true +--- +Whether to allow dirty Git/Mercurial trees. diff --git a/src/libfetchers/settings/commit-lockfile-summary.md b/src/libfetchers/settings/commit-lockfile-summary.md new file mode 100644 index 000000000..fb0e61a94 --- /dev/null +++ b/src/libfetchers/settings/commit-lockfile-summary.md @@ -0,0 +1,9 @@ +--- +name: commit-lockfile-summary +internalName: commitLockFileSummary +type: std::string +default: '' +experimentalFeature: flakes +--- +The commit summary to use when committing changed flake lock files. If +empty, the summary is generated based on the action performed. diff --git a/src/libfetchers/settings/flake-registry.md b/src/libfetchers/settings/flake-registry.md new file mode 100644 index 000000000..7c465a2eb --- /dev/null +++ b/src/libfetchers/settings/flake-registry.md @@ -0,0 +1,13 @@ +--- +name: flake-registry +internalName: flakeRegistry +type: std::string +default: vendored +experimentalFeature: flakes +--- +Path or URI of the global flake registry. + +URIs are deprecated. When set to 'vendored', defaults to a vendored +copy of https://channels.nixos.org/flake-registry.json. + +When empty, disables the global flake registry. diff --git a/src/libfetchers/settings/use-registries.md b/src/libfetchers/settings/use-registries.md new file mode 100644 index 000000000..dc1a59c84 --- /dev/null +++ b/src/libfetchers/settings/use-registries.md @@ -0,0 +1,8 @@ +--- +name: use-registries +internalName: useRegistries +type: bool +default: true +experimentalFeature: flakes +--- +Whether to use flake registries to resolve flake references. diff --git a/src/libfetchers/settings/warn-dirty.md b/src/libfetchers/settings/warn-dirty.md new file mode 100644 index 000000000..02ab2dd58 --- /dev/null +++ b/src/libfetchers/settings/warn-dirty.md @@ -0,0 +1,7 @@ +--- +name: warn-dirty +internalName: warnDirty +type: bool +default: true +--- +Whether to warn about dirty Git/Mercurial trees. diff --git a/src/libstore/file-transfer-settings/connect-timeout.md b/src/libstore/file-transfer-settings/connect-timeout.md new file mode 100644 index 000000000..b76fc306d --- /dev/null +++ b/src/libstore/file-transfer-settings/connect-timeout.md @@ -0,0 +1,9 @@ +--- +name: connect-timeout +internalName: connectTimeout +type: unsigned long +default: 0 +--- +The timeout (in seconds) for establishing connections in the +binary cache substituter. It corresponds to `curl`’s +`--connect-timeout` option. A value of 0 means no limit. diff --git a/src/libstore/file-transfer-settings/download-attempts.md b/src/libstore/file-transfer-settings/download-attempts.md new file mode 100644 index 000000000..db614219b --- /dev/null +++ b/src/libstore/file-transfer-settings/download-attempts.md @@ -0,0 +1,7 @@ +--- +name: download-attempts +internalName: tries +type: unsigned int +default: 5 +--- +How often Lix will attempt to download a file before giving up. diff --git a/src/libstore/file-transfer-settings/http-connections.md b/src/libstore/file-transfer-settings/http-connections.md new file mode 100644 index 000000000..45a75b029 --- /dev/null +++ b/src/libstore/file-transfer-settings/http-connections.md @@ -0,0 +1,10 @@ +--- +name: http-connections +internalName: httpConnections +type: size_t +default: 25 +aliases: [binary-caches-parallel-connections] +--- +The maximum number of parallel TCP connections used to fetch +files from binary caches and by other downloads. It defaults +to 25. 0 means no limit. diff --git a/src/libstore/file-transfer-settings/http2.md b/src/libstore/file-transfer-settings/http2.md new file mode 100644 index 000000000..b485d9844 --- /dev/null +++ b/src/libstore/file-transfer-settings/http2.md @@ -0,0 +1,7 @@ +--- +name: http2 +internalName: enableHttp2 +type: bool +default: true +--- +Whether to enable HTTP/2 support. diff --git a/src/libstore/file-transfer-settings/stalled-download-timeout.md b/src/libstore/file-transfer-settings/stalled-download-timeout.md new file mode 100644 index 000000000..6a4248224 --- /dev/null +++ b/src/libstore/file-transfer-settings/stalled-download-timeout.md @@ -0,0 +1,9 @@ +--- +name: stalled-download-timeout +internalName: stalledDownloadTimeout +type: unsigned long +default: 300 +--- +The timeout (in seconds) for receiving data from servers +during download. Lix cancels idle downloads after this +timeout's duration. diff --git a/src/libstore/file-transfer-settings/user-agent-suffix.md b/src/libstore/file-transfer-settings/user-agent-suffix.md new file mode 100644 index 000000000..31ee9f51c --- /dev/null +++ b/src/libstore/file-transfer-settings/user-agent-suffix.md @@ -0,0 +1,7 @@ +--- +name: user-agent-suffix +internalName: userAgentSuffix +type: std::string +default: '' +--- +String appended to the user agent in HTTP requests. diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index 1cdd1af42..417102dde 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -15,39 +15,7 @@ namespace nix { struct FileTransferSettings : Config { - Setting enableHttp2{this, true, "http2", - "Whether to enable HTTP/2 support."}; - - Setting userAgentSuffix{this, "", "user-agent-suffix", - "String appended to the user agent in HTTP requests."}; - - Setting httpConnections{ - this, 25, "http-connections", - R"( - The maximum number of parallel TCP connections used to fetch - files from binary caches and by other downloads. It defaults - to 25. 0 means no limit. - )", - {"binary-caches-parallel-connections"}}; - - Setting connectTimeout{ - this, 0, "connect-timeout", - R"( - The timeout (in seconds) for establishing connections in the - binary cache substituter. It corresponds to `curl`’s - `--connect-timeout` option. A value of 0 means no limit. - )"}; - - Setting stalledDownloadTimeout{ - this, 300, "stalled-download-timeout", - R"( - The timeout (in seconds) for receiving data from servers - during download. Lix cancels idle downloads after this - timeout's duration. - )"}; - - Setting tries{this, 5, "download-attempts", - "How often Lix will attempt to download a file before giving up."}; + #include "file-transfer-settings.gen.inc" }; extern FileTransferSettings fileTransferSettings; diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 5fadc21e6..6c0a62741 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -23,8 +23,11 @@ struct MaxBuildJobsSetting : public BaseSetting unsigned int def, const std::string & name, const std::string & description, - const std::set & aliases = {}) - : BaseSetting(def, true, name, description, aliases) + const std::set & aliases = {}, + const bool documentDefault = true, + std::optional experimentalFeature = std::nullopt, + bool deprecated = false) + : BaseSetting(def, true, name, description, aliases, experimentalFeature, deprecated) { options->addSetting(this); } @@ -40,8 +43,11 @@ struct PluginFilesSetting : public BaseSetting const Paths & def, const std::string & name, const std::string & description, - const std::set & aliases = {}) - : BaseSetting(def, true, name, description, aliases) + const std::set & aliases = {}, + const bool documentDefault = true, + std::optional experimentalFeature = std::nullopt, + bool deprecated = false) + : BaseSetting(def, true, name, description, aliases, experimentalFeature, deprecated) { options->addSetting(this); } @@ -117,984 +123,18 @@ public: */ Path nixDaemonSocketFile; - Setting storeUri{this, getEnv("NIX_REMOTE").value_or("auto"), "store", - R"( - The [URL of the Nix store](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) - to use for most operations. - See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) - for supported store types and settings. - )"}; - - Setting keepFailed{this, false, "keep-failed", - "Whether to keep temporary directories of failed builds."}; - - Setting keepGoing{this, false, "keep-going", - "Whether to keep building derivations when another build fails."}; - - Setting tryFallback{ - this, false, "fallback", - R"( - If set to `true`, Lix will fall back to building from source if a - binary substitute fails. This is equivalent to the `--fallback` - flag. The default is `false`. - )", - {"build-fallback"}}; - /** * Whether to show build log output in real time. */ bool verboseBuild = true; - Setting logLines{this, 25, "log-lines", - "The number of lines of the tail of " - "the log to show if a build fails."}; - - MaxBuildJobsSetting maxBuildJobs{ - this, 1, "max-jobs", - R"( - This option defines the maximum number of jobs that Lix will try to - build in parallel. The default is `1`. The special value `auto` - causes Lix to use the number of CPUs in your system. `0` is useful - when using remote builders to prevent any local builds (except for - `preferLocalBuild` derivation attribute which executes locally - regardless). It can be overridden using the `--max-jobs` (`-j`) - command line switch. - )", - {"build-max-jobs"}}; - - Setting maxSubstitutionJobs{ - this, 16, "max-substitution-jobs", - R"( - This option defines the maximum number of substitution jobs that Nix - will try to run in parallel. The default is `16`. The minimum value - one can choose is `1` and lower values will be interpreted as `1`. - )", - {"substitution-max-jobs"}}; - - Setting buildCores{ - this, - getDefaultCores(), - "cores", - R"( - Sets the value of the `NIX_BUILD_CORES` environment variable in the - invocation of builders. Builders can use this variable at their - discretion to control the maximum amount of parallelism. For - instance, in Nixpkgs, if the derivation attribute - `enableParallelBuilding` is set to `true`, the builder passes the - `-jN` flag to GNU Make. It can be overridden using the `--cores` - command line switch and defaults to `1`. The value `0` means that - the builder should use all available CPU cores in the system. - )", - {"build-cores"}, - // Don't document the machine-specific default value - false}; - /** * Read-only mode. Don't copy stuff to the store, don't change * the database. */ bool readOnlyMode = false; - Setting thisSystem{ - this, SYSTEM, "system", - R"( - The system type of the current Lix installation. - Lix will only build a given [derivation](@docroot@/language/derivations.md) locally when its `system` attribute equals any of the values specified here or in [`extra-platforms`](#conf-extra-platforms). - - The default value is set when Lix itself is compiled for the system it will run on. - The following system types are widely used, as [Lix is actively supported on these platforms](@docroot@/contributing/hacking.md#platforms): - - - `x86_64-linux` - - `x86_64-darwin` - - `i686-linux` - - `aarch64-linux` - - `aarch64-darwin` - - `armv6l-linux` - - `armv7l-linux` - - In general, you do not have to modify this setting. - While you can force Lix to run a Darwin-specific `builder` executable on a Linux machine, the result would obviously be wrong. - - This value is available in the Nix language as - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) - if the - [`eval-system`](#conf-eval-system) - configuration option is set as the empty string. - )"}; - - Setting maxSilentTime{ - this, 0, "max-silent-time", - R"( - This option defines the maximum number of seconds that a builder can - go without producing any data on standard output or standard error. - This is useful (for instance in an automated build system) to catch - builds that are stuck in an infinite loop, or to catch remote builds - that are hanging due to network problems. It can be overridden using - the `--max-silent-time` command line switch. - - The value `0` means that there is no timeout. This is also the - default. - )", - {"build-max-silent-time"}}; - - Setting buildTimeout{ - this, 0, "timeout", - R"( - This option defines the maximum number of seconds that a builder can - run. This is useful (for instance in an automated build system) to - catch builds that are stuck in an infinite loop but keep writing to - their standard output or standard error. It can be overridden using - the `--timeout` command line switch. - - The value `0` means that there is no timeout. This is also the - default. - )", - {"build-timeout"}}; - - Setting buildHook{ - AbstractSetting::deprecated_t{}, - this, {}, "build-hook", - R"( - The path to the helper program that executes remote builds. - - Lix communicates with the build hook over `stdio` using a custom protocol to request builds that cannot be performed directly by the Nix daemon. - The default value is the internal Lix binary that implements remote building. - - > **Warning** - > - > This setting is deprecated and will be removed in a future version of Lix. - > - > Change this setting only if you really know what you’re doing. - )"}; - - Setting builders{ - this, "@" + nixConfDir + "/machines", "builders", - R"( - A semicolon-separated list of build machines. - For the exact format and examples, see [the manual chapter on remote builds](../advanced-topics/distributed-builds.md) - )"}; - - Setting alwaysAllowSubstitutes{ - this, false, "always-allow-substitutes", - R"( - If set to `true`, Lix will ignore the `allowSubstitutes` attribute in - derivations and always attempt to use available substituters. - For more information on `allowSubstitutes`, see [the manual chapter on advanced attributes](../language/advanced-attributes.md). - )"}; - - Setting buildersUseSubstitutes{ - this, false, "builders-use-substitutes", - R"( - If set to `true`, Lix will instruct remote build machines to use - their own binary substitutes if available. In practical terms, this - means that remote hosts will fetch as many build dependencies as - possible from their own substitutes (e.g, from `cache.nixos.org`), - instead of waiting for this host to upload them all. This can - drastically reduce build times if the network connection between - this computer and the remote build host is slow. - )"}; - - Setting reservedSize{this, 8 * 1024 * 1024, "gc-reserved-space", - "Amount of reserved disk space for the garbage collector."}; - - Setting enableCoreDumps{ - this, false, "enable-core-dumps", - R"( - If set to `false` (the default), `RLIMIT_CORE` has a soft limit of zero. - If set to `true`, the soft limit is infinite. - - The hard limit is always infinite. - )"}; - - Setting fsyncMetadata{ - this, true, "fsync-metadata", - R"( - If set to `true`, changes to the Nix store metadata (in - `/nix/var/nix/db`) are synchronously flushed to disk. This improves - robustness in case of system crashes, but reduces performance. The - default is `true`. - )"}; - - Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", - "Whether SQLite should use WAL mode."}; - - Setting syncBeforeRegistering{this, false, "sync-before-registering", - "Whether to call `sync()` before registering a path as valid."}; - - Setting useSubstitutes{ - this, true, "substitute", - R"( - If set to `true` (default), Lix will use binary substitutes if - available. This option can be disabled to force building from - source. - )", - {"build-use-substitutes"}}; - - Setting buildUsersGroup{ - this, "", "build-users-group", - R"( - This options specifies the Unix group containing the Lix build user - accounts. In multi-user Lix installations, builds should not be - performed by the Lix account since that would allow users to - arbitrarily modify the Nix store and database by supplying specially - crafted builders; and they cannot be performed by the calling user - since that would allow them to influence the build result. - - Therefore, if this option is non-empty and specifies a valid group, - builds will be performed under the user accounts that are a member - of the group specified here (as listed in `/etc/group`). Those user - accounts should not be used for any other purpose\! - - Lix will never run two builds under the same user account at the - same time. This is to prevent an obvious security hole: a malicious - user writing a Nix expression that modifies the build result of a - legitimate Nix expression being built by another user. Therefore it - is good to have as many Lix build user accounts as you can spare. - (Remember: uids are cheap.) - - The build users should have permission to create files in the Nix - store, but not delete them. Therefore, `/nix/store` should be owned - by the Nix account, its group should be the group specified here, - and its mode should be `1775`. - - If the build users group is empty, builds will be performed under - the uid of the Lix process (that is, the uid of the caller if - both `NIX_REMOTE` is either empty or `auto` and the Nix store is - owned by that user, or, alternatively, the uid under which the Nix - daemon runs if `NIX_REMOTE` is `daemon` or if it is `auto` and the - store is not owned by the caller). Obviously, this should not be used - with a nix daemon accessible to untrusted clients. - - For the avoidance of doubt, explicitly setting this to *empty* with a - Lix daemon running as root means that builds will be executed as root - with respect to the rest of the system. - We intend to fix this: https://git.lix.systems/lix-project/lix/issues/242 - - Defaults to `nixbld` when running as root, *empty* otherwise. - )", - {}, false}; - - Setting autoAllocateUids{this, false, "auto-allocate-uids", - R"( - Whether to select UIDs for builds automatically, instead of using the - users in `build-users-group`. - - UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS. - )", - {}, true, Xp::AutoAllocateUids}; - - Setting startId{this, - #if __linux__ - 0x34000000, - #else - 56930, - #endif - "start-id", - "The first UID and GID to use for dynamic ID allocation.", - {}, - true, - Xp::AutoAllocateUids}; - - Setting uidCount{this, - #if __linux__ - maxIdsPerBuild * 128, - #else - 128, - #endif - "id-count", - "The number of UIDs/GIDs to use for dynamic ID allocation.", - {}, - true, - Xp::AutoAllocateUids}; - - #if __linux__ - Setting useCgroups{ - this, false, "use-cgroups", - R"( - Whether to execute builds inside cgroups. - This is only supported on Linux. - - Cgroups are required and enabled automatically for derivations - that require the `uid-range` system feature. - )", - {}, true, Xp::Cgroups}; - - Setting impersonateLinux26{this, false, "impersonate-linux-26", - "Whether to impersonate a Linux 2.6 machine on newer kernels.", - {"build-impersonate-linux-26"}}; - #endif - - Setting keepLog{ - this, true, "keep-build-log", - R"( - If set to `true` (the default), Lix will write the build log of a - derivation (i.e. the standard output and error of its builder) to - the directory `/nix/var/log/nix/drvs`. The build log can be - retrieved using the command `nix-store -l path`. - )", - {"build-keep-log"}}; - - Setting compressLog{ - this, true, "compress-build-log", - R"( - If set to `true` (the default), build logs written to - `/nix/var/log/nix/drvs` will be compressed on the fly using bzip2. - Otherwise, they will not be compressed. - )", - {"build-compress-log"}}; - - Setting maxLogSize{ - this, 0, "max-build-log-size", - R"( - This option defines the maximum number of bytes that a builder can - write to its stdout/stderr. If the builder exceeds this limit, it’s - killed. A value of `0` (the default) means that there is no limit. - )", - {"build-max-log-size"}}; - - Setting pollInterval{this, 5, "build-poll-interval", - "How often (in seconds) to poll for locks."}; - - Setting gcKeepOutputs{ - this, false, "keep-outputs", - R"( - If `true`, the garbage collector will keep the outputs of - non-garbage derivations. If `false` (default), outputs will be - deleted unless they are GC roots themselves (or reachable from other - roots). - - In general, outputs must be registered as roots separately. However, - even if the output of a derivation is registered as a root, the - collector will still delete store paths that are used only at build - time (e.g., the C compiler, or source tarballs downloaded from the - network). To prevent it from doing so, set this option to `true`. - )", - {"gc-keep-outputs"}}; - - Setting gcKeepDerivations{ - this, true, "keep-derivations", - R"( - If `true` (default), the garbage collector will keep the derivations - from which non-garbage store paths were built. If `false`, they will - be deleted unless explicitly registered as a root (or reachable from - other roots). - - Keeping derivation around is useful for querying and traceability - (e.g., it allows you to ask with what dependencies or options a - store path was built), so by default this option is on. Turn it off - to save a bit of disk space (or a lot if `keep-outputs` is also - turned on). - )", - {"gc-keep-derivations"}}; - - Setting autoOptimiseStore{ - this, false, "auto-optimise-store", - R"( - If set to `true`, Lix automatically detects files in the store - that have identical contents, and replaces them with hard links to - a single copy. This saves disk space. If set to `false` (the - default), you can still run `nix-store --optimise` to get rid of - duplicate files. - )"}; - - Setting envKeepDerivations{ - this, false, "keep-env-derivations", - R"( - If `false` (default), derivations are not stored in Nix user - environments. That is, the derivations of any build-time-only - dependencies may be garbage-collected. - - If `true`, when you add a Nix derivation to a user environment, the - path of the derivation is stored in the user environment. Thus, the - derivation will not be garbage-collected until the user environment - generation is deleted (`nix-env --delete-generations`). To prevent - build-time-only dependencies from being collected, you should also - turn on `keep-outputs`. - - The difference between this option and `keep-derivations` is that - this one is “sticky”: it applies to any user environment created - while this option was enabled, while `keep-derivations` only applies - at the moment the garbage collector is run. - )", - {"env-keep-derivations"}}; - - Setting sandboxMode{ - this, - #if __linux__ - smEnabled - #else - smDisabled - #endif - , "sandbox", - R"( - If set to `true`, builds will be performed in a *sandboxed - environment*, i.e., they’re isolated from the normal file system - hierarchy and will only see their dependencies in the Nix store, - the temporary build directory, private versions of `/proc`, - `/dev`, `/dev/shm` and `/dev/pts` (on Linux), and the paths - configured with the `sandbox-paths` option. This is useful to - prevent undeclared dependencies on files in directories such as - `/usr/bin`. In addition, on Linux, builds run in private PID, - mount, network, IPC and UTS namespaces to isolate them from other - processes in the system (except that fixed-output derivations do - not run in private network namespace to ensure they can access the - network). - - Currently, sandboxing only work on Linux and macOS. The use of a - sandbox requires that Lix is run as root (so you should use the - “build users” feature to perform the actual builds under different - users than root). - - If this option is set to `relaxed`, then fixed-output derivations - and derivations that have the `__noChroot` attribute set to `true` - do not run in sandboxes. - - The default is `true` on Linux and `false` on all other platforms. - )", - {"build-use-chroot", "build-use-sandbox"}}; - - Setting sandboxPaths{ - this, {}, "sandbox-paths", - R"( - A list of paths bind-mounted into Nix sandbox environments. You can - use the syntax `target=source` to mount a path in a different - location in the sandbox; for instance, `/bin=/nix-bin` will mount - the path `/nix-bin` as `/bin` inside the sandbox. If *source* is - followed by `?`, then it is not an error if *source* does not exist; - for example, `/dev/nvidiactl?` specifies that `/dev/nvidiactl` will - only be mounted in the sandbox if it exists in the host filesystem. - - If the source is in the Nix store, then its closure will be added to - the sandbox as well. - - Depending on how Lix was built, the default value for this option - may be empty or provide `/bin/sh` as a bind-mount of `bash`. - )", - {"build-chroot-dirs", "build-sandbox-paths"}}; - - Setting sandboxFallback{this, true, "sandbox-fallback", - "Whether to disable sandboxing when the kernel doesn't allow it."}; - -#if __linux__ - Setting requireDropSupplementaryGroups{this, getuid() == 0, "require-drop-supplementary-groups", - R"( - Following the principle of least privilege, - Lix will attempt to drop supplementary groups when building with sandboxing. - - However this can fail under some circumstances. - For example, if the user lacks the `CAP_SETGID` capability. - Search `setgroups(2)` for `EPERM` to find more detailed information on this. - - If you encounter such a failure, setting this option to `false` will let you ignore it and continue. - But before doing so, you should consider the security implications carefully. - Not dropping supplementary groups means the build sandbox will be less restricted than intended. - - This option defaults to `true` when the user is root - (since `root` usually has permissions to call setgroups) - and `false` otherwise. - )"}; - - Setting sandboxShmSize{ - this, "50%", "sandbox-dev-shm-size", - R"( - *Linux only* - - This option determines the maximum size of the `tmpfs` filesystem - mounted on `/dev/shm` in Linux sandboxes. For the format, see the - description of the `size` option of `tmpfs` in mount(8). The default - is `50%`. - )"}; - - Setting sandboxBuildDir{this, "/build", "sandbox-build-dir", - R"( - *Linux only* - - The build directory inside the sandbox. - - This directory is backed by [`build-dir`](#conf-build-dir) on the host. - )"}; -#endif - - PathsSetting> buildDir{this, std::nullopt, "build-dir", - R"( - The directory on the host, in which derivations' temporary build directories are created. - - If not set, Nix will use the [`temp-dir`](#conf-temp-dir) setting if set, otherwise the system temporary directory indicated by the `TMPDIR` environment variable. - Note that builds are often performed by the Nix daemon, so its `TMPDIR` is used, and not that of the Nix command line interface. - - This is also the location where [`--keep-failed`](@docroot@/command-ref/opt-common.md#opt-keep-failed) leaves its files. - - If Nix runs without sandbox, or if the platform does not support sandboxing with bind mounts (e.g. macOS), then the [`builder`](@docroot@/language/derivations.md#attr-builder)'s environment will contain this directory, instead of the virtual location [`sandbox-build-dir`](#conf-sandbox-build-dir). - )"}; - - PathsSetting> tempDir{this, std::nullopt, "temp-dir", - R"( - The directory on the host used as the default temporary directory. - - If not set, Nix will use the system temporary directory indicated by the `TMPDIR` environment variable. - - This will be used for anything that would otherwise fall back to `TMPDIR`, and the inherited `TMPDIR` value will be preserved for child processes to use. - If [`build-dir`](#conf-build-dir) is set, that takes precedence over this where it applies. - - If set, the value must be a path that exists and is accessible to all users. - )"}; - - Setting allowedImpureHostPrefixes{this, {}, "allowed-impure-host-deps", - "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."}; - -#if __APPLE__ - Setting darwinLogSandboxViolations{this, false, "darwin-log-sandbox-violations", - "Whether to log Darwin sandbox access violations to the system log."}; -#endif - - Setting runDiffHook{ - this, false, "run-diff-hook", - R"( - If true, enable the execution of the `diff-hook` program. - - When using the Nix daemon, `run-diff-hook` must be set in the - `nix.conf` configuration file, and cannot be passed at the command - line. - )"}; - - PathsSetting> diffHook{ - this, std::nullopt, "diff-hook", - R"( - Path to an executable capable of diffing build results. The hook is - executed if `run-diff-hook` is true, and the output of a build is - known to not be the same. This program is not executed to determine - if two results are the same. - - The diff hook is executed by the same user and group who ran the - build. However, the diff hook does not have write access to the - store path just built. - - The diff hook program receives three parameters: - - 1. A path to the previous build's results - - 2. A path to the current build's results - - 3. The path to the build's derivation - - 4. The path to the build's scratch directory. This directory will - exist only if the build was run with `--keep-failed`. - - The stderr and stdout output from the diff hook will not be - displayed to the user. Instead, it will print to the nix-daemon's - log. - - When using the Nix daemon, `diff-hook` must be set in the `nix.conf` - configuration file, and cannot be passed at the command line. - )"}; - - Setting trustedPublicKeys{ - this, - {"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="}, - "trusted-public-keys", - R"( - A whitespace-separated list of public keys. - - At least one of the following condition must be met - for Lix to accept copying a store object from another - Nix store (such as a substituter): - - - the store object has been signed using a key in the trusted keys list - - the [`require-sigs`](#conf-require-sigs) option has been set to `false` - - the store object is [output-addressed](@docroot@/glossary.md#gloss-output-addressed-store-object) - )", - {"binary-cache-public-keys"}}; - - Setting secretKeyFiles{ - this, {}, "secret-key-files", - R"( - A whitespace-separated list of files containing secret (private) - keys. These are used to sign locally-built paths. They can be - generated using `nix-store --generate-binary-cache-key`. The - corresponding public key can be distributed to other users, who - can add it to `trusted-public-keys` in their `nix.conf`. - )"}; - - Setting tarballTtl{ - this, 60 * 60, "tarball-ttl", - R"( - The number of seconds a downloaded tarball is considered fresh. If - the cached tarball is stale, Lix will check whether it is still up - to date using the ETag header. Lix will download a new version if - the ETag header is unsupported, or the cached ETag doesn't match. - - Setting the TTL to `0` forces Lix to always check if the tarball is - up to date. - - Lix caches tarballs in `$XDG_CACHE_HOME/nix/tarballs`. - - Files fetched via `NIX_PATH`, `fetchGit`, `fetchMercurial`, - `fetchTarball`, and `fetchurl` respect this TTL. - )"}; - - Setting requireSigs{ - this, true, "require-sigs", - R"( - If set to `true` (the default), any non-content-addressed path added - or copied to the Nix store (e.g. when substituting from a binary - cache) must have a signature by a trusted key. A trusted key is one - listed in `trusted-public-keys`, or a public key counterpart to a - private key stored in a file listed in `secret-key-files`. - - Set to `false` to disable signature checking and trust all - non-content-addressed paths unconditionally. - - (Content-addressed paths are inherently trustworthy and thus - unaffected by this configuration option.) - )"}; - - Setting extraPlatforms{ - this, - getDefaultExtraPlatforms(), - "extra-platforms", - R"( - System types of executables that can be run on this machine. - - Lix will only build a given [derivation](@docroot@/language/derivations.md) locally when its `system` attribute equals any of the values specified here or in the [`system` option](#conf-system). - - Setting this can be useful to build derivations locally on compatible machines: - - `i686-linux` executables can be run on `x86_64-linux` machines (set by default) - - `x86_64-darwin` executables can be run on macOS `aarch64-darwin` with Rosetta 2 (set by default where applicable) - - `armv6` and `armv5tel` executables can be run on `armv7` - - some `aarch64` machines can also natively run 32-bit ARM code - - `qemu-user` may be used to support non-native platforms (though this - may be slow and buggy) - - Build systems will usually detect the target platform to be the current physical system and therefore produce machine code incompatible with what may be intended in the derivation. - You should design your derivation's `builder` accordingly and cross-check the results when using this option against natively-built versions of your derivation. - )", - {}, - // Don't document the machine-specific default value - false}; - - Setting systemFeatures{ - this, - getDefaultSystemFeatures(), - "system-features", - R"( - A set of system “features” supported by this machine, e.g. `kvm`. - Derivations can express a dependency on such features through the - derivation attribute `requiredSystemFeatures`. For example, the - attribute - - requiredSystemFeatures = [ "kvm" ]; - - ensures that the derivation can only be built on a machine with the - `kvm` feature. - - This setting by default includes `kvm` if `/dev/kvm` is accessible, - `apple-virt` if hardware virtualization is available on macOS, - and the pseudo-features `nixos-test`, `benchmark` and `big-parallel` - that are used in Nixpkgs to route builds to specific machines. - )", - {}, - // Don't document the machine-specific default value - false}; - - Setting substituters{ - this, - Strings{"https://cache.nixos.org/"}, - "substituters", - R"( - A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) to be used as substituters, separated by whitespace. - A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Lix can obtain [store objects](@docroot@/glossary.md#gloss-store-object) instead of building them. - - Substituters are tried based on their priority value, which each substituter can set independently. - Lower value means higher priority. - The default is `https://cache.nixos.org`, which has a priority of 40. - - At least one of the following conditions must be met for Lix to use a substituter: - - - The substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list - - The user calling Lix is in the [`trusted-users`](#conf-trusted-users) list - - In addition, each store path should be trusted as described in [`trusted-public-keys`](#conf-trusted-public-keys) - )", - {"binary-caches"}}; - - Setting trustedSubstituters{ - this, {}, "trusted-substituters", - R"( - A list of [Nix store URLs](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format), separated by whitespace. - These are not used by default, but users of the Nix daemon can enable them by specifying [`substituters`](#conf-substituters). - - Unprivileged users (those set in only [`allowed-users`](#conf-allowed-users) but not [`trusted-users`](#conf-trusted-users)) can pass as `substituters` only those URLs listed in `trusted-substituters`. - )", - {"trusted-binary-caches"}}; - - Setting ttlNegativeNarInfoCache{ - this, 3600, "narinfo-cache-negative-ttl", - R"( - The TTL in seconds for negative lookups. If a store path is queried - from a substituter but was not found, there will be a negative - lookup cached in the local disk cache database for the specified - duration. - )"}; - - Setting ttlPositiveNarInfoCache{ - this, 30 * 24 * 3600, "narinfo-cache-positive-ttl", - R"( - The TTL in seconds for positive lookups. If a store path is queried - from a substituter, the result of the query will be cached in the - local disk cache database including some of the NAR metadata. The - default TTL is a month, setting a shorter TTL for positive lookups - can be useful for binary caches that have frequent garbage - collection, in which case having a more frequent cache invalidation - would prevent trying to pull the path again and failing with a hash - mismatch if the build isn't reproducible. - )"}; - - Setting printMissing{this, true, "print-missing", - "Whether to print what paths need to be built or downloaded."}; - - Setting preBuildHook{ - this, "", "pre-build-hook", - R"( - If set, the path to a program that can set extra derivation-specific - settings for this system. This is used for settings that can't be - captured by the derivation model itself and are too variable between - different versions of the same system to be hard-coded into nix. - - The hook is passed the derivation path and, if sandboxes are - enabled, the sandbox directory. It can then modify the sandbox and - send a series of commands to modify various settings to stdout. The - currently recognized commands are: - - - `extra-sandbox-paths`\ - Pass a list of files and directories to be included in the - sandbox for this build. One entry per line, terminated by an - empty line. Entries have the same format as `sandbox-paths`. - )"}; - - Setting postBuildHook{ - this, "", "post-build-hook", - R"( - Optional. The path to a program to execute after each build. - - This option is only settable in the global `nix.conf`, or on the - command line by trusted users. - - When using the nix-daemon, the daemon executes the hook as `root`. - If the nix-daemon is not involved, the hook runs as the user - executing the nix-build. - - - The hook executes after an evaluation-time build. - - - The hook does not execute on substituted paths. - - - The hook's output always goes to the user's terminal. - - - If the hook fails, the build succeeds but no further builds - execute. - - - The hook executes synchronously, and blocks other builds from - progressing while it runs. - - The program executes with no arguments. The program's environment - contains the following environment variables: - - - `DRV_PATH` - The derivation for the built paths. - - Example: - `/nix/store/5nihn1a7pa8b25l9zafqaqibznlvvp3f-bash-4.4-p23.drv` - - - `OUT_PATHS` - Output paths of the built derivation, separated by a space - character. - - Example: - `/nix/store/zf5lbh336mnzf1nlswdn11g4n2m8zh3g-bash-4.4-p23-dev - /nix/store/rjxwxwv1fpn9wa2x5ssk5phzwlcv4mna-bash-4.4-p23-doc - /nix/store/6bqvbzjkcp9695dq0dpl5y43nvy37pq1-bash-4.4-p23-info - /nix/store/r7fng3kk3vlpdlh2idnrbn37vh4imlj2-bash-4.4-p23-man - /nix/store/xfghy8ixrhz3kyy6p724iv3cxji088dx-bash-4.4-p23`. - )"}; - - Setting downloadSpeed { - this, 0, "download-speed", - R"( - Specify the maximum transfer rate in kilobytes per second you want - Lix to use for downloads. - )"}; - - Setting netrcFile{ - this, fmt("%s/%s", nixConfDir, "netrc"), "netrc-file", - R"( - If set to an absolute path to a `netrc` file, Lix will use the HTTP - authentication credentials in this file when trying to download from - a remote host through HTTP or HTTPS. Defaults to - `$NIX_CONF_DIR/netrc`. - - The `netrc` file consists of a list of accounts in the following - format: - - machine my-machine - login my-username - password my-password - - For the exact syntax, see [the `curl` - documentation](https://ec.haxx.se/usingcurl-netrc.html). - - > **Note** - > - > This must be an absolute path, and `~` is not resolved. For - > example, `~/.netrc` won't resolve to your home directory's - > `.netrc`. - )"}; - - Setting caFile{ - this, getDefaultSSLCertFile(), "ssl-cert-file", - R"( - The path of a file containing CA certificates used to - authenticate `https://` downloads. Lix by default will use - the first of the following files that exists: - - 1. `/etc/ssl/certs/ca-certificates.crt` - 2. `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt` - - The path can be overridden by the following environment - variables, in order of precedence: - - 1. `NIX_SSL_CERT_FILE` - 2. `SSL_CERT_FILE` - )"}; - -#if __linux__ - Setting ignoredAcls{ - this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls", - R"( - A list of ACLs that should be ignored, normally Lix attempts to - remove all ACLs from files and directories in the Nix store, but - some ACLs like `security.selinux` or `system.nfs4_acl` can't be - removed even by root. Therefore it's best to just ignore them. - )"}; -#endif - - Setting hashedMirrors{ - this, {}, "hashed-mirrors", - R"( - A list of web servers used by `builtins.fetchurl` to obtain files by - hash. Given a hash type *ht* and a base-16 hash *h*, Lix will try to - download the file from *hashed-mirror*/*ht*/*h*. This allows files to - be downloaded even if they have disappeared from their original URI. - For example, given an example mirror `http://tarballs.nixos.org/`, - when building the derivation - - ```nix - builtins.fetchurl { - url = "https://example.org/foo-1.2.3.tar.xz"; - sha256 = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"; - } - ``` - - Lix will attempt to download this file from - `http://tarballs.nixos.org/sha256/2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae` - first. If it is not available there, if will try the original URI. - )"}; - - Setting minFree{ - this, 0, "min-free", - R"( - When free disk space in `/nix/store` drops below `min-free` during a - build, Lix performs a garbage-collection until `max-free` bytes are - available or there is no more garbage. A value of `0` (the default) - disables this feature. - )"}; - - Setting maxFree{ - // n.b. this is deliberately int64 max rather than uint64 max because - // this goes through the Nix language JSON parser and thus needs to be - // representable in Nix language integers. - this, std::numeric_limits::max(), "max-free", - R"( - When a garbage collection is triggered by the `min-free` option, it - stops as soon as `max-free` bytes are available. The default is - infinity (i.e. delete all garbage). - )"}; - - Setting minFreeCheckInterval{this, 5, "min-free-check-interval", - "Number of seconds between checking free disk space."}; - - PluginFilesSetting pluginFiles{ - this, {}, "plugin-files", - R"( - A list of plugin files to be loaded by Nix. Each of these files will - be dlopened by Nix, allowing them to affect execution through static - initialization. In particular, these plugins may construct static - instances of RegisterPrimOp to add new primops or constants to the - expression language, RegisterStoreImplementation to add new store - implementations, RegisterCommand to add new subcommands to the `nix` - command, and RegisterSetting to add new nix config settings. See the - constructors for those types for more details. - - Warning! These APIs are inherently unstable and may change from - release to release. - - Since these files are loaded into the same address space as Nix - itself, they must be DSOs compatible with the instance of Nix - running at the time (i.e. compiled against the same headers, not - linked to any incompatible libraries). They should not be linked to - any Lix libs directly, as those will be available already at load - time. - - If an entry in the list is a directory, all files in the directory - are loaded as plugins (non-recursively). - )"}; - - Setting narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size", - "Maximum size of NARs before spilling them to disk."}; - - Setting allowSymlinkedStore{ - this, false, "allow-symlinked-store", - R"( - If set to `true`, Lix will stop complaining if the store directory - (typically /nix/store) contains symlink components. - - This risks making some builds "impure" because builders sometimes - "canonicalise" paths by resolving all symlink components. Problems - occur if those builds are then deployed to machines where /nix/store - resolves to a different location from that of the build machine. You - can enable this setting if you are sure you're not going to do that. - )"}; - - Setting useXDGBaseDirectories{ - this, false, "use-xdg-base-directories", - R"( - If set to `true`, Lix will conform to the [XDG Base Directory Specification] for files in `$HOME`. - The environment variables used to implement this are documented in the [Environment Variables section](@docroot@/command-ref/env-common.md). - - [XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - - > **Warning** - > This changes the location of some well-known symlinks that Lix creates, which might break tools that rely on the old, non-XDG-conformant locations. - - In particular, the following locations change: - - | Old | New | - |-------------------|--------------------------------| - | `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` | - | `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` | - | `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` | - - If you already have Lix installed and are using [profiles](@docroot@/package-management/profiles.md) or [channels](@docroot@/command-ref/nix-channel.md), you should migrate manually when you enable this option. - If `$XDG_STATE_HOME` is not set, use `$HOME/.local/state/nix` instead of `$XDG_STATE_HOME/nix`. - This can be achieved with the following shell commands: - - ```sh - nix_state_home=${XDG_STATE_HOME-$HOME/.local/state}/nix - mkdir -p $nix_state_home - mv $HOME/.nix-profile $nix_state_home/profile - mv $HOME/.nix-defexpr $nix_state_home/defexpr - mv $HOME/.nix-channels $nix_state_home/channels - ``` - )" - }; + #include "libstore-settings.gen.inc" }; diff --git a/src/libstore/meson.build b/src/libstore/meson.build index bde8792aa..04bdf70dd 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -28,6 +28,120 @@ if enable_embedded_sandbox_shell libstore_generated_headers += embedded_sandbox_shell_gen endif +libstore_settings_headers = [] + +file_transfer_setting_definitions = files( + 'file-transfer-settings/connect-timeout.md', + 'file-transfer-settings/download-attempts.md', + 'file-transfer-settings/http-connections.md', + 'file-transfer-settings/http2.md', + 'file-transfer-settings/stalled-download-timeout.md', + 'file-transfer-settings/user-agent-suffix.md', +) +libstore_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : file_transfer_setting_definitions, + output : 'file-transfer-settings.gen.inc', + install : true, + install_dir : includedir / 'lix/libstore', +) + +libstore_setting_definitions = files( + 'settings/allow-symlinked-store.md', + 'settings/allowed-impure-host-deps.md', + 'settings/always-allow-substitutes.md', + 'settings/auto-allocate-uids.md', + 'settings/auto-optimise-store.md', + 'settings/build-dir.md', + 'settings/build-hook.md', + 'settings/build-poll-interval.md', + 'settings/build-users-group.md', + 'settings/builders-use-substitutes.md', + 'settings/builders.md', + 'settings/compress-build-log.md', + 'settings/cores.md', + 'settings/darwin-log-sandbox-violations.md', + 'settings/diff-hook.md', + 'settings/download-speed.md', + 'settings/enable-core-dumps.md', + 'settings/extra-platforms.md', + 'settings/fallback.md', + 'settings/fsync-metadata.md', + 'settings/gc-reserved-space.md', + 'settings/hashed-mirrors.md', + 'settings/id-count.md', + 'settings/ignored-acls.md', + 'settings/impersonate-linux-26.md', + 'settings/keep-build-log.md', + 'settings/keep-derivations.md', + 'settings/keep-env-derivations.md', + 'settings/keep-failed.md', + 'settings/keep-going.md', + 'settings/keep-outputs.md', + 'settings/log-lines.md', + 'settings/max-build-log-size.md', + 'settings/max-free.md', + 'settings/max-jobs.md', + 'settings/max-silent-time.md', + 'settings/max-substitution-jobs.md', + 'settings/min-free-check-interval.md', + 'settings/min-free.md', + 'settings/nar-buffer-size.md', + 'settings/narinfo-cache-negative-ttl.md', + 'settings/narinfo-cache-positive-ttl.md', + 'settings/netrc-file.md', + 'settings/plugin-files.md', + 'settings/post-build-hook.md', + 'settings/pre-build-hook.md', + 'settings/print-missing.md', + 'settings/require-drop-supplementary-groups.md', + 'settings/require-sigs.md', + 'settings/run-diff-hook.md', + 'settings/sandbox-build-dir.md', + 'settings/sandbox-dev-shm-size.md', + 'settings/sandbox-fallback.md', + 'settings/sandbox-paths.md', + 'settings/sandbox.md', + 'settings/secret-key-files.md', + 'settings/ssl-cert-file.md', + 'settings/start-id.md', + 'settings/store.md', + 'settings/substitute.md', + 'settings/substituters.md', + 'settings/sync-before-registering.md', + 'settings/system-features.md', + 'settings/system.md', + 'settings/tarball-ttl.md', + 'settings/temp-dir.md', + 'settings/timeout.md', + 'settings/trusted-public-keys.md', + 'settings/trusted-substituters.md', + 'settings/use-cgroups.md', + 'settings/use-sqlite-wal.md', + 'settings/use-xdg-base-directories.md', +) +libstore_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : libstore_setting_definitions, + output : 'libstore-settings.gen.inc', + install : true, + install_dir : includedir / 'lix/libstore', +) + libstore_sources = files( 'binary-cache-store.cc', 'build-result.cc', @@ -232,8 +346,9 @@ endif libstore = library( 'lixstore', - libstore_generated_headers, libstore_sources, + libstore_settings_headers, + libstore_generated_headers, dependencies : dependencies, cpp_args : cpp_args, cpp_pch : cpp_pch, @@ -247,6 +362,7 @@ install_headers(libstore_headers, subdir : 'lix/libstore', preserve_path : true) # Used by libfetchers. liblixstore = declare_dependency( include_directories : include_directories('.'), + sources : libstore_settings_headers, link_with : libstore, ) @@ -254,6 +370,7 @@ liblixstore = declare_dependency( if is_static liblixstore_mstatic = declare_dependency( include_directories : include_directories('.'), + sources : libstore_settings_headers, link_whole : libstore, ) else diff --git a/src/libstore/settings/allow-symlinked-store.md b/src/libstore/settings/allow-symlinked-store.md new file mode 100644 index 000000000..5907a8b80 --- /dev/null +++ b/src/libstore/settings/allow-symlinked-store.md @@ -0,0 +1,14 @@ +--- +name: allow-symlinked-store +internalName: allowSymlinkedStore +type: bool +default: false +--- +If set to `true`, Lix will stop complaining if the store directory +(typically /nix/store) contains symlink components. + +This risks making some builds "impure" because builders sometimes +"canonicalise" paths by resolving all symlink components. Problems +occur if those builds are then deployed to machines where /nix/store +resolves to a different location from that of the build machine. You +can enable this setting if you are sure you're not going to do that. diff --git a/src/libstore/settings/allowed-impure-host-deps.md b/src/libstore/settings/allowed-impure-host-deps.md new file mode 100644 index 000000000..1b1793b53 --- /dev/null +++ b/src/libstore/settings/allowed-impure-host-deps.md @@ -0,0 +1,7 @@ +--- +name: allowed-impure-host-deps +internalName: allowedImpureHostPrefixes +type: PathSet +default: [] +--- +Which prefixes to allow derivations to ask for access to (primarily for Darwin). diff --git a/src/libstore/settings/always-allow-substitutes.md b/src/libstore/settings/always-allow-substitutes.md new file mode 100644 index 000000000..9d27c3252 --- /dev/null +++ b/src/libstore/settings/always-allow-substitutes.md @@ -0,0 +1,9 @@ +--- +name: always-allow-substitutes +internalName: alwaysAllowSubstitutes +type: bool +default: false +--- +If set to `true`, Lix will ignore the `allowSubstitutes` attribute in +derivations and always attempt to use available substituters. +For more information on `allowSubstitutes`, see [the manual chapter on advanced attributes](../language/advanced-attributes.md). diff --git a/src/libstore/settings/auto-allocate-uids.md b/src/libstore/settings/auto-allocate-uids.md new file mode 100644 index 000000000..39520ca65 --- /dev/null +++ b/src/libstore/settings/auto-allocate-uids.md @@ -0,0 +1,11 @@ +--- +name: auto-allocate-uids +internalName: autoAllocateUids +type: bool +default: false +experimentalFeature: auto-allocate-uids +--- +Whether to select UIDs for builds automatically, instead of using the +users in `build-users-group`. + +UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS. diff --git a/src/libstore/settings/auto-optimise-store.md b/src/libstore/settings/auto-optimise-store.md new file mode 100644 index 000000000..825271705 --- /dev/null +++ b/src/libstore/settings/auto-optimise-store.md @@ -0,0 +1,11 @@ +--- +name: auto-optimise-store +internalName: autoOptimiseStore +type: bool +default: false +--- +If set to `true`, Lix automatically detects files in the store +that have identical contents, and replaces them with hard links to +a single copy. This saves disk space. If set to `false` (the +default), you can still run `nix-store --optimise` to get rid of +duplicate files. diff --git a/src/libstore/settings/build-dir.md b/src/libstore/settings/build-dir.md new file mode 100644 index 000000000..f518d52a5 --- /dev/null +++ b/src/libstore/settings/build-dir.md @@ -0,0 +1,14 @@ +--- +name: build-dir +internalName: buildDir +settingType: PathsSetting> +default: null +--- +The directory on the host, in which derivations' temporary build directories are created. + +If not set, Nix will use the [`temp-dir`](#conf-temp-dir) setting if set, otherwise the system temporary directory indicated by the `TMPDIR` environment variable. +Note that builds are often performed by the Nix daemon, so its `TMPDIR` is used, and not that of the Nix command line interface. + +This is also the location where [`--keep-failed`](@docroot@/command-ref/opt-common.md#opt-keep-failed) leaves its files. + +If Nix runs without sandbox, or if the platform does not support sandboxing with bind mounts (e.g. macOS), then the [`builder`](@docroot@/language/derivations.md#attr-builder)'s environment will contain this directory, instead of the virtual location [`sandbox-build-dir`](#conf-sandbox-build-dir). diff --git a/src/libstore/settings/build-hook.md b/src/libstore/settings/build-hook.md new file mode 100644 index 000000000..ac80ee0af --- /dev/null +++ b/src/libstore/settings/build-hook.md @@ -0,0 +1,14 @@ +--- +name: build-hook +internalName: buildHook +type: Strings +default: [] +deprecated: true +--- +The path to the helper program that executes remote builds. + +Lix communicates with the build hook over `stdio` using a custom protocol to request builds that cannot be performed directly by the Nix daemon. +The default value is the internal Lix binary that implements remote building. + +> **Warning** +> Change this setting only if you really know what you’re doing. diff --git a/src/libstore/settings/build-poll-interval.md b/src/libstore/settings/build-poll-interval.md new file mode 100644 index 000000000..c7641c3d0 --- /dev/null +++ b/src/libstore/settings/build-poll-interval.md @@ -0,0 +1,7 @@ +--- +name: build-poll-interval +internalName: pollInterval +type: unsigned int +default: 5 +--- +How often (in seconds) to poll for locks. diff --git a/src/libstore/settings/build-users-group.md b/src/libstore/settings/build-users-group.md new file mode 100644 index 000000000..4db021ad3 --- /dev/null +++ b/src/libstore/settings/build-users-group.md @@ -0,0 +1,43 @@ +--- +name: build-users-group +internalName: buildUsersGroup +type: std::string +defaultExpr: '""' # overridden in the code if running as root +defaultText: '*running as root:* `nixbld`, *otherwise:* *empty*' +--- +This options specifies the Unix group containing the Lix build user +accounts. In multi-user Lix installations, builds should not be +performed by the Lix account since that would allow users to +arbitrarily modify the Nix store and database by supplying specially +crafted builders; and they cannot be performed by the calling user +since that would allow them to influence the build result. + +Therefore, if this option is non-empty and specifies a valid group, +builds will be performed under the user accounts that are a member +of the group specified here (as listed in `/etc/group`). Those user +accounts should not be used for any other purpose\! + +Lix will never run two builds under the same user account at the +same time. This is to prevent an obvious security hole: a malicious +user writing a Nix expression that modifies the build result of a +legitimate Nix expression being built by another user. Therefore it +is good to have as many Lix build user accounts as you can spare. +(Remember: uids are cheap.) + +The build users should have permission to create files in the Nix +store, but not delete them. Therefore, `/nix/store` should be owned +by the Nix account, its group should be the group specified here, +and its mode should be `1775`. + +If the build users group is empty, builds will be performed under +the uid of the Lix process (that is, the uid of the caller if +both `NIX_REMOTE` is either empty or `auto` and the Nix store is +owned by that user, or, alternatively, the uid under which the Nix +daemon runs if `NIX_REMOTE` is `daemon` or if it is `auto` and the +store is not owned by the caller). Obviously, this should not be used +with a nix daemon accessible to untrusted clients. + +For the avoidance of doubt, explicitly setting this to *empty* with a +Lix daemon running as root means that builds will be executed as root +with respect to the rest of the system. +We intend to fix this: https://git.lix.systems/lix-project/lix/issues/242 diff --git a/src/libstore/settings/builders-use-substitutes.md b/src/libstore/settings/builders-use-substitutes.md new file mode 100644 index 000000000..d9dab4f32 --- /dev/null +++ b/src/libstore/settings/builders-use-substitutes.md @@ -0,0 +1,13 @@ +--- +name: builders-use-substitutes +internalName: buildersUseSubstitutes +type: bool +default: false +--- +If set to `true`, Lix will instruct remote build machines to use +their own binary substitutes if available. In practical terms, this +means that remote hosts will fetch as many build dependencies as +possible from their own substitutes (e.g, from `cache.nixos.org`), +instead of waiting for this host to upload them all. This can +drastically reduce build times if the network connection between +this computer and the remote build host is slow. diff --git a/src/libstore/settings/builders.md b/src/libstore/settings/builders.md new file mode 100644 index 000000000..14b4e3831 --- /dev/null +++ b/src/libstore/settings/builders.md @@ -0,0 +1,12 @@ +--- +name: builders +internalName: builders +type: std::string +defaultExpr: '"@" + nixConfDir + "/machines"' +defaultText: '`@/etc/nix/machines`' +--- +A semicolon-separated list of build machines. +For the exact format and examples, see [the manual chapter on remote builds](../advanced-topics/distributed-builds.md) + +Defaults to `@$NIX_CONF_DIR/machines`. +The default shown below is only accurate when the value of `NIX_CONF_DIR` has not been overridden at build time or using the environment variable. diff --git a/src/libstore/settings/compress-build-log.md b/src/libstore/settings/compress-build-log.md new file mode 100644 index 000000000..c1b5a13ba --- /dev/null +++ b/src/libstore/settings/compress-build-log.md @@ -0,0 +1,10 @@ +--- +name: compress-build-log +internalName: compressLog +type: bool +default: true +aliases: [build-compress-log] +--- +If set to `true` (the default), build logs written to +`/nix/var/log/nix/drvs` will be compressed on the fly using bzip2. +Otherwise, they will not be compressed. diff --git a/src/libstore/settings/cores.md b/src/libstore/settings/cores.md new file mode 100644 index 000000000..a65f20a0a --- /dev/null +++ b/src/libstore/settings/cores.md @@ -0,0 +1,16 @@ +--- +name: cores +internalName: buildCores +type: unsigned int +defaultExpr: 'getDefaultCores()' +defaultText: '*machine-specific*' +aliases: [build-cores] +--- +Sets the value of the `NIX_BUILD_CORES` environment variable in the +invocation of builders. Builders can use this variable at their +discretion to control the maximum amount of parallelism. For +instance, in Nixpkgs, if the derivation attribute +`enableParallelBuilding` is set to `true`, the builder passes the +`-jN` flag to GNU Make. It can be overridden using the `--cores` +command line switch and defaults to `1`. The value `0` means that +the builder should use all available CPU cores in the system. diff --git a/src/libstore/settings/darwin-log-sandbox-violations.md b/src/libstore/settings/darwin-log-sandbox-violations.md new file mode 100644 index 000000000..4e130ef38 --- /dev/null +++ b/src/libstore/settings/darwin-log-sandbox-violations.md @@ -0,0 +1,8 @@ +--- +name: darwin-log-sandbox-violations +internalName: darwinLogSandboxViolations +platforms: [darwin] +type: bool +default: false +--- +Whether to log Darwin sandbox access violations to the system log. diff --git a/src/libstore/settings/diff-hook.md b/src/libstore/settings/diff-hook.md new file mode 100644 index 000000000..2cd995021 --- /dev/null +++ b/src/libstore/settings/diff-hook.md @@ -0,0 +1,32 @@ +--- +name: diff-hook +internalName: diffHook +settingType: PathsSetting> +default: null +--- +Path to an executable capable of diffing build results. The hook is +executed if `run-diff-hook` is true, and the output of a build is +known to not be the same. This program is not executed to determine +if two results are the same. + +The diff hook is executed by the same user and group who ran the +build. However, the diff hook does not have write access to the +store path just built. + +The diff hook program receives three parameters: + +1. A path to the previous build's results + +2. A path to the current build's results + +3. The path to the build's derivation + +4. The path to the build's scratch directory. This directory will + exist only if the build was run with `--keep-failed`. + +The stderr and stdout output from the diff hook will not be +displayed to the user. Instead, it will print to the nix-daemon's +log. + +When using the Nix daemon, `diff-hook` must be set in the `nix.conf` +configuration file, and cannot be passed at the command line. diff --git a/src/libstore/settings/download-speed.md b/src/libstore/settings/download-speed.md new file mode 100644 index 000000000..3419b7b83 --- /dev/null +++ b/src/libstore/settings/download-speed.md @@ -0,0 +1,8 @@ +--- +name: download-speed +internalName: downloadSpeed +type: unsigned int +default: 0 +--- +Specify the maximum transfer rate in kilobytes per second you want +Lix to use for downloads. diff --git a/src/libstore/settings/enable-core-dumps.md b/src/libstore/settings/enable-core-dumps.md new file mode 100644 index 000000000..2a5bd7f11 --- /dev/null +++ b/src/libstore/settings/enable-core-dumps.md @@ -0,0 +1,10 @@ +--- +name: enable-core-dumps +internalName: enableCoreDumps +type: bool +default: false +--- +If set to `false` (the default), `RLIMIT_CORE` has a soft limit of zero. +If set to `true`, the soft limit is infinite. + +The hard limit is always infinite. diff --git a/src/libstore/settings/extra-platforms.md b/src/libstore/settings/extra-platforms.md new file mode 100644 index 000000000..ca3f082f5 --- /dev/null +++ b/src/libstore/settings/extra-platforms.md @@ -0,0 +1,21 @@ +--- +name: extra-platforms +internalName: extraPlatforms +type: StringSet +defaultExpr: 'getDefaultExtraPlatforms()' +defaultText: '*machine-specific*' +--- +System types of executables that can be run on this machine. + +Lix will only build a given [derivation](@docroot@/language/derivations.md) locally when its `system` attribute equals any of the values specified here or in the [`system` option](#conf-system). + +Setting this can be useful to build derivations locally on compatible machines: +- `i686-linux` executables can be run on `x86_64-linux` machines (set by default) +- `x86_64-darwin` executables can be run on macOS `aarch64-darwin` with Rosetta 2 (set by default where applicable) +- `armv6` and `armv5tel` executables can be run on `armv7` +- some `aarch64` machines can also natively run 32-bit ARM code +- `qemu-user` may be used to support non-native platforms (though this +may be slow and buggy) + +Build systems will usually detect the target platform to be the current physical system and therefore produce machine code incompatible with what may be intended in the derivation. +You should design your derivation's `builder` accordingly and cross-check the results when using this option against natively-built versions of your derivation. diff --git a/src/libstore/settings/fallback.md b/src/libstore/settings/fallback.md new file mode 100644 index 000000000..465ee6e0f --- /dev/null +++ b/src/libstore/settings/fallback.md @@ -0,0 +1,10 @@ +--- +name: fallback +internalName: tryFallback +type: bool +default: false +aliases: [build-fallback] +--- +If set to `true`, Lix will fall back to building from source if a +binary substitute fails. This is equivalent to the `--fallback` +flag. The default is `false`. diff --git a/src/libstore/settings/fsync-metadata.md b/src/libstore/settings/fsync-metadata.md new file mode 100644 index 000000000..ffe62862a --- /dev/null +++ b/src/libstore/settings/fsync-metadata.md @@ -0,0 +1,10 @@ +--- +name: fsync-metadata +internalName: fsyncMetadata +type: bool +default: true +--- +If set to `true`, changes to the Nix store metadata (in +`/nix/var/nix/db`) are synchronously flushed to disk. This improves +robustness in case of system crashes, but reduces performance. The +default is `true`. diff --git a/src/libstore/settings/gc-reserved-space.md b/src/libstore/settings/gc-reserved-space.md new file mode 100644 index 000000000..f6d62db74 --- /dev/null +++ b/src/libstore/settings/gc-reserved-space.md @@ -0,0 +1,7 @@ +--- +name: gc-reserved-space +internalName: reservedSize +type: off_t +default: 8388608 # 8 * 1024 * 1024 +--- +Amount of reserved disk space for the garbage collector. diff --git a/src/libstore/settings/hashed-mirrors.md b/src/libstore/settings/hashed-mirrors.md new file mode 100644 index 000000000..740412616 --- /dev/null +++ b/src/libstore/settings/hashed-mirrors.md @@ -0,0 +1,23 @@ +--- +name: hashed-mirrors +internalName: hashedMirrors +type: Strings +default: [] +--- +A list of web servers used by `builtins.fetchurl` to obtain files by +hash. Given a hash type *ht* and a base-16 hash *h*, Lix will try to +download the file from *hashed-mirror*/*ht*/*h*. This allows files to +be downloaded even if they have disappeared from their original URI. +For example, given an example mirror `http://tarballs.nixos.org/`, +when building the derivation + +```nix +builtins.fetchurl { + url = "https://example.org/foo-1.2.3.tar.xz"; + sha256 = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"; +} +``` + +Lix will attempt to download this file from +`http://tarballs.nixos.org/sha256/2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae` +first. If it is not available there, if will try the original URI. diff --git a/src/libstore/settings/id-count.md b/src/libstore/settings/id-count.md new file mode 100644 index 000000000..73a88793f --- /dev/null +++ b/src/libstore/settings/id-count.md @@ -0,0 +1,14 @@ +--- +name: id-count +internalName: uidCount +type: uint32_t +defaultExpr: | + #if __linux__ + maxIdsPerBuild * 128 + #else + 128 + #endif +defaultText: '*Linux:* `8388608`, *other platforms:* `128`' +experimentalFeature: auto-allocate-uids +--- +The number of UIDs/GIDs to use for dynamic ID allocation. diff --git a/src/libstore/settings/ignored-acls.md b/src/libstore/settings/ignored-acls.md new file mode 100644 index 000000000..825426d0d --- /dev/null +++ b/src/libstore/settings/ignored-acls.md @@ -0,0 +1,11 @@ +--- +name: ignored-acls +internalName: ignoredAcls +platforms: [linux] +type: StringSet +default: [security.csm, security.selinux, system.nfs4_acl] +--- +A list of ACLs that should be ignored, normally Lix attempts to +remove all ACLs from files and directories in the Nix store, but +some ACLs like `security.selinux` or `system.nfs4_acl` can't be +removed even by root. Therefore it's best to just ignore them. diff --git a/src/libstore/settings/impersonate-linux-26.md b/src/libstore/settings/impersonate-linux-26.md new file mode 100644 index 000000000..620423401 --- /dev/null +++ b/src/libstore/settings/impersonate-linux-26.md @@ -0,0 +1,9 @@ +--- +name: impersonate-linux-26 +internalName: impersonateLinux26 +platforms: [linux] +type: bool +default: false +aliases: [build-impersonate-linux-26] +--- +Whether to impersonate a Linux 2.6 machine on newer kernels. diff --git a/src/libstore/settings/keep-build-log.md b/src/libstore/settings/keep-build-log.md new file mode 100644 index 000000000..ae698c668 --- /dev/null +++ b/src/libstore/settings/keep-build-log.md @@ -0,0 +1,11 @@ +--- +name: keep-build-log +internalName: keepLog +type: bool +default: true +aliases: [build-keep-log] +--- +If set to `true` (the default), Lix will write the build log of a +derivation (i.e. the standard output and error of its builder) to +the directory `/nix/var/log/nix/drvs`. The build log can be +retrieved using the command `nix-store -l path`. diff --git a/src/libstore/settings/keep-derivations.md b/src/libstore/settings/keep-derivations.md new file mode 100644 index 000000000..069f31e69 --- /dev/null +++ b/src/libstore/settings/keep-derivations.md @@ -0,0 +1,17 @@ +--- +name: keep-derivations +internalName: gcKeepDerivations +type: bool +default: true +aliases: [gc-keep-derivations] +--- +If `true` (default), the garbage collector will keep the derivations +from which non-garbage store paths were built. If `false`, they will +be deleted unless explicitly registered as a root (or reachable from +other roots). + +Keeping derivation around is useful for querying and traceability +(e.g., it allows you to ask with what dependencies or options a +store path was built), so by default this option is on. Turn it off +to save a bit of disk space (or a lot if `keep-outputs` is also +turned on). diff --git a/src/libstore/settings/keep-env-derivations.md b/src/libstore/settings/keep-env-derivations.md new file mode 100644 index 000000000..0a1b96ffd --- /dev/null +++ b/src/libstore/settings/keep-env-derivations.md @@ -0,0 +1,22 @@ +--- +name: keep-env-derivations +internalName: envKeepDerivations +type: bool +default: false +aliases: [env-keep-derivations] +--- +If `false` (default), derivations are not stored in Nix user +environments. That is, the derivations of any build-time-only +dependencies may be garbage-collected. + +If `true`, when you add a Nix derivation to a user environment, the +path of the derivation is stored in the user environment. Thus, the +derivation will not be garbage-collected until the user environment +generation is deleted (`nix-env --delete-generations`). To prevent +build-time-only dependencies from being collected, you should also +turn on `keep-outputs`. + +The difference between this option and `keep-derivations` is that +this one is “sticky”: it applies to any user environment created +while this option was enabled, while `keep-derivations` only applies +at the moment the garbage collector is run. diff --git a/src/libstore/settings/keep-failed.md b/src/libstore/settings/keep-failed.md new file mode 100644 index 000000000..1c4a892e8 --- /dev/null +++ b/src/libstore/settings/keep-failed.md @@ -0,0 +1,7 @@ +--- +name: keep-failed +internalName: keepFailed +type: bool +default: false +--- +Whether to keep temporary directories of failed builds. diff --git a/src/libstore/settings/keep-going.md b/src/libstore/settings/keep-going.md new file mode 100644 index 000000000..03a9e1523 --- /dev/null +++ b/src/libstore/settings/keep-going.md @@ -0,0 +1,7 @@ +--- +name: keep-going +internalName: keepGoing +type: bool +default: false +--- +Whether to keep building derivations when another build fails. diff --git a/src/libstore/settings/keep-outputs.md b/src/libstore/settings/keep-outputs.md new file mode 100644 index 000000000..535549be4 --- /dev/null +++ b/src/libstore/settings/keep-outputs.md @@ -0,0 +1,17 @@ +--- +name: keep-outputs +internalName: gcKeepOutputs +type: bool +default: false +aliases: [gc-keep-outputs] +--- +If `true`, the garbage collector will keep the outputs of +non-garbage derivations. If `false` (default), outputs will be +deleted unless they are GC roots themselves (or reachable from other +roots). + +In general, outputs must be registered as roots separately. However, +even if the output of a derivation is registered as a root, the +collector will still delete store paths that are used only at build +time (e.g., the C compiler, or source tarballs downloaded from the +network). To prevent it from doing so, set this option to `true`. diff --git a/src/libstore/settings/log-lines.md b/src/libstore/settings/log-lines.md new file mode 100644 index 000000000..6a9e5ae9b --- /dev/null +++ b/src/libstore/settings/log-lines.md @@ -0,0 +1,7 @@ +--- +name: log-lines +internalName: logLines +type: size_t +default: 25 +--- +The number of lines of the tail of the log to show if a build fails. diff --git a/src/libstore/settings/max-build-log-size.md b/src/libstore/settings/max-build-log-size.md new file mode 100644 index 000000000..5e17774aa --- /dev/null +++ b/src/libstore/settings/max-build-log-size.md @@ -0,0 +1,10 @@ +--- +name: max-build-log-size +internalName: maxLogSize +type: unsigned long +default: 0 +aliases: [build-max-log-size] +--- +This option defines the maximum number of bytes that a builder can +write to its stdout/stderr. If the builder exceeds this limit, it’s +killed. A value of `0` (the default) means that there is no limit. diff --git a/src/libstore/settings/max-free.md b/src/libstore/settings/max-free.md new file mode 100644 index 000000000..425c5dc71 --- /dev/null +++ b/src/libstore/settings/max-free.md @@ -0,0 +1,13 @@ +--- +name: max-free +internalName: maxFree +type: uint64_t +# n.b. this is deliberately int64 max rather than uint64 max because +# this goes through the Nix language JSON parser and thus needs to be +# representable in Nix language integers. +defaultExpr: 'std::numeric_limits::max()' +defaultText: '*infinity*' +--- +When a garbage collection is triggered by the `min-free` option, it +stops as soon as `max-free` bytes are available. The default is +infinity (i.e. delete all garbage). diff --git a/src/libstore/settings/max-jobs.md b/src/libstore/settings/max-jobs.md new file mode 100644 index 000000000..be6013a5a --- /dev/null +++ b/src/libstore/settings/max-jobs.md @@ -0,0 +1,14 @@ +--- +name: max-jobs +internalName: maxBuildJobs +settingType: MaxBuildJobsSetting +default: 1 +aliases: [build-max-jobs] +--- +This option defines the maximum number of jobs that Lix will try to +build in parallel. The default is `1`. The special value `auto` +causes Lix to use the number of CPUs in your system. `0` is useful +when using remote builders to prevent any local builds (except for +`preferLocalBuild` derivation attribute which executes locally +regardless). It can be overridden using the `--max-jobs` (`-j`) +command line switch. diff --git a/src/libstore/settings/max-silent-time.md b/src/libstore/settings/max-silent-time.md new file mode 100644 index 000000000..e88db3917 --- /dev/null +++ b/src/libstore/settings/max-silent-time.md @@ -0,0 +1,16 @@ +--- +name: max-silent-time +internalName: maxSilentTime +type: time_t +default: 0 +aliases: [build-max-silent-time] +--- +This option defines the maximum number of seconds that a builder can +go without producing any data on standard output or standard error. +This is useful (for instance in an automated build system) to catch +builds that are stuck in an infinite loop, or to catch remote builds +that are hanging due to network problems. It can be overridden using +the `--max-silent-time` command line switch. + +The value `0` means that there is no timeout. This is also the +default. diff --git a/src/libstore/settings/max-substitution-jobs.md b/src/libstore/settings/max-substitution-jobs.md new file mode 100644 index 000000000..2237e7d1f --- /dev/null +++ b/src/libstore/settings/max-substitution-jobs.md @@ -0,0 +1,10 @@ +--- +name: max-substitution-jobs +internalName: maxSubstitutionJobs +type: unsigned int +default: 16 +aliases: [substitution-max-jobs] +--- +This option defines the maximum number of substitution jobs that Nix +will try to run in parallel. The default is `16`. The minimum value +one can choose is `1` and lower values will be interpreted as `1`. diff --git a/src/libstore/settings/min-free-check-interval.md b/src/libstore/settings/min-free-check-interval.md new file mode 100644 index 000000000..26444bed8 --- /dev/null +++ b/src/libstore/settings/min-free-check-interval.md @@ -0,0 +1,7 @@ +--- +name: min-free-check-interval +internalName: minFreeCheckInterval +type: uint64_t +default: 5 +--- +Number of seconds between checking free disk space. diff --git a/src/libstore/settings/min-free.md b/src/libstore/settings/min-free.md new file mode 100644 index 000000000..896344afe --- /dev/null +++ b/src/libstore/settings/min-free.md @@ -0,0 +1,10 @@ +--- +name: min-free +internalName: minFree +type: uint64_t +default: 0 +--- +When free disk space in `/nix/store` drops below `min-free` during a +build, Lix performs a garbage-collection until `max-free` bytes are +available or there is no more garbage. A value of `0` (the default) +disables this feature. diff --git a/src/libstore/settings/nar-buffer-size.md b/src/libstore/settings/nar-buffer-size.md new file mode 100644 index 000000000..be384fbff --- /dev/null +++ b/src/libstore/settings/nar-buffer-size.md @@ -0,0 +1,7 @@ +--- +name: nar-buffer-size +internalName: narBufferSize +type: size_t +default: 33554432 # 32 * 1024 * 1024 +--- +Maximum size of NARs before spilling them to disk. diff --git a/src/libstore/settings/narinfo-cache-negative-ttl.md b/src/libstore/settings/narinfo-cache-negative-ttl.md new file mode 100644 index 000000000..f4013ec0d --- /dev/null +++ b/src/libstore/settings/narinfo-cache-negative-ttl.md @@ -0,0 +1,10 @@ +--- +name: narinfo-cache-negative-ttl +internalName: ttlNegativeNarInfoCache +type: unsigned int +default: 3600 +--- +The TTL in seconds for negative lookups. If a store path is queried +from a substituter but was not found, there will be a negative +lookup cached in the local disk cache database for the specified +duration. diff --git a/src/libstore/settings/narinfo-cache-positive-ttl.md b/src/libstore/settings/narinfo-cache-positive-ttl.md new file mode 100644 index 000000000..11ecde747 --- /dev/null +++ b/src/libstore/settings/narinfo-cache-positive-ttl.md @@ -0,0 +1,14 @@ +--- +name: narinfo-cache-positive-ttl +internalName: ttlPositiveNarInfoCache +type: unsigned int +default: 2592000 # 30 * 24 * 3600 +--- +The TTL in seconds for positive lookups. If a store path is queried +from a substituter, the result of the query will be cached in the +local disk cache database including some of the NAR metadata. The +default TTL is a month, setting a shorter TTL for positive lookups +can be useful for binary caches that have frequent garbage +collection, in which case having a more frequent cache invalidation +would prevent trying to pull the path again and failing with a hash +mismatch if the build isn't reproducible. diff --git a/src/libstore/settings/netrc-file.md b/src/libstore/settings/netrc-file.md new file mode 100644 index 000000000..12093c466 --- /dev/null +++ b/src/libstore/settings/netrc-file.md @@ -0,0 +1,29 @@ +--- +name: netrc-file +internalName: netrcFile +type: std::string +defaultExpr: 'fmt("%s/%s", nixConfDir, "netrc")' +defaultText: '`/etc/nix/netrc`' +--- +If set to an absolute path to a `netrc` file, Lix will use the HTTP +authentication credentials in this file when trying to download from +a remote host through HTTP or HTTPS. + +The `netrc` file consists of a list of accounts in the following +format: + + machine my-machine + login my-username + password my-password + +For the exact syntax, see [the `curl` +documentation](https://ec.haxx.se/usingcurl-netrc.html). + +> **Note** +> +> This must be an absolute path, and `~` is not resolved. For +> example, `~/.netrc` won't resolve to your home directory's +> `.netrc`. + +Defaults to `$NIX_CONF_DIR/netrc`. +The default shown below is only accurate when the value of `NIX_CONF_DIR` has not been overridden at build time or using the environment variable. diff --git a/src/libstore/settings/plugin-files.md b/src/libstore/settings/plugin-files.md new file mode 100644 index 000000000..f873012f0 --- /dev/null +++ b/src/libstore/settings/plugin-files.md @@ -0,0 +1,27 @@ +--- +name: plugin-files +internalName: pluginFiles +settingType: PluginFilesSetting +default: [] +--- +A list of plugin files to be loaded by Nix. Each of these files will +be dlopened by Nix, allowing them to affect execution through static +initialization. In particular, these plugins may construct static +instances of RegisterPrimOp to add new primops or constants to the +expression language, RegisterStoreImplementation to add new store +implementations, RegisterCommand to add new subcommands to the `nix` +command, and RegisterSetting to add new nix config settings. See the +constructors for those types for more details. + +Warning! These APIs are inherently unstable and may change from +release to release. + +Since these files are loaded into the same address space as Nix +itself, they must be DSOs compatible with the instance of Nix +running at the time (i.e. compiled against the same headers, not +linked to any incompatible libraries). They should not be linked to +any Lix libs directly, as those will be available already at load +time. + +If an entry in the list is a directory, all files in the directory +are loaded as plugins (non-recursively). diff --git a/src/libstore/settings/post-build-hook.md b/src/libstore/settings/post-build-hook.md new file mode 100644 index 000000000..575062a40 --- /dev/null +++ b/src/libstore/settings/post-build-hook.md @@ -0,0 +1,46 @@ +--- +name: post-build-hook +internalName: postBuildHook +type: std::string +default: '' +--- +Optional. The path to a program to execute after each build. + +This option is only settable in the global `nix.conf`, or on the +command line by trusted users. + +When using the nix-daemon, the daemon executes the hook as `root`. +If the nix-daemon is not involved, the hook runs as the user +executing the nix-build. + + - The hook executes after an evaluation-time build. + + - The hook does not execute on substituted paths. + + - The hook's output always goes to the user's terminal. + + - If the hook fails, the build succeeds but no further builds + execute. + + - The hook executes synchronously, and blocks other builds from + progressing while it runs. + +The program executes with no arguments. The program's environment +contains the following environment variables: + + - `DRV_PATH` + The derivation for the built paths. + + Example: + `/nix/store/5nihn1a7pa8b25l9zafqaqibznlvvp3f-bash-4.4-p23.drv` + + - `OUT_PATHS` + Output paths of the built derivation, separated by a space + character. + + Example: + `/nix/store/zf5lbh336mnzf1nlswdn11g4n2m8zh3g-bash-4.4-p23-dev + /nix/store/rjxwxwv1fpn9wa2x5ssk5phzwlcv4mna-bash-4.4-p23-doc + /nix/store/6bqvbzjkcp9695dq0dpl5y43nvy37pq1-bash-4.4-p23-info + /nix/store/r7fng3kk3vlpdlh2idnrbn37vh4imlj2-bash-4.4-p23-man + /nix/store/xfghy8ixrhz3kyy6p724iv3cxji088dx-bash-4.4-p23`. diff --git a/src/libstore/settings/pre-build-hook.md b/src/libstore/settings/pre-build-hook.md new file mode 100644 index 000000000..c3f1a0482 --- /dev/null +++ b/src/libstore/settings/pre-build-hook.md @@ -0,0 +1,20 @@ +--- +name: pre-build-hook +internalName: preBuildHook +type: std::string +default: '' +--- +If set, the path to a program that can set extra derivation-specific +settings for this system. This is used for settings that can't be +captured by the derivation model itself and are too variable between +different versions of the same system to be hard-coded into nix. + +The hook is passed the derivation path and, if sandboxes are +enabled, the sandbox directory. It can then modify the sandbox and +send a series of commands to modify various settings to stdout. The +currently recognized commands are: + + - `extra-sandbox-paths`\ + Pass a list of files and directories to be included in the + sandbox for this build. One entry per line, terminated by an + empty line. Entries have the same format as `sandbox-paths`. diff --git a/src/libstore/settings/print-missing.md b/src/libstore/settings/print-missing.md new file mode 100644 index 000000000..41f7d63af --- /dev/null +++ b/src/libstore/settings/print-missing.md @@ -0,0 +1,7 @@ +--- +name: print-missing +internalName: printMissing +type: bool +default: true +--- +Whether to print what paths need to be built or downloaded. diff --git a/src/libstore/settings/require-drop-supplementary-groups.md b/src/libstore/settings/require-drop-supplementary-groups.md new file mode 100644 index 000000000..8f60dbb30 --- /dev/null +++ b/src/libstore/settings/require-drop-supplementary-groups.md @@ -0,0 +1,18 @@ +--- +name: require-drop-supplementary-groups +internalName: requireDropSupplementaryGroups +platforms: [linux] +type: bool +defaultExpr: 'getuid() == 0' +defaultText: '*running as root:* `true`, *otherwise:* `false`' +--- +Following the principle of least privilege, +Lix will attempt to drop supplementary groups when building with sandboxing. + +However this can fail under some circumstances. +For example, if the user lacks the `CAP_SETGID` capability. +Search `setgroups(2)` for `EPERM` to find more detailed information on this. + +If you encounter such a failure, setting this option to `false` will let you ignore it and continue. +But before doing so, you should consider the security implications carefully. +Not dropping supplementary groups means the build sandbox will be less restricted than intended. diff --git a/src/libstore/settings/require-sigs.md b/src/libstore/settings/require-sigs.md new file mode 100644 index 000000000..6519bd6e4 --- /dev/null +++ b/src/libstore/settings/require-sigs.md @@ -0,0 +1,17 @@ +--- +name: require-sigs +internalName: requireSigs +type: bool +default: true +--- +If set to `true` (the default), any non-content-addressed path added +or copied to the Nix store (e.g. when substituting from a binary +cache) must have a signature by a trusted key. A trusted key is one +listed in `trusted-public-keys`, or a public key counterpart to a +private key stored in a file listed in `secret-key-files`. + +Set to `false` to disable signature checking and trust all +non-content-addressed paths unconditionally. + +(Content-addressed paths are inherently trustworthy and thus +unaffected by this configuration option.) diff --git a/src/libstore/settings/run-diff-hook.md b/src/libstore/settings/run-diff-hook.md new file mode 100644 index 000000000..579a5bcab --- /dev/null +++ b/src/libstore/settings/run-diff-hook.md @@ -0,0 +1,11 @@ +--- +name: run-diff-hook +internalName: runDiffHook +type: bool +default: false +--- +If true, enable the execution of the `diff-hook` program. + +When using the Nix daemon, `run-diff-hook` must be set in the +`nix.conf` configuration file, and cannot be passed at the command +line. diff --git a/src/libstore/settings/sandbox-build-dir.md b/src/libstore/settings/sandbox-build-dir.md new file mode 100644 index 000000000..824e81683 --- /dev/null +++ b/src/libstore/settings/sandbox-build-dir.md @@ -0,0 +1,10 @@ +--- +name: sandbox-build-dir +internalName: sandboxBuildDir +platforms: [linux] +type: std::string +default: /build +--- +The build directory inside the sandbox. + +This directory is backed by [`build-dir`](#conf-build-dir) on the host. diff --git a/src/libstore/settings/sandbox-dev-shm-size.md b/src/libstore/settings/sandbox-dev-shm-size.md new file mode 100644 index 000000000..c93af60b3 --- /dev/null +++ b/src/libstore/settings/sandbox-dev-shm-size.md @@ -0,0 +1,11 @@ +--- +name: sandbox-dev-shm-size +internalName: sandboxShmSize +platforms: [linux] +type: std::string +default: 50% +--- +This option determines the maximum size of the `tmpfs` filesystem +mounted on `/dev/shm` in Linux sandboxes. For the format, see the +description of the `size` option of `tmpfs` in mount(8). The default +is `50%`. diff --git a/src/libstore/settings/sandbox-fallback.md b/src/libstore/settings/sandbox-fallback.md new file mode 100644 index 000000000..3a0275a41 --- /dev/null +++ b/src/libstore/settings/sandbox-fallback.md @@ -0,0 +1,7 @@ +--- +name: sandbox-fallback +internalName: sandboxFallback +type: bool +default: true +--- +Whether to disable sandboxing when the kernel doesn't allow it. diff --git a/src/libstore/settings/sandbox-paths.md b/src/libstore/settings/sandbox-paths.md new file mode 100644 index 000000000..051384956 --- /dev/null +++ b/src/libstore/settings/sandbox-paths.md @@ -0,0 +1,20 @@ +--- +name: sandbox-paths +internalName: sandboxPaths +type: PathSet +default: [] +aliases: [build-chroot-dirs, build-sandbox-paths] +--- +A list of paths bind-mounted into Nix sandbox environments. You can +use the syntax `target=source` to mount a path in a different +location in the sandbox; for instance, `/bin=/nix-bin` will mount +the path `/nix-bin` as `/bin` inside the sandbox. If *source* is +followed by `?`, then it is not an error if *source* does not exist; +for example, `/dev/nvidiactl?` specifies that `/dev/nvidiactl` will +only be mounted in the sandbox if it exists in the host filesystem. + +If the source is in the Nix store, then its closure will be added to +the sandbox as well. + +Depending on how Lix was built, the default value for this option +may be empty or provide `/bin/sh` as a bind-mount of `bash`. diff --git a/src/libstore/settings/sandbox.md b/src/libstore/settings/sandbox.md new file mode 100644 index 000000000..0c3ede418 --- /dev/null +++ b/src/libstore/settings/sandbox.md @@ -0,0 +1,36 @@ +--- +name: sandbox +internalName: sandboxMode +type: SandboxMode +defaultExpr: | + #if __linux__ + smEnabled + #else + smDisabled + #endif +defaultText: '*Linux:* `true`, *other platforms:* `false`' +aliases: [build-use-chroot, build-use-sandbox] +--- +If set to `true`, builds will be performed in a *sandboxed +environment*, i.e., they’re isolated from the normal file system +hierarchy and will only see their dependencies in the Nix store, +the temporary build directory, private versions of `/proc`, +`/dev`, `/dev/shm` and `/dev/pts` (on Linux), and the paths +configured with the `sandbox-paths` option. This is useful to +prevent undeclared dependencies on files in directories such as +`/usr/bin`. In addition, on Linux, builds run in private PID, +mount, network, IPC and UTS namespaces to isolate them from other +processes in the system (except that fixed-output derivations do +not run in private network namespace to ensure they can access the +network). + +Currently, sandboxing only work on Linux and macOS. The use of a +sandbox requires that Lix is run as root (so you should use the +“build users” feature to perform the actual builds under different +users than root). + +If this option is set to `relaxed`, then fixed-output derivations +and derivations that have the `__noChroot` attribute set to `true` +do not run in sandboxes. + +The default is `true` on Linux and `false` on all other platforms. diff --git a/src/libstore/settings/secret-key-files.md b/src/libstore/settings/secret-key-files.md new file mode 100644 index 000000000..c7ee9303c --- /dev/null +++ b/src/libstore/settings/secret-key-files.md @@ -0,0 +1,11 @@ +--- +name: secret-key-files +internalName: secretKeyFiles +type: Strings +default: [] +--- +A whitespace-separated list of files containing secret (private) +keys. These are used to sign locally-built paths. They can be +generated using `nix-store --generate-binary-cache-key`. The +corresponding public key can be distributed to other users, who +can add it to `trusted-public-keys` in their `nix.conf`. diff --git a/src/libstore/settings/ssl-cert-file.md b/src/libstore/settings/ssl-cert-file.md new file mode 100644 index 000000000..b9c581da3 --- /dev/null +++ b/src/libstore/settings/ssl-cert-file.md @@ -0,0 +1,19 @@ +--- +name: ssl-cert-file +internalName: caFile +type: Path +defaultExpr: 'getDefaultSSLCertFile()' +defaultText: '*machine-specific*' +--- +The path of a file containing CA certificates used to +authenticate `https://` downloads. Lix by default will use +the first of the following files that exists: + +1. `/etc/ssl/certs/ca-certificates.crt` +2. `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt` + +The path can be overridden by the following environment +variables, in order of precedence: + +1. `NIX_SSL_CERT_FILE` +2. `SSL_CERT_FILE` diff --git a/src/libstore/settings/start-id.md b/src/libstore/settings/start-id.md new file mode 100644 index 000000000..59dc532b0 --- /dev/null +++ b/src/libstore/settings/start-id.md @@ -0,0 +1,14 @@ +--- +name: start-id +internalName: startId +type: uint32_t +defaultExpr: | + #if __linux__ + 0x34000000 + #else + 56930 + #endif +defaultText: '*Linux:* `872415232`, *other platforms:* `56930`' +experimentalFeature: auto-allocate-uids +--- +The first UID and GID to use for dynamic ID allocation. diff --git a/src/libstore/settings/store.md b/src/libstore/settings/store.md new file mode 100644 index 000000000..71153606a --- /dev/null +++ b/src/libstore/settings/store.md @@ -0,0 +1,11 @@ +--- +name: store +internalName: storeUri +type: std::string +defaultExpr: 'getEnv("NIX_REMOTE").value_or("auto")' +defaultText: '`auto`' +--- +The [URL of the Nix store](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) +to use for most operations. +See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) +for supported store types and settings. diff --git a/src/libstore/settings/substitute.md b/src/libstore/settings/substitute.md new file mode 100644 index 000000000..a4dd2cc73 --- /dev/null +++ b/src/libstore/settings/substitute.md @@ -0,0 +1,10 @@ +--- +name: substitute +internalName: useSubstitutes +type: bool +default: true +aliases: [build-use-substitutes] +--- +If set to `true` (default), Lix will use binary substitutes if +available. This option can be disabled to force building from +source. diff --git a/src/libstore/settings/substituters.md b/src/libstore/settings/substituters.md new file mode 100644 index 000000000..73a12e472 --- /dev/null +++ b/src/libstore/settings/substituters.md @@ -0,0 +1,20 @@ +--- +name: substituters +internalName: substituters +type: Strings +default: [https://cache.nixos.org/] +aliases: [binary-caches] +--- +A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) to be used as substituters, separated by whitespace. +A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Lix can obtain [store objects](@docroot@/glossary.md#gloss-store-object) instead of building them. + +Substituters are tried based on their priority value, which each substituter can set independently. +Lower value means higher priority. +The default is `https://cache.nixos.org`, which has a priority of 40. + +At least one of the following conditions must be met for Lix to use a substituter: + +- The substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list +- The user calling Lix is in the [`trusted-users`](#conf-trusted-users) list + +In addition, each store path should be trusted as described in [`trusted-public-keys`](#conf-trusted-public-keys) diff --git a/src/libstore/settings/sync-before-registering.md b/src/libstore/settings/sync-before-registering.md new file mode 100644 index 000000000..0789bac5b --- /dev/null +++ b/src/libstore/settings/sync-before-registering.md @@ -0,0 +1,7 @@ +--- +name: sync-before-registering +internalName: syncBeforeRegistering +type: bool +default: false +--- +Whether to call `sync()` before registering a path as valid. diff --git a/src/libstore/settings/system-features.md b/src/libstore/settings/system-features.md new file mode 100644 index 000000000..501ff00ed --- /dev/null +++ b/src/libstore/settings/system-features.md @@ -0,0 +1,21 @@ +--- +name: system-features +internalName: systemFeatures +type: StringSet +defaultExpr: 'getDefaultSystemFeatures()' +defaultText: '*machine-specific*' +--- +A set of system “features” supported by this machine, e.g. `kvm`. +Derivations can express a dependency on such features through the +derivation attribute `requiredSystemFeatures`. For example, the +attribute + + requiredSystemFeatures = [ "kvm" ]; + +ensures that the derivation can only be built on a machine with the +`kvm` feature. + +This setting by default includes `kvm` if `/dev/kvm` is accessible, +`apple-virt` if hardware virtualization is available on macOS, +and the pseudo-features `nixos-test`, `benchmark` and `big-parallel` +that are used in Nixpkgs to route builds to specific machines. diff --git a/src/libstore/settings/system.md b/src/libstore/settings/system.md new file mode 100644 index 000000000..44d988606 --- /dev/null +++ b/src/libstore/settings/system.md @@ -0,0 +1,29 @@ +--- +name: system +internalName: thisSystem +type: std::string +defaultExpr: 'SYSTEM' +defaultText: '*machine-specific*' +--- +The system type of the current Lix installation. +Lix will only build a given [derivation](@docroot@/language/derivations.md) locally when its `system` attribute equals any of the values specified here or in [`extra-platforms`](#conf-extra-platforms). + +The default value is set when Lix itself is compiled for the system it will run on. +The following system types are widely used, as [Lix is actively supported on these platforms](@docroot@/contributing/hacking.md#platforms): + +- `x86_64-linux` +- `x86_64-darwin` +- `i686-linux` +- `aarch64-linux` +- `aarch64-darwin` +- `armv6l-linux` +- `armv7l-linux` + +In general, you do not have to modify this setting. +While you can force Lix to run a Darwin-specific `builder` executable on a Linux machine, the result would obviously be wrong. + +This value is available in the Nix language as +[`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) +if the +[`eval-system`](#conf-eval-system) +configuration option is set as the empty string. diff --git a/src/libstore/settings/tarball-ttl.md b/src/libstore/settings/tarball-ttl.md new file mode 100644 index 000000000..b59402acf --- /dev/null +++ b/src/libstore/settings/tarball-ttl.md @@ -0,0 +1,18 @@ +--- +name: tarball-ttl +internalName: tarballTtl +type: unsigned int +default: 3600 # 60 * 60 +--- +The number of seconds a downloaded tarball is considered fresh. If +the cached tarball is stale, Lix will check whether it is still up +to date using the ETag header. Lix will download a new version if +the ETag header is unsupported, or the cached ETag doesn't match. + +Setting the TTL to `0` forces Lix to always check if the tarball is +up to date. + +Lix caches tarballs in `$XDG_CACHE_HOME/nix/tarballs`. + +Files fetched via `NIX_PATH`, `fetchGit`, `fetchMercurial`, +`fetchTarball`, and `fetchurl` respect this TTL. diff --git a/src/libstore/settings/temp-dir.md b/src/libstore/settings/temp-dir.md new file mode 100644 index 000000000..a9da9bbf8 --- /dev/null +++ b/src/libstore/settings/temp-dir.md @@ -0,0 +1,14 @@ +--- +name: temp-dir +internalName: tempDir +settingType: PathsSetting> +default: null +--- +The directory on the host used as the default temporary directory. + +If not set, Nix will use the system temporary directory indicated by the `TMPDIR` environment variable. + +This will be used for anything that would otherwise fall back to `TMPDIR`, and the inherited `TMPDIR` value will be preserved for child processes to use. +If [`build-dir`](#conf-build-dir) is set, that takes precedence over this where it applies. + +If set, the value must be a path that exists and is accessible to all users. diff --git a/src/libstore/settings/timeout.md b/src/libstore/settings/timeout.md new file mode 100644 index 000000000..d17d5479f --- /dev/null +++ b/src/libstore/settings/timeout.md @@ -0,0 +1,15 @@ +--- +name: timeout +internalName: buildTimeout +type: time_t +default: 0 +aliases: [build-timeout] +--- +This option defines the maximum number of seconds that a builder can +run. This is useful (for instance in an automated build system) to +catch builds that are stuck in an infinite loop but keep writing to +their standard output or standard error. It can be overridden using +the `--timeout` command line switch. + +The value `0` means that there is no timeout. This is also the +default. diff --git a/src/libstore/settings/trusted-public-keys.md b/src/libstore/settings/trusted-public-keys.md new file mode 100644 index 000000000..2529ec80d --- /dev/null +++ b/src/libstore/settings/trusted-public-keys.md @@ -0,0 +1,16 @@ +--- +name: trusted-public-keys +internalName: trustedPublicKeys +type: Strings +default: [cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=] +aliases: [binary-cache-public-keys] +--- +A whitespace-separated list of public keys. + +At least one of the following condition must be met +for Lix to accept copying a store object from another +Nix store (such as a substituter): + +- the store object has been signed using a key in the trusted keys list +- the [`require-sigs`](#conf-require-sigs) option has been set to `false` +- the store object is [output-addressed](@docroot@/glossary.md#gloss-output-addressed-store-object) diff --git a/src/libstore/settings/trusted-substituters.md b/src/libstore/settings/trusted-substituters.md new file mode 100644 index 000000000..37ab2358b --- /dev/null +++ b/src/libstore/settings/trusted-substituters.md @@ -0,0 +1,11 @@ +--- +name: trusted-substituters +internalName: trustedSubstituters +type: StringSet +default: [] +aliases: [trusted-binary-caches] +--- +A list of [Nix store URLs](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format), separated by whitespace. +These are not used by default, but users of the Nix daemon can enable them by specifying [`substituters`](#conf-substituters). + +Unprivileged users (those set in only [`allowed-users`](#conf-allowed-users) but not [`trusted-users`](#conf-trusted-users)) can pass as `substituters` only those URLs listed in `trusted-substituters`. diff --git a/src/libstore/settings/use-cgroups.md b/src/libstore/settings/use-cgroups.md new file mode 100644 index 000000000..7dac2a0f1 --- /dev/null +++ b/src/libstore/settings/use-cgroups.md @@ -0,0 +1,12 @@ +--- +name: use-cgroups +internalName: useCgroups +platforms: [linux] +type: bool +default: false +experimentalFeature: cgroups +--- +Whether to execute builds inside cgroups. + +Cgroups are required and enabled automatically for derivations +that require the `uid-range` system feature. diff --git a/src/libstore/settings/use-sqlite-wal.md b/src/libstore/settings/use-sqlite-wal.md new file mode 100644 index 000000000..6b7f19d12 --- /dev/null +++ b/src/libstore/settings/use-sqlite-wal.md @@ -0,0 +1,8 @@ +--- +name: use-sqlite-wal +internalName: useSQLiteWAL +type: bool +defaultExpr: '!isWSL1()' +defaultText: '*WSL 1:* `false`, *other platforms:* `true`' +--- +Whether SQLite should use WAL mode. diff --git a/src/libstore/settings/use-xdg-base-directories.md b/src/libstore/settings/use-xdg-base-directories.md new file mode 100644 index 000000000..38814906c --- /dev/null +++ b/src/libstore/settings/use-xdg-base-directories.md @@ -0,0 +1,33 @@ +--- +name: use-xdg-base-directories +internalName: useXDGBaseDirectories +type: bool +default: false +--- +If set to `true`, Lix will conform to the [XDG Base Directory Specification] for files in `$HOME`. +The environment variables used to implement this are documented in the [Environment Variables section](@docroot@/command-ref/env-common.md). + +[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + +> **Warning** +> This changes the location of some well-known symlinks that Lix creates, which might break tools that rely on the old, non-XDG-conformant locations. + +In particular, the following locations change: + +| Old | New | +|-------------------|--------------------------------| +| `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` | +| `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` | +| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` | + +If you already have Lix installed and are using [profiles](@docroot@/package-management/profiles.md) or [channels](@docroot@/command-ref/nix-channel.md), you should migrate manually when you enable this option. +If `$XDG_STATE_HOME` is not set, use `$HOME/.local/state/nix` instead of `$XDG_STATE_HOME/nix`. +This can be achieved with the following shell commands: + +```sh +nix_state_home=${XDG_STATE_HOME-$HOME/.local/state}/nix +mkdir -p $nix_state_home +mv $HOME/.nix-profile $nix_state_home/profile +mv $HOME/.nix-defexpr $nix_state_home/defexpr +mv $HOME/.nix-channels $nix_state_home/channels +``` diff --git a/src/libutil/archive-settings/preallocate-contents.md b/src/libutil/archive-settings/preallocate-contents.md new file mode 100644 index 000000000..c71b6be39 --- /dev/null +++ b/src/libutil/archive-settings/preallocate-contents.md @@ -0,0 +1,7 @@ +--- +name: preallocate-contents +internalName: preallocateContents +type: bool +default: false +--- +Whether to preallocate files when writing objects with known size. diff --git a/src/libutil/archive-settings/use-case-hack.md b/src/libutil/archive-settings/use-case-hack.md new file mode 100644 index 000000000..07b9c4e28 --- /dev/null +++ b/src/libutil/archive-settings/use-case-hack.md @@ -0,0 +1,13 @@ +--- +name: use-case-hack +internalName: useCaseHack +type: bool +defaultExpr: | + #if __APPLE__ + true + #else + false + #endif +defaultText: '*Darwin:* `true`, *other platforms:* `false`' +--- +Whether to enable a Darwin-specific hack for dealing with file name collisions. diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index ab8193f21..ab7c1ce55 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -24,16 +24,7 @@ namespace nix { struct ArchiveSettings : Config { - Setting useCaseHack{this, - #if __APPLE__ - true, - #else - false, - #endif - "use-case-hack", - "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; - Setting preallocateContents{this, false, "preallocate-contents", - "Whether to preallocate files when writing objects with known size."}; + #include "archive-settings.gen.inc" }; static ArchiveSettings archiveSettings; diff --git a/src/libutil/config.hh b/src/libutil/config.hh index eadc71926..88aa66367 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -330,18 +330,6 @@ public: { options->addSetting(this); } - - Setting(AbstractSetting::deprecated_t, - Config * options, - const T & def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}, - const bool documentDefault = true, - std::optional experimentalFeature = std::nullopt) - : Setting(options, def, name, description, aliases, documentDefault, std::move(experimentalFeature), true) - { - } }; /** @@ -359,8 +347,9 @@ public: const std::string & description, const std::set & aliases = {}, const bool documentDefault = true, - std::optional experimentalFeature = std::nullopt) - : BaseSetting(def, documentDefault, name, description, aliases, std::move(experimentalFeature)) + std::optional experimentalFeature = std::nullopt, + bool deprecated = false) + : BaseSetting(def, documentDefault, name, description, aliases, std::move(experimentalFeature), deprecated) { options->addSetting(this); } @@ -396,24 +385,7 @@ extern GlobalConfig globalConfig; struct FeatureSettings : Config { - - Setting experimentalFeatures{ - this, {}, "experimental-features", - R"( - Experimental features that are enabled. - - Example: - - ``` - experimental-features = nix-command flakes - ``` - - The following experimental features are available: - - {{#include @generated@/../../../src/libutil/experimental-features-shortlist.md}} - - Experimental features are [further documented in the manual](@docroot@/contributing/experimental-features.md). - )"}; + #include "feature-settings.gen.inc" /** * Check whether the given experimental feature is enabled. @@ -437,23 +409,6 @@ struct FeatureSettings : Config { * disabled, and so the function does nothing in that case. */ void require(const std::optional &) const; - Setting deprecatedFeatures{ - this, {}, "deprecated-features", - R"( - Deprecated features that are allowed. - - Example: - - ``` - deprecated-features = url-literals - ``` - - The following deprecated feature features can be re-activated: - - {{#include @generated@/../../../src/libutil/deprecated-features-shortlist.md}} - - Deprecated features are [further documented in the manual](@docroot@/contributing/deprecated-features.md). - )"}; /** * Check whether the given deprecated feature is enabled. diff --git a/src/libutil/feature-settings/deprecated-features.md b/src/libutil/feature-settings/deprecated-features.md new file mode 100644 index 000000000..74fe5ae35 --- /dev/null +++ b/src/libutil/feature-settings/deprecated-features.md @@ -0,0 +1,19 @@ +--- +name: deprecated-features +internalName: deprecatedFeatures +type: DeprecatedFeatures +default: [] +--- +Deprecated features that are allowed. + +Example: + +``` +deprecated-features = url-literals +``` + +The following deprecated feature features can be re-activated: + +{{#include @generated@/../../../src/libutil/deprecated-features-shortlist.md}} + +Deprecated features are [further documented in the manual](@docroot@/contributing/deprecated-features.md). diff --git a/src/libutil/feature-settings/experimental-features.md b/src/libutil/feature-settings/experimental-features.md new file mode 100644 index 000000000..04871003b --- /dev/null +++ b/src/libutil/feature-settings/experimental-features.md @@ -0,0 +1,19 @@ +--- +name: experimental-features +internalName: experimentalFeatures +type: ExperimentalFeatures +default: [] +--- +Experimental features that are enabled. + +Example: + +``` +experimental-features = nix-command flakes +``` + +The following experimental features are available: + +{{#include @generated@/../../../src/libutil/experimental-features-shortlist.md}} + +Experimental features are [further documented in the manual](@docroot@/contributing/experimental-features.md). diff --git a/src/libutil/logging-settings/show-trace.md b/src/libutil/logging-settings/show-trace.md new file mode 100644 index 000000000..ca8fb20bf --- /dev/null +++ b/src/libutil/logging-settings/show-trace.md @@ -0,0 +1,8 @@ +--- +name: show-trace +internalName: showTrace +type: bool +default: false +--- +Whether Lix should print out a stack trace in case of Nix +expression evaluation errors. diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index b2d73d8ba..8cefcb5e5 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -86,12 +86,7 @@ typedef uint64_t ActivityId; struct LoggerSettings : Config { - Setting showTrace{ - this, false, "show-trace", - R"( - Whether Lix should print out a stack trace in case of Nix - expression evaluation errors. - )"}; + #include "logging-settings.gen.inc" }; extern LoggerSettings loggerSettings; diff --git a/src/libutil/meson.build b/src/libutil/meson.build index ca75df2d5..5aec64416 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -216,6 +216,62 @@ deprecated_features_impl_header = deprecated_features_gen[1] deprecated_features_shortlist_md = deprecated_features_gen[2] deprecated_feature_descriptions_md = deprecated_features_gen[3] +libutil_settings_headers = [] + +archive_setting_definitions = files( + 'archive-settings/preallocate-contents.md', + 'archive-settings/use-case-hack.md', +) +libutil_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : archive_setting_definitions, + output : 'archive-settings.gen.inc', +) + +feature_setting_definitions = files( + 'feature-settings/deprecated-features.md', + 'feature-settings/experimental-features.md', +) +libutil_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : feature_setting_definitions, + output : 'feature-settings.gen.inc', + install : true, + install_dir : includedir / 'lix/libutil', +) + +logging_setting_definitions = files( + 'logging-settings/show-trace.md', +) +libutil_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : logging_setting_definitions, + output : 'logging-settings.gen.inc', + install : true, + install_dir : includedir / 'lix/libutil', +) + libutil = library( 'lixutil', libutil_sources, @@ -223,6 +279,7 @@ libutil = library( experimental_features_impl_header, deprecated_features_header, deprecated_features_impl_header, + libutil_settings_headers, dependencies : [ aws_sdk, aws_s3, @@ -262,6 +319,7 @@ liblixutil = declare_dependency( sources : [ experimental_features_header, deprecated_features_header, + libutil_settings_headers, ], link_with : libutil ) @@ -273,6 +331,7 @@ if is_static sources : [ experimental_features_header, deprecated_features_header, + libutil_settings_headers, ], link_whole : libutil, ) diff --git a/src/nix/daemon-settings/allowed-users.md b/src/nix/daemon-settings/allowed-users.md new file mode 100644 index 000000000..aada459c5 --- /dev/null +++ b/src/nix/daemon-settings/allowed-users.md @@ -0,0 +1,16 @@ +--- +name: allowed-users +internalName: allowedUsers +type: Strings +default: ['*'] +--- +A list user names, separated by whitespace. +These users are allowed to connect to the Nix daemon. + +You can specify groups by prefixing names with `@`. +For instance, `@wheel` means all users in the `wheel` group. +Also, you can allow all users by specifying `*`. + +> **Note** +> +> Trusted users (set in [`trusted-users`](#conf-trusted-users)) can always connect to the Nix daemon. diff --git a/src/nix/daemon-settings/trusted-users.md b/src/nix/daemon-settings/trusted-users.md new file mode 100644 index 000000000..11e684215 --- /dev/null +++ b/src/nix/daemon-settings/trusted-users.md @@ -0,0 +1,16 @@ +--- +name: trusted-users +internalName: trustedUsers +type: Strings +default: [root] +--- +A list of user names, separated by whitespace. +These users will have additional rights when connecting to the Nix daemon, such as the ability to specify additional [substituters](#conf-substituters), or to import unsigned [NARs](@docroot@/glossary.md#gloss-nar). + +You can also specify groups by prefixing names with `@`. +For instance, `@wheel` means all users in the `wheel` group. + +> **Warning** +> +> Adding a user to `trusted-users` is essentially equivalent to giving that user root access to the system. +> For example, the user can access or replace store path contents that are critical for system security. diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 4b84d19eb..5a0f781ec 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -55,39 +55,7 @@ using namespace nix::daemon; * `daemon.cc`. */ struct AuthorizationSettings : Config { - - Setting trustedUsers{ - this, {"root"}, "trusted-users", - R"( - A list of user names, separated by whitespace. - These users will have additional rights when connecting to the Nix daemon, such as the ability to specify additional [substituters](#conf-substituters), or to import unsigned [NARs](@docroot@/glossary.md#gloss-nar). - - You can also specify groups by prefixing names with `@`. - For instance, `@wheel` means all users in the `wheel` group. - - > **Warning** - > - > Adding a user to `trusted-users` is essentially equivalent to giving that user root access to the system. - > For example, the user can access or replace store path contents that are critical for system security. - )"}; - - /** - * Who we trust to use the daemon in safe ways - */ - Setting allowedUsers{ - this, {"*"}, "allowed-users", - R"( - A list user names, separated by whitespace. - These users are allowed to connect to the Nix daemon. - - You can specify groups by prefixing names with `@`. - For instance, `@wheel` means all users in the `wheel` group. - Also, you can allow all users by specifying `*`. - - > **Note** - > - > Trusted users (set in [`trusted-users`](#conf-trusted-users)) can always connect to the Nix daemon. - )"}; + #include "daemon-settings.gen.inc" }; AuthorizationSettings authorizationSettings; diff --git a/src/nix/develop-settings/bash-prompt-prefix.md b/src/nix/develop-settings/bash-prompt-prefix.md new file mode 100644 index 000000000..c0808909f --- /dev/null +++ b/src/nix/develop-settings/bash-prompt-prefix.md @@ -0,0 +1,8 @@ +--- +name: bash-prompt-prefix +internalName: bashPromptPrefix +type: std::string +default: '' +experimentalFeature: nix-command +--- +Prefix prepended to the `PS1` environment variable in `nix develop` shells. diff --git a/src/nix/develop-settings/bash-prompt-suffix.md b/src/nix/develop-settings/bash-prompt-suffix.md new file mode 100644 index 000000000..1c2740461 --- /dev/null +++ b/src/nix/develop-settings/bash-prompt-suffix.md @@ -0,0 +1,8 @@ +--- +name: bash-prompt-suffix +internalName: bashPromptSuffix +type: std::string +default: '' +experimentalFeature: nix-command +--- +Suffix appended to the `PS1` environment variable in `nix develop` shells. diff --git a/src/nix/develop-settings/bash-prompt.md b/src/nix/develop-settings/bash-prompt.md new file mode 100644 index 000000000..0a58054fd --- /dev/null +++ b/src/nix/develop-settings/bash-prompt.md @@ -0,0 +1,8 @@ +--- +name: bash-prompt +internalName: bashPrompt +type: std::string +default: '' +experimentalFeature: nix-command +--- +The bash prompt (`PS1`) in `nix develop` shells. diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 87c31ef35..e461687ea 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -19,17 +19,7 @@ using namespace nix; struct DevelopSettings : Config { - Setting bashPrompt{this, "", "bash-prompt", - "The bash prompt (`PS1`) in `nix develop` shells.", - {}, true, Xp::NixCommand}; - - Setting bashPromptPrefix{this, "", "bash-prompt-prefix", - "Prefix prepended to the `PS1` environment variable in `nix develop` shells.", - {}, true, Xp::NixCommand}; - - Setting bashPromptSuffix{this, "", "bash-prompt-suffix", - "Suffix appended to the `PS1` environment variable in `nix develop` shells.", - {}, true, Xp::NixCommand}; + #include "develop-settings.gen.inc" }; static DevelopSettings developSettings; diff --git a/src/nix/meson.build b/src/nix/meson.build index cabdf0d2c..2ffe97aab 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -23,6 +23,43 @@ nix_generated_headers += gen_header.process( preserve_path_from : meson.current_build_dir(), ) +nix_settings_headers = [] + +daemon_setting_definitions = files( + 'daemon-settings/allowed-users.md', + 'daemon-settings/trusted-users.md', +) +nix_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : daemon_setting_definitions, + output : 'daemon-settings.gen.inc', +) + +develop_settings_definitions = files( + 'develop-settings/bash-prompt-prefix.md', + 'develop-settings/bash-prompt-suffix.md', + 'develop-settings/bash-prompt.md', +) +nix_settings_headers += custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_settings.py', + '--kernel', host_machine.system(), + '--header', '@OUTPUT@', + '--experimental-features', '@SOURCE_ROOT@/src/libutil/experimental-features', + '@INPUT@', + ], + input : develop_settings_definitions, + output : 'develop-settings.gen.inc', +) + nix_sources = files( 'add-to-store.cc', 'app.cc', @@ -81,6 +118,7 @@ nix = executable( 'nix', nix_sources, legacy_sources, + nix_settings_headers, nix_generated_headers, nix_headers, legacy_headers, From c1746300a9a9c239fe3e2c3c4d9201d6221a513f Mon Sep 17 00:00:00 2001 From: Alois Wohlschlager Date: Sat, 28 Sep 2024 15:21:59 +0200 Subject: [PATCH 3/3] libexpr: generate builtins from data Most builtins are now generated from data too, with two exceptions: * Undocumented builtins, since supporting them would add complexity to the generator, the harms of the current implementation mostly don't apply, and the proper fix is to document them. * `derivation` is somewhat magic (it is a function, but defined in the code as a constant), so the current treatment of having it separately documented is kept (for now, at least). Since it is slightly easier to do and probably a good idea anyway, the builtin function registrations generated this way are now processed directly in code and don't go through global variables any more. Unfortunately, a slight breaking change is introduced because the order of the builtins' names in the symbol table changes. Hopefully, this will turn out to not matter in practice. Change-Id: I7b4379a93ae380b6524e41a916a21c5c6f70555e --- doc/manual/meson.build | 11 - doc/manual/src/contributing/hacking.md | 33 +- doc/manual/src/language/builtin-constants.md | 2 +- doc/manual/src/language/builtins.md | 2 +- doc/manual/src/language/meson.build | 27 - .../build_builtin_constants.py | 93 + src/code-generation/build_builtins.py | 82 + src/libexpr/builtin-constants/builtins.md | 14 + .../builtin-constants/currentSystem.md | 27 + src/libexpr/builtin-constants/currentTime.md | 23 + src/libexpr/builtin-constants/false.md | 19 + src/libexpr/builtin-constants/langVersion.md | 15 + src/libexpr/builtin-constants/nixPath.md | 21 + src/libexpr/builtin-constants/nixVersion.md | 14 + src/libexpr/builtin-constants/null.md | 14 + src/libexpr/builtin-constants/storeDir.md | 13 + src/libexpr/builtin-constants/true.md | 19 + src/libexpr/builtins/abort.md | 6 + src/libexpr/builtins/add.md | 5 + .../builtins/addDrvOutputDependencies.md | 12 + src/libexpr/builtins/all.md | 6 + src/libexpr/builtins/any.md | 6 + src/libexpr/builtins/attrNames.md | 7 + src/libexpr/builtins/attrValues.md | 6 + src/libexpr/builtins/baseNameOf.md | 8 + src/libexpr/builtins/bitAnd.md | 5 + src/libexpr/builtins/bitOr.md | 5 + src/libexpr/builtins/bitXor.md | 5 + src/libexpr/builtins/break.md | 7 + src/libexpr/builtins/catAttrs.md | 13 + src/libexpr/builtins/ceil.md | 9 + src/libexpr/builtins/compareVersions.md | 9 + src/libexpr/builtins/concatLists.md | 5 + src/libexpr/builtins/concatMap.md | 6 + src/libexpr/builtins/concatStringsSep.md | 7 + src/libexpr/builtins/deepSeq.md | 7 + src/libexpr/builtins/dirOf.md | 8 + src/libexpr/builtins/div.md | 5 + src/libexpr/builtins/elem.md | 6 + src/libexpr/builtins/elemAt.md | 6 + src/libexpr/builtins/fetchClosure.md | 72 + src/libexpr/builtins/fetchGit.md | 138 ++ src/libexpr/builtins/fetchTarball.md | 42 + src/libexpr/builtins/fetchurl.md | 7 + src/libexpr/builtins/filter.md | 6 + src/libexpr/builtins/filterSource.md | 51 + src/libexpr/builtins/findFile.md | 45 + src/libexpr/builtins/flakeRefToString.md | 21 + src/libexpr/builtins/floor.md | 9 + src/libexpr/builtins/foldlStrict.md | 10 + src/libexpr/builtins/fromJSON.md | 11 + src/libexpr/builtins/fromTOML.md | 17 + src/libexpr/builtins/functionArgs.md | 13 + src/libexpr/builtins/genList.md | 12 + src/libexpr/builtins/genericClosure.md | 28 + src/libexpr/builtins/getAttr.md | 8 + src/libexpr/builtins/getContext.md | 21 + src/libexpr/builtins/getEnv.md | 14 + src/libexpr/builtins/getFlake.md | 19 + src/libexpr/builtins/groupBy.md | 20 + src/libexpr/builtins/hasAttr.md | 7 + src/libexpr/builtins/hasContext.md | 20 + src/libexpr/builtins/hashFile.md | 7 + src/libexpr/builtins/hashString.md | 7 + src/libexpr/builtins/head.md | 7 + src/libexpr/builtins/import.md | 63 + src/libexpr/builtins/intersectAttrs.md | 8 + src/libexpr/builtins/isAttrs.md | 5 + src/libexpr/builtins/isBool.md | 5 + src/libexpr/builtins/isFloat.md | 5 + src/libexpr/builtins/isFunction.md | 5 + src/libexpr/builtins/isInt.md | 5 + src/libexpr/builtins/isList.md | 5 + src/libexpr/builtins/isNull.md | 8 + src/libexpr/builtins/isPath.md | 5 + src/libexpr/builtins/isString.md | 5 + src/libexpr/builtins/length.md | 5 + src/libexpr/builtins/lessThan.md | 7 + src/libexpr/builtins/listToAttrs.md | 27 + src/libexpr/builtins/map.md | 13 + src/libexpr/builtins/mapAttrs.md | 11 + src/libexpr/builtins/match.md | 32 + src/libexpr/builtins/mul.md | 5 + src/libexpr/builtins/outputOf.md | 23 + src/libexpr/builtins/parseDrvName.md | 10 + src/libexpr/builtins/parseFlakeRef.md | 19 + src/libexpr/builtins/partition.md | 19 + src/libexpr/builtins/path.md | 31 + src/libexpr/builtins/pathExists.md | 6 + src/libexpr/builtins/placeholder.md | 8 + src/libexpr/builtins/readDir.md | 15 + src/libexpr/builtins/readFile.md | 5 + src/libexpr/builtins/readFileType.md | 6 + src/libexpr/builtins/removeAttrs.md | 13 + src/libexpr/builtins/replaceStrings.md | 16 + src/libexpr/builtins/seq.md | 6 + src/libexpr/builtins/sort.md | 17 + src/libexpr/builtins/split.md | 33 + src/libexpr/builtins/splitVersion.md | 7 + src/libexpr/builtins/storePath.md | 17 + src/libexpr/builtins/stringLength.md | 6 + src/libexpr/builtins/sub.md | 5 + src/libexpr/builtins/substring.md | 16 + src/libexpr/builtins/tail.md | 12 + src/libexpr/builtins/throw.md | 10 + src/libexpr/builtins/toFile.md | 73 + src/libexpr/builtins/toJSON.md | 10 + src/libexpr/builtins/toPath.md | 6 + src/libexpr/builtins/toString.md | 21 + src/libexpr/builtins/toXML.md | 93 + src/libexpr/builtins/trace.md | 13 + src/libexpr/builtins/traceVerbose.md | 8 + src/libexpr/builtins/tryEval.md | 17 + src/libexpr/builtins/typeOf.md | 7 + .../builtins/unsafeDiscardOutputDependency.md | 15 + src/libexpr/builtins/zipAttrsWith.md | 28 + src/libexpr/eval.cc | 2 +- src/libexpr/eval.hh | 2 +- src/libexpr/extra-primops.hh | 29 + src/libexpr/flake/flake.cc | 76 +- src/libexpr/meson.build | 150 ++ src/libexpr/primops.cc | 1802 +---------------- src/libexpr/primops/context.cc | 93 +- src/libexpr/primops/fetchClosure.cc | 79 +- src/libexpr/primops/fetchTree.cc | 204 +- src/libexpr/primops/fromTOML.cc | 23 +- 126 files changed, 2216 insertions(+), 2254 deletions(-) delete mode 100644 doc/manual/src/language/meson.build create mode 100644 src/code-generation/build_builtin_constants.py create mode 100644 src/code-generation/build_builtins.py create mode 100644 src/libexpr/builtin-constants/builtins.md create mode 100644 src/libexpr/builtin-constants/currentSystem.md create mode 100644 src/libexpr/builtin-constants/currentTime.md create mode 100644 src/libexpr/builtin-constants/false.md create mode 100644 src/libexpr/builtin-constants/langVersion.md create mode 100644 src/libexpr/builtin-constants/nixPath.md create mode 100644 src/libexpr/builtin-constants/nixVersion.md create mode 100644 src/libexpr/builtin-constants/null.md create mode 100644 src/libexpr/builtin-constants/storeDir.md create mode 100644 src/libexpr/builtin-constants/true.md create mode 100644 src/libexpr/builtins/abort.md create mode 100644 src/libexpr/builtins/add.md create mode 100644 src/libexpr/builtins/addDrvOutputDependencies.md create mode 100644 src/libexpr/builtins/all.md create mode 100644 src/libexpr/builtins/any.md create mode 100644 src/libexpr/builtins/attrNames.md create mode 100644 src/libexpr/builtins/attrValues.md create mode 100644 src/libexpr/builtins/baseNameOf.md create mode 100644 src/libexpr/builtins/bitAnd.md create mode 100644 src/libexpr/builtins/bitOr.md create mode 100644 src/libexpr/builtins/bitXor.md create mode 100644 src/libexpr/builtins/break.md create mode 100644 src/libexpr/builtins/catAttrs.md create mode 100644 src/libexpr/builtins/ceil.md create mode 100644 src/libexpr/builtins/compareVersions.md create mode 100644 src/libexpr/builtins/concatLists.md create mode 100644 src/libexpr/builtins/concatMap.md create mode 100644 src/libexpr/builtins/concatStringsSep.md create mode 100644 src/libexpr/builtins/deepSeq.md create mode 100644 src/libexpr/builtins/dirOf.md create mode 100644 src/libexpr/builtins/div.md create mode 100644 src/libexpr/builtins/elem.md create mode 100644 src/libexpr/builtins/elemAt.md create mode 100644 src/libexpr/builtins/fetchClosure.md create mode 100644 src/libexpr/builtins/fetchGit.md create mode 100644 src/libexpr/builtins/fetchTarball.md create mode 100644 src/libexpr/builtins/fetchurl.md create mode 100644 src/libexpr/builtins/filter.md create mode 100644 src/libexpr/builtins/filterSource.md create mode 100644 src/libexpr/builtins/findFile.md create mode 100644 src/libexpr/builtins/flakeRefToString.md create mode 100644 src/libexpr/builtins/floor.md create mode 100644 src/libexpr/builtins/foldlStrict.md create mode 100644 src/libexpr/builtins/fromJSON.md create mode 100644 src/libexpr/builtins/fromTOML.md create mode 100644 src/libexpr/builtins/functionArgs.md create mode 100644 src/libexpr/builtins/genList.md create mode 100644 src/libexpr/builtins/genericClosure.md create mode 100644 src/libexpr/builtins/getAttr.md create mode 100644 src/libexpr/builtins/getContext.md create mode 100644 src/libexpr/builtins/getEnv.md create mode 100644 src/libexpr/builtins/getFlake.md create mode 100644 src/libexpr/builtins/groupBy.md create mode 100644 src/libexpr/builtins/hasAttr.md create mode 100644 src/libexpr/builtins/hasContext.md create mode 100644 src/libexpr/builtins/hashFile.md create mode 100644 src/libexpr/builtins/hashString.md create mode 100644 src/libexpr/builtins/head.md create mode 100644 src/libexpr/builtins/import.md create mode 100644 src/libexpr/builtins/intersectAttrs.md create mode 100644 src/libexpr/builtins/isAttrs.md create mode 100644 src/libexpr/builtins/isBool.md create mode 100644 src/libexpr/builtins/isFloat.md create mode 100644 src/libexpr/builtins/isFunction.md create mode 100644 src/libexpr/builtins/isInt.md create mode 100644 src/libexpr/builtins/isList.md create mode 100644 src/libexpr/builtins/isNull.md create mode 100644 src/libexpr/builtins/isPath.md create mode 100644 src/libexpr/builtins/isString.md create mode 100644 src/libexpr/builtins/length.md create mode 100644 src/libexpr/builtins/lessThan.md create mode 100644 src/libexpr/builtins/listToAttrs.md create mode 100644 src/libexpr/builtins/map.md create mode 100644 src/libexpr/builtins/mapAttrs.md create mode 100644 src/libexpr/builtins/match.md create mode 100644 src/libexpr/builtins/mul.md create mode 100644 src/libexpr/builtins/outputOf.md create mode 100644 src/libexpr/builtins/parseDrvName.md create mode 100644 src/libexpr/builtins/parseFlakeRef.md create mode 100644 src/libexpr/builtins/partition.md create mode 100644 src/libexpr/builtins/path.md create mode 100644 src/libexpr/builtins/pathExists.md create mode 100644 src/libexpr/builtins/placeholder.md create mode 100644 src/libexpr/builtins/readDir.md create mode 100644 src/libexpr/builtins/readFile.md create mode 100644 src/libexpr/builtins/readFileType.md create mode 100644 src/libexpr/builtins/removeAttrs.md create mode 100644 src/libexpr/builtins/replaceStrings.md create mode 100644 src/libexpr/builtins/seq.md create mode 100644 src/libexpr/builtins/sort.md create mode 100644 src/libexpr/builtins/split.md create mode 100644 src/libexpr/builtins/splitVersion.md create mode 100644 src/libexpr/builtins/storePath.md create mode 100644 src/libexpr/builtins/stringLength.md create mode 100644 src/libexpr/builtins/sub.md create mode 100644 src/libexpr/builtins/substring.md create mode 100644 src/libexpr/builtins/tail.md create mode 100644 src/libexpr/builtins/throw.md create mode 100644 src/libexpr/builtins/toFile.md create mode 100644 src/libexpr/builtins/toJSON.md create mode 100644 src/libexpr/builtins/toPath.md create mode 100644 src/libexpr/builtins/toString.md create mode 100644 src/libexpr/builtins/toXML.md create mode 100644 src/libexpr/builtins/trace.md create mode 100644 src/libexpr/builtins/traceVerbose.md create mode 100644 src/libexpr/builtins/tryEval.md create mode 100644 src/libexpr/builtins/typeOf.md create mode 100644 src/libexpr/builtins/unsafeDiscardOutputDependency.md create mode 100644 src/libexpr/builtins/zipAttrsWith.md create mode 100644 src/libexpr/extra-primops.hh diff --git a/doc/manual/meson.build b/doc/manual/meson.build index c34a0e3ca..e4ff0684b 100644 --- a/doc/manual/meson.build +++ b/doc/manual/meson.build @@ -15,15 +15,6 @@ nix_eval_for_docs_common = nix_for_docs + [ ] nix_eval_for_docs = nix_eval_for_docs_common + '--raw' -language_json = custom_target( - command: [nix, '__dump-language'], - output : 'language.json', - capture : true, - env : nix_env_for_docs, - # FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though. - build_always_stale : true, -) - nix3_cli_json = custom_target( command : [ nix, '__dump-cli' ], capture : true, @@ -37,8 +28,6 @@ generate_manual_deps = files( 'generate-deps.py', ) -# Generates builtins.md and builtin-constants.md. -subdir('src/language') # Generates new-cli pages and conf-file.md. subdir('src/command-ref') # Generates rl-next-generated.md. diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 4798e4b7f..60f113cbb 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -400,9 +400,9 @@ The following properties are supported: Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. Set `buildUnreleasedNotes = true;` in `flake.nix` to build the release notes on the fly. -## Adding experimental or deprecated features, or global settings +## Adding experimental or deprecated features, global settings, or builtins -Experimental and deprecated features, and global settings are generally referenced both in the code and in the documentation. +Experimental and deprecated features, global settings, and builtins are generally referenced both in the code and in the documentation. To prevent duplication or divergence, they are defined in data files, and a script generates the necessary glue. The data file format is similar to the release notes: it consists of a YAML metadata header, followed by the documentation in Markdown format. @@ -443,3 +443,32 @@ Global settings support the following metadata properties: Settings are not collected in a single place in the source tree, so an appropriate place needs to be found for the setting to live. Look for related setting definition files under second-level subdirectories of `src` whose name includes `settings`. Then add the new file there, and don't forget to register it in the appropriate `meson.build` file. + +### Builtin functions + +The following metadata properties are supported for builtin functions: +* `name` (required): the language-facing name (as a member of the `builtins` attribute set) of the function. +* `implementation` (optional): a C++ expression specifying the implementation of the builtin. + It must be a function of signature `void(EvalState &, PosIdx, Value * *, Value &)`. + If not specified, defaults to `prim_${name}`. +* `renameInGlobalScope` (optional): whether the definiton should be "hidden" in the global scope by prefixing its name with two underscores. + If not specified, defaults to `true`. +* `args` (required): list containing the names of the arguments, as shown in the documentation. + All arguments must be listed here since the function arity is derived as the length of this list. +* `experimental_feature` (optional): the user-facing name of the experimental feature which needs to be enabled for the bultin function to be available. + If not specified, no experimental feature is required. + +New builtin function definition files must be added to `src/libexpr/builtins` and registered in the `builtin_definitions` list in `src/libexpr/meson.build`. + +### Builtin constants +The following metadata properties are supported for builtin constants: +* `name` (required): the language-facing name (as a member of the `builtins` attribute set) of the constant. +* `type` (required): the Nix language type of the constant; the C++ type is automatically derived. +* `constructorArgs` (optional): list of strings containing C++ expressions passed as arguments to the appropriate `Value` constructor. + If the value computation is more complex, `implementation` can be used instead. +* `implementation` (required if `constructorArgs` is not specified): string containing a C++ expressing computing the value of the constant. +* `impure` (optional): whether the constant is considered impure. + Impure constants are not available when pure evaluation mode is activated. + Defaults to `false` when not specified. + +New builtin constant definition files must be added to `src/libexpr/builtin-constants` and registered in the `builtin_constant_definitions` list in `src/libexpr/meson.build`. diff --git a/doc/manual/src/language/builtin-constants.md b/doc/manual/src/language/builtin-constants.md index 970bf6c5a..6197d1f76 100644 --- a/doc/manual/src/language/builtin-constants.md +++ b/doc/manual/src/language/builtin-constants.md @@ -4,7 +4,7 @@ These constants are built into the Nix language evaluator:
-{{#include @generated@/language/builtin-constants.md}} +{{#include @generated@/../../../src/libexpr/builtin-constants.md}}
diff --git a/doc/manual/src/language/builtins.md b/doc/manual/src/language/builtins.md index 2a3972f05..ae42d8dbd 100644 --- a/doc/manual/src/language/builtins.md +++ b/doc/manual/src/language/builtins.md @@ -15,6 +15,6 @@ For convenience, some built-ins can be accessed directly:

derivation is described in its own section.

-{{#include @generated@/language/builtins.md}} +{{#include @generated@/../../../src/libexpr/builtins.md}} diff --git a/doc/manual/src/language/meson.build b/doc/manual/src/language/meson.build deleted file mode 100644 index bc0d147f3..000000000 --- a/doc/manual/src/language/meson.build +++ /dev/null @@ -1,27 +0,0 @@ -builtins_md = custom_target( - command : nix_eval_for_docs + [ - '--expr', - 'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@)).builtins', - ], - capture : true, - input : [ - '../../generate-builtins.nix', - language_json, - ], - output : 'builtins.md', - env : nix_env_for_docs, -) - -builtin_constants_md = custom_target( - command : nix_eval_for_docs + [ - '--expr', - 'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@)).constants', - ], - capture : true, - input : [ - '../../generate-builtin-constants.nix', - language_json, - ], - output : 'builtin-constants.md', - env : nix_env_for_docs, -) diff --git a/src/code-generation/build_builtin_constants.py b/src/code-generation/build_builtin_constants.py new file mode 100644 index 000000000..beb7b6bfa --- /dev/null +++ b/src/code-generation/build_builtin_constants.py @@ -0,0 +1,93 @@ +from typing import List, NamedTuple + +from common import cxx_literal, generate_file, load_data + +KNOWN_KEYS = set([ + 'name', + 'type', + 'constructorArgs', + 'implementation', + 'impure', + 'renameInGlobalScope', +]) + +class BuiltinConstant(NamedTuple): + name: str + type: str + implementation: str + impure: bool + rename_in_global_scope: bool + documentation: str + + def parse(datum): + unknown_keys = set(datum.keys()) - KNOWN_KEYS + if unknown_keys: + raise Exception('unknown keys', unknown_keys) + return BuiltinConstant( + name = datum['name'], + type = datum['type'], + implementation = ('{' + ', '.join([f'NewValueAs::{datum["type"]}', *datum['constructorArgs']]) + '}') if 'constructorArgs' in datum else datum['implementation'], + impure = datum.get('impure', False), + rename_in_global_scope = datum.get('renameInGlobalScope', True), + documentation = datum.content, + ) + +VALUE_TYPES = { + 'attrs': 'nAttrs', + 'boolean': 'nBool', + 'integer': 'nInt', + 'list': 'nList', + 'null': 'nNull', + 'string': 'nString', +} + +HUMAN_TYPES = { + 'attrs': 'set', + 'boolean': 'Boolean', + 'integer': 'integer', + 'list': 'list', + 'null': 'null', + 'string': 'string', +} + +def main(): + import argparse + + ap = argparse.ArgumentParser() + ap.add_argument('--header', help='Path of the header to generate') + ap.add_argument('--docs', help='Path of the documentation file to generate') + ap.add_argument('defs', help='Builtin definition files', nargs='+') + args = ap.parse_args() + + builtin_constants = load_data(args.defs, BuiltinConstant.parse) + + generate_file(args.header, builtin_constants, lambda constant: + # `builtins` is magic and must come first + '' if constant.name == 'builtins' else constant.name, + lambda constant: + f'''{'if (!evalSettings.pureEval) ' if constant.impure else ''}{{ + addConstant({cxx_literal(('__' if constant.rename_in_global_scope else '') + constant.name)}, {constant.implementation}, {{ + .type = {VALUE_TYPES[constant.type]}, + .doc = {cxx_literal(constant.documentation)}, + .impureOnly = {cxx_literal(constant.impure)}, + }}); +}} +''') + generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda constant: + f'''
+ {constant.name} ({HUMAN_TYPES[constant.type]}) +
+
+ +{constant.documentation} + +''' + ('''> **Note** +> +> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). + +''' if constant.impure else '') + '''
+ +''') + +if __name__ == '__main__': + main() diff --git a/src/code-generation/build_builtins.py b/src/code-generation/build_builtins.py new file mode 100644 index 000000000..64021b4d3 --- /dev/null +++ b/src/code-generation/build_builtins.py @@ -0,0 +1,82 @@ +from typing import List, NamedTuple, Optional + +from build_experimental_features import ExperimentalFeature +from common import cxx_literal, generate_file, load_data + +KNOWN_KEYS = set([ + 'name', + 'implementation', + 'renameInGlobalScope', + 'args', + 'experimentalFeature', +]) + +class Builtin(NamedTuple): + name: str + implementation: str + rename_in_global_scope: bool + args: List[str] + experimental_feature: Optional[str] + documentation: str + + def parse(datum): + unknown_keys = set(datum.keys()) - KNOWN_KEYS + if unknown_keys: + raise Exception('unknown keys', unknown_keys) + return Builtin( + name = datum['name'], + implementation = datum['implementation'] if 'implementation' in datum else f'prim_{datum["name"]}', + rename_in_global_scope = datum.get('renameInGlobalScope', True), + args = datum['args'], + experimental_feature = datum.get('experimentalFeature', None), + documentation = datum.content, + ) + +def main(): + import argparse + + ap = argparse.ArgumentParser() + ap.add_argument('--header', help='Path of the header to generate') + ap.add_argument('--docs', help='Path of the documentation file to generate') + ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions') + ap.add_argument('defs', help='Builtin definition files', nargs='+') + args = ap.parse_args() + + builtins = load_data(args.defs, Builtin.parse) + + experimental_feature_names = set([builtin.experimental_feature for (_, builtin) in builtins]) + experimental_feature_names.discard(None) + experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names] + experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse) + experimental_features = dict(map(lambda path_and_feature: + (path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features)) + experimental_features[None] = 'std::nullopt' + + generate_file(args.header, builtins, lambda builtin: builtin.name, lambda builtin: + f'''{'' if builtin.experimental_feature is None else f'if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) '}{{ + addPrimOp({{ + .name = {cxx_literal(('__' if builtin.rename_in_global_scope else '') + builtin.name)}, + .args = {cxx_literal(builtin.args)}, + .arity = {len(builtin.args)}, + .doc = {cxx_literal(builtin.documentation)}, + .fun = {builtin.implementation}, + .experimentalFeature = {experimental_features[builtin.experimental_feature]}, + }}); +}} +''') + generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda builtin: + f'''
+ {builtin.name} {' '.join([f'{arg}' for arg in builtin.args])} +
+
+ +{builtin.documentation} + +''' + (f'''This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled. + +''' if builtin.experimental_feature is not None else '') + '''
+ +''') + +if __name__ == '__main__': + main() diff --git a/src/libexpr/builtin-constants/builtins.md b/src/libexpr/builtin-constants/builtins.md new file mode 100644 index 000000000..4a46cad27 --- /dev/null +++ b/src/libexpr/builtin-constants/builtins.md @@ -0,0 +1,14 @@ +--- +name: builtins +type: attrs +constructorArgs: [buildBindings(128).finish()] +renameInGlobalScope: false +--- +Contains all the [built-in functions](@docroot@/language/builtins.md) and values. + +Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations: + +```nix +# if hasContext is not available, we assume `s` has a context +if builtins ? hasContext then builtins.hasContext s else true +``` diff --git a/src/libexpr/builtin-constants/currentSystem.md b/src/libexpr/builtin-constants/currentSystem.md new file mode 100644 index 000000000..3da22bd4d --- /dev/null +++ b/src/libexpr/builtin-constants/currentSystem.md @@ -0,0 +1,27 @@ +--- +name: currentSystem +type: string +constructorArgs: [evalSettings.getCurrentSystem()] +impure: true +--- +The value of the +[`eval-system`](@docroot@/command-ref/conf-file.md#conf-eval-system) +or else +[`system`](@docroot@/command-ref/conf-file.md#conf-system) +configuration option. + +It can be used to set the `system` attribute for [`builtins.derivation`](@docroot@/language/derivations.md) such that the resulting derivation can be built on the same system that evaluates the Nix expression: + +```nix + builtins.derivation { + # ... + system = builtins.currentSystem; +} +``` + +It can be overridden in order to create derivations for different system than the current one: + +```console +$ nix-instantiate --system "mips64-linux" --eval --expr 'builtins.currentSystem' +"mips64-linux" +``` diff --git a/src/libexpr/builtin-constants/currentTime.md b/src/libexpr/builtin-constants/currentTime.md new file mode 100644 index 000000000..818646eaa --- /dev/null +++ b/src/libexpr/builtin-constants/currentTime.md @@ -0,0 +1,23 @@ +--- +name: currentTime +type: integer +constructorArgs: ['NixInt{time(0)}'] +impure: true +--- +Return the [Unix time](https://en.wikipedia.org/wiki/Unix_time) at first evaluation. +Repeated references to that name will re-use the initially obtained value. + +Example: + +```console +$ nix repl +Welcome to Nix 2.15.1 Type :? for help. + +nix-repl> builtins.currentTime +1683705525 + +nix-repl> builtins.currentTime +1683705525 +``` + +The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second. diff --git a/src/libexpr/builtin-constants/false.md b/src/libexpr/builtin-constants/false.md new file mode 100644 index 000000000..91c98d59f --- /dev/null +++ b/src/libexpr/builtin-constants/false.md @@ -0,0 +1,19 @@ +--- +name: 'false' +type: boolean +constructorArgs: ['false'] +renameInGlobalScope: false +--- +Primitive value. + +It can be returned by +[comparison operators](@docroot@/language/operators.md#Comparison) +and used in +[conditional expressions](@docroot@/language/constructs.md#Conditionals). + +The name `false` is not special, and can be shadowed: + +```nix-repl +nix-repl> let false = 1; in false +1 +``` diff --git a/src/libexpr/builtin-constants/langVersion.md b/src/libexpr/builtin-constants/langVersion.md new file mode 100644 index 000000000..eb3c8d477 --- /dev/null +++ b/src/libexpr/builtin-constants/langVersion.md @@ -0,0 +1,15 @@ +--- +name: langVersion +type: integer +constructorArgs: ['NixInt{6}'] +--- +The legacy version of the Nix language. Always is `6` on Lix, +matching Nix 2.18. + +Code in the Nix language should use other means of feature detection +like detecting the presence of builtins, rather than trying to find +the version of the Nix implementation, as there may be other Nix +implementations with different feature combinations. + +If the feature you want to write compatibility code for cannot be +detected by any means, please file a Lix bug. diff --git a/src/libexpr/builtin-constants/nixPath.md b/src/libexpr/builtin-constants/nixPath.md new file mode 100644 index 000000000..8b88d99ca --- /dev/null +++ b/src/libexpr/builtin-constants/nixPath.md @@ -0,0 +1,21 @@ +--- +name: nixPath +type: list +implementation: getNixPath(*this, searchPath) +--- +The search path used to resolve angle bracket path lookups. + +Angle bracket expressions can be +[desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) +using this and +[`builtins.findFile`](./builtins.html#builtins-findFile): + +```nix + +``` + +is equivalent to: + +```nix +builtins.findFile builtins.nixPath "nixpkgs" +``` diff --git a/src/libexpr/builtin-constants/nixVersion.md b/src/libexpr/builtin-constants/nixVersion.md new file mode 100644 index 000000000..53d73f84c --- /dev/null +++ b/src/libexpr/builtin-constants/nixVersion.md @@ -0,0 +1,14 @@ +--- +name: nixVersion +type: string +constructorArgs: ['"2.18.3-lix"'] +--- +Legacy version of Nix. Always returns "2.18.3-lix" on Lix. + +Code in the Nix language should use other means of feature detection +like detecting the presence of builtins, rather than trying to find +the version of the Nix implementation, as there may be other Nix +implementations with different feature combinations. + +If the feature you want to write compatibility code for cannot be +detected by any means, please file a Lix bug. diff --git a/src/libexpr/builtin-constants/null.md b/src/libexpr/builtin-constants/null.md new file mode 100644 index 000000000..97d025ba7 --- /dev/null +++ b/src/libexpr/builtin-constants/null.md @@ -0,0 +1,14 @@ +--- +name: 'null' +type: 'null' +constructorArgs: [] +renameInGlobalScope: false +--- +Primitive value. + +The name `null` is not special, and can be shadowed: + +```nix-repl +nix-repl> let null = 1; in null +1 +``` diff --git a/src/libexpr/builtin-constants/storeDir.md b/src/libexpr/builtin-constants/storeDir.md new file mode 100644 index 000000000..d37530bdd --- /dev/null +++ b/src/libexpr/builtin-constants/storeDir.md @@ -0,0 +1,13 @@ +--- +name: storeDir +type: string +constructorArgs: [store->storeDir] +--- +Logical file system location of the [Nix store](@docroot@/glossary.md#gloss-store) currently in use. + +This value is determined by the `store` parameter in [Store URLs](@docroot@/command-ref/new-cli/nix3-help-stores.md): + +```shell-session +$ nix-instantiate --store 'dummy://?store=/blah' --eval --expr builtins.storeDir +"/blah" +``` diff --git a/src/libexpr/builtin-constants/true.md b/src/libexpr/builtin-constants/true.md new file mode 100644 index 000000000..203328c3b --- /dev/null +++ b/src/libexpr/builtin-constants/true.md @@ -0,0 +1,19 @@ +--- +name: 'true' +type: boolean +constructorArgs: ['true'] +renameInGlobalScope: false +--- +Primitive value. + +It can be returned by +[comparison operators](@docroot@/language/operators.md#Comparison) +and used in +[conditional expressions](@docroot@/language/constructs.md#Conditionals). + +The name `true` is not special, and can be shadowed: + +```nix-repl +nix-repl> let true = 1; in true +1 +``` diff --git a/src/libexpr/builtins/abort.md b/src/libexpr/builtins/abort.md new file mode 100644 index 000000000..30ef7b13f --- /dev/null +++ b/src/libexpr/builtins/abort.md @@ -0,0 +1,6 @@ +--- +name: abort +args: [s] +renameInGlobalScope: false +--- +Abort Nix expression evaluation and print the error message *s*. diff --git a/src/libexpr/builtins/add.md b/src/libexpr/builtins/add.md new file mode 100644 index 000000000..22e23de7b --- /dev/null +++ b/src/libexpr/builtins/add.md @@ -0,0 +1,5 @@ +--- +name: add +args: [e1, e2] +--- +Return the sum of the numbers *e1* and *e2*. diff --git a/src/libexpr/builtins/addDrvOutputDependencies.md b/src/libexpr/builtins/addDrvOutputDependencies.md new file mode 100644 index 000000000..2dcd97a4d --- /dev/null +++ b/src/libexpr/builtins/addDrvOutputDependencies.md @@ -0,0 +1,12 @@ +--- +name: addDrvOutputDependencies +args: [s] +--- +Create a copy of the given string where a single constant string context element is turned into a "derivation deep" string context element. + +The store path that is the constant string context element should point to a valid derivation, and end in `.drv`. + +The original string context element must not be empty or have multiple elements, and it must not have any other type of element other than a constant or derivation deep element. +The latter is supported so this function is idempotent. + +This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency). diff --git a/src/libexpr/builtins/all.md b/src/libexpr/builtins/all.md new file mode 100644 index 000000000..22af750e6 --- /dev/null +++ b/src/libexpr/builtins/all.md @@ -0,0 +1,6 @@ +--- +name: all +args: [pred, list] +--- +Return `true` if the function *pred* returns `true` for all elements +of *list*, and `false` otherwise. diff --git a/src/libexpr/builtins/any.md b/src/libexpr/builtins/any.md new file mode 100644 index 000000000..e6644ef8b --- /dev/null +++ b/src/libexpr/builtins/any.md @@ -0,0 +1,6 @@ +--- +name: any +args: [pred, list] +--- +Return `true` if the function *pred* returns `true` for at least one +element of *list*, and `false` otherwise. diff --git a/src/libexpr/builtins/attrNames.md b/src/libexpr/builtins/attrNames.md new file mode 100644 index 000000000..e63c728e7 --- /dev/null +++ b/src/libexpr/builtins/attrNames.md @@ -0,0 +1,7 @@ +--- +name: attrNames +args: [set] +--- +Return the names of the attributes in the set *set* in an +alphabetically sorted list. For instance, `builtins.attrNames { y += 1; x = "foo"; }` evaluates to `[ "x" "y" ]`. diff --git a/src/libexpr/builtins/attrValues.md b/src/libexpr/builtins/attrValues.md new file mode 100644 index 000000000..ea83214d8 --- /dev/null +++ b/src/libexpr/builtins/attrValues.md @@ -0,0 +1,6 @@ +--- +name: attrValues +args: [set] +--- +Return the values of the attributes in the set *set* in the order +corresponding to the sorted attribute names. diff --git a/src/libexpr/builtins/baseNameOf.md b/src/libexpr/builtins/baseNameOf.md new file mode 100644 index 000000000..dc52eaab9 --- /dev/null +++ b/src/libexpr/builtins/baseNameOf.md @@ -0,0 +1,8 @@ +--- +name: baseNameOf +args: [s] +renameInGlobalScope: false +--- +Return the *base name* of the string *s*, that is, everything +following the final slash in the string. This is similar to the GNU +`basename` command. diff --git a/src/libexpr/builtins/bitAnd.md b/src/libexpr/builtins/bitAnd.md new file mode 100644 index 000000000..a0a815f04 --- /dev/null +++ b/src/libexpr/builtins/bitAnd.md @@ -0,0 +1,5 @@ +--- +name: bitAnd +args: [e1, e2] +--- +Return the bitwise AND of the integers *e1* and *e2*. diff --git a/src/libexpr/builtins/bitOr.md b/src/libexpr/builtins/bitOr.md new file mode 100644 index 000000000..16bf74b3a --- /dev/null +++ b/src/libexpr/builtins/bitOr.md @@ -0,0 +1,5 @@ +--- +name: bitOr +args: [e1, e2] +--- +Return the bitwise OR of the integers *e1* and *e2*. diff --git a/src/libexpr/builtins/bitXor.md b/src/libexpr/builtins/bitXor.md new file mode 100644 index 000000000..5009cba3f --- /dev/null +++ b/src/libexpr/builtins/bitXor.md @@ -0,0 +1,5 @@ +--- +name: bitXor +args: [e1, e2] +--- +Return the bitwise XOR of the integers *e1* and *e2*. diff --git a/src/libexpr/builtins/break.md b/src/libexpr/builtins/break.md new file mode 100644 index 000000000..bebfe6fb8 --- /dev/null +++ b/src/libexpr/builtins/break.md @@ -0,0 +1,7 @@ +--- +name: break +args: [v] +renameInGlobalScope: false +--- +In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL. +Otherwise, return the argument `v`. diff --git a/src/libexpr/builtins/catAttrs.md b/src/libexpr/builtins/catAttrs.md new file mode 100644 index 000000000..438fd0355 --- /dev/null +++ b/src/libexpr/builtins/catAttrs.md @@ -0,0 +1,13 @@ +--- +name: catAttrs +args: [attr, list] +--- +Collect each attribute named *attr* from a list of attribute +sets. Attrsets that don't contain the named attribute are +ignored. For example, + +```nix +builtins.catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] +``` + +evaluates to `[1 2]`. diff --git a/src/libexpr/builtins/ceil.md b/src/libexpr/builtins/ceil.md new file mode 100644 index 000000000..78f123339 --- /dev/null +++ b/src/libexpr/builtins/ceil.md @@ -0,0 +1,9 @@ +--- +name: ceil +args: [double] +--- +Converts an IEEE-754 double-precision floating-point number (*double*) to +the next higher integer. + +If the datatype is neither an integer nor a "float", an evaluation error will be +thrown. diff --git a/src/libexpr/builtins/compareVersions.md b/src/libexpr/builtins/compareVersions.md new file mode 100644 index 000000000..4155c645e --- /dev/null +++ b/src/libexpr/builtins/compareVersions.md @@ -0,0 +1,9 @@ +--- +name: compareVersions +args: [s1, s2] +--- +Compare two strings representing versions and return `-1` if +version *s1* is older than version *s2*, `0` if they are the same, +and `1` if *s1* is newer than *s2*. The version comparison +algorithm is the same as the one used by [`nix-env +-u`](../command-ref/nix-env.md#operation---upgrade). diff --git a/src/libexpr/builtins/concatLists.md b/src/libexpr/builtins/concatLists.md new file mode 100644 index 000000000..0171ddbfe --- /dev/null +++ b/src/libexpr/builtins/concatLists.md @@ -0,0 +1,5 @@ +--- +name: concatLists +args: [lists] +--- +Concatenate a list of lists into a single list. diff --git a/src/libexpr/builtins/concatMap.md b/src/libexpr/builtins/concatMap.md new file mode 100644 index 000000000..0fef7c169 --- /dev/null +++ b/src/libexpr/builtins/concatMap.md @@ -0,0 +1,6 @@ +--- +name: concatMap +args: [f, list] +--- +This function is equivalent to `builtins.concatLists (map f list)` +but is more efficient. diff --git a/src/libexpr/builtins/concatStringsSep.md b/src/libexpr/builtins/concatStringsSep.md new file mode 100644 index 000000000..b2a9d3d25 --- /dev/null +++ b/src/libexpr/builtins/concatStringsSep.md @@ -0,0 +1,7 @@ +--- +name: concatStringsSep +args: [separator, list] +--- +Concatenate a list of strings with a separator between each +element, e.g. `concatStringsSep "/" ["usr" "local" "bin"] == +"usr/local/bin"`. diff --git a/src/libexpr/builtins/deepSeq.md b/src/libexpr/builtins/deepSeq.md new file mode 100644 index 000000000..93bfe35e5 --- /dev/null +++ b/src/libexpr/builtins/deepSeq.md @@ -0,0 +1,7 @@ +--- +name: deepSeq +args: [e1, e2] +--- +This is like `seq e1 e2`, except that *e1* is evaluated *deeply*: +if it’s a list or set, its elements or attributes are also +evaluated recursively. diff --git a/src/libexpr/builtins/dirOf.md b/src/libexpr/builtins/dirOf.md new file mode 100644 index 000000000..560611eb9 --- /dev/null +++ b/src/libexpr/builtins/dirOf.md @@ -0,0 +1,8 @@ +--- +name: dirOf +args: [s] +renameInGlobalScope: false +--- +Return the directory part of the string *s*, that is, everything +before the final slash in the string. This is similar to the GNU +`dirname` command. diff --git a/src/libexpr/builtins/div.md b/src/libexpr/builtins/div.md new file mode 100644 index 000000000..503acaad8 --- /dev/null +++ b/src/libexpr/builtins/div.md @@ -0,0 +1,5 @@ +--- +name: div +args: [e1, e2] +--- +Return the quotient of the numbers *e1* and *e2*. diff --git a/src/libexpr/builtins/elem.md b/src/libexpr/builtins/elem.md new file mode 100644 index 000000000..b8a4db932 --- /dev/null +++ b/src/libexpr/builtins/elem.md @@ -0,0 +1,6 @@ +--- +name: elem +args: [x, xs] +--- +Return `true` if a value equal to *x* occurs in the list *xs*, and +`false` otherwise. diff --git a/src/libexpr/builtins/elemAt.md b/src/libexpr/builtins/elemAt.md new file mode 100644 index 000000000..4a11c3dc6 --- /dev/null +++ b/src/libexpr/builtins/elemAt.md @@ -0,0 +1,6 @@ +--- +name: elemAt +args: [xs, n] +--- +Return element *n* from the list *xs*. Elements are counted starting +from 0. A fatal error occurs if the index is out of bounds. diff --git a/src/libexpr/builtins/fetchClosure.md b/src/libexpr/builtins/fetchClosure.md new file mode 100644 index 000000000..6fc1ee6fe --- /dev/null +++ b/src/libexpr/builtins/fetchClosure.md @@ -0,0 +1,72 @@ +--- +name: fetchClosure +args: [args] +experimentalFeature: fetch-closure +--- +Fetch a store path [closure](@docroot@/glossary.md#gloss-closure) from a binary cache, and return the store path as a string with context. + +This function can be invoked in three ways, that we will discuss in order of preference. + +**Fetch a content-addressed store path** + +Example: + +```nix +builtins.fetchClosure { + fromStore = "https://cache.nixos.org"; + fromPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1; +} +``` + +This is the simplest invocation, and it does not require the user of the expression to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity. + +If your store path is [input addressed](@docroot@/glossary.md#gloss-input-addressed-store-object) instead of content addressed, consider the other two invocations. + +**Fetch any store path and rewrite it to a fully content-addressed store path** + +Example: + +```nix +builtins.fetchClosure { + fromStore = "https://cache.nixos.org"; + fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1; + toPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1; +} +``` + +This example fetches `/nix/store/r2jd...` from the specified binary cache, +and rewrites it into the content-addressed store path +`/nix/store/ldbh...`. + +Like the previous example, no extra configuration or privileges are required. + +To find out the correct value for `toPath` given a `fromPath`, +use [`nix store make-content-addressed`](@docroot@/command-ref/new-cli/nix3-store-make-content-addressed.md): + +```console +# nix store make-content-addressed --from https://cache.nixos.org /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1 +rewrote '/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1' to '/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1' +``` + +Alternatively, set `toPath = ""` and find the correct `toPath` in the error message. + +**Fetch an input-addressed store path as is** + +Example: + +```nix +builtins.fetchClosure { + fromStore = "https://cache.nixos.org"; + fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1; + inputAddressed = true; +} +``` + +It is possible to fetch an [input-addressed store path](@docroot@/glossary.md#gloss-input-addressed-store-object) and return it as is. +However, this is the least preferred way of invoking `fetchClosure`, because it requires that the input-addressed paths are trusted by the Lix configuration. + +**`builtins.storePath`** + +`fetchClosure` is similar to [`builtins.storePath`](#builtins-storePath) in that it allows you to use a previously built store path in a Nix expression. +However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched. +Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity. diff --git a/src/libexpr/builtins/fetchGit.md b/src/libexpr/builtins/fetchGit.md new file mode 100644 index 000000000..9dc128fab --- /dev/null +++ b/src/libexpr/builtins/fetchGit.md @@ -0,0 +1,138 @@ +--- +name: fetchGit +args: [args] +renameInGlobalScope: false +--- +Fetch a path from git. *args* can be a URL, in which case the HEAD +of the repo at that URL is fetched. Otherwise, it can be an +attribute with the following attributes (all except `url` optional): + +- `url` + + The URL of the repo. + +- `name` (default: *basename of the URL*) + + The name of the directory the repo should be exported to in the store. + +- `rev` (default: *the tip of `ref`*) + + The [Git revision] to fetch. + This is typically a commit hash. + + [Git revision]: https://git-scm.com/docs/git-rev-parse#_specifying_revisions + +- `ref` (default: `HEAD`) + + The [Git reference] under which to look for the requested revision. + This is often a branch or tag name. + + [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References + + By default, the `ref` value is prefixed with `refs/heads/`. + As of 2.3.0, Nix will not prefix `refs/heads/` if `ref` starts with `refs/` or + if `ref` looks like a commit hash for backwards compatibility with CppNix 2.3. + +- `submodules` (default: `false`) + + A Boolean parameter that specifies whether submodules should be checked out. + +- `shallow` (default: `false`) + + A Boolean parameter that specifies whether fetching from a shallow remote repository is allowed. + This still performs a full clone of what is available on the remote. + +- `allRefs` + + Whether to fetch all references of the repository. + With this argument being true, it's possible to load a `rev` from *any* `ref` + (by default only `rev`s from the specified `ref` are supported). + +Here are some examples of how to use `fetchGit`. + + - To fetch a private repository over SSH: + + ```nix + builtins.fetchGit { + url = "git@github.com:my-secret/repository.git"; + ref = "master"; + rev = "adab8b916a45068c044658c4158d81878f9ed1c3"; + } + ``` + + - To fetch an arbitrary reference: + + ```nix + builtins.fetchGit { + url = "https://github.com/NixOS/nix.git"; + ref = "refs/heads/0.5-release"; + } + ``` + + - If the revision you're looking for is in the default branch of + the git repository you don't strictly need to specify the branch + name in the `ref` attribute. + + However, if the revision you're looking for is in a future + branch for the non-default branch you will need to specify the + the `ref` attribute as well. + + ```nix + builtins.fetchGit { + url = "https://github.com/nixos/nix.git"; + rev = "841fcbd04755c7a2865c51c1e2d3b045976b7452"; + ref = "1.11-maintenance"; + } + ``` + + > **Note** + > + > It is nice to always specify the branch which a revision + > belongs to. Without the branch being specified, the fetcher + > might fail if the default branch changes. Additionally, it can + > be confusing to try a commit from a non-default branch and see + > the fetch fail. If the branch is specified the fault is much + > more obvious. + + - If the revision you're looking for is in the default branch of + the git repository you may omit the `ref` attribute. + + ```nix + builtins.fetchGit { + url = "https://github.com/nixos/nix.git"; + rev = "841fcbd04755c7a2865c51c1e2d3b045976b7452"; + } + ``` + + - To fetch a specific tag: + + ```nix + builtins.fetchGit { + url = "https://github.com/nixos/nix.git"; + ref = "refs/tags/1.9"; + } + ``` + + - To fetch the latest version of a remote branch: + + ```nix + builtins.fetchGit { + url = "ssh://git@github.com/nixos/nix.git"; + ref = "master"; + } + ``` + + Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting. + + This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). + + - To fetch the content of a checked-out work directory: + + ```nix + builtins.fetchGit ./work-dir + ``` + +If the URL points to a local directory, and no `ref` or `rev` is +given, `fetchGit` will use the current content of the checked-out +files, even if they are not committed or added to Git's index. It will +only consider files added to the Git repository, as listed by `git ls-files`. diff --git a/src/libexpr/builtins/fetchTarball.md b/src/libexpr/builtins/fetchTarball.md new file mode 100644 index 000000000..b0a2c750c --- /dev/null +++ b/src/libexpr/builtins/fetchTarball.md @@ -0,0 +1,42 @@ +--- +name: fetchTarball +args: [args] +renameInGlobalScope: false +--- +Download the specified URL, unpack it and return the path of the +unpacked tree. The file must be a tape archive (`.tar`) compressed +with `gzip`, `bzip2` or `xz`. The top-level path component of the +files in the tarball is removed, so it is best if the tarball +contains a single directory at top level. The typical use of the +function is to obtain external Nix expression dependencies, such as +a particular version of Nixpkgs, e.g. + +```nix +with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz") {}; + +stdenv.mkDerivation { … } +``` + +The fetched tarball is cached for a certain amount of time (1 +hour by default) in `~/.cache/nix/tarballs/`. You can change the +cache timeout either on the command line with `--tarball-ttl` +*number-of-seconds* or in the Nix configuration file by adding +the line `tarball-ttl = ` *number-of-seconds*. + +Note that when obtaining the hash with `nix-prefetch-url` the +option `--unpack` is required. + +This function can also verify the contents against a hash. In that +case, the function takes a set instead of a URL. The set requires +the attribute `url` and the attribute `sha256`, e.g. + +```nix +with import (fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz"; + sha256 = "1jppksrfvbk5ypiqdz4cddxdl8z6zyzdb2srq8fcffr327ld5jj2"; +}) {}; + +stdenv.mkDerivation { … } +``` + +Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). diff --git a/src/libexpr/builtins/fetchurl.md b/src/libexpr/builtins/fetchurl.md new file mode 100644 index 000000000..f2575e509 --- /dev/null +++ b/src/libexpr/builtins/fetchurl.md @@ -0,0 +1,7 @@ +--- +name: fetchurl +args: [url] +--- +Download the specified URL and return the path of the downloaded file. + +Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). diff --git a/src/libexpr/builtins/filter.md b/src/libexpr/builtins/filter.md new file mode 100644 index 000000000..e387fc68c --- /dev/null +++ b/src/libexpr/builtins/filter.md @@ -0,0 +1,6 @@ +--- +name: filter +args: [f, list] +--- +Return a list consisting of the elements of *list* for which the +function *f* returns `true`. diff --git a/src/libexpr/builtins/filterSource.md b/src/libexpr/builtins/filterSource.md new file mode 100644 index 000000000..045d9e641 --- /dev/null +++ b/src/libexpr/builtins/filterSource.md @@ -0,0 +1,51 @@ +--- +name: filterSource +args: [e1, e2] +--- +> **Warning** +> +> `filterSource` should not be used to filter store paths. Since +> `filterSource` uses the name of the input directory while naming +> the output directory, doing so will produce a directory name in +> the form of `--`, where `-` is +> the name of the input directory. Since `` depends on the +> unfiltered directory, the name of the output directory will +> indirectly depend on files that are filtered out by the +> function. This will trigger a rebuild even when a filtered out +> file is changed. Use `builtins.path` instead, which allows +> specifying the name of the output directory. + +This function allows you to copy sources into the Nix store while +filtering certain files. For instance, suppose that you want to use +the directory `source-dir` as an input to a Nix expression, e.g. + +```nix +stdenv.mkDerivation { + ... + src = ./source-dir; +} +``` + +However, if `source-dir` is a Subversion working copy, then all +those annoying `.svn` subdirectories will also be copied to the +store. Worse, the contents of those directories may change a lot, +causing lots of spurious rebuilds. With `filterSource` you can +filter out the `.svn` directories: + +```nix +src = builtins.filterSource + (path: type: type != "directory" || baseNameOf path != ".svn") + ./source-dir; +``` + +Thus, the first argument *e1* must be a predicate function that is +called for each regular file, directory or symlink in the source +tree *e2*. If the function returns `true`, the file is copied to the +Nix store, otherwise it is omitted. The function is called with two +arguments. The first is the full path of the file. The second is a +string that identifies the type of the file, which is either +`"regular"`, `"directory"`, `"symlink"` or `"unknown"` (for other +kinds of files such as device nodes or fifos — but note that those +cannot be copied to the Nix store, so if the predicate returns +`true` for them, the copy will fail). If you exclude a directory, +the entire corresponding subtree of *e2* will be excluded. diff --git a/src/libexpr/builtins/findFile.md b/src/libexpr/builtins/findFile.md new file mode 100644 index 000000000..e08491402 --- /dev/null +++ b/src/libexpr/builtins/findFile.md @@ -0,0 +1,45 @@ +--- +name: findFile +args: ['search path', 'lookup path'] +--- +Look up the given path with the given search path. + +A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes, `prefix`, and `path`. +`prefix` is a relative path. +`path` denotes a file system location; the exact syntax depends on the command line interface. + +Examples of search path attribute sets: + +- ``` + { + prefix = "nixos-config"; + path = "/etc/nixos/configuration.nix"; + } + ``` + +- ``` + { + prefix = ""; + path = "/nix/var/nix/profiles/per-user/root/channels"; + } + ``` + +The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match. + +This is the process for each entry: +If the lookup path matches `prefix`, then the remainder of the lookup path (the "suffix") is searched for within the directory denoted by `patch`. +Note that the `path` may need to be downloaded at this point to look inside. +If the suffix is found inside that directory, then the entry is a match; +the combined absolute path of the directory (now downloaded if need be) and the suffix is returned. + +The syntax + +```nix + +``` + +is equivalent to: + +```nix +builtins.findFile builtins.nixPath "nixpkgs" +``` diff --git a/src/libexpr/builtins/flakeRefToString.md b/src/libexpr/builtins/flakeRefToString.md new file mode 100644 index 000000000..847e23d11 --- /dev/null +++ b/src/libexpr/builtins/flakeRefToString.md @@ -0,0 +1,21 @@ +--- +name: flakeRefToString +implementation: flake::prim_flakeRefToString +args: [attrs] +experimentalFeature: flakes +--- +Convert a flake reference from attribute set format to URL format. + +For example: + +```nix +builtins.flakeRefToString { + dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; +} +``` + +evaluates to + +```nix +"github:NixOS/nixpkgs/23.05?dir=lib" +``` diff --git a/src/libexpr/builtins/floor.md b/src/libexpr/builtins/floor.md new file mode 100644 index 000000000..9645a358c --- /dev/null +++ b/src/libexpr/builtins/floor.md @@ -0,0 +1,9 @@ +--- +name: floor +args: [double] +--- +Converts an IEEE-754 double-precision floating-point number (*double*) to +the next lower integer. + +If the datatype is neither an integer nor a "float", an evaluation error will be +thrown. diff --git a/src/libexpr/builtins/foldlStrict.md b/src/libexpr/builtins/foldlStrict.md new file mode 100644 index 000000000..8818b3f0b --- /dev/null +++ b/src/libexpr/builtins/foldlStrict.md @@ -0,0 +1,10 @@ +--- +name: foldl' +implementation: prim_foldlStrict +args: [op, nul, list] +--- +Reduce a list by applying a binary operator, from left to right, +e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2) +...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6. +The return value of each application of `op` is evaluated immediately, +even for intermediate values. diff --git a/src/libexpr/builtins/fromJSON.md b/src/libexpr/builtins/fromJSON.md new file mode 100644 index 000000000..40f7e9616 --- /dev/null +++ b/src/libexpr/builtins/fromJSON.md @@ -0,0 +1,11 @@ +--- +name: fromJSON +args: [e] +--- +Convert a JSON string to a Nix value. For example, + +```nix +builtins.fromJSON ''{"x": [1, 2, 3], "y": null}'' +``` + +returns the value `{ x = [ 1 2 3 ]; y = null; }`. diff --git a/src/libexpr/builtins/fromTOML.md b/src/libexpr/builtins/fromTOML.md new file mode 100644 index 000000000..2d9ea343b --- /dev/null +++ b/src/libexpr/builtins/fromTOML.md @@ -0,0 +1,17 @@ +--- +name: fromTOML +args: [e] +renameInGlobalScope: false +--- +Convert a TOML string to a Nix value. For example, + +```nix +builtins.fromTOML '' + x=1 + s="a" + [table] + y=2 +'' +``` + +returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`. diff --git a/src/libexpr/builtins/functionArgs.md b/src/libexpr/builtins/functionArgs.md new file mode 100644 index 000000000..582122108 --- /dev/null +++ b/src/libexpr/builtins/functionArgs.md @@ -0,0 +1,13 @@ +--- +name: functionArgs +args: [f] +--- +Return a set containing the names of the formal arguments expected +by the function *f*. The value of each attribute is a Boolean +denoting whether the corresponding argument has a default value. For +instance, `functionArgs ({ x, y ? 123}: ...) = { x = false; y = +true; }`. + +"Formal argument" here refers to the attributes pattern-matched by +the function. Plain lambdas are not included, e.g. `functionArgs (x: +...) = { }`. diff --git a/src/libexpr/builtins/genList.md b/src/libexpr/builtins/genList.md new file mode 100644 index 000000000..b3813ec05 --- /dev/null +++ b/src/libexpr/builtins/genList.md @@ -0,0 +1,12 @@ +--- +name: genList +args: [generator, length] +--- +Generate list of size *length*, with each element *i* equal to the +value returned by *generator* `i`. For example, + +```nix +builtins.genList (x: x * x) 5 +``` + +returns the list `[ 0 1 4 9 16 ]`. diff --git a/src/libexpr/builtins/genericClosure.md b/src/libexpr/builtins/genericClosure.md new file mode 100644 index 000000000..9e2e3adc0 --- /dev/null +++ b/src/libexpr/builtins/genericClosure.md @@ -0,0 +1,28 @@ +--- +name: genericClosure +args: [attrset] +--- +Take an *attrset* with values named `startSet` and `operator` in order to +return a *list of attrsets* by starting with the `startSet` and recursively +applying the `operator` function to each `item`. The *attrsets* in the +`startSet` and the *attrsets* produced by `operator` must contain a value +named `key` which is comparable. The result is produced by calling `operator` +for each `item` with a value for `key` that has not been called yet including +newly produced `item`s. The function terminates when no new `item`s are +produced. The resulting *list of attrsets* contains only *attrsets* with a +unique key. For example, + +``` +builtins.genericClosure { + startSet = [ {key = 5;} ]; + operator = item: [{ + key = if (item.key / 2 ) * 2 == item.key + then item.key / 2 + else 3 * item.key + 1; + }]; +} +``` +evaluates to +``` +[ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ] +``` diff --git a/src/libexpr/builtins/getAttr.md b/src/libexpr/builtins/getAttr.md new file mode 100644 index 000000000..df9f0f26f --- /dev/null +++ b/src/libexpr/builtins/getAttr.md @@ -0,0 +1,8 @@ +--- +name: getAttr +args: [s, set] +--- +`getAttr` returns the attribute named *s* from *set*. Evaluation +aborts if the attribute doesn’t exist. This is a dynamic version of +the `.` operator, since *s* is an expression rather than an +identifier. diff --git a/src/libexpr/builtins/getContext.md b/src/libexpr/builtins/getContext.md new file mode 100644 index 000000000..66d611d12 --- /dev/null +++ b/src/libexpr/builtins/getContext.md @@ -0,0 +1,21 @@ +--- +name: getContext +args: [s] +--- +Return the string context of *s*. + +The string context tracks references to derivations within a string. +It is represented as an attribute set of [store derivation](@docroot@/glossary.md#gloss-store-derivation) paths mapping to output names. + +Using [string interpolation](@docroot@/language/string-interpolation.md) on a derivation will add that derivation to the string context. +For example, + +```nix +builtins.getContext "${derivation { name = "a"; builder = "b"; system = "c"; }}" +``` + +evaluates to + +``` +{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; } +``` diff --git a/src/libexpr/builtins/getEnv.md b/src/libexpr/builtins/getEnv.md new file mode 100644 index 000000000..a3f1a3b72 --- /dev/null +++ b/src/libexpr/builtins/getEnv.md @@ -0,0 +1,14 @@ +--- +name: getEnv +args: [s] +--- +`getEnv` returns the value of the environment variable *s*, or an +empty string if the variable doesn't exist. This function should be +used with care, as it can introduce all sorts of nasty environment +dependencies in your Nix expression. + +`getEnv` is used in nixpkgs for evil impurities such as locating the file +`~/.config/nixpkgs/config.nix` which contains user-local settings for nixpkgs. +(That is, it does a `getEnv "HOME"` to locate the user's home directory.) + +When in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval), this function always returns an empty string. diff --git a/src/libexpr/builtins/getFlake.md b/src/libexpr/builtins/getFlake.md new file mode 100644 index 000000000..5858120c7 --- /dev/null +++ b/src/libexpr/builtins/getFlake.md @@ -0,0 +1,19 @@ +--- +name: getFlake +implementation: flake::prim_getFlake +args: [args] +experimentalFeature: flakes +--- +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 +``` diff --git a/src/libexpr/builtins/groupBy.md b/src/libexpr/builtins/groupBy.md new file mode 100644 index 000000000..c1e22149e --- /dev/null +++ b/src/libexpr/builtins/groupBy.md @@ -0,0 +1,20 @@ +--- +name: groupBy +args: [f, list] +--- +Groups elements of *list* together by the string returned from the +function *f* called on each element. It returns an attribute set +where each attribute value contains the elements of *list* that are +mapped to the same corresponding attribute name returned by *f*. + +For example, + +```nix +builtins.groupBy (builtins.substring 0 1) ["foo" "bar" "baz"] +``` + +evaluates to + +```nix +{ b = [ "bar" "baz" ]; f = [ "foo" ]; } +``` diff --git a/src/libexpr/builtins/hasAttr.md b/src/libexpr/builtins/hasAttr.md new file mode 100644 index 000000000..18c6ca3b7 --- /dev/null +++ b/src/libexpr/builtins/hasAttr.md @@ -0,0 +1,7 @@ +--- +name: hasAttr +args: [s, set] +--- +`hasAttr` returns `true` if *set* has an attribute named *s*, and +`false` otherwise. This is a dynamic version of the `?` operator, +since *s* is an expression rather than an identifier. diff --git a/src/libexpr/builtins/hasContext.md b/src/libexpr/builtins/hasContext.md new file mode 100644 index 000000000..c4ea78c97 --- /dev/null +++ b/src/libexpr/builtins/hasContext.md @@ -0,0 +1,20 @@ +--- +name: hasContext +args: [s] +--- +Return `true` if string *s* has a non-empty context. +The context can be obtained with +[`getContext`](#builtins-getContext). + +> **Example** +> +> Many operations require a string context to be empty because they are intended only to work with "regular" strings, and also to help users avoid unintentionally losing track of string context elements. +> `builtins.hasContext` can help create better domain-specific errors in those case. +> +> ```nix +> name: meta: +> +> if builtins.hasContext name +> then throw "package name cannot contain string context" +> else { ${name} = meta; } +> ``` diff --git a/src/libexpr/builtins/hashFile.md b/src/libexpr/builtins/hashFile.md new file mode 100644 index 000000000..e7229f92f --- /dev/null +++ b/src/libexpr/builtins/hashFile.md @@ -0,0 +1,7 @@ +--- +name: hashFile +args: [type, p] +--- +Return a base-16 representation of the cryptographic hash of the +file at path *p*. The hash algorithm specified by *type* must be one +of `"md5"`, `"sha1"`, `"sha256"` or `"sha512"`. diff --git a/src/libexpr/builtins/hashString.md b/src/libexpr/builtins/hashString.md new file mode 100644 index 000000000..6655bc878 --- /dev/null +++ b/src/libexpr/builtins/hashString.md @@ -0,0 +1,7 @@ +--- +name: hashString +args: [type, s] +--- +Return a base-16 representation of the cryptographic hash of string +*s*. The hash algorithm specified by *type* must be one of `"md5"`, +`"sha1"`, `"sha256"` or `"sha512"`. diff --git a/src/libexpr/builtins/head.md b/src/libexpr/builtins/head.md new file mode 100644 index 000000000..312bec131 --- /dev/null +++ b/src/libexpr/builtins/head.md @@ -0,0 +1,7 @@ +--- +name: head +args: [list] +--- +Return the first element of a list; abort evaluation if the argument +isn’t a list or is an empty list. You can test whether a list is +empty by comparing it with `[]`. diff --git a/src/libexpr/builtins/import.md b/src/libexpr/builtins/import.md new file mode 100644 index 000000000..ec43e9c2c --- /dev/null +++ b/src/libexpr/builtins/import.md @@ -0,0 +1,63 @@ +--- +name: import +args: [path] +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). + +If *path* is a directory, the file `default.nix` in that directory +is loaded. + +Evaluation aborts if the file doesn’t exist or contains +an incorrect Nix expression. `import` implements Nix’s module +system: you can put any Nix expression (such as a set or a +function) in a separate file, and use it from Nix expressions in +other files. + +> **Note** +> +> Unlike some languages, `import` is a regular function in Nix. +> Paths using the angle bracket syntax (e.g., `import` *\*) +> are normal [path values](@docroot@/language/values.md#type-path). + +A Nix expression loaded by `import` must not contain any *free +variables* (identifiers that are not defined in the Nix expression +itself and are not built-in). Therefore, it cannot refer to +variables that are in scope at the call site. For instance, if you +have a calling expression + +```nix +rec { + x = 123; + y = import ./foo.nix; +} +``` + +then the following `foo.nix` will give an error: + +```nix +x + 456 +``` + +since `x` is not in scope in `foo.nix`. If you want `x` to be +available in `foo.nix`, you should pass it as a function argument: + +```nix +rec { + x = 123; + y = import ./foo.nix x; +} +``` + +and + +```nix +x: x + 456 +``` + +(The function argument doesn’t have to be called `x` in `foo.nix`; +any name would work.) diff --git a/src/libexpr/builtins/intersectAttrs.md b/src/libexpr/builtins/intersectAttrs.md new file mode 100644 index 000000000..d452f60c9 --- /dev/null +++ b/src/libexpr/builtins/intersectAttrs.md @@ -0,0 +1,8 @@ +--- +name: intersectAttrs +args: [e1, e2] +--- +Return a set consisting of the attributes in the set *e2* which have the +same name as some attribute in *e1*. + +Performs in O(*n* log *m*) where *n* is the size of the smaller set and *m* the larger set's size. diff --git a/src/libexpr/builtins/isAttrs.md b/src/libexpr/builtins/isAttrs.md new file mode 100644 index 000000000..a5052f85c --- /dev/null +++ b/src/libexpr/builtins/isAttrs.md @@ -0,0 +1,5 @@ +--- +name: isAttrs +args: [e] +--- +Return `true` if *e* evaluates to a set, and `false` otherwise. diff --git a/src/libexpr/builtins/isBool.md b/src/libexpr/builtins/isBool.md new file mode 100644 index 000000000..f6ee3d6c3 --- /dev/null +++ b/src/libexpr/builtins/isBool.md @@ -0,0 +1,5 @@ +--- +name: isBool +args: [e] +--- +Return `true` if *e* evaluates to a bool, and `false` otherwise. diff --git a/src/libexpr/builtins/isFloat.md b/src/libexpr/builtins/isFloat.md new file mode 100644 index 000000000..f7d2fec7c --- /dev/null +++ b/src/libexpr/builtins/isFloat.md @@ -0,0 +1,5 @@ +--- +name: isFloat +args: [e] +--- +Return `true` if *e* evaluates to a float, and `false` otherwise. diff --git a/src/libexpr/builtins/isFunction.md b/src/libexpr/builtins/isFunction.md new file mode 100644 index 000000000..01304bc51 --- /dev/null +++ b/src/libexpr/builtins/isFunction.md @@ -0,0 +1,5 @@ +--- +name: isFunction +args: [e] +--- +Return `true` if *e* evaluates to a function, and `false` otherwise. diff --git a/src/libexpr/builtins/isInt.md b/src/libexpr/builtins/isInt.md new file mode 100644 index 000000000..f1eee0d0c --- /dev/null +++ b/src/libexpr/builtins/isInt.md @@ -0,0 +1,5 @@ +--- +name: isInt +args: [e] +--- +Return `true` if *e* evaluates to an integer, and `false` otherwise. diff --git a/src/libexpr/builtins/isList.md b/src/libexpr/builtins/isList.md new file mode 100644 index 000000000..eaf0004ce --- /dev/null +++ b/src/libexpr/builtins/isList.md @@ -0,0 +1,5 @@ +--- +name: isList +args: [e] +--- +Return `true` if *e* evaluates to a list, and `false` otherwise. diff --git a/src/libexpr/builtins/isNull.md b/src/libexpr/builtins/isNull.md new file mode 100644 index 000000000..0ce9b830b --- /dev/null +++ b/src/libexpr/builtins/isNull.md @@ -0,0 +1,8 @@ +--- +name: isNull +args: [e] +renameInGlobalScope: false +--- +Return `true` if *e* evaluates to `null`, and `false` otherwise. + +This is equivalent to `e == null`. diff --git a/src/libexpr/builtins/isPath.md b/src/libexpr/builtins/isPath.md new file mode 100644 index 000000000..cbde7bd13 --- /dev/null +++ b/src/libexpr/builtins/isPath.md @@ -0,0 +1,5 @@ +--- +name: isPath +args: [e] +--- +Return `true` if *e* evaluates to a path, and `false` otherwise. diff --git a/src/libexpr/builtins/isString.md b/src/libexpr/builtins/isString.md new file mode 100644 index 000000000..6afea2466 --- /dev/null +++ b/src/libexpr/builtins/isString.md @@ -0,0 +1,5 @@ +--- +name: isString +args: [e] +--- +Return `true` if *e* evaluates to a string, and `false` otherwise. diff --git a/src/libexpr/builtins/length.md b/src/libexpr/builtins/length.md new file mode 100644 index 000000000..b04559245 --- /dev/null +++ b/src/libexpr/builtins/length.md @@ -0,0 +1,5 @@ +--- +name: length +args: [e] +--- +Return the length of the list *e*. diff --git a/src/libexpr/builtins/lessThan.md b/src/libexpr/builtins/lessThan.md new file mode 100644 index 000000000..dac095d5e --- /dev/null +++ b/src/libexpr/builtins/lessThan.md @@ -0,0 +1,7 @@ +--- +name: lessThan +args: [e1, e2] +--- +Return `true` if the number *e1* is less than the number *e2*, and +`false` otherwise. Evaluation aborts if either *e1* or *e2* does not +evaluate to a number. diff --git a/src/libexpr/builtins/listToAttrs.md b/src/libexpr/builtins/listToAttrs.md new file mode 100644 index 000000000..6192aec79 --- /dev/null +++ b/src/libexpr/builtins/listToAttrs.md @@ -0,0 +1,27 @@ +--- +name: listToAttrs +args: [e] +--- +Construct a set from a list specifying the names and values of each +attribute. Each element of the list should be a set consisting of a +string-valued attribute `name` specifying the name of the attribute, +and an attribute `value` specifying its value. + +In case of duplicate occurrences of the same name, the first +takes precedence. + +Example: + +```nix +builtins.listToAttrs + [ { name = "foo"; value = 123; } + { name = "bar"; value = 456; } + { name = "bar"; value = 420; } + ] +``` + +evaluates to + +```nix +{ foo = 123; bar = 456; } +``` diff --git a/src/libexpr/builtins/map.md b/src/libexpr/builtins/map.md new file mode 100644 index 000000000..c280c96b6 --- /dev/null +++ b/src/libexpr/builtins/map.md @@ -0,0 +1,13 @@ +--- +name: map +args: [f, list] +renameInGlobalScope: false +--- +Apply the function *f* to each element in the list *list*. For +example, + +```nix +map (x: "foo" + x) [ "bar" "bla" "abc" ] +``` + +evaluates to `[ "foobar" "foobla" "fooabc" ]`. diff --git a/src/libexpr/builtins/mapAttrs.md b/src/libexpr/builtins/mapAttrs.md new file mode 100644 index 000000000..a5131314d --- /dev/null +++ b/src/libexpr/builtins/mapAttrs.md @@ -0,0 +1,11 @@ +--- +name: mapAttrs +args: [f, attrset] +--- +Apply function *f* to every element of *attrset*. For example, + +```nix +builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; } +``` + +evaluates to `{ a = 10; b = 20; }`. diff --git a/src/libexpr/builtins/match.md b/src/libexpr/builtins/match.md new file mode 100644 index 000000000..5f9dbbc6e --- /dev/null +++ b/src/libexpr/builtins/match.md @@ -0,0 +1,32 @@ +--- +name: match +args: [regex, str] +--- +Returns a list if the [extended POSIX regular +expression](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04) +*regex* matches *str* precisely, otherwise returns `null`. Each item +in the list is a regex group. + +```nix +builtins.match "ab" "abc" +``` + +Evaluates to `null`. + +```nix +builtins.match "abc" "abc" +``` + +Evaluates to `[ ]`. + +```nix +builtins.match "a(b)(c)" "abc" +``` + +Evaluates to `[ "b" "c" ]`. + +```nix +builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO " +``` + +Evaluates to `[ "FOO" ]`. diff --git a/src/libexpr/builtins/mul.md b/src/libexpr/builtins/mul.md new file mode 100644 index 000000000..6a8ed2f5d --- /dev/null +++ b/src/libexpr/builtins/mul.md @@ -0,0 +1,5 @@ +--- +name: mul +args: [e1, e2] +--- +Return the product of the numbers *e1* and *e2*. diff --git a/src/libexpr/builtins/outputOf.md b/src/libexpr/builtins/outputOf.md new file mode 100644 index 000000000..f1b2bedf0 --- /dev/null +++ b/src/libexpr/builtins/outputOf.md @@ -0,0 +1,23 @@ +--- +name: outputOf +args: [derivation-reference, output-name] +experimentalFeature: dynamic-derivations +--- +Return the output path of a derivation, literally or using a placeholder if needed. + +If the derivation has a statically-known output path (i.e. the derivation output is input-addressed, or fixed content-addresed), the output path will just be returned. +But if the derivation is content-addressed or if the derivation is itself not-statically produced (i.e. is the output of another derivation), a placeholder will be returned instead. + +*`derivation reference`* must be a string that may contain a regular store path to a derivation, or may be a placeholder reference. If the derivation is produced by a derivation, you must explicitly select `drv.outPath`. +This primop can be chained arbitrarily deeply. +For instance, + +```nix +builtins.outputOf + (builtins.outputOf myDrv "out) + "out" +``` + +will return a placeholder for the output of the output of `myDrv`. + +This primop corresponds to the `^` sigil for derivable paths, e.g. as part of installable syntax on the command line. diff --git a/src/libexpr/builtins/parseDrvName.md b/src/libexpr/builtins/parseDrvName.md new file mode 100644 index 000000000..4629a6439 --- /dev/null +++ b/src/libexpr/builtins/parseDrvName.md @@ -0,0 +1,10 @@ +--- +name: parseDrvName +args: [s] +--- +Split the string *s* into a package name and version. The package +name is everything up to but not including the first dash not followed +by a letter, and the version is everything following that dash. The +result is returned in a set `{ name, version }`. Thus, +`builtins.parseDrvName "nix-0.12pre12876"` returns `{ name = +"nix"; version = "0.12pre12876"; }`. diff --git a/src/libexpr/builtins/parseFlakeRef.md b/src/libexpr/builtins/parseFlakeRef.md new file mode 100644 index 000000000..83260d94b --- /dev/null +++ b/src/libexpr/builtins/parseFlakeRef.md @@ -0,0 +1,19 @@ +--- +name: parseFlakeRef +implementation: flake::prim_parseFlakeRef +args: [flake-ref] +experimentalFeature: flakes +--- +Parse a flake reference, and return its exploded form. + +For example: + +```nix +builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib" +``` + +evaluates to: + +```nix +{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } +``` diff --git a/src/libexpr/builtins/partition.md b/src/libexpr/builtins/partition.md new file mode 100644 index 000000000..d0f41e88c --- /dev/null +++ b/src/libexpr/builtins/partition.md @@ -0,0 +1,19 @@ +--- +name: partition +args: [pred, list] +--- +Given a predicate function *pred*, this function returns an +attrset containing a list named `right`, containing the elements +in *list* for which *pred* returned `true`, and a list named +`wrong`, containing the elements for which it returned +`false`. For example, + +```nix +builtins.partition (x: x > 10) [1 23 9 3 42] +``` + +evaluates to + +```nix +{ right = [ 23 42 ]; wrong = [ 1 9 3 ]; } +``` diff --git a/src/libexpr/builtins/path.md b/src/libexpr/builtins/path.md new file mode 100644 index 000000000..de1f335cc --- /dev/null +++ b/src/libexpr/builtins/path.md @@ -0,0 +1,31 @@ +--- +name: path +args: [args] +--- +An enrichment of the built-in path type, based on the attributes +present in *args*. All are optional except `path`: + + - path\ + The underlying path. + + - name\ + The name of the path when added to the store. This can used to + reference paths that have nix-illegal characters in their names, + like `@`. + + - filter\ + A function of the type expected by `builtins.filterSource`, + with the same semantics. + + - recursive\ + When `false`, when `path` is added to the store it is with a + flat hash, rather than a hash of the NAR serialization of the + file. Thus, `path` must refer to a regular file, not a + directory. This allows similar behavior to `fetchurl`. Defaults + to `true`. + + - sha256\ + When provided, this is the expected hash of the file at the + path. Evaluation will fail if the hash is incorrect, and + providing a hash allows `builtins.path` to be used even when the + `pure-eval` nix config option is on. diff --git a/src/libexpr/builtins/pathExists.md b/src/libexpr/builtins/pathExists.md new file mode 100644 index 000000000..54e5d8fcf --- /dev/null +++ b/src/libexpr/builtins/pathExists.md @@ -0,0 +1,6 @@ +--- +name: pathExists +args: [path] +--- +Return `true` if the path *path* exists at evaluation time, and +`false` otherwise. diff --git a/src/libexpr/builtins/placeholder.md b/src/libexpr/builtins/placeholder.md new file mode 100644 index 000000000..49e0b443a --- /dev/null +++ b/src/libexpr/builtins/placeholder.md @@ -0,0 +1,8 @@ +--- +name: placeholder +args: [output] +renameInGlobalScope: false +--- +Return a placeholder string for the specified *output* that will be +substituted by the corresponding output path at build time. Typical +outputs would be `"out"`, `"bin"` or `"dev"`. diff --git a/src/libexpr/builtins/readDir.md b/src/libexpr/builtins/readDir.md new file mode 100644 index 000000000..0399c06bf --- /dev/null +++ b/src/libexpr/builtins/readDir.md @@ -0,0 +1,15 @@ +--- +name: readDir +args: [path] +--- +Return the contents of the directory *path* as a set mapping +directory entries to the corresponding file type. For instance, if +directory `A` contains a regular file `B` and another directory +`C`, then `builtins.readDir ./A` will return the set + +```nix +{ B = "regular"; C = "directory"; } +``` + +The possible values for the file type are `"regular"`, +`"directory"`, `"symlink"` and `"unknown"`. diff --git a/src/libexpr/builtins/readFile.md b/src/libexpr/builtins/readFile.md new file mode 100644 index 000000000..6702a31ad --- /dev/null +++ b/src/libexpr/builtins/readFile.md @@ -0,0 +1,5 @@ +--- +name: readFile +args: [path] +--- +Return the contents of the file *path* as a string. diff --git a/src/libexpr/builtins/readFileType.md b/src/libexpr/builtins/readFileType.md new file mode 100644 index 000000000..ec1c68e14 --- /dev/null +++ b/src/libexpr/builtins/readFileType.md @@ -0,0 +1,6 @@ +--- +name: readFileType +args: [p] +--- +Determine the directory entry type of a filesystem node, being +one of "directory", "regular", "symlink", or "unknown". diff --git a/src/libexpr/builtins/removeAttrs.md b/src/libexpr/builtins/removeAttrs.md new file mode 100644 index 000000000..eef4f25c1 --- /dev/null +++ b/src/libexpr/builtins/removeAttrs.md @@ -0,0 +1,13 @@ +--- +name: removeAttrs +args: [set, list] +renameInGlobalScope: false +--- +Remove the attributes listed in *list* from *set*. The attributes +don’t have to exist in *set*. For instance, + +```nix +removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ] +``` + +evaluates to `{ y = 2; }`. diff --git a/src/libexpr/builtins/replaceStrings.md b/src/libexpr/builtins/replaceStrings.md new file mode 100644 index 000000000..7f24f0cb1 --- /dev/null +++ b/src/libexpr/builtins/replaceStrings.md @@ -0,0 +1,16 @@ +--- +name: replaceStrings +args: [from, to, s] +--- +Given string *s*, replace every occurrence of the strings in *from* +with the corresponding string in *to*. + +The argument *to* is lazy, that is, it is only evaluated when its corresponding pattern in *from* is matched in the string *s* + +Example: + +```nix +builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar" +``` + +evaluates to `"fabir"`. diff --git a/src/libexpr/builtins/seq.md b/src/libexpr/builtins/seq.md new file mode 100644 index 000000000..b17e8f2be --- /dev/null +++ b/src/libexpr/builtins/seq.md @@ -0,0 +1,6 @@ +--- +name: seq +args: [e1, e2] +--- +Evaluate *e1*, then evaluate and return *e2*. This ensures that a +computation is strict in the value of *e1*. diff --git a/src/libexpr/builtins/sort.md b/src/libexpr/builtins/sort.md new file mode 100644 index 000000000..cba814fa3 --- /dev/null +++ b/src/libexpr/builtins/sort.md @@ -0,0 +1,17 @@ +--- +name: sort +args: [comparator, list] +--- +Return *list* in sorted order. It repeatedly calls the function +*comparator* with two elements. The comparator should return `true` +if the first element is less than the second, and `false` otherwise. +For example, + +```nix +builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ] +``` + +produces the list `[ 42 77 147 249 483 526 ]`. + +This is a stable sort: it preserves the relative order of elements +deemed equal by the comparator. diff --git a/src/libexpr/builtins/split.md b/src/libexpr/builtins/split.md new file mode 100644 index 000000000..c5d200b4c --- /dev/null +++ b/src/libexpr/builtins/split.md @@ -0,0 +1,33 @@ +--- +name: split +args: [regex, str] +--- +Returns a list composed of non matched strings interleaved with the +lists of the [extended POSIX regular +expression](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04) +*regex* matches of *str*. Each item in the lists of matched +sequences is a regex group. + +```nix +builtins.split "(a)b" "abc" +``` + +Evaluates to `[ "" [ "a" ] "c" ]`. + +```nix +builtins.split "([ac])" "abc" +``` + +Evaluates to `[ "" [ "a" ] "b" [ "c" ] "" ]`. + +```nix +builtins.split "(a)|(c)" "abc" +``` + +Evaluates to `[ "" [ "a" null ] "b" [ null "c" ] "" ]`. + +```nix +builtins.split "([[:upper:]]+)" " FOO " +``` + +Evaluates to `[ " " [ "FOO" ] " " ]`. diff --git a/src/libexpr/builtins/splitVersion.md b/src/libexpr/builtins/splitVersion.md new file mode 100644 index 000000000..4cd2fb450 --- /dev/null +++ b/src/libexpr/builtins/splitVersion.md @@ -0,0 +1,7 @@ +--- +name: splitVersion +args: [s] +--- +Split a string representing a version into its components, by the +same version splitting logic underlying the version comparison in +[`nix-env -u`](../command-ref/nix-env.md#operation---upgrade). diff --git a/src/libexpr/builtins/storePath.md b/src/libexpr/builtins/storePath.md new file mode 100644 index 000000000..0ef025cf3 --- /dev/null +++ b/src/libexpr/builtins/storePath.md @@ -0,0 +1,17 @@ +--- +name: storePath +args: [path] +--- +This function allows you to define a dependency on an already +existing store path. For example, the derivation attribute `src += builtins.storePath /nix/store/f1d18v1y…-source` causes the +derivation to depend on the specified path, which must exist or +be substitutable. Note that this differs from a plain path +(e.g. `src = /nix/store/f1d18v1y…-source`) in that the latter +causes the path to be *copied* again to the Nix store, resulting +in a new path (e.g. `/nix/store/ld01dnzc…-source-source`). + +Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). +Lix may change this, tracking issue: + +See also [`builtins.fetchClosure`](#builtins-fetchClosure). diff --git a/src/libexpr/builtins/stringLength.md b/src/libexpr/builtins/stringLength.md new file mode 100644 index 000000000..90caba4de --- /dev/null +++ b/src/libexpr/builtins/stringLength.md @@ -0,0 +1,6 @@ +--- +name: stringLength +args: [e] +--- +Return the length of the string *e*. If *e* is not a string, +evaluation is aborted. diff --git a/src/libexpr/builtins/sub.md b/src/libexpr/builtins/sub.md new file mode 100644 index 000000000..9c490089e --- /dev/null +++ b/src/libexpr/builtins/sub.md @@ -0,0 +1,5 @@ +--- +name: sub +args: [e1, e2] +--- +Return the difference between the numbers *e1* and *e2*. diff --git a/src/libexpr/builtins/substring.md b/src/libexpr/builtins/substring.md new file mode 100644 index 000000000..a10d6b4cf --- /dev/null +++ b/src/libexpr/builtins/substring.md @@ -0,0 +1,16 @@ +--- +name: substring +args: [start, len, s] +--- +Return the substring of *s* from character position *start* +(zero-based) up to but not including *start + len*. If *start* is +greater than the length of the string, an empty string is returned, +and if *start + len* lies beyond the end of the string, only the +substring up to the end of the string is returned. *start* must be +non-negative. For example, + +```nix +builtins.substring 0 3 "nixos" +``` + +evaluates to `"nix"`. diff --git a/src/libexpr/builtins/tail.md b/src/libexpr/builtins/tail.md new file mode 100644 index 000000000..ed3b869aa --- /dev/null +++ b/src/libexpr/builtins/tail.md @@ -0,0 +1,12 @@ +--- +name: tail +args: [list] +--- +Return the second to last elements of a list; abort evaluation if +the argument isn’t a list or is an empty list. + +> **Warning** +> +> This function should generally be avoided since it's inefficient: +> unlike Haskell's `tail`, it takes O(n) time, so recursing over a +> list by repeatedly calling `tail` takes O(n^2) time. diff --git a/src/libexpr/builtins/throw.md b/src/libexpr/builtins/throw.md new file mode 100644 index 000000000..53f85a093 --- /dev/null +++ b/src/libexpr/builtins/throw.md @@ -0,0 +1,10 @@ +--- +name: throw +args: [s] +renameInGlobalScope: false +--- +Throw an error message *s*. This usually aborts Nix expression +evaluation, but in `nix-env -qa` and other commands that try to +evaluate a set of derivations to get information about those +derivations, a derivation that throws an error is silently skipped +(which is not the case for `abort`). diff --git a/src/libexpr/builtins/toFile.md b/src/libexpr/builtins/toFile.md new file mode 100644 index 000000000..d9fd057ef --- /dev/null +++ b/src/libexpr/builtins/toFile.md @@ -0,0 +1,73 @@ +--- +name: toFile +args: [name, s] +--- +Store the string *s* in a file in the Nix store and return its +path. The file has suffix *name*. This file can be used as an +input to derivations. One application is to write builders +“inline”. For instance, the following Nix expression combines the +Nix expression for GNU Hello and its build script into one file: + +```nix +{ stdenv, fetchurl, perl }: + +stdenv.mkDerivation { + name = "hello-2.1.1"; + + builder = builtins.toFile "builder.sh" " + source $stdenv/setup + + PATH=$perl/bin:$PATH + + tar xvfz $src + cd hello-* + ./configure --prefix=$out + make + make install + "; + + src = fetchurl { + url = "http://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz"; + sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465"; + }; + inherit perl; +} +``` + +It is even possible for one file to refer to another, e.g., + +```nix +builder = let + configFile = builtins.toFile "foo.conf" " + # This is some dummy configuration file. + ... + "; +in builtins.toFile "builder.sh" " + source $stdenv/setup + ... + cp ${configFile} $out/etc/foo.conf +"; +``` + +Note that `${configFile}` is a +[string interpolation](@docroot@/language/values.md#type-string), so the result of the +expression `configFile` +(i.e., a path like `/nix/store/m7p7jfny445k...-foo.conf`) will be +spliced into the resulting string. + +It is however *not* allowed to have files mutually referring to each +other, like so: + +```nix +let + foo = builtins.toFile "foo" "...${bar}..."; + bar = builtins.toFile "bar" "...${foo}..."; +in foo +``` + +This is not allowed because it would cause a cyclic dependency in +the computation of the cryptographic hashes for `foo` and `bar`. + +It is also not possible to reference the result of a derivation. If +you are using Nixpkgs, the `writeTextFile` function is able to do +that. diff --git a/src/libexpr/builtins/toJSON.md b/src/libexpr/builtins/toJSON.md new file mode 100644 index 000000000..f8116aba4 --- /dev/null +++ b/src/libexpr/builtins/toJSON.md @@ -0,0 +1,10 @@ +--- +name: toJSON +args: [e] +--- +Return a string containing a JSON representation of *e*. Strings, +integers, floats, booleans, nulls and lists are mapped to their JSON +equivalents. Sets (except derivations) are represented as objects. +Derivations are translated to a JSON string containing the +derivation’s output path. Paths are copied to the store and +represented as a JSON string of the resulting store path. diff --git a/src/libexpr/builtins/toPath.md b/src/libexpr/builtins/toPath.md new file mode 100644 index 000000000..5823ee2fa --- /dev/null +++ b/src/libexpr/builtins/toPath.md @@ -0,0 +1,6 @@ +--- +name: toPath +args: [s] +--- +**DEPRECATED.** Use `/. + "/path"` to convert a string into an absolute +path. For relative paths, use `./. + "/path"`. diff --git a/src/libexpr/builtins/toString.md b/src/libexpr/builtins/toString.md new file mode 100644 index 000000000..1db5cd91a --- /dev/null +++ b/src/libexpr/builtins/toString.md @@ -0,0 +1,21 @@ +--- +name: toString +args: [e] +renameInGlobalScope: false +--- +Convert the expression *e* to a string. *e* can be: + + - A string (in which case the string is returned unmodified). + + - A path (e.g., `toString /foo/bar` yields `"/foo/bar"`. + + - A set containing `{ __toString = self: ...; }` or `{ outPath = ...; }`. + + - An integer. + + - A list, in which case the string representations of its elements + are joined with spaces. + + - A Boolean (`false` yields `""`, `true` yields `"1"`). + + - `null`, which yields the empty string. diff --git a/src/libexpr/builtins/toXML.md b/src/libexpr/builtins/toXML.md new file mode 100644 index 000000000..a03730fe5 --- /dev/null +++ b/src/libexpr/builtins/toXML.md @@ -0,0 +1,93 @@ +--- +name: toXML +args: [e] +--- +Return a string containing an XML representation of *e*. The main +application for `toXML` is to communicate information with the +builder in a more structured format than plain environment +variables. + +Here is an example where this is the case: + +```nix +{ stdenv, fetchurl, libxslt, jira, uberwiki }: + +stdenv.mkDerivation (rec { + name = "web-server"; + + buildInputs = [ libxslt ]; + + builder = builtins.toFile "builder.sh" " + source $stdenv/setup + mkdir $out + echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml ① + "; + + stylesheet = builtins.toFile "stylesheet.xsl" ② + " + + + + + + + + + + + + + "; + + servlets = builtins.toXML [ ③ + { path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; } + { path = "/wiki"; war = uberwiki + "/uberwiki.war"; } + ]; +}) +``` + +The builder is supposed to generate the configuration file for a +[Jetty servlet container](http://jetty.mortbay.org/). A servlet +container contains a number of servlets (`*.war` files) each +exported under a specific URI prefix. So the servlet configuration +is a list of sets containing the `path` and `war` of the servlet +(①). This kind of information is difficult to communicate with the +normal method of passing information through an environment +variable, which just concatenates everything together into a +string (which might just work in this case, but wouldn’t work if +fields are optional or contain lists themselves). Instead the Nix +expression is converted to an XML representation with `toXML`, +which is unambiguous and can easily be processed with the +appropriate tools. For instance, in the example an XSLT stylesheet +(at point ②) is applied to it (at point ①) to generate the XML +configuration file for the Jetty server. The XML representation +produced at point ③ by `toXML` is as follows: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +Note that we used the `toFile` built-in to write the builder and +the stylesheet “inline” in the Nix expression. The path of the +stylesheet is spliced into the builder using the syntax `xsltproc +${stylesheet}`. diff --git a/src/libexpr/builtins/trace.md b/src/libexpr/builtins/trace.md new file mode 100644 index 000000000..630133b33 --- /dev/null +++ b/src/libexpr/builtins/trace.md @@ -0,0 +1,13 @@ +--- +name: trace +args: [e1, e2] +--- +Evaluate *e1* and print its abstract syntax representation on +standard error. Then return *e2*. This function is useful for +debugging. + +If the +[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace) +option is set to `true` and the `--debugger` flag is given, the +interactive debugger will be started when `trace` is called (like +[`break`](@docroot@/language/builtins.md#builtins-break)). diff --git a/src/libexpr/builtins/traceVerbose.md b/src/libexpr/builtins/traceVerbose.md new file mode 100644 index 000000000..06c831309 --- /dev/null +++ b/src/libexpr/builtins/traceVerbose.md @@ -0,0 +1,8 @@ +--- +name: traceVerbose +implementation: 'evalSettings.traceVerbose ? prim_trace : prim_second' +args: [e1, e2] +--- +Evaluate *e1* and print its abstract syntax representation on standard +error if `--trace-verbose` is enabled. Then return *e2*. This function +is useful for debugging. diff --git a/src/libexpr/builtins/tryEval.md b/src/libexpr/builtins/tryEval.md new file mode 100644 index 000000000..7d8c141e1 --- /dev/null +++ b/src/libexpr/builtins/tryEval.md @@ -0,0 +1,17 @@ +--- +name: tryEval +args: [e] +--- +Try to shallowly evaluate *e*. Return a set containing the +attributes `success` (`true` if *e* evaluated successfully, +`false` if an error was thrown) and `value`, equalling *e* if +successful and `false` otherwise. `tryEval` will only prevent +errors created by `throw` or `assert` from being thrown. +Errors `tryEval` will not catch are for example those created +by `abort` and type errors generated by builtins. Also note that +this doesn't evaluate *e* deeply, so `let e = { x = throw ""; }; +in (builtins.tryEval e).success` will be `true`. Using +`builtins.deepSeq` one can get the expected result: +`let e = { x = throw ""; }; in +(builtins.tryEval (builtins.deepSeq e e)).success` will be +`false`. diff --git a/src/libexpr/builtins/typeOf.md b/src/libexpr/builtins/typeOf.md new file mode 100644 index 000000000..26a3c420d --- /dev/null +++ b/src/libexpr/builtins/typeOf.md @@ -0,0 +1,7 @@ +--- +name: typeOf +args: [e] +--- +Return a string representing the type of the value *e*, namely +`"int"`, `"bool"`, `"string"`, `"path"`, `"null"`, `"set"`, +`"list"`, `"lambda"` or `"float"`. diff --git a/src/libexpr/builtins/unsafeDiscardOutputDependency.md b/src/libexpr/builtins/unsafeDiscardOutputDependency.md new file mode 100644 index 000000000..23c4de043 --- /dev/null +++ b/src/libexpr/builtins/unsafeDiscardOutputDependency.md @@ -0,0 +1,15 @@ +--- +name: unsafeDiscardOutputDependency +args: [s] +--- +Create a copy of the given string where every "derivation deep" string context element is turned into a constant string context element. + +This is the opposite of [`builtins.addDrvOutputDependencies`](#builtins-addDrvOutputDependencies). + +This is unsafe because it allows us to "forget" store objects we would have otherwise refered to with the string context, +whereas Nix normally tracks all dependencies consistently. +Safe operations "grow" but never "shrink" string contexts. +[`builtins.addDrvOutputDependencies`] in contrast is safe because "derivation deep" string context element always refers to the underlying derivation (among many more things). +Replacing a constant string context element with a "derivation deep" element is a safe operation that just enlargens the string context without forgetting anything. + +[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies diff --git a/src/libexpr/builtins/zipAttrsWith.md b/src/libexpr/builtins/zipAttrsWith.md new file mode 100644 index 000000000..a90b6de17 --- /dev/null +++ b/src/libexpr/builtins/zipAttrsWith.md @@ -0,0 +1,28 @@ +--- +name: zipAttrsWith +args: [f, list] +--- +Transpose a list of attribute sets into an attribute set of lists, +then apply `mapAttrs`. + +`f` receives two arguments: the attribute name and a non-empty +list of all values encountered for that attribute name. + +The result is an attribute set where the attribute names are the +union of the attribute names in each element of `list`. The attribute +values are the return values of `f`. + +```nix +builtins.zipAttrsWith + (name: values: { inherit name values; }) + [ { a = "x"; } { a = "y"; b = "z"; } ] +``` + +evaluates to + +``` +{ + a = { name = "a"; values = [ "x" "y" ]; }; + b = { name = "b"; values = [ "z" ]; }; +} +``` diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a87383f7f..d6ad72f3c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -424,7 +424,7 @@ Path EvalState::toRealPath(const Path & path, const NixStringContext & context) } -Value * EvalState::addConstant(const std::string & name, Value & v, Constant info) +Value * EvalState::addConstant(const std::string & name, const Value & v, Constant info) { Value * v2 = allocValue(); *v2 = v; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ba1e4a820..78669e464 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -540,7 +540,7 @@ private: void createBaseEnv(); - Value * addConstant(const std::string & name, Value & v, Constant info); + Value * addConstant(const std::string & name, const Value & v, Constant info); void addConstant(const std::string & name, Value * v, Constant info); diff --git a/src/libexpr/extra-primops.hh b/src/libexpr/extra-primops.hh new file mode 100644 index 000000000..7b32aeb6f --- /dev/null +++ b/src/libexpr/extra-primops.hh @@ -0,0 +1,29 @@ +#pragma once +///@file + +#include "pos-idx.hh" + +namespace nix { + +class EvalState; +struct Value; + +void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v); + +namespace flake { + +void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value * * args, Value & v); + +} + +} diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index ab13ba3be..6895f3943 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -1,6 +1,7 @@ #include "flake.hh" #include "eval.hh" #include "eval-settings.hh" +#include "extra-primops.hh" #include "lockfile.hh" #include "primops.hh" #include "eval-inline.hh" @@ -796,7 +797,7 @@ void callFlake(EvalState & state, state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos); } -static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_getFlake(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(flakeRefS, {}, true); @@ -814,29 +815,7 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V v); } -static RegisterPrimOp r2({ - .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( +void prim_parseFlakeRef( EvalState & state, const PosIdx pos, Value * * args, @@ -858,30 +837,7 @@ static void prim_parseFlakeRef( v.mkAttrs(binds); } -static RegisterPrimOp r3({ - .name = "__parseFlakeRef", - .args = {"flake-ref"}, - .doc = R"( - Parse a flake reference, and return its exploded form. - - For example: - - ```nix - builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib" - ``` - - evaluates to: - - ```nix - { dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } - ``` - )", - .fun = prim_parseFlakeRef, - .experimentalFeature = Xp::Flakes, -}); - - -static void prim_flakeRefToString( +void prim_flakeRefToString( EvalState & state, const PosIdx pos, Value * * args, @@ -919,30 +875,6 @@ static void prim_flakeRefToString( v.mkString(flakeRef.to_string()); } -static RegisterPrimOp r4({ - .name = "__flakeRefToString", - .args = {"attrs"}, - .doc = R"( - Convert a flake reference from attribute set format to URL format. - - For example: - - ```nix - builtins.flakeRefToString { - dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; - } - ``` - - evaluates to - - ```nix - "github:NixOS/nixpkgs/23.05?dir=lib" - ``` - )", - .fun = prim_flakeRefToString, - .experimentalFeature = Xp::Flakes, -}); - } Fingerprint LockedFlake::getFingerprint() const diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index f3ea93c71..96ffb8fdd 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -44,6 +44,154 @@ libexpr_settings_header = custom_target( install_dir : includedir / 'lib/libexpr', ) +builtin_definitions = files( + 'builtins/abort.md', + 'builtins/add.md', + 'builtins/addDrvOutputDependencies.md', + 'builtins/all.md', + 'builtins/any.md', + 'builtins/attrNames.md', + 'builtins/attrValues.md', + 'builtins/baseNameOf.md', + 'builtins/bitAnd.md', + 'builtins/bitOr.md', + 'builtins/bitXor.md', + 'builtins/break.md', + 'builtins/catAttrs.md', + 'builtins/ceil.md', + 'builtins/compareVersions.md', + 'builtins/concatLists.md', + 'builtins/concatMap.md', + 'builtins/concatStringsSep.md', + 'builtins/deepSeq.md', + 'builtins/dirOf.md', + 'builtins/div.md', + 'builtins/elem.md', + 'builtins/elemAt.md', + 'builtins/fetchClosure.md', + 'builtins/fetchGit.md', + 'builtins/fetchTarball.md', + 'builtins/fetchurl.md', + 'builtins/filter.md', + 'builtins/filterSource.md', + 'builtins/findFile.md', + 'builtins/flakeRefToString.md', + 'builtins/floor.md', + 'builtins/foldlStrict.md', + 'builtins/fromJSON.md', + 'builtins/fromTOML.md', + 'builtins/functionArgs.md', + 'builtins/genList.md', + 'builtins/genericClosure.md', + 'builtins/getAttr.md', + 'builtins/getContext.md', + 'builtins/getEnv.md', + 'builtins/getFlake.md', + 'builtins/groupBy.md', + 'builtins/hasAttr.md', + 'builtins/hasContext.md', + 'builtins/hashFile.md', + 'builtins/hashString.md', + 'builtins/head.md', + 'builtins/import.md', + 'builtins/intersectAttrs.md', + 'builtins/isAttrs.md', + 'builtins/isBool.md', + 'builtins/isFloat.md', + 'builtins/isFunction.md', + 'builtins/isInt.md', + 'builtins/isList.md', + 'builtins/isNull.md', + 'builtins/isPath.md', + 'builtins/isString.md', + 'builtins/length.md', + 'builtins/lessThan.md', + 'builtins/listToAttrs.md', + 'builtins/map.md', + 'builtins/mapAttrs.md', + 'builtins/match.md', + 'builtins/mul.md', + 'builtins/outputOf.md', + 'builtins/parseDrvName.md', + 'builtins/parseFlakeRef.md', + 'builtins/partition.md', + 'builtins/path.md', + 'builtins/pathExists.md', + 'builtins/placeholder.md', + 'builtins/readDir.md', + 'builtins/readFile.md', + 'builtins/readFileType.md', + 'builtins/removeAttrs.md', + 'builtins/replaceStrings.md', + 'builtins/seq.md', + 'builtins/sort.md', + 'builtins/split.md', + 'builtins/splitVersion.md', + 'builtins/storePath.md', + 'builtins/stringLength.md', + 'builtins/sub.md', + 'builtins/substring.md', + 'builtins/tail.md', + 'builtins/throw.md', + 'builtins/toFile.md', + 'builtins/toJSON.md', + 'builtins/toPath.md', + 'builtins/toString.md', + 'builtins/toXML.md', + 'builtins/trace.md', + 'builtins/traceVerbose.md', + 'builtins/tryEval.md', + 'builtins/typeOf.md', + 'builtins/unsafeDiscardOutputDependency.md', + 'builtins/zipAttrsWith.md', +) +builtins_gen = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_builtins.py', + '--header', '@OUTPUT0@', + '--docs', '@OUTPUT1@', + '--experimental-features', '@SOURCE_ROOT@/src/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] + +builtin_constant_definitions = files( + 'builtin-constants/builtins.md', + 'builtin-constants/currentSystem.md', + 'builtin-constants/currentTime.md', + 'builtin-constants/false.md', + 'builtin-constants/langVersion.md', + 'builtin-constants/nixPath.md', + 'builtin-constants/nixVersion.md', + 'builtin-constants/null.md', + 'builtin-constants/storeDir.md', + 'builtin-constants/true.md', +) +builtin_constants_gen = custom_target( + command : [ + python.full_path(), + '@SOURCE_ROOT@/src/code-generation/build_builtin_constants.py', + '--header', '@OUTPUT0@', + '--docs', '@OUTPUT1@', + '@INPUT@', + ], + input : builtin_constant_definitions, + output : [ + 'register-builtin-constants.gen.inc', + 'builtin-constants.md', + ], +) +register_builtin_constants_header = builtin_constants_gen[0] +builtin_constants_md = builtin_constants_gen[1] + libexpr_sources = files( 'attr-path.cc', 'attr-set.cc', @@ -117,6 +265,8 @@ libexpr = library( libexpr_sources, libexpr_settings_header, libexpr_generated_headers, + register_builtins_header, + register_builtin_constants_header, dependencies : [ liblixutil, liblixstore, diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index dab96d6d4..183a52f8d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4,6 +4,7 @@ #include "eval-inline.hh" #include "eval.hh" #include "eval-settings.hh" +#include "extra-primops.hh" #include "gc-small-vector.hh" #include "globals.hh" #include "json-to-value.hh" @@ -260,75 +261,10 @@ static RegisterPrimOp primop_scopedImport(PrimOp { } }); -static RegisterPrimOp primop_import({ - .name = "import", - .args = {"path"}, - // TODO turn "normal path values" into link below - .doc = R"( - 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). - - If *path* is a directory, the file `default.nix` in that directory - is loaded. - - Evaluation aborts if the file doesn’t exist or contains - an incorrect Nix expression. `import` implements Nix’s module - system: you can put any Nix expression (such as a set or a - function) in a separate file, and use it from Nix expressions in - other files. - - > **Note** - > - > Unlike some languages, `import` is a regular function in Nix. - > Paths using the angle bracket syntax (e.g., `import` *\*) - > are normal [path values](@docroot@/language/values.md#type-path). - - A Nix expression loaded by `import` must not contain any *free - variables* (identifiers that are not defined in the Nix expression - itself and are not built-in). Therefore, it cannot refer to - variables that are in scope at the call site. For instance, if you - have a calling expression - - ```nix - rec { - x = 123; - y = import ./foo.nix; - } - ``` - - then the following `foo.nix` will give an error: - - ```nix - x + 456 - ``` - - since `x` is not in scope in `foo.nix`. If you want `x` to be - available in `foo.nix`, you should pass it as a function argument: - - ```nix - rec { - x = 123; - y = import ./foo.nix x; - } - ``` - - and - - ```nix - x: x + 456 - ``` - - (The function argument doesn’t have to be called `x` in `foo.nix`; - any name would work.) - )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - import(state, pos, *args[0], nullptr, v); - } -}); +static void prim_import(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + import(state, pos, *args[0], nullptr, v); +} /* Want reasonable symbol names, so extern C */ /* !!! Should we pass the Pos or the file name too? */ @@ -426,17 +362,6 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val v.mkString(t); } -static RegisterPrimOp primop_typeOf({ - .name = "__typeOf", - .args = {"e"}, - .doc = R"( - Return a string representing the type of the value *e*, namely - `"int"`, `"bool"`, `"string"`, `"path"`, `"null"`, `"set"`, - `"list"`, `"lambda"` or `"float"`. - )", - .fun = prim_typeOf, -}); - /* Determine whether the argument is the null value. */ static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -444,17 +369,6 @@ static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Val v.mkBool(args[0]->type() == nNull); } -static RegisterPrimOp primop_isNull({ - .name = "isNull", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to `null`, and `false` otherwise. - - This is equivalent to `e == null`. - )", - .fun = prim_isNull, -}); - /* Determine whether the argument is a function. */ static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -462,15 +376,6 @@ static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, v.mkBool(args[0]->type() == nFunction); } -static RegisterPrimOp primop_isFunction({ - .name = "__isFunction", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a function, and `false` otherwise. - )", - .fun = prim_isFunction, -}); - /* Determine whether the argument is an integer. */ static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -478,15 +383,6 @@ static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Valu v.mkBool(args[0]->type() == nInt); } -static RegisterPrimOp primop_isInt({ - .name = "__isInt", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to an integer, and `false` otherwise. - )", - .fun = prim_isInt, -}); - /* Determine whether the argument is a float. */ static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -494,15 +390,6 @@ static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Va v.mkBool(args[0]->type() == nFloat); } -static RegisterPrimOp primop_isFloat({ - .name = "__isFloat", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a float, and `false` otherwise. - )", - .fun = prim_isFloat, -}); - /* Determine whether the argument is a string. */ static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -510,15 +397,6 @@ static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, V v.mkBool(args[0]->type() == nString); } -static RegisterPrimOp primop_isString({ - .name = "__isString", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a string, and `false` otherwise. - )", - .fun = prim_isString, -}); - /* Determine whether the argument is a Boolean. */ static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -526,15 +404,6 @@ static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Val v.mkBool(args[0]->type() == nBool); } -static RegisterPrimOp primop_isBool({ - .name = "__isBool", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a bool, and `false` otherwise. - )", - .fun = prim_isBool, -}); - /* Determine whether the argument is a path. */ static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -542,15 +411,6 @@ static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Val v.mkBool(args[0]->type() == nPath); } -static RegisterPrimOp primop_isPath({ - .name = "__isPath", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a path, and `false` otherwise. - )", - .fun = prim_isPath, -}); - template static inline void withExceptionContext(Trace trace, Callable&& func) { @@ -703,98 +563,39 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a v.listElems()[n++] = i; } -static RegisterPrimOp primop_genericClosure(PrimOp { - .name = "__genericClosure", - .args = {"attrset"}, - .arity = 1, - .doc = R"( - Take an *attrset* with values named `startSet` and `operator` in order to - return a *list of attrsets* by starting with the `startSet` and recursively - applying the `operator` function to each `item`. The *attrsets* in the - `startSet` and the *attrsets* produced by `operator` must contain a value - named `key` which is comparable. The result is produced by calling `operator` - for each `item` with a value for `key` that has not been called yet including - newly produced `item`s. The function terminates when no new `item`s are - produced. The resulting *list of attrsets* contains only *attrsets* with a - unique key. For example, - ``` - builtins.genericClosure { - startSet = [ {key = 5;} ]; - operator = item: [{ - key = if (item.key / 2 ) * 2 == item.key - then item.key / 2 - else 3 * item.key + 1; - }]; - } - ``` - evaluates to - ``` - [ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ] - ``` - )", - .fun = prim_genericClosure, -}); +static void prim_break(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + if (state.debugRepl && !state.debugTraces.empty()) { + auto error = Error(ErrorInfo { + .level = lvlInfo, + .msg = HintFmt("breakpoint reached"), + .pos = state.positions[pos], + }); - -static RegisterPrimOp primop_break({ - .name = "break", - .args = {"v"}, - .doc = R"( - In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL. - Otherwise, return the argument `v`. - )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - if (state.debugRepl && !state.debugTraces.empty()) { - auto error = Error(ErrorInfo { - .level = lvlInfo, - .msg = HintFmt("breakpoint reached"), - .pos = state.positions[pos], - }); - - auto & dt = state.debugTraces.front(); - state.runDebugRepl(&error, dt.env, dt.expr); - } - - // Return the value we were passed. - v = *args[0]; + auto & dt = state.debugTraces.front(); + state.runDebugRepl(&error, dt.env, dt.expr); } -}); -static RegisterPrimOp primop_abort({ - .name = "abort", - .args = {"s"}, - .doc = R"( - Abort Nix expression evaluation and print the error message *s*. - )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - NixStringContext context; - auto s = state.coerceToString(pos, *args[0], context, - "while evaluating the error message passed to builtins.abort").toOwned(); - state.error("evaluation aborted with the following error message: '%1%'", s).debugThrow(); - } -}); + // Return the value we were passed. + v = *args[0]; +} -static RegisterPrimOp primop_throw({ - .name = "throw", - .args = {"s"}, - .doc = R"( - Throw an error message *s*. This usually aborts Nix expression - evaluation, but in `nix-env -qa` and other commands that try to - evaluate a set of derivations to get information about those - derivations, a derivation that throws an error is silently skipped - (which is not the case for `abort`). - )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - NixStringContext context; - auto s = state.coerceToString(pos, *args[0], context, - "while evaluating the error message passed to builtin.throw").toOwned(); - state.error(s).debugThrow(); - } -}); +static void prim_abort(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + NixStringContext context; + auto s = state.coerceToString(pos, *args[0], context, + "while evaluating the error message passed to builtins.abort").toOwned(); + state.error("evaluation aborted with the following error message: '%1%'", s).debugThrow(); +} + +static void prim_throw(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + NixStringContext context; + auto s = state.coerceToString(pos, *args[0], context, + "while evaluating the error message passed to builtin.throw").toOwned(); + state.error(s).debugThrow(); +} static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -824,38 +625,12 @@ static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value v.mkInt(ceil(value)); } -static RegisterPrimOp primop_ceil({ - .name = "__ceil", - .args = {"double"}, - .doc = R"( - Converts an IEEE-754 double-precision floating-point number (*double*) to - the next higher integer. - - If the datatype is neither an integer nor a "float", an evaluation error will be - thrown. - )", - .fun = prim_ceil, -}); - static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "while evaluating the first argument passed to builtins.floor"); v.mkInt(floor(value)); } -static RegisterPrimOp primop_floor({ - .name = "__floor", - .args = {"double"}, - .doc = R"( - Converts an IEEE-754 double-precision floating-point number (*double*) to - the next lower integer. - - If the datatype is neither an integer nor a "float", an evaluation error will be - thrown. - )", - .fun = prim_floor, -}); - /* Try evaluating the argument. Success => {success=true; value=something;}, * else => {success=false; value=false;} */ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -889,27 +664,6 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va v.mkAttrs(attrs); } -static RegisterPrimOp primop_tryEval({ - .name = "__tryEval", - .args = {"e"}, - .doc = R"( - Try to shallowly evaluate *e*. Return a set containing the - attributes `success` (`true` if *e* evaluated successfully, - `false` if an error was thrown) and `value`, equalling *e* if - successful and `false` otherwise. `tryEval` will only prevent - errors created by `throw` or `assert` from being thrown. - Errors `tryEval` will not catch are for example those created - by `abort` and type errors generated by builtins. Also note that - this doesn't evaluate *e* deeply, so `let e = { x = throw ""; }; - in (builtins.tryEval e).success` will be `true`. Using - `builtins.deepSeq` one can get the expected result: - `let e = { x = throw ""; }; in - (builtins.tryEval (builtins.deepSeq e e)).success` will be - `false`. - )", - .fun = prim_tryEval, -}); - /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -917,24 +671,6 @@ static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Val v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); } -static RegisterPrimOp primop_getEnv({ - .name = "__getEnv", - .args = {"s"}, - .doc = R"( - `getEnv` returns the value of the environment variable *s*, or an - empty string if the variable doesn't exist. This function should be - used with care, as it can introduce all sorts of nasty environment - dependencies in your Nix expression. - - `getEnv` is used in nixpkgs for evil impurities such as locating the file - `~/.config/nixpkgs/config.nix` which contains user-local settings for nixpkgs. - (That is, it does a `getEnv "HOME"` to locate the user's home directory.) - - When in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval), this function always returns an empty string. - )", - .fun = prim_getEnv, -}); - /* Evaluate the first argument, then return the second argument. */ static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -943,16 +679,6 @@ static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value v = *args[1]; } -static RegisterPrimOp primop_seq({ - .name = "__seq", - .args = {"e1", "e2"}, - .doc = R"( - Evaluate *e1*, then evaluate and return *e2*. This ensures that a - computation is strict in the value of *e1*. - )", - .fun = prim_seq, -}); - /* Evaluate the first argument deeply (i.e. recursing into lists and attrsets), then return the second argument. */ static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -962,17 +688,6 @@ static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Va v = *args[1]; } -static RegisterPrimOp primop_deepSeq({ - .name = "__deepSeq", - .args = {"e1", "e2"}, - .doc = R"( - This is like `seq e1 e2`, except that *e1* is evaluated *deeply*: - if it’s a list or set, its elements or attributes are also - evaluated recursively. - )", - .fun = prim_deepSeq, -}); - /* Evaluate the first expression and print it on standard error. Then return the second expression. Useful for debugging. */ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -990,23 +705,6 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu v = *args[1]; } -static RegisterPrimOp primop_trace({ - .name = "__trace", - .args = {"e1", "e2"}, - .doc = R"( - Evaluate *e1* and print its abstract syntax representation on - standard error. Then return *e2*. This function is useful for - debugging. - - If the - [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace) - option is set to `true` and the `--debugger` flag is given, the - interactive debugger will be started when `trace` is called (like - [`break`](@docroot@/language/builtins.md#builtins-break)). - )", - .fun = prim_trace, -}); - /* Takes two arguments and evaluates to the second one. Used as the * builtins.traceVerbose implementation when --trace-verbose is not enabled @@ -1438,17 +1136,6 @@ static void prim_placeholder(EvalState & state, const PosIdx pos, Value * * args v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.placeholder"))); } -static RegisterPrimOp primop_placeholder({ - .name = "placeholder", - .args = {"output"}, - .doc = R"( - Return a placeholder string for the specified *output* that will be - substituted by the corresponding output path at build time. Typical - outputs would be `"out"`, `"bin"` or `"dev"`. - )", - .fun = prim_placeholder, -}); - /************************************************************* * Paths @@ -1463,16 +1150,6 @@ static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Val v.mkString(path.path.abs(), context); } -static RegisterPrimOp primop_toPath({ - .name = "__toPath", - .args = {"s"}, - .doc = R"( - **DEPRECATED.** Use `/. + "/path"` to convert a string into an absolute - path. For relative paths, use `./. + "/path"`. - )", - .fun = prim_toPath, -}); - /* Allow a valid store path to be used in an expression. This is useful in some generated expressions such as in nix-push, which generates a call to a function with an already existing store path @@ -1506,27 +1183,6 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, v.mkString(path.abs(), context); } -static RegisterPrimOp primop_storePath({ - .name = "__storePath", - .args = {"path"}, - .doc = R"( - This function allows you to define a dependency on an already - existing store path. For example, the derivation attribute `src - = builtins.storePath /nix/store/f1d18v1y…-source` causes the - derivation to depend on the specified path, which must exist or - be substitutable. Note that this differs from a plain path - (e.g. `src = /nix/store/f1d18v1y…-source`) in that the latter - causes the path to be *copied* again to the Nix store, resulting - in a new path (e.g. `/nix/store/ld01dnzc…-source-source`). - - Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). - Lix may change this, tracking issue: - - See also [`builtins.fetchClosure`](#builtins-fetchClosure). - )", - .fun = prim_storePath, -}); - static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto & arg = *args[0]; @@ -1560,16 +1216,6 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, } } -static RegisterPrimOp primop_pathExists({ - .name = "__pathExists", - .args = {"path"}, - .doc = R"( - Return `true` if the path *path* exists at evaluation time, and - `false` otherwise. - )", - .fun = prim_pathExists, -}); - /* Return the base name of the given string, i.e., everything following the last slash. */ static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -1580,17 +1226,6 @@ static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, false, false)), context); } -static RegisterPrimOp primop_baseNameOf({ - .name = "baseNameOf", - .args = {"s"}, - .doc = R"( - Return the *base name* of the string *s*, that is, everything - following the final slash in the string. This is similar to the GNU - `basename` command. - )", - .fun = prim_baseNameOf, -}); - /* Return the directory of the given path, i.e., everything before the last slash. Return either a path or a string depending on the type of the argument. */ @@ -1610,17 +1245,6 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Valu } } -static RegisterPrimOp primop_dirOf({ - .name = "dirOf", - .args = {"s"}, - .doc = R"( - Return the directory part of the string *s*, that is, everything - before the final slash in the string. This is similar to the GNU - `dirname` command. - )", - .fun = prim_dirOf, -}); - /* Return the contents of a file as a string. */ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -1651,15 +1275,6 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V v.mkString(s, context); } -static RegisterPrimOp primop_readFile({ - .name = "__readFile", - .args = {"path"}, - .doc = R"( - Return the contents of the file *path* as a string. - )", - .fun = prim_readFile, -}); - /* Find a file in the Nix search path. Used to implement paths, which are desugared to 'findFile __nixPath "x"'. */ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -1705,55 +1320,6 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); } -static RegisterPrimOp primop_findFile(PrimOp { - .name = "__findFile", - .args = {"search path", "lookup path"}, - .doc = R"( - Look up the given path with the given search path. - - A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes, `prefix`, and `path`. - `prefix` is a relative path. - `path` denotes a file system location; the exact syntax depends on the command line interface. - - Examples of search path attribute sets: - - - ``` - { - prefix = "nixos-config"; - path = "/etc/nixos/configuration.nix"; - } - ``` - - - ``` - { - prefix = ""; - path = "/nix/var/nix/profiles/per-user/root/channels"; - } - ``` - - The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match. - - This is the process for each entry: - If the lookup path matches `prefix`, then the remainder of the lookup path (the "suffix") is searched for within the directory denoted by `patch`. - Note that the `path` may need to be downloaded at this point to look inside. - If the suffix is found inside that directory, then the entry is a match; - the combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - - The syntax - - ```nix - - ``` - - is equivalent to: - - ```nix - builtins.findFile builtins.nixPath "nixpkgs" - ``` - )", - .fun = prim_findFile, -}); - /* Return the cryptographic hash of a file in base-16. */ static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -1767,17 +1333,6 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, V v.mkString(hashString(*ht, path.readFile()).to_string(Base::Base16, false)); } -static RegisterPrimOp primop_hashFile({ - .name = "__hashFile", - .args = {"type", "p"}, - .doc = R"( - Return a base-16 representation of the cryptographic hash of the - file at path *p*. The hash algorithm specified by *type* must be one - of `"md5"`, `"sha1"`, `"sha256"` or `"sha512"`. - )", - .fun = prim_hashFile, -}); - static std::string_view fileTypeToString(InputAccessor::Type type) { return @@ -1794,16 +1349,6 @@ static void prim_readFileType(EvalState & state, const PosIdx pos, Value * * arg v.mkString(fileTypeToString(path.lstat().type)); } -static RegisterPrimOp primop_readFileType({ - .name = "__readFileType", - .args = {"p"}, - .doc = R"( - Determine the directory entry type of a filesystem node, being - one of "directory", "regular", "symlink", or "unknown". - )", - .fun = prim_readFileType, -}); - /* Read a directory (without . or ..) */ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -1842,25 +1387,6 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va v.mkAttrs(attrs); } -static RegisterPrimOp primop_readDir({ - .name = "__readDir", - .args = {"path"}, - .doc = R"( - Return the contents of the directory *path* as a set mapping - directory entries to the corresponding file type. For instance, if - directory `A` contains a regular file `B` and another directory - `C`, then `builtins.readDir ./A` will return the set - - ```nix - { B = "regular"; C = "directory"; } - ``` - - The possible values for the file type are `"regular"`, - `"directory"`, `"symlink"` and `"unknown"`. - )", - .fun = prim_readDir, -}); - /* Extend single element string context with another output. */ static void prim_outputOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -1876,33 +1402,6 @@ static void prim_outputOf(EvalState & state, const PosIdx pos, Value * * args, V v); } -static RegisterPrimOp primop_outputOf({ - .name = "__outputOf", - .args = {"derivation-reference", "output-name"}, - .doc = R"( - Return the output path of a derivation, literally or using a placeholder if needed. - - If the derivation has a statically-known output path (i.e. the derivation output is input-addressed, or fixed content-addresed), the output path will just be returned. - But if the derivation is content-addressed or if the derivation is itself not-statically produced (i.e. is the output of another derivation), a placeholder will be returned instead. - - *`derivation reference`* must be a string that may contain a regular store path to a derivation, or may be a placeholder reference. If the derivation is produced by a derivation, you must explicitly select `drv.outPath`. - This primop can be chained arbitrarily deeply. - For instance, - - ```nix - builtins.outputOf - (builtins.outputOf myDrv "out) - "out" - ``` - - will return a placeholder for the output of the output of `myDrv`. - - This primop corresponds to the `^` sigil for derivable paths, e.g. as part of installable syntax on the command line. - )", - .fun = prim_outputOf, - .experimentalFeature = Xp::DynamicDerivations, -}); - /************************************************************* * Creating files *************************************************************/ @@ -1919,103 +1418,6 @@ static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Valu v.mkString(out.str(), context); } -static RegisterPrimOp primop_toXML({ - .name = "__toXML", - .args = {"e"}, - .doc = R"( - Return a string containing an XML representation of *e*. The main - application for `toXML` is to communicate information with the - builder in a more structured format than plain environment - variables. - - Here is an example where this is the case: - - ```nix - { stdenv, fetchurl, libxslt, jira, uberwiki }: - - stdenv.mkDerivation (rec { - name = "web-server"; - - buildInputs = [ libxslt ]; - - builder = builtins.toFile "builder.sh" " - source $stdenv/setup - mkdir $out - echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml ① - "; - - stylesheet = builtins.toFile "stylesheet.xsl" ② - " - - - - - - - - - - - - - "; - - servlets = builtins.toXML [ ③ - { path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; } - { path = "/wiki"; war = uberwiki + "/uberwiki.war"; } - ]; - }) - ``` - - The builder is supposed to generate the configuration file for a - [Jetty servlet container](http://jetty.mortbay.org/). A servlet - container contains a number of servlets (`*.war` files) each - exported under a specific URI prefix. So the servlet configuration - is a list of sets containing the `path` and `war` of the servlet - (①). This kind of information is difficult to communicate with the - normal method of passing information through an environment - variable, which just concatenates everything together into a - string (which might just work in this case, but wouldn’t work if - fields are optional or contain lists themselves). Instead the Nix - expression is converted to an XML representation with `toXML`, - which is unambiguous and can easily be processed with the - appropriate tools. For instance, in the example an XSLT stylesheet - (at point ②) is applied to it (at point ①) to generate the XML - configuration file for the Jetty server. The XML representation - produced at point ③ by `toXML` is as follows: - - ```xml - - - - - - - - - - - - - - - - - - - - - - ``` - - Note that we used the `toFile` built-in to write the builder and - the stylesheet “inline” in the Nix expression. The path of the - stylesheet is spliced into the builder using the syntax `xsltproc - ${stylesheet}`. - )", - .fun = prim_toXML, -}); - /* Convert the argument (which can be any Nix expression) to a JSON string. Not all Nix expressions can be sensibly or completely represented (e.g., functions). */ @@ -2027,20 +1429,6 @@ static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Val v.mkString(out.str(), context); } -static RegisterPrimOp primop_toJSON({ - .name = "__toJSON", - .args = {"e"}, - .doc = R"( - Return a string containing a JSON representation of *e*. Strings, - integers, floats, booleans, nulls and lists are mapped to their JSON - equivalents. Sets (except derivations) are represented as objects. - Derivations are translated to a JSON string containing the - derivation’s output path. Paths are copied to the store and - represented as a JSON string of the resulting store path. - )", - .fun = prim_toJSON, -}); - /* Parse a JSON string to a value. */ static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -2053,21 +1441,6 @@ static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, V } } -static RegisterPrimOp primop_fromJSON({ - .name = "__fromJSON", - .args = {"e"}, - .doc = R"( - Convert a JSON string to a Nix value. For example, - - ```nix - builtins.fromJSON ''{"x": [1, 2, 3], "y": null}'' - ``` - - returns the value `{ x = [ 1 2 3 ]; y = null; }`. - )", - .fun = prim_fromJSON, -}); - /* Store a string in the Nix store as a source file that can be used as an input by derivations. */ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -2102,83 +1475,6 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val state.allowAndSetStorePathString(storePath, v); } -static RegisterPrimOp primop_toFile({ - .name = "__toFile", - .args = {"name", "s"}, - .doc = R"( - Store the string *s* in a file in the Nix store and return its - path. The file has suffix *name*. This file can be used as an - input to derivations. One application is to write builders - “inline”. For instance, the following Nix expression combines the - Nix expression for GNU Hello and its build script into one file: - - ```nix - { stdenv, fetchurl, perl }: - - stdenv.mkDerivation { - name = "hello-2.1.1"; - - builder = builtins.toFile "builder.sh" " - source $stdenv/setup - - PATH=$perl/bin:$PATH - - tar xvfz $src - cd hello-* - ./configure --prefix=$out - make - make install - "; - - src = fetchurl { - url = "http://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz"; - sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465"; - }; - inherit perl; - } - ``` - - It is even possible for one file to refer to another, e.g., - - ```nix - builder = let - configFile = builtins.toFile "foo.conf" " - # This is some dummy configuration file. - ... - "; - in builtins.toFile "builder.sh" " - source $stdenv/setup - ... - cp ${configFile} $out/etc/foo.conf - "; - ``` - - Note that `${configFile}` is a - [string interpolation](@docroot@/language/values.md#type-string), so the result of the - expression `configFile` - (i.e., a path like `/nix/store/m7p7jfny445k...-foo.conf`) will be - spliced into the resulting string. - - It is however *not* allowed to have files mutually referring to each - other, like so: - - ```nix - let - foo = builtins.toFile "foo" "...${bar}..."; - bar = builtins.toFile "bar" "...${foo}..."; - in foo - ``` - - This is not allowed because it would cause a cyclic dependency in - the computation of the cryptographic hashes for `foo` and `bar`. - - It is also not possible to reference the result of a derivation. If - you are using Nixpkgs, the `writeTextFile` function is able to do - that. - )", - .fun = prim_toFile, -}); - static void addPath( EvalState & state, const PosIdx pos, @@ -2269,61 +1565,6 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg addPath(state, pos, path.baseName(), path.path.abs(), args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); } -static RegisterPrimOp primop_filterSource({ - .name = "__filterSource", - .args = {"e1", "e2"}, - .doc = R"( - > **Warning** - > - > `filterSource` should not be used to filter store paths. Since - > `filterSource` uses the name of the input directory while naming - > the output directory, doing so will produce a directory name in - > the form of `--`, where `-` is - > the name of the input directory. Since `` depends on the - > unfiltered directory, the name of the output directory will - > indirectly depend on files that are filtered out by the - > function. This will trigger a rebuild even when a filtered out - > file is changed. Use `builtins.path` instead, which allows - > specifying the name of the output directory. - - This function allows you to copy sources into the Nix store while - filtering certain files. For instance, suppose that you want to use - the directory `source-dir` as an input to a Nix expression, e.g. - - ```nix - stdenv.mkDerivation { - ... - src = ./source-dir; - } - ``` - - However, if `source-dir` is a Subversion working copy, then all - those annoying `.svn` subdirectories will also be copied to the - store. Worse, the contents of those directories may change a lot, - causing lots of spurious rebuilds. With `filterSource` you can - filter out the `.svn` directories: - - ```nix - src = builtins.filterSource - (path: type: type != "directory" || baseNameOf path != ".svn") - ./source-dir; - ``` - - Thus, the first argument *e1* must be a predicate function that is - called for each regular file, directory or symlink in the source - tree *e2*. If the function returns `true`, the file is copied to the - Nix store, otherwise it is omitted. The function is called with two - arguments. The first is the full path of the file. The second is a - string that identifies the type of the file, which is either - `"regular"`, `"directory"`, `"symlink"` or `"unknown"` (for other - kinds of files such as device nodes or fifos — but note that those - cannot be copied to the Nix store, so if the predicate returns - `true` for them, the copy will fail). If you exclude a directory, - the entire corresponding subtree of *e2* will be excluded. - )", - .fun = prim_filterSource, -}); - static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v) { std::optional path; @@ -2363,41 +1604,6 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value addPath(state, pos, name, path->path.abs(), filterFun, method, expectedHash, v, context); } -static RegisterPrimOp primop_path({ - .name = "__path", - .args = {"args"}, - .doc = R"( - An enrichment of the built-in path type, based on the attributes - present in *args*. All are optional except `path`: - - - path\ - The underlying path. - - - name\ - The name of the path when added to the store. This can used to - reference paths that have nix-illegal characters in their names, - like `@`. - - - filter\ - A function of the type expected by `builtins.filterSource`, - with the same semantics. - - - recursive\ - When `false`, when `path` is added to the store it is with a - flat hash, rather than a hash of the NAR serialization of the - file. Thus, `path` must refer to a regular file, not a - directory. This allows similar behavior to `fetchurl`. Defaults - to `true`. - - - sha256\ - When provided, this is the expected hash of the file at the - path. Evaluation will fail if the hash is incorrect, and - providing a hash allows `builtins.path` to be used even when the - `pure-eval` nix config option is on. - )", - .fun = prim_path, -}); - /************************************************************* * Sets @@ -2420,17 +1626,6 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; }); } -static RegisterPrimOp primop_attrNames({ - .name = "__attrNames", - .args = {"set"}, - .doc = R"( - Return the names of the attributes in the set *set* in an - alphabetically sorted list. For instance, `builtins.attrNames { y - = 1; x = "foo"; }` evaluates to `[ "x" "y" ]`. - )", - .fun = prim_attrNames, -}); - /* Return the values of the attributes in a set as a list, in the same order as attrNames. */ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -2457,16 +1652,6 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast) } -static RegisterPrimOp primop_attrValues({ - .name = "__attrValues", - .args = {"set"}, - .doc = R"( - Return the values of the attributes in the set *set* in the order - corresponding to the sorted attribute names. - )", - .fun = prim_attrValues, -}); - /* Dynamic version of the `.' operator. */ void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -2484,18 +1669,6 @@ void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v v = *i->value; } -static RegisterPrimOp primop_getAttr({ - .name = "__getAttr", - .args = {"s", "set"}, - .doc = R"( - `getAttr` returns the attribute named *s* from *set*. Evaluation - aborts if the attribute doesn’t exist. This is a dynamic version of - the `.` operator, since *s* is an expression rather than an - identifier. - )", - .fun = prim_getAttr, -}); - /* Return position information of the specified attribute. */ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -2570,17 +1743,6 @@ static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Va v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); } -static RegisterPrimOp primop_hasAttr({ - .name = "__hasAttr", - .args = {"s", "set"}, - .doc = R"( - `hasAttr` returns `true` if *set* has an attribute named *s*, and - `false` otherwise. This is a dynamic version of the `?` operator, - since *s* is an expression rather than an identifier. - )", - .fun = prim_hasAttr, -}); - /* Determine whether the argument is a set. */ static void prim_isAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -2588,15 +1750,6 @@ static void prim_isAttrs(EvalState & state, const PosIdx pos, Value * * args, Va v.mkBool(args[0]->type() == nAttrs); } -static RegisterPrimOp primop_isAttrs({ - .name = "__isAttrs", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a set, and `false` otherwise. - )", - .fun = prim_isAttrs, -}); - static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.removeAttrs"); @@ -2625,22 +1778,6 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args v.mkAttrs(attrs.alreadySorted()); } -static RegisterPrimOp primop_removeAttrs({ - .name = "removeAttrs", - .args = {"set", "list"}, - .doc = R"( - Remove the attributes listed in *list* from *set*. The attributes - don’t have to exist in *set*. For instance, - - ```nix - removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ] - ``` - - evaluates to `{ y = 2; }`. - )", - .fun = prim_removeAttrs, -}); - /* Builds a set from a list specifying (name, value) pairs. To be precise, a list [{name = "name1"; value = value1;} ... {name = "nameN"; value = valueN;}] is transformed to {name1 = value1; @@ -2671,37 +1808,6 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args v.mkAttrs(attrs); } -static RegisterPrimOp primop_listToAttrs({ - .name = "__listToAttrs", - .args = {"e"}, - .doc = R"( - Construct a set from a list specifying the names and values of each - attribute. Each element of the list should be a set consisting of a - string-valued attribute `name` specifying the name of the attribute, - and an attribute `value` specifying its value. - - In case of duplicate occurrences of the same name, the first - takes precedence. - - Example: - - ```nix - builtins.listToAttrs - [ { name = "foo"; value = 123; } - { name = "bar"; value = 456; } - { name = "bar"; value = 420; } - ] - ``` - - evaluates to - - ```nix - { foo = 123; bar = 456; } - ``` - )", - .fun = prim_listToAttrs, -}); - static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs"); @@ -2768,18 +1874,6 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a v.mkAttrs(attrs.alreadySorted()); } -static RegisterPrimOp primop_intersectAttrs({ - .name = "__intersectAttrs", - .args = {"e1", "e2"}, - .doc = R"( - Return a set consisting of the attributes in the set *e2* which have the - same name as some attribute in *e1*. - - Performs in O(*n* log *m*) where *n* is the size of the smaller set and *m* the larger set's size. - )", - .fun = prim_intersectAttrs, -}); - static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.catAttrs")); @@ -2800,23 +1894,6 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V v.listElems()[n] = res[n]; } -static RegisterPrimOp primop_catAttrs({ - .name = "__catAttrs", - .args = {"attr", "list"}, - .doc = R"( - Collect each attribute named *attr* from a list of attribute - sets. Attrsets that don't contain the named attribute are - ignored. For example, - - ```nix - builtins.catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] - ``` - - evaluates to `[1 2]`. - )", - .fun = prim_catAttrs, -}); - static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); @@ -2839,23 +1916,6 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg v.mkAttrs(attrs); } -static RegisterPrimOp primop_functionArgs({ - .name = "__functionArgs", - .args = {"f"}, - .doc = R"( - Return a set containing the names of the formal arguments expected - by the function *f*. The value of each attribute is a Boolean - denoting whether the corresponding argument has a default value. For - instance, `functionArgs ({ x, y ? 123}: ...) = { x = false; y = - true; }`. - - "Formal argument" here refers to the attributes pattern-matched by - the function. Plain lambdas are not included, e.g. `functionArgs (x: - ...) = { }`. - )", - .fun = prim_functionArgs, -}); - /* */ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -2874,21 +1934,6 @@ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, V v.mkAttrs(attrs.alreadySorted()); } -static RegisterPrimOp primop_mapAttrs({ - .name = "__mapAttrs", - .args = {"f", "attrset"}, - .doc = R"( - Apply function *f* to every element of *attrset*. For example, - - ```nix - builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; } - ``` - - evaluates to `{ a = 10; b = 20; }`. - )", - .fun = prim_mapAttrs, -}); - static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * args, Value & v) { // we will first count how many values are present for each given key. @@ -2937,38 +1982,6 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg } } -static RegisterPrimOp primop_zipAttrsWith({ - .name = "__zipAttrsWith", - .args = {"f", "list"}, - .doc = R"( - Transpose a list of attribute sets into an attribute set of lists, - then apply `mapAttrs`. - - `f` receives two arguments: the attribute name and a non-empty - list of all values encountered for that attribute name. - - The result is an attribute set where the attribute names are the - union of the attribute names in each element of `list`. The attribute - values are the return values of `f`. - - ```nix - builtins.zipAttrsWith - (name: values: { inherit name values; }) - [ { a = "x"; } { a = "y"; b = "z"; } ] - ``` - - evaluates to - - ``` - { - a = { name = "a"; values = [ "x" "y" ]; }; - b = { name = "b"; values = [ "z" ]; }; - } - ``` - )", - .fun = prim_zipAttrsWith, -}); - /************************************************************* * Lists @@ -2982,15 +1995,6 @@ static void prim_isList(EvalState & state, const PosIdx pos, Value * * args, Val v.mkBool(args[0]->type() == nList); } -static RegisterPrimOp primop_isList({ - .name = "__isList", - .args = {"e"}, - .doc = R"( - Return `true` if *e* evaluates to a list, and `false` otherwise. - )", - .fun = prim_isList, -}); - static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v) { state.forceList(list, pos, "while evaluating the first argument passed to builtins.elemAt"); @@ -3010,33 +2014,12 @@ static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Val elemAt(state, pos, *args[0], elem, v); } -static RegisterPrimOp primop_elemAt({ - .name = "__elemAt", - .args = {"xs", "n"}, - .doc = R"( - Return element *n* from the list *xs*. Elements are counted starting - from 0. A fatal error occurs if the index is out of bounds. - )", - .fun = prim_elemAt, -}); - /* Return the first element of a list. */ static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v) { elemAt(state, pos, *args[0], 0, v); } -static RegisterPrimOp primop_head({ - .name = "__head", - .args = {"list"}, - .doc = R"( - Return the first element of a list; abort evaluation if the argument - isn’t a list or is an empty list. You can test whether a list is - empty by comparing it with `[]`. - )", - .fun = prim_head, -}); - /* Return a list consisting of everything but the first element of a list. Warning: this function takes O(n) time, so you probably don't want to use it! */ @@ -3051,22 +2034,6 @@ static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value v.listElems()[n] = args[0]->listElems()[n + 1]; } -static RegisterPrimOp primop_tail({ - .name = "__tail", - .args = {"list"}, - .doc = R"( - Return the second to last elements of a list; abort evaluation if - the argument isn’t a list or is an empty list. - - > **Warning** - > - > This function should generally be avoided since it's inefficient: - > unlike Haskell's `tail`, it takes O(n) time, so recursing over a - > list by repeatedly calling `tail` takes O(n^2) time. - )", - .fun = prim_tail, -}); - /* Apply a function to every element of a list. */ static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -3085,22 +2052,6 @@ static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value args[0], args[1]->listElems()[n]); } -static RegisterPrimOp primop_map({ - .name = "map", - .args = {"f", "list"}, - .doc = R"( - Apply the function *f* to each element in the list *list*. For - example, - - ```nix - map (x: "foo" + x) [ "bar" "bla" "abc" ] - ``` - - evaluates to `[ "foobar" "foobla" "fooabc" ]`. - )", - .fun = prim_map, -}); - /* Filter a list using a predicate; that is, return a list containing every element from the list for which the predicate function returns true. */ @@ -3136,16 +2087,6 @@ static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Val } } -static RegisterPrimOp primop_filter({ - .name = "__filter", - .args = {"f", "list"}, - .doc = R"( - Return a list consisting of the elements of *list* for which the - function *f* returns `true`. - )", - .fun = prim_filter, -}); - /* Return true if a list contains a given element. */ static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -3159,16 +2100,6 @@ static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value v.mkBool(res); } -static RegisterPrimOp primop_elem({ - .name = "__elem", - .args = {"x", "xs"}, - .doc = R"( - Return `true` if a value equal to *x* occurs in the list *xs*, and - `false` otherwise. - )", - .fun = prim_elem, -}); - /* Concatenate a list of lists. */ static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -3176,15 +2107,6 @@ static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos, "while evaluating a value of the list passed to builtins.concatLists"); } -static RegisterPrimOp primop_concatLists({ - .name = "__concatLists", - .args = {"lists"}, - .doc = R"( - Concatenate a list of lists into a single list. - )", - .fun = prim_concatLists, -}); - /* Return the length of a list. This is an O(1) time operation. */ static void prim_length(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -3192,15 +2114,6 @@ static void prim_length(EvalState & state, const PosIdx pos, Value * * args, Val v.mkInt(args[0]->listSize()); } -static RegisterPrimOp primop_length({ - .name = "__length", - .args = {"e"}, - .doc = R"( - Return the length of the list *e*. - )", - .fun = prim_length, -}); - /* Reduce a list by applying a binary operator, from left to right. The operator is applied strictly. */ static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -3223,19 +2136,6 @@ static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args } } -static RegisterPrimOp primop_foldlStrict({ - .name = "__foldl'", - .args = {"op", "nul", "list"}, - .doc = R"( - Reduce a list by applying a binary operator, from left to right, - e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2) - ...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6. - The return value of each application of `op` is evaluated immediately, - even for intermediate values. - )", - .fun = prim_foldlStrict, -}); - static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceFunction(*args[0], pos, std::string("while evaluating the first argument passed to builtins.") + (any ? "any" : "all")); @@ -3264,31 +2164,11 @@ static void prim_any(EvalState & state, const PosIdx pos, Value * * args, Value anyOrAll(true, state, pos, args, v); } -static RegisterPrimOp primop_any({ - .name = "__any", - .args = {"pred", "list"}, - .doc = R"( - Return `true` if the function *pred* returns `true` for at least one - element of *list*, and `false` otherwise. - )", - .fun = prim_any, -}); - static void prim_all(EvalState & state, const PosIdx pos, Value * * args, Value & v) { anyOrAll(false, state, pos, args, v); } -static RegisterPrimOp primop_all({ - .name = "__all", - .args = {"pred", "list"}, - .doc = R"( - Return `true` if the function *pred* returns `true` for all elements - of *list*, and `false` otherwise. - )", - .fun = prim_all, -}); - static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto len_ = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.genList").value; @@ -3310,22 +2190,6 @@ static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Va } } -static RegisterPrimOp primop_genList({ - .name = "__genList", - .args = {"generator", "length"}, - .doc = R"( - Generate list of size *length*, with each element *i* equal to the - value returned by *generator* `i`. For example, - - ```nix - builtins.genList (x: x * x) 5 - ``` - - returns the list `[ 0 1 4 9 16 ]`. - )", - .fun = prim_genList, -}); - static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v); @@ -3370,27 +2234,6 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value std::stable_sort(v.listElems(), v.listElems() + len, comparator); } -static RegisterPrimOp primop_sort({ - .name = "__sort", - .args = {"comparator", "list"}, - .doc = R"( - Return *list* in sorted order. It repeatedly calls the function - *comparator* with two elements. The comparator should return `true` - if the first element is less than the second, and `false` otherwise. - For example, - - ```nix - builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ] - ``` - - produces the list `[ 42 77 147 249 483 526 ]`. - - This is a stable sort: it preserves the relative order of elements - deemed equal by the comparator. - )", - .fun = prim_sort, -}); - static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.partition"); @@ -3428,29 +2271,6 @@ static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, v.mkAttrs(attrs); } -static RegisterPrimOp primop_partition({ - .name = "__partition", - .args = {"pred", "list"}, - .doc = R"( - Given a predicate function *pred*, this function returns an - attrset containing a list named `right`, containing the elements - in *list* for which *pred* returned `true`, and a list named - `wrong`, containing the elements for which it returned - `false`. For example, - - ```nix - builtins.partition (x: x > 10) [1 23 9 3 42] - ``` - - evaluates to - - ```nix - { right = [ 23 42 ]; wrong = [ 1 9 3 ]; } - ``` - )", - .fun = prim_partition, -}); - static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.groupBy"); @@ -3479,30 +2299,6 @@ static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Va v.mkAttrs(attrs2.alreadySorted()); } -static RegisterPrimOp primop_groupBy({ - .name = "__groupBy", - .args = {"f", "list"}, - .doc = R"( - Groups elements of *list* together by the string returned from the - function *f* called on each element. It returns an attribute set - where each attribute value contains the elements of *list* that are - mapped to the same corresponding attribute name returned by *f*. - - For example, - - ```nix - builtins.groupBy (builtins.substring 0 1) ["foo" "bar" "baz"] - ``` - - evaluates to - - ```nix - { b = [ "bar" "baz" ]; f = [ "foo" ]; } - ``` - )", - .fun = prim_groupBy, -}); - static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.concatMap"); @@ -3530,16 +2326,6 @@ static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, } } -static RegisterPrimOp primop_concatMap({ - .name = "__concatMap", - .args = {"f", "list"}, - .doc = R"( - This function is equivalent to `builtins.concatLists (map f list)` - but is more efficient. - )", - .fun = prim_concatMap, -}); - /************************************************************* * Integer arithmetic @@ -3566,15 +2352,6 @@ static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value } } -static RegisterPrimOp primop_add({ - .name = "__add", - .args = {"e1", "e2"}, - .doc = R"( - Return the sum of the numbers *e1* and *e2*. - )", - .fun = prim_add, -}); - static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); @@ -3596,15 +2373,6 @@ static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value } } -static RegisterPrimOp primop_sub({ - .name = "__sub", - .args = {"e1", "e2"}, - .doc = R"( - Return the difference between the numbers *e1* and *e2*. - )", - .fun = prim_sub, -}); - static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); @@ -3626,15 +2394,6 @@ static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value } } -static RegisterPrimOp primop_mul({ - .name = "__mul", - .args = {"e1", "e2"}, - .doc = R"( - Return the product of the numbers *e1* and *e2*. - )", - .fun = prim_mul, -}); - static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); @@ -3659,15 +2418,6 @@ static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value } } -static RegisterPrimOp primop_div({ - .name = "__div", - .args = {"e1", "e2"}, - .doc = R"( - Return the quotient of the numbers *e1* and *e2*. - )", - .fun = prim_div, -}); - static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitAnd"); @@ -3675,15 +2425,6 @@ static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Val v.mkInt(i1.value & i2.value); } -static RegisterPrimOp primop_bitAnd({ - .name = "__bitAnd", - .args = {"e1", "e2"}, - .doc = R"( - Return the bitwise AND of the integers *e1* and *e2*. - )", - .fun = prim_bitAnd, -}); - static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitOr"); @@ -3692,15 +2433,6 @@ static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Valu v.mkInt(i1.value | i2.value); } -static RegisterPrimOp primop_bitOr({ - .name = "__bitOr", - .args = {"e1", "e2"}, - .doc = R"( - Return the bitwise OR of the integers *e1* and *e2*. - )", - .fun = prim_bitOr, -}); - static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitXor"); @@ -3709,15 +2441,6 @@ static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Val v.mkInt(i1.value ^ i2.value); } -static RegisterPrimOp primop_bitXor({ - .name = "__bitXor", - .args = {"e1", "e2"}, - .doc = R"( - Return the bitwise XOR of the integers *e1* and *e2*. - )", - .fun = prim_bitXor, -}); - static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); @@ -3727,17 +2450,6 @@ static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, V v.mkBool(comp(args[0], args[1])); } -static RegisterPrimOp primop_lessThan({ - .name = "__lessThan", - .args = {"e1", "e2"}, - .doc = R"( - Return `true` if the number *e1* is less than the number *e2*, and - `false` otherwise. Evaluation aborts if either *e1* or *e2* does not - evaluate to a number. - )", - .fun = prim_lessThan, -}); - /************************************************************* * String manipulation @@ -3756,30 +2468,6 @@ static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, V v.mkString(*s, context); } -static RegisterPrimOp primop_toString({ - .name = "toString", - .args = {"e"}, - .doc = R"( - Convert the expression *e* to a string. *e* can be: - - - A string (in which case the string is returned unmodified). - - - A path (e.g., `toString /foo/bar` yields `"/foo/bar"`. - - - A set containing `{ __toString = self: ...; }` or `{ outPath = ...; }`. - - - An integer. - - - A list, in which case the string representations of its elements - are joined with spaces. - - - A Boolean (`false` yields `""`, `true` yields `"1"`). - - - `null`, which yields the empty string. - )", - .fun = prim_toString, -}); - /* `substring start len str' returns the substring of `str' starting at character position `min(start, stringLength str)' inclusive and ending at `min(start + len, stringLength str)'. `start' must be @@ -3816,26 +2504,6 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context); } -static RegisterPrimOp primop_substring({ - .name = "__substring", - .args = {"start", "len", "s"}, - .doc = R"( - Return the substring of *s* from character position *start* - (zero-based) up to but not including *start + len*. If *start* is - greater than the length of the string, an empty string is returned, - and if *start + len* lies beyond the end of the string, only the - substring up to the end of the string is returned. *start* must be - non-negative. For example, - - ```nix - builtins.substring 0 3 "nixos" - ``` - - evaluates to `"nix"`. - )", - .fun = prim_substring, -}); - static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v) { NixStringContext context; @@ -3843,16 +2511,6 @@ static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * arg v.mkInt(NixInt::Inner(s->size())); } -static RegisterPrimOp primop_stringLength({ - .name = "__stringLength", - .args = {"e"}, - .doc = R"( - Return the length of the string *e*. If *e* is not a string, - evaluation is aborted. - )", - .fun = prim_stringLength, -}); - /* Return the cryptographic hash of a string in base-16. */ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -3867,17 +2525,6 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, v.mkString(hashString(*ht, s).to_string(Base::Base16, false)); } -static RegisterPrimOp primop_hashString({ - .name = "__hashString", - .args = {"type", "s"}, - .doc = R"( - Return a base-16 representation of the cryptographic hash of string - *s*. The hash algorithm specified by *type* must be one of `"md5"`, - `"sha1"`, `"sha256"` or `"sha512"`. - )", - .fun = prim_hashString, -}); - struct RegexCache { // TODO use C++20 transparent comparison when available @@ -3939,42 +2586,6 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) } } -static RegisterPrimOp primop_match({ - .name = "__match", - .args = {"regex", "str"}, - .doc = R"s( - Returns a list if the [extended POSIX regular - expression](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04) - *regex* matches *str* precisely, otherwise returns `null`. Each item - in the list is a regex group. - - ```nix - builtins.match "ab" "abc" - ``` - - Evaluates to `null`. - - ```nix - builtins.match "abc" "abc" - ``` - - Evaluates to `[ ]`. - - ```nix - builtins.match "a(b)(c)" "abc" - ``` - - Evaluates to `[ "b" "c" ]`. - - ```nix - builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO " - ``` - - Evaluates to `[ "FOO" ]`. - )s", - .fun = prim_match, -}); - /* Split a string with a regular expression, and return a list of the non-matching parts interleaved by the lists of the matching groups. */ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -4041,43 +2652,6 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) } } -static RegisterPrimOp primop_split({ - .name = "__split", - .args = {"regex", "str"}, - .doc = R"s( - Returns a list composed of non matched strings interleaved with the - lists of the [extended POSIX regular - expression](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04) - *regex* matches of *str*. Each item in the lists of matched - sequences is a regex group. - - ```nix - builtins.split "(a)b" "abc" - ``` - - Evaluates to `[ "" [ "a" ] "c" ]`. - - ```nix - builtins.split "([ac])" "abc" - ``` - - Evaluates to `[ "" [ "a" ] "b" [ "c" ] "" ]`. - - ```nix - builtins.split "(a)|(c)" "abc" - ``` - - Evaluates to `[ "" [ "a" null ] "b" [ null "c" ] "" ]`. - - ```nix - builtins.split "([[:upper:]]+)" " FOO " - ``` - - Evaluates to `[ " " [ "FOO" ] " " ]`. - )s", - .fun = prim_split, -}); - static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v) { NixStringContext context; @@ -4097,17 +2671,6 @@ static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * v.mkString(res, context); } -static RegisterPrimOp primop_concatStringsSep({ - .name = "__concatStringsSep", - .args = {"separator", "list"}, - .doc = R"( - Concatenate a list of strings with a separator between each - element, e.g. `concatStringsSep "/" ["usr" "local" "bin"] == - "usr/local/bin"`. - )", - .fun = prim_concatStringsSep, -}); - static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.replaceStrings"); @@ -4166,26 +2729,6 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a v.mkString(res, context); } -static RegisterPrimOp primop_replaceStrings({ - .name = "__replaceStrings", - .args = {"from", "to", "s"}, - .doc = R"( - Given string *s*, replace every occurrence of the strings in *from* - with the corresponding string in *to*. - - The argument *to* is lazy, that is, it is only evaluated when its corresponding pattern in *from* is matched in the string *s* - - Example: - - ```nix - builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar" - ``` - - evaluates to `"fabir"`. - )", - .fun = prim_replaceStrings, -}); - /************************************************************* * Versions @@ -4202,20 +2745,6 @@ static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value * * arg v.mkAttrs(attrs); } -static RegisterPrimOp primop_parseDrvName({ - .name = "__parseDrvName", - .args = {"s"}, - .doc = R"( - Split the string *s* into a package name and version. The package - name is everything up to but not including the first dash not followed - by a letter, and the version is everything following that dash. The - result is returned in a set `{ name, version }`. Thus, - `builtins.parseDrvName "nix-0.12pre12876"` returns `{ name = - "nix"; version = "0.12pre12876"; }`. - )", - .fun = prim_parseDrvName, -}); - static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto version1 = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.compareVersions"); @@ -4224,19 +2753,6 @@ static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * v.mkInt(result < 0 ? -1 : result > 0 ? 1 : 0); } -static RegisterPrimOp primop_compareVersions({ - .name = "__compareVersions", - .args = {"s1", "s2"}, - .doc = R"( - Compare two strings representing versions and return `-1` if - version *s1* is older than version *s2*, `0` if they are the same, - and `1` if *s1* is newer than *s2*. The version comparison - algorithm is the same as the one used by [`nix-env - -u`](../command-ref/nix-env.md#operation---upgrade). - )", - .fun = prim_compareVersions, -}); - static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto version = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.splitVersion"); @@ -4253,17 +2769,6 @@ static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * arg (v.listElems()[n] = state.allocValue())->mkString(std::move(component)); } -static RegisterPrimOp primop_splitVersion({ - .name = "__splitVersion", - .args = {"s"}, - .doc = R"( - Split a string representing a version into its components, by the - same version splitting logic underlying the version comparison in - [`nix-env -u`](../command-ref/nix-env.md#operation---upgrade). - )", - .fun = prim_splitVersion, -}); - /************************************************************* * Primop registration @@ -4280,191 +2785,27 @@ RegisterPrimOp::RegisterPrimOp(PrimOp && primOp) } +static Value getNixPath(EvalState & state, SearchPath & searchPath) +{ + Value v; + state.mkList(v, searchPath.elements.size()); + int n = 0; + for (auto & i : searchPath.elements) { + auto attrs = state.buildBindings(2); + attrs.alloc("path").mkString(i.path.s); + attrs.alloc("prefix").mkString(i.prefix.s); + (v.listElems()[n++] = state.allocValue())->mkAttrs(attrs); + } + return v; +} + void EvalState::createBaseEnv() { baseEnv.up = 0; - /* Add global constants such as `true' to the base environment. */ - Value v; - - /* `builtins' must be first! */ - v.mkAttrs(buildBindings(128).finish()); - addConstant("builtins", v, { - .type = nAttrs, - .doc = R"( - Contains all the [built-in functions](@docroot@/language/builtins.md) and values. - - Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations: - - ```nix - # if hasContext is not available, we assume `s` has a context - if builtins ? hasContext then builtins.hasContext s else true - ``` - )", - }); - - v.mkBool(true); - addConstant("true", v, { - .type = nBool, - .doc = R"( - Primitive value. - - It can be returned by - [comparison operators](@docroot@/language/operators.md#Comparison) - and used in - [conditional expressions](@docroot@/language/constructs.md#Conditionals). - - The name `true` is not special, and can be shadowed: - - ```nix-repl - nix-repl> let true = 1; in true - 1 - ``` - )", - }); - - v.mkBool(false); - addConstant("false", v, { - .type = nBool, - .doc = R"( - Primitive value. - - It can be returned by - [comparison operators](@docroot@/language/operators.md#Comparison) - and used in - [conditional expressions](@docroot@/language/constructs.md#Conditionals). - - The name `false` is not special, and can be shadowed: - - ```nix-repl - nix-repl> let false = 1; in false - 1 - ``` - )", - }); - - v.mkNull(); - addConstant("null", v, { - .type = nNull, - .doc = R"( - Primitive value. - - The name `null` is not special, and can be shadowed: - - ```nix-repl - nix-repl> let null = 1; in null - 1 - ``` - )", - }); - - if (!evalSettings.pureEval) { - v.mkInt(time(0)); - } - addConstant("__currentTime", v, { - .type = nInt, - .doc = R"( - Return the [Unix time](https://en.wikipedia.org/wiki/Unix_time) at first evaluation. - Repeated references to that name will re-use the initially obtained value. - - Example: - - ```console - $ nix repl - Welcome to Nix 2.15.1 Type :? for help. - - nix-repl> builtins.currentTime - 1683705525 - - nix-repl> builtins.currentTime - 1683705525 - ``` - - The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second. - )", - .impureOnly = true, - }); - - if (!evalSettings.pureEval) - v.mkString(evalSettings.getCurrentSystem()); - addConstant("__currentSystem", v, { - .type = nString, - .doc = R"( - The value of the - [`eval-system`](@docroot@/command-ref/conf-file.md#conf-eval-system) - or else - [`system`](@docroot@/command-ref/conf-file.md#conf-system) - configuration option. - - It can be used to set the `system` attribute for [`builtins.derivation`](@docroot@/language/derivations.md) such that the resulting derivation can be built on the same system that evaluates the Nix expression: - - ```nix - builtins.derivation { - # ... - system = builtins.currentSystem; - } - ``` - - It can be overridden in order to create derivations for different system than the current one: - - ```console - $ nix-instantiate --system "mips64-linux" --eval --expr 'builtins.currentSystem' - "mips64-linux" - ``` - )", - .impureOnly = true, - }); - - v.mkString("2.18.3-lix"); - addConstant("__nixVersion", v, { - .type = nString, - .doc = R"( - Legacy version of Nix. Always returns "2.18.3-lix" on Lix. - - Code in the Nix language should use other means of feature detection - like detecting the presence of builtins, rather than trying to find - the version of the Nix implementation, as there may be other Nix - implementations with different feature combinations. - - If the feature you want to write compatibility code for cannot be - detected by any means, please file a Lix bug. - )", - }); - - v.mkString(store->storeDir); - addConstant("__storeDir", v, { - .type = nString, - .doc = R"( - Logical file system location of the [Nix store](@docroot@/glossary.md#gloss-store) currently in use. - - This value is determined by the `store` parameter in [Store URLs](@docroot@/command-ref/new-cli/nix3-help-stores.md): - - ```shell-session - $ nix-instantiate --store 'dummy://?store=/blah' --eval --expr builtins.storeDir - "/blah" - ``` - )", - }); - - /* Legacy language version. - * This is fixed at 6, and will never change in the future on Lix. - * A better language versioning construct needs to be built instead. */ - v.mkInt(6); - addConstant("__langVersion", v, { - .type = nInt, - .doc = R"( - The legacy version of the Nix language. Always is `6` on Lix, - matching Nix 2.18. - - Code in the Nix language should use other means of feature detection - like detecting the presence of builtins, rather than trying to find - the version of the Nix implementation, as there may be other Nix - implementations with different feature combinations. - - If the feature you want to write compatibility code for cannot be - detected by any means, please file a Lix bug. - )", - }); + // constants include the magic `builtins` which must come first + #include "register-builtin-constants.gen.inc" + #include "register-builtins.gen.inc" // Miscellaneous if (evalSettings.enableNativeCode) { @@ -4480,49 +2821,6 @@ void EvalState::createBaseEnv() }); } - addPrimOp({ - .name = "__traceVerbose", - .args = { "e1", "e2" }, - .arity = 2, - .doc = R"( - Evaluate *e1* and print its abstract syntax representation on standard - error if `--trace-verbose` is enabled. Then return *e2*. This function - is useful for debugging. - )", - .fun = evalSettings.traceVerbose ? prim_trace : prim_second, - }); - - /* Add a value containing the current Nix expression search path. */ - mkList(v, searchPath.elements.size()); - int n = 0; - for (auto & i : searchPath.elements) { - auto attrs = buildBindings(2); - attrs.alloc("path").mkString(i.path.s); - attrs.alloc("prefix").mkString(i.prefix.s); - (v.listElems()[n++] = allocValue())->mkAttrs(attrs); - } - addConstant("__nixPath", v, { - .type = nList, - .doc = R"( - The search path used to resolve angle bracket path lookups. - - Angle bracket expressions can be - [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) - using this and - [`builtins.findFile`](./builtins.html#builtins-findFile): - - ```nix - - ``` - - is equivalent to: - - ```nix - builtins.findFile builtins.nixPath "nixpkgs" - ``` - )", - }); - if (RegisterPrimOp::primOps) for (auto & primOp : *RegisterPrimOp::primOps) if (experimentalFeatureSettings.isEnabled(primOp.experimentalFeature)) diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 36692aafb..0708f5d78 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -1,5 +1,6 @@ #include "primops.hh" #include "eval-inline.hh" +#include "extra-primops.hh" #include "derivations.hh" #include "store-api.hh" @@ -19,39 +20,15 @@ static RegisterPrimOp primop_unsafeDiscardStringContext({ }); -static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) { NixStringContext context; state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext"); v.mkBool(!context.empty()); } -static RegisterPrimOp primop_hasContext({ - .name = "__hasContext", - .args = {"s"}, - .doc = R"( - Return `true` if string *s* has a non-empty context. - The context can be obtained with - [`getContext`](#builtins-getContext). - > **Example** - > - > Many operations require a string context to be empty because they are intended only to work with "regular" strings, and also to help users avoid unintentionally losing track of string context elements. - > `builtins.hasContext` can help create better domain-specific errors in those case. - > - > ```nix - > name: meta: - > - > if builtins.hasContext name - > then throw "package name cannot contain string context" - > else { ${name} = meta; } - > ``` - )", - .fun = prim_hasContext -}); - - -static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v) { NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); @@ -71,27 +48,8 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p v.mkString(*s, context2); } -static RegisterPrimOp primop_unsafeDiscardOutputDependency({ - .name = "__unsafeDiscardOutputDependency", - .args = {"s"}, - .doc = R"( - Create a copy of the given string where every "derivation deep" string context element is turned into a constant string context element. - This is the opposite of [`builtins.addDrvOutputDependencies`](#builtins-addDrvOutputDependencies). - - This is unsafe because it allows us to "forget" store objects we would have otherwise refered to with the string context, - whereas Nix normally tracks all dependencies consistently. - Safe operations "grow" but never "shrink" string contexts. - [`builtins.addDrvOutputDependencies`] in contrast is safe because "derivation deep" string context element always refers to the underlying derivation (among many more things). - Replacing a constant string context element with a "derivation deep" element is a safe operation that just enlargens the string context without forgetting anything. - - [`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies - )", - .fun = prim_unsafeDiscardOutputDependency -}); - - -static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v) { NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies"); @@ -133,22 +91,6 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V v.mkString(*s, context2); } -static RegisterPrimOp primop_addDrvOutputDependencies({ - .name = "__addDrvOutputDependencies", - .args = {"s"}, - .doc = R"( - Create a copy of the given string where a single constant string context element is turned into a "derivation deep" string context element. - - The store path that is the constant string context element should point to a valid derivation, and end in `.drv`. - - The original string context element must not be empty or have multiple elements, and it must not have any other type of element other than a constant or derivation deep element. - The latter is supported so this function is idempotent. - - This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency). - )", - .fun = prim_addDrvOutputDependencies -}); - /* Extract the context of a string as a structured Nix value. @@ -169,7 +111,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies({ Note that for a given path any combination of the above attributes may be present. */ -static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) { struct ContextInfo { bool path = false; @@ -218,31 +160,6 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, v.mkAttrs(attrs); } -static RegisterPrimOp primop_getContext({ - .name = "__getContext", - .args = {"s"}, - .doc = R"( - Return the string context of *s*. - - The string context tracks references to derivations within a string. - It is represented as an attribute set of [store derivation](@docroot@/glossary.md#gloss-store-derivation) paths mapping to output names. - - Using [string interpolation](@docroot@/language/string-interpolation.md) on a derivation will add that derivation to the string context. - For example, - - ```nix - builtins.getContext "${derivation { name = "a"; builder = "b"; system = "c"; }}" - ``` - - evaluates to - - ``` - { "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; } - ``` - )", - .fun = prim_getContext -}); - /* Append the given context to a given string. diff --git a/src/libexpr/primops/fetchClosure.cc b/src/libexpr/primops/fetchClosure.cc index a736957eb..10da79018 100644 --- a/src/libexpr/primops/fetchClosure.cc +++ b/src/libexpr/primops/fetchClosure.cc @@ -1,4 +1,5 @@ #include "primops.hh" +#include "extra-primops.hh" #include "store-api.hh" #include "make-content-addressed.hh" #include "url.hh" @@ -111,7 +112,7 @@ static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosId typedef std::optional StorePathOrGap; -static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure"); @@ -207,80 +208,4 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg runFetchClosureWithContentAddressedPath(state, pos, *fromStore, *fromPath, v); } -static RegisterPrimOp primop_fetchClosure({ - .name = "__fetchClosure", - .args = {"args"}, - .doc = R"( - Fetch a store path [closure](@docroot@/glossary.md#gloss-closure) from a binary cache, and return the store path as a string with context. - - This function can be invoked in three ways, that we will discuss in order of preference. - - **Fetch a content-addressed store path** - - Example: - - ```nix - builtins.fetchClosure { - fromStore = "https://cache.nixos.org"; - fromPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1; - } - ``` - - This is the simplest invocation, and it does not require the user of the expression to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity. - - If your store path is [input addressed](@docroot@/glossary.md#gloss-input-addressed-store-object) instead of content addressed, consider the other two invocations. - - **Fetch any store path and rewrite it to a fully content-addressed store path** - - Example: - - ```nix - builtins.fetchClosure { - fromStore = "https://cache.nixos.org"; - fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1; - toPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1; - } - ``` - - This example fetches `/nix/store/r2jd...` from the specified binary cache, - and rewrites it into the content-addressed store path - `/nix/store/ldbh...`. - - Like the previous example, no extra configuration or privileges are required. - - To find out the correct value for `toPath` given a `fromPath`, - use [`nix store make-content-addressed`](@docroot@/command-ref/new-cli/nix3-store-make-content-addressed.md): - - ```console - # nix store make-content-addressed --from https://cache.nixos.org /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1 - rewrote '/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1' to '/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1' - ``` - - Alternatively, set `toPath = ""` and find the correct `toPath` in the error message. - - **Fetch an input-addressed store path as is** - - Example: - - ```nix - builtins.fetchClosure { - fromStore = "https://cache.nixos.org"; - fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1; - inputAddressed = true; - } - ``` - - It is possible to fetch an [input-addressed store path](@docroot@/glossary.md#gloss-input-addressed-store-object) and return it as is. - However, this is the least preferred way of invoking `fetchClosure`, because it requires that the input-addressed paths are trusted by the Lix configuration. - - **`builtins.storePath`** - - `fetchClosure` is similar to [`builtins.storePath`](#builtins-storePath) in that it allows you to use a previously built store path in a Nix expression. - However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched. - Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity. - )", - .fun = prim_fetchClosure, - .experimentalFeature = Xp::FetchClosure, -}); - } diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index c98fe2a03..57b8825b7 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -1,6 +1,7 @@ #include "primops.hh" #include "eval-inline.hh" #include "eval-settings.hh" +#include "extra-primops.hh" #include "store-api.hh" #include "fetchers.hh" #include "filetransfer.hh" @@ -292,216 +293,19 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v state.allowAndSetStorePathString(storePath, v); } -static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v) { fetch(state, pos, args, v, "fetchurl", false, ""); } -static RegisterPrimOp primop_fetchurl({ - .name = "__fetchurl", - .args = {"url"}, - .doc = R"( - Download the specified URL and return the path of the downloaded file. - - Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). - )", - .fun = prim_fetchurl, -}); - -static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v) { fetch(state, pos, args, v, "fetchTarball", true, "source"); } -static RegisterPrimOp primop_fetchTarball({ - .name = "fetchTarball", - .args = {"args"}, - .doc = R"( - Download the specified URL, unpack it and return the path of the - unpacked tree. The file must be a tape archive (`.tar`) compressed - with `gzip`, `bzip2` or `xz`. The top-level path component of the - files in the tarball is removed, so it is best if the tarball - contains a single directory at top level. The typical use of the - function is to obtain external Nix expression dependencies, such as - a particular version of Nixpkgs, e.g. - - ```nix - with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz") {}; - - stdenv.mkDerivation { … } - ``` - - The fetched tarball is cached for a certain amount of time (1 - hour by default) in `~/.cache/nix/tarballs/`. You can change the - cache timeout either on the command line with `--tarball-ttl` - *number-of-seconds* or in the Nix configuration file by adding - the line `tarball-ttl = ` *number-of-seconds*. - - Note that when obtaining the hash with `nix-prefetch-url` the - option `--unpack` is required. - - This function can also verify the contents against a hash. In that - case, the function takes a set instead of a URL. The set requires - the attribute `url` and the attribute `sha256`, e.g. - - ```nix - with import (fetchTarball { - url = "https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz"; - sha256 = "1jppksrfvbk5ypiqdz4cddxdl8z6zyzdb2srq8fcffr327ld5jj2"; - }) {}; - - stdenv.mkDerivation { … } - ``` - - Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). - )", - .fun = prim_fetchTarball, -}); - -static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v) { fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true }); } -static RegisterPrimOp primop_fetchGit({ - .name = "fetchGit", - .args = {"args"}, - .doc = R"( - Fetch a path from git. *args* can be a URL, in which case the HEAD - of the repo at that URL is fetched. Otherwise, it can be an - attribute with the following attributes (all except `url` optional): - - - `url` - - The URL of the repo. - - - `name` (default: *basename of the URL*) - - The name of the directory the repo should be exported to in the store. - - - `rev` (default: *the tip of `ref`*) - - The [Git revision] to fetch. - This is typically a commit hash. - - [Git revision]: https://git-scm.com/docs/git-rev-parse#_specifying_revisions - - - `ref` (default: `HEAD`) - - The [Git reference] under which to look for the requested revision. - This is often a branch or tag name. - - [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References - - By default, the `ref` value is prefixed with `refs/heads/`. - As of 2.3.0, Nix will not prefix `refs/heads/` if `ref` starts with `refs/` or - if `ref` looks like a commit hash for backwards compatibility with CppNix 2.3. - - - `submodules` (default: `false`) - - A Boolean parameter that specifies whether submodules should be checked out. - - - `shallow` (default: `false`) - - A Boolean parameter that specifies whether fetching from a shallow remote repository is allowed. - This still performs a full clone of what is available on the remote. - - - `allRefs` - - Whether to fetch all references of the repository. - With this argument being true, it's possible to load a `rev` from *any* `ref` - (by default only `rev`s from the specified `ref` are supported). - - Here are some examples of how to use `fetchGit`. - - - To fetch a private repository over SSH: - - ```nix - builtins.fetchGit { - url = "git@github.com:my-secret/repository.git"; - ref = "master"; - rev = "adab8b916a45068c044658c4158d81878f9ed1c3"; - } - ``` - - - To fetch an arbitrary reference: - - ```nix - builtins.fetchGit { - url = "https://github.com/NixOS/nix.git"; - ref = "refs/heads/0.5-release"; - } - ``` - - - If the revision you're looking for is in the default branch of - the git repository you don't strictly need to specify the branch - name in the `ref` attribute. - - However, if the revision you're looking for is in a future - branch for the non-default branch you will need to specify the - the `ref` attribute as well. - - ```nix - builtins.fetchGit { - url = "https://github.com/nixos/nix.git"; - rev = "841fcbd04755c7a2865c51c1e2d3b045976b7452"; - ref = "1.11-maintenance"; - } - ``` - - > **Note** - > - > It is nice to always specify the branch which a revision - > belongs to. Without the branch being specified, the fetcher - > might fail if the default branch changes. Additionally, it can - > be confusing to try a commit from a non-default branch and see - > the fetch fail. If the branch is specified the fault is much - > more obvious. - - - If the revision you're looking for is in the default branch of - the git repository you may omit the `ref` attribute. - - ```nix - builtins.fetchGit { - url = "https://github.com/nixos/nix.git"; - rev = "841fcbd04755c7a2865c51c1e2d3b045976b7452"; - } - ``` - - - To fetch a specific tag: - - ```nix - builtins.fetchGit { - url = "https://github.com/nixos/nix.git"; - ref = "refs/tags/1.9"; - } - ``` - - - To fetch the latest version of a remote branch: - - ```nix - builtins.fetchGit { - url = "ssh://git@github.com/nixos/nix.git"; - ref = "master"; - } - ``` - - Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting. - - This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). - - - To fetch the content of a checked-out work directory: - - ```nix - builtins.fetchGit ./work-dir - ``` - - If the URL points to a local directory, and no `ref` or `rev` is - given, `fetchGit` will use the current content of the checked-out - files, even if they are not committed or added to Git's index. It will - only consider files added to the Git repository, as listed by `git ls-files`. - )", - .fun = prim_fetchGit, -}); - } diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index c51683df7..c4c625332 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -1,12 +1,13 @@ #include "primops.hh" #include "eval-inline.hh" +#include "extra-primops.hh" #include #include namespace nix { -static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val) +void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val) { auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML"); @@ -86,24 +87,4 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V } } -static RegisterPrimOp primop_fromTOML({ - .name = "fromTOML", - .args = {"e"}, - .doc = R"( - Convert a TOML string to a Nix value. For example, - - ```nix - builtins.fromTOML '' - x=1 - s="a" - [table] - y=2 - '' - ``` - - returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`. - )", - .fun = prim_fromTOML -}); - }