cbindgen 0.8.6

A tool for generating C bindings to Rust code.
Documentation
extern crate cbindgen;

use cbindgen::*;
use std::path::Path;
use std::process::Command;
use std::{env, fs};

fn run_cbindgen(
    profile: &'static str,
    path: &Path,
    output: &Path,
    language: Language,
    style: Option<Style>,
) {
    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let program = Path::new(&crate_dir)
        .join("target")
        .join(profile)
        .join("cbindgen");

    let mut command = Command::new(program);
    match language {
        Language::Cxx => {}
        Language::C => {
            command.arg("--lang").arg("c");
        }
    }

    if let Some(style) = style {
        command.arg("--style");
        command.arg(match style {
            Style::Both => "both",
            Style::Tag => "tag",
            Style::Type => "type",
        });
    }

    command.arg("-o").arg(output);

    if env::var("CBINDGEN_TEST_VERIFY").is_ok() {
        command.arg("--verify");
    }

    let mut config = path.clone().to_path_buf();
    config.set_extension("toml");
    if config.exists() {
        command.arg("--config").arg(config);
    }

    command.arg(path);

    println!("Running: {:?}", command);
    let cbindgen_output = command.output().expect("failed to execute process");
    assert!(
        cbindgen_output.status.success(),
        "cbindgen failed: {:?}",
        output
    );
}

fn compile(cbindgen_output: &Path, language: Language) {
    let cc = env::var("CC").unwrap_or_else(|_| match language {
        Language::Cxx => "g++".to_owned(),
        Language::C => "gcc".to_owned(),
    });

    let mut object = cbindgen_output.to_path_buf();
    object.set_extension("o");

    let mut command = Command::new(cc);
    command.arg("-D").arg("DEFINED");
    command.arg("-c").arg(cbindgen_output);
    command.arg("-o").arg(&object);

    println!("Running: {:?}", command);
    let out = command.output().expect("failed to compile");
    assert!(out.status.success(), "Output failed to compile: {:?}", out);

    if object.exists() {
        fs::remove_file(object).unwrap();
    }
}

fn run_compile_test(
    profile: &'static str,
    name: &'static str,
    path: &Path,
    language: Language,
    style: Option<Style>,
) {
    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let mut output = Path::new(&crate_dir).join("tests").join("expectations");
    if let Some(style) = style {
        match style {
            Style::Both => {
                output.push("both");
            }
            Style::Tag => {
                output.push("tag");
            }
            Style::Type => {}
        }
    }
    match language {
        Language::Cxx => {
            output.push(format!("{}.cpp", name));
        }
        Language::C => {
            output.push(format!("{}.c", name));
        }
    }

    run_cbindgen(profile, path, &output, language, style);
    compile(&output, language);
}

fn test_file(profile: &'static str, name: &'static str, filename: &'static str) {
    let test = Path::new(filename);
    for style in &[Style::Type, Style::Tag, Style::Both] {
        run_compile_test(profile, name, &test, Language::C, Some(*style));
    }
    run_compile_test(profile, name, &test, Language::Cxx, None);
}

macro_rules! test_file {
    ($profile:ident, $test_function_name:ident, $name:expr, $file:tt) => {
        #[test]
        fn $test_function_name() {
            test_file(stringify!($profile), $name, $file);
        }
    };
}

// This file is generated by build.rs
include!(concat!(env!("OUT_DIR"), "/tests.rs"));