1 use std::env;
2 use std::ffi::OsString;
3 use std::process::Command;
4 
main()5 fn main() {
6     // We check rustc version to enable features beyond MSRV, such as:
7     // - 1.59 => neon_intrinsics
8     let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc"));
9     let output = Command::new(rustc)
10         .arg("--version")
11         .output()
12         .expect("failed to check 'rustc --version'")
13         .stdout;
14 
15     let raw_version = String::from_utf8(output)
16         .expect("rustc version output should be utf-8");
17 
18     let version = match Version::parse(&raw_version) {
19         Ok(version) => version,
20         Err(err) => {
21             println!("cargo:warning=failed to parse `rustc --version`: {}", err);
22             return;
23         }
24     };
25 
26     enable_new_features(version);
27 }
28 
enable_new_features(version: Version)29 fn enable_new_features(version: Version) {
30     enable_simd(version);
31 }
32 
enable_simd(version: Version)33 fn enable_simd(version: Version) {
34     if env::var_os("CARGO_FEATURE_STD").is_none() {
35         println!("cargo:warning=building for no_std disables httparse SIMD");
36         return;
37     }
38     if env::var_os("CARGO_CFG_MIRI").is_some() {
39         println!("cargo:warning=building for Miri disables httparse SIMD");
40         return;
41     }
42 
43     let env_disable = "CARGO_CFG_HTTPARSE_DISABLE_SIMD";
44     if var_is(env_disable, "1") {
45         println!("cargo:warning=detected {} environment variable, disabling SIMD", env_disable);
46         return;
47     }
48 
49     // 1.59.0 is the first version to support neon_intrinsics
50     if version >= Version(1, 59, 0) {
51         println!("cargo:rustc-cfg=httparse_simd_neon_intrinsics");
52     }
53 
54     println!("cargo:rustc-cfg=httparse_simd");
55 
56     // cfg(target_feature) isn't stable yet, but CARGO_CFG_TARGET_FEATURE has
57     // a list... We aren't doing anything unsafe, since the is_x86_feature_detected
58     // macro still checks in the actual lib, BUT!
59     //
60     // By peeking at the list here, we can change up slightly how we do feature
61     // detection in the lib. If our features aren't in the feature list, we
62     // stick with a cached runtime detection strategy.
63     //
64     // But if the features *are* in the list, we benefit from removing our cache,
65     // since the compiler will eliminate several branches with its internal
66     // cfg(target_feature) usage.
67 
68 
69     let env_runtime_only = "CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME";
70     if var_is(env_runtime_only, "1") {
71         println!("cargo:warning=detected {} environment variable, using runtime SIMD detection only", env_runtime_only);
72         return;
73     }
74     let feature_list = match env::var_os("CARGO_CFG_TARGET_FEATURE") {
75         Some(var) => match var.into_string() {
76             Ok(s) => s,
77             Err(_) => {
78                 println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not valid utf-8");
79                 return;
80             },
81         },
82         None => {
83             println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not set");
84             return
85         },
86     };
87 
88     let features = feature_list.split(',').map(|s| s.trim());
89     if features.clone().any(|f| f == "sse4.2") {
90         println!("cargo:rustc-cfg=httparse_simd_target_feature_sse42");
91     }
92     if features.clone().any(|f| f == "avx2") {
93         println!("cargo:rustc-cfg=httparse_simd_target_feature_avx2");
94     }
95 }
96 
97 #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
98 struct Version (u32, u32, u32);
99 
100 impl Version {
parse(s: &str) -> Result<Version, String>101     fn parse(s: &str) -> Result<Version, String> {
102         if !s.starts_with("rustc ") {
103             return Err(format!("unrecognized version string: {}", s));
104         }
105         let s = s.trim_start_matches("rustc ");
106 
107         let mut iter = s
108             .split('.')
109             .take(3)
110             .map(|s| match s.find(|c: char| !c.is_ascii_digit()) {
111                 Some(end) => &s[..end],
112                 None => s,
113             })
114             .map(|s| s.parse::<u32>().map_err(|e| e.to_string()));
115 
116         if iter.clone().count() != 3 {
117             return Err(format!("not enough version parts: {:?}", s));
118         }
119 
120         let major = iter.next().unwrap()?;
121         let minor = iter.next().unwrap()?;
122         let patch = iter.next().unwrap()?;
123 
124         Ok(Version(major, minor, patch))
125     }
126 }
127 
var_is(key: &str, val: &str) -> bool128 fn var_is(key: &str, val: &str) -> bool {
129     match env::var(key) {
130         Ok(v) => v == val,
131         Err(_) => false,
132     }
133 }
134