Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to pathType, pathIsDirectory and pathIsRegularFile #224834

Merged
merged 8 commits into from
May 23, 2023
5 changes: 3 additions & 2 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,11 @@ let
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
hiPrioSet getLicenseFromSpdxId getExe;
inherit (self.sources) pathType pathIsDirectory cleanSourceFilter
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile;
inherit (self.sources) cleanSourceFilter
cleanSource sourceByRegex sourceFilesBySuffices
commitIdFromGitRepo cleanSourceWith pathHasContext
canCleanSource pathIsRegularFile pathIsGitRepo;
canCleanSource pathIsGitRepo;
inherit (self.modules) evalModules setDefaultModuleLocation
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions
Expand Down
82 changes: 81 additions & 1 deletion lib/filesystem.nix
Original file line number Diff line number Diff line change
@@ -1,13 +1,93 @@
# Functions for copying sources to the Nix store.
# Functions for querying information about the filesystem
# without copying any files to the Nix store.
{ lib }:
roberth marked this conversation as resolved.
Show resolved Hide resolved

# Tested in lib/tests/filesystem.sh
let
inherit (builtins)
readDir
pathExists
;

inherit (lib.strings)
hasPrefix
;

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 =
builtins.readFileType or
# Nix <2.14 compatibility shim
(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}
);

/*
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
Expand Down
37 changes: 18 additions & 19 deletions lib/sources.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,11 @@ let
pathExists
readFile
;

/*
Returns the type of a path: regular (for file), symlink, or directory.
*/
pathType = path: getAttr (baseNameOf path) (readDir (dirOf path));

/*
Returns true if the path exists and is a directory, false otherwise.
*/
pathIsDirectory = path: if pathExists path then (pathType path) == "directory" else false;

/*
Returns true if the path exists and is a regular file, false otherwise.
*/
pathIsRegularFile = path: if pathExists path then (pathType path) == "regular" else false;
inherit (lib.filesystem)
pathType
pathIsDirectory
pathIsRegularFile
;

/*
A basic filter for `cleanSourceWith` that removes
Expand Down Expand Up @@ -271,11 +261,20 @@ let
};

in {
inherit
pathType
pathIsDirectory
pathIsRegularFile

pathType = lib.warnIf (lib.isInOldestRelease 2305)
"lib.sources.pathType has been moved to lib.filesystem.pathType."
lib.filesystem.pathType;

pathIsDirectory = lib.warnIf (lib.isInOldestRelease 2305)
"lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory."
lib.filesystem.pathIsDirectory;

pathIsRegularFile = lib.warnIf (lib.isInOldestRelease 2305)
"lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile."
lib.filesystem.pathIsRegularFile;

inherit
pathIsGitRepo
commitIdFromGitRepo

Expand Down
92 changes: 92 additions & 0 deletions lib/tests/filesystem.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bash
infinisil marked this conversation as resolved.
Show resolved Hide resolved

# Tests lib/filesystem.nix
# Run:
# [nixpkgs]$ lib/tests/filesystem.sh
# or:
# [nixpkgs]$ nix-build lib/tests/release.nix

set -euo pipefail
shopt -s inherit_errexit

# Use
# || die
die() {
echo >&2 "test case failed: " "$@"
exit 1
}

if test -n "${TEST_LIB:-}"; then
NIX_PATH=nixpkgs="$(dirname "$TEST_LIB")"
else
NIX_PATH=nixpkgs="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.."; pwd)"
fi
export NIX_PATH

work="$(mktemp -d)"
clean_up() {
rm -rf "$work"
}
trap clean_up EXIT
cd "$work"

mkdir directory
touch regular
ln -s target symlink
mkfifo fifo

checkPathType() {
local path=$1
local expectedPathType=$2
local actualPathType=$(nix-instantiate --eval --strict --json 2>&1 \
-E '{ path }: let lib = import <nixpkgs/lib>; in lib.filesystem.pathType path' \
--argstr path "$path")
if [[ "$actualPathType" != "$expectedPathType" ]]; then
die "lib.filesystem.pathType \"$path\" == $actualPathType, but $expectedPathType was expected"
fi
}

checkPathType "/" '"directory"'
checkPathType "$PWD/directory" '"directory"'
checkPathType "$PWD/regular" '"regular"'
checkPathType "$PWD/symlink" '"symlink"'
checkPathType "$PWD/fifo" '"unknown"'
checkPathType "$PWD/non-existent" "error: evaluation aborted with the following error message: 'lib.filesystem.pathType: Path $PWD/non-existent does not exist.'"

checkPathIsDirectory() {
local path=$1
local expectedIsDirectory=$2
local actualIsDirectory=$(nix-instantiate --eval --strict --json 2>&1 \
-E '{ path }: let lib = import <nixpkgs/lib>; in lib.filesystem.pathIsDirectory path' \
--argstr path "$path")
if [[ "$actualIsDirectory" != "$expectedIsDirectory" ]]; then
die "lib.filesystem.pathIsDirectory \"$path\" == $actualIsDirectory, but $expectedIsDirectory was expected"
fi
}

checkPathIsDirectory "/" "true"
checkPathIsDirectory "$PWD/directory" "true"
checkPathIsDirectory "$PWD/regular" "false"
checkPathIsDirectory "$PWD/symlink" "false"
checkPathIsDirectory "$PWD/fifo" "false"
checkPathIsDirectory "$PWD/non-existent" "false"

checkPathIsRegularFile() {
local path=$1
local expectedIsRegularFile=$2
local actualIsRegularFile=$(nix-instantiate --eval --strict --json 2>&1 \
-E '{ path }: let lib = import <nixpkgs/lib>; in lib.filesystem.pathIsRegularFile path' \
--argstr path "$path")
if [[ "$actualIsRegularFile" != "$expectedIsRegularFile" ]]; then
die "lib.filesystem.pathIsRegularFile \"$path\" == $actualIsRegularFile, but $expectedIsRegularFile was expected"
fi
}

checkPathIsRegularFile "/" "false"
checkPathIsRegularFile "$PWD/directory" "false"
checkPathIsRegularFile "$PWD/regular" "true"
checkPathIsRegularFile "$PWD/symlink" "false"
checkPathIsRegularFile "$PWD/fifo" "false"
checkPathIsRegularFile "$PWD/non-existent" "false"

echo >&2 tests ok
3 changes: 3 additions & 0 deletions lib/tests/release.nix
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pkgs.runCommand "nixpkgs-lib-tests" {
echo "Running lib/tests/modules.sh"
bash lib/tests/modules.sh

echo "Running lib/tests/filesystem.sh"
TEST_LIB=$PWD/lib bash lib/tests/filesystem.sh

echo "Running lib/tests/sources.sh"
TEST_LIB=$PWD/lib bash lib/tests/sources.sh

Expand Down