cargo: `cargo new` sometimes generates invalid Cargo.toml when new crate is in a subdirectory of a workspace with Cargo 1.71
Problem
A crate in a subdirectory of a workspace opting out of being in the workspace via an empty [workspace] table or specifying its path in workspace.exclude in the workspace root’s Cargo.toml fails on Windows with Cargo 1.71 whereas it worked with previous versions of Cargo.
Steps
Create a crate in a subdirectory of a workspace with the following Cargo.toml:
[package]
name = "required_libs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[lib]
crate-type=["staticlib"]
[workspace]
Running cargo commands fail:
'C:/Program Files/Microsoft Visual Studio/2022/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin/cmake.exe' '-E' 'env' 'CARGO_BUILD_RUSTC=C:/Users/runneradmin/.rustup/toolchains/stable-x86_64-pc-windows-msvc/bin/rustc.exe' 'C:/Users/runneradmin/.rustup/toolchains/stable-x86_64-pc-windows-msvc/bin/cargo.exe' 'rustc' '--verbose' '--color' 'never' '--target=x86_64-pc-windows-msvc' '--' '--print=native-static-libs'
error: failed to parse manifest at `C:\cxx-qt\build\corrosion\required_libs\Cargo.toml`
Caused by:
error inheriting `version` from workspace root manifest's `workspace.package.version`
Caused by:
`workspace.package.version` was not defined
Removing the empty [workspace] table from Cargo.toml fails with a different error. This error is also shown when specifying workspace.exclude = [ "path/to/subdir" ] in the workspace root’s Cargo.toml:
'C:/Program Files/Microsoft Visual Studio/2022/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin/cmake.exe' '-E' 'env' 'CARGO_BUILD_RUSTC=C:/Users/runneradmin/.rustup/toolchains/stable-x86_64-pc-windows-msvc/bin/rustc.exe' 'C:/Users/runneradmin/.rustup/toolchains/stable-x86_64-pc-windows-msvc/bin/cargo.exe' 'rustc' '--verbose' '--color' 'never' '--target=x86_64-pc-windows-msvc' '--' '--print=native-static-libs'
error: current package believes it's in a workspace when it's not:
current: C:\cxx-qt\build\corrosion\required_libs\Cargo.toml
workspace: C:\cxx-qt\Cargo.toml
this may be fixable by adding `build\corrosion\required_libs` to the `workspace.members` array of the manifest located at: C:\cxx-qt\Cargo.toml
Alternatively, to keep it out of the workspace, add the package to the `workspace.exclude` array, or add an empty `[workspace]` table to the package's manifest.
Possible Solution(s)
I encounter this when using Corrosion to invoke Cargo via CMake as part of building a C++ application. At CMake configure time, Corrosion autogenerates a dummy staticlib crate with the above Cargo.toml to run cargo rustc -- --print=native-static-libs, which is used to tell CMake what system libraries need to be linked whenever linking a staticlib into a C/C++ build. The generated dummy crate is put in the CMake build directory, which conventionally is inside the code repository, which also contains the workspace’s virtual Cargo.toml manifest.
A workaround for this particular scenario is to create the CMake build directory outside of the code repository.
If rustc had some way to get the list of native-static-libs needed for every staticlib crate without needing to generate a dummy crate, this situation could be avoided.
Notes
Downstream Corrosion bug: https://github.com/corrosion-rs/corrosion/issues/418
Somehow this bug is only occurring on Windows. Linux and macOS builds still work fine with Rust 1.71.
The root Cargo.toml for the workspace is:
[workspace]
members = [
"crates/cxx-qt",
"crates/cxx-qt-build",
"crates/cxx-qt-gen",
"crates/cxx-qt-lib",
"crates/cxx-qt-lib-headers",
"crates/qt-build-utils",
"examples/cargo_without_cmake",
"examples/demo_threading/rust",
"examples/qml_extension_plugin/plugin/rust",
"examples/qml_features/rust",
"examples/qml_minimal/rust",
"tests/basic_cxx_only/rust",
"tests/basic_cxx_qt/rust",
"tests/qt_types_standalone/rust",
]
[workspace.package]
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/KDAB/cxx-qt/"
version = "0.5.3"
# Note a version needs to be specified on dependencies of packages
# we publish, otherwise crates.io complains as it doesn't know the version.
[workspace.dependencies]
cxx-qt = { path = "crates/cxx-qt" }
cxx-qt-macro = { path = "crates/cxx-qt-macro" }
cxx-qt-build = { path = "crates/cxx-qt-build" }
cxx-qt-gen = { path = "crates/cxx-qt-gen", version = "0.5.3" }
cxx-qt-lib = { path = "crates/cxx-qt-lib" }
cxx-qt-lib-headers = { path = "crates/cxx-qt-lib-headers", version = "0.5.3" }
qt-build-utils = { path = "crates/qt-build-utils", version = "0.5.3" }
# Ensure that the example comments are kept in sync
# examples/cargo_without_cmake/Cargo.toml
# examples/qml_minimal/rust/Cargo.toml
cxx = "1.0.95"
cxx-build = { version = "1.0.95", features = [ "parallel" ] }
cxx-gen = "0.7.95"
convert_case = "0.6"
proc-macro2 = "1.0"
syn = { version = "2.0", features = ["extra-traits", "full"] }
quote = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Version
1.71
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 22 (12 by maintainers)
I made a minimal example here: https://github.com/jschwe/cargo_regression_repro
What corrosion wants is:
cargo build(or more preciselycargo rustc -- --print=native-static-libsworks. The content of the library is not important, we just want to know which libraries need to be linked against forstdto work.Previously,
cargo newseemed like a good fit for this. The only limitation was, that when a parent directory contained a workspace, we needed to add an[workspace]to the Cargo.toml generated bycargo new.I guess if this is going to happen, we can’t use
cargo newanymore and need to ship our own dummy package, which is also fine.We discussed this in the cargo team meeting today.
While we didn’t formally nail it down, our primary concern for programmatic usage is in having processes (like a button in an IDE) that run
cargo newto create a package. There was also interest in having the placement of files within what is created to be predictable so the IDE could then immediately opensrc/main.rsfor editing.For programmatic usage that expects content of the files to be generated in a specific way we felt was out of scope. For a case like in this issue, the files could be generated directly or with tools like cargo-generate or cargo-hatch.
That still leaves the question of what we consider the right UX. Our primary concern was not regressing in what works. Since
cargo new && cargo checkwill will in this case, putting in workspace inheritance doesn’t regress that behavior. We would like to move away from any erroring and the general tone seemed like we were interested in automatically adding the package to the workspace members with a potential opt-out.Since the use case (programmatic use) is out of scope and the proposed fix (don’t inherit) was decided against, I’m closing this and discussion on workspace interactions will be carried forward in #6378.
Thanks for the repro!
I guess I’ve figured out what’s going on. #12069 use
find_root_manifest_for_wdto discover the possible workspace manifest, however, without checkingworkspace.membersfield. If the newly added package is not included inworkspace.members, then Cargo shouldn’t try to make it inherit anything from the workspace.For now, I consider this as a regression. We at least need to stop the invalid member from inheriting things. There are two solutions come up to my mind:
Workspace::newto check if the new package is a member. If yes, edit the manifest for the inheritance.WorkspaceRootConfig::members_pathsor something to determine whether it is a member when creating the manifest.Not sure which one is simpler. Probably the first one?
@hi-rustin are you willing to have a look at it?