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