Skip to content

Part 6 of RFC2906 - Switch the inheritance source from workspace to… #10564

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 13, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/cargo/core/mod.rs
Original file line number Diff line number Diff line change
@@ -11,9 +11,10 @@ pub use self::shell::{Shell, Verbosity};
pub use self::source::{GitReference, Source, SourceId, SourceMap};
pub use self::summary::{FeatureMap, FeatureValue, Summary};
pub use self::workspace::{
find_workspace_root, resolve_relative_path, InheritableFields, MaybePackage, Workspace,
WorkspaceConfig, WorkspaceRootConfig,
find_workspace_root, resolve_relative_path, MaybePackage, Workspace, WorkspaceConfig,
WorkspaceRootConfig,
};
pub use crate::util::toml::InheritableFields;

pub mod compiler;
pub mod dependency;
211 changes: 1 addition & 210 deletions src/cargo/core/workspace.rs
Original file line number Diff line number Diff line change
@@ -23,9 +23,7 @@ use crate::sources::{PathSource, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::toml::{
read_manifest, readme_for_project, StringOrBool, TomlDependency, TomlProfiles, VecStringOrBool,
};
use crate::util::toml::{read_manifest, InheritableFields, TomlDependency, TomlProfiles};
use crate::util::{config::ConfigRelativePath, Config, Filesystem, IntoUrl};
use cargo_util::paths;
use cargo_util::paths::normalize_path;
@@ -1644,213 +1642,6 @@ impl WorkspaceRootConfig {
}
}

/// A group of fields that are inheritable by members of the workspace
#[derive(Clone, Debug, Default)]
pub struct InheritableFields {
dependencies: Option<BTreeMap<String, TomlDependency>>,
version: Option<semver::Version>,
authors: Option<Vec<String>>,
description: Option<String>,
homepage: Option<String>,
documentation: Option<String>,
readme: Option<StringOrBool>,
keywords: Option<Vec<String>>,
categories: Option<Vec<String>>,
license: Option<String>,
license_file: Option<String>,
repository: Option<String>,
publish: Option<VecStringOrBool>,
edition: Option<String>,
badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
rust_version: Option<String>,
ws_root: PathBuf,
}

