use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
let lib_dir = match env::var("FLOWBRIGADE_LIB_DIR") {
Ok(value) => PathBuf::from(value),
Err(_) => build_from_flowbrigade_source(),
};
println!("cargo:rustc-link-search=native={}", lib_dir.display());
println!("cargo:rustc-link-lib=dylib=flowbrigade");
if cfg!(any(target_os = "linux", target_os = "macos")) {
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display());
}
println!("cargo:rerun-if-env-changed=FLOWBRIGADE_LIB_DIR");
println!("cargo:rerun-if-env-changed=FLOWBRIGADE_SOURCE_DIR");
}
fn build_from_flowbrigade_source() -> PathBuf {
let source_dir = env::var("FLOWBRIGADE_SOURCE_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| nimble_flowbrigade_path());
let (source_file, import_path) = flowbrigade_source_layout(&source_dir);
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set by Cargo"));
let native_dir = out_dir.join("flowbrigade-native");
std::fs::create_dir_all(&native_dir).expect("failed to create native build output directory");
let lib_path = native_dir.join(platform_library_name());
let status = Command::new("nim")
.arg("c")
.arg("--mm:arc")
.arg("--app:lib")
.arg(format!("-p:{}", import_path.display()))
.arg(format!("--out:{}", lib_path.display()))
.arg(&source_file)
.status()
.unwrap_or_else(|error| {
panic!(
"failed to run `nim`: {error}\n\
Install Nim and Nimble first, then run `nimble refresh` and \
`nimble install flowbrigade`.\n\
See https://nim-lang.org/install.html"
)
});
if !status.success() {
panic!(
"failed to build FlowBrigade native library from '{}'.\n\
Make sure Nim is installed and FlowBrigade is available via \
`nimble refresh` and `nimble install flowbrigade`, or set \
FLOWBRIGADE_LIB_DIR to an existing native library directory.",
source_dir.display()
);
}
native_dir
}
fn flowbrigade_source_layout(source_dir: &Path) -> (PathBuf, PathBuf) {
let checkout_source = source_dir.join("src").join("flowbrigade_c.nim");
if checkout_source.is_file() {
return (checkout_source, source_dir.join("src"));
}
let nimble_source = source_dir.join("flowbrigade_c.nim");
if nimble_source.is_file() {
return (nimble_source, source_dir.to_path_buf());
}
panic!(
"FlowBrigade source was found at '{}', but no C ABI source file was found.\n\
Expected either '{}' for a source checkout or '{}' for a Nimble package.\n\
Install FlowBrigade with `nimble refresh` and `nimble install flowbrigade`, \
or set FLOWBRIGADE_SOURCE_DIR to a local FlowBrigade checkout.",
source_dir.display(),
checkout_source.display(),
nimble_source.display()
);
}
fn nimble_flowbrigade_path() -> PathBuf {
let output = Command::new("nimble")
.arg("path")
.arg("flowbrigade")
.output()
.unwrap_or_else(|error| {
panic!(
"failed to run `nimble path flowbrigade`: {error}\n\
Install Nim/Nimble and then run `nimble refresh` and \
`nimble install flowbrigade`, or set FLOWBRIGADE_SOURCE_DIR \
to a local FlowBrigade checkout."
)
});
if !output.status.success() {
panic!(
"`nimble path flowbrigade` could not find FlowBrigade.\n\
Install it with `nimble refresh` and `nimble install flowbrigade`, or set \
FLOWBRIGADE_SOURCE_DIR to a local FlowBrigade checkout.\n\
stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
let stdout = String::from_utf8_lossy(&output.stdout);
let path = stdout
.lines()
.map(str::trim)
.find(|line| !line.is_empty())
.expect("`nimble path flowbrigade` returned no path");
Path::new(path).to_path_buf()
}
fn platform_library_name() -> &'static str {
if cfg!(target_os = "windows") {
"flowbrigade.dll"
} else if cfg!(target_os = "macos") {
"libflowbrigade.dylib"
} else {
"libflowbrigade.so"
}
}