83e81def23
When a file is added to the depot tree that is picked up by read-tree, but it’s not a function like ({...}: {}), `readTree` will fail on the function application, leading to a bad error message. We can do slightly better, by checking the type and throwing a nicer trace message. `assertMsg` is copied from `nixpkgs/lib/assert.nix`, since at this point we don’t have a reference to the lib. There is another evaluation failure that can happen, which is when the function we try to call does not have dots; however, nix does not provide any inflection capabilies for checking whether a function attrset is open (`builtins.functionArgs` only tells us the attrs it mentions explicitly). Maybe the locality of the error could be improved somehow. Change-Id: Ibe38ce78bb56902075f7c31f2eeeb93485b34be3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2469 Tested-by: BuildkiteCI Reviewed-by: tazjin <mail@tazj.in>
93 lines
2.7 KiB
Nix
93 lines
2.7 KiB
Nix
{ ... }:
|
||
|
||
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: parts:
|
||
let
|
||
dir = readDirVisible initPath;
|
||
self = importWithMark args initPath parts;
|
||
joinChild = c: initPath + ("/" + c);
|
||
|
||
# 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 (joinChild c) (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 args initPath [ (baseNameOf initPath) ];
|
||
}
|