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