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