ext-php-rs/build.rs
David 73526d62b5
Drop object contents rather than deallocating whole object (#57)
The object memory is automatically deallocated, however, anything that
has been allocated on the heap (strings, vectors etc.) must be
deallocated while we are freeing the object.
2021-09-05 00:53:29 +12:00

164 lines
5.4 KiB
Rust

use std::{
collections::HashSet,
env,
path::{Path, PathBuf},
process::Command,
};
use bindgen::callbacks::{MacroParsingBehavior, ParseCallbacks};
use regex::{Captures, Regex};
extern crate bindgen;
// https://github.com/rust-lang/rust-bindgen/issues/687#issuecomment-450750547
#[derive(Debug)]
struct IgnoreMacros(HashSet<String>);
impl ParseCallbacks for IgnoreMacros {
fn will_parse_macro(&self, name: &str) -> MacroParsingBehavior {
if self.0.contains(name) {
MacroParsingBehavior::Ignore
} else {
MacroParsingBehavior::Default
}
}
}
const MIN_PHP_API_VER: u32 = 20200930;
const MAX_PHP_API_VER: u32 = 20200930;
fn main() {
// rerun if wrapper header is changed
println!("cargo:rerun-if-changed=src/wrapper/wrapper.h");
println!("cargo:rerun-if-changed=src/wrapper/wrapper.c");
// use php-config to fetch includes
let includes_cmd = Command::new("php-config")
.arg("--includes")
.output()
.expect("Unable to run `php-config`. Please ensure it is visible in your PATH.");
if !includes_cmd.status.success() {
let stderr = String::from_utf8(includes_cmd.stderr)
.unwrap_or_else(|_| String::from("Unable to read stderr"));
panic!("Error running `php-config`: {}", stderr);
}
// Ensure the PHP API version is supported.
// We could easily use grep and sed here but eventually we want to support Windows,
// so it's easier to just use regex.
let php_i_cmd = Command::new("php")
.arg("-i")
.output()
.expect("Unable to run `php -i`. Please ensure it is visible in your PATH.");
if !php_i_cmd.status.success() {
let stderr = String::from_utf8(includes_cmd.stderr)
.unwrap_or_else(|_| String::from("Unable to read stderr"));
panic!("Error running `php -i`: {}", stderr);
}
let php_i = String::from_utf8(php_i_cmd.stdout).expect("unable to parse `php -i` stdout");
let php_api_regex = Regex::new(r"PHP API => ([0-9]+)").unwrap();
let api_ver: Vec<Captures> = php_api_regex.captures_iter(php_i.as_ref()).collect();
match api_ver.first() {
Some(api_ver) => match api_ver.get(1) {
Some(api_ver) => {
let api_ver: u32 = api_ver.as_str().parse().unwrap();
if api_ver < MIN_PHP_API_VER || api_ver > MAX_PHP_API_VER {
panic!("The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}", api_ver, MIN_PHP_API_VER, MAX_PHP_API_VER);
}
},
None => panic!("Unable to retrieve PHP API version from `php -i`. Please check the installation and ensure it is callable.")
},
None => panic!("Unable to retrieve PHP API version from `php -i`. Please check the installation and ensure it is callable.")
};
let includes =
String::from_utf8(includes_cmd.stdout).expect("unable to parse `php-config` stdout");
// Build `wrapper.c` and link to Rust.
cc::Build::new()
.file("src/wrapper/wrapper.c")
.includes(
str::replace(includes.as_ref(), "-I", "")
.split(' ')
.map(|path| Path::new(path)),
)
.compile("wrapper");
let ignore_math_h_macros = IgnoreMacros(
vec![
// math.h:914 - enum which uses #define for values
"FP_NAN".into(),
"FP_INFINITE".into(),
"FP_ZERO".into(),
"FP_SUBNORMAL".into(),
"FP_NORMAL".into(),
// math.h:237 - enum which uses #define for values
"FP_INT_UPWARD".into(),
"FP_INT_DOWNWARD".into(),
"FP_INT_TOWARDZERO".into(),
"FP_INT_TONEARESTFROMZERO".into(),
"FP_INT_TONEAREST".into(),
]
.into_iter()
.collect(),
);
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindgen::Builder::default()
.header("src/wrapper/wrapper.h")
.clang_args(includes.split(' '))
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.parse_callbacks(Box::new(ignore_math_h_macros))
.rustfmt_bindings(true)
.no_copy("_zend_value")
.generate()
.expect("Unable to generate bindings for PHP")
.write_to_file(out_path.join("bindings.rs"))
.expect("Unable to write bindings file.");
let configure = Configure::get();
if configure.has_zts() {
println!("cargo:rustc-cfg=feature=\"zts\"");
}
if configure.debug() {
println!("cargo:rustc-cfg=feature=\"debug\"");
}
}
struct Configure(String);
impl Configure {
pub fn get() -> Self {
let cmd = Command::new("php-config")
.arg("--configure-options")
.output()
.expect("Unable to run `php-config --configure-options`. Please ensure it is visible in your PATH.");
if !cmd.status.success() {
let stderr = String::from_utf8(cmd.stderr)
.unwrap_or_else(|_| String::from("Unable to read stderr"));
panic!("Error running `php -i`: {}", stderr);
}
// check for the ZTS feature flag in configure
let stdout =
String::from_utf8(cmd.stdout).expect("Unable to read stdout from `php-config`.");
Self(stdout)
}
pub fn has_zts(&self) -> bool {
self.0.contains("--enable-zts")
}
pub fn debug(&self) -> bool {
self.0.contains("--enable-debug")
}
}