diff -r 7ec1cfe19a24 -r a4137aedcb3a build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build.rs Thu Feb 26 09:32:12 2026 -0500 @@ -0,0 +1,138 @@ +/*! +Build script for `pointsource_pde` +*/ + +//use pyo3::{prepare_freethreaded_python, types::PyModule, PyErr, Python}; +use conda_build::python_config; +use std::env::split_paths; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::process::Command; + +const REQUIRED_PYTHON_MODULES: [&str; 7] = [ + "dolfinx", "ufl", "numpy", "time", "petsc4py", "mpi4py", "json", +]; + +const DOLFINX_ACCESS_CXX_SOURCES: [&str; 3] = + ["nanobind_helpers.cc", "minmax_p2.cc", "function.cc"]; + +const PY_SRC: [&str; 7] = [ + "src/dolfinx_extras.py", + "src/compose.py", + "src/measure.py", + "src/quadratic_dataterm.py", + "src/convection_diffusion.py", + "src/laser_sampling.py", + "src/full_sampling.py", +]; + +fn main() -> Result<(), std::io::Error> { + // Check Python module availability. + // This doesn't necessarily work in a Conda setup. + // prepare_freethreaded_python(); + // Python::with_gil(|py| { + // for m in REQUIRED_PYTHON_MODULES { + // PyModule::import(py, m)?; + // } + // Ok::<_, PyErr>(()) + // })?; + + let (pyc_e, py_is_conda) = python_config(); + let py_path = pyc_e.and_then(|pyc| pyc.exec_prefix_path())?; + let py_exec = py_path.join("bin").join("python3"); + let import_check_code = REQUIRED_PYTHON_MODULES + .map(|m| format!("import {}\n", m)) + .concat(); + let res = Command::new(&py_exec) + .args(["-c", import_check_code.as_ref()]) + .status()?; + if !res.success() { + return Err(std::io::Error::other(format!( + "Failed to load required Python modules (python execuitable {}{})", + py_exec.display(), + if py_is_conda { " from Conda" } else { "" } + ))); + } + + // Set up local-to-crate paths + let cc_mod_name = "dolfinx_access"; + let crate_name = "pointsource_pde"; + let src = PathBuf::from("src"); + let cc_mod_root = src.join(cc_mod_name); + let bridge = src.join(format!("{cc_mod_name}.rs")); + let cc_sources = DOLFINX_ACCESS_CXX_SOURCES.map(|f| cc_mod_root.join(f)); + let mut include_dirs = Vec::from([PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("include")]); + + // Add (an estimate of) dolfinx ldflags. This shouldn't really be our problem to deal with, + // but everything that is not piss, is shit. + add_dep_ldflags("DEP_DOLFINX_LDFLAGS"); + + // Add external include paths + add_dep_includes(&mut include_dirs, "DEP_DOLFINX_INCLUDE"); + add_dep_includes(&mut include_dirs, "DEP_NANOBIND_INCLUDE"); + + // This may be useful to enable sometimes, when there's + // trouble location GSL. + // pkg_config::Config::new().probe("gsl").unwrap(); + + // Build the bridge + cxx_build::bridge(&bridge) + .std("c++20") + .files(&cc_sources) + .includes(&include_dirs) + .compile(crate_name); + + for dep in PY_SRC { + println!("cargo::rerun-if-changed={}", dep); + } + + let header_prefix = PathBuf::from("include").join(cc_mod_name); + let headers = DOLFINX_ACCESS_CXX_SOURCES + .into_iter() + .map(|s| header_prefix.join(s.replace(".cc", ".h"))); + + for dep in cc_sources + .into_iter() + .chain(headers) + .chain(std::iter::once(bridge)) + { + println!("cargo:rerun-if-changed={}", dep.display()); + } + + // Generate .clangd + let mut dot_clangd = File::create(".clangd")?; + write!( + dot_clangd, + "\ +# Automatically generated by build.rs +CompileFlags: + Add: +" + )?; + for i in include_dirs { + writeln!(dot_clangd, " - --include-directory={}", i.display())?; + } + + Ok(()) +} + +/// Add list of include directions in the environment variable `dep` to +/// `include_dirs`. +fn add_dep_includes(include_dirs: &mut Vec, dep: &str) { + if let Some(include) = std::env::var_os(&dep) { + include_dirs.extend(split_paths(&include).collect::>()); + } else { + panic!("{dep} unset."); + } +} + +fn add_dep_ldflags(dep: &str) { + if let Some(ldflags) = std::env::var_os(&dep) { + for lp in split_paths(&ldflags) { + println!("cargo:rustc-link-arg=-Wl,{}", lp.display()); + } + } else { + panic!("{dep} unset."); + } +}