Change-Id: I040fbcbf1eefa69a2e37bf6e40fa6bc453ea1c90 Reviewed-on: https://cl.tvl.fyi/c/depot/+/13179 Tested-by: BuildkiteCI Reviewed-by: tazjin <tazjin@tvl.su> Autosubmit: sterni <sternenseemann@systemli.org>
		
			
				
	
	
		
			182 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ depot, lib, ... }:
 | 
						|
 | 
						|
let
 | 
						|
  /* Get the basename of a store path without
 | 
						|
     the leading hash.
 | 
						|
 | 
						|
     Type: (path | drv | string) -> string
 | 
						|
 | 
						|
     Example:
 | 
						|
       storePathName ./foo.c
 | 
						|
       => "foo.c"
 | 
						|
 | 
						|
       storePathName (writeText "foo.c" "int main() { return 0; }")
 | 
						|
       => "foo.c"
 | 
						|
 | 
						|
       storePathName "${hello}/bin/hello"
 | 
						|
       => "hello"
 | 
						|
  */
 | 
						|
  storePathName = p:
 | 
						|
    if lib.isDerivation p
 | 
						|
    then p.name
 | 
						|
    else if builtins.isPath p
 | 
						|
    then builtins.baseNameOf p
 | 
						|
    else if builtins.isString p || (builtins.isAttrs p && (p ? outPath || p ? __toString))
 | 
						|
    then
 | 
						|
      let
 | 
						|
        strPath = toString p;
 | 
						|
        # strip leading storeDir and trailing slashes
 | 
						|
        noStoreDir = lib.removeSuffix "/"
 | 
						|
          (lib.removePrefix "${builtins.storeDir}/" strPath);
 | 
						|
        # a basename of a child of a store path isn't really
 | 
						|
        # referring to a store path, so removing the string
 | 
						|
        # context is safe (e. g. "hello" for "${hello}/bin/hello").
 | 
						|
        basename = builtins.unsafeDiscardStringContext
 | 
						|
          (builtins.baseNameOf strPath);
 | 
						|
      in
 | 
						|
      # If p is a direct child of storeDir, we need to remove
 | 
						|
        # the leading hash as well to make sure that:
 | 
						|
        # `storePathName drv == storePathName (toString drv)`.
 | 
						|
      if noStoreDir == basename
 | 
						|
      then builtins.substring 33 (-1) basename
 | 
						|
      else basename
 | 
						|
    else builtins.throw "Don't know how to get (base)name of "
 | 
						|
      + lib.generators.toPretty { } p;
 | 
						|
 | 
						|
  /* Query the type of a path exposing the same information as would be by
 | 
						|
     `builtins.readDir`, but for a single, specific target path.
 | 
						|
 | 
						|
     The information is returned as a tagged value, i. e. an attribute set with
 | 
						|
     exactly one attribute where the type of the path is encoded in the name
 | 
						|
     of the single attribute. The allowed tags and values are as follows:
 | 
						|
 | 
						|
     * `regular`: is a regular file, always `true` if returned
 | 
						|
     * `directory`: is a directory, always `true` if returned
 | 
						|
     * `missing`: path does not exist, always `true` if returned
 | 
						|
     * `symlink`: path is a symlink, always `true` if returned
 | 
						|
 | 
						|
     Type: path(-like) -> tag
 | 
						|
 | 
						|
     `tag` refers to the attribute set format of `//nix/tag`.
 | 
						|
 | 
						|
     Example:
 | 
						|
       pathType ./foo.c
 | 
						|
       => { regular = true; }
 | 
						|
 | 
						|
       pathType /home/lukas
 | 
						|
       => { directory = true; }
 | 
						|
 | 
						|
       pathType ./result
 | 
						|
       => { symlink = true; }
 | 
						|
 | 
						|
       pathType ./link-to-file
 | 
						|
       => { symlink = true; }
 | 
						|
 | 
						|
       pathType /does/not/exist
 | 
						|
       => { missing = true; }
 | 
						|
 | 
						|
       # Check if a path exists
 | 
						|
       !(pathType /file ? missing)
 | 
						|
 | 
						|
       # Check if a path is a directory or a symlink to a directory
 | 
						|
       # A handy shorthand for this is provided as `realPathIsDirectory`.
 | 
						|
       pathType /path ? directory || (pathType /path).symlink or null == "directory"
 | 
						|
 | 
						|
       # Match on the result using //nix/tag
 | 
						|
       nix.tag.match (nix.utils.pathType ./result) {
 | 
						|
         symlink = _: "symlink";
 | 
						|
         directory  = _: "directory";
 | 
						|
         regular = _: "regular";
 | 
						|
         missing = _: "path does not exist";
 | 
						|
       }
 | 
						|
       => "symlink"
 | 
						|
 | 
						|
       # Query path type
 | 
						|
       nix.tag.tagName (pathType /path)
 | 
						|
  */
 | 
						|
  pathType = path:
 | 
						|
    let
 | 
						|
      # baseNameOf is very annoyed if we proceed with string context.
 | 
						|
      # We need to call toString to prevent unsafeDiscardStringContext
 | 
						|
      # from importing a path into store which messes with base- and
 | 
						|
      # dirname of course.
 | 
						|
      path' = builtins.unsafeDiscardStringContext (toString path);
 | 
						|
      # To read the containing directory we absolutely need
 | 
						|
      # to keep the string context, otherwise a derivation
 | 
						|
      # would not be realized before our check (at eval time)
 | 
						|
      containingDir = builtins.readDir (builtins.dirOf path);
 | 
						|
      # Construct tag to use for the value
 | 
						|
      thisPathType = containingDir.${builtins.baseNameOf path'} or "missing";
 | 
						|
      # Trick to check if the symlink target exists and is a directory:
 | 
						|
      # if we append a "/." to the string version of the path, Nix won't
 | 
						|
      # canocalize it (which would strip any "/." in the path), so if
 | 
						|
      # path' + "/." exists, we know that the symlink points to an existing
 | 
						|
      # directory. If not, either the target doesn't exist or is a regular file.
 | 
						|
      # TODO(sterni): is there a way to check reliably if the symlink target exists?
 | 
						|
      isSymlinkDir = builtins.pathExists (path' + "/.");
 | 
						|
    in
 | 
						|
    {
 | 
						|
      ${thisPathType} = true;
 | 
						|
    };
 | 
						|
 | 
						|
  pathType' = path:
 | 
						|
    let
 | 
						|
      p = pathType path;
 | 
						|
    in
 | 
						|
    if p ? missing
 | 
						|
    then builtins.throw "${lib.generators.toPretty {} path} does not exist"
 | 
						|
    else p;
 | 
						|
 | 
						|
  /* Check whether the given path is a directory.
 | 
						|
     Throws if the path in question doesn't exist.
 | 
						|
 | 
						|
     Type: path(-like) -> bool
 | 
						|
  */
 | 
						|
  isDirectory = path: pathType' path ? directory;
 | 
						|
 | 
						|
  /* Check whether the given path is a regular file.
 | 
						|
     Throws if the path in question doesn't exist.
 | 
						|
 | 
						|
     Type: path(-like) -> bool
 | 
						|
  */
 | 
						|
  isRegularFile = path: pathType' path ? regular;
 | 
						|
 | 
						|
  /* Check whether the given path is a symbolic link.
 | 
						|
     Throws if the path in question doesn't exist.
 | 
						|
 | 
						|
     Type: path(-like) -> bool
 | 
						|
  */
 | 
						|
  isSymlink = path: pathType' path ? symlink;
 | 
						|
 | 
						|
  /* Checks whether the given value is (or contains) a reference to a
 | 
						|
     path that will be retained in the store path resulting from a derivation.
 | 
						|
     So if isReferencablePath returns true, the given value may be used in a
 | 
						|
     way that allows accessing it at runtime of any Nix built program.
 | 
						|
 | 
						|
     Returns true for:
 | 
						|
 | 
						|
     - Strings with context (if the string is/contains a single path is not verified!)
 | 
						|
     - Path values
 | 
						|
     - Derivations
 | 
						|
 | 
						|
     Note that the value still needs to used in a way that forces string context
 | 
						|
     (and thus reference tracking) to be created, e.g. in string interpolation.
 | 
						|
 | 
						|
     Type: any -> bool
 | 
						|
  */
 | 
						|
  isReferencablePath = value:
 | 
						|
    builtins.isPath value
 | 
						|
    || lib.isDerivation value
 | 
						|
    || (builtins.isString value && builtins.hasContext value);
 | 
						|
 | 
						|
in
 | 
						|
{
 | 
						|
  inherit
 | 
						|
    storePathName
 | 
						|
    pathType
 | 
						|
    isDirectory
 | 
						|
    isRegularFile
 | 
						|
    isSymlink
 | 
						|
    isReferencablePath
 | 
						|
    ;
 | 
						|
}
 |