Skip to content

Commit

Permalink
Auto merge of #12069 - hi-rustin:rustin-patch-ws, r=epage
Browse files Browse the repository at this point in the history
Automatically inherit workspace fields when running cargo new/init
  • Loading branch information
bors committed May 23, 2023
2 parents 5a39627 + 821725d commit 50d56ca
Show file tree
Hide file tree
Showing 62 changed files with 730 additions and 50 deletions.
141 changes: 91 additions & 50 deletions src/cargo/ops/cargo_new.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::core::{Edition, Shell, Workspace};
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
use crate::util::{restricted_names, Config};
use anyhow::{anyhow, Context as _};
Expand Down Expand Up @@ -759,69 +760,72 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
init_vcs(path, vcs, config)?;
write_ignore_file(path, &ignore, vcs)?;

let mut cargotoml_path_specifier = String::new();
// Create `Cargo.toml` file with necessary `[lib]` and `[[bin]]` sections, if needed.
let mut manifest = toml_edit::Document::new();
manifest["package"] = toml_edit::Item::Table(toml_edit::Table::new());
manifest["package"]["name"] = toml_edit::value(name);
manifest["package"]["version"] = toml_edit::value("0.1.0");
let edition = match opts.edition {
Some(edition) => edition.to_string(),
None => Edition::LATEST_STABLE.to_string(),
};
manifest["package"]["edition"] = toml_edit::value(edition);
if let Some(registry) = opts.registry {
let mut array = toml_edit::Array::default();
array.push(registry);
manifest["package"]["publish"] = toml_edit::value(array);
}
let mut dep_table = toml_edit::Table::default();
dep_table.decor_mut().set_prefix("\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n");
manifest["dependencies"] = toml_edit::Item::Table(dep_table);

// Calculate what `[lib]` and `[[bin]]`s we need to append to `Cargo.toml`.

for i in &opts.source_files {
if i.bin {
if i.relative_path != "src/main.rs" {
cargotoml_path_specifier.push_str(&format!(
r#"
[[bin]]
name = "{}"
path = {}
"#,
i.target_name,
toml::Value::String(i.relative_path.clone())
));
let mut bin = toml_edit::Table::new();
bin["name"] = toml_edit::value(i.target_name.clone());
bin["path"] = toml_edit::value(i.relative_path.clone());
manifest["bin"]
.or_insert(toml_edit::Item::ArrayOfTables(
toml_edit::ArrayOfTables::new(),
))
.as_array_of_tables_mut()
.expect("bin is an array of tables")
.push(bin);
}
} else if i.relative_path != "src/lib.rs" {
cargotoml_path_specifier.push_str(&format!(
r#"
[lib]
name = "{}"
path = {}
"#,
i.target_name,
toml::Value::String(i.relative_path.clone())
));
let mut lib = toml_edit::Table::new();
lib["name"] = toml_edit::value(i.target_name.clone());
lib["path"] = toml_edit::value(i.relative_path.clone());
manifest["lib"] = toml_edit::Item::Table(lib);
}
}

// Create `Cargo.toml` file with necessary `[lib]` and `[[bin]]` sections, if needed.
let manifest_path = path.join("Cargo.toml");
if let Ok(root_manifest_path) = find_root_manifest_for_wd(&manifest_path) {
let root_manifest = paths::read(&root_manifest_path)?;
// Sometimes the root manifest is not a valid manifest, so we only try to parse it if it is.
// This should not block the creation of the new project. It is only a best effort to
// inherit the workspace package keys.
if let Ok(workspace_document) = root_manifest.parse::<toml_edit::Document>() {
if let Some(workspace_package_keys) = workspace_document
.get("workspace")
.and_then(|workspace| workspace.get("package"))
.and_then(|package| package.as_table())
{
update_manifest_with_inherited_workspace_package_keys(
opts,
&mut manifest,
workspace_package_keys,
)
}
}
}

paths::write(
&path.join("Cargo.toml"),
format!(
r#"[package]
name = "{}"
version = "0.1.0"
edition = {}
{}
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
{}"#,
name,
match opts.edition {
Some(edition) => toml::Value::String(edition.to_string()),
None => toml::Value::String(Edition::LATEST_STABLE.to_string()),
},
match opts.registry {
Some(registry) => format!(
"publish = {}\n",
toml::Value::Array(vec!(toml::Value::String(registry.to_string())))
),
None => "".to_string(),
},
cargotoml_path_specifier
)
.as_bytes(),
)?;
paths::write(&manifest_path, manifest.to_string())?;

