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) ];
 | ||
| }
 |