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..e4ff0684b 100644 --- a/doc/manual/meson.build +++ b/doc/manual/meson.build @@ -15,56 +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', -) - -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', - 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, @@ -78,12 +28,8 @@ generate_manual_deps = files( 'generate-deps.py', ) -# 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..67f7261ae 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. @@ -65,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/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..60f113cbb 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -399,3 +399,76 @@ 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, global settings, or builtins + +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. + +### 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. + +### 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/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/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/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_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/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/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 new file mode 100644 index 000000000..38e8122a0 --- /dev/null +++ b/src/code-generation/common.py @@ -0,0 +1,60 @@ +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 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++') + +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/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-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/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 af72bd549..96ffb8fdd 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -13,6 +13,185 @@ 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', +) + +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', @@ -84,7 +263,10 @@ libexpr_headers = files( libexpr = library( 'lixexpr', libexpr_sources, + libexpr_settings_header, libexpr_generated_headers, + register_builtins_header, + register_builtin_constants_header, dependencies : [ liblixutil, liblixstore, @@ -112,6 +294,7 @@ install_headers( liblixexpr = declare_dependency( include_directories : include_directories('.'), + sources : libexpr_settings_header, link_with : libexpr, ) @@ -119,6 +302,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/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 -}); - } 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 b3dcc122f..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@/command-ref/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@/command-ref/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/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/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 afca4e021..5aec64416 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -130,9 +130,156 @@ 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_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, + experimental_features_header, + experimental_features_impl_header, + deprecated_features_header, + deprecated_features_impl_header, + libutil_settings_headers, dependencies : [ aws_sdk, aws_s3, @@ -169,6 +316,11 @@ configure_file( # Used by libstore and libfetchers. liblixutil = declare_dependency( include_directories : include_directories('.'), + sources : [ + experimental_features_header, + deprecated_features_header, + libutil_settings_headers, + ], link_with : libutil ) @@ -176,6 +328,11 @@ liblixutil = declare_dependency( if is_static liblixutil_mstatic = declare_dependency( include_directories : include_directories('.'), + sources : [ + experimental_features_header, + deprecated_features_header, + libutil_settings_headers, + ], link_whole : libutil, ) else 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,