diff --git a/Cargo.lock b/Cargo.lock index d552bb655b40a..69066c2f3dbe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3323,6 +3323,13 @@ dependencies = [ "serde_json", ] +[[package]] +name = "run_make_support" +version = "0.0.0" +dependencies = [ + "shell-words", +] + [[package]] name = "rust-demangler" version = "0.0.1" @@ -5034,6 +5041,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 2ea16c2266615..5847a817e76fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "src/tools/clippy", "src/tools/clippy/clippy_dev", "src/tools/compiletest", + "src/tools/run-make-support", "src/tools/error_index_generator", "src/tools/linkchecker", "src/tools/lint-docs", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 1dbda405128ab..d11a41c4a0756 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -27,6 +27,7 @@ use crate::core::config::flags::get_completion; use crate::core::config::flags::Subcommand; use crate::core::config::TargetSelection; use crate::utils::cache::{Interned, INTERNER}; +use crate::utils::dylib::shared_lib_name; use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::{ self, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, @@ -1327,6 +1328,53 @@ macro_rules! coverage_test_alias { }; } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] +pub struct RunMakeSupport { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for RunMakeSupport { + type Output = PathBuf; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.never() + } + + fn make_run(run: RunConfig<'_>) { + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() }); + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + builder.ensure(compile::Std::new(self.compiler, self.target)); + + let cargo = tool::prepare_tool_cargo( + builder, + self.compiler, + Mode::ToolStd, + self.target, + "build", + "src/tools/run-make-support", + SourceType::InTree, + &[], + ); + + let mut cargo = Command::from(cargo); + cargo.env("RUSTFLAGS", "-C prefer-dynamic"); + builder.run(&mut cargo); + + let lib_name = shared_lib_name("run_make_support", &self.target.to_string()); + let lib = builder.tools_dir(self.compiler).join(&lib_name); + + let cargo_out = + builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(&lib_name); + builder.copy(&cargo_out, &lib); + lib + } +} + default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" }); default_test!(RunPassValgrind { @@ -1361,7 +1409,40 @@ host_test!(RustdocJson { path: "tests/rustdoc-json", mode: "rustdoc-json", suite host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" }); -default_test!(RunMake { path: "tests/run-make", mode: "run-make", suite: "run-make" }); +// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` +// tests. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct RunMake { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for RunMake { + type Output = (); + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.suite_path("tests/run-make") + } + + fn make_run(run: RunConfig<'_>) { + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() }); + run.builder.ensure(RunMake { compiler, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + builder.ensure(Compiletest { + compiler: self.compiler, + target: self.target, + mode: "run-make", + suite: "run-make", + path: "tests/run-make", + compare_mode: None, + }); + } +} host_test!(RunMakeFullDeps { path: "tests/run-make-fulldeps", diff --git a/src/bootstrap/src/utils/dylib.rs b/src/bootstrap/src/utils/dylib.rs index b6e7aec1756b6..ca1216ccf48ea 100644 --- a/src/bootstrap/src/utils/dylib.rs +++ b/src/bootstrap/src/utils/dylib.rs @@ -38,3 +38,16 @@ pub fn exe(name: &str, target: &str) -> String { name.to_string() } } + +/// Given a shared library called `name`, return the filename for the +/// shared library for a particular target. +#[allow(dead_code)] +pub fn shared_lib_name(name: &str, target: &str) -> String { + if target.contains("windows") { + format!("lib{name}.dll") + } else if target.contains("apple") { + format!("lib{name}.dylib") + } else { + format!("lib{name}.so") + } +} diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 60dd15841b766..07ece9b0b4b10 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -655,13 +655,21 @@ fn collect_tests_from_dir( return Ok(()); } - if config.mode == Mode::RunMake && dir.join("Makefile").exists() { - let paths = TestPaths { - file: dir.to_path_buf(), - relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), - }; - tests.extend(make_test(config, cache, &paths, inputs, poisoned)); - return Ok(()); + if config.mode == Mode::RunMake { + if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() { + return Err(io::Error::other( + "run-make tests cannot have both `Makefile` and `rmake.rs`", + )); + } + + if dir.join("Makefile").exists() || dir.join("rmake.rs").exists() { + let paths = TestPaths { + file: dir.to_path_buf(), + relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), + }; + tests.extend(make_test(config, cache, &paths, inputs, poisoned)); + return Ok(()); + } } // If we find a test foo/bar.rs, we have to build the @@ -731,8 +739,17 @@ fn make_test( poisoned: &mut bool, ) -> Vec { let test_path = if config.mode == Mode::RunMake { - // Parse directives in the Makefile - testpaths.file.join("Makefile") + if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() { + panic!("run-make tests cannot have both `rmake.rs` and `Makefile`"); + } + + if testpaths.file.join("rmake.rs").exists() { + // Parse directives in rmake.rs. + testpaths.file.join("rmake.rs") + } else { + // Parse directives in the Makefile. + testpaths.file.join("Makefile") + } } else { PathBuf::from(&testpaths.file) }; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ed1c559e1f692..4e84dff6352ed 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3560,6 +3560,17 @@ impl<'test> TestCx<'test> { } fn run_rmake_test(&self) { + let test_dir = &self.testpaths.file; + if test_dir.join("rmake.rs").exists() { + self.run_rmake_v2_test(); + } else if test_dir.join("Makefile").exists() { + self.run_rmake_legacy_test(); + } else { + self.fatal("failed to find either `rmake.rs` or `Makefile`") + } + } + + fn run_rmake_legacy_test(&self) { let cwd = env::current_dir().unwrap(); let src_root = self.config.src_base.parent().unwrap().parent().unwrap(); let src_root = cwd.join(&src_root); @@ -3727,6 +3738,241 @@ impl<'test> TestCx<'test> { fs::remove_dir(path) } + fn run_rmake_v2_test(&self) { + // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe + // (`rmake.rs`) to run the actual tests. The support library is already built as a tool + // dylib and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.so`. + // + // 1. We need to build the recipe `rmake.rs` and link in the support library. + // 2. We need to run the recipe to build and run the tests. + let cwd = env::current_dir().unwrap(); + let src_root = self.config.src_base.parent().unwrap().parent().unwrap(); + let src_root = cwd.join(&src_root); + let build_root = self.config.build_base.parent().unwrap().parent().unwrap(); + let build_root = cwd.join(&build_root); + + let tmpdir = cwd.join(self.output_base_name()); + if tmpdir.exists() { + self.aggressive_rm_rf(&tmpdir).unwrap(); + } + create_dir_all(&tmpdir).unwrap(); + + // HACK: assume stageN-target, we only want stageN. + let stage = self.config.stage_id.split('-').next().unwrap(); + + // First, we construct the path to the built support library. + let mut support_dylib_path = PathBuf::new(); + support_dylib_path.push(&build_root); + support_dylib_path.push(format!("{}-tools-bin", stage)); + if self.config.target.contains("windows") { + support_dylib_path.push("librun_make_support.dll"); + } else if self.config.target.contains("apple") { + support_dylib_path.push("librun_make_support.dylib"); + } else { + support_dylib_path.push("librun_make_support.so"); + } + + let mut stage_std_path = PathBuf::new(); + stage_std_path.push(&build_root); + stage_std_path.push(&stage); + stage_std_path.push("lib"); + + // Then, we need to build the recipe `rmake.rs` and link in the support library. + let recipe_bin = tmpdir.join("rmake"); + + let mut support_dylib_deps = PathBuf::new(); + support_dylib_deps.push(&build_root); + support_dylib_deps.push(format!("{}-tools", stage)); + support_dylib_deps.push(&self.config.host); + support_dylib_deps.push("release/deps"); + + let mut support_dylib_deps_deps = PathBuf::new(); + support_dylib_deps_deps.push(&build_root); + support_dylib_deps_deps.push(format!("{}-tools", stage)); + support_dylib_deps_deps.push("release/deps"); + + debug!(?support_dylib_deps); + debug!(?support_dylib_deps_deps); + + let res = self.cmd2procres( + Command::new(&self.config.rustc_path) + .arg("-o") + .arg(&recipe_bin) + .arg(format!( + "-Ldependency={}", + &support_dylib_path.parent().unwrap().to_string_lossy() + )) + .arg(format!("-Ldependency={}", &support_dylib_deps.to_string_lossy())) + .arg(format!("-Ldependency={}", &support_dylib_deps_deps.to_string_lossy())) + .arg("--extern") + .arg(format!("run_make_support={}", &support_dylib_path.to_string_lossy())) + .arg(&self.testpaths.file.join("rmake.rs")) + .env("TARGET", &self.config.target) + .env("PYTHON", &self.config.python) + .env("S", &src_root) + .env("RUST_BUILD_STAGE", &self.config.stage_id) + .env("RUSTC", cwd.join(&self.config.rustc_path)) + .env("TMPDIR", &tmpdir) + .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) + .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) + .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) + .env("LLVM_COMPONENTS", &self.config.llvm_components) + // We for sure don't want these tests to run in parallel, so make + // sure they don't have access to these vars if we run via `make` + // at the top level + .env_remove("MAKEFLAGS") + .env_remove("MFLAGS") + .env_remove("CARGO_MAKEFLAGS"), + ); + if !res.status.success() { + self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res); + } + + // Finally, we need to run the recipe binary to build and run the actual tests. + debug!(?recipe_bin); + + let mut dylib_env_paths = String::new(); + dylib_env_paths.push_str(&env::var(dylib_env_var()).unwrap()); + dylib_env_paths.push(':'); + dylib_env_paths.push_str(&support_dylib_path.parent().unwrap().to_string_lossy()); + dylib_env_paths.push(':'); + dylib_env_paths.push_str( + &stage_std_path.join("rustlib").join(&self.config.target).join("lib").to_string_lossy(), + ); + + let mut target_rpath_env_path = String::new(); + target_rpath_env_path.push_str(&tmpdir.to_string_lossy()); + target_rpath_env_path.push(':'); + target_rpath_env_path.push_str(&dylib_env_paths); + + let mut cmd = Command::new(&recipe_bin); + cmd.current_dir(&self.testpaths.file) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) + .env("TARGET_RPATH_ENV", &target_rpath_env_path) + .env(dylib_env_var(), &dylib_env_paths) + .env("TARGET", &self.config.target) + .env("PYTHON", &self.config.python) + .env("S", &src_root) + .env("RUST_BUILD_STAGE", &self.config.stage_id) + .env("RUSTC", cwd.join(&self.config.rustc_path)) + .env("TMPDIR", &tmpdir) + .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) + .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) + .env("LLVM_COMPONENTS", &self.config.llvm_components) + // We for sure don't want these tests to run in parallel, so make + // sure they don't have access to these vars if we run via `make` + // at the top level + .env_remove("MAKEFLAGS") + .env_remove("MFLAGS") + .env_remove("CARGO_MAKEFLAGS"); + + if let Some(ref rustdoc) = self.config.rustdoc_path { + cmd.env("RUSTDOC", cwd.join(rustdoc)); + } + + if let Some(ref rust_demangler) = self.config.rust_demangler_path { + cmd.env("RUST_DEMANGLER", cwd.join(rust_demangler)); + } + + if let Some(ref node) = self.config.nodejs { + cmd.env("NODE", node); + } + + if let Some(ref linker) = self.config.target_linker { + cmd.env("RUSTC_LINKER", linker); + } + + if let Some(ref clang) = self.config.run_clang_based_tests_with { + cmd.env("CLANG", clang); + } + + if let Some(ref filecheck) = self.config.llvm_filecheck { + cmd.env("LLVM_FILECHECK", filecheck); + } + + if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir { + cmd.env("LLVM_BIN_DIR", llvm_bin_dir); + } + + if let Some(ref remote_test_client) = self.config.remote_test_client { + cmd.env("REMOTE_TEST_CLIENT", remote_test_client); + } + + // We don't want RUSTFLAGS set from the outside to interfere with + // compiler flags set in the test cases: + cmd.env_remove("RUSTFLAGS"); + + // Use dynamic musl for tests because static doesn't allow creating dylibs + if self.config.host.contains("musl") { + cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1"); + } + + if self.config.bless { + cmd.env("RUSTC_BLESS_TEST", "--bless"); + // Assume this option is active if the environment variable is "defined", with _any_ value. + // As an example, a `Makefile` can use this option by: + // + // ifdef RUSTC_BLESS_TEST + // cp "$(TMPDIR)"/actual_something.ext expected_something.ext + // else + // $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext + // endif + } + + if self.config.target.contains("msvc") && self.config.cc != "" { + // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` + // and that `lib.exe` lives next to it. + let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); + + // MSYS doesn't like passing flags of the form `/foo` as it thinks it's + // a path and instead passes `C:\msys64\foo`, so convert all + // `/`-arguments to MSVC here to `-` arguments. + let cflags = self + .config + .cflags + .split(' ') + .map(|s| s.replace("/", "-")) + .collect::>() + .join(" "); + let cxxflags = self + .config + .cxxflags + .split(' ') + .map(|s| s.replace("/", "-")) + .collect::>() + .join(" "); + + cmd.env("IS_MSVC", "1") + .env("IS_WINDOWS", "1") + .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) + .env("CC", format!("'{}' {}", self.config.cc, cflags)) + .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags)); + } else { + cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) + .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags)) + .env("AR", &self.config.ar); + + if self.config.target.contains("windows") { + cmd.env("IS_WINDOWS", "1"); + } + } + + let (Output { stdout, stderr, status }, truncated) = + self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`")); + if !status.success() { + let res = ProcRes { + status, + stdout: String::from_utf8_lossy(&stdout).into_owned(), + stderr: String::from_utf8_lossy(&stderr).into_owned(), + truncated, + cmdline: format!("{:?}", cmd), + }; + self.fatal_proc_rec("rmake recipe failed to complete", &res); + } + } + fn run_js_doc_test(&self) { if let Some(nodejs) = &self.config.nodejs { let out_dir = self.output_base_dir(); diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml new file mode 100644 index 0000000000000..6a25cc337eebc --- /dev/null +++ b/src/tools/run-make-support/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "run_make_support" +version = "0.0.0" +edition = "2021" + +[lib] +crate-type = ["dylib"] + +[dependencies] +shell-words = "1.1" diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs new file mode 100644 index 0000000000000..0a12022df4c11 --- /dev/null +++ b/src/tools/run-make-support/src/lib.rs @@ -0,0 +1,83 @@ +use std::env; +use std::path::PathBuf; +use std::process::{Command, Output}; + +pub fn aux_build(args: &str) -> Output { + let (words, mut cmd) = build_common(args); + cmd.arg("--crate-type=lib"); + for word in words { + cmd.arg(word); + } + let output = cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:?}", cmd), output); + } + output +} + +fn build_common(args: &str) -> (Vec, Command) { + let rustc = env::var("RUSTC").unwrap(); + let words = shell_words::split(args).expect("failed to parse arguments"); + let mut cmd = Command::new(rustc); + cmd.arg("--out-dir") + .arg(env::var("TMPDIR").unwrap()) + .arg("-L") + .arg(env::var("TMPDIR").unwrap()); + (words, cmd) +} + +fn handle_failed_output(cmd: &str, output: Output) -> ! { + println!("command failed: `{}`", cmd); + println!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); + println!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap()); + std::process::exit(1) +} + +pub fn rustc(args: &str) -> Output { + let (words, mut cmd) = build_common(args); + for word in words { + cmd.arg(word); + } + let output = cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:?}", cmd), output); + } + output +} + +fn run_common(bin_name: &str) -> (Command, Output) { + let mut bin_path = PathBuf::new(); + bin_path.push(std::env::var("TMPDIR").unwrap()); + bin_path.push(&bin_name); + let ld_lib_path_envvar = std::env::var("LD_LIB_PATH_ENVVAR").unwrap(); + let mut cmd = Command::new(bin_path); + cmd.env(&ld_lib_path_envvar, { + let mut target_rpath_env_path = String::new(); + target_rpath_env_path.push_str(&std::env::var("TMPDIR").unwrap()); + target_rpath_env_path.push(':'); + target_rpath_env_path.push_str(&std::env::var("TARGET_RPATH_DIR").unwrap()); + target_rpath_env_path.push(':'); + target_rpath_env_path.push_str(&std::env::var(&ld_lib_path_envvar).unwrap()); + target_rpath_env_path + }); + let output = cmd.output().unwrap(); + (cmd, output) +} + +/// Run a built binary and make sure it succeeds. +pub fn run(bin_name: &str) -> Output { + let (cmd, output) = run_common(bin_name); + if !output.status.success() { + handle_failed_output(&format!("{:?}", cmd), output); + } + output +} + +/// Run a built binary and make sure it fails. +pub fn run_fail(bin_name: &str) -> Output { + let (cmd, output) = run_common(bin_name); + if output.status.success() { + handle_failed_output(&format!("{:?}", cmd), output); + } + output +} diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/Makefile b/tests/run-make/CURRENT_RUSTC_VERSION/Makefile deleted file mode 100644 index 7940dae207b09..0000000000000 --- a/tests/run-make/CURRENT_RUSTC_VERSION/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) --emit=metadata --crate-type lib stable.rs - $(RUSTC) --emit=metadata --extern stable=$(TMPDIR)/libstable.rmeta main.rs 2>&1 >/dev/null \ - | $(CGREP) -e "stable since $$(cat $(S)/src/version)(-[a-zA-Z]+)?" diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs new file mode 100644 index 0000000000000..d46b5e2508e18 --- /dev/null +++ b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs @@ -0,0 +1,18 @@ +// ignore-tidy-linelength + +extern crate run_make_support; + +use run_make_support::{aux_build, rustc}; + +fn main() { + aux_build("--emit=metadata stable.rs"); + let output = rustc(&format!( + "--emit=metadata --extern stable={}/libstable.rmeta main.rs", + env!("TMPDIR") + )); + + let stderr = String::from_utf8_lossy(&output.stderr); + let version = include_str!(concat!(env!("S"), "/src/version")); + let expected_string = format!("stable since {}", version.trim()); + assert!(stderr.contains(&expected_string)); +} diff --git a/tests/run-make/a-b-a-linker-guard/Makefile b/tests/run-make/a-b-a-linker-guard/Makefile deleted file mode 100644 index 43282eae09c59..0000000000000 --- a/tests/run-make/a-b-a-linker-guard/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Test that if we build `b` against a version of `a` that has one set -# of types, it will not run with a dylib that has a different set of -# types. - -# NOTE(eddyb) this test only works with the `legacy` mangling, -# and will probably get removed once `legacy` is gone. - -all: - $(RUSTC) a.rs --cfg x -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy - $(RUSTC) b.rs -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy - $(call RUN,b) - $(RUSTC) a.rs --cfg y -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy - $(call FAIL,b) diff --git a/tests/run-make/a-b-a-linker-guard/rmake.rs b/tests/run-make/a-b-a-linker-guard/rmake.rs new file mode 100644 index 0000000000000..014c1a689fdcd --- /dev/null +++ b/tests/run-make/a-b-a-linker-guard/rmake.rs @@ -0,0 +1,13 @@ +// ignore-tidy-linelength + +extern crate run_make_support; + +use run_make_support::{run, run_fail, rustc}; + +fn main() { + rustc("a.rs --cfg x -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy"); + rustc("b.rs -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy"); + run("b"); + rustc("a.rs --cfg y -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy"); + run_fail("b"); +}