extern crate bindgen;
use regex::Regex;
use std::panic::catch_unwind;
use std::path::PathBuf;
use std::{env, io};
use bindgen::callbacks::{IntKind, ParseCallbacks};
type FeatureFilter<'a, T> = (T, Option<&'a str>);
const SOURCE_FILES: &[FeatureFilter<&str>] = &[
("src/c/defaults.c", None), ("src/c/strings.c", None), ("src/c/exits.c", Some("exits")), ("src/c/pcf.c", Some("pcf")), ];
const HEADER_FILES: &[FeatureFilter<&str>] = &[
("cmqc.h", None), ("cmqxc.h", None), ("cmqstrc.h", None), ("cmqbc.h", Some("mqai")), ("cmqcfc.h", Some("pcf")), ];
const FUNCTIONS: &[FeatureFilter<&str>] = &[
("MQ.+", None),
("mq.+", Some("mqai"))
];
const TYPES: &[FeatureFilter<&[&str]>] = &[
(
&[
"MQMD", "MQMDE", "MQMD1", "MQMD2", "MQPD", "MQIMPO", "MQMHBO", "MQBO", "MQDMHO", "MQCMHO", "MQSRO", "MQSD",
"MQGMO", "MQPMO", "MQOD", "MQCNO", "MQCD", "MQCSP", "MQSCO", "MQBNO", "MQAIR", "MQBMHO", "MQCBD",
"MQCHARV", "MQCIH", "MQCTLO", "MQDH", "MQDLH", "MQDMPO", "MQIIH", "MQOR", "MQRFH", "MQRFH2", "MQRMH",
"MQRR", "MQSMPO", "MQSTS", "MQTM", "MQTMC2", "MQWIH", "MQXQH",
],
None,
),
(
&[
"MQCFH", "MQCFBF", "MQCFBS", "MQCFGR", "MQCFIF", "MQCFIL", "MQCFIL64", "MQCFIN", "MQCFIN64", "MQCFSF",
"MQCFSL", "MQCFST", "MQEPH",
],
Some("pcf"),
),
(
&[
"MQACH", "MQAXC", "MQAXP", "MQCXP", "MQDXP", "MQNXP", "MQPBC", "MQPSXP", "MQSBC", "MQWCR", "MQWDR",
"MQWDR1", "MQWDR2", "MQWQR", "MQWQR1", "MQWQR2", "MQWQR3", "MQWQR4", "MQWXP", "MQWXP1", "MQWXP2", "MQWXP3",
"MQWXP4", "MQXEPO",
],
Some("exits"),
),
];
const DEF_CONST: &[(&[&str], IntKind)] = &[
(
&["^MQ.*_ERROR$"], IntKind::Custom {
name: "MQLONG",
is_signed: true,
},
),
(
&[".+_LENGTH(_.)?"], IntKind::Custom {
name: "usize",
is_signed: false,
},
),
(
&["^MQHM_.+"], IntKind::Custom {
name: "MQHMSG",
is_signed: true,
},
),
(
&["^MQHO_.+"], IntKind::Custom {
name: "MQHOBJ",
is_signed: true,
},
),
(
&["^MQHC_.+"], IntKind::Custom {
name: "MQHCONN",
is_signed: true,
},
),
(&["^MQ.+_MASK$"], IntKind::U32), (
&["^MQ[A-Z]{2,12}_.+"], IntKind::Custom {
name: "MQLONG",
is_signed: true,
},
),
];
#[derive(Debug)]
struct MQCTypeChooser;
impl ParseCallbacks for MQCTypeChooser {
fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
DEF_CONST
.iter()
.find(|&(matchers, _)| matchers.iter().any(|r| Regex::new(r).unwrap().is_match(name)))
.map(|&(_, int_kind)| int_kind)
}
}
fn feature_filter<T>((.., feature): &(T, Option<&str>)) -> bool {
match feature {
Some(name) => env::var("CARGO_FEATURE_".to_string() + &(name.to_uppercase())).is_ok(),
None => true,
}
}
fn main() -> Result<(), io::Error> {
let mq_home_path = PathBuf::from(env::var("MQ_HOME").unwrap_or_else(|_| "/opt/mqm".to_owned()));
let out_path = PathBuf::from(env::var("OUT_DIR").map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?);
if env::var("CARGO_FEATURE_LINK").is_ok() {
let mq_lib_path = mq_home_path.join("lib64");
println!("cargo:rustc-link-search={}", mq_lib_path.display());
println!("cargo:rustc-link-lib=mqm_r");
}
let mq_inc_path = mq_home_path.join("inc");
let sources = SOURCE_FILES
.iter()
.filter(|t| feature_filter(t))
.map(|(source, ..)| source);
catch_unwind(|| {
cc::Build::new()
.static_flag(false)
.flag_if_supported("-nostartfiles")
.include(&mq_inc_path)
.files(sources)
.warnings(true)
.compile("defaults")
})
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Failed to compile c files"))?;
let builder = bindgen::builder()
.generate_cstr(true)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.parse_callbacks(Box::new(MQCTypeChooser))
.allowlist_var(".*");
let builder = HEADER_FILES
.iter()
.filter(|t| feature_filter(t))
.try_fold(builder, |builder, (header, ..)| {
mq_inc_path
.join(header)
.to_str()
.ok_or(io::Error::new(
io::ErrorKind::InvalidData,
format!("\"{header}\" is not valid"),
))
.map(|h| builder.header(h))
})?;
let builder = TYPES
.iter()
.filter(|t| feature_filter(t))
.flat_map(|(struc, ..)| *struc)
.fold(builder, |builder, struc| builder.allowlist_type(struc));
let builder = FUNCTIONS
.iter()
.filter(|t| feature_filter(t))
.fold(builder, |builder, (func, ..)| builder.allowlist_function(func));
builder
.generate()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))? .write_to_file(out_path.join("bindings.rs")) }