#
cargo/ops/cargo_compile/
compile_filter.rs

    1//! Filters and their rules to select which Cargo targets will be built.
    2
    3use crate::core::compiler::CompileMode;
    4
    5use crate::core::{Target, TargetKind};
    6use crate::util::restricted_names::is_glob_pattern;
    7
    8#[derive(Debug, PartialEq, Eq, Clone)]
    9/// Indicates whether or not the library target gets included.
    10pub enum LibRule {
    11    /// Include the library, fail if not present
    12    True,
    13    /// Include the library if present
    14    Default,
    15    /// Exclude the library
    16    False,
    17}
    18
    19#[derive(Debug, Clone)]
    20/// Indicates which Cargo targets will be selected to be built.
    21pub enum FilterRule {
    22    /// All included.
    23    All,
    24    /// Just a subset of Cargo targets based on names given.
    25    Just(Vec<String>),
    26}
    27
    28/// Filter to apply to the root package to select which Cargo targets will be built.
    29/// (examples, bins, benches, tests, ...)
    30///
    31/// The actual filter process happens inside [`generate_root_units`].
    32///
    33/// Not to be confused with [`Packages`], which opts in packages to be built.
    34///
    35/// [`generate_root_units`]: super::UnitGenerator::generate_root_units
    36/// [`Packages`]: crate::ops::Packages
    37#[derive(Debug, Clone)]
    38pub enum CompileFilter {
    39    /// The default set of Cargo targets.
    40    Default {
    41        /// Flag whether targets can be safely skipped when required-features are not satisfied.
    42        required_features_filterable: bool,
    43    },
    44    /// Only includes a subset of all Cargo targets.
    45    Only {
    46        /// Include all Cargo targets.
    47        all_targets: bool,
    48        lib: LibRule,
    49        bins: FilterRule,
    50        examples: FilterRule,
    51        tests: FilterRule,
    52        benches: FilterRule,
    53    },
    54}
    55
    56impl FilterRule {
    57    pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
    58        if all {
    59            FilterRule::All
    60        } else {
    61            FilterRule::Just(targets)
    62        }
    63    }
    64
    65    /// Creates a filter with no rule.
    66    ///
    67    /// In the current Cargo implementation, filter without a rule implies
    68    /// Cargo will follows the default behaviour to filter targets.
    69    pub fn none() -> FilterRule {
    70        FilterRule::Just(Vec::new())
    71    }
    72
    73    /// Checks if a target definition matches this filter rule.
    74    fn matches(&self, target: &Target) -> bool {
    75        match *self {
    76            FilterRule::All => true,
    77            FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
    78        }
    79    }
    80
    81    /// Check if a filter is specific.
    82    ///
    83    /// Only filters without rules are considered as not specific.
    84    fn is_specific(&self) -> bool {
    85        match *self {
    86            FilterRule::All => true,
    87            FilterRule::Just(ref targets) => !targets.is_empty(),
    88        }
    89    }
    90
    91    /// Checks if any specified target name contains glob patterns.
    92    pub(crate) fn contains_glob_patterns(&self) -> bool {
    93        match self {
    94            FilterRule::All => false,
    95            FilterRule::Just(targets) => targets.iter().any(is_glob_pattern),
    96        }
    97    }
    98}
    99
    100impl CompileFilter {
    101    /// Constructs a filter from raw command line arguments.
    102    pub fn from_raw_arguments(
    103        lib_only: bool,
    104        bins: Vec<String>,
    105        all_bins: bool,
    106        tsts: Vec<String>,
    107        all_tsts: bool,
    108        exms: Vec<String>,
    109        all_exms: bool,
    110        bens: Vec<String>,
    111        all_bens: bool,
    112        all_targets: bool,
    113    ) -> CompileFilter {
    114        if all_targets {
    115            return CompileFilter::new_all_targets();
    116        }
    117        let rule_lib = if lib_only {
    118            LibRule::True
    119        } else {
    120            LibRule::False
    121        };
    122        let rule_bins = FilterRule::new(bins, all_bins);
    123        let rule_tsts = FilterRule::new(tsts, all_tsts);
    124        let rule_exms = FilterRule::new(exms, all_exms);
    125        let rule_bens = FilterRule::new(bens, all_bens);
    126
    127        CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens)
    128    }
    129
    130    /// Constructs a filter from underlying primitives.
    131    pub fn new(
    132        rule_lib: LibRule,
    133        rule_bins: FilterRule,
    134        rule_tsts: FilterRule,
    135        rule_exms: FilterRule,
    136        rule_bens: FilterRule,
    137    ) -> CompileFilter {
    138        if rule_lib == LibRule::True
    139            || rule_bins.is_specific()
    140            || rule_tsts.is_specific()
    141            || rule_exms.is_specific()
    142            || rule_bens.is_specific()
    143        {
    144            CompileFilter::Only {
    145                all_targets: false,
    146                lib: rule_lib,
    147                bins: rule_bins,
    148                examples: rule_exms,
    149                benches: rule_bens,
    150                tests: rule_tsts,
    151            }
    152        } else {
    153            CompileFilter::Default {
    154                required_features_filterable: true,
    155            }
    156        }
    157    }
    158
    159    /// Constructs a filter that includes all targets.
    160    pub fn new_all_targets() -> CompileFilter {
    161        CompileFilter::Only {
    162            all_targets: true,
    163            lib: LibRule::Default,
    164            bins: FilterRule::All,
    165            examples: FilterRule::All,
    166            benches: FilterRule::All,
    167            tests: FilterRule::All,
    168        }
    169    }
    170
    171    /// Constructs a filter that includes all test targets.
    172    ///
    173    /// Being different from the behavior of [`CompileFilter::Default`], this
    174    /// function only recognizes test targets, which means cargo might compile
    175    /// all targets with `tested` flag on, whereas [`CompileFilter::Default`]
    176    /// may include additional example targets to ensure they can be compiled.
    177    ///
    178    /// Note that the actual behavior is subject to [`filter_default_targets`]
    179    /// and [`generate_root_units`] though.
    180    ///
    181    /// [`generate_root_units`]: super::UnitGenerator::generate_root_units
    182    /// [`filter_default_targets`]: super::UnitGenerator::filter_default_targets
    183    pub fn all_test_targets() -> Self {
    184        Self::Only {
    185            all_targets: false,
    186            lib: LibRule::Default,
    187            bins: FilterRule::none(),
    188            examples: FilterRule::none(),
    189            tests: FilterRule::All,
    190            benches: FilterRule::none(),
    191        }
    192    }
    193
    194    /// Constructs a filter that includes lib target only.
    195    pub fn lib_only() -> Self {
    196        Self::Only {
    197            all_targets: false,
    198            lib: LibRule::True,
    199            bins: FilterRule::none(),
    200            examples: FilterRule::none(),
    201            tests: FilterRule::none(),
    202            benches: FilterRule::none(),
    203        }
    204    }
    205
    206    /// Constructs a filter that includes the given binary. No more. No less.
    207    pub fn single_bin(bin: String) -> Self {
    208        Self::Only {
    209            all_targets: false,
    210            lib: LibRule::False,
    211            bins: FilterRule::new(vec![bin], false),
    212            examples: FilterRule::none(),
    213            tests: FilterRule::none(),
    214            benches: FilterRule::none(),
    215        }
    216    }
    217
    218    /// Indicates if Cargo needs to build any dev dependency.
    219    pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
    220        match mode {
    221            CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
    222            CompileMode::Check { test: true } => true,
    223            CompileMode::Build
    224            | CompileMode::Doc { .. }
    225            | CompileMode::Docscrape
    226            | CompileMode::Check { test: false } => match *self {
    227                CompileFilter::Default { .. } => false,
    228                CompileFilter::Only {
    229                    ref examples,
    230                    ref tests,
    231                    ref benches,
    232                    ..
    233                } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
    234            },
    235            CompileMode::RunCustomBuild => panic!("Invalid mode"),
    236        }
    237    }
    238
    239    /// Selects targets for "cargo run". for logic to select targets for other
    240    /// subcommands, see [`generate_root_units`] and [`filter_default_targets`].
    241    ///
    242    /// [`generate_root_units`]: super::UnitGenerator::generate_root_units
    243    /// [`filter_default_targets`]: super::UnitGenerator::filter_default_targets
    244    pub fn target_run(&self, target: &Target) -> bool {
    245        match *self {
    246            CompileFilter::Default { .. } => true,
    247            CompileFilter::Only {
    248                ref lib,
    249                ref bins,
    250                ref examples,
    251                ref tests,
    252                ref benches,
    253                ..
    254            } => {
    255                let rule = match *target.kind() {
    256                    TargetKind::Bin => bins,
    257                    TargetKind::Test => tests,
    258                    TargetKind::Bench => benches,
    259                    TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
    260                    TargetKind::Lib(..) => {
    261                        return match *lib {
    262                            LibRule::True => true,
    263                            LibRule::Default => true,
    264                            LibRule::False => false,
    265                        };
    266                    }
    267                    TargetKind::CustomBuild => return false,
    268                };
    269                rule.matches(target)
    270            }
    271        }
    272    }
    273
    274    pub fn is_specific(&self) -> bool {
    275        match *self {
    276            CompileFilter::Default { .. } => false,
    277            CompileFilter::Only { .. } => true,
    278        }
    279    }
    280
    281    pub fn is_all_targets(&self) -> bool {
    282        matches!(
    283            *self,
    284            CompileFilter::Only {
    285                all_targets: true,
    286                ..
    287            }
    288        )
    289    }
    290
    291    /// Checks if any specified target name contains glob patterns.
    292    pub(crate) fn contains_glob_patterns(&self) -> bool {
    293        match self {
    294            CompileFilter::Default { .. } => false,
    295            CompileFilter::Only {
    296                bins,
    297                examples,
    298                tests,
    299                benches,
    300                ..
    301            } => {
    302                bins.contains_glob_patterns()
    303                    || examples.contains_glob_patterns()
    304                    || tests.contains_glob_patterns()
    305                    || benches.contains_glob_patterns()
    306            }
    307        }
    308    }
    309}