build.rs

changeset 1
a4137aedcb3a
child 3
c3a4f4bb87f7
--- /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<PathBuf>, dep: &str) {
+    if let Some(include) = std::env::var_os(&dep) {
+        include_dirs.extend(split_paths(&include).collect::<Vec<_>>());
+    } 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.");
+    }
+}

mercurial