Skip to content

Commit 50d56ca

Browse files
committed
Auto merge of #12069 - hi-rustin:rustin-patch-ws, r=epage
Automatically inherit workspace fields when running cargo new/init
2 parents 5a39627 + 821725d commit 50d56ca

File tree

62 files changed

+730
-50
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+730
-50
lines changed

src/cargo/ops/cargo_new.rs

+91-50
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::core::{Edition, Shell, Workspace};
22
use crate::util::errors::CargoResult;
3+
use crate::util::important_paths::find_root_manifest_for_wd;
34
use crate::util::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
45
use crate::util::{restricted_names, Config};
56
use anyhow::{anyhow, Context as _};
@@ -759,69 +760,72 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
759760
init_vcs(path, vcs, config)?;
760761
write_ignore_file(path, &ignore, vcs)?;
761762

762-
let mut cargotoml_path_specifier = String::new();
763+
// Create `Cargo.toml` file with necessary `[lib]` and `[[bin]]` sections, if needed.
764+
let mut manifest = toml_edit::Document::new();
765+
manifest["package"] = toml_edit::Item::Table(toml_edit::Table::new());
766+
manifest["package"]["name"] = toml_edit::value(name);
767+
manifest["package"]["version"] = toml_edit::value("0.1.0");
768+
let edition = match opts.edition {
769+
Some(edition) => edition.to_string(),
770+
None => Edition::LATEST_STABLE.to_string(),
771+
};
772+
manifest["package"]["edition"] = toml_edit::value(edition);
773+
if let Some(registry) = opts.registry {
774+
let mut array = toml_edit::Array::default();
775+
array.push(registry);
776+
manifest["package"]["publish"] = toml_edit::value(array);
777+
}
778+
let mut dep_table = toml_edit::Table::default();
779+
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");
780+
manifest["dependencies"] = toml_edit::Item::Table(dep_table);
763781

764782
// Calculate what `[lib]` and `[[bin]]`s we need to append to `Cargo.toml`.
765-
766783
for i in &opts.source_files {
767784
if i.bin {
768785
if i.relative_path != "src/main.rs" {
769-
cargotoml_path_specifier.push_str(&format!(
770-
r#"
771-
[[bin]]
772-
name = "{}"
773-
path = {}
774-
"#,
775-
i.target_name,
776-
toml::Value::String(i.relative_path.clone())
777-
));
786+
let mut bin = toml_edit::Table::new();
787+
bin["name"] = toml_edit::value(i.target_name.clone());
788+
bin["path"] = toml_edit::value(i.relative_path.clone());
789+
manifest["bin"]
790+
.or_insert(toml_edit::Item::ArrayOfTables(
791+
toml_edit::ArrayOfTables::new(),
792+
))
793+
.as_array_of_tables_mut()
794+
.expect("bin is an array of tables")
795+
.push(bin);
778796
}
779797
} else if i.relative_path != "src/lib.rs" {
780-
cargotoml_path_specifier.push_str(&format!(
781-
r#"
782-
[lib]
783-
name = "{}"
784-
path = {}
785-
"#,
786-
i.target_name,
787-
toml::Value::String(i.relative_path.clone())
788-
));
798+
let mut lib = toml_edit::Table::new();
799+
lib["name"] = toml_edit::value(i.target_name.clone());
800+
lib["path"] = toml_edit::value(i.relative_path.clone());
801+
manifest["lib"] = toml_edit::Item::Table(lib);
789802
}
790803
}
791804

792-
// Create `Cargo.toml` file with necessary `[lib]` and `[[bin]]` sections, if needed.
805+
let manifest_path = path.join("Cargo.toml");
806+
if let Ok(root_manifest_path) = find_root_manifest_for_wd(&manifest_path) {
807+
let root_manifest = paths::read(&root_manifest_path)?;
808+
// Sometimes the root manifest is not a valid manifest, so we only try to parse it if it is.
809+
// This should not block the creation of the new project. It is only a best effort to
810+
// inherit the workspace package keys.
811+
if let Ok(workspace_document) = root_manifest.parse::<toml_edit::Document>() {
812+
if let Some(workspace_package_keys) = workspace_document
813+
.get("workspace")
814+
.and_then(|workspace| workspace.get("package"))
815+
.and_then(|package| package.as_table())
816+
{
817+
update_manifest_with_inherited_workspace_package_keys(
818+
opts,
819+
&mut manifest,
820+
workspace_package_keys,
821+
)
822+
}
823+
}
824+
}
793825

