diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index a512599f723b7..6ff47dbffbcba 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -36,6 +36,7 @@ impl HeadersCache { #[derive(Default)] pub struct EarlyProps { pub aux: Vec, + pub aux_bin: Vec, pub aux_crate: Vec<(String, String)>, pub revisions: Vec, } @@ -59,6 +60,12 @@ impl EarlyProps { config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| { r.trim().to_string() }); + config.push_name_value_directive( + ln, + directives::AUX_BIN, + &mut props.aux_bin, + |r| r.trim().to_string(), + ); config.push_name_value_directive( ln, directives::AUX_CRATE, @@ -95,6 +102,8 @@ pub struct TestProps { // directory as the test, but for backwards compatibility reasons // we also check the auxiliary directory) pub aux_builds: Vec, + // Auxiliary crates that should be compiled as `#![crate_type = "bin"]`. + pub aux_bins: Vec, // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies // to build and pass with the `--extern` flag. pub aux_crates: Vec<(String, String)>, @@ -217,6 +226,7 @@ mod directives { pub const PRETTY_EXPANDED: &'static str = "pretty-expanded"; pub const PRETTY_MODE: &'static str = "pretty-mode"; pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only"; + pub const AUX_BIN: &'static str = "aux-bin"; pub const AUX_BUILD: &'static str = "aux-build"; pub const AUX_CRATE: &'static str = "aux-crate"; pub const EXEC_ENV: &'static str = "exec-env"; @@ -252,6 +262,7 @@ impl TestProps { run_flags: None, pp_exact: None, aux_builds: vec![], + aux_bins: vec![], aux_crates: vec![], revisions: vec![], rustc_env: vec![("RUSTC_ICE".to_string(), "0".to_string())], @@ -417,6 +428,9 @@ impl TestProps { config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| { r.trim().to_string() }); + config.push_name_value_directive(ln, AUX_BIN, &mut self.aux_bins, |r| { + r.trim().to_string() + }); config.push_name_value_directive( ln, AUX_CRATE, @@ -683,6 +697,7 @@ pub fn line_directive<'line>( const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ // tidy-alphabetical-start "assembly-output", + "aux-bin", "aux-build", "aux-crate", "build-aux-docs", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 4b2136583493a..67db33abf65ff 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -82,22 +82,21 @@ fn disable_error_reporting R, R>(f: F) -> R { } /// The platform-specific library name -fn get_lib_name(lib: &str, aux_type: AuxType) -> String { +fn get_lib_name(lib: &str, aux_type: AuxType) -> Option { match aux_type { + AuxType::Bin => None, // In some cases (e.g. MUSL), we build a static // library, rather than a dynamic library. // In this case, the only path we can pass // with '--extern-meta' is the '.rlib' file - AuxType::Lib => format!("lib{}.rlib", lib), - AuxType::Dylib => { - if cfg!(windows) { - format!("{}.dll", lib) - } else if cfg!(target_os = "macos") { - format!("lib{}.dylib", lib) - } else { - format!("lib{}.so", lib) - } - } + AuxType::Lib => Some(format!("lib{}.rlib", lib)), + AuxType::Dylib => Some(if cfg!(windows) { + format!("{}.dll", lib) + } else if cfg!(target_os = "macos") { + format!("lib{}.dylib", lib) + } else { + format!("lib{}.so", lib) + }), } } @@ -2099,19 +2098,36 @@ impl<'test> TestCx<'test> { create_dir_all(&aux_dir).unwrap(); } + if !self.props.aux_bins.is_empty() { + let aux_bin_dir = self.aux_bin_output_dir_name(); + let _ = fs::remove_dir_all(&aux_bin_dir); + create_dir_all(&aux_bin_dir).unwrap(); + } + aux_dir } fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) { for rel_ab in &self.props.aux_builds { - self.build_auxiliary(of, rel_ab, &aux_dir); + self.build_auxiliary(of, rel_ab, &aux_dir, false /* is_bin */); + } + + for rel_ab in &self.props.aux_bins { + self.build_auxiliary(of, rel_ab, &aux_dir, true /* is_bin */); } for (aux_name, aux_path) in &self.props.aux_crates { - let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir); + let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir, false /* is_bin */); let lib_name = get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), aux_type); - rustc.arg("--extern").arg(format!("{}={}/{}", aux_name, aux_dir.display(), lib_name)); + if let Some(lib_name) = lib_name { + rustc.arg("--extern").arg(format!( + "{}={}/{}", + aux_name, + aux_dir.display(), + lib_name + )); + } } } @@ -2130,12 +2146,23 @@ impl<'test> TestCx<'test> { } /// Builds an aux dependency. - /// - /// Returns whether or not it is a dylib. - fn build_auxiliary(&self, of: &TestPaths, source_path: &str, aux_dir: &Path) -> AuxType { + fn build_auxiliary( + &self, + of: &TestPaths, + source_path: &str, + aux_dir: &Path, + is_bin: bool, + ) -> AuxType { let aux_testpaths = self.compute_aux_test_paths(of, source_path); let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config); - let aux_output = TargetLocation::ThisDirectory(aux_dir.to_path_buf()); + let mut aux_dir = aux_dir.to_path_buf(); + if is_bin { + // On unix, the binary of `auxiliary/foo.rs` will be named + // `auxiliary/foo` which clashes with the _dir_ `auxiliary/foo`, so + // put bins in a `bin` subfolder. + aux_dir.push("bin"); + } + let aux_output = TargetLocation::ThisDirectory(aux_dir.clone()); let aux_cx = TestCx { config: self.config, props: &aux_props, @@ -2153,14 +2180,16 @@ impl<'test> TestCx<'test> { LinkToAux::No, Vec::new(), ); - aux_cx.build_all_auxiliary(of, aux_dir, &mut aux_rustc); + aux_cx.build_all_auxiliary(of, &aux_dir, &mut aux_rustc); for key in &aux_props.unset_rustc_env { aux_rustc.env_remove(key); } aux_rustc.envs(aux_props.rustc_env.clone()); - let (aux_type, crate_type) = if aux_props.no_prefer_dynamic { + let (aux_type, crate_type) = if is_bin { + (AuxType::Bin, Some("bin")) + } else if aux_props.no_prefer_dynamic { (AuxType::Dylib, None) } else if self.config.target.contains("emscripten") || (self.config.target.contains("musl") @@ -2678,6 +2707,12 @@ impl<'test> TestCx<'test> { .with_extra_extension(self.config.mode.aux_dir_disambiguator()) } + /// Gets the directory where auxiliary binaries are written. + /// E.g., `/.../testname.revision.mode/auxiliary/bin`. + fn aux_bin_output_dir_name(&self) -> PathBuf { + self.aux_output_dir_name().join("bin") + } + /// Generates a unique name for the test, such as `testname.revision.mode`. fn output_testname_unique(&self) -> PathBuf { output_testname_unique(self.config, self.testpaths, self.safe_revision()) @@ -4829,6 +4864,7 @@ enum LinkToAux { } enum AuxType { + Bin, Lib, Dylib, } diff --git a/tests/ui/compiletest-self-test/auxiliary/print-it-works.rs b/tests/ui/compiletest-self-test/auxiliary/print-it-works.rs new file mode 100644 index 0000000000000..09411eb121ceb --- /dev/null +++ b/tests/ui/compiletest-self-test/auxiliary/print-it-works.rs @@ -0,0 +1,3 @@ +fn main() { + println!("it works"); +} diff --git a/tests/ui/compiletest-self-test/test-aux-bin.rs b/tests/ui/compiletest-self-test/test-aux-bin.rs new file mode 100644 index 0000000000000..9e01e3ffabff1 --- /dev/null +++ b/tests/ui/compiletest-self-test/test-aux-bin.rs @@ -0,0 +1,9 @@ +//@ ignore-cross-compile because we run the compiled code +//@ aux-bin: print-it-works.rs +//@ run-pass + +fn main() { + let stdout = + std::process::Command::new("auxiliary/bin/print-it-works").output().unwrap().stdout; + assert_eq!(stdout, b"it works\n"); +}