diff --git a/Cargo.lock b/Cargo.lock index 7263d6a0b..b0a2169b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1285,6 +1285,7 @@ dependencies = [ "regex", "rpassword", "rustc_version", + "rustversion", "semver", "serde", "serde_json", @@ -2016,6 +2017,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "ryu" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index 1f5e238f5..d151501ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ native-tls-crate = { package = "native-tls", version = "0.2.8", optional = true [dev-dependencies] indoc = "1.0.3" pretty_assertions = "1.3.0" +rustversion = "1.0.9" [features] default = ["log", "upload", "rustls", "human-panic"] diff --git a/Changelog.md b/Changelog.md index 94a5d320b..0bd1f6fa3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fix `Cargo.toml` in new project template in [#1109](https://github.com/PyO3/maturin/pull/1109) * Fix `maturin develop` on Windows when using Python installed from msys2 in [#1112](https://github.com/PyO3/maturin/pull/1112) * Fix duplicated `Cargo.toml` of local dependencies in sdist in [#1114](https://github.com/PyO3/maturin/pull/1114) +* Add support for Cargo workspace dependencies inheritance in [#1123](https://github.com/PyO3/maturin/pull/1123) ## [0.13.3] - 2022-09-15 diff --git a/src/source_distribution.rs b/src/source_distribution.rs index 9bcc54e91..71e27bdad 100644 --- a/src/source_distribution.rs +++ b/src/source_distribution.rs @@ -19,17 +19,19 @@ const LOCAL_DEPENDENCIES_FOLDER: &str = "local_dependencies"; /// This method is rather frail, but unfortunately I don't know a better solution. fn rewrite_cargo_toml( manifest_path: impl AsRef, + workspace_manifest: &toml_edit::Document, known_path_deps: &HashMap, local_deps_folder: String, root_crate: bool, ) -> Result { + let manifest_path = manifest_path.as_ref(); let text = fs::read_to_string(&manifest_path).context(format!( "Can't read Cargo.toml at {}", - manifest_path.as_ref().display(), + manifest_path.display(), ))?; let mut data = text.parse::().context(format!( "Failed to parse Cargo.toml at {}", - manifest_path.as_ref().display() + manifest_path.display() ))?; let mut rewritten = false; // ˇˇˇˇˇˇˇˇˇˇˇˇ dep_category @@ -39,28 +41,84 @@ fn rewrite_cargo_toml( // ^^^^^^^^^^^^^ dep_name for dep_category in &["dependencies", "dev-dependencies", "build-dependencies"] { if let Some(table) = data.get_mut(*dep_category).and_then(|x| x.as_table_mut()) { + let workspace_deps = workspace_manifest + .get("workspace") + .and_then(|x| x.get(dep_category)) + .and_then(|x| x.as_table()); let dep_names: Vec<_> = table.iter().map(|(key, _)| key.to_string()).collect(); for dep_name in dep_names { - // There should either be no value for path, or it should be a string - if table.get(&dep_name).and_then(|x| x.get("path")).is_none() { - continue; - } - if !table[&dep_name]["path"].is_str() { - bail!( - "In {}, {} {} has a path value that is not a string", - manifest_path.as_ref().display(), - dep_category, - dep_name - ) - } - if !known_path_deps.contains_key(&dep_name) { - bail!( - "cargo metadata does not know about the path for {}.{} present in {}, \ - which should never happen ಠ_ಠ", - dep_category, - dep_name, - manifest_path.as_ref().display() - ); + let workspace_inherit = table + .get(&dep_name) + .and_then(|x| x.get("workspace")) + .and_then(|x| x.as_bool()) + .unwrap_or_default(); + + if !workspace_inherit { + // There should either be no value for path, or it should be a string + if table.get(&dep_name).and_then(|x| x.get("path")).is_none() { + continue; + } + if !table[&dep_name]["path"].is_str() { + bail!( + "In {}, {} {} has a path value that is not a string", + manifest_path.display(), + dep_category, + dep_name + ) + } + if !known_path_deps.contains_key(&dep_name) { + bail!( + "cargo metadata does not know about the path for {}.{} present in {}, \ + which should never happen ಠ_ಠ", + dep_category, + dep_name, + manifest_path.display() + ); + } + } else { + // If a workspace inherited dependency isn't a path dep, + // we need to replace `workspace = true` with its full requirement spec. + if !known_path_deps.contains_key(&dep_name) { + if let Some(workspace_dep) = workspace_deps.and_then(|x| x.get(&dep_name)) { + let mut workspace_dep = workspace_dep.clone(); + // Merge optional and features from the current Cargo.toml + if table[&dep_name].get("optional").is_some() { + workspace_dep["optional"] = table[&dep_name]["optional"].clone(); + } + if let Some(features) = + table[&dep_name].get("features").and_then(|x| x.as_array()) + { + let existing_features = workspace_dep + .as_table_like_mut() + .unwrap() + .entry("features") + .or_insert_with(|| { + toml_edit::Item::Value(toml_edit::Array::new().into()) + }) + .as_array_mut() + .with_context(|| { + format!( + "In {}, {} {} has a features value that is not an array", + manifest_path.display(), + dep_category, + dep_name + ) + })?; + existing_features.extend(features); + } + table[&dep_name] = workspace_dep; + rewritten = true; + } else { + bail!( + "In {}, {} {} is marked as `workspace = true`, but it is found neither in \ + the workspace manifest nor in the known path dependencies", + manifest_path.display(), + dep_category, + dep_name + ) + } + continue; + } } // This is the location of the targeted crate in the source distribution table[&dep_name]["path"] = if root_crate { @@ -69,6 +127,13 @@ fn rewrite_cargo_toml( // Cargo.toml contains relative paths, and we're already in LOCAL_DEPENDENCIES_FOLDER toml_edit::value(format!("../{}", dep_name)) }; + if workspace_inherit { + // Remove workspace inheritance now that we converted it into a path dependency + table[&dep_name] + .as_table_like_mut() + .unwrap() + .remove("workspace"); + } rewritten = true; } } @@ -124,6 +189,7 @@ fn add_crate_to_source_distribution( writer: &mut SDistWriter, pyproject_toml_path: impl AsRef, manifest_path: impl AsRef, + workspace_manifest: &toml_edit::Document, prefix: impl AsRef, known_path_deps: &HashMap, root_crate: bool, @@ -218,6 +284,7 @@ fn add_crate_to_source_distribution( }; let rewritten_cargo_toml = rewrite_cargo_toml( &manifest_path, + workspace_manifest, known_path_deps, local_deps_folder, root_crate, @@ -284,6 +351,12 @@ pub fn source_distribution( let metadata21 = &build_context.metadata21; let manifest_path = &build_context.manifest_path; let pyproject_toml_path = fs::canonicalize(&build_context.pyproject_toml_path)?; + let workspace_manifest_path = build_context + .cargo_metadata + .workspace_root + .join("Cargo.toml"); + let workspace_manifest: toml_edit::Document = + fs::read_to_string(&workspace_manifest_path)?.parse()?; let known_path_deps = find_path_deps(&build_context.cargo_metadata)?; @@ -300,6 +373,7 @@ pub fn source_distribution( &mut writer, &pyproject_toml_path, &path_dep, + &workspace_manifest, &root_dir.join(LOCAL_DEPENDENCIES_FOLDER).join(name), &known_path_deps, false, @@ -316,6 +390,7 @@ pub fn source_distribution( &mut writer, &pyproject_toml_path, &manifest_path, + &workspace_manifest, &root_dir, &known_path_deps, true, diff --git a/test-crates/workspace-inheritance/Cargo.lock b/test-crates/workspace-inheritance/Cargo.lock new file mode 100644 index 000000000..da75ecd5c --- /dev/null +++ b/test-crates/workspace-inheritance/Cargo.lock @@ -0,0 +1,255 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "generic_lib" +version = "0.1.0" + +[[package]] +name = "indoc" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" + +[[package]] +name = "libc" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "proc-macro2" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pyo3" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6302e85060011447471887705bb7838f14aba43fcb06957d823739a496b3dc" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b65b546c35d8a3b1b2f0ddbac7c6a569d759f357f2b9df884f5d6b719152c8" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c275a07127c1aca33031a563e384ffdd485aee34ef131116fcd58e3430d1742b" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284fc4485bfbcc9850a6d661d627783f18d19c2ab55880b021671c4ba83e90f7" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53bda0f58f73f5c5429693c96ed57f7abdb38fdfc28ae06da4101a257adb7faf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "syn" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "target-lexicon" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "unindent" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "workspace-inheritance" +version = "0.1.0" +dependencies = [ + "generic_lib", + "libc", + "pyo3", +] diff --git a/test-crates/workspace-inheritance/Cargo.toml b/test-crates/workspace-inheritance/Cargo.toml new file mode 100644 index 000000000..21af48430 --- /dev/null +++ b/test-crates/workspace-inheritance/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +members = [ + "generic_lib", + "python" +] + +[workspace.dependencies] +libc = { version = "0.2", features = ["std"] } +generic_lib = { path = "generic_lib" } diff --git a/test-crates/workspace-inheritance/generic_lib/Cargo.toml b/test-crates/workspace-inheritance/generic_lib/Cargo.toml new file mode 100644 index 000000000..c3bb1ca14 --- /dev/null +++ b/test-crates/workspace-inheritance/generic_lib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "generic_lib" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/test-crates/workspace-inheritance/generic_lib/src/lib.rs b/test-crates/workspace-inheritance/generic_lib/src/lib.rs new file mode 100644 index 000000000..9673d1684 --- /dev/null +++ b/test-crates/workspace-inheritance/generic_lib/src/lib.rs @@ -0,0 +1,3 @@ +pub fn foo() -> &'static str { + "foo" +} diff --git a/test-crates/workspace-inheritance/python/Cargo.toml b/test-crates/workspace-inheritance/python/Cargo.toml new file mode 100644 index 000000000..7b25fcf6f --- /dev/null +++ b/test-crates/workspace-inheritance/python/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "workspace-inheritance" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "workspace_inheritance" +crate-type = ["cdylib"] + +[dependencies] +pyo3 = { version = "0.16.5", features = ["extension-module"] } +generic_lib.workspace = true + +[dependencies.libc] +workspace = true +optional = true +features = ["extra_traits"] diff --git a/test-crates/workspace-inheritance/python/pyproject.toml b/test-crates/workspace-inheritance/python/pyproject.toml new file mode 100644 index 000000000..5d44682cd --- /dev/null +++ b/test-crates/workspace-inheritance/python/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["maturin>=0.13,<0.14"] +build-backend = "maturin" diff --git a/test-crates/workspace-inheritance/python/src/lib.rs b/test-crates/workspace-inheritance/python/src/lib.rs new file mode 100644 index 000000000..3210813e0 --- /dev/null +++ b/test-crates/workspace-inheritance/python/src/lib.rs @@ -0,0 +1,14 @@ +use pyo3::prelude::*; + +/// Formats the sum of two numbers as string. +#[pyfunction] +fn sum_as_string(a: usize, b: usize) -> PyResult { + Ok((a + b).to_string()) +} + +/// A Python module implemented in Rust. +#[pymodule] +fn workspace_with_path_dep(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; + Ok(()) +} diff --git a/tests/run.rs b/tests/run.rs index 221267448..66c9e5303 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -293,6 +293,23 @@ fn workspace_with_path_dep_sdist() { )) } +#[rustversion::since(1.64)] +#[test] +fn workspace_inheritance_sdist() { + handle_result(other::test_source_distribution( + "test-crates/workspace-inheritance/python", + vec![ + "workspace_inheritance-0.1.0/local_dependencies/generic_lib/Cargo.toml", + "workspace_inheritance-0.1.0/local_dependencies/generic_lib/src/lib.rs", + "workspace_inheritance-0.1.0/Cargo.toml", + "workspace_inheritance-0.1.0/pyproject.toml", + "workspace_inheritance-0.1.0/src/lib.rs", + "workspace_inheritance-0.1.0/PKG-INFO", + ], + "workspace_inheritance_sdist", + )) +} + #[test] fn abi3_python_interpreter_args() { handle_result(other::abi3_python_interpreter_args());