blob: 8968ab7bdebfcc8b1670f2d3c5d27addd6c66d07 [file] [log] [blame]
//! > This crate is maintained by the Cargo team, primarily for use by Cargo
//! > and not intended for external use. This
//! > crate may make major changes to its APIs or be deprecated without warning.
#![allow(clippy::disallowed_methods)]
use cargo::GlobalContext;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use url::Url;
#[macro_export]
macro_rules! fixtures {
() => {
$crate::Fixtures::new(env!("CARGO_TARGET_TMPDIR"))
};
}
// This is an arbitrary commit that existed when I started. This helps
// ensure consistent results. It can be updated if needed, but that can
// make it harder to compare results with older versions of cargo.
const CRATES_IO_COMMIT: &str = "85f7bfd61ea4fee08ec68c468762e886b2aebec6";
pub struct Fixtures {
cargo_target_tmpdir: PathBuf,
}
impl Fixtures {
pub fn new(cargo_target_tmpdir: &str) -> Self {
let bench = Self {
cargo_target_tmpdir: PathBuf::from(cargo_target_tmpdir),
};
bench.create_home();
bench.create_target_dir();
bench.clone_index();
bench.unpack_workspaces();
bench
}
fn root(&self) -> PathBuf {
self.cargo_target_tmpdir.join("bench")
}
fn target_dir(&self) -> PathBuf {
let mut p = self.root();
p.push("target");
p
}
fn cargo_home(&self) -> PathBuf {
let mut p = self.root();
p.push("chome");
p
}
fn index(&self) -> PathBuf {
let mut p = self.root();
p.push("index");
p
}
fn workspaces_path(&self) -> PathBuf {
let mut p = self.root();
p.push("workspaces");
p
}
fn registry_url(&self) -> Url {
Url::from_file_path(self.index()).unwrap()
}
fn create_home(&self) {
let home = self.cargo_home();
if !home.exists() {
fs::create_dir_all(&home).unwrap();
}
fs::write(
home.join("config.toml"),
format!(
r#"
[source.crates-io]
replace-with = 'local-snapshot'
[source.local-snapshot]
registry = '{}'
"#,
self.registry_url()
),
)
.unwrap();
}
fn create_target_dir(&self) {
// This is necessary to ensure the .rustc_info.json file is written.
// Otherwise it won't be written, and it is very expensive to create.
if !self.target_dir().exists() {
fs::create_dir_all(self.target_dir()).unwrap();
}
}
/// This clones crates.io at a specific point in time into tmp/index.
fn clone_index(&self) {
let index = self.index();
let maybe_git = |command: &str| {
let status = Command::new("git")
.current_dir(&index)
.args(command.split_whitespace().collect::<Vec<_>>())
.status()
.expect("git should be installed");
status.success()
};
let git = |command: &str| {
if !maybe_git(command) {
panic!("failed to run git command: {}", command);
}
};
if index.exists() {
if maybe_git(&format!(
"rev-parse -q --verify {}^{{commit}}",
CRATES_IO_COMMIT
)) {
// Already fetched.
return;
}
} else {
fs::create_dir_all(&index).unwrap();
// git 2.48.0 changed the behavior of setting HEAD when doing a
// fetch, so let's just force it to match
// crates.io-index-archive's default branch. This also accounts
// for users who may override init.defaultBranch.
git("init --bare --initial-branch=main");
git("remote add origin https://github.com/rust-lang/crates.io-index-archive");
}
git(&format!("fetch origin {}", CRATES_IO_COMMIT));
git("branch -f main FETCH_HEAD");
}
/// This unpacks the compressed workspace skeletons into tmp/workspaces.
fn unpack_workspaces(&self) {
let ws_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("workspaces");
let archives = fs::read_dir(ws_dir)
.unwrap()
.map(|e| e.unwrap().path())
.filter(|p| p.extension() == Some(std::ffi::OsStr::new("tgz")));
for archive in archives {
let name = archive.file_stem().unwrap();
let f = fs::File::open(&archive).unwrap();
let f = flate2::read::GzDecoder::new(f);
let dest = self.workspaces_path().join(&name);
if dest.exists() {
fs::remove_dir_all(&dest).unwrap();
}
let mut archive = tar::Archive::new(f);
archive.unpack(self.workspaces_path()).unwrap();
}
}
/// Vec of `(ws_name, ws_root)`.
pub fn workspaces(&self) -> Vec<(String, PathBuf)> {
// CARGO_BENCH_WORKSPACES can be used to override, otherwise it just uses
// the workspaces in the workspaces directory.
let mut ps: Vec<_> = match std::env::var_os("CARGO_BENCH_WORKSPACES") {
Some(s) => std::env::split_paths(&s).collect(),
None => fs::read_dir(self.workspaces_path())
.unwrap()
.map(|e| e.unwrap().path())
// These currently fail in most cases on Windows due to long
// filenames in the git checkouts.
.filter(|p| {
!(cfg!(windows)
&& matches!(p.file_name().unwrap().to_str().unwrap(), "servo" | "tikv"))
})
.collect(),
};
// Sort so it is consistent.
ps.sort();
ps.into_iter()
.map(|p| (p.file_name().unwrap().to_str().unwrap().to_owned(), p))
.collect()
}
/// Creates a new Context.
pub fn make_context(&self, ws_root: &Path) -> GlobalContext {
let shell = cargo::core::Shell::new();
let mut gctx = GlobalContext::new(shell, ws_root.to_path_buf(), self.cargo_home());
// Configure is needed to set the target_dir which is needed to write
// the .rustc_info.json file which is very expensive.
gctx.configure(
0,
false,
None,
false,
false,
false,
&Some(self.target_dir()),
&[],
&[],
)
.unwrap();
gctx
}
}