// Create all specified source files (with respective parent directories) if they don't exist.

for i in &opts.source_files {
let path_of_source_file = path.join(i.relative_path.clone());

Expand Down Expand Up @@ -878,3 +882,40 @@ mod tests {

Ok(())
}

// Update the manifest with the inherited workspace package keys.
// If the option is not set, the key is removed from the manifest.
// If the option is set, keep the value from the manifest.
fn update_manifest_with_inherited_workspace_package_keys(
opts: &MkOptions<'_>,
manifest: &mut toml_edit::Document,
workspace_package_keys: &toml_edit::Table,
) {
if workspace_package_keys.is_empty() {
return;
}

let try_remove_and_inherit_package_key = |key: &str, manifest: &mut toml_edit::Document| {
let package = manifest["package"]
.as_table_mut()
.expect("package is a table");
package.remove(key);
let mut table = toml_edit::Table::new();
table.set_dotted(true);
table["workspace"] = toml_edit::value(true);
package.insert(key, toml_edit::Item::Table(table));
};

// Inherit keys from the workspace.
// Only keep the value from the manifest if the option is set.
for (key, _) in workspace_package_keys {
if key == "edition" && opts.edition.is_some() {
continue;
}
if key == "publish" && opts.registry.is_some() {
continue;
}

try_remove_and_inherit_package_key(key, manifest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[workspace]
members = [
"crates/*",
]

[workspace.package]
authors = ["Rustaceans"]
description = "foo"
edition = "2018"
homepage = "foo"
keywords = ["foo", "bar"]
readme = "README.md"
rust-version = "1.67.0"
categories = ["algorithms"]
documentation = "foo"
exclude = ["foo"]
include = ["foo"]
license = "MIT OR Apache-2.0"
publish = false
repository = "foo"
version = "1.2.3"
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
22 changes: 22 additions & 0 deletions tests/testsuite/cargo_new/inherit_workspace_package_table/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;

#[cargo_test]
fn case() {
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;

snapbox::cmd::Command::cargo_ui()
.arg("new")
.args(["crates/foo"])
.current_dir(cwd)
.assert()
.success()
.stdout_matches_path(curr_dir!().join("stdout.log"))
.stderr_matches_path(curr_dir!().join("stderr.log"));

assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[workspace]
members = [
"crates/*",
]

[workspace.package]
authors = ["Rustaceans"]
description = "foo"
edition = "2018"
homepage = "foo"
keywords = ["foo", "bar"]
readme = "README.md"
rust-version = "1.67.0"
categories = ["algorithms"]
documentation = "foo"
exclude = ["foo"]
include = ["foo"]
license = "MIT OR Apache-2.0"
publish = false
repository = "foo"
version = "1.2.3"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "foo"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
readme.workspace = true
rust-version.workspace = true
categories.workspace = true
documentation.workspace = true
exclude.workspace = true
include.workspace = true
license.workspace = true
publish.workspace = true
repository.workspace = true
version.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Created binary (application) `crates/foo` package
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;

#[cargo_test]
fn case() {
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;

snapbox::cmd::Command::cargo_ui()
.arg("new")
.args(["crates/foo", "--edition", "2021"])
.current_dir(cwd)
.assert()
.success()
.stdout_matches_path(curr_dir!().join("stdout.log"))
.stderr_matches_path(curr_dir!().join("stderr.log"));

assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[workspace]
members = [
"crates/*",
]

[workspace.package]
authors = ["Rustaceans"]
description = "foo"
edition = "2018"
homepage = "foo"
keywords = ["foo", "bar"]
readme = "README.md"
rust-version = "1.67.0"
categories = ["algorithms"]
documentation = "foo"
exclude = ["foo"]
include = ["foo"]
license = "MIT OR Apache-2.0"
publish = false
repository = "foo"
version = "1.2.3"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "foo"
edition = "2021"
authors.workspace = true
description.workspace = true
homepage.workspace = true
keywords.workspace = true
readme.workspace = true
rust-version.workspace = true
categories.workspace = true
documentation.workspace = true
exclude.workspace = true
include.workspace = true
license.workspace = true
publish.workspace = true
repository.workspace = true
version.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Created binary (application) `crates/foo` package
Empty file.
Loading

0 comments on commit 50d56ca

Please sign in to comment.