1 use crate::core
::{Dependency, PackageId, SourceId}
;
2 use crate::util
::interning
::InternedString
;
3 use crate::util
::{CargoResult, Config}
;
6 use std
::collections
::{BTreeMap, HashMap, HashSet}
;
8 use std
::hash
::{Hash, Hasher}
;
12 /// Subset of a `Manifest`. Contains only the most important information about
15 /// Summaries are cloned, and should not be mutated after creation
16 #[derive(Debug, Clone)]
21 #[derive(Debug, Clone)]
23 package_id
: PackageId
,
24 dependencies
: Vec
<Dependency
>,
25 features
: Rc
<FeatureMap
>,
26 checksum
: Option
<String
>,
27 links
: Option
<InternedString
>,
34 dependencies
: Vec
<Dependency
>,
35 features
: &BTreeMap
<InternedString
, Vec
<InternedString
>>,
36 links
: Option
<impl Into
<InternedString
>>,
37 ) -> CargoResult
<Summary
> {
38 // ****CAUTION**** If you change anything here that may raise a new
39 // error, be sure to coordinate that change with either the index
40 // schema field or the SummariesCache version.
41 for dep
in dependencies
.iter() {
42 let dep_name
= dep
.name_in_toml();
43 if dep
.is_optional() && !dep
.is_transitive() {
45 "dev-dependencies are not allowed to be optional: `{}`",
50 let feature_map
= build_feature_map(config
, pkg_id
, features
, &dependencies
)?
;
52 inner
: Rc
::new(Inner
{
55 features
: Rc
::new(feature_map
),
57 links
: links
.map(|l
| l
.into()),
62 pub fn package_id(&self) -> PackageId
{
65 pub fn name(&self) -> InternedString
{
66 self.package_id().name()
68 pub fn version(&self) -> &Version
{
69 self.package_id().version()
71 pub fn source_id(&self) -> SourceId
{
72 self.package_id().source_id()
74 pub fn dependencies(&self) -> &[Dependency
] {
75 &self.inner
.dependencies
77 pub fn features(&self) -> &FeatureMap
{
81 pub fn checksum(&self) -> Option
<&str> {
82 self.inner
.checksum
.as_deref()
84 pub fn links(&self) -> Option
<InternedString
> {
88 pub fn override_id(mut self, id
: PackageId
) -> Summary
{
89 Rc
::make_mut(&mut self.inner
).package_id
= id
;
93 pub fn set_checksum(&mut self, cksum
: String
) {
94 Rc
::make_mut(&mut self.inner
).checksum
= Some(cksum
);
97 pub fn map_dependencies
<F
>(mut self, f
: F
) -> Summary
99 F
: FnMut(Dependency
) -> Dependency
,
102 let slot
= &mut Rc
::make_mut(&mut self.inner
).dependencies
;
103 *slot
= mem
::take(slot
).into_iter().map(f
).collect();
108 pub fn map_source(self, to_replace
: SourceId
, replace_with
: SourceId
) -> Summary
{
109 let me
= if self.package_id().source_id() == to_replace
{
110 let new_id
= self.package_id().with_source_id(replace_with
);
111 self.override_id(new_id
)
115 me
.map_dependencies(|dep
| dep
.map_source(to_replace
, replace_with
))
119 impl PartialEq
for Summary
{
120 fn eq(&self, other
: &Summary
) -> bool
{
121 self.inner
.package_id
== other
.inner
.package_id
125 impl Eq
for Summary {}
127 impl Hash
for Summary
{
128 fn hash
<H
: Hasher
>(&self, state
: &mut H
) {
129 self.inner
.package_id
.hash(state
);
133 /// Checks features for errors, bailing out a CargoResult:Err if invalid,
134 /// and creates FeatureValues for each feature.
135 fn build_feature_map(
138 features
: &BTreeMap
<InternedString
, Vec
<InternedString
>>,
139 dependencies
: &[Dependency
],
140 ) -> CargoResult
<FeatureMap
> {
141 use self::FeatureValue
::*;
142 let mut dep_map
= HashMap
::new();
143 for dep
in dependencies
.iter() {
145 .entry(dep
.name_in_toml())
146 .or_insert_with(Vec
::new
)
150 let mut map
: FeatureMap
= features
152 .map(|(feature
, list
)| {
153 let fvs
: Vec
<_
> = list
155 .map(|feat_value
| FeatureValue
::new(*feat_value
))
161 // Add implicit features for optional dependencies if they weren't
162 // explicitly listed anywhere.
163 let explicitly_listed
: HashSet
<_
> = map
166 .filter_map(|fv
| match fv
{
167 Dep { dep_name }
=> Some(*dep_name
),
171 for dep
in dependencies
{
172 if !dep
.is_optional() {
175 let dep_name_in_toml
= dep
.name_in_toml();
176 if features
.contains_key(&dep_name_in_toml
) || explicitly_listed
.contains(&dep_name_in_toml
)
181 dep_name
: dep_name_in_toml
,
183 map
.insert(dep_name_in_toml
, vec
![fv
]);
186 // Validate features are listed properly.
187 for (feature
, fvs
) in &map
{
188 if feature
.starts_with("dep:") {
190 "feature named `{}` is not allowed to start with `dep:`",
194 if feature
.contains('
/'
) {
196 "feature named `{}` is not allowed to contain slashes",
200 validate_feature_name(config
, pkg_id
, feature
)?
;
202 // Find data for the referenced dependency...
205 Feature(dep_name
) | Dep { dep_name, .. }
| DepFeature { dep_name, .. }
=> {
206 dep_map
.get(dep_name
)
210 let is_optional_dep
= dep_data
212 .flat_map(|d
| d
.iter())
213 .any(|d
| d
.is_optional());
214 let is_any_dep
= dep_data
.is_some();
217 if !features
.contains_key(f
) {
220 "feature `{}` includes `{}` which is neither a dependency \
221 nor another feature",
227 if !map
.contains_key(f
) {
229 "feature `{}` includes `{}`, but `{}` is an \
230 optional dependency without an implicit feature\n\
231 Use `dep:{}` to enable the dependency.",
239 bail
!("feature `{}` includes `{}`, but `{}` is not an optional dependency\n\
240 A non-optional dependency of the same name is defined; \
241 consider adding `optional = true` to its definition.",
246 Dep { dep_name }
=> {
249 "feature `{}` includes `{}`, but `{}` is not listed as a dependency",
255 if !is_optional_dep
{
257 "feature `{}` includes `{}`, but `{}` is not an optional dependency\n\
258 A non-optional dependency of the same name is defined; \
259 consider adding `optional = true` to its definition.",
272 // Early check for some unlikely syntax.
273 if dep_feature
.contains('
/'
) {
275 "multiple slashes in feature `{}` (included by feature `{}`) are not allowed",
280 // Validation of the feature name will be performed in the resolver.
283 "feature `{}` includes `{}`, but `{}` is not a dependency",
289 if *weak
&& !is_optional_dep
{
290 bail
!("feature `{}` includes `{}` with a `?`, but `{}` is not an optional dependency\n\
291 A non-optional dependency of the same name is defined; \
292 consider removing the `?` or changing the dependency to be optional",
293 feature
, fv
, dep_name
);
300 // Make sure every optional dep is mentioned at least once.
301 let used
: HashSet
<_
> = map
304 .filter_map(|fv
| match fv
{
305 Dep { dep_name }
| DepFeature { dep_name, .. }
=> Some(dep_name
),
309 if let Some(dep
) = dependencies
311 .find(|dep
| dep
.is_optional() && !used
.contains(&dep
.name_in_toml()))
314 "optional dependency `{}` is not included in any feature\n\
315 Make sure that `dep:{}` is included in one of features in the [features] table.",
324 /// FeatureValue represents the types of dependencies a feature can have.
325 #[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
326 pub enum FeatureValue
{
327 /// A feature enabling another feature.
328 Feature(InternedString
),
329 /// A feature enabling a dependency with `dep:dep_name` syntax.
330 Dep { dep_name: InternedString }
,
331 /// A feature enabling a feature on a dependency with `crate_name/feat_name` syntax.
333 dep_name
: InternedString
,
334 dep_feature
: InternedString
,
335 /// If `true`, indicates the `?` syntax is used, which means this will
336 /// not automatically enable the dependency unless the dependency is
337 /// activated through some other means.
343 pub fn new(feature
: InternedString
) -> FeatureValue
{
344 match feature
.find('
/'
) {
346 let (dep
, dep_feat
) = feature
.split_at(pos
);
347 let dep_feat
= &dep_feat
[1..];
348 let (dep
, weak
) = if let Some(dep
) = dep
.strip_suffix('?'
) {
353 FeatureValue
::DepFeature
{
354 dep_name
: InternedString
::new(dep
),
355 dep_feature
: InternedString
::new(dep_feat
),
360 if let Some(dep_name
) = feature
.strip_prefix("dep:") {
362 dep_name
: InternedString
::new(dep_name
),
365 FeatureValue
::Feature(feature
)
371 /// Returns `true` if this feature explicitly used `dep:` syntax.
372 pub fn has_dep_prefix(&self) -> bool
{
373 matches
!(self, FeatureValue
::Dep { .. }
)
377 impl fmt
::Display
for FeatureValue
{
378 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
379 use self::FeatureValue
::*;
381 Feature(feat
) => write
!(f
, "{}", feat
),
382 Dep { dep_name }
=> write
!(f
, "dep:{}", dep_name
),
388 let weak
= if *weak { "?" }
else { "" }
;
389 write
!(f
, "{}{}/{}", dep_name
, weak
, dep_feature
)
395 pub type FeatureMap
= BTreeMap
<InternedString
, Vec
<FeatureValue
>>;
397 fn validate_feature_name(config
: &Config
, pkg_id
: PackageId
, name
: &str) -> CargoResult
<()> {
398 let mut chars
= name
.chars();
399 const FUTURE
: &str = "This was previously accepted but is being phased out; \
400 it will become a hard error in a future release.\n\
401 For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, \
402 and please leave a comment if this will be a problem for your project.";
403 if let Some(ch
) = chars
.next() {
404 if !(unicode_xid
::UnicodeXID
::is_xid_start(ch
) || ch
== '_'
|| ch
.is_digit(10)) {
405 config
.shell().warn(&format
!(
406 "invalid character `{}` in feature `{}` in package {}, \
407 the first character must be a Unicode XID start character or digit \
408 (most letters or `_` or `0` to `9`)\n\
410 ch
, name
, pkg_id
, FUTURE
415 if !(unicode_xid
::UnicodeXID
::is_xid_continue(ch
) || ch
== '
-'
|| ch
== '
+'
|| ch
== '
.'
) {
416 config
.shell().warn(&format
!(
417 "invalid character `{}` in feature `{}` in package {}, \
418 characters must be Unicode XID characters, `+`, or `.` \
419 (numbers, `+`, `-`, `_`, `.`, or most letters)\n\
421 ch
, name
, pkg_id
, FUTURE