impl InheritableFields {
pub fn new(
dependencies: Option<BTreeMap<String, TomlDependency>>,
version: Option<semver::Version>,
authors: Option<Vec<String>>,
description: Option<String>,
homepage: Option<String>,
documentation: Option<String>,
readme: Option<StringOrBool>,
keywords: Option<Vec<String>>,
categories: Option<Vec<String>>,
license: Option<String>,
license_file: Option<String>,
repository: Option<String>,
publish: Option<VecStringOrBool>,
edition: Option<String>,
badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
rust_version: Option<String>,
ws_root: PathBuf,
) -> InheritableFields {
Self {
dependencies,
version,
authors,
description,
homepage,
documentation,
readme,
keywords,
categories,
license,
license_file,
repository,
publish,
edition,
badges,
rust_version,
ws_root,
}
}

pub fn dependencies(&self) -> CargoResult<BTreeMap<String, TomlDependency>> {
self.dependencies.clone().map_or(
Err(anyhow!("`workspace.dependencies` was not defined")),
|d| Ok(d),
)
}

pub fn get_dependency(&self, name: &str) -> CargoResult<TomlDependency> {
self.dependencies.clone().map_or(
Err(anyhow!("`workspace.dependencies` was not defined")),
|deps| {
deps.get(name).map_or(
Err(anyhow!(
"`dependency.{}` was not found in `workspace.dependencies`",
name
)),
|dep| Ok(dep.clone()),
)
},
)
}

pub fn version(&self) -> CargoResult<semver::Version> {
self.version
.clone()
.map_or(Err(anyhow!("`workspace.version` was not defined")), |d| {
Ok(d)
})
}

pub fn authors(&self) -> CargoResult<Vec<String>> {
self.authors
.clone()
.map_or(Err(anyhow!("`workspace.authors` was not defined")), |d| {
Ok(d)
})
}

pub fn description(&self) -> CargoResult<String> {
self.description.clone().map_or(
Err(anyhow!("`workspace.description` was not defined")),
|d| Ok(d),
)
}

pub fn homepage(&self) -> CargoResult<String> {
self.homepage
.clone()
.map_or(Err(anyhow!("`workspace.homepage` was not defined")), |d| {
Ok(d)
})
}

pub fn documentation(&self) -> CargoResult<String> {
self.documentation.clone().map_or(
Err(anyhow!("`workspace.documentation` was not defined")),
|d| Ok(d),
)
}

pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> {
readme_for_project(self.ws_root.as_path(), self.readme.clone()).map_or(
Err(anyhow!("`workspace.readme` was not defined")),
|readme| {
let rel_path =
resolve_relative_path("readme", &self.ws_root, package_root, &readme)?;
Ok(StringOrBool::String(rel_path))
},
)
}

pub fn keywords(&self) -> CargoResult<Vec<String>> {
self.keywords
.clone()
.map_or(Err(anyhow!("`workspace.keywords` was not defined")), |d| {
Ok(d)
})
}

pub fn categories(&self) -> CargoResult<Vec<String>> {
self.categories.clone().map_or(
Err(anyhow!("`workspace.categories` was not defined")),
|d| Ok(d),
)
}

pub fn license(&self) -> CargoResult<String> {
self.license
.clone()
.map_or(Err(anyhow!("`workspace.license` was not defined")), |d| {
Ok(d)
})
}

pub fn license_file(&self, package_root: &Path) -> CargoResult<String> {
self.license_file.clone().map_or(
Err(anyhow!("`workspace.license_file` was not defined")),
|d| resolve_relative_path("license-file", &self.ws_root, package_root, &d),
)
}

pub fn repository(&self) -> CargoResult<String> {
self.repository.clone().map_or(
Err(anyhow!("`workspace.repository` was not defined")),
|d| Ok(d),
)
}

pub fn publish(&self) -> CargoResult<VecStringOrBool> {
self.publish
.clone()
.map_or(Err(anyhow!("`workspace.publish` was not defined")), |d| {
Ok(d)
})
}

pub fn edition(&self) -> CargoResult<String> {
self.edition
.clone()
.map_or(Err(anyhow!("`workspace.edition` was not defined")), |d| {
Ok(d)
})
}

pub fn rust_version(&self) -> CargoResult<String> {
self.rust_version.clone().map_or(
Err(anyhow!("`workspace.rust-version` was not defined")),
|d| Ok(d),
)
}

pub fn badges(&self) -> CargoResult<BTreeMap<String, BTreeMap<String, String>>> {
self.badges
.clone()
.map_or(Err(anyhow!("`workspace.badges` was not defined")), |d| {
Ok(d)
})
}

pub fn ws_root(&self) -> &PathBuf {
&self.ws_root
}
}

