Skip to content

Commit

Permalink
Add wheel data support
Browse files Browse the repository at this point in the history
This also introduces tracing for logging, which is neat because you can e.g. `RUST_LOG=...` at runtime.

Fixes #258
  • Loading branch information
konstin committed May 10, 2022
1 parent d147bd5 commit 5eb0fcb
Show file tree
Hide file tree
Showing 22 changed files with 344 additions and 163 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: psf/black@22.1.0
- uses: psf/black@22.3.0

spellcheck:
runs-on: ubuntu-latest
Expand Down
33 changes: 33 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ multipart = { version = "0.18.0", features = ["client"], default-features = fals
rpassword = { version = "6.0.1", optional = true }
ureq = { version = "2.3.1", features = ["gzip"], default-features = false, optional = true }
native-tls-crate = { package = "native-tls", version = "0.2.8", optional = true }
tracing = "0.1.34"

[dev-dependencies]
indoc = "1.0.3"
Expand Down
14 changes: 14 additions & 0 deletions guide/src/project_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,17 @@ my-project
└── src
└── lib.rs
```

## Data

You can add wheel data by creating a `<module_name>.data` folder or setting its location as `data` in pyproject.toml under `[tool.maturin]` or in Cargo.toml under `[project.metadata.maturin]`.

The data folder may have the following subfolder:

* `data`: The contents of this folder will simply be unpacked into the virtualenv
* `scripts`: Treated similar to entry points, files in there are installed as standalone executable
* `headers`: For `.h` C header files
* `purelib`: This also exists, but seems to be barely used
* `platlib`: This also exists, but seems to be barely used

If you add a symlink in the data directory, we'll include the actual file so you more flexibility
96 changes: 48 additions & 48 deletions src/build_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::auditwheel::{get_policy_and_libs, patchelf, relpath};
use crate::auditwheel::{PlatformTag, Policy};
use crate::compile::warn_missing_py_init;
use crate::module_writer::{
write_bin, write_bindings_module, write_cffi_module, write_python_part, WheelWriter,
add_data, write_bin, write_bindings_module, write_cffi_module, write_python_part, WheelWriter,
};
use crate::python_interpreter::InterpreterKind;
use crate::source_distribution::source_distribution;
Expand Down Expand Up @@ -51,25 +51,19 @@ impl BridgeModel {
}
}

/// Whether this project is pure rust or rust mixed with python
/// Whether this project is pure rust or rust mixed with python and whether it has wheel data
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ProjectLayout {
/// A rust crate compiled into a shared library with only some glue python for cffi
PureRust {
/// Contains the canonicialized (i.e. absolute) path to the rust part of the project
rust_module: PathBuf,
/// rust extension name
extension_name: String,
},
/// A python package that is extended by a native rust module.
Mixed {
/// Contains the canonicialized (i.e. absolute) path to the python part of the project
python_module: PathBuf,
/// Contains the canonicialized (i.e. absolute) path to the rust part of the project
rust_module: PathBuf,
/// rust extension name
extension_name: String,
},
pub struct ProjectLayout {
/// Contains the canonicalized (i.e. absolute) path to the python part of the project
/// If none, we have a rust crate compiled into a shared library with only some glue python for cffi
/// If some, we have a python package that is extended by a native rust module.
pub python_module: Option<PathBuf>,
/// Contains the canonicalized (i.e. absolute) path to the rust part of the project
pub rust_module: PathBuf,
/// rust extension name
pub extension_name: String,
/// The location of the wheel data, if any
pub data: Option<PathBuf>,
}

impl ProjectLayout {
Expand All @@ -78,6 +72,7 @@ impl ProjectLayout {
project_root: impl AsRef<Path>,
module_name: &str,
py_src: Option<impl AsRef<Path>>,
data: Option<impl AsRef<Path>>,
) -> Result<ProjectLayout> {
// A dot in the module name means the extension module goes into the module folder specified by the path
let parts: Vec<&str> = module_name.split('.').collect();
Expand All @@ -100,36 +95,45 @@ impl ProjectLayout {
module_name.to_string(),
)
};

let data = if let Some(data) = data {
let data = if data.as_ref().is_absolute() {
data.as_ref().to_path_buf()
} else {
project_root.join(data)
};
if !data.is_dir() {
bail!("No such data directory {}", data.display());
}
Some(data)
} else if project_root.join(format!("{}.data", module_name)).is_dir() {
Some(project_root.join(format!("{}.data", module_name)))
} else {
None
};

if python_module.is_dir() {
if !python_module.join("__init__.py").is_file() {
bail!("Found a directory with the module name ({}) next to Cargo.toml, which indicates a mixed python/rust project, but the directory didn't contain an __init__.py file.", module_name)
}

println!("🍹 Building a mixed python/rust project");

Ok(ProjectLayout::Mixed {
python_module,
Ok(ProjectLayout {
python_module: Some(python_module),
rust_module,
extension_name,
data,
})
} else {
Ok(ProjectLayout::PureRust {
Ok(ProjectLayout {
python_module: None,
rust_module: project_root.to_path_buf(),
extension_name,
data,
})
}
}

pub fn extension_name(&self) -> &str {
match *self {
ProjectLayout::PureRust {
ref extension_name, ..
} => extension_name,
ProjectLayout::Mixed {
ref extension_name, ..
} => extension_name,
}
}
}

/// Contains all the metadata required to build the crate
Expand Down Expand Up @@ -246,6 +250,7 @@ impl BuildContext {
&self.cargo_metadata,
pyproject.sdist_include(),
include_cargo_lock,
self.project_layout.data.as_deref(),
)
.context("Failed to build source distribution")?;
Ok(Some((sdist_path, "source".to_string())))
Expand Down Expand Up @@ -396,7 +401,7 @@ impl BuildContext {
.context("Failed to add the files to the wheel")?;

self.add_pth(&mut writer)?;

add_data(&mut writer, self.project_layout.data.as_deref())?;
let wheel_path = writer.finish()?;
Ok((wheel_path, format!("cp{}{}", major, min_minor)))
}
Expand All @@ -415,7 +420,7 @@ impl BuildContext {
let python_interpreter = interpreters.get(0);
let artifact = self.compile_cdylib(
python_interpreter,
Some(self.project_layout.extension_name()),
Some(&self.project_layout.extension_name),
)?;
let (policy, external_libs) = self.auditwheel(&artifact, self.platform_tag)?;
let (wheel_path, tag) = self.write_binding_wheel_abi3(
Expand Down Expand Up @@ -461,7 +466,7 @@ impl BuildContext {
.context("Failed to add the files to the wheel")?;

self.add_pth(&mut writer)?;

add_data(&mut writer, self.project_layout.data.as_deref())?;
let wheel_path = writer.finish()?;
Ok((
wheel_path,
Expand All @@ -484,7 +489,7 @@ impl BuildContext {
for python_interpreter in interpreters {
let artifact = self.compile_cdylib(
Some(python_interpreter),
Some(self.project_layout.extension_name()),
Some(&self.project_layout.extension_name),
)?;
let (policy, external_libs) = self.auditwheel(&artifact, self.platform_tag)?;
let (wheel_path, tag) = self.write_binding_wheel(
Expand Down Expand Up @@ -568,7 +573,7 @@ impl BuildContext {
)?;

self.add_pth(&mut writer)?;

add_data(&mut writer, self.project_layout.data.as_deref())?;
let wheel_path = writer.finish()?;
Ok((wheel_path, "py3".to_string()))
}
Expand Down Expand Up @@ -616,16 +621,11 @@ impl BuildContext {

let mut writer = WheelWriter::new(&tag, &self.out, &self.metadata21, &tags)?;

match self.project_layout {
ProjectLayout::Mixed {
ref python_module, ..
} => {
if !self.editable {
write_python_part(&mut writer, python_module)
.context("Failed to add the python module to the package")?;
}
if let Some(python_module) = &self.project_layout.python_module {
if !self.editable {
write_python_part(&mut writer, python_module)
.context("Failed to add the python module to the package")?;
}
ProjectLayout::PureRust { .. } => {}
}

// I wouldn't know of any case where this would be the wrong (and neither do
Expand All @@ -638,7 +638,7 @@ impl BuildContext {
write_bin(&mut writer, artifact, &self.metadata21, bin_name)?;

self.add_pth(&mut writer)?;

add_data(&mut writer, self.project_layout.data.as_deref())?;
let wheel_path = writer.finish()?;
Ok((wheel_path, "py3".to_string()))
}
Expand Down
6 changes: 5 additions & 1 deletion src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::env;
use std::io;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

// This is used for BridgeModel::Bindings("pyo3-ffi") and BridgeModel::Bindings("pyo3").
// These should be treated almost identically but must be correctly identified
Expand Down Expand Up @@ -209,10 +209,14 @@ impl BuildOptions {
.filter(|name| name.contains('.'))
.unwrap_or(&module_name);

let data = pyproject
.and_then(|x| x.data())
.or_else(|| extra_metadata.data.as_ref().map(Path::new));
let project_layout = ProjectLayout::determine(
manifest_dir,
extension_name,
extra_metadata.python_source.as_deref(),
data,
)?;

let mut args_from_pyproject = Vec::new();
Expand Down
3 changes: 3 additions & 0 deletions src/cargo_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ pub struct RemainingCoreMetadata {
pub project_url: Option<HashMap<String, String>>,
pub provides_extra: Option<Vec<String>>,
pub description_content_type: Option<String>,
/// The directory with python module, contains `<module_name>/__init__.py`
pub python_source: Option<String>,
/// The directory containing the wheel data
pub data: Option<String>,
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ fn pep517(subcommand: Pep517Command) -> Result<()> {
metadata_directory,
strip,
} => {
assert!(build_options.interpreter.len() == 1);
assert_eq!(build_options.interpreter.len(), 1);
let context = build_options.into_build_context(true, strip, false)?;

// Since afaik all other PEP 517 backends also return linux tagged wheels, we do so too
Expand Down
Loading

0 comments on commit 5eb0fcb

Please sign in to comment.