fish-shell/fish-rust/build.rs
Johannes Altmanninger 05bad5eda1 Port common.{h,cpp} to Rust
Most of it is duplicated, hence untested.

Functions like mbrtowc are not exposed by the libc crate, so declare them
ourselves.
Since we don't know the definition of C macros, add two big hacks to make
this work:
1. Replace MB_LEN_MAX and mbstate_t with values (resp types) that should
   be large enough for any implementation.
2. Detect the definition of MB_CUR_MAX in the build script. This requires
   more changes for each new libc. We could also use this approach for 1.

Additionally, this commit brings a small behavior change to
read_unquoted_escape(): we cannot decode surrogate code points like \UDE01
into a Rust char, so use � (\UFFFD, replacement character) instead.
Previously, we added such code points to a wcstring; looks like they were
ignored when printed.
2023-04-02 15:17:06 +02:00

119 lines
4.5 KiB
Rust

use miette::miette;
fn main() -> miette::Result<()> {
cc::Build::new().file("src/compat.c").compile("libcompat.a");
let rust_dir = std::env::var("CARGO_MANIFEST_DIR").expect("Env var CARGO_MANIFEST_DIR missing");
let target_dir =
std::env::var("FISH_RUST_TARGET_DIR").unwrap_or(format!("{}/{}", rust_dir, "target/"));
let fish_src_dir = format!("{}/{}", rust_dir, "../src/");
// Where cxx emits its header.
let cxx_include_dir = format!("{}/{}", target_dir, "cxxbridge/rust/");
// If FISH_BUILD_DIR is given by CMake, then use it; otherwise assume it's at ../build.
let fish_build_dir =
std::env::var("FISH_BUILD_DIR").unwrap_or(format!("{}/{}", rust_dir, "../build/"));
// Where autocxx should put its stuff.
let autocxx_gen_dir = std::env::var("FISH_AUTOCXX_GEN_DIR")
.unwrap_or(format!("{}/{}", fish_build_dir, "fish-autocxx-gen/"));
detect_features();
// Emit cxx junk.
// This allows "Rust to be used from C++"
// This must come before autocxx so that cxx can emit its cxx.h header.
let source_files = vec![
"src/abbrs.rs",
"src/event.rs",
"src/common.rs",
"src/fd_monitor.rs",
"src/fd_readable_set.rs",
"src/fds.rs",
"src/ffi_init.rs",
"src/ffi_tests.rs",
"src/future_feature_flags.rs",
"src/job_group.rs",
"src/parse_constants.rs",
"src/redirection.rs",
"src/smoke.rs",
"src/termsize.rs",
"src/timer.rs",
"src/tokenizer.rs",
"src/topic_monitor.rs",
"src/trace.rs",
"src/util.rs",
"src/wait_handle.rs",
"src/builtins/shared.rs",
];
cxx_build::bridges(&source_files)
.flag_if_supported("-std=c++11")
.include(&fish_src_dir)
.include(&fish_build_dir) // For config.h
.include(&cxx_include_dir) // For cxx.h
.flag("-Wno-comment")
.compile("fish-rust");
// Emit autocxx junk.
// This allows "C++ to be used from Rust."
let include_paths = [&fish_src_dir, &fish_build_dir, &cxx_include_dir];
let mut builder = autocxx_build::Builder::new("src/ffi.rs", include_paths);
// Use autocxx's custom output directory unless we're being called by `rust-analyzer` and co.,
// in which case stick to the default target directory so code intelligence continues to work.
if std::env::var("RUSTC_WRAPPER").map_or(true, |wrapper| {
!(wrapper.contains("rust-analyzer") || wrapper.contains("intellij-rust-native-helper"))
}) {
// We need this reassignment because of how the builder pattern works
builder = builder.custom_gendir(autocxx_gen_dir.into());
}
let mut b = builder.build()?;
b.flag_if_supported("-std=c++11")
.flag("-Wno-comment")
.compile("fish-rust-autocxx");
for file in source_files {
println!("cargo:rerun-if-changed={file}");
}
Ok(())
}
/// Dynamically enables certain features at build-time, without their having to be explicitly
/// enabled in the `cargo build --features xxx` invocation.
///
/// This can be used to enable features that we check for and conditionally compile according to in
/// our own codebase, but [can't be used to pull in dependencies](0) even if they're gated (in
/// `Cargo.toml`) behind a feature we just enabled.
///
/// [0]: https://github.com/rust-lang/cargo/issues/5499
fn detect_features() {
for (feature, detector) in [
// Ignore the first line, it just sets up the type inference. Model new entries after the
// second line.
("", &(|| Ok(false)) as &dyn Fn() -> miette::Result<bool>),
("bsd", &detect_bsd),
] {
match detector() {
Err(e) => eprintln!("{feature} detect: {e}"),
Ok(true) => println!("cargo:rustc-cfg=feature=\"{feature}\""),
Ok(false) => (),
}
}
}
/// Detect if we're being compiled on a BSD-derived OS. Does not yet play nicely with
/// cross-compilation.
///
/// Rust offers fine-grained conditional compilation per-os for the popular operating systems, but
/// doesn't necessarily include less-popular forks nor does it group them into families more
/// specific than "windows" vs "unix" so we can conditionally compile code for BSD systems.
fn detect_bsd() -> miette::Result<bool> {
let uname = std::process::Command::new("uname")
.output()
.map_err(|_| miette!("Error executing uname!"))?;
Ok(std::str::from_utf8(&uname.stdout)
.map(|s| s.to_ascii_lowercase())
.map(|s| s.contains("bsd"))
.unwrap_or(false))
}