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