794-
paths::write(
795-
&path.join("Cargo.toml"),
796-
format!(
797-
r#"[package]
798-
name = "{}"
799-
version = "0.1.0"
800-
edition = {}
801-
{}
802-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
803-
804-
[dependencies]
805-
{}"#,
806-
name,
807-
match opts.edition {
808-
Some(edition) => toml::Value::String(edition.to_string()),
809-
None => toml::Value::String(Edition::LATEST_STABLE.to_string()),
810-
},
811-
match opts.registry {
812-
Some(registry) => format!(
813-
"publish = {}\n",
814-
toml::Value::Array(vec!(toml::Value::String(registry.to_string())))
815-
),
816-
None => "".to_string(),
817-
},
818-
cargotoml_path_specifier
819-
)
820-
.as_bytes(),
821-
)?;
826+
paths::write(&manifest_path, manifest.to_string())?;
822827

823828
// Create all specified source files (with respective parent directories) if they don't exist.
824-
825829
for i in &opts.source_files {
826830
let path_of_source_file = path.join(i.relative_path.clone());
827831

@@ -878,3 +882,40 @@ mod tests {
878882

879883
Ok(())
880884
}
885+
886+
// Update the manifest with the inherited workspace package keys.
887+
// If the option is not set, the key is removed from the manifest.
888+
// If the option is set, keep the value from the manifest.
889+
fn update_manifest_with_inherited_workspace_package_keys(
890+
opts: &MkOptions<'_>,
891+
manifest: &mut toml_edit::Document,
892+
workspace_package_keys: &toml_edit::Table,
893+
) {
894+
if workspace_package_keys.is_empty() {
895+
return;
896+
}
897+
898+
let try_remove_and_inherit_package_key = |key: &str, manifest: &mut toml_edit::Document| {
899+
let package = manifest["package"]
900+
.as_table_mut()
901+
.expect("package is a table");
902+
package.remove(key);
903+
let mut table = toml_edit::Table::new();
904+
table.set_dotted(true);
905+
table["workspace"] = toml_edit::value(true);
906+
package.insert(key, toml_edit::Item::Table(table));
907+
};
908+
909+
// Inherit keys from the workspace.
910+
// Only keep the value from the manifest if the option is set.
911+
for (key, _) in workspace_package_keys {
912+
if key == "edition" && opts.edition.is_some() {
913+
continue;
914+
}
915+
if key == "publish" && opts.registry.is_some() {
916+
continue;
917+
}
918+
919+
try_remove_and_inherit_package_key(key, manifest);
920+
}
921+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[workspace]
2+
members = [
3+
"crates/*",
4+
]
5+
6+
[workspace.package]
7+
authors = ["Rustaceans"]
8+
description = "foo"
9+
edition = "2018"
10+
homepage = "foo"
11+
keywords = ["foo", "bar"]
12+
readme = "README.md"
13+
rust-version = "1.67.0"
14+
categories = ["algorithms"]
15+
documentation = "foo"
16+
exclude = ["foo"]
17+
include = ["foo"]
18+
license = "MIT OR Apache-2.0"
19+
publish = false
20+
repository = "foo"
21+
version = "1.2.3"

tests/testsuite/cargo_new/inherit_workspace_package_table.in/README.md

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
pub fn add(left: usize, right: usize) -> usize {
2+
left + right
3+
}
4+
5+
#[cfg(test)]
6+
mod tests {
7+
use super::*;
8+
9+
#[test]
10+
fn it_works() {
11+
let result = add(2, 2);
12+
assert_eq!(result, 4);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../inherit_workspace_package_table.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use cargo_test_support::compare::assert_ui;
2+
use cargo_test_support::curr_dir;
3+
use cargo_test_support::CargoCommand;
4+
use cargo_test_support::Project;
5+
6+
#[cargo_test]
7+
fn case() {
8+
let project = Project::from_template(curr_dir!().join("in"));
9+
let project_root = project.root();
10+
let cwd = &project_root;
11+
12+
snapbox::cmd::Command::cargo_ui()
13+
.arg("new")
14+
.args(["crates/foo"])
15+
.current_dir(cwd)
16+
.assert()
17+
.success()
18+
.stdout_matches_path(curr_dir!().join("stdout.log"))
19+
.stderr_matches_path(curr_dir!().join("stderr.log"));
20+
21+
assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[workspace]
2+
members = [
3+
"crates/*",
4+
]
5+
6+
[workspace.package]
7+
authors = ["Rustaceans"]
8+
description = "foo"
9+
edition = "2018"
10+
homepage = "foo"
11+
keywords = ["foo", "bar"]
12+
readme = "README.md"
13+
rust-version = "1.67.0"
14+
categories = ["algorithms"]
15+
documentation = "foo"
16+
exclude = ["foo"]
17+
include = ["foo"]
18+
license = "MIT OR Apache-2.0"
19+
publish = false
20+
repository = "foo"
21+
version = "1.2.3"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "foo"
3+
authors.workspace = true
4+
description.workspace = true
5+
edition.workspace = true
6+
homepage.workspace = true
7+
keywords.workspace = true
8+
readme.workspace = true
9+
rust-version.workspace = true
10+
categories.workspace = true
11+
documentation.workspace = true
12+
exclude.workspace = true
13+
include.workspace = true
14+
license.workspace = true
15+
publish.workspace = true
16+
repository.workspace = true
17+
version.workspace = true
18+
19+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
20+
21+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("Hello, world!");
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
pub fn add(left: usize, right: usize) -> usize {
2+
left + right
3+
}
4+
5+
#[cfg(test)]
6+
mod tests {
7+
use super::*;
8+
9+
#[test]
10+
fn it_works() {
11+
let result = add(2, 2);
12+
assert_eq!(result, 4);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Created binary (application) `crates/foo` package

tests/testsuite/cargo_new/inherit_workspace_package_table/stdout.log

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../inherit_workspace_package_table.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use cargo_test_support::compare::assert_ui;
2+
use cargo_test_support::curr_dir;
3+
use cargo_test_support::CargoCommand;
4+
use cargo_test_support::Project;
5+
6+
#[cargo_test]
7+
fn case() {
8+
let project = Project::from_template(curr_dir!().join("in"));
9+
let project_root = project.root();
10+
let cwd = &project_root;
11+
12+
snapbox::cmd::Command::cargo_ui()
13+
.arg("new")
14+
.args(["crates/foo", "--edition", "2021"])
15+
.current_dir(cwd)
16+
.assert()
17+
.success()
18+
.stdout_matches_path(curr_dir!().join("stdout.log"))
19+
.stderr_matches_path(curr_dir!().join("stderr.log"));
20+
21+
assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[workspace]
2+
members = [
3+
"crates/*",
4+
]
5+
6+
[workspace.package]
7+
authors = ["Rustaceans"]
8+
description = "foo"
9+
edition = "2018"
10+
homepage = "foo"
11+
keywords = ["foo", "bar"]
12+
readme = "README.md"
13+
rust-version = "1.67.0"
14+
categories = ["algorithms"]
15+
documentation = "foo"
16+
exclude = ["foo"]
17+
include = ["foo"]
18+
license = "MIT OR Apache-2.0"
19+
publish = false
20+
repository = "foo"
21+
version = "1.2.3"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "foo"
3+
edition = "2021"
4+
authors.workspace = true
5+
description.workspace = true
6+
homepage.workspace = true
7+
keywords.workspace = true
8+
readme.workspace = true
9+
rust-version.workspace = true
10+
categories.workspace = true
11+
documentation.workspace = true
12+
exclude.workspace = true
13+
include.workspace = true
14+
license.workspace = true
15+
publish.workspace = true
16+
repository.workspace = true
17+
version.workspace = true
18+
19+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
20+
21+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("Hello, world!");
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
pub fn add(left: usize, right: usize) -> usize {
2+
left + right
3+
}
4+
5+
#[cfg(test)]
6+
mod tests {
7+
use super::*;
8+
9+
#[test]
10+
fn it_works() {
11+
let result = add(2, 2);
12+
assert_eq!(result, 4);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Created binary (application) `crates/foo` package

tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/stdout.log

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../inherit_workspace_package_table.in

0 commit comments

Comments
 (0)