Skip to content

Commit 560fcb6

Browse files
committed
Only compile executables once per process
Use auxiliary files to store version information instead of embedding it directly in the executable.
1 parent 42d84c6 commit 560fcb6

File tree

3 files changed

+96
-133
lines changed

3 files changed

+96
-133
lines changed

src/rustup-mock/src/clitools.rs

Lines changed: 52 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
//! tests/cli-v2.rs
33
44
use std::cell::RefCell;
5-
use std::collections::HashMap;
65
use std::env::consts::EXE_SUFFIX;
76
use std::env;
87
use std::fs::{self, File};
98
use std::io::{self, Read, Write};
109
use std::mem;
1110
use std::path::{PathBuf, Path};
1211
use std::process::{Command, Stdio};
13-
use std::sync::{Arc, Mutex};
12+
use std::sync::Arc;
1413
use std::time::Duration;
1514
use tempdir::TempDir;
1615
use {MockInstallerBuilder, MockFile, MockComponentBuilder};
@@ -565,10 +564,7 @@ fn build_mock_rustc_installer(target: &str, version: &str, version_hash_: &str)
565564
MockInstallerBuilder {
566565
components: vec![MockComponentBuilder {
567566
name: "rustc".to_string(),
568-
files: vec![
569-
MockFile::new_arc(format!("bin/rustc{}", EXE_SUFFIX),
570-
mock_bin("rustc", version, &version_hash)),
571-
],
567+
files: mock_bin("rustc", version, &version_hash),
572568
}],
573569
}
574570
}
@@ -577,10 +573,7 @@ fn build_mock_cargo_installer(version: &str, version_hash: &str) -> MockInstalle
577573
MockInstallerBuilder {
578574
components: vec![MockComponentBuilder {
579575
name: "cargo".to_string(),
580-
files: vec![
581-
MockFile::new_arc(format!("bin/cargo{}", EXE_SUFFIX),
582-
mock_bin("cargo", version, &version_hash)),
583-
],
576+
files: mock_bin("cargo", version, &version_hash),
584577
}],
585578
}
586579
}
@@ -589,10 +582,7 @@ fn build_mock_rls_installer(version: &str, version_hash: &str) -> MockInstallerB
589582
MockInstallerBuilder {
590583
components: vec![MockComponentBuilder {
591584
name: "rls".to_string(),
592-
files: vec![
593-
MockFile::new_arc(format!("bin/rls{}", EXE_SUFFIX),
594-
mock_bin("rls", version, &version_hash)),
595-
],
585+
files: mock_bin("rls", version, version_hash),
596586
}],
597587
}
598588
}
@@ -640,112 +630,68 @@ fn build_combined_installer(components: &[&MockInstallerBuilder]) -> MockInstall
640630
/// prints some version information. These binaries are stuffed into
641631
/// the mock installers so we have executables for rustup to run.
642632
///
643-
/// This does a really crazy thing. Because we need to generate a lot
644-
/// of these, and running rustc is slow, it does it once, stuffs the
645-
/// bin into memory, then does a string replacement of the version
646-
/// information it needs to report to create subsequent bins.
647-
fn mock_bin(_name: &str, version: &str, version_hash: &str) -> Arc<Vec<u8>> {
633+
/// To avoid compiling tons of files we globally cache one compiled executable
634+
/// and then we store some associated files next to it which indicate
635+
/// the version/version hash information.
636+
fn mock_bin(name: &str, version: &str, version_hash: &str) -> Vec<MockFile> {
648637
lazy_static! {
649-
static ref MOCK_BIN_TEMPLATE: Mutex<HashMap<(String, String), Arc<Vec<u8>>>> =
650-
Mutex::new(HashMap::new());
651-
}
652-
653-
let key = (version.to_string(), version_hash.to_string());
654-
let map = MOCK_BIN_TEMPLATE.lock().unwrap();
655-
if let Some(ret) = map.get(&key) {
656-
return ret.clone()
657-
}
658-
drop(map);
659-
660-
struct Stderr;
661-
use std::fmt::{self, Write};
662-
663-
impl fmt::Write for Stderr {
664-
fn write_str(&mut self, s: &str) -> fmt::Result {
665-
unsafe {
666-
extern {
667-
fn write(a: i32, b: *const u8, c: usize) -> isize;
668-
}
669-
write(2, s.as_ptr(), s.len());
670-
Ok(())
638+
static ref MOCK_BIN: Arc<Vec<u8>> = {
639+
// Create a temp directory to hold the source and the output
640+
let ref tempdir = TempDir::new("rustup").unwrap();
641+
let ref source_path = tempdir.path().join("in.rs");
642+
let ref dest_path = tempdir.path().join(&format!("out{}", EXE_SUFFIX));
643+
644+
// Write the source
645+
let source = include_str!("mock_bin_src.rs");
646+
File::create(source_path).and_then(|mut f| f.write_all(source.as_bytes())).unwrap();
647+
648+
// Create the executable
649+
let status = Command::new("rustc")
650+
.arg(&source_path)
651+
.arg("-C").arg("panic=abort")
652+
.arg("-O")
653+
.arg("-o").arg(&dest_path)
654+
.status()
655+
.unwrap();
656+
assert!(status.success());
657+
assert!(dest_path.exists());
658+
659+
// If we're on unix this will remove debuginfo, otherwise we just ignore
660+
// the return result here
661+
if cfg!(unix) {
662+
drop(Command::new("strip").arg(&dest_path).status());
671663
}
672-
}
673-
}
674-
drop(write!(Stderr, "cache miss {:?}\n", key));
675-
676-
// Create a temp directory to hold the source and the output
677-
let ref tempdir = TempDir::new("rustup").unwrap();
678-
let ref source_path = tempdir.path().join("in.rs");
679-
let ref dest_path = tempdir.path().join(&format!("out{}", EXE_SUFFIX));
680-
681-
// Write the source
682-
let source = include_str!("mock_bin_src.rs");
683-
File::create(source_path).and_then(|mut f| f.write_all(source.as_bytes())).unwrap();
684664

685-
// Create the executable
686-
let status = Command::new("rustc")
687-
.arg(&source_path)
688-
.arg("-C").arg("panic=abort")
689-
.arg("-O")
690-
.arg("-o").arg(&dest_path)
691-
.env("EXAMPLE_VERSION", version)
692-
.env("EXAMPLE_VERSION_HASH", version_hash)
693-
.status()
694-
.unwrap();
695-
assert!(status.success());
696-
assert!(dest_path.exists());
665+
// Now load it into memory
666+
let mut f = File::open(dest_path).unwrap();
667+
let mut buf = Vec::new();
668+
f.read_to_end(&mut buf).unwrap();
697669

698-
// If we're on unix this will remove debuginfo, otherwise we just ignore
699-
// the return result here
700-
if cfg!(unix) {
701-
drop(Command::new("strip").arg(&dest_path).status());
670+
Arc::new(buf)
671+
};
702672
}
703673

704-
// Now load it into memory
705-
let mut f = File::open(dest_path).unwrap();
706-
let mut buf = Vec::new();
707-
f.read_to_end(&mut buf).unwrap();
708-
709-
let buf = Arc::new(buf);
710-
MOCK_BIN_TEMPLATE.lock().unwrap().insert(key, buf.clone());
711-
return buf
674+
let name = format!("bin/{}{}", name, EXE_SUFFIX);
675+
vec![
676+
MockFile::new(format!("{}.version", name), version.as_bytes()),
677+
MockFile::new(format!("{}.version-hash", name), version_hash.as_bytes()),
678+
MockFile::new_arc(name, MOCK_BIN.clone()).executable(true),
679+
]
712680
}
713681

714682
// These are toolchains for installation with --link-local and --copy-local
715683
fn create_custom_toolchains(customdir: &Path) {
716-
let ref dir = customdir.join("custom-1/bin");
717-
fs::create_dir_all(dir).unwrap();
718684
let ref libdir = customdir.join("custom-1/lib");
719685
fs::create_dir_all(libdir).unwrap();
720-
let rustc = mock_bin("rustc", "1.0.0", "hash-c-1");
721-
let ref path = customdir.join(format!("custom-1/bin/rustc{}", EXE_SUFFIX));
722-
let mut file = File::create(path).unwrap();
723-
file.write_all(&rustc).unwrap();
724-
make_exe(dir, path);
725-
726-
let ref dir = customdir.join("custom-2/bin");
727-
fs::create_dir_all(dir).unwrap();
686+
for file in mock_bin("rustc", "1.0.0", "hash-c-1") {
687+
file.build(&customdir.join("custom-1"));
688+
}
689+
728690
let ref libdir = customdir.join("custom-2/lib");
729691
fs::create_dir_all(libdir).unwrap();
730-
let rustc = mock_bin("rustc", "1.0.0", "hash-c-2");
731-
let ref path = customdir.join(format!("custom-2/bin/rustc{}", EXE_SUFFIX));
732-
let mut file = File::create(path).unwrap();
733-
file.write_all(&rustc).unwrap();
734-
make_exe(dir, path);
735-
736-
#[cfg(unix)]
737-
fn make_exe(dir: &Path, bin: &Path) {
738-
use std::os::unix::fs::PermissionsExt;
739-
let mut perms = fs::metadata(dir).unwrap().permissions();
740-
perms.set_mode(0o755);
741-
fs::set_permissions(dir, perms).unwrap();
742-
let mut perms = fs::metadata(bin).unwrap().permissions();
743-
perms.set_mode(0o755);
744-
fs::set_permissions(bin, perms).unwrap();
745-
}
746-
747-
#[cfg(windows)]
748-
fn make_exe(_: &Path, _: &Path) { }
692+
for file in mock_bin("rustc", "1.0.0", "hash-c-2") {
693+
file.build(&customdir.join("custom-2"));
694+
}
749695
}
750696

751697
pub fn hard_link<A, B>(a: A, b: B) -> io::Result<()>

src/rustup-mock/src/lib.rs

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,36 +76,15 @@ impl MockInstallerBuilder {
7676
// Create the component files and manifest
7777
let ref mut manifest = File::create(component_dir.join("manifest.in")).unwrap();
7878
for file in component.files.iter() {
79-
let mk = |path: &Path, contents: &MockContents| {
80-
let dir_path = path.parent().unwrap().to_owned();
81-
fs::create_dir_all(dir_path).unwrap();
82-
File::create(&path).unwrap()
83-
.write_all(&contents.contents).unwrap();
84-
85-
#[cfg(unix)]
86-
{
87-
use std::os::unix::fs::PermissionsExt;
88-
if contents.executable {
89-
let mut perm = fs::metadata(path).unwrap().permissions();
90-
perm.set_mode(0o755);
91-
fs::set_permissions(path, perm).unwrap();
92-
}
93-
}
94-
};
9579
match file.contents {
96-
Contents::Dir(ref files) => {
80+
Contents::Dir(_) => {
9781
writeln!(manifest, "dir:{}", file.path).unwrap();
98-
for &(ref name, ref contents) in files {
99-
let fname = component_dir.join(&file.path).join(name);
100-
mk(&fname, contents);
101-
}
10282
}
103-
Contents::File(ref contents) => {
83+
Contents::File(_) => {
10484
writeln!(manifest, "file:{}", file.path).unwrap();
105-
let fname = component_dir.join(&file.path);
106-
mk(&fname, contents);
10785
}
10886
}
87+
file.build(&component_dir);
10988
}
11089
}
11190

@@ -152,6 +131,38 @@ impl MockFile {
152131
}
153132
self
154133
}
134+
135+
pub fn build(&self, path: &Path) {
136+
let path = path.join(&self.path);
137+
match self.contents {
138+
Contents::Dir(ref files) => {
139+
for &(ref name, ref contents) in files {
140+
let fname = path.join(name);
141+
contents.build(&fname);
142+
}
143+
}
144+
Contents::File(ref contents) => contents.build(&path),
145+
}
146+
}
147+
}
148+
149+
impl MockContents {
150+
fn build(&self, path: &Path) {
151+
let dir_path = path.parent().unwrap().to_owned();
152+
fs::create_dir_all(dir_path).unwrap();
153+
File::create(&path).unwrap()
154+
.write_all(&self.contents).unwrap();
155+
156+
#[cfg(unix)]
157+
{
158+
use std::os::unix::fs::PermissionsExt;
159+
if self.executable {
160+
let mut perm = fs::metadata(path).unwrap().permissions();
161+
perm.set_mode(0o755);
162+
fs::set_permissions(path, perm).unwrap();
163+
}
164+
}
165+
}
155166
}
156167

157168
#[cfg(windows)]

src/rustup-mock/src/mock_bin_src.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
use std::env::consts::EXE_SUFFIX;
22
use std::env;
3-
use std::io::{self, Write};
3+
use std::fs::File;
4+
use std::io::{self, Write, Read};
45
use std::process::Command;
56

67
fn main() {
78
let mut args = env::args().skip(1);
89
match args.next().as_ref().map(|s| &**s) {
910
Some("--version") => {
10-
let version = env!("EXAMPLE_VERSION");
11-
let hash = env!("EXAMPLE_VERSION_HASH");
11+
let me = env::current_exe().unwrap().display().to_string();
12+
let version_file = format!("{}.version", me);
13+
let hash_file = format!("{}.version-hash", me);
14+
let mut version = String::new();
15+
let mut hash = String::new();
16+
File::open(&version_file).unwrap().read_to_string(&mut version).unwrap();
17+
File::open(&hash_file).unwrap().read_to_string(&mut hash).unwrap();
1218
println!("{} ({})", version, hash);
1319
}
1420
Some("--empty-arg-test") => {

0 commit comments

Comments
 (0)