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}