a559135970
Plumbs an additional internal argument through readTree that indicates whether the top-level of a tree is being read, and avoids recursing into itself in that case. This changes the externally visible behaviour of readTree (it is now expected to be called a level higher than previously). This allows us to reduce the amount of boilerplate needed to bootstrap the TVL repository (by not having to specify the individual folders that need to be read). For reasons related to an infinite recursion we could not (be bothered to) debug, the top-level `config` key (which held the attribute set passed on by readTree) has been removed. This is not needed, as it is already passed on by readTree ... Co-Authored-By: Florian Klink <flokli@flokli.de> Change-Id: Id6e39b57b2f5b3473c4b695a72dd1d01fcfb7a66 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2961 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org> Reviewed-by: grfn <grfn@gws.fyi>
111 lines
3.1 KiB
Nix
111 lines
3.1 KiB
Nix
# Copyright (c) 2019 Vincent Ambo
|
||
# Copyright (c) 2020-2021 The TVL Authors
|
||
# SPDX-License-Identifier: MIT
|
||
#
|
||
# Provides a function to automatically read a a filesystem structure
|
||
# into a Nix attribute set.
|
||
{ ... }:
|
||
|
||
let
|
||
inherit (builtins)
|
||
attrNames
|
||
baseNameOf
|
||
concatStringsSep
|
||
filter
|
||
hasAttr
|
||
head
|
||
isAttrs
|
||
length
|
||
listToAttrs
|
||
map
|
||
match
|
||
readDir
|
||
substring;
|
||
|
||
assertMsg = pred: msg:
|
||
if pred
|
||
then true
|
||
else builtins.trace msg false;
|
||
|
||
argsWithPath = args: parts:
|
||
let meta.locatedAt = parts;
|
||
in meta // (if isAttrs args then args else args meta);
|
||
|
||
readDirVisible = path:
|
||
let
|
||
children = readDir path;
|
||
isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != ".";
|
||
names = filter isVisible (attrNames children);
|
||
in listToAttrs (map (name: {
|
||
inherit name;
|
||
value = children.${name};
|
||
}) names);
|
||
|
||
# Create a mark containing the location of this attribute.
|
||
marker = parts: {
|
||
__readTree = parts;
|
||
};
|
||
|
||
# The marker is added to every set that was imported directly by
|
||
# readTree.
|
||
importWithMark = args: path: parts:
|
||
let
|
||
importedFile = import path;
|
||
pathType = builtins.typeOf importedFile;
|
||
imported =
|
||
assert assertMsg
|
||
(pathType == "lambda")
|
||
"readTree: trying to import ${toString path}, but it’s a ${pathType}, you need to make it a function like { depot, pkgs, ... }";
|
||
importedFile (argsWithPath args parts);
|
||
in if (isAttrs imported)
|
||
then imported // (marker parts)
|
||
else imported;
|
||
|
||
nixFileName = file:
|
||
let res = match "(.*)\\.nix" file;
|
||
in if res == null then null else head res;
|
||
|
||
readTree = { args, initPath, rootDir, parts }:
|
||
let
|
||
dir = readDirVisible initPath;
|
||
joinChild = c: initPath + ("/" + c);
|
||
|
||
self = if rootDir
|
||
then { __readTree = []; }
|
||
else importWithMark args initPath parts;
|
||
|
||
# Import subdirectories of the current one, unless the special
|
||
# `.skip-subtree` file exists which makes readTree ignore the
|
||
# children.
|
||
#
|
||
# This file can optionally contain information on why the tree
|
||
# should be ignored, but its content is not inspected by
|
||
# readTree
|
||
filterDir = f: dir."${f}" == "directory";
|
||
children = if hasAttr ".skip-subtree" dir then [] else map (c: {
|
||
name = c;
|
||
value = readTree {
|
||
args = args;
|
||
initPath = (joinChild c);
|
||
rootDir = false;
|
||
parts = (parts ++ [ c ]);
|
||
};
|
||
}) (filter filterDir (attrNames dir));
|
||
|
||
# Import Nix files
|
||
nixFiles = filter (f: f != null) (map nixFileName (attrNames dir));
|
||
nixChildren = map (c: let p = joinChild (c + ".nix"); in {
|
||
name = c;
|
||
value = importWithMark args p (parts ++ [ c ]);
|
||
}) nixFiles;
|
||
in if dir ? "default.nix"
|
||
then (if isAttrs self then self // (listToAttrs children) else self)
|
||
else (listToAttrs (nixChildren ++ children) // (marker parts));
|
||
|
||
in {
|
||
__functor = _: args: initPath: readTree {
|
||
inherit args initPath;
|
||
rootDir = true;
|
||
parts = [];
|
||
};
|
||
}
|