Skip to content

Commit

Permalink
Allow Rust crate to be placed outside of the directory containing `py…
Browse files Browse the repository at this point in the history
…project.toml`
  • Loading branch information
messense committed Dec 9, 2022
1 parent 0ed2757 commit e7e40a8
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 24 deletions.
9 changes: 0 additions & 9 deletions src/project_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,6 @@ impl ProjectResolver {
let pyproject =
PyProjectToml::new(&pyproject_file).context("pyproject.toml is invalid")?;
if let Some(path) = pyproject.manifest_path() {
// pyproject.toml must be placed at top directory
let manifest_dir = path
.parent()
.context("missing parent directory")?
.normalize()?
.into_path_buf();
if !manifest_dir.starts_with(&current_dir) {
bail!("Cargo.toml can not be placed outside of the directory containing pyproject.toml");
}
debug!("Using cargo manifest path from pyproject.toml {:?}", path);
return Ok((path.normalize()?.into_path_buf(), pyproject_file));
} else {
Expand Down
88 changes: 73 additions & 15 deletions src/source_distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use std::str;
use tracing::debug;

const LOCAL_DEPENDENCIES_FOLDER: &str = "local_dependencies";
const RUST_SRC_FOLDER: &str = "rust_src";

/// Inheritable workspace fields, see
/// https://github.com/rust-lang/cargo/blob/13ae438cf079da58272edc71f4d4968043dbd27b/src/cargo/util/toml/mod.rs#L1140-L1158
const WORKSPACE_INHERITABLE_FIELDS: &[&str] = &[
Expand Down Expand Up @@ -256,6 +258,38 @@ fn ensure_dep_is_inline_table(dep: &mut toml_edit::Item) {
}
}

/// When `Cargo.toml` is outside of the directory containing `pyproject.toml`,
/// we put Rust crate source to `RUST_SRC_FOLDER` and
/// update `tool.maturin.manifest-path` in `pyproject.toml`.
fn rewrite_pyproject_toml(pyproject_toml_path: &Path) -> Result<String> {
let text = fs::read_to_string(pyproject_toml_path).context(format!(
"Can't read pyproject.toml at {}",
pyproject_toml_path.display(),
))?;
let mut data = text.parse::<toml_edit::Document>().context(format!(
"Failed to parse pyproject.toml at {}",
pyproject_toml_path.display()
))?;
if let Some(tool) = data.get_mut("tool").and_then(|x| x.as_table_mut()) {
if let Some(maturin) = tool.get_mut("maturin").and_then(|x| x.as_table_mut()) {
if let Some(manifest_path) = maturin.get_mut("manifest-path") {
// original: ../$crate/Cargo.toml or ../../$crate/Cargo.toml
// rewrite to: $RUST_SRC_FOLDER/$crate/Cargo.toml
let path =
Path::new(manifest_path.as_str().context(
"tool.maturin.manifest-path in pyproject.toml must be a string",
)?);
let crate_name = path.parent().unwrap().file_name().unwrap();
let new_path = Path::new(RUST_SRC_FOLDER)
.join(crate_name)
.join("Cargo.toml");
*manifest_path = toml_edit::value(new_path.to_str().unwrap());
}
}
}
Ok(data.to_string())
}

/// Copies the files of a crate to a source distribution, recursively adding path dependencies
/// and rewriting path entries in Cargo.toml
///
Expand Down Expand Up @@ -340,33 +374,66 @@ fn add_crate_to_source_distribution(
})
.collect();

let prefix = prefix.as_ref();
writer.add_directory(prefix)?;

let mut cargo_toml_in_rust_src = false;

if root_crate
&& !target_source
.iter()
.any(|(target, _)| target == Path::new("pyproject.toml"))
{
// Add pyproject.toml to the source distribution
// if Cargo.toml is in subdirectory of pyproject.toml directory
if cargo_toml_in_subdir {
// if Cargo.toml is in subdirectory of pyproject.toml directory
target_source.push((
PathBuf::from("pyproject.toml"),
pyproject_toml_path.to_path_buf(),
));
} else {
bail!(
"pyproject.toml was not included by `cargo package`. \
Please make sure pyproject.toml is not excluded or build without `--sdist`"
)
// if pyproject.toml was not included by `cargo package --list`
// (e.g. because it is in a parent directory)
// we need to add `cargo package --list` files to a subdirectory
// and rewrite `tool.maturin.manifest-path` in pyproject.toml
let crate_name = abs_manifest_dir.file_name().unwrap();
target_source.iter_mut().for_each(|(target, _)| {
*target = PathBuf::from(RUST_SRC_FOLDER)
.join(crate_name)
.join(&target);
});
// rewrite `tool.maturin.manifest-path` in pyproject.toml
let rewritten_pyproject_toml = rewrite_pyproject_toml(pyproject_toml_path)?;
writer.add_bytes(
prefix.join("pyproject.toml"),
rewritten_pyproject_toml.as_bytes(),
)?;
cargo_toml_in_rust_src = true;
}
}

let cargo_toml_path = if cargo_toml_in_subdir {
let relative_manifest_path = abs_manifest_path.strip_prefix(pyproject_dir).unwrap();
prefix.join(relative_manifest_path)
} else if cargo_toml_in_rust_src {
let crate_name = abs_manifest_dir.file_name().unwrap();
prefix
.join(RUST_SRC_FOLDER)
.join(crate_name)
.join(manifest_path.file_name().unwrap())
} else {
prefix.join(manifest_path.file_name().unwrap())
};

let local_deps_folder = if cargo_toml_in_subdir {
let level = abs_manifest_dir
.strip_prefix(pyproject_dir)
.unwrap()
.components()
.count();
format!("{}{}", "../".repeat(level), LOCAL_DEPENDENCIES_FOLDER)
} else if cargo_toml_in_rust_src {
format!("../../{}", LOCAL_DEPENDENCIES_FOLDER)
} else {
LOCAL_DEPENDENCIES_FOLDER.to_string()
};
Expand All @@ -378,16 +445,7 @@ fn add_crate_to_source_distribution(
root_crate,
)?;

let prefix = prefix.as_ref();
writer.add_directory(prefix)?;

let cargo_toml = if cargo_toml_in_subdir {
let relative_manifest_path = abs_manifest_path.strip_prefix(pyproject_dir).unwrap();
prefix.join(relative_manifest_path)
} else {
prefix.join(manifest_path.file_name().unwrap())
};
writer.add_bytes(cargo_toml, rewritten_cargo_toml.as_bytes())?;
writer.add_bytes(cargo_toml_path, rewritten_cargo_toml.as_bytes())?;

for (target, source) in target_source {
writer.add_file(prefix.join(target), source)?;
Expand Down

0 comments on commit e7e40a8

Please sign in to comment.