Merge pull request #8330 from NixOS/doc-auto-builtin-constants
Automatically document builtin constants
This commit is contained in:
commit
71d4fd8ebb
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -18,7 +18,7 @@ perl/Makefile.config
|
|||
/doc/manual/generated/*
|
||||
/doc/manual/nix.json
|
||||
/doc/manual/conf-file.json
|
||||
/doc/manual/builtins.json
|
||||
/doc/manual/language.json
|
||||
/doc/manual/xp-features.json
|
||||
/doc/manual/src/SUMMARY.md
|
||||
/doc/manual/src/command-ref/new-cli
|
||||
|
@ -26,6 +26,7 @@ perl/Makefile.config
|
|||
/doc/manual/src/command-ref/experimental-features-shortlist.md
|
||||
/doc/manual/src/contributing/experimental-feature-descriptions.md
|
||||
/doc/manual/src/language/builtins.md
|
||||
/doc/manual/src/language/builtin-constants.md
|
||||
|
||||
# /scripts/
|
||||
/scripts/nix-profile.sh
|
||||
|
|
29
doc/manual/generate-builtin-constants.nix
Normal file
29
doc/manual/generate-builtin-constants.nix
Normal file
|
@ -0,0 +1,29 @@
|
|||
let
|
||||
inherit (builtins) concatStringsSep attrValues mapAttrs;
|
||||
inherit (import ./utils.nix) optionalString squash;
|
||||
in
|
||||
|
||||
builtinsInfo:
|
||||
let
|
||||
showBuiltin = name: { doc, type, impure-only }:
|
||||
let
|
||||
type' = optionalString (type != null) " (${type})";
|
||||
|
||||
impureNotice = optionalString impure-only ''
|
||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||
'';
|
||||
in
|
||||
squash ''
|
||||
<dt id="builtin-constants-${name}">
|
||||
<a href="#builtin-constants-${name}"><code>${name}</code>${type'}</a>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
${doc}
|
||||
|
||||
${impureNotice}
|
||||
|
||||
</dd>
|
||||
'';
|
||||
in
|
||||
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))
|
|
@ -1,24 +1,28 @@
|
|||
let
|
||||
inherit (builtins) concatStringsSep attrNames;
|
||||
inherit (builtins) concatStringsSep attrValues mapAttrs;
|
||||
inherit (import ./utils.nix) optionalString squash;
|
||||
in
|
||||
|
||||
builtinsInfo:
|
||||
let
|
||||
showBuiltin = name:
|
||||
showBuiltin = name: { doc, args, arity, experimental-feature }:
|
||||
let
|
||||
inherit (builtinsInfo.${name}) doc args;
|
||||
experimentalNotice = optionalString (experimental-feature != null) ''
|
||||
This function is only available if the [${experimental-feature}](@docroot@/contributing/experimental-features.md#xp-feature-${experimental-feature}) experimental feature is enabled.
|
||||
'';
|
||||
in
|
||||
''
|
||||
squash ''
|
||||
<dt id="builtins-${name}">
|
||||
<a href="#builtins-${name}"><code>${name} ${listArgs args}</code></a>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
${doc}
|
||||
${doc}
|
||||
|
||||
${experimentalNotice}
|
||||
|
||||
</dd>
|
||||
'';
|
||||
listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args);
|
||||
in
|
||||
concatStringsSep "\n" (map showBuiltin (attrNames builtinsInfo))
|
||||
|
||||
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))
|
||||
|
|
|
@ -128,14 +128,20 @@ $(d)/xp-features.json: $(bindir)/nix
|
|||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
||||
$(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
||||
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<)).builtins' >> $@.tmp;
|
||||
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/builtins.json: $(bindir)/nix
|
||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-builtins > $@.tmp
|
||||
$(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(bindir)/nix
|
||||
@cat doc/manual/src/language/builtin-constants-prefix.md > $@.tmp
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtin-constants.nix (builtins.fromJSON (builtins.readFile $<)).constants' >> $@.tmp;
|
||||
@cat doc/manual/src/language/builtin-constants-suffix.md >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/language.json: $(bindir)/nix
|
||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-language > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
# Generate the HTML manual.
|
||||
|
@ -167,7 +173,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
|
|||
done
|
||||
@touch $@
|
||||
|
||||
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md
|
||||
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md
|
||||
$(trace-gen) \
|
||||
tmp="$$(mktemp -d)"; \
|
||||
cp -r doc/manual "$$tmp"; \
|
||||
|
|
5
doc/manual/src/language/builtin-constants-prefix.md
Normal file
5
doc/manual/src/language/builtin-constants-prefix.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Built-in Constants
|
||||
|
||||
These constants are built into the Nix language evaluator:
|
||||
|
||||
<dl>
|
1
doc/manual/src/language/builtin-constants-suffix.md
Normal file
1
doc/manual/src/language/builtin-constants-suffix.md
Normal file
|
@ -0,0 +1 @@
|
|||
</dl>
|
|
@ -1,43 +0,0 @@
|
|||
# Built-in Constants
|
||||
|
||||
These constants are built into the Nix language evaluator:
|
||||
|
||||
- [`builtins`]{#builtins-builtins} (attribute set)
|
||||
|
||||
Contains all the [built-in functions](./builtins.md) and values, in order to avoid polluting the global scope.
|
||||
|
||||
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 builtins ? getEnv then builtins.getEnv "PATH" else ""
|
||||
```
|
||||
|
||||
- [`builtins.currentSystem`]{#builtins-currentSystem} (string)
|
||||
|
||||
The built-in value `currentSystem` evaluates to the Nix platform
|
||||
identifier for the Nix installation on which the expression is being
|
||||
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
|
||||
|
||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||
|
||||
- [`builtins.currentTime`]{#builtins-currentTime} (integer)
|
||||
|
||||
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.
|
||||
|
||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
|
@ -211,20 +211,21 @@ const Value * getPrimOp(const Value &v) {
|
|||
return primOp;
|
||||
}
|
||||
|
||||
std::string_view showType(ValueType type)
|
||||
std::string_view showType(ValueType type, bool withArticle)
|
||||
{
|
||||
#define WA(a, w) withArticle ? a " " w : w
|
||||
switch (type) {
|
||||
case nInt: return "an integer";
|
||||
case nBool: return "a Boolean";
|
||||
case nString: return "a string";
|
||||
case nPath: return "a path";
|
||||
case nInt: return WA("an", "integer");
|
||||
case nBool: return WA("a", "Boolean");
|
||||
case nString: return WA("a", "string");
|
||||
case nPath: return WA("a", "path");
|
||||
case nNull: return "null";
|
||||
case nAttrs: return "a set";
|
||||
case nList: return "a list";
|
||||
case nFunction: return "a function";
|
||||
case nExternal: return "an external value";
|
||||
case nFloat: return "a float";
|
||||
case nThunk: return "a thunk";
|
||||
case nAttrs: return WA("a", "set");
|
||||
case nList: return WA("a", "list");
|
||||
case nFunction: return WA("a", "function");
|
||||
case nExternal: return WA("an", "external value");
|
||||
case nFloat: return WA("a", "float");
|
||||
case nThunk: return WA("a", "thunk");
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
@ -702,28 +703,34 @@ Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
|
|||
}
|
||||
|
||||
|
||||
Value * EvalState::addConstant(const std::string & name, Value & v)
|
||||
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
|
||||
{
|
||||
Value * v2 = allocValue();
|
||||
*v2 = v;
|
||||
addConstant(name, v2);
|
||||
addConstant(name, v2, info);
|
||||
return v2;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::addConstant(const std::string & name, Value * v)
|
||||
void EvalState::addConstant(const std::string & name, Value * v, Constant info)
|
||||
{
|
||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
|
||||
}
|
||||
|
||||
constantInfos.push_back({name2, info});
|
||||
|
||||
Value * EvalState::addPrimOp(const std::string & name,
|
||||
size_t arity, PrimOpFun primOp)
|
||||
{
|
||||
return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name });
|
||||
if (!(evalSettings.pureEval && info.impureOnly)) {
|
||||
/* Check the type, if possible.
|
||||
|
||||
We might know the type of a thunk in advance, so be allowed
|
||||
to just write it down in that case. */
|
||||
if (auto gotType = v->type(true); gotType != nThunk)
|
||||
assert(info.type == gotType);
|
||||
|
||||
/* Install value the base environment. */
|
||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -737,7 +744,10 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
|||
vPrimOp->mkPrimOp(new PrimOp(primOp));
|
||||
Value v;
|
||||
v.mkApp(vPrimOp, vPrimOp);
|
||||
return addConstant(primOp.name, v);
|
||||
return addConstant(primOp.name, v, {
|
||||
.type = nThunk, // FIXME
|
||||
.doc = primOp.doc,
|
||||
});
|
||||
}
|
||||
|
||||
auto envName = symbols.create(primOp.name);
|
||||
|
@ -763,13 +773,13 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
|||
{
|
||||
if (v.isPrimOp()) {
|
||||
auto v2 = &v;
|
||||
if (v2->primOp->doc)
|
||||
if (auto * doc = v2->primOp->doc)
|
||||
return Doc {
|
||||
.pos = {},
|
||||
.name = v2->primOp->name,
|
||||
.arity = v2->primOp->arity,
|
||||
.args = v2->primOp->args,
|
||||
.doc = v2->primOp->doc,
|
||||
.doc = doc,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
|
|
|
@ -25,15 +25,72 @@ struct DerivedPath;
|
|||
enum RepairFlag : bool;
|
||||
|
||||
|
||||
/**
|
||||
* Function that implements a primop.
|
||||
*/
|
||||
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
|
||||
/**
|
||||
* Info about a primitive operation, and its implementation
|
||||
*/
|
||||
struct PrimOp
|
||||
{
|
||||
PrimOpFun fun;
|
||||
size_t arity;
|
||||
/**
|
||||
* Name of the primop. `__` prefix is treated specially.
|
||||
*/
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* Names of the parameters of a primop, for primops that take a
|
||||
* fixed number of arguments to be substituted for these parameters.
|
||||
*/
|
||||
std::vector<std::string> args;
|
||||
|
||||
/**
|
||||
* Aritiy of the primop.
|
||||
*
|
||||
* If `args` is not empty, this field will be computed from that
|
||||
* field instead, so it doesn't need to be manually set.
|
||||
*/
|
||||
size_t arity = 0;
|
||||
|
||||
/**
|
||||
* Optional free-form documentation about the primop.
|
||||
*/
|
||||
const char * doc = nullptr;
|
||||
|
||||
/**
|
||||
* Implementation of the primop.
|
||||
*/
|
||||
PrimOpFun fun;
|
||||
|
||||
/**
|
||||
* Optional experimental for this to be gated on.
|
||||
*/
|
||||
std::optional<ExperimentalFeature> experimentalFeature;
|
||||
};
|
||||
|
||||
/**
|
||||
* Info about a constant
|
||||
*/
|
||||
struct Constant
|
||||
{
|
||||
/**
|
||||
* Optional type of the constant (known since it is a fixed value).
|
||||
*
|
||||
* @todo we should use an enum for this.
|
||||
*/
|
||||
ValueType type = nThunk;
|
||||
|
||||
/**
|
||||
* Optional free-form documentation about the constant.
|
||||
*/
|
||||
const char * doc = nullptr;
|
||||
|
||||
/**
|
||||
* Whether the constant is impure, and not available in pure mode.
|
||||
*/
|
||||
bool impureOnly = false;
|
||||
};
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
@ -513,18 +570,23 @@ public:
|
|||
*/
|
||||
std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
|
||||
|
||||
/**
|
||||
* Name and documentation about every constant.
|
||||
*
|
||||
* Constants from primops are hard to crawl, and their docs will go
|
||||
* here too.
|
||||
*/
|
||||
std::vector<std::pair<std::string, Constant>> constantInfos;
|
||||
|
||||
private:
|
||||
|
||||
unsigned int baseEnvDispl = 0;
|
||||
|
||||
void createBaseEnv();
|
||||
|
||||
Value * addConstant(const std::string & name, Value & v);
|
||||
Value * addConstant(const std::string & name, Value & v, Constant info);
|
||||
|
||||
void addConstant(const std::string & name, Value * v);
|
||||
|
||||
Value * addPrimOp(const std::string & name,
|
||||
size_t arity, PrimOpFun primOp);
|
||||
void addConstant(const std::string & name, Value * v, Constant info);
|
||||
|
||||
Value * addPrimOp(PrimOp && primOp);
|
||||
|
||||
|
@ -538,6 +600,10 @@ public:
|
|||
std::optional<std::string> name;
|
||||
size_t arity;
|
||||
std::vector<std::string> args;
|
||||
/**
|
||||
* Unlike the other `doc` fields in this file, this one should never be
|
||||
* `null`.
|
||||
*/
|
||||
const char * doc;
|
||||
};
|
||||
|
||||
|
@ -704,8 +770,11 @@ struct DebugTraceStacker {
|
|||
|
||||
/**
|
||||
* @return A string representing the type of the value `v`.
|
||||
*
|
||||
* @param withArticle Whether to begin with an english article, e.g. "an
|
||||
* integer" vs "integer".
|
||||
*/
|
||||
std::string_view showType(ValueType type);
|
||||
std::string_view showType(ValueType type, bool withArticle = true);
|
||||
std::string showType(const Value & v);
|
||||
|
||||
/**
|
||||
|
@ -737,7 +806,12 @@ struct EvalSettings : Config
|
|||
|
||||
Setting<Strings> nixPath{
|
||||
this, getDefaultNixPath(), "nix-path",
|
||||
"List of directories to be searched for `<...>` file references."};
|
||||
R"(
|
||||
List of directories to be searched for `<...>` file references
|
||||
|
||||
In particular, outside of [pure evaluation mode](#conf-pure-evaluation), this determines the value of
|
||||
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtin-constants-nixPath).
|
||||
)"};
|
||||
|
||||
Setting<bool> restrictEval{
|
||||
this, false, "restrict-eval",
|
||||
|
|
|
@ -788,9 +788,6 @@ static RegisterPrimOp r2({
|
|||
```nix
|
||||
(builtins.getFlake "github:edolstra/dwarffs").rev
|
||||
```
|
||||
|
||||
This function is only available if you enable the experimental feature
|
||||
`flakes`.
|
||||
)",
|
||||
.fun = prim_getFlake,
|
||||
.experimentalFeature = Xp::Flakes,
|
||||
|
|
|
@ -238,7 +238,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
|
|||
}
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info {
|
||||
static RegisterPrimOp primop_scopedImport(PrimOp {
|
||||
.name = "scopedImport",
|
||||
.arity = 2,
|
||||
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
|
@ -692,7 +692,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
|||
v.listElems()[n++] = i;
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info {
|
||||
static RegisterPrimOp primop_genericClosure(PrimOp {
|
||||
.name = "__genericClosure",
|
||||
.args = {"attrset"},
|
||||
.arity = 1,
|
||||
|
@ -809,7 +809,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
|||
}
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
|
||||
static RegisterPrimOp primop_addErrorContext(PrimOp {
|
||||
.name = "__addErrorContext",
|
||||
.arity = 2,
|
||||
.fun = prim_addErrorContext,
|
||||
|
@ -1400,7 +1400,7 @@ drvName, Bindings * attrs, Value & v)
|
|||
v.mkAttrs(result);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
|
||||
static RegisterPrimOp primop_derivationStrict(PrimOp {
|
||||
.name = "derivationStrict",
|
||||
.arity = 1,
|
||||
.fun = prim_derivationStrict,
|
||||
|
@ -1667,9 +1667,52 @@ 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(RegisterPrimOp::Info {
|
||||
static RegisterPrimOp primop_findFile(PrimOp {
|
||||
.name = "__findFile",
|
||||
.arity = 2,
|
||||
.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
|
||||
<nixpkgs>
|
||||
```
|
||||
|
||||
is equivalent to:
|
||||
|
||||
```nix
|
||||
builtins.findFile builtins.nixPath "nixpkgs"
|
||||
```
|
||||
)",
|
||||
.fun = prim_findFile,
|
||||
});
|
||||
|
||||
|
@ -2388,7 +2431,7 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * *
|
|||
state.mkPos(v, i->pos);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
|
||||
static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
|
||||
.name = "__unsafeGetAttrPos",
|
||||
.arity = 2,
|
||||
.fun = prim_unsafeGetAttrPos,
|
||||
|
@ -4061,10 +4104,10 @@ static RegisterPrimOp primop_splitVersion({
|
|||
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
||||
|
||||
|
||||
RegisterPrimOp::RegisterPrimOp(Info && info)
|
||||
RegisterPrimOp::RegisterPrimOp(PrimOp && primOp)
|
||||
{
|
||||
if (!primOps) primOps = new PrimOps;
|
||||
primOps->push_back(std::move(info));
|
||||
primOps->push_back(std::move(primOp));
|
||||
}
|
||||
|
||||
|
||||
|
@ -4077,54 +4120,202 @@ void EvalState::createBaseEnv()
|
|||
|
||||
/* `builtins' must be first! */
|
||||
v.mkAttrs(buildBindings(128).finish());
|
||||
addConstant("builtins", v);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
v.mkString(settings.thisSystem.get());
|
||||
addConstant("__currentSystem", v);
|
||||
}
|
||||
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(settings.thisSystem.get());
|
||||
}
|
||||
addConstant("__currentSystem", v, {
|
||||
.type = nString,
|
||||
.doc = R"(
|
||||
The value of the [`system` configuration option](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||
|
||||
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(nixVersion);
|
||||
addConstant("__nixVersion", v);
|
||||
addConstant("__nixVersion", v, {
|
||||
.type = nString,
|
||||
.doc = R"(
|
||||
The version of Nix.
|
||||
|
||||
For example, where the command line returns the current Nix version,
|
||||
|
||||
```shell-session
|
||||
$ nix --version
|
||||
nix (Nix) 2.16.0
|
||||
```
|
||||
|
||||
the Nix language evaluator returns the same value:
|
||||
|
||||
```nix-repl
|
||||
nix-repl> builtins.nixVersion
|
||||
"2.16.0"
|
||||
```
|
||||
)",
|
||||
});
|
||||
|
||||
v.mkString(store->storeDir);
|
||||
addConstant("__storeDir", v);
|
||||
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"
|
||||
```
|
||||
)",
|
||||
});
|
||||
|
||||
/* Language version. This should be increased every time a new
|
||||
language feature gets added. It's not necessary to increase it
|
||||
when primops get added, because you can just use `builtins ?
|
||||
primOp' to check. */
|
||||
v.mkInt(6);
|
||||
addConstant("__langVersion", v);
|
||||
addConstant("__langVersion", v, {
|
||||
.type = nInt,
|
||||
.doc = R"(
|
||||
The current version of the Nix language.
|
||||
)",
|
||||
});
|
||||
|
||||
// Miscellaneous
|
||||
if (evalSettings.enableNativeCode) {
|
||||
addPrimOp("__importNative", 2, prim_importNative);
|
||||
addPrimOp("__exec", 1, prim_exec);
|
||||
addPrimOp({
|
||||
.name = "__importNative",
|
||||
.arity = 2,
|
||||
.fun = prim_importNative,
|
||||
});
|
||||
addPrimOp({
|
||||
.name = "__exec",
|
||||
.arity = 1,
|
||||
.fun = prim_exec,
|
||||
});
|
||||
}
|
||||
|
||||
addPrimOp({
|
||||
.fun = evalSettings.traceVerbose ? prim_trace : prim_second,
|
||||
.arity = 2,
|
||||
.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. */
|
||||
|
@ -4136,26 +4327,46 @@ void EvalState::createBaseEnv()
|
|||
attrs.alloc("prefix").mkString(i.prefix);
|
||||
(v.listElems()[n++] = allocValue())->mkAttrs(attrs);
|
||||
}
|
||||
addConstant("__nixPath", v);
|
||||
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
|
||||
<nixpkgs>
|
||||
```
|
||||
|
||||
is equivalent to:
|
||||
|
||||
```nix
|
||||
builtins.findFile builtins.nixPath "nixpkgs"
|
||||
```
|
||||
)",
|
||||
});
|
||||
|
||||
if (RegisterPrimOp::primOps)
|
||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||
if (!primOp.experimentalFeature
|
||||
|| experimentalFeatureSettings.isEnabled(*primOp.experimentalFeature))
|
||||
if (experimentalFeatureSettings.isEnabled(primOp.experimentalFeature))
|
||||
{
|
||||
addPrimOp({
|
||||
.fun = primOp.fun,
|
||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
||||
.name = primOp.name,
|
||||
.args = primOp.args,
|
||||
.doc = primOp.doc,
|
||||
});
|
||||
auto primOpAdjusted = primOp;
|
||||
primOpAdjusted.arity = std::max(primOp.args.size(), primOp.arity);
|
||||
addPrimOp(std::move(primOpAdjusted));
|
||||
}
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
`drvPath' and `outPath' attributes lazily.
|
||||
|
||||
Null docs because it is documented separately.
|
||||
*/
|
||||
auto vDerivation = allocValue();
|
||||
addConstant("derivation", vDerivation);
|
||||
addConstant("derivation", vDerivation, {
|
||||
.type = nFunction,
|
||||
});
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' set,
|
||||
because attribute lookups expect it to be sorted. */
|
||||
|
|
|
@ -10,17 +10,7 @@ namespace nix {
|
|||
|
||||
struct RegisterPrimOp
|
||||
{
|
||||
struct Info
|
||||
{
|
||||
std::string name;
|
||||
std::vector<std::string> args;
|
||||
size_t arity = 0;
|
||||
const char * doc;
|
||||
PrimOpFun fun;
|
||||
std::optional<ExperimentalFeature> experimentalFeature;
|
||||
};
|
||||
|
||||
typedef std::vector<Info> PrimOps;
|
||||
typedef std::vector<PrimOp> PrimOps;
|
||||
static PrimOps * primOps;
|
||||
|
||||
/**
|
||||
|
@ -28,7 +18,7 @@ struct RegisterPrimOp
|
|||
* will get called during EvalState initialization, so there
|
||||
* may be primops not yet added and builtins is not yet sorted.
|
||||
*/
|
||||
RegisterPrimOp(Info && info);
|
||||
RegisterPrimOp(PrimOp && primOp);
|
||||
};
|
||||
|
||||
/* These primops are disabled without enableNativeCode, but plugins
|
||||
|
|
|
@ -154,9 +154,6 @@ static RegisterPrimOp primop_fetchClosure({
|
|||
specifying a binary cache from which the path can be fetched.
|
||||
Also, requiring a content-addressed final store path avoids the
|
||||
need for users to configure binary cache public keys.
|
||||
|
||||
This function is only available if you enable the experimental
|
||||
feature `fetch-closure`.
|
||||
)",
|
||||
.fun = prim_fetchClosure,
|
||||
.experimentalFeature = Xp::FetchClosure,
|
||||
|
|
|
@ -218,8 +218,11 @@ public:
|
|||
/**
|
||||
* Returns the normal type of a Value. This only returns nThunk if
|
||||
* the Value hasn't been forceValue'd
|
||||
*
|
||||
* @param invalidIsThunk Instead of aborting an an invalid (probably
|
||||
* 0, so uninitialized) internal type, return `nThunk`.
|
||||
*/
|
||||
inline ValueType type() const
|
||||
inline ValueType type(bool invalidIsThunk = false) const
|
||||
{
|
||||
switch (internalType) {
|
||||
case tInt: return nInt;
|
||||
|
@ -234,7 +237,10 @@ public:
|
|||
case tFloat: return nFloat;
|
||||
case tThunk: case tApp: case tBlackhole: return nThunk;
|
||||
}
|
||||
abort();
|
||||
if (invalidIsThunk)
|
||||
return nThunk;
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -352,7 +352,7 @@ void mainWrapped(int argc, char * * argv)
|
|||
return;
|
||||
}
|
||||
|
||||
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
|
||||
if (argc == 2 && std::string(argv[1]) == "__dump-language") {
|
||||
experimentalFeatureSettings.experimentalFeatures = {
|
||||
Xp::Flakes,
|
||||
Xp::FetchClosure,
|
||||
|
@ -360,17 +360,34 @@ void mainWrapped(int argc, char * * argv)
|
|||
evalSettings.pureEval = false;
|
||||
EvalState state({}, openStore("dummy://"));
|
||||
auto res = nlohmann::json::object();
|
||||
auto builtins = state.baseEnv.values[0]->attrs;
|
||||
for (auto & builtin : *builtins) {
|
||||
auto b = nlohmann::json::object();
|
||||
if (!builtin.value->isPrimOp()) continue;
|
||||
auto primOp = builtin.value->primOp;
|
||||
if (!primOp->doc) continue;
|
||||
b["arity"] = primOp->arity;
|
||||
b["args"] = primOp->args;
|
||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||
res[state.symbols[builtin.name]] = std::move(b);
|
||||
}
|
||||
res["builtins"] = ({
|
||||
auto builtinsJson = nlohmann::json::object();
|
||||
auto builtins = state.baseEnv.values[0]->attrs;
|
||||
for (auto & builtin : *builtins) {
|
||||
auto b = nlohmann::json::object();
|
||||
if (!builtin.value->isPrimOp()) continue;
|
||||
auto primOp = builtin.value->primOp;
|
||||
if (!primOp->doc) continue;
|
||||
b["arity"] = primOp->arity;
|
||||
b["args"] = primOp->args;
|
||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||
b["experimental-feature"] = primOp->experimentalFeature;
|
||||
builtinsJson[state.symbols[builtin.name]] = std::move(b);
|
||||
}
|
||||
std::move(builtinsJson);
|
||||
});
|
||||
res["constants"] = ({
|
||||
auto constantsJson = nlohmann::json::object();
|
||||
for (auto & [name, info] : state.constantInfos) {
|
||||
auto c = nlohmann::json::object();
|
||||
if (!info.doc) continue;
|
||||
c["doc"] = trim(stripIndentation(info.doc));
|
||||
c["type"] = showType(info.type, false);
|
||||
c["impure-only"] = info.impureOnly;
|
||||
constantsJson[name] = std::move(c);
|
||||
}
|
||||
std::move(constantsJson);
|
||||
});
|
||||
logger->cout("%s", res);
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue