134 lines
4.2 KiB
Rust
134 lines
4.2 KiB
Rust
|
|
use std::env;
|
||
|
|
use std::ffi::OsString;
|
||
|
|
use std::process::Command;
|
||
|
|
|
||
|
|
fn main() {
|
||
|
|
// We check rustc version to enable features beyond MSRV, such as:
|
||
|
|
// - 1.59 => neon_intrinsics
|
||
|
|
let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc"));
|
||
|
|
let output = Command::new(rustc)
|
||
|
|
.arg("--version")
|
||
|
|
.output()
|
||
|
|
.expect("failed to check 'rustc --version'")
|
||
|
|
.stdout;
|
||
|
|
|
||
|
|
let raw_version = String::from_utf8(output)
|
||
|
|
.expect("rustc version output should be utf-8");
|
||
|
|
|
||
|
|
let version = match Version::parse(&raw_version) {
|
||
|
|
Ok(version) => version,
|
||
|
|
Err(err) => {
|
||
|
|
println!("cargo:warning=failed to parse `rustc --version`: {}", err);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
enable_new_features(version);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn enable_new_features(version: Version) {
|
||
|
|
enable_simd(version);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn enable_simd(version: Version) {
|
||
|
|
if env::var_os("CARGO_FEATURE_STD").is_none() {
|
||
|
|
println!("cargo:warning=building for no_std disables httparse SIMD");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if env::var_os("CARGO_CFG_MIRI").is_some() {
|
||
|
|
println!("cargo:warning=building for Miri disables httparse SIMD");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
let env_disable = "CARGO_CFG_HTTPARSE_DISABLE_SIMD";
|
||
|
|
if var_is(env_disable, "1") {
|
||
|
|
println!("cargo:warning=detected {} environment variable, disabling SIMD", env_disable);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 1.59.0 is the first version to support neon_intrinsics
|
||
|
|
if version >= Version(1, 59, 0) {
|
||
|
|
println!("cargo:rustc-cfg=httparse_simd_neon_intrinsics");
|
||
|
|
}
|
||
|
|
|
||
|
|
println!("cargo:rustc-cfg=httparse_simd");
|
||
|
|
|
||
|
|
// cfg(target_feature) isn't stable yet, but CARGO_CFG_TARGET_FEATURE has
|
||
|
|
// a list... We aren't doing anything unsafe, since the is_x86_feature_detected
|
||
|
|
// macro still checks in the actual lib, BUT!
|
||
|
|
//
|
||
|
|
// By peeking at the list here, we can change up slightly how we do feature
|
||
|
|
// detection in the lib. If our features aren't in the feature list, we
|
||
|
|
// stick with a cached runtime detection strategy.
|
||
|
|
//
|
||
|
|
// But if the features *are* in the list, we benefit from removing our cache,
|
||
|
|
// since the compiler will eliminate several branches with its internal
|
||
|
|
// cfg(target_feature) usage.
|
||
|
|
|
||
|
|
|
||
|
|
let env_runtime_only = "CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME";
|
||
|
|
if var_is(env_runtime_only, "1") {
|
||
|
|
println!("cargo:warning=detected {} environment variable, using runtime SIMD detection only", env_runtime_only);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
let feature_list = match env::var_os("CARGO_CFG_TARGET_FEATURE") {
|
||
|
|
Some(var) => match var.into_string() {
|
||
|
|
Ok(s) => s,
|
||
|
|
Err(_) => {
|
||
|
|
println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not valid utf-8");
|
||
|
|
return;
|
||
|
|
},
|
||
|
|
},
|
||
|
|
None => {
|
||
|
|
println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not set");
|
||
|
|
return
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
let features = feature_list.split(',').map(|s| s.trim());
|
||
|
|
if features.clone().any(|f| f == "sse4.2") {
|
||
|
|
println!("cargo:rustc-cfg=httparse_simd_target_feature_sse42");
|
||
|
|
}
|
||
|
|
if features.clone().any(|f| f == "avx2") {
|
||
|
|
println!("cargo:rustc-cfg=httparse_simd_target_feature_avx2");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||
|
|
struct Version (u32, u32, u32);
|
||
|
|
|
||
|
|
impl Version {
|
||
|
|
fn parse(s: &str) -> Result<Version, String> {
|
||
|
|
if !s.starts_with("rustc ") {
|
||
|
|
return Err(format!("unrecognized version string: {}", s));
|
||
|
|
}
|
||
|
|
let s = s.trim_start_matches("rustc ");
|
||
|
|
|
||
|
|
let mut iter = s
|
||
|
|
.split('.')
|
||
|
|
.take(3)
|
||
|
|
.map(|s| match s.find(|c: char| !c.is_ascii_digit()) {
|
||
|
|
Some(end) => &s[..end],
|
||
|
|
None => s,
|
||
|
|
})
|
||
|
|
.map(|s| s.parse::<u32>().map_err(|e| e.to_string()));
|
||
|
|
|
||
|
|
if iter.clone().count() != 3 {
|
||
|
|
return Err(format!("not enough version parts: {:?}", s));
|
||
|
|
}
|
||
|
|
|
||
|
|
let major = iter.next().unwrap()?;
|
||
|
|
let minor = iter.next().unwrap()?;
|
||
|
|
let patch = iter.next().unwrap()?;
|
||
|
|
|
||
|
|
Ok(Version(major, minor, patch))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn var_is(key: &str, val: &str) -> bool {
|
||
|
|
match env::var(key) {
|
||
|
|
Ok(v) => v == val,
|
||
|
|
Err(_) => false,
|
||
|
|
}
|
||
|
|
}
|