Skip to content

Commit

Permalink
Add support for manifest based runfiles. (#760)
Browse files Browse the repository at this point in the history
* Add support for manifest based runfiles.

These are typically used on Windows when using symlinks which are needed
for directory based runfiles is not possible.
  • Loading branch information
hlopko committed Jun 2, 2021
1 parent 031214d commit b8219ac
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 13 deletions.
2 changes: 0 additions & 2 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ tasks:
- "-//test/chained_direct_deps:mod3_doc_test"
- "-@examples//fibonacci:fibonacci_doc_test"
- "-@examples//hello_lib:hello_lib_doc_test"
- "-//tools/runfiles:runfiles_doc_test"
- "-@examples//ffi/rust_calling_c/simple/..."
# See https://github.com/bazelbuild/bazel/issues/9987
- "-@examples//ffi/rust_calling_c:matrix_dylib_test"
Expand All @@ -71,7 +70,6 @@ tasks:
- "-//test/test_env/..."
- "-//test/proto/..."
- "-//tools/rust_analyzer/..."
- "-//tools/runfiles/..."
- "-//test/rustfmt/..."
- "@examples//..."
- "-@examples//ffi/rust_calling_c:matrix_dylib_test"
Expand Down
99 changes: 88 additions & 11 deletions tools/runfiles/runfiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! ```
//!
//! 2. Import the runfiles library.
//! ```
//! ```ignore
//! extern crate runfiles;
//!
//! use runfiles::Runfiles;
Expand All @@ -31,23 +31,55 @@
//! // ...
//! ```

use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::Path;
use std::path::PathBuf;

enum Mode {
DirectoryBased(PathBuf),
ManifestBased(HashMap<PathBuf, PathBuf>),
}

pub struct Runfiles {
runfiles_dir: PathBuf,
mode: Mode,
}

impl Runfiles {
/// Creates a directory based Runfiles object.
///
/// Manifest based creation is not currently supported.
/// Creates a manifest based Runfiles object when
/// RUNFILES_MANIFEST_ONLY environment variable is present,
/// or a directory based Runfiles object otherwise.
pub fn create() -> io::Result<Self> {
if is_manifest_only() {
Self::create_manifest_based()
} else {
Self::create_directory_based()
}
}

fn create_directory_based() -> io::Result<Self> {
Ok(Runfiles {
mode: Mode::DirectoryBased(find_runfiles_dir()?),
})
}

fn create_manifest_based() -> io::Result<Self> {
let manifest_path = find_manifest_path()?;
let manifest_content = std::fs::read_to_string(manifest_path)?;
let path_mapping = manifest_content
.lines()
.map(|line| {
let pair = line
.split_once(" ")
.expect("manifest file contained unexpected content");
(pair.0.into(), pair.1.into())
})
.collect::<HashMap<_, _>>();
Ok(Runfiles {
runfiles_dir: find_runfiles_dir()?,
mode: Mode::ManifestBased(path_mapping),
})
}

Expand All @@ -61,12 +93,25 @@ impl Runfiles {
if path.is_absolute() {
return path.to_path_buf();
}
self.runfiles_dir.join(path)
match &self.mode {
Mode::DirectoryBased(runfiles_dir) => runfiles_dir.join(path),
Mode::ManifestBased(path_mapping) => path_mapping
.get(path)
.expect(&format!(
"Path {} not found among runfiles.",
path.to_string_lossy()
))
.clone(),
}
}
}

/// Returns the .runfiles directory for the currently executing binary.
pub fn find_runfiles_dir() -> io::Result<PathBuf> {
assert_ne!(
std::env::var_os("RUNFILES_MANIFEST_ONLY").unwrap_or(OsString::from("0")),
"1"
);
let exec_path = std::env::args().nth(0).expect("arg 0 was not set");

let mut binary_path = PathBuf::from(&exec_path);
Expand Down Expand Up @@ -108,10 +153,31 @@ pub fn find_runfiles_dir() -> io::Result<PathBuf> {
}
}

Err(io::Error::new(
io::ErrorKind::Other,
"Failed to find .runfiles directory.",
))
Err(make_io_error("failed to find .runfiles directory"))
}

fn make_io_error(msg: &str) -> io::Error {
io::Error::new(io::ErrorKind::Other, msg)
}

fn is_manifest_only() -> bool {
match std::env::var("RUNFILES_MANIFEST_ONLY") {
Ok(val) => val == "1",
Err(_) => false,
}
}

fn find_manifest_path() -> io::Result<PathBuf> {
assert_eq!(
std::env::var_os("RUNFILES_MANIFEST_ONLY").expect("RUNFILES_MANIFEST_ONLY was not set"),
OsString::from("1")
);
match std::env::var_os("RUNFILES_MANIFEST_FILE") {
Some(path) => Ok(path.into()),
None => Err(
make_io_error(
"RUNFILES_MANIFEST_ONLY was set to '1', but RUNFILES_MANIFEST_FILE was not set. Did Bazel change?"))
}
}

#[cfg(test)]
Expand All @@ -132,4 +198,15 @@ mod test {

assert_eq!("Example Text!", buffer);
}

#[test]
fn test_manifest_based_can_read_data_from_runfiles() {
let mut path_mapping = HashMap::new();
path_mapping.insert("a/b".into(), "c/d".into());
let r = Runfiles {
mode: Mode::ManifestBased(path_mapping),
};

assert_eq!(r.rlocation("a/b"), PathBuf::from("c/d"));
}
}

0 comments on commit b8219ac

Please sign in to comment.