Skip to content

Commit bae1ca2

Browse files
committed
Systematize builtins.fetchTree docs
And also render the docs nicely. I would like to use a markdown AST for this, but to avoid new deps (lowdown's AST doesn't suffice) I am just doing crude string manipulations for now.
1 parent 2cc0b1b commit bae1ca2

File tree

12 files changed

+587
-295
lines changed

12 files changed

+587
-295
lines changed

src/libexpr/eval.cc

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -517,15 +517,16 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
517517
if (primOp.arity == 0) {
518518
primOp.arity = 1;
519519
auto vPrimOp = allocValue();
520-
vPrimOp->mkPrimOp(new PrimOp(primOp));
520+
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
521521
Value v;
522522
v.mkApp(vPrimOp, vPrimOp);
523+
auto & primOp1 = *vPrimOp->primOp();
523524
return addConstant(
524-
primOp.name,
525+
primOp1.name,
525526
v,
526527
{
527528
.type = nThunk, // FIXME
528-
.doc = primOp.doc,
529+
.doc = primOp1.doc ? primOp1.doc->c_str() : nullptr,
529530
});
530531
}
531532

@@ -565,13 +566,14 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
565566
{
566567
if (v.isPrimOp()) {
567568
auto v2 = &v;
568-
if (auto * doc = v2->primOp()->doc)
569+
auto & primOp = *v2->primOp();
570+
if (primOp.doc)
569571
return Doc{
570572
.pos = {},
571-
.name = v2->primOp()->name,
572-
.arity = v2->primOp()->arity,
573-
.args = v2->primOp()->args,
574-
.doc = doc,
573+
.name = primOp.name,
574+
.arity = primOp.arity,
575+
.args = primOp.args,
576+
.doc = primOp.doc->c_str(),
575577
};
576578
}
577579
if (v.isLambda()) {

src/libexpr/include/nix/expr/eval.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct PrimOp
108108
/**
109109
* Optional free-form documentation about the primop.
110110
*/
111-
const char * doc = nullptr;
111+
std::optional<std::string> doc;
112112

113113
/**
114114
* Add a trace item, while calling the `<name>` builtin.

src/libexpr/primops/fetchTree.cc

Lines changed: 95 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -235,229 +235,127 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, V
235235
static RegisterPrimOp primop_fetchTree({
236236
.name = "fetchTree",
237237
.args = {"input"},
238-
.doc = R"(
239-
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
240-
241-
- the resulting fixed-output [store path](@docroot@/store/store-path.md)
242-
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
243-
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
244-
245-
*input* must be an attribute set with the following attributes:
246-
247-
- `type` (String, required)
248-
249-
One of the [supported source types](#source-types).
250-
This determines other required and allowed input attributes.
251-
252-
- `narHash` (String, optional)
253-
254-
The `narHash` parameter can be used to substitute the source of the tree.
255-
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
256-
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
257-
258-
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
259-
That is, `fetchTree` is idempotent.
260-
261-
Downloads are cached in `$XDG_CACHE_HOME/nix`.
262-
The remote source is fetched from the network if both are true:
263-
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
264-
265-
> **Note**
266-
>
267-
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
268-
269-
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
270-
271-
## Source types
272-
273-
The following source types and associated input attributes are supported.
274-
275-
<!-- TODO: It would be soooo much more predictable to work with (and
276-
document) if `fetchTree` was a curried call with the first parameter for
277-
`type` or an attribute like `builtins.fetchTree.git`! -->
238+
.doc = []() -> std::string {
239+
std::string doc = stripIndentation(R"(
240+
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
278241
279-
- `"file"`
242+
- the resulting fixed-output [store path](@docroot@/store/store-path.md)
243+
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
244+
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
280245
281-
Place a plain file into the Nix store.
282-
This is similar to [`builtins.fetchurl`](@docroot@/language/builtins.md#builtins-fetchurl)
246+
*input* must be an attribute set with the following attributes:
283247
284-
- `url` (String, required)
248+
- `type` (String, required)
285249
286-
Supported protocols:
250+
One of the [supported source types](#source-types).
251+
This determines other required and allowed input attributes.
287252
288-
- `https`
253+
- `narHash` (String, optional)
289254
290-
> **Example**
291-
>
292-
> ```nix
293-
> fetchTree {
294-
> type = "file";
295-
> url = "https://example.com/index.html";
296-
> }
297-
> ```
255+
The `narHash` parameter can be used to substitute the source of the tree.
256+
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
257+
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
298258
299-
- `http`
259+
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
260+
That is, `fetchTree` is idempotent.
300261
301-
Insecure HTTP transfer for legacy sources.
262+
Downloads are cached in `$XDG_CACHE_HOME/nix`.
263+
The remote source is fetched from the network if both are true:
264+
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
302265
303-
> **Warning**
266+
> **Note**
304267
>
305-
> HTTP performs no encryption or authentication.
306-
> Use a `narHash` known in advance to ensure the output has expected contents.
307-
308-
- `file`
309-
310-
A file on the local file system.
311-
312-
> **Example**
313-
>
314-
> ```nix
315-
> fetchTree {
316-
> type = "file";
317-
> url = "file:///home/eelco/nix/README.md";
318-
> }
319-
> ```
320-
321-
- `"git"`
322-
323-
Fetch a Git tree and copy it to the Nix store.
324-
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
325-
326-
- `allRefs` (Bool, optional)
327-
328-
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
329-
330-
Whether to fetch all references (eg. branches and tags) of the repository.
331-
With this argument being true, it's possible to load a `rev` from *any* `ref`.
332-
(Without setting this option, only `rev`s from the specified `ref` are supported).
333-
334-
Default: `false`
335-
336-
- `lastModified` (Integer, optional)
337-
338-
Unix timestamp of the fetched commit.
339-
340-
If set, pass through the value to the output attribute set.
341-
Otherwise, generated from the fetched Git tree.
268+
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
342269
343-
- `lfs` (Bool, optional)
270+
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
344271
345-
Fetch any [Git LFS](https://git-lfs.com/) files.
272+
## Source types
346273
347-
Default: `false`
274+
The following source types and associated input attributes are supported.
348275
349-
- `ref` (String, optional)
276+
<!-- TODO: It would be soooo much more predictable to work with (and
277+
document) if `fetchTree` was a curried call with the first parameter for
278+
`type` or an attribute like `builtins.fetchTree.git`! -->
279+
)");
350280

351-
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
352-
353-
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
354-
355-
Default: `"HEAD"`
356-
357-
- `rev` (String, optional)
358-
359-
A Git revision; a commit hash.
360-
361-
Default: the tip of `ref`
362-
363-
- `revCount` (Integer, optional)
364-
365-
Number of revisions in the history of the Git repository before the fetched commit.
366-
367-
If set, pass through the value to the output attribute set.
368-
Otherwise, generated from the fetched Git tree.
369-
370-
- `shallow` (Bool, optional)
371-
372-
Make a shallow clone when fetching the Git tree.
373-
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
374-
375-
Default: `true`
376-
377-
- `submodules` (Bool, optional)
378-
379-
Also fetch submodules if available.
281+
auto indentString = [](std::string const & str, std::string const & indent) {
282+
std::string result;
283+
std::istringstream stream(str);
284+
std::string line;
285+
bool first = true;
286+
while (std::getline(stream, line)) {
287+
if (!first)
288+
result += "\n";
289+
result += indent + line;
290+
first = false;
291+
}
292+
return result;
293+
};
294+
295+
for (const auto & [schemeName, scheme] : fetchers::getAllInputSchemes()) {
296+
doc += "\n- `" + quoteString(schemeName, '"') + "`\n\n";
297+
doc += indentString(scheme->schemeDescription(), " ");
298+
if (!doc.empty() && doc.back() != '\n')
299+
doc += "\n";
300+
301+
for (const auto & [attrName, attribute] : scheme->allowedAttrs()) {
302+
doc += "\n - `" + attrName + "` (" + attribute.type + ", "
303+
+ (attribute.required ? "required" : "optional") + ")\n\n";
304+
doc += indentString(stripIndentation(attribute.doc), " ");
305+
if (!doc.empty() && doc.back() != '\n')
306+
doc += "\n";
307+
}
308+
}
380309

381-
Default: `false`
310+
doc += "\n" + stripIndentation(R"(
311+
The following input types are still subject to change:
382312
383-
- `url` (String, required)
313+
- `"path"`
314+
- `"github"`
315+
- `"gitlab"`
316+
- `"sourcehut"`
317+
- `"mercurial"`
384318
385-
The URL formats supported are the same as for Git itself.
319+
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
320+
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
386321
387322
> **Example**
388323
>
324+
> Fetch a GitHub repository using the attribute set representation:
325+
>
389326
> ```nix
390-
> fetchTree {
391-
> type = "git";
392-
> url = "[email protected]:NixOS/nixpkgs.git";
327+
> builtins.fetchTree {
328+
> type = "github";
329+
> owner = "NixOS";
330+
> repo = "nixpkgs";
331+
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
332+
> }
333+
> ```
334+
>
335+
> This evaluates to the following attribute set:
336+
>
337+
> ```nix
338+
> {
339+
> lastModified = 1686503798;
340+
> lastModifiedDate = "20230611171638";
341+
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
342+
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
343+
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
344+
> shortRev = "ae2e6b3";
393345
> }
394346
> ```
395347
396-
> **Note**
348+
> **Example**
397349
>
398-
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but use the *current file contents* of the Git working directory.
399-
400-
- `"tarball"`
401-
402-
Download a tar archive and extract it into the Nix store.
403-
This has the same underlying implementation as [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball)
404-
405-
- `url` (String, required)
406-
407-
> **Example**
408-
>
409-
> ```nix
410-
> fetchTree {
411-
> type = "tarball";
412-
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
413-
> }
414-
> ```
415-
416-
The following input types are still subject to change:
417-
418-
- `"path"`
419-
- `"github"`
420-
- `"gitlab"`
421-
- `"sourcehut"`
422-
- `"mercurial"`
423-
424-
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
425-
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
426-
427-
> **Example**
428-
>
429-
> Fetch a GitHub repository using the attribute set representation:
430-
>
431-
> ```nix
432-
> builtins.fetchTree {
433-
> type = "github";
434-
> owner = "NixOS";
435-
> repo = "nixpkgs";
436-
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
437-
> }
438-
> ```
439-
>
440-
> This evaluates to the following attribute set:
441-
>
442-
> ```nix
443-
> {
444-
> lastModified = 1686503798;
445-
> lastModifiedDate = "20230611171638";
446-
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
447-
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
448-
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
449-
> shortRev = "ae2e6b3";
450-
> }
451-
> ```
452-
453-
> **Example**
454-
>
455-
> Fetch the same GitHub repository using the URL-like syntax:
456-
>
457-
> ```nix
458-
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
459-
> ```
460-
)",
350+
> Fetch the same GitHub repository using the URL-like syntax:
351+
>
352+
> ```nix
353+
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
354+
> ```
355+
)");
356+
357+
return doc;
358+
}(),
461359
.fun = prim_fetchTree,
462360
.experimentalFeature = Xp::FetchTree,
463361
});

src/libfetchers/fetchers.cc

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,9 @@ void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
2626
throw Error("Input scheme with name %s already registered", schemeName);
2727
}
2828

29-
nlohmann::json dumpRegisterInputSchemeInfo()
29+
const InputSchemeMap & getAllInputSchemes()
3030
{
31-
using nlohmann::json;
32-
33-
auto res = json::object();
34-
35-
for (auto & [name, scheme] : inputSchemes()) {
36-
auto & r = res[name] = json::object();
37-
r["allowedAttrs"] = scheme->allowedAttrs();
38-
}
39-
40-
return res;
31+
return inputSchemes();
4132
}
4233

4334
Input Input::fromURL(const Settings & settings, const std::string & url, bool requireTree)

0 commit comments

Comments
 (0)