]>
Commit | Line | Data |
---|---|---|
4b012472 FG |
1 | #![allow(clippy::disallowed_methods)] |
2 | ||
0a29b90c FG |
3 | use cargo::Config; |
4 | use std::fs; | |
5 | use std::path::{Path, PathBuf}; | |
6 | use std::process::Command; | |
7 | use url::Url; | |
8 | ||
9 | #[macro_export] | |
10 | macro_rules! fixtures { | |
11 | () => { | |
12 | $crate::Fixtures::new(env!("CARGO_TARGET_TMPDIR")) | |
13 | }; | |
14 | } | |
15 | ||
16 | // This is an arbitrary commit that existed when I started. This helps | |
17 | // ensure consistent results. It can be updated if needed, but that can | |
18 | // make it harder to compare results with older versions of cargo. | |
19 | const CRATES_IO_COMMIT: &str = "85f7bfd61ea4fee08ec68c468762e886b2aebec6"; | |
20 | ||
21 | pub struct Fixtures { | |
22 | cargo_target_tmpdir: PathBuf, | |
23 | } | |
24 | ||
25 | impl Fixtures { | |
26 | pub fn new(cargo_target_tmpdir: &str) -> Self { | |
27 | let bench = Self { | |
28 | cargo_target_tmpdir: PathBuf::from(cargo_target_tmpdir), | |
29 | }; | |
30 | bench.create_home(); | |
31 | bench.create_target_dir(); | |
32 | bench.clone_index(); | |
33 | bench.unpack_workspaces(); | |
34 | bench | |
35 | } | |
36 | ||
37 | fn root(&self) -> PathBuf { | |
38 | self.cargo_target_tmpdir.join("bench") | |
39 | } | |
40 | ||
41 | fn target_dir(&self) -> PathBuf { | |
42 | let mut p = self.root(); | |
43 | p.push("target"); | |
44 | p | |
45 | } | |
46 | ||
47 | fn cargo_home(&self) -> PathBuf { | |
48 | let mut p = self.root(); | |
49 | p.push("chome"); | |
50 | p | |
51 | } | |
52 | ||
53 | fn index(&self) -> PathBuf { | |
54 | let mut p = self.root(); | |
55 | p.push("index"); | |
56 | p | |
57 | } | |
58 | ||
59 | fn workspaces_path(&self) -> PathBuf { | |
60 | let mut p = self.root(); | |
61 | p.push("workspaces"); | |
62 | p | |
63 | } | |
64 | ||
65 | fn registry_url(&self) -> Url { | |
66 | Url::from_file_path(self.index()).unwrap() | |
67 | } | |
68 | ||
69 | fn create_home(&self) { | |
70 | let home = self.cargo_home(); | |
71 | if !home.exists() { | |
72 | fs::create_dir_all(&home).unwrap(); | |
73 | } | |
74 | fs::write( | |
75 | home.join("config.toml"), | |
76 | format!( | |
77 | r#" | |
78 | [source.crates-io] | |
79 | replace-with = 'local-snapshot' | |
80 | ||
81 | [source.local-snapshot] | |
82 | registry = '{}' | |
83 | "#, | |
84 | self.registry_url() | |
85 | ), | |
86 | ) | |
87 | .unwrap(); | |
88 | } | |
89 | ||
90 | fn create_target_dir(&self) { | |
91 | // This is necessary to ensure the .rustc_info.json file is written. | |
92 | // Otherwise it won't be written, and it is very expensive to create. | |
93 | if !self.target_dir().exists() { | |
94 | fs::create_dir_all(self.target_dir()).unwrap(); | |
95 | } | |
96 | } | |
97 | ||
98 | /// This clones crates.io at a specific point in time into tmp/index. | |
99 | fn clone_index(&self) { | |
100 | let index = self.index(); | |
101 | let maybe_git = |command: &str| { | |
102 | let status = Command::new("git") | |
103 | .current_dir(&index) | |
104 | .args(command.split_whitespace().collect::<Vec<_>>()) | |
105 | .status() | |
106 | .expect("git should be installed"); | |
107 | status.success() | |
108 | }; | |
109 | let git = |command: &str| { | |
110 | if !maybe_git(command) { | |
111 | panic!("failed to run git command: {}", command); | |
112 | } | |
113 | }; | |
114 | if index.exists() { | |
115 | if maybe_git(&format!( | |
116 | "rev-parse -q --verify {}^{{commit}}", | |
117 | CRATES_IO_COMMIT | |
118 | )) { | |
119 | // Already fetched. | |
120 | return; | |
121 | } | |
122 | } else { | |
123 | fs::create_dir_all(&index).unwrap(); | |
124 | git("init --bare"); | |
125 | git("remote add origin https://github.com/rust-lang/crates.io-index-archive"); | |
126 | } | |
127 | git(&format!("fetch origin {}", CRATES_IO_COMMIT)); | |
128 | git("branch -f master FETCH_HEAD"); | |
129 | } | |
130 | ||
131 | /// This unpacks the compressed workspace skeletons into tmp/workspaces. | |
132 | fn unpack_workspaces(&self) { | |
133 | let ws_dir = Path::new(env!("CARGO_MANIFEST_DIR")) | |
134 | .parent() | |
135 | .unwrap() | |
136 | .join("workspaces"); | |
137 | let archives = fs::read_dir(ws_dir) | |
138 | .unwrap() | |
139 | .map(|e| e.unwrap().path()) | |
140 | .filter(|p| p.extension() == Some(std::ffi::OsStr::new("tgz"))); | |
141 | for archive in archives { | |
142 | let name = archive.file_stem().unwrap(); | |
143 | let f = fs::File::open(&archive).unwrap(); | |
144 | let f = flate2::read::GzDecoder::new(f); | |
145 | let dest = self.workspaces_path().join(&name); | |
146 | if dest.exists() { | |
147 | fs::remove_dir_all(&dest).unwrap(); | |
148 | } | |
149 | let mut archive = tar::Archive::new(f); | |
150 | archive.unpack(self.workspaces_path()).unwrap(); | |
151 | } | |
152 | } | |
153 | ||
154 | /// Vec of `(ws_name, ws_root)`. | |
155 | pub fn workspaces(&self) -> Vec<(String, PathBuf)> { | |
156 | // CARGO_BENCH_WORKSPACES can be used to override, otherwise it just uses | |
157 | // the workspaces in the workspaces directory. | |
158 | let mut ps: Vec<_> = match std::env::var_os("CARGO_BENCH_WORKSPACES") { | |
159 | Some(s) => std::env::split_paths(&s).collect(), | |
160 | None => fs::read_dir(self.workspaces_path()) | |
161 | .unwrap() | |
162 | .map(|e| e.unwrap().path()) | |
163 | // These currently fail in most cases on Windows due to long | |
164 | // filenames in the git checkouts. | |
165 | .filter(|p| { | |
166 | !(cfg!(windows) | |
167 | && matches!(p.file_name().unwrap().to_str().unwrap(), "servo" | "tikv")) | |
168 | }) | |
169 | .collect(), | |
170 | }; | |
171 | // Sort so it is consistent. | |
172 | ps.sort(); | |
173 | ps.into_iter() | |
174 | .map(|p| (p.file_name().unwrap().to_str().unwrap().to_owned(), p)) | |
175 | .collect() | |
176 | } | |
177 | ||
178 | /// Creates a new Config. | |
179 | pub fn make_config(&self, ws_root: &Path) -> Config { | |
180 | let shell = cargo::core::Shell::new(); | |
181 | let mut config = Config::new(shell, ws_root.to_path_buf(), self.cargo_home()); | |
182 | // Configure is needed to set the target_dir which is needed to write | |
183 | // the .rustc_info.json file which is very expensive. | |
184 | config | |
185 | .configure( | |
186 | 0, | |
187 | false, | |
188 | None, | |
189 | false, | |
190 | false, | |
191 | false, | |
192 | &Some(self.target_dir()), | |
193 | &[], | |
194 | &[], | |
195 | ) | |
196 | .unwrap(); | |
197 | config | |
198 | } | |
199 | } |