pub fn resolve_relative_path(
label: &str,
old_root: &Path,
226 changes: 175 additions & 51 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
@@ -23,9 +23,7 @@ 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, InheritableFields, VirtualManifest, Workspace,
};
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};
@@ -1013,7 +1011,7 @@ impl<T> MaybeWorkspace<T> {
MaybeWorkspace::Workspace(TomlWorkspaceField { workspace: true }) => {
cargo_features.require(Feature::workspace_inheritance())?;
get_ws_field().context(format!(
"error inheriting `{}` from workspace root manifest's `workspace.{}`",
"error inheriting `{}` from workspace root manifest's `workspace.package.{}`",
label, label
))
}
@@ -1095,28 +1093,187 @@ pub struct TomlWorkspace {
resolver: Option<String>,

// Properties that can be inherited by members.
package: Option<InheritableFields>,
dependencies: Option<BTreeMap<String, TomlDependency>>,

// Note that this field must come last due to the way toml serialization
// works which requires tables to be emitted after all values.
metadata: Option<toml::Value>,
}

/// A group of fields that are inheritable by members of the workspace
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct InheritableFields {
// We use skip here since it will never be present when deserializing
// and we don't want it present when serializing
#[serde(skip)]
dependencies: Option<BTreeMap<String, TomlDependency>>,
version: Option<semver::Version>,
authors: Option<Vec<String>>,
description: Option<String>,
homepage: Option<String>,
documentation: Option<String>,
readme: Option<StringOrBool>,
homepage: Option<String>,
repository: Option<String>,
keywords: Option<Vec<String>>,
categories: Option<Vec<String>>,
license: Option<String>,
#[serde(rename = "license-file")]
license_file: Option<String>,
keywords: Option<Vec<String>>,
categories: Option<Vec<String>>,
repository: Option<String>,
publish: Option<VecStringOrBool>,
edition: Option<String>,
badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
#[serde(rename = "rust-version")]
rust_version: Option<String>,
// We use skip here since it will never be present when deserializing
// and we don't want it present when serializing
#[serde(skip)]
ws_root: PathBuf,
}

// Note that this field must come last due to the way toml serialization
// works which requires tables to be emitted after all values.
metadata: Option<toml::Value>,
impl InheritableFields {
pub fn update_deps(&mut self, deps: Option<BTreeMap<String, TomlDependency>>) {
self.dependencies = deps;
}

pub fn update_ws_path(&mut self, ws_root: PathBuf) {
self.ws_root = ws_root;
}

pub fn dependencies(&self) -> CargoResult<BTreeMap<String, TomlDependency>> {
self.dependencies.clone().map_or(
Err(anyhow!("`workspace.dependencies` was not defined")),
|d| Ok(d),
)
}

pub fn get_dependency(&self, name: &str) -> CargoResult<TomlDependency> {
self.dependencies.clone().map_or(
Err(anyhow!("`workspace.dependencies` was not defined")),
|deps| {
deps.get(name).map_or(
Err(anyhow!(
"`dependency.{}` was not found in `workspace.dependencies`",
name
)),
|dep| Ok(dep.clone()),
)
},
)
}

pub fn version(&self) -> CargoResult<semver::Version> {
self.version.clone().map_or(
Err(anyhow!("`workspace.package.version` was not defined")),
|d| Ok(d),
)
}

pub fn authors(&self) -> CargoResult<Vec<String>> {
self.authors.clone().map_or(
Err(anyhow!("`workspace.package.authors` was not defined")),
|d| Ok(d),
)
}

pub fn description(&self) -> CargoResult<String> {
self.description.clone().map_or(
Err(anyhow!("`workspace.package.description` was not defined")),
|d| Ok(d),
)
}

pub fn homepage(&self) -> CargoResult<String> {
self.homepage.clone().map_or(
Err(anyhow!("`workspace.package.homepage` was not defined")),
|d| Ok(d),
)
}

pub fn documentation(&self) -> CargoResult<String> {
self.documentation.clone().map_or(
Err(anyhow!("`workspace.package.documentation` was not defined")),
|d| Ok(d),
)
}

pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> {
readme_for_project(self.ws_root.as_path(), self.readme.clone()).map_or(
Err(anyhow!("`workspace.package.readme` was not defined")),
|readme| {
let rel_path =
resolve_relative_path("readme", &self.ws_root, package_root, &readme)?;
Ok(StringOrBool::String(rel_path))
},
)
}

pub fn keywords(&self) -> CargoResult<Vec<String>> {
self.keywords.clone().map_or(
Err(anyhow!("`workspace.package.keywords` was not defined")),
|d| Ok(d),
)
}

pub fn categories(&self) -> CargoResult<Vec<String>> {
self.categories.clone().map_or(
Err(anyhow!("`workspace.package.categories` was not defined")),
|d| Ok(d),
)
}

pub fn license(&self) -> CargoResult<String> {
self.license.clone().map_or(
Err(anyhow!("`workspace.package.license` was not defined")),
|d| Ok(d),
)
}

pub fn license_file(&self, package_root: &Path) -> CargoResult<String> {
self.license_file.clone().map_or(
Err(anyhow!("`workspace.package.license_file` was not defined")),
|d| resolve_relative_path("license-file", &self.ws_root, package_root, &d),
)
}

pub fn repository(&self) -> CargoResult<String> {
self.repository.clone().map_or(
Err(anyhow!("`workspace.package.repository` was not defined")),
|d| Ok(d),
)
}

pub fn publish(&self) -> CargoResult<VecStringOrBool> {
self.publish.clone().map_or(
Err(anyhow!("`workspace.package.publish` was not defined")),
|d| Ok(d),
)
}

pub fn edition(&self) -> CargoResult<String> {
self.edition.clone().map_or(
Err(anyhow!("`workspace.package.edition` was not defined")),
|d| Ok(d),
)
}

pub fn rust_version(&self) -> CargoResult<String> {
self.rust_version.clone().map_or(
Err(anyhow!("`workspace.package.rust-version` was not defined")),
|d| Ok(d),
)
}

pub fn badges(&self) -> CargoResult<BTreeMap<String, BTreeMap<String, String>>> {
self.badges.clone().map_or(
Err(anyhow!("`workspace.package.badges` was not defined")),
|d| Ok(d),
)
}

pub fn ws_root(&self) -> &PathBuf {
&self.ws_root
}
}

impl TomlProject {
@@ -1362,32 +1519,15 @@ impl TomlManifest {

let workspace_config = match (me.workspace.as_ref(), project.workspace.as_ref()) {
(Some(config), None) => {
let inheritable = InheritableFields::new(
config.dependencies.clone(),
config.version.clone(),
config.authors.clone(),
config.description.clone(),
config.homepage.clone(),
config.documentation.clone(),
config.readme.clone(),
config.keywords.clone(),
config.categories.clone(),
config.license.clone(),
config.license_file.clone(),
config.repository.clone(),
config.publish.clone(),
config.edition.clone(),
config.badges.clone(),
config.rust_version.clone(),
package_root.to_path_buf(),
);

let mut inheritable = config.package.clone().unwrap_or_default();
inheritable.update_ws_path(package_root.to_path_buf());
inheritable.update_deps(config.dependencies.clone());
WorkspaceConfig::Root(WorkspaceRootConfig::new(
package_root,
&config.members,
&config.default_members,
&config.exclude,
&Some(inheritable.clone()),
&Some(inheritable),
&config.metadata,
))
}
@@ -2057,25 +2197,9 @@ impl TomlManifest {
.transpose()?;
let workspace_config = match me.workspace {
Some(ref config) => {
let inheritable = InheritableFields::new(
config.dependencies.clone(),
config.version.clone(),
config.authors.clone(),
config.description.clone(),
config.homepage.clone(),
config.documentation.clone(),
config.readme.clone(),
config.keywords.clone(),
config.categories.clone(),
config.license.clone(),
config.license_file.clone(),
config.repository.clone(),
config.publish.clone(),
config.edition.clone(),
config.badges.clone(),
config.rust_version.clone(),
root.to_path_buf(),
);
let mut inheritable = config.package.clone().unwrap_or_default();
inheritable.update_ws_path(root.to_path_buf());
inheritable.update_deps(config.dependencies.clone());
WorkspaceConfig::Root(WorkspaceRootConfig::new(
root,
&config.members,
2 changes: 1 addition & 1 deletion src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
@@ -1439,7 +1439,7 @@ log2 = { workspace = true }
Example 2:
```toml
# in workspace's Cargo.toml
[workspace]
[workspace.package]
version = "1.2.3"
authors = ["Nice Folks"]
description = "..."
15 changes: 9 additions & 6 deletions tests/testsuite/inheritable_workspace_fields.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ fn permit_additional_workspace_fields() {
r#"
[workspace]
members = ["bar"]
[workspace.package]
version = "1.2.3"
authors = ["Rustaceans"]
description = "This is a crate"
@@ -27,7 +28,7 @@ fn permit_additional_workspace_fields() {
edition = "2018"
rust-version = "1.60"
[workspace.badges]
[workspace.package.badges]
gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" }
[workspace.dependencies]
@@ -135,6 +136,7 @@ fn inherit_own_workspace_fields() {
[workspace]
members = []
[workspace.package]
version = "1.2.3"
authors = ["Rustaceans"]
description = "This is a crate"
@@ -147,7 +149,7 @@ fn inherit_own_workspace_fields() {
publish = true
edition = "2018"
rust-version = "1.60"
[workspace.badges]
[workspace.package.badges]
gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" }
"#,
)
@@ -501,10 +503,10 @@ fn inherit_from_own_undefined_field() {
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
error inheriting `description` from workspace root manifest's `workspace.description`
error inheriting `description` from workspace root manifest's `workspace.package.description`
Caused by:
`workspace.description` was not defined
`workspace.package.description` was not defined
",
)
.run();
@@ -581,6 +583,7 @@ fn inherit_workspace_fields() {
r#"
[workspace]
members = ["bar"]
[workspace.package]
version = "1.2.3"
authors = ["Rustaceans"]
description = "This is a crate"
@@ -595,7 +598,7 @@ fn inherit_workspace_fields() {
publish = true
edition = "2018"
rust-version = "1.60"
[workspace.badges]
[workspace.package.badges]
gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" }
"#,
)
@@ -1281,7 +1284,7 @@ fn error_no_root_workspace() {
[ERROR] failed to parse manifest at `[..]/Cargo.toml`
Caused by:
error inheriting `description` from workspace root manifest's `workspace.description`
error inheriting `description` from workspace root manifest's `workspace.package.description`
Caused by:
root of a workspace inferred but wasn't a root: [..]/Cargo.toml