// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Note that `target_triple.rs` module is auto-generated by `gnrt`'s `build.rs`.
use crate::target_triple::{RustTargetTriple, RUST_TRIPLE_PROPERTIES};
use anyhow::{anyhow, Result};
use std::{
collections::{HashMap, HashSet},
sync::LazyLock,
};
/// Representation of a `Condition` associated with a conditional/optional
/// dependency from a `Cargo.toml` file - see an example here:
/// https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/chromium_crates_io/vendor/windows-targets-v0_52/Cargo.toml;l=38;drc=5977f5edc277200b0674e8df80ba7495980d87bb
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Condition(Result<HashSet<RustTargetTriple>, String>);
impl Condition {
pub fn is_always_false(&self) -> bool {
self.0.as_ref().is_ok_and(|triple_set| triple_set.is_empty())
}
/// Creates a `Condition` that is never met - target platforms supported in
/// Chromium builds never meet this condition. For example
/// `#[cfg(target_arch = "powerpc")]` is effectively equivalent to
/// `Condition::always_false()`.
pub fn always_false() -> Self {
Condition(Ok(HashSet::new()))
}
pub fn is_always_true(&self) -> bool {
self.0.as_ref().is_ok_and(|triple_set| *triple_set == *RustTargetTriple::all())
}
/// Creates a `Condition` that is always true - *all* target platforms
/// supported in Chromium builds meet this condition. For example
/// `#[cfg(not(target_arch = "powerpc"))]` is effectively equivalent to
/// `Condition::always_true()`.
pub fn always_true() -> Self {
Condition(Ok(RustTargetTriple::all().clone()))
}
fn from_triple(triple: RustTargetTriple) -> Self {
Self::from_triple_set([triple].into())
}
fn from_triple_set(set: HashSet<RustTargetTriple>) -> Self {
Condition(Ok(set))
}
pub fn or(lhs: Condition, rhs: Condition) -> Self {
// First check if one of the operands (potentially an `Err` variant!)
// can be ignored (when the other operand `is_always_true`).
if lhs.is_always_true() {
return lhs;
}
if rhs.is_always_true() {
return rhs;
}
match (lhs, rhs) {
(err @ Condition(Err(_)), _) | (_, err @ Condition(Err(_))) => err.clone(),
(Condition(Ok(lhs)), Condition(Ok(rhs))) => Condition::from_triple_set(&lhs | &rhs),
}
}
pub fn and(lhs: Condition, rhs: Condition) -> Self {
// First check if one of the operands (potentially an `Err` variant!)
// can be ignored (when the other operand `is_always_false`).
if lhs.is_always_false() {
return lhs;
}
if rhs.is_always_false() {
return rhs;
}
match (lhs, rhs) {
(err @ Condition(Err(_)), _) | (_, err @ Condition(Err(_))) => err.clone(),
(Condition(Ok(lhs)), Condition(Ok(rhs))) => Condition::from_triple_set(&lhs & &rhs),
}
}
fn not(other: Condition) -> Self {
match other {
err @ Condition(Err(_)) => err,
Condition(Ok(triple_set)) => Condition(Ok(negate_triple_set(&triple_set))),
}
}
pub fn to_handlebars_value(&self) -> Result<Option<String>> {
assert!(
!self.is_always_false(),
"'always false' dependencies should be filtered out \
by `fn collect_dependencies` from `deps.rs`"
);
self.0.as_ref().map_err(|msg| anyhow!("{msg}")).map(|triple_set| {
if *triple_set == *RustTargetTriple::all() {
None
} else {
Some(format_as_gn_expr::format(triple_set))
}
})
}
pub fn from_target_spec(spec: &target_spec::TargetSpec) -> Self {
use target_spec::TargetSpec::*;
match spec {
PlainString(triple) => triple_to_condition(triple.as_str()),
Expression(expr) => {
let cfg_expr = expr.expression_str().parse().unwrap();
cfg_expr_to_condition(&cfg_expr)
}
}
}
}
fn negate_triple_set(set: &HashSet<RustTargetTriple>) -> HashSet<RustTargetTriple> {
RustTargetTriple::all() - set
}
/// Module for converting a set of target triples into a conditional expression
/// that uses GN/Chromium variables and syntax.
///
/// The main entrypoint is the `pub fn format` function below.
///
/// The conditional expression is built as a combination of:
///
/// * OS filters (e.g. `"is_win"`, see `fn get_gn_os_expr`)
/// * CPU filters (e.g. `"current_cpu == \"x64\""`, see `fn get_gn_arch_expr`)
/// * Triple filters (e.g. `"rust_abi_target == \"aarch64-apple-ios-sim\"`, see
/// `gn get_gn_target_triple_expr`). This is used as a last resort, because
/// this results in long and not very readable expressions.
mod format_as_gn_expr {
use super::negate_triple_set;
use crate::target_triple::{RustTargetArch, RustTargetOs, RustTargetTriple};
use itertools::Itertools;
use std::collections::HashSet;
/// Translates `target_triples` into a condition expressed in GN/Chromium
/// syntax (e.g. `is_win && current_cpu == "x64"`). Tries to use
/// heuristics to return a short, readable expression.
pub fn format(target_triples: &HashSet<RustTargetTriple>) -> String {
assert!(!target_triples.is_empty());
assert_ne!(target_triples, RustTargetTriple::all());
if let Some(expr) = get_single_filter(target_triples, get_gn_os_expr) {
return expr;
}
if let Some(expr) = get_single_filter(target_triples, get_gn_arch_expr) {
return expr;
}
let result1 = get_double_filter(target_triples, get_gn_os_expr, get_gn_arch_expr);
let result2 = get_double_filter(target_triples, get_gn_arch_expr, get_gn_os_expr);
if let Some((result1, result2)) = result1.zip(result2) {
if result1.len() <= result2.len() {
return result1;
} else {
return result2;
}
}
target_triples.iter().map(get_gn_target_triple_expr).sorted().join(" || ")
}
/// If `target_triples` can be calculated with a single-dimensional filter
/// (e.g. "all triples with OS=A or OS=B" or "all triples with Arch=x86
/// or Arch=x64") then returns an expression that represents such a
/// filter. `get_expr` should typically be `get_gn_os_expr` or
/// `get_gn_arch_expr`.
fn get_single_filter(
target_triples: &HashSet<RustTargetTriple>,
get_expr: fn(&RustTargetTriple) -> &'static str,
) -> Option<String> {
let get_expr_set = |triples: &HashSet<RustTargetTriple>| -> HashSet<&'static str> {
triples.iter().map(get_expr).collect()
};
fn negate(expr: &str) -> String {
if expr.contains("==") {
expr.replace("==", "!=")
} else {
format!("!{expr}")
}
}
let expr_in_target = get_expr_set(target_triples);
let expr_in_rest = get_expr_set(&negate_triple_set(target_triples));
if !expr_in_target.is_disjoint(&expr_in_rest)
|| expr_in_target.is_empty()
|| expr_in_rest.is_empty()
{
return None;
}
if expr_in_target.len() <= expr_in_rest.len() {
Some(expr_in_target.iter().sorted().join(" || "))
} else {
Some(expr_in_rest.iter().sorted().map(|&expr| negate(expr)).join(" && "))
}
}
/// If `target_triples` can be calculated with a two-dimensional filter
/// (e.g. "all triples with (OS=A && Arch=X) or (OS=B && Arch=Y)") then
/// returns an expression that represents such a filter. `get_expr1`
/// and or `get_expr2` should typically be `get_gn_os_expr` or
/// `get_gn_arch_expr`.
fn get_double_filter(
target_triples: &HashSet<RustTargetTriple>,
get_expr1: fn(&RustTargetTriple) -> &'static str,
get_expr2: fn(&RustTargetTriple) -> &'static str,
) -> Option<String> {
let mut or_operands = vec![];
let chunked = target_triples.iter().copied().sorted_by_key(get_expr1).chunk_by(get_expr1);
for (expr1, chunk) in chunked.into_iter() {
let target_triples_matching_expr1 = chunk.collect::<HashSet<_>>();
let all_triples_matching_expr1 = RustTargetTriple::all()
.iter()
.copied()
.filter(|triple| get_expr1(triple) == expr1)
.collect::<HashSet<_>>();
assert!(all_triples_matching_expr1.is_superset(&target_triples_matching_expr1));
if target_triples_matching_expr1 == all_triples_matching_expr1 {
or_operands.push(expr1.to_string());
} else {
let expr2_or_operands = target_triples_matching_expr1
.iter()
.map(get_expr2)
.sorted()
.dedup()
.collect_vec();
let all_triples_matching_expr1_and_expr2 = all_triples_matching_expr1
.iter()
.copied()
.filter(|triple| expr2_or_operands.contains(&get_expr2(triple)))
.collect::<HashSet<_>>();
if all_triples_matching_expr1_and_expr2 != target_triples_matching_expr1 {
return None;
}
let final_expr_for_chunk = match expr2_or_operands.as_slice() {
[] => unreachable!(),
[single_operand] => format!("{expr1} && {single_operand}"),
multiple_operands => {
format!("{expr1} && ({})", multiple_operands.iter().join(" || "),)
}
};
or_operands.push(final_expr_for_chunk);
}
}
let final_expr_for_target_triples = match or_operands.as_slice() {
[] => unreachable!(),
[single_operand] => single_operand.clone(),
multiple_operands => {
multiple_operands.iter().map(|operand| format!("({operand})")).join(" || ")
}
};
Some(final_expr_for_target_triples)
}
/// Translates `RustTargetTriple` into a GN conditional expression that will
/// match the GN notion of the OS of that triple.
fn get_gn_os_expr(rust_triple: &RustTargetTriple) -> &'static str {
// `RustTargetOs` and `into` come from `target_triples.rs` which is
// auto-generated by `gnrt`'s `build.rs`.
let rust_os = (*rust_triple).into();
match rust_os {
RustTargetOs::Android => "is_android",
RustTargetOs::Fuchsia => "is_fuchsia",
RustTargetOs::Linux => "(is_linux || is_chromeos)",
RustTargetOs::Macos => "is_mac",
RustTargetOs::Windows => "is_win",
RustTargetOs::Ios | RustTargetOs::Tvos => "is_ios",
}
}
/// Translates `RustTargetTriple` into a GN conditional expression that will
/// match the GN notion of the CPU architecture of that triple.
fn get_gn_arch_expr(rust_triple: &RustTargetTriple) -> &'static str {
// `RustTargetArch` and `into` come from `target_triples.rs` which is
// auto-generated by `gnrt`'s `build.rs`.
let rust_arch = (*rust_triple).into();
match rust_arch {
RustTargetArch::Aarch64 => "current_cpu == \"arm64\"",
RustTargetArch::Arm => "current_cpu == \"arm\"",
RustTargetArch::Riscv64 => "current_cpu == \"riscv64\"",
RustTargetArch::X86 => "current_cpu == \"x86\"",
RustTargetArch::X8664 => "current_cpu == \"x64\"",
RustTargetArch::Powerpc64 => "(current_cpu == \"ppc64le\")",
RustTargetArch::S390x => "(current_cpu == \"s390x\")",
RustTargetArch::Loongarch64 => "(current_cpu == \"loong64\")",
}
}
/// Translates `RustTargetTriple` into a GN conditional expression that will
/// match the value of `rust_abi_target` as set by
/// `//build/config/rust.gni`.
fn get_gn_target_triple_expr(rust_triple: &RustTargetTriple) -> String {
// `as_triple_name` comes from `target_triples.rs` which is auto-generated by
// `gnrt`'s `build.rs`.
let rust_triple_name = rust_triple.as_triple_name();
format!("rust_abi_target == \"{rust_triple_name}\"")
}
}
fn cfg_expr_to_condition(cfg_expr: &cargo_platform::CfgExpr) -> Condition {
match cfg_expr {
cargo_platform::CfgExpr::Not(expr) => Condition::not(cfg_expr_to_condition(expr)),
cargo_platform::CfgExpr::All(exprs) => {
// https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.predicate.all
// says that "It is true if "all of the given predicates are true, or if the
// list is empty."
exprs
.iter()
.map(cfg_expr_to_condition)
.fold(Condition::always_true(), |accumulated, condition| {
Condition::and(accumulated, condition)
})
}
cargo_platform::CfgExpr::Any(exprs) => {
// https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.predicate.any
// says that "It is true if at least one of the given predicates is true. If
// there are no predicates, it is false.".
exprs
.iter()
.map(cfg_expr_to_condition)
.fold(Condition::always_false(), |accumulated, condition| {
Condition::or(accumulated, condition)
})
}
cargo_platform::CfgExpr::Value(cfg) => cfg_to_condition(cfg),
}
}
fn cfg_to_condition(cfg: &cargo_platform::Cfg) -> Condition {
match cfg {
cargo_platform::Cfg::Name(name) => cfg_name_to_condition(name.as_str()),
cargo_platform::Cfg::KeyPair(key, value) => {
cfg_key_value_pair_to_condition(key.as_str(), value)
}
}
}
/// A mapping from 1) name of Rust condition key (e.g. `target_os`,
/// `target_env`, etc) and 2) value of Rust condition key (e.g. `msvc` or `gnu`
/// for `target_env`) into 3) a set of `RustTargetTriple`s where this condition
/// is true (e.g. `target_env = "msvc"` is true for all 3 of Chromium Windows
/// target triples).
static PROP_NAME_TO_PROP_VALUE_TO_TRIPLE_SET: LazyLock<
HashMap<&str, HashMap<&str, HashSet<RustTargetTriple>>>,
> = LazyLock::new(|| {
// `RUST_TRIPLE_PROPERTIES` comes from `target_triples.rs` which is
// auto-generated by `gnrt`'s `build.rs`.
let mut result: HashMap<_, HashMap<_, HashSet<_>>> = HashMap::new();
for (triple, prop_name, prop_value) in RUST_TRIPLE_PROPERTIES {
let triple = triple.parse().unwrap();
result.entry(prop_name).or_default().entry(prop_value).or_default().insert(triple);
}
result
});
fn cfg_key_value_pair_to_condition(key: &str, value: &str) -> Condition {
if key == "panic" {
return panic_cfg_to_condition(value);
}
if let Some(value_to_triple_set_map) = PROP_NAME_TO_PROP_VALUE_TO_TRIPLE_SET.get(key) {
match value_to_triple_set_map.get(value) {
None => return Condition::always_false(),
Some(set) => return Condition::from_triple_set(set.clone()),
}
}
// `KNOWN_UNSUPPORTED_KEYS` is based on
//
// 1. Reading https://doc.rust-lang.org/reference/conditional-compilation.html
// 2. Looking at `rustc --print=cfg --target <some target> | grep = | grep -v
// target_` (`target_...=...` keys are handled in `build/builds.rs`).
const KNOWN_UNSUPPORTED_KEYS: [&str; 3] = ["feature", "fmt_debug", "relocation_model"];
if KNOWN_UNSUPPORTED_KEYS.contains(&key) {
return Condition(Err(format!(
"Condition depends on an unsupported, compiler-provided key: `{key}`"
)));
}
// If `key` is not handled above (in particular not in the list of *known*
// `KNOWN_UNSUPPORTED_KEYS`) then we assume that it is not set *by* `rustc` nor
// `cargo`, but instead is passed via `--cfg key=value` *to* the compiler.
//
// And we also assume that Chromium will never ask GN/ninja to pass `--cfg
// 'unrecognized_key="something"'` to `rustc` (this is not done today by
// `//build/rust/*.gni` nor `BUILD.gn.hbs`).
//
// And therefore we treat this as `AlwaysFalse`. See also
// https://crbug.com/404598090#comment4.
//
// (We used to warn about such unrecognized `key`s, but it was too noisy in
// practice.)
Condition::always_false()
}
/// `name` should correspond to https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.option-name
fn cfg_name_to_condition(name: &str) -> Condition {
// See https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows
const FAMILY_NAMES: [&str; 2] = ["unix", "windows"];
if FAMILY_NAMES.contains(&name) {
return cfg_key_value_pair_to_condition("target_family", name);
}
// We don't support `windows_raw_dylib` in Chromium. See also
// https://github.com/rust-lang/rust/issues/58713
if ["windows_raw_dylib"].contains(&name) {
return Condition::always_false();
}
// See https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions
if name == "debug_assertions" {
// Returning "always true" is not 100% correct and may bring in unnecessary
// dependencies. But this conservative behavior shouldn't cause any
// major issues.
//
// TODO(https://crbug.com/402096443): Handle this by tracking not only a set of
// `RustTargetTriple` but also a parallel set/bitflag of `RustDebugConfig` (with
// just two bits - on and off).
return Condition::always_true();
}
// See https://doc.rust-lang.org/reference/conditional-compilation.html#test
if name == "test" {
// Returning "always true" is not 100% correct and may bring in unnecessary
// dependencies. But this seems unlikely, given that test-only
// dependencies should be listed in the `[dev-dependencies]` section of
// `Cargo.toml` and reported as `guppy::DependencyKind::Development`.
// At any rate, this conservative behavior shouldn't cause any major
// issues.
//
// TODO(https://crbug.com/402096443): Handle this better.
return Condition::always_true();
}
// `KNOWN_UNSUPPORTED_NAMES` is based on
//
// 1. Reading https://doc.rust-lang.org/reference/conditional-compilation.html
// 2. Looking at `rustc --print=cfg --target <some target> | grep -v =`
// (`target_...=...` keys are handled in `build/builds.rs`).
//
// TODO(https://crbug.com/402096443): Extract those in `build/build/rs`.
// (It seems that all of them are properties of the target platform.)
const KNOWN_UNSUPPORTED_NAMES: [&str; 9] = [
"overflow_checks",
"ub_checks",
"target_has_atomic",
"target_has_atomic_load_store",
"target_has_reliable_f128",
"target_has_reliable_f128_math",
"target_has_reliable_f16",
"target_has_reliable_f16_math",
"target_thread_local",
];
if KNOWN_UNSUPPORTED_NAMES.contains(&name) {
return Condition(Err(format!(
"Condition depends on an unsupported, compiler-provided configuration name: `{name}`"
)));
}
// If `name` is not handled above (in particular not in the list of *known*
// `KNOWN_UNSUPPORTED_NAMES`) then we assume that it is not set *by* `rustc`,
// but instead is passed via `--cfg name` *to* the compiler.
//
// And we also assume that Chromium will never ask GN/ninja to pass `--cfg
// 'unrecognized_name'` to `rustc` (this is not done today by
// `//build/rust/*.gni` nor `BUILD.gn.hbs`).
//
// And therefore we treat this as `AlwaysFalse`. See also
// https://crbug.com/404598090#comment4.
//
// We used to warn about such unrecognized `name`s, but it was too noisy in
// practice. Example of such a `name` can be found here:
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml;l=258;drc=d41c4d24cd81aef76cda21e4ea84ab2ddb2c71e6
Condition::always_false()
}
/// `value` should correspond to https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.panic.values
fn panic_cfg_to_condition(value: &str) -> Condition {
// `//build/config/compiler/BUILD.gn` always hardcodes `-Cpanic=abort` into
// `rustflags`.
match value {
"abort" => Condition::always_true(),
"unwind" => Condition::always_false(),
_ => Condition(Err(format!(
"Unrecognized panic configuration: `#[cfg(panic = \"{value}\")]`"
))),
}
}
fn triple_to_condition(triple: &str) -> Condition {
triple.parse().map(Condition::from_triple).unwrap_or_else(
// Triples outside of `//build/rust/known-target-triples.txt` won't parse.
// Such target triples are never used in Chromium builds and therefore we
// represent tham as "always false".
|_err| Condition::always_false(),
)
}
#[cfg(test)]
mod tests {
use super::Condition;
use crate::target_triple::RustTargetTriple;
fn condition_from_test_triple(triple: &str) -> Condition {
let spec = target_spec::TargetSpec::PlainString(
target_spec::TargetSpecPlainString::new(triple.to_string()).unwrap(),
);
Condition::from_target_spec(&spec)
}
fn condition_from_test_expr(expr: &str) -> Condition {
let spec = target_spec::TargetSpec::Expression(
target_spec::TargetSpecExpression::new(expr).unwrap(),
);
Condition::from_target_spec(&spec)
}
fn gn_condition_from_test_expr(expr: &str) -> String {
let condition = condition_from_test_expr(expr);
match condition.to_handlebars_value() {
Ok(Some(s)) => s,
Ok(None) => panic!("Got 'always true' / `None` when formatting `{expr}`"),
Err(err) => panic!("Error when formatting `{expr}`: `{err}`"),
}
}
#[test]
fn test_target_spec_to_condition() {
// Try a target triple.
assert_eq!(
condition_from_test_triple("x86_64-pc-windows-msvc").to_handlebars_value().unwrap(),
Some("is_win && current_cpu == \"x64\"".to_string()),
);
// Try a cfg expression.
assert_eq!(
gn_condition_from_test_expr("any(windows, target_os = \"android\")"),
"is_android || is_win",
);
// Redundant cfg expression.
assert_eq!(gn_condition_from_test_expr("any(windows, windows)"), "is_win",);
// Testing for brevity (e.g. `!is_win` is shorted than `is_ios || is_linux ||
// ...`).
assert_eq!(gn_condition_from_test_expr("windows"), "is_win",);
assert_eq!(gn_condition_from_test_expr("unix"), "!is_win",);
assert_eq!(
gn_condition_from_test_expr("target_arch = \"x86_64\""),
"current_cpu == \"x64\"",
);
assert_eq!(
gn_condition_from_test_expr("not(target_arch = \"x86_64\")"),
"current_cpu != \"x64\"",
);
// Try sets of target triples that cannot be uniquely identified with GN
// OS+arch.
let triple1 = condition_from_test_triple("aarch64-apple-tvos");
let triple2 = condition_from_test_triple("aarch64-apple-tvos-sim");
assert_eq!(
triple1.to_handlebars_value().unwrap(),
Some("rust_abi_target == \"aarch64-apple-tvos\"".to_string()),
);
assert_eq!(
Condition::or(triple1, triple2).to_handlebars_value().unwrap(),
Some(
"\
rust_abi_target == \"aarch64-apple-tvos\" || \
rust_abi_target == \"aarch64-apple-tvos-sim\""
.to_string()
),
);
// Try a PlatformSet with multiple filters.
let filter1 = condition_from_test_expr(
"all(target_os = \"android\", \
target_arch = \"arm\")",
);
let filter2 = condition_from_test_expr("windows");
assert_eq!(
Condition::or(filter1, filter2).to_handlebars_value().unwrap(),
Some("(is_android && current_cpu == \"arm\") || (is_win)".to_string()),
);
// A cfg expression on arch only.
assert_eq!(
gn_condition_from_test_expr("target_arch = \"aarch64\""),
"current_cpu == \"arm64\"",
);
// A cfg expression on arch and OS (but not via the target triple string).
assert_eq!(
gn_condition_from_test_expr("all(target_arch = \"aarch64\", unix)"),
"current_cpu == \"arm64\" && (\
(is_linux || is_chromeos) || \
is_android || \
is_fuchsia || \
is_ios || \
is_mac)",
);
// A cfg expression taken from `windows-targets-v0_52/Cargo.toml`
// (condition for depending on `windows_x86_64_msvc`).
assert_eq!(
gn_condition_from_test_expr(
"all(any(target_arch = \"x86_64\", target_arch = \"arm64ec\"), \
target_env = \"msvc\", \
not(windows_raw_dylib))"
),
"is_win && current_cpu == \"x64\"",
);
// A cfg expression taken from `windows-targets-v0_52/Cargo.toml` (condition for
// depending on `windows_i686_gnu`). When Chromium targets Windows, it
// always uses `msvc` (rather than `gnu`) environment - this is why we'd
// like to expect `AlwaysFalse` result here. But to get `AlwaysFalse` we
// need to know that this condition is only evaluated when the parent/
// dependent crate is a Windows-only crate - this is covered by unit tests
// in `deps.rs`.
assert_eq!(
condition_from_test_expr(
"all(target_arch = \"x86\", \
target_env = \"gnu\", \
not(target_abi = \"llvm\"), \
not(windows_raw_dylib))"
),
Condition::from_triple(RustTargetTriple::I686UnknownLinuxGnu),
);
// Cfg expressions taken from `getrandom-0.3` => `libc` dependency.
assert_eq!(
gn_condition_from_test_expr(
"any( \
target_os = \"ios\", \
target_os = \"visionos\", \
target_os = \"watchos\", \
target_os = \"tvos\")",
),
"is_ios",
);
assert_eq!(
gn_condition_from_test_expr(
"any( \
target_os = \"macos\", \
target_os = \"openbsd\", \
target_os = \"vita\", \
target_os = \"emscripten\")",
),
"is_mac",
);
assert_eq!(
condition_from_test_expr(
// Simplification of one of the real expressions below.
"all(target_os = \"linux\", target_env = \"\")",
),
Condition::always_false(),
);
assert_eq!(
gn_condition_from_test_expr(
"all( \
any(target_os = \"linux\", target_os = \"android\"), \
not(any( \
all(target_os = \"linux\", target_env = \"\"), \
getrandom_backend = \"custom\", \
getrandom_backend = \"linux_raw\", \
getrandom_backend = \"rdrand\", \
getrandom_backend = \"rndr\")))",
),
"(is_linux || is_chromeos) || is_android",
);
}
/// Test that unsupported terms disappear when possible
/// (i.e. that we don't needlessly propagate an error).
#[test]
fn test_err_suppression() {
let err = Condition(Err("some err msg".to_string()));
assert_eq!(Condition::always_true(), Condition::or(Condition::always_true(), err.clone()),);
assert_eq!(Condition::always_true(), Condition::or(err.clone(), Condition::always_true()),);
assert_eq!(
Condition::always_false(),
Condition::and(Condition::always_false(), err.clone()),
);
assert_eq!(
Condition::always_false(),
Condition::and(err.clone(), Condition::always_false()),
);
}
}