feat(tvix/eval): Implement builtins.readFileType

builtins.readFileType was added to Nix back in version 2.14.

The tests were also moved out of notyetpassing in addition to the
readDir fixtures they depend on.

I caught a bug where we previously used std::fs::metadata (via the
.metadata() method on File) which follows symlinks so it would always
return false for is_symlink(). Instead we now use
std::fs::symlink_metadata directly which does not follow symlinks, so
tests now pass. This wasn't an issue for builtins.readDir as it uses
walkdir and walkdir doesn't follow symlinks either.

Change-Id: I58eb97bdb5ec95df4f6882f495f8c572fe7c6793
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12130
Reviewed-by: flokli <flokli@flokli.de>
Autosubmit: Ilan Joselevich <personal@ilanjoselevich.com>
Tested-by: BuildkiteCI
This commit is contained in:
Ilan Joselevich 2024-08-04 01:00:25 +03:00
parent 9c4b57ac63
commit 3511e328ec
10 changed files with 47 additions and 3 deletions

View file

@ -81,6 +81,18 @@ mod impure_builtins {
}
}
}
#[builtin("readFileType")]
async fn builtin_read_file_type(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
match coerce_value_to_path(&co, path).await? {
Err(cek) => Ok(Value::from(cek)),
Ok(path) => Ok(Value::from(
generators::request_read_file_type(&co, path)
.await
.to_string(),
)),
}
}
}
/// Return all impure builtins, that is all builtins which may perform I/O

View file

@ -26,7 +26,7 @@ use std::os::unix::ffi::OsStringExt;
#[cfg(feature = "impure")]
use std::fs::File;
/// Types of files as represented by `builtins.readDir` in Nix.
/// Types of files as represented by `builtins.readFileType` and `builtins.readDir` in Nix.
#[derive(Debug)]
pub enum FileType {
Directory,
@ -120,7 +120,7 @@ impl EvalIO for StdIO {
}
fn file_type(&self, path: &Path) -> io::Result<FileType> {
let file_type = File::open(path)?.metadata()?.file_type();
let file_type = std::fs::symlink_metadata(path)?;
Ok(if file_type.is_dir() {
FileType::Directory

View file

@ -121,6 +121,9 @@ pub enum VMRequest {
/// Request serialisation of a value to JSON, according to the
/// slightly odd Nix evaluation rules.
ToJson(Value),
/// Request the VM for the file type of the given path.
ReadFileType(PathBuf),
}
/// Human-readable representation of a generator message, used by observers.
@ -178,6 +181,7 @@ impl Display for VMRequest {
VMRequest::Span => write!(f, "span"),
VMRequest::TryForce(v) => write!(f, "try_force({})", v.type_of()),
VMRequest::ToJson(v) => write!(f, "to_json({})", v.type_of()),
VMRequest::ReadFileType(p) => write!(f, "read_file_type({})", p.to_string_lossy()),
}
}
}
@ -202,6 +206,8 @@ pub enum VMResponse {
/// [std::io::Reader] produced by the VM in response to some IO operation.
Reader(Box<dyn std::io::Read>),
FileType(FileType),
}
impl Display for VMResponse {
@ -213,6 +219,7 @@ impl Display for VMResponse {
VMResponse::Directory(d) => write!(f, "dir(len = {})", d.len()),
VMResponse::Span(_) => write!(f, "span"),
VMResponse::Reader(_) => write!(f, "reader"),
VMResponse::FileType(t) => write!(f, "file_type({})", t),
}
}
}
@ -497,6 +504,20 @@ where
});
return Ok(false);
}
VMRequest::ReadFileType(path) => {
let file_type = self
.io_handle
.as_ref()
.file_type(&path)
.map_err(|e| ErrorKind::IO {
path: Some(path),
error: e.into(),
})
.with_span(span, self)?;
message = VMResponse::FileType(file_type);
}
}
}
@ -791,6 +812,17 @@ pub(crate) async fn request_to_json(
}
}
#[cfg_attr(not(feature = "impure"), allow(unused))]
pub(crate) async fn request_read_file_type(co: &GenCo, path: PathBuf) -> FileType {
match co.yield_(VMRequest::ReadFileType(path)).await {
VMResponse::FileType(file_type) => file_type,
msg => panic!(
"Tvix bug: VM responded with incorrect generator message: {}",
msg
),
}
}
/// Call the given value as if it was an attribute set containing a functor. The
/// arguments must already be prepared on the stack when a generator frame from
/// this function is invoked.

View file

@ -70,7 +70,7 @@ let
"eval-okay-cycle-display-cpp-nix-2.13.nix" = [ nix_2_3 ];
# builtins.replaceStrings becomes lazier in Nix 2.16
"eval-okay-replacestrings.nix" = [ nix_2_3 ];
# builtins.readFileType is added in Nix 2.15
# builtins.readFileType is added in Nix 2.14
"eval-okay-readFileType.nix" = [ nix_2_3 ];
# builtins.fromTOML gains support for timestamps in Nix 2.16
"eval-okay-fromTOML-timestamps.nix" = [ nix_2_3 ];