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