libwfa2 0.1.1

Bindings to the C implementation of WFA2-lib
Documentation
extern crate bindgen;
// extern crate openmp_sys; // might be useful for statically comiling with openmp

use std::{
    env,
    fs::{read_dir, remove_dir_all},
    path::PathBuf,
    process::Command,
};

/// To avoid any mistypes in paths, I chose to use this struct to set all paths once and use them for here on.
struct BuildPaths(PathBuf);

impl BuildPaths {
    const WFA2FOLDER: &str = "WFA2-lib";
    fn out_dir(&self) -> &PathBuf { &self.0 }
    // The path of the WFA2-lib right in the base folder of this project (source)
    fn wfa_src(&self) -> PathBuf { Self::WFA2FOLDER.into() }
    // Copy in OUT_DIR, this is where WFA is built!
    fn wfa_out(&self) -> PathBuf { self.out_dir().join(Self::WFA2FOLDER) }
    // Library path for WFA lib in build directory (wfa_out)
    // This is needed for the linker, otherwise wfalib will not be found.
    fn wfa_out_lib(&self) -> PathBuf { self.wfa_out().join("lib") }
}

impl Default for BuildPaths {
    fn default() -> Self {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        Self(out_dir)
    }
}

fn build_wfa() -> Option<()> {
    let build_paths = BuildPaths::default();

    let mut wfa_dir = read_dir(build_paths.wfa_src()).ok()?;
    if !wfa_dir.any(|f| f.unwrap().file_name() == "Makefile") {
        return None;
    }
    
    let _ = remove_dir_all(build_paths.wfa_out());

    // copy the WFA dir to OUT_PATH and build it there... clunky, but
    // don't want to pull in the entire 100MB WFA repo, since git2
    // doesn't seem to support shallow clones, and build scripts
    // should only modify things inside OUT_PATH. since the WFA folder
    // is just a couple MB, this is fine for now.
    let _cp_wfa = Command::new("cp")
        .arg("-r")
        .arg(build_paths.wfa_src())
        .arg(build_paths.out_dir())
        .output()
        .expect("Copy failed");

    // hotfix Makefile
    let _makefile_fix = Command::new("sed")
        .arg("-i")
        .arg("s/CC_FLAGS=-Wall -g/CC_FLAGS=-Wall -g -fPIC/g")
        .output()
        .expect("Failed hotfixing makefile");


    let output = Command::new("make")
        .arg("clean")
        .arg("all")
        .current_dir(build_paths.wfa_out())
        .output();

    match output {
        Ok(output) => { 
            if output.status.success() {
            Some(())
        } else {
            panic!("1) make error: {}", String::from_utf8_lossy(&output.stderr));
        }
    },
        Err(err) => { 
            panic!("2) make error: {}", err);
        },
    }
}


fn wfa() {
    let build_paths = BuildPaths::default();

    // 1. Link instructions for Cargo.

    // The directory of the WFA libraries, added to the search path.
    // println!("cargo:rustc-link-search=../wfa2/lib");
    // println!("cargo:rustc-link-search=WFA2-lib/lib");
    // Link the `wfa-lib` library.
    // Mind that the name of wfalib is set in the makefile, not here.
    // Relevant part in makefile:
    //  LIB_WFA=$(FOLDER_LIB)/libwfa.a
    //  LIB_WFA_CPP=$(FOLDER_LIB)/libwfacpp.a
    // Despite the folder being named WFA2-lib, the library has to be linked with wfa
    println!("cargo:rustc-link-lib=wfa");
    // Also link `omp`.
    println!("cargo:rustc-link-lib=gomp"); // omp does not work for some reason, gomp does.
    // Invalidate the built crate whenever the linked library changes.
    println!("cargo:rerun-if-changed={}/libwfa.a", build_paths.wfa_out_lib().display());
    // Rustc lib search for library in "OUT_DIR"
    println!("cargo:rustc-link-search={}", build_paths.wfa_out_lib().display());
    // 2. Generate bindings.

    let bindings = bindgen::Builder::default()
        // Generate bindings for this header file.
        // .header("../wfa2/wavefront/wavefront_align.h")
        .header("WFA2-lib/wavefront/wavefront_align.h")
        // Add this directory to the include path to find included header files.
        // .clang_arg("-I../wfa2")
        .clang_arg(format!("-I{}", build_paths.wfa_src().display()))
        // Generate bindings for all functions starting with `wavefront_`.
        .allowlist_function("wavefront_.*")
        // Generate bindings for all variables starting with `wavefront_`.
        .allowlist_var("wavefront_.*")
        // Invalidate the built crate whenever any of the included header files
        // changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings_wfa.rs file.
    bindings
        .write_to_file(build_paths.out_dir().join("bindings_wfa.rs"))
        .expect("Couldn't write bindings!");
}

fn main() {
    build_wfa();
    wfa();
}