Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port argument-non-c-like-enum to Rust #123149

Merged
merged 3 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3926,11 +3926,17 @@ impl<'test> TestCx<'test> {
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));
// Note: we diverge from legacy run_make and don't lump `CC` the compiler and
// default flags together.
.env("CC_DEFAULT_FLAGS", &cflags)
.env("CC", &self.config.cc)
.env("CXX_DEFAULT_FLAGS", &cxxflags)
.env("CXX", &self.config.cxx);
} else {
cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
.env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags))
cmd.env("CC_DEFAULT_FLAGS", &self.config.cflags)
.env("CC", &self.config.cc)
.env("CXX_DEFAULT_FLAGS", &self.config.cxxflags)
.env("CXX", &self.config.cxx)
.env("AR", &self.config.ar);

if self.config.target.contains("windows") {
Expand Down
202 changes: 202 additions & 0 deletions src/tools/run-make-support/src/cc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use std::env;
use std::path::Path;
use std::process::{Command, Output};

use crate::{bin_name, cygpath_windows, handle_failed_output, is_msvc, is_windows, tmp_dir, uname};

/// Construct a new platform-specific C compiler invocation.
///
/// WARNING: This means that what flags are accepted by the underlying C compiler is
/// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`.
pub fn cc() -> Cc {
Cc::new()
}

/// A platform-specific C compiler invocation builder. The specific C compiler used is
/// passed down from compiletest.
#[derive(Debug)]
pub struct Cc {
cmd: Command,
}

impl Cc {
/// Construct a new platform-specific C compiler invocation.
///
/// WARNING: This means that what flags are accepted by the underlying C compile is
/// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`.
pub fn new() -> Self {
let compiler = env::var("CC").unwrap();

let mut cmd = Command::new(compiler);

let default_cflags = env::var("CC_DEFAULT_FLAGS").unwrap();
for flag in default_cflags.split(char::is_whitespace) {
cmd.arg(flag);
}

Self { cmd }
}

/// Specify path of the input file.
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg(path.as_ref());
self
}

/// Add a *platform-and-compiler-specific* argument. Please consult the docs for the various
/// possible C compilers on the various platforms to check which arguments are legal for
/// which compiler.
pub fn arg(&mut self, flag: &str) -> &mut Self {
self.cmd.arg(flag);
self
}

/// Add multiple *platform-and-compiler-specific* arguments. Please consult the docs for the
/// various possible C compilers on the various platforms to check which arguments are legal
/// for which compiler.
pub fn args(&mut self, args: &[&str]) -> &mut Self {
self.cmd.args(args);
self
}

/// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable
/// is under `$TMPDIR`.
pub fn out_exe(&mut self, name: &str) -> &mut Self {
// Ref: tools.mk (irrelevant lines omitted):
//
// ```makefile
// ifdef IS_MSVC
// OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \
// -Fo:`cygpath -w $(TMPDIR)/$(1).obj`
// else
// OUT_EXE=-o $(TMPDIR)/$(1)
// endif
// ```

if is_msvc() {
let fe_path = cygpath_windows(tmp_dir().join(bin_name(name)));
let fo_path = cygpath_windows(tmp_dir().join(format!("{name}.obj")));
self.cmd.arg(format!("-Fe:{fe_path}"));
self.cmd.arg(format!("-Fo:{fo_path}"));
} else {
self.cmd.arg("-o");
self.cmd.arg(tmp_dir().join(name));
}

self
}

/// Run the constructed C invocation command and assert that it is successfully run.
#[track_caller]
pub fn run(&mut self) -> Output {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();

let output = self.cmd.output().unwrap();
if !output.status.success() {
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
}
output
}

/// Inspect what the underlying [`Command`] is up to the current construction.
pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
f(&self.cmd);
self
}
}

