aboutsummaryrefslogtreecommitdiff
path: root/filesystem.nix
diff options
context:
space:
mode:
Diffstat (limited to 'filesystem.nix')
-rw-r--r--filesystem.nix194
1 files changed, 194 insertions, 0 deletions
diff --git a/filesystem.nix b/filesystem.nix
new file mode 100644
index 000000000..5d048c0a1
--- /dev/null
+++ b/filesystem.nix
@@ -0,0 +1,194 @@
+# Functions for querying information about the filesystem
+# without copying any files to the Nix store.
+{ lib }:
+
+# Tested in lib/tests/filesystem.sh
+let
+ inherit (builtins)
+ readDir
+ pathExists
+ storeDir
+ ;
+
+ inherit (lib.trivial)
+ inPureEvalMode
+ ;
+
+ inherit (lib.path)
+ deconstruct
+ ;
+
+ inherit (lib.path.components)
+ fromSubpath
+ ;
+
+ inherit (lib.lists)
+ take
+ length
+ ;
+
+ inherit (lib.filesystem)
+ pathType
+ ;
+in
+
+{
+
+ /*
+ The type of a path. The path needs to exist and be accessible.
+ The result is either "directory" for a directory, "regular" for a regular file, "symlink" for a symlink, or "unknown" for anything else.
+
+ Type:
+ pathType :: Path -> String
+
+ Example:
+ pathType /.
+ => "directory"
+
+ pathType /some/file.nix
+ => "regular"
+ */
+ pathType =
+ let
+ storeDirComponents = fromSubpath "./${storeDir}";
+
+ legacy = path:
+ if ! pathExists path
+ # Fail irrecoverably to mimic the historic behavior of this function and
+ # the new builtins.readFileType
+ then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
+ # The filesystem root is the only path where `dirOf / == /` and
+ # `baseNameOf /` is not valid. We can detect this and directly return
+ # "directory", since we know the filesystem root can't be anything else.
+ else if dirOf path == path
+ then "directory"
+ else (readDir (dirOf path)).${baseNameOf path};
+
+ legacyPureEval = path:
+ let
+ # This is a workaround for the fact that `lib.filesystem.pathType` doesn't work correctly on /nix/store/...-name paths in pure evaluation mode. For file set combinators it's safe to assume that
+ pathParts = deconstruct path;
+ assumeDirectory =
+ storeDirComponents == take (length pathParts.components - 1) pathParts.components;
+ in
+ if assumeDirectory then
+ "directory"
+ else
+ legacy path;
+
+ in
+ if builtins ? readFileType then
+ builtins.readFileType
+ # Nix <2.14 compatibility shim
+ else if inPureEvalMode then
+ legacyPureEval
+ else
+ legacy;
+
+ /*
+ Whether a path exists and is a directory.
+
+ Type:
+ pathIsDirectory :: Path -> Bool
+
+ Example:
+ pathIsDirectory /.
+ => true
+
+ pathIsDirectory /this/does/not/exist
+ => false
+
+ pathIsDirectory /some/file.nix
+ => false
+ */
+ pathIsDirectory = path:
+ pathExists path && pathType path == "directory";
+
+ /*
+ Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
+
+ Type:
+ pathIsRegularFile :: Path -> Bool
+
+ Example:
+ pathIsRegularFile /.
+ => false
+
+ pathIsRegularFile /this/does/not/exist
+ => false
+
+ pathIsRegularFile /some/file.nix
+ => true
+ */
+ pathIsRegularFile = path:
+ pathExists path && pathType path == "regular";
+
+ /*
+ A map of all haskell packages defined in the given path,
+ identified by having a cabal file with the same name as the
+ directory itself.
+
+ Type: Path -> Map String Path
+ */
+ haskellPathsInDir =
+ # The directory within to search
+ root:
+ let # Files in the root
+ root-files = builtins.attrNames (builtins.readDir root);
+ # Files with their full paths
+ root-files-with-paths =
+ map (file:
+ { name = file; value = root + "/${file}"; }
+ ) root-files;
+ # Subdirectories of the root with a cabal file.
+ cabal-subdirs =
+ builtins.filter ({ name, value }:
+ builtins.pathExists (value + "/${name}.cabal")
+ ) root-files-with-paths;
+ in builtins.listToAttrs cabal-subdirs;
+ /*
+ Find the first directory containing a file matching 'pattern'
+ upward from a given 'file'.
+ Returns 'null' if no directories contain a file matching 'pattern'.
+
+ Type: RegExp -> Path -> Nullable { path : Path; matches : [ MatchResults ]; }
+ */
+ locateDominatingFile =
+ # The pattern to search for
+ pattern:
+ # The file to start searching upward from
+ file:
+ let go = path:
+ let files = builtins.attrNames (builtins.readDir path);
+ matches = builtins.filter (match: match != null)
+ (map (builtins.match pattern) files);
+ in
+ if builtins.length matches != 0
+ then { inherit path matches; }
+ else if path == /.
+ then null
+ else go (dirOf path);
+ parent = dirOf file;
+ isDir =
+ let base = baseNameOf file;
+ type = (builtins.readDir parent).${base} or null;
+ in file == /. || type == "directory";
+ in go (if isDir then file else parent);
+
+
+ /*
+ Given a directory, return a flattened list of all files within it recursively.
+
+ Type: Path -> [ Path ]
+ */
+ listFilesRecursive =
+ # The path to recursively list
+ dir:
+ lib.flatten (lib.mapAttrsToList (name: type:
+ if type == "directory" then
+ lib.filesystem.listFilesRecursive (dir + "/${name}")
+ else
+ dir + "/${name}"
+ ) (builtins.readDir dir));
+
+}