picotcp-sys 0.2.1

Rust binding of picoTCP
extern crate bindgen;

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::fs::read_dir;
use std::path::PathBuf;
use std::process::Command;

use bindgen::Builder;

/// Run picoTCP's Makefile
fn build_picotcp(prefix: &Path, debug: bool) {
    println!("Building picotcp to: {}", prefix.to_string_lossy());
    Command::new("make")
            .arg("-C").arg("picotcp") // $RUSTOTCP_SOURCE/picotcp/
            .env("PREFIX", prefix.as_os_str())
            .env("DEBUG", if debug { "1" } else { "0" })
            .status().unwrap();
}

fn generate_bindings(out_file: &mut File, header_path: &Path) {
    let header_path = header_path.to_str().expect("Could not decode Path to String");
    let mut bindings = Builder::new(header_path);
    bindings.builtins();
    bindings.forbid_unknown_types();
    bindings.convert_macros(true);
    // bindings.link("picotcp", bindgen::LinkType::Static);
    // bindings.clang_arg("-L").clang_arg(picotcp_lib.to_str().expect("Could not decode Path to String"));
    let generated_bindings = bindings.generate().expect(&format!("Failed to generate bindings for {}", header_path));

    out_file.write(generated_bindings.to_string().as_bytes()).unwrap();
}

fn write_includes(includer_file: &mut File, includer_file_path: &Path, path: &Path) {
    let file_name = path.file_name().unwrap().to_str().expect("Could not decode Path to String");


    if path.file_name().unwrap() == "arch" {
        // Files in include/arch/ are a bit hackish and do some do not work
        // with clang.
        // So we'll just skip them (for now).
    }
    else if path == includer_file_path {
        // Do not self-include.
    }
    else if path.is_dir() {
        for path in read_dir(path).unwrap() {
            write_includes(includer_file, includer_file_path, path.unwrap().path().as_path());
        }
    }
    else {
        // If it's a file, just include it.
        assert!(file_name.ends_with(".h"));
        includer_file.write(format!("#include \"{}\"\n", path.to_str().unwrap()).as_bytes()).unwrap();
    }
}

fn link_picotcp(picotcp_lib_dir: &Path) {
    println!("cargo:rustc-link-lib=static=picotcp");
    println!("cargo:rustc-link-search=native={}", picotcp_lib_dir.to_str().expect("Could not decode Path to String"));
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let debug = env::var("DEBUG").unwrap_or("false".to_owned()) == "true";
    let out_dir = Path::new(&out_dir).to_path_buf();
    let out_file_path: PathBuf = Path::new("picotcp.rs").to_path_buf();
    let picotcp_prefix: PathBuf = { let mut p = out_dir.clone(); p.push("picotcp"); p };
    let picotcp_headers_dir: PathBuf = { let mut p = picotcp_prefix.clone(); p.push("include"); p };
    let includer_file_path: PathBuf = { let mut p = picotcp_headers_dir.clone(); p.push("picotcp_include_all.h"); p };
    let picotcp_lib_dir: PathBuf = { let mut p = picotcp_prefix.clone(); p.push("lib"); p };
    let mut out_file = File::create(out_file_path).expect("Failed to open file");

    build_picotcp(picotcp_prefix.as_path(), debug);

    {
        let mut includer_file = File::create(includer_file_path.as_path()).expect("Failed to open file");
        write_includes(&mut includer_file, includer_file_path.as_path(), picotcp_headers_dir.as_path());
    }

    generate_bindings(&mut out_file, includer_file_path.as_path());

    link_picotcp(picotcp_lib_dir.as_path());
}