/// `EXTRACFLAGS`
pub fn extra_c_flags() -> Vec<&'static str> {
// Adapted from tools.mk (trimmed):
//
// ```makefile
// ifdef IS_WINDOWS
// ifdef IS_MSVC
// EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib
// else
// EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization
// endif
// else
// ifeq ($(UNAME),Darwin)
// EXTRACFLAGS := -lresolv
// else
// ifeq ($(UNAME),FreeBSD)
// EXTRACFLAGS := -lm -lpthread -lgcc_s
// else
// ifeq ($(UNAME),SunOS)
// EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv
// else
// ifeq ($(UNAME),OpenBSD)
// EXTRACFLAGS := -lm -lpthread -lc++abi
// else
// EXTRACFLAGS := -lm -lrt -ldl -lpthread
// endif
// endif
// endif
// endif
// endif
// ```

if is_windows() {
if is_msvc() {
vec![
"ws2_32.lib",
"userenv.lib",
"advapi32.lib",
"bcrypt.lib",
"ntdll.lib",
"synchronization.lib",
]
} else {
vec!["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"]
}
} else {
match uname() {
n if n.contains("Darwin") => vec!["-lresolv"],
n if n.contains("FreeBSD") => vec!["-lm", "-lpthread", "-lgcc_s"],
n if n.contains("SunOS") => {
vec!["-lm", "-lpthread", "-lposix4", "-lsocket", "-lresolv"]
}
n if n.contains("OpenBSD") => vec!["-lm", "-lpthread", "-lc++abi"],
_ => vec!["-lm", "-lrt", "-ldl", "-lpthread"],
}
}
}

/// `EXTRACXXFLAGS`
pub fn extra_cxx_flags() -> Vec<&'static str> {
// Adapted from tools.mk (trimmed):
//
// ```makefile
// ifdef IS_WINDOWS
// ifdef IS_MSVC
// else
// EXTRACXXFLAGS := -lstdc++
// endif
// else
// ifeq ($(UNAME),Darwin)
// EXTRACXXFLAGS := -lc++
// else
// ifeq ($(UNAME),FreeBSD)
// else
// ifeq ($(UNAME),SunOS)
// else
// ifeq ($(UNAME),OpenBSD)
// else
// EXTRACXXFLAGS := -lstdc++
// endif
// endif
// endif
// endif
// endif
// ```
if is_windows() {
if is_msvc() { vec![] } else { vec!["-lstdc++"] }
} else {
match uname() {
n if n.contains("Darwin") => vec!["-lc++"],
_ => vec!["-lstdc++"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This drops the vec![] for FreeBSD, SunOS, OpenBSD that the original had

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original tools.mk had

# Extra flags needed to compile a working executable with the standard library
ifdef IS_WINDOWS
ifdef IS_MSVC
	#EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib
else
	#EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization
	EXTRACXXFLAGS := -lstdc++
	#EXTRARSCXXFLAGS := -l static:-bundle=stdc++
endif
else
ifeq ($(UNAME),Darwin)
	#EXTRACFLAGS := -lresolv
	EXTRACXXFLAGS := -lc++
	#EXTRARSCXXFLAGS := -lc++
else
ifeq ($(UNAME),FreeBSD)
	#EXTRACFLAGS := -lm -lpthread -lgcc_s
else
ifeq ($(UNAME),SunOS)
	#EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv
else
ifeq ($(UNAME),OpenBSD)
	#EXTRACFLAGS := -lm -lpthread -lc++abi
	#RUSTC := $(RUSTC) -C linker="$(word 1,$(CC:ccache=))"
else
	#EXTRACFLAGS := -lm -lrt -ldl -lpthread
	EXTRACXXFLAGS := -lstdc++
	#EXTRARSCXXFLAGS := -lstdc++
endif
endif
endif
endif
endif

It should've been empty vec![] for FreeBSD, SunOS and OpenBSD is what you mean right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch, I'll send a patch for it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly that, you got it

}
}
}
89 changes: 87 additions & 2 deletions src/tools/run-make-support/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
pub mod cc;
pub mod run;
pub mod rustc;
pub mod rustdoc;

use std::env;
use std::path::PathBuf;
use std::process::Output;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};

pub use object;
pub use wasmparser;

pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc};
pub use run::{run, run_fail};
pub use rustc::{aux_build, rustc, Rustc};
pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
Expand All @@ -18,6 +20,89 @@ pub fn tmp_dir() -> PathBuf {
env::var_os("TMPDIR").unwrap().into()
}

/// `TARGET`
pub fn target() -> String {
env::var("TARGET").unwrap()
}

