1use std::env;
2use std::fs::File;
3use std::path::PathBuf;
4
5use std::io::{BufRead, BufReader};
6use std::sync::OnceLock;
7
8#[derive(Debug, Clone)]
9struct IgnorePattern {
10 os: Option<String>,
11 arch: Option<String>,
12 target_env: Option<String>,
13 engine: Option<String>,
14 compiler: Option<String>,
15 pattern_to_ignore: String,
16}
17
18impl IgnorePattern {
19 fn should_ignore(
20 &self,
21 os: &str,
22 arch: &str,
23 target_env: &str,
24 engine: &str,
25 compiler: &str,
26 canonical_path: &str,
27 ) -> bool {
28 self.os.as_ref().is_none_or(|val| val == os)
29 && self.arch.as_ref().is_none_or(|val| val == arch)
30 && self.target_env.as_ref().is_none_or(|val| val == target_env)
31 && self.engine.as_ref().is_none_or(|val| val == engine)
32 && self.compiler.as_ref().is_none_or(|val| val == compiler)
33 && (self.pattern_to_ignore == "*" || canonical_path.contains(&*self.pattern_to_ignore))
34 }
35}
36
37#[derive(Debug, Clone)]
38pub struct Ignores {
39 patterns: Vec<IgnorePattern>,
41}
42
43impl Ignores {
44 pub fn should_ignore(
46 &self,
47 os: &str,
48 arch: &str,
49 target_env: &str,
50 engine: &str,
51 compiler: &str,
52 canonical_path: &str,
53 ) -> bool {
54 self.patterns.iter().any(|p| {
55 p.should_ignore(os, arch, target_env, engine, compiler, canonical_path)
57 })
58 }
59
60 pub fn should_ignore_host(&self, engine: &str, compiler: &str, canonical_path: &str) -> bool {
61 static CFG_TARGET_OS: OnceLock<String> = OnceLock::new();
62 let target_os = CFG_TARGET_OS.get_or_init(|| {
63 env::var("CFG_TARGET_OS")
64 .expect("CFG_TARGET_OS variable expected from build.rs")
65 .to_string()
66 });
67 static CFG_TARGET_ARCH: OnceLock<String> = OnceLock::new();
68 let target_arch = CFG_TARGET_ARCH.get_or_init(|| {
69 env::var("CFG_TARGET_ARCH")
70 .expect("CFG_TARGET_ARCH variable expected from build.rs")
71 .to_string()
72 });
73 static CFG_TARGET_ENV: OnceLock<String> = OnceLock::new();
74 let target_env = CFG_TARGET_ENV.get_or_init(|| {
75 env::var("CFG_TARGET_ENV")
76 .expect("CFG_TARGET_ENV variable expected from build.rs")
77 .to_string()
78 });
79
80 self.should_ignore(
81 target_os,
82 target_arch,
83 target_env,
84 engine,
85 compiler,
86 canonical_path,
87 )
88 }
89
90 pub fn build_from_path(path: PathBuf) -> Ignores {
92 let file = File::open(path).unwrap();
93 let reader = BufReader::new(file);
94 let mut patterns = Vec::new();
95
96 for (i, line) in reader.lines().enumerate() {
97 let line = line.unwrap();
98 let line = if line.contains('#') {
100 let l: Vec<&str> = line.splitn(2, '#').collect();
101 l[0].to_string()
102 } else {
103 line
104 };
105
106 let line = line.trim().to_string();
107
108 if line.contains(' ') {
111 let l: Vec<&str> = line.splitn(2, ' ').collect();
112 let mut os: Option<String> = None;
113 let mut arch: Option<String> = None;
114 let mut target_env: Option<String> = None;
115 let mut engine: Option<String> = None;
116 let mut compiler: Option<String> = None;
117 for alias in l[0].trim().split('+') {
118 match alias {
119 "windows" | "macos" | "linux" => {
121 os = Some(alias.to_string());
122 }
123 "musl" => {
125 target_env = Some(alias.to_string());
126 }
127 "aarch64" | "x86" | "x64" | "riscv64" | "loongarch64" => {
129 arch = Some(alias.to_string());
130 }
131 "universal" | "engine" => {
133 engine = Some(alias.to_string());
134 }
135 "cranelift" | "llvm" | "singlepass" => {
137 compiler = Some(alias.to_string());
138 }
139 other => {
140 panic!(
141 "Alias {:?} not currently supported (defined in ignores.txt in line {})",
142 other,
143 i + 1
144 );
145 }
146 }
147 }
148 let pattern_to_ignore = l[1].trim().to_string();
149 patterns.push(IgnorePattern {
150 os,
151 arch,
152 target_env,
153 engine,
154 compiler,
155 pattern_to_ignore,
156 });
157 } else {
158 if line.is_empty() {
159 continue;
160 }
161 patterns.push(IgnorePattern {
162 os: None,
163 arch: None,
164 target_env: None,
165 engine: None,
166 compiler: None,
167 pattern_to_ignore: line,
168 });
169 };
170 }
171 Ignores { patterns }
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn features_match() -> Result<(), ()> {
181 assert!(
182 IgnorePattern {
183 os: None,
184 arch: None,
185 target_env: None,
186 engine: None,
187 compiler: None,
188 pattern_to_ignore: "*".to_string()
189 }
190 .should_ignore(
191 "unknown",
192 "unknown",
193 "",
194 "engine",
195 "compiler",
196 "some::random::text"
197 )
198 );
199 assert!(
200 IgnorePattern {
201 os: None,
202 arch: None,
203 target_env: None,
204 engine: None,
205 compiler: None,
206 pattern_to_ignore: "some::random".to_string()
207 }
208 .should_ignore(
209 "unknown",
210 "unknown",
211 "",
212 "engine",
213 "compiler",
214 "some::random::text"
215 )
216 );
217 assert!(
218 !IgnorePattern {
219 os: Some("macos".to_string()),
220 arch: None,
221 target_env: None,
222 engine: None,
223 compiler: None,
224 pattern_to_ignore: "other".to_string()
225 }
226 .should_ignore(
227 "unknown",
228 "unknown",
229 "",
230 "engine",
231 "compiler",
232 "some::random::text"
233 )
234 );
235 assert!(
236 !IgnorePattern {
237 os: Some("macos".to_string()),
238 arch: None,
239 target_env: None,
240 engine: Some("universal".to_string()),
241 compiler: None,
242 pattern_to_ignore: "other".to_string()
243 }
244 .should_ignore(
245 "macos",
246 "unknown",
247 "",
248 "universal",
249 "compiler",
250 "some::random::text"
251 )
252 );
253 Ok(())
254 }
255}