#![allow(unused_imports)]
#![allow(clippy::default_trait_access)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::semicolon_if_nothing_returned)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::uninlined_format_args)]
#![cfg_attr(
dylint_lib = "non_local_effect_before_error_return",
allow(non_local_effect_before_error_return)
)]
#[allow(clippy::too_many_arguments)]
impl<'a, 'b> Context<'a, 'b> {
pub fn new(
deps: &'a mut Vec<Dependency>,
source_id: SourceId,
nested_paths: &'a mut Vec<PathBuf>,
config: &'b Config,
warnings: &'a mut Vec<String>,
platform: Option<Platform>,
root: &'a Path,
features: &'a Features,
) -> Self {
Self {
deps,
source_id,
nested_paths,
config,
warnings,
platform,
root,
features,
}
}
}
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str::{self, FromStr};
use anyhow::{anyhow, bail, Context as _};
use cargo_platform::Platform;
use cargo_util::paths;
use log::{debug, trace};
use semver::{self, VersionReq};
use serde::de;
use serde::ser;
use serde::{Deserialize, Serialize};
use crate::core::compiler::{CompileKind, CompileTarget};
use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
use crate::core::manifest::{ManifestMetadata, TargetSourcePath, Warnings};
use crate::core::resolver::ResolveBehavior;
use crate::core::{
find_workspace_root, resolve_relative_path, Dependency, Manifest, PackageId, Summary, Target,
};
use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace};
use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
use crate::util::{
self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, VersionReqExt,
};
fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec<String>) {
let old_path = new_path.replace("-", "_");
warnings.push(format!(
"conflicting between `{new_path}` and `{old_path}` in the `{name}` {kind}.\n
`{old_path}` is ignored and not recommended for use in the future"
))
}
pub trait ResolveToPath {
fn resolve(&self, config: &Config) -> PathBuf;
}
impl ResolveToPath for String {
fn resolve(&self, _: &Config) -> PathBuf {
self.into()
}
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct DetailedTomlDependency<P: Clone = String> {
version: Option<String>,
registry: Option<String>,
registry_index: Option<String>,
path: Option<P>,
git: Option<String>,
branch: Option<String>,
tag: Option<String>,
rev: Option<String>,
features: Option<Vec<String>>,
optional: Option<bool>,
default_features: Option<bool>,
#[serde(rename = "default_features")]
default_features2: Option<bool>,
package: Option<String>,
public: Option<bool>,
artifact: Option<StringOrVec>,
lib: Option<bool>,
target: Option<String>,
}
impl<P: Clone> Default for DetailedTomlDependency<P> {
fn default() -> Self {
Self {
version: Default::default(),
registry: Default::default(),
registry_index: Default::default(),
path: Default::default(),
git: Default::default(),
branch: Default::default(),
tag: Default::default(),
rev: Default::default(),
features: Default::default(),
optional: Default::default(),
default_features: Default::default(),
default_features2: Default::default(),
package: Default::default(),
public: Default::default(),
artifact: Default::default(),
lib: Default::default(),
target: Default::default(),
}
}
}
#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct StringOrVec(Vec<String>);
impl<'de> de::Deserialize<'de> for StringOrVec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = StringOrVec;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("string or list of strings")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(StringOrVec(vec![s.to_string()]))
}
fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
let seq = de::value::SeqAccessDeserializer::new(v);
Vec::deserialize(seq).map(StringOrVec)
}
}
deserializer.deserialize_any(Visitor)
}
}
#[allow(dead_code)]
pub struct Context<'a, 'b> {
deps: &'a mut Vec<Dependency>,
source_id: SourceId,
nested_paths: &'a mut Vec<PathBuf>,
config: &'b Config,
warnings: &'a mut Vec<String>,
platform: Option<Platform>,
root: &'a Path,
features: &'a Features,
}
impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
pub fn to_dependency(
&self,
name_in_toml: &str,
cx: &mut Context<'_, '_>,
kind: Option<DepKind>,
) -> CargoResult<Dependency> {
if self.version.is_none() && self.path.is_none() && self.git.is_none() {
let msg = format!(
"dependency ({}) specified without \
providing a local path, Git repository, or \
version to use. This will be considered an \
error in future versions",
name_in_toml
);
cx.warnings.push(msg);
}
if let Some(version) = &self.version {
if version.contains('+') {
cx.warnings.push(format!(
"version requirement `{}` for dependency `{}` \
includes semver metadata which will be ignored, removing the \
metadata is recommended to avoid confusion",
version, name_in_toml
));
}
}
if self.git.is_none() {
let git_only_keys = [
(&self.branch, "branch"),
(&self.tag, "tag"),
(&self.rev, "rev"),
];
for &(key, key_name) in &git_only_keys {
if key.is_some() {
bail!(
"key `{}` is ignored for dependency ({}).",
key_name,
name_in_toml
);
}
}
}
if let Some(features) = &self.features {
for feature in features {
if feature.contains('/') {
bail!(
"feature `{}` in dependency `{}` is not allowed to contain slashes\n\
If you want to enable features of a transitive dependency, \
the direct dependency needs to re-export those features from \
the `[features]` table.",
feature,
name_in_toml
);
}
if feature.starts_with("dep:") {
bail!(
"feature `{}` in dependency `{}` is not allowed to use explicit \
`dep:` syntax\n\
If you want to enable an optional dependency, specify the name \
of the optional dependency without the `dep:` prefix, or specify \
a feature from the dependency's `[features]` table that enables \
the optional dependency.",
feature,
name_in_toml
);
}
}
}
let new_source_id = match (
self.git.as_ref(),
self.path.as_ref(),
self.registry.as_ref(),
self.registry_index.as_ref(),
) {
(Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!(
"dependency ({}) specification is ambiguous. \
Only one of `git` or `registry` is allowed.",
name_in_toml
),
(_, _, Some(_), Some(_)) => bail!(
"dependency ({}) specification is ambiguous. \
Only one of `registry` or `registry-index` is allowed.",
name_in_toml
),
(Some(git), maybe_path, _, _) => {
if maybe_path.is_some() {
bail!(
"dependency ({}) specification is ambiguous. \
Only one of `git` or `path` is allowed.",
name_in_toml
);
}
let n_details = [&self.branch, &self.tag, &self.rev]
.iter()
.filter(|d| d.is_some())
.count();
if n_details > 1 {
bail!(
"dependency ({}) specification is ambiguous. \
Only one of `branch`, `tag` or `rev` is allowed.",
name_in_toml
);
}
let reference = self
.branch
.clone()
.map(GitReference::Branch)
.or_else(|| self.tag.clone().map(GitReference::Tag))
.or_else(|| self.rev.clone().map(GitReference::Rev))
.unwrap_or(GitReference::DefaultBranch);
let loc = git.into_url()?;
if let Some(fragment) = loc.fragment() {
let msg = format!(
"URL fragment `#{}` in git URL is ignored for dependency ({}). \
If you were trying to specify a specific git revision, \
use `rev = \"{}\"` in the dependency declaration.",
fragment, name_in_toml, fragment
);
cx.warnings.push(msg)
}
SourceId::for_git(&loc, reference)?
}
(None, Some(path), _, _) => {
let path = path.resolve(cx.config);
cx.nested_paths.push(path.clone());
if cx.source_id.is_path() {
let path = cx.root.join(path);
let path = paths::normalize_path(&path);
SourceId::for_path(&path)?
} else {
cx.source_id
}
}
(None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?,
(None, None, None, Some(registry_index)) => {
let url = registry_index.into_url()?;
SourceId::for_registry(&url)?
}
(None, None, None, None) => SourceId::crates_io(cx.config)?,
};
let (pkg_name, explicit_name_in_toml) = match self.package {
Some(ref s) => (&s[..], Some(name_in_toml)),
None => (name_in_toml, None),
};
let version = self.version.as_deref();
let mut dep = Dependency::parse(pkg_name, version, new_source_id)?;
if self.default_features.is_some() && self.default_features2.is_some() {
warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings);
}
dep.set_features(self.features.iter().flatten())
.set_default_features(
self.default_features
.or(self.default_features2)
.unwrap_or(true),
)
.set_optional(self.optional.unwrap_or(false))
.set_platform(cx.platform.clone());
if let Some(registry) = &self.registry {
let registry_id = SourceId::alt_registry(cx.config, registry)?;
dep.set_registry_id(registry_id);
}
if let Some(registry_index) = &self.registry_index {
let url = registry_index.into_url()?;
let registry_id = SourceId::for_registry(&url)?;
dep.set_registry_id(registry_id);
}
if let Some(kind) = kind {
dep.set_kind(kind);
}
if let Some(name_in_toml) = explicit_name_in_toml {
dep.set_explicit_name_in_toml(name_in_toml);
}
if let Some(p) = self.public {
cx.features.require(Feature::public_dependency())?;
if dep.kind() != DepKind::Normal {
bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
}
dep.set_public(p);
}
#[cfg(any())]
if let (Some(artifact), is_lib, target) = (
self.artifact.as_ref(),
self.lib.unwrap_or(false),
self.target.as_deref(),
) {
if cx.config.cli_unstable().bindeps {
let artifact = Artifact::parse(artifact, is_lib, target)?;
if dep.kind() != DepKind::Build
&& artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget)
{
bail!(
r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#,
name_in_toml
);
}
dep.set_artifact(artifact)
} else {
bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml);
}
} else if self.lib.is_some() || self.target.is_some() {
for (is_set, specifier) in [
(self.lib.is_some(), "lib"),
(self.target.is_some(), "target"),
] {
if !is_set {
continue;
}
bail!(
"'{}' specifier cannot be used without an 'artifact = …' value ({})",
specifier,
name_in_toml
)
}
}
Ok(dep)
}
}