/// Check if target is windows-like.
pub fn is_windows() -> bool {
env::var_os("IS_WINDOWS").is_some()
}

/// Check if target uses msvc.
pub fn is_msvc() -> bool {
env::var_os("IS_MSVC").is_some()
}

/// Construct a path to a static library under `$TMPDIR` given the library name. This will return a
/// path with `$TMPDIR` joined with platform-and-compiler-specific library name.
pub fn static_lib(name: &str) -> PathBuf {
tmp_dir().join(static_lib_name(name))
}

/// Construct the static library name based on the platform.
pub fn static_lib_name(name: &str) -> String {
// See tools.mk (irrelevant lines omitted):
//
// ```makefile
// ifeq ($(UNAME),Darwin)
// STATICLIB = $(TMPDIR)/lib$(1).a
// else
// ifdef IS_WINDOWS
// ifdef IS_MSVC
// STATICLIB = $(TMPDIR)/$(1).lib
// else
// STATICLIB = $(TMPDIR)/lib$(1).a
// endif
// else
// STATICLIB = $(TMPDIR)/lib$(1).a
// endif
// endif
// ```
assert!(!name.contains(char::is_whitespace), "name cannot contain whitespace");

if target().contains("msvc") { format!("{name}.lib") } else { format!("lib{name}.a") }
}

/// Construct the binary name based on platform.
pub fn bin_name(name: &str) -> String {
if is_windows() { format!("{name}.exe") } else { name.to_string() }
}

/// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is
/// available on the platform!
#[track_caller]
pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();

let mut cygpath = Command::new("cygpath");
cygpath.arg("-w");
cygpath.arg(path.as_ref());
let output = cygpath.output().unwrap();
if !output.status.success() {
handle_failed_output(&format!("{:#?}", cygpath), output, caller_line_number);
}
let s = String::from_utf8(output.stdout).unwrap();
// cygpath -w can attach a newline
s.trim().to_string()
}

/// Run `uname`. This assumes that `uname` is available on the platform!
#[track_caller]
pub fn uname() -> String {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();

let mut uname = Command::new("uname");
let output = uname.output().unwrap();
if !output.status.success() {
handle_failed_output(&format!("{:#?}", uname), output, caller_line_number);
}
String::from_utf8(output.stdout).unwrap()
}

fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! {
if output.status.success() {
eprintln!("command incorrectly succeeded at line {caller_line_number}");
Expand Down
21 changes: 9 additions & 12 deletions src/tools/run-make-support/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};

use super::handle_failed_output;
use crate::is_windows;

fn run_common(bin_name: &str) -> (Command, Output) {
let target = env::var("TARGET").unwrap();

let bin_name =
if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
use super::{bin_name, handle_failed_output};

fn run_common(name: &str) -> (Command, Output) {
let mut bin_path = PathBuf::new();
bin_path.push(env::var("TMPDIR").unwrap());
bin_path.push(&bin_name);
bin_path.push(&bin_name(name));
let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
let mut cmd = Command::new(bin_path);
cmd.env(&ld_lib_path_envvar, {
Expand All @@ -27,7 +24,7 @@ fn run_common(bin_name: &str) -> (Command, Output) {
env::join_paths(paths.iter()).unwrap()
});

if target.contains("windows") {
if is_windows() {
let mut paths = vec![];
for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
paths.push(p.to_path_buf());
Expand All @@ -42,11 +39,11 @@ fn run_common(bin_name: &str) -> (Command, Output) {

/// Run a built binary and make sure it succeeds.
#[track_caller]
pub fn run(bin_name: &str) -> Output {
pub fn run(name: &str) -> Output {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();

let (cmd, output) = run_common(bin_name);
let (cmd, output) = run_common(name);
if !output.status.success() {
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
}
Expand All @@ -55,11 +52,11 @@ pub fn run(bin_name: &str) -> Output {

/// Run a built binary and make sure it fails.
#[track_caller]
pub fn run_fail(bin_name: &str) -> Output {
pub fn run_fail(name: &str) -> Output {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();

let (cmd, output) = run_common(bin_name);
let (cmd, output) = run_common(name);
if output.status.success() {
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
}
Expand Down