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}