1 pub mod lexer; 2 mod parser; 3 4 use smallvec::SmallVec; 5 use std::ops::Range; 6 7 /// A predicate function, used to combine 1 or more predicates 8 /// into a single value 9 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] 10 pub enum Func { 11 /// `not()` with a configuration predicate. It is true if its predicate 12 /// is false and false if its predicate is true. 13 Not, 14 /// `all()` with a comma separated list of configuration predicates. It 15 /// is false if at least one predicate is false. If there are no predicates, 16 /// it is true. 17 /// 18 /// The associated `usize` is the number of predicates inside the `all()`. 19 All(usize), 20 /// `any()` with a comma separated list of configuration predicates. It 21 /// is true if at least one predicate is true. If there are no predicates, 22 /// it is false. 23 /// 24 /// The associated `usize` is the number of predicates inside the `any()`. 25 Any(usize), 26 } 27 28 use crate::targets as targ; 29 30 /// All predicates that pertains to a target, except for `target_feature` 31 #[derive(Clone, Copy, PartialEq, Debug)] 32 pub enum TargetPredicate<'a> { 33 /// [target_arch](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch) 34 Arch(targ::Arch<'a>), 35 /// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian) 36 Endian(targ::Endian), 37 /// [target_env](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env) 38 Env(targ::Env<'a>), 39 /// [target_family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family) 40 /// This also applies to the bare [`unix` and `windows`](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows) 41 /// predicates. 42 Family(targ::Family), 43 /// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) 44 Os(targ::Os<'a>), 45 /// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width) 46 PointerWidth(u8), 47 /// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor) 48 Vendor(targ::Vendor<'a>), 49 } 50 51 pub trait TargetMatcher { matches(&self, tp: TargetPredicate<'_>) -> bool52 fn matches(&self, tp: TargetPredicate<'_>) -> bool; 53 } 54 55 impl<'a> TargetMatcher for targ::TargetInfo<'a> { matches(&self, tp: TargetPredicate<'_>) -> bool56 fn matches(&self, tp: TargetPredicate<'_>) -> bool { 57 use TargetPredicate::{Arch, Endian, Env, Family, Os, PointerWidth, Vendor}; 58 59 match tp { 60 Arch(a) => a == self.arch, 61 Endian(end) => end == self.endian, 62 // The environment is allowed to be an empty string 63 Env(env) => match self.env { 64 Some(e) => env == e, 65 None => env.0.is_empty(), 66 }, 67 Family(fam) => Some(fam) == self.family, 68 Os(os) => Some(os) == self.os, 69 PointerWidth(w) => w == self.pointer_width, 70 Vendor(ven) => match self.vendor { 71 Some(v) => ven == v, 72 None => ven.0 == "unknown", 73 }, 74 } 75 } 76 } 77 78 #[cfg(feature = "targets")] 79 impl TargetMatcher for target_lexicon::Triple { 80 #[allow(clippy::cognitive_complexity)] 81 #[allow(clippy::match_same_arms)] matches(&self, tp: TargetPredicate<'_>) -> bool82 fn matches(&self, tp: TargetPredicate<'_>) -> bool { 83 use target_lexicon::*; 84 use TargetPredicate::{Arch, Endian, Env, Family, Os, PointerWidth, Vendor}; 85 86 match tp { 87 Arch(arch) => { 88 if arch.0 == "x86" { 89 matches!(self.architecture, Architecture::X86_32(_)) 90 } else if arch.0 == "wasm32" { 91 self.architecture == Architecture::Wasm32 92 || self.architecture == Architecture::Asmjs 93 } else if arch.0 == "arm" { 94 matches!(self.architecture, Architecture::Arm(_)) 95 } else { 96 match arch.0.parse::<Architecture>() { 97 Ok(a) => match (self.architecture, a) { 98 (Architecture::Mips32(_), Architecture::Mips32(_)) 99 | (Architecture::Mips64(_), Architecture::Mips64(_)) 100 | (Architecture::Powerpc64le, Architecture::Powerpc64) 101 | (Architecture::Riscv32(_), Architecture::Riscv32(_)) 102 | (Architecture::Riscv64(_), Architecture::Riscv64(_)) 103 | (Architecture::Sparcv9, Architecture::Sparc64) => true, 104 (a, b) => a == b, 105 }, 106 Err(_) => false, 107 } 108 } 109 } 110 Endian(end) => match self.architecture.endianness() { 111 Ok(endian) => matches!( 112 (end, endian), 113 (crate::targets::Endian::little, Endianness::Little) 114 | (crate::targets::Endian::big, Endianness::Big) 115 ), 116 117 Err(_) => false, 118 }, 119 Env(env) => { 120 // The environment is implied by some operating systems 121 match self.operating_system { 122 OperatingSystem::Redox => env.0 == "relibc", 123 OperatingSystem::VxWorks => env.0 == "gnu", 124 OperatingSystem::Freebsd => match self.architecture { 125 Architecture::Arm(ArmArchitecture::Armv6) 126 | Architecture::Arm(ArmArchitecture::Armv7) => env.0 == "gnueabihf", 127 _ => env.0.is_empty(), 128 }, 129 OperatingSystem::Netbsd => match self.architecture { 130 Architecture::Arm(ArmArchitecture::Armv6) 131 | Architecture::Arm(ArmArchitecture::Armv7) => env.0 == "eabihf", 132 _ => env.0.is_empty(), 133 }, 134 OperatingSystem::None_ 135 | OperatingSystem::Cloudabi 136 | OperatingSystem::Hermit 137 | OperatingSystem::Ios => env.0.is_empty(), 138 _ => { 139 if env.0.is_empty() { 140 matches!( 141 self.environment, 142 Environment::Unknown 143 | Environment::Android 144 | Environment::Softfloat 145 | Environment::Androideabi 146 | Environment::Eabi 147 ) 148 } else { 149 match env.0.parse::<Environment>() { 150 Ok(e) => { 151 // Rustc shortens multiple "gnu*" environments to just "gnu" 152 if env.0 == "gnu" { 153 match self.environment { 154 Environment::Gnu 155 | Environment::Gnuabi64 156 | Environment::Gnueabi 157 | Environment::Gnuspe 158 | Environment::Gnux32 159 | Environment::Gnueabihf => true, 160 // Rust 1.49.0 changed all android targets to have the 161 // gnu environment 162 Environment::Android | Environment::Androideabi 163 if self.operating_system 164 == OperatingSystem::Linux => 165 { 166 true 167 } 168 Environment::Kernel => { 169 self.operating_system == OperatingSystem::Linux 170 } 171 _ => false, 172 } 173 } else if env.0 == "musl" { 174 matches!( 175 self.environment, 176 Environment::Musl 177 | Environment::Musleabi 178 | Environment::Musleabihf 179 | Environment::Muslabi64 180 ) 181 } else if env.0 == "uclibc" { 182 matches!( 183 self.environment, 184 Environment::Uclibc | Environment::Uclibceabi 185 ) 186 } else { 187 self.environment == e 188 } 189 } 190 Err(_) => false, 191 } 192 } 193 } 194 } 195 } 196 Family(fam) => { 197 use target_lexicon::OperatingSystem::{ 198 AmdHsa, Bitrig, Cloudabi, Cuda, Darwin, Dragonfly, Emscripten, Freebsd, 199 Fuchsia, Haiku, Hermit, Illumos, Ios, L4re, Linux, MacOSX, Nebulet, Netbsd, 200 None_, Openbsd, Redox, Solaris, Tvos, Uefi, Unknown, VxWorks, Wasi, Windows, 201 }; 202 Some(fam) 203 == match self.operating_system { 204 Unknown | AmdHsa | Bitrig | Cloudabi | Cuda | Hermit | Nebulet | None_ 205 | Uefi | Wasi => None, 206 Darwin 207 | Dragonfly 208 | Emscripten 209 | Freebsd 210 | Fuchsia 211 | Haiku 212 | Illumos 213 | Ios 214 | L4re 215 | MacOSX { .. } 216 | Netbsd 217 | Openbsd 218 | Redox 219 | Solaris 220 | Tvos 221 | VxWorks => Some(crate::targets::Family::unix), 222 Linux => { 223 // The 'kernel' environment is treated specially as not-unix 224 if self.environment != Environment::Kernel { 225 Some(crate::targets::Family::unix) 226 } else { 227 None 228 } 229 } 230 Windows => Some(crate::targets::Family::windows), 231 // I really dislike non-exhaustive :( 232 _ => None, 233 } 234 } 235 Os(os) => match os.0.parse::<OperatingSystem>() { 236 Ok(o) => self.operating_system == o, 237 Err(_) => { 238 // Handle special case for darwin/macos, where the triple is 239 // "darwin", but rustc identifies the OS as "macos" 240 if os.0 == "macos" && self.operating_system == OperatingSystem::Darwin { 241 true 242 } else { 243 // For android, the os is still linux, but the environment is android 244 os.0 == "android" 245 && self.operating_system == OperatingSystem::Linux 246 && (self.environment == Environment::Android 247 || self.environment == Environment::Androideabi) 248 } 249 } 250 }, 251 Vendor(ven) => match ven.0.parse::<target_lexicon::Vendor>() { 252 Ok(v) => match self.operating_system { 253 OperatingSystem::Solaris => v == target_lexicon::Vendor::Sun, 254 _ => self.vendor == v, 255 }, 256 Err(_) => false, 257 }, 258 PointerWidth(pw) => { 259 // The gnux32 environment is a special case, where it has an 260 // x86_64 architecture, but a 32-bit pointer width 261 if self.environment != Environment::Gnux32 { 262 pw == match self.pointer_width() { 263 Ok(pw) => pw.bits(), 264 Err(_) => return false, 265 } 266 } else { 267 pw == 32 268 } 269 } 270 } 271 } 272 } 273 274 impl<'a> TargetPredicate<'a> { 275 /// Returns true of the predicate matches the specified target 276 /// 277 /// ``` 278 /// use cfg_expr::{targets::*, expr::TargetPredicate as tp}; 279 /// let win = get_builtin_target_by_triple("x86_64-pc-windows-msvc").unwrap(); 280 /// 281 /// assert!( 282 /// tp::Arch(Arch::x86_64).matches(win) && 283 /// tp::Endian(Endian::little).matches(win) && 284 /// tp::Env(Env::msvc).matches(win) && 285 /// tp::Family(Family::windows).matches(win) && 286 /// tp::Os(Os::windows).matches(win) && 287 /// tp::PointerWidth(64).matches(win) && 288 /// tp::Vendor(Vendor::pc).matches(win) 289 /// ); 290 /// ``` matches<T>(self, target: &T) -> bool where T: TargetMatcher,291 pub fn matches<T>(self, target: &T) -> bool 292 where 293 T: TargetMatcher, 294 { 295 target.matches(self) 296 } 297 } 298 299 #[derive(Clone, Debug)] 300 pub(crate) enum Which { 301 Arch, 302 Endian(targ::Endian), 303 Env, 304 Family(targ::Family), 305 Os, 306 PointerWidth(u8), 307 Vendor, 308 } 309 310 #[derive(Clone, Debug)] 311 pub(crate) struct InnerTarget { 312 which: Which, 313 span: Option<Range<usize>>, 314 } 315 316 /// A single predicate in a `cfg()` expression 317 #[derive(Debug, PartialEq)] 318 pub enum Predicate<'a> { 319 /// A target predicate, with the `target_` prefix 320 Target(TargetPredicate<'a>), 321 /// Whether rustc's test harness is [enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#test) 322 Test, 323 /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions) 324 /// when compiling without optimizations. 325 DebugAssertions, 326 /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#proc_macro) for 327 /// crates of the proc_macro type. 328 ProcMacro, 329 /// A [`feature = "<name>"`](https://doc.rust-lang.org/nightly/cargo/reference/features.html) 330 Feature(&'a str), 331 /// [target_feature](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature) 332 TargetFeature(&'a str), 333 /// A generic bare predicate key that doesn't match one of the known options, eg `cfg(bare)` 334 Flag(&'a str), 335 /// A generic key = "value" predicate that doesn't match one of the known options, eg `cfg(foo = "bar")` 336 KeyValue { key: &'a str, val: &'a str }, 337 } 338 339 #[derive(Clone, Debug)] 340 pub(crate) enum InnerPredicate { 341 Target(InnerTarget), 342 Test, 343 DebugAssertions, 344 ProcMacro, 345 Feature(Range<usize>), 346 TargetFeature(Range<usize>), 347 Other { 348 identifier: Range<usize>, 349 value: Option<Range<usize>>, 350 }, 351 } 352 353 impl InnerPredicate { to_pred<'a>(&self, s: &'a str) -> Predicate<'a>354 fn to_pred<'a>(&self, s: &'a str) -> Predicate<'a> { 355 use InnerPredicate as IP; 356 use Predicate::{ 357 DebugAssertions, Feature, Flag, KeyValue, ProcMacro, Target, TargetFeature, Test, 358 }; 359 360 match self { 361 IP::Target(it) => match &it.which { 362 Which::Arch => Target(TargetPredicate::Arch(targ::Arch( 363 &s[it.span.clone().unwrap()], 364 ))), 365 Which::Os => Target(TargetPredicate::Os(targ::Os(&s[it.span.clone().unwrap()]))), 366 Which::Vendor => Target(TargetPredicate::Vendor(targ::Vendor( 367 &s[it.span.clone().unwrap()], 368 ))), 369 Which::Env => Target(TargetPredicate::Env(targ::Env( 370 &s[it.span.clone().unwrap()], 371 ))), 372 Which::Endian(end) => Target(TargetPredicate::Endian(*end)), 373 Which::Family(fam) => Target(TargetPredicate::Family(*fam)), 374 Which::PointerWidth(pw) => Target(TargetPredicate::PointerWidth(*pw)), 375 }, 376 IP::Test => Test, 377 IP::DebugAssertions => DebugAssertions, 378 IP::ProcMacro => ProcMacro, 379 IP::Feature(rng) => Feature(&s[rng.clone()]), 380 IP::TargetFeature(rng) => TargetFeature(&s[rng.clone()]), 381 IP::Other { identifier, value } => match value { 382 Some(vs) => KeyValue { 383 key: &s[identifier.clone()], 384 val: &s[vs.clone()], 385 }, 386 None => Flag(&s[identifier.clone()]), 387 }, 388 } 389 } 390 } 391 392 #[derive(Clone, Debug)] 393 pub(crate) enum ExprNode { 394 Fn(Func), 395 Predicate(InnerPredicate), 396 } 397 398 /// A parsed `cfg()` expression that can evaluated 399 #[derive(Clone, Debug)] 400 pub struct Expression { 401 pub(crate) expr: SmallVec<[ExprNode; 5]>, 402 // We keep the original string around for providing the arbitrary 403 // strings that can make up an expression 404 pub(crate) original: String, 405 } 406 407 impl Expression { 408 /// An iterator over each predicate in the expression predicates(&self) -> impl Iterator<Item = Predicate<'_>>409 pub fn predicates(&self) -> impl Iterator<Item = Predicate<'_>> { 410 self.expr.iter().filter_map(move |item| match item { 411 ExprNode::Predicate(pred) => { 412 let pred = pred.clone().to_pred(&self.original); 413 Some(pred) 414 } 415 _ => None, 416 }) 417 } 418 419 /// Evaluates the expression, using the provided closure to determine the value of 420 /// each predicate, which are then combined into a final result depending on the 421 /// functions not(), all(), or any() in the expression. 422 /// 423 /// `eval_predicate` typically returns `bool`, but may return any type that implements 424 /// the `Logic` trait. 425 /// 426 /// ## Examples 427 /// 428 /// ``` 429 /// use cfg_expr::{targets::*, Expression, Predicate}; 430 /// 431 /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap(); 432 /// 433 /// let expr = Expression::parse(r#"all(not(windows), target_env = "musl", any(target_arch = "x86", target_arch = "x86_64"))"#).unwrap(); 434 /// 435 /// assert!(expr.eval(|pred| { 436 /// match pred { 437 /// Predicate::Target(tp) => tp.matches(linux_musl), 438 /// _ => false, 439 /// } 440 /// })); 441 /// ``` 442 /// 443 /// Returning `Option<bool>`, where `None` indicates the result is unknown: 444 /// 445 /// ``` 446 /// use cfg_expr::{targets::*, Expression, Predicate}; 447 /// 448 /// let expr = Expression::parse(r#"any(target_feature = "sse2", target_env = "musl")"#).unwrap(); 449 /// 450 /// let linux_gnu = get_builtin_target_by_triple("x86_64-unknown-linux-gnu").unwrap(); 451 /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap(); 452 /// 453 /// fn eval(expr: &Expression, target: &TargetInfo) -> Option<bool> { 454 /// expr.eval(|pred| { 455 /// match pred { 456 /// Predicate::Target(tp) => Some(tp.matches(target)), 457 /// Predicate::TargetFeature(_) => None, 458 /// _ => panic!("unexpected predicate"), 459 /// } 460 /// }) 461 /// } 462 /// 463 /// // Whether the target feature is present is unknown, so the whole expression evaluates to 464 /// // None (unknown). 465 /// assert_eq!(eval(&expr, linux_gnu), None); 466 /// 467 /// // Whether the target feature is present is irrelevant for musl, since the any() always 468 /// // evaluates to true. 469 /// assert_eq!(eval(&expr, linux_musl), Some(true)); 470 /// ``` eval<EP, T>(&self, mut eval_predicate: EP) -> T where EP: FnMut(&Predicate<'_>) -> T, T: Logic + std::fmt::Debug,471 pub fn eval<EP, T>(&self, mut eval_predicate: EP) -> T 472 where 473 EP: FnMut(&Predicate<'_>) -> T, 474 T: Logic + std::fmt::Debug, 475 { 476 let mut result_stack = SmallVec::<[T; 8]>::new(); 477 478 // We store the expression as postfix, so just evaluate each license 479 // requirement in the order it comes, and then combining the previous 480 // results according to each operator as it comes 481 for node in self.expr.iter() { 482 match node { 483 ExprNode::Predicate(pred) => { 484 let pred = pred.to_pred(&self.original); 485 486 result_stack.push(eval_predicate(&pred)); 487 } 488 ExprNode::Fn(Func::All(count)) => { 489 // all() with a comma separated list of configuration predicates. 490 let mut result = T::top(); 491 492 for _ in 0..*count { 493 let r = result_stack.pop().unwrap(); 494 result = result.and(r); 495 } 496 497 result_stack.push(result); 498 } 499 ExprNode::Fn(Func::Any(count)) => { 500 // any() with a comma separated list of configuration predicates. 501 let mut result = T::bottom(); 502 503 for _ in 0..*count { 504 let r = result_stack.pop().unwrap(); 505 result = result.or(r); 506 } 507 508 result_stack.push(result); 509 } 510 ExprNode::Fn(Func::Not) => { 511 // not() with a configuration predicate. 512 // It is true if its predicate is false 513 // and false if its predicate is true. 514 let r = result_stack.pop().unwrap(); 515 result_stack.push(r.not()); 516 } 517 } 518 } 519 520 result_stack.pop().unwrap() 521 } 522 523 /// The original string which has been parsed to produce this ['Expression`]. 524 /// 525 /// ``` 526 /// use cfg_expr::Expression; 527 /// 528 /// assert_eq!( 529 /// Expression::parse("any()").unwrap().original(), 530 /// "any()" 531 /// ); 532 /// ``` 533 #[inline] original(&self) -> &str534 pub fn original(&self) -> &str { 535 &self.original 536 } 537 } 538 539 /// [`PartialEq`] will do a **syntactical** comparaison, so will just check if both 540 /// expressions have been parsed from the same string, **not** if they are semantically 541 /// equivalent. 542 /// 543 /// ``` 544 /// use cfg_expr::Expression; 545 /// 546 /// assert_eq!( 547 /// Expression::parse("any()").unwrap(), 548 /// Expression::parse("any()").unwrap() 549 /// ); 550 /// assert_ne!( 551 /// Expression::parse("any()").unwrap(), 552 /// Expression::parse("unix").unwrap() 553 /// ); 554 /// ``` 555 impl PartialEq for Expression { eq(&self, other: &Self) -> bool556 fn eq(&self, other: &Self) -> bool { 557 self.original.eq(&other.original) 558 } 559 } 560 561 /// A propositional logic used to evaluate `Expression` instances. 562 /// 563 /// An `Expression` consists of some predicates and the `any`, `all` and `not` operators. An 564 /// implementation of `Logic` defines how the `any`, `all` and `not` operators should be evaluated. 565 pub trait Logic { 566 /// The result of an `all` operation with no operands, akin to Boolean `true`. top() -> Self567 fn top() -> Self; 568 569 /// The result of an `any` operation with no operands, akin to Boolean `false`. bottom() -> Self570 fn bottom() -> Self; 571 572 /// `AND`, which corresponds to the `all` operator. and(self, other: Self) -> Self573 fn and(self, other: Self) -> Self; 574 575 /// `OR`, which corresponds to the `any` operator. or(self, other: Self) -> Self576 fn or(self, other: Self) -> Self; 577 578 /// `NOT`, which corresponds to the `not` operator. not(self) -> Self579 fn not(self) -> Self; 580 } 581 582 /// A boolean logic. 583 impl Logic for bool { 584 #[inline] top() -> Self585 fn top() -> Self { 586 true 587 } 588 589 #[inline] bottom() -> Self590 fn bottom() -> Self { 591 false 592 } 593 594 #[inline] and(self, other: Self) -> Self595 fn and(self, other: Self) -> Self { 596 self && other 597 } 598 599 #[inline] or(self, other: Self) -> Self600 fn or(self, other: Self) -> Self { 601 self || other 602 } 603 604 #[inline] not(self) -> Self605 fn not(self) -> Self { 606 !self 607 } 608 } 609 610 /// A three-valued logic -- `None` stands for the value being unknown. 611 /// 612 /// The truth tables for this logic are described on 613 /// [Wikipedia](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics). 614 impl Logic for Option<bool> { 615 #[inline] top() -> Self616 fn top() -> Self { 617 Some(true) 618 } 619 620 #[inline] bottom() -> Self621 fn bottom() -> Self { 622 Some(false) 623 } 624 625 #[inline] and(self, other: Self) -> Self626 fn and(self, other: Self) -> Self { 627 match (self, other) { 628 // If either is false, the expression is false. 629 (Some(false), _) | (_, Some(false)) => Some(false), 630 // If both are true, the expression is true. 631 (Some(true), Some(true)) => Some(true), 632 // One or both are unknown -- the result is unknown. 633 _ => None, 634 } 635 } 636 637 #[inline] or(self, other: Self) -> Self638 fn or(self, other: Self) -> Self { 639 match (self, other) { 640 // If either is true, the expression is true. 641 (Some(true), _) | (_, Some(true)) => Some(true), 642 // If both are false, the expression is false. 643 (Some(false), Some(false)) => Some(false), 644 // One or both are unknown -- the result is unknown. 645 _ => None, 646 } 647 } 648 649 #[inline] not(self) -> Self650 fn not(self) -> Self { 651 self.map(|v| !v) 652 } 653 } 654