Skip to content

Commit

Permalink
Merge pull request #543 from messense/musllinux
Browse files Browse the repository at this point in the history
Add PEP 656 musllinux support
  • Loading branch information
messense authored May 26, 2021
2 parents 83ef0bb + 3c851f7 commit 606db17
Showing 17 changed files with 268 additions and 213 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (for the cli, not for the crate).

## Unrelease

* Add PEP 656 musllinux support in [#543](https://github.com/PyO3/maturin/pull/543)

## 0.10.6 - 2021-05-21

* Fix corrupted macOS binary release in [#547](https://github.com/PyO3/maturin/pull/547)
42 changes: 24 additions & 18 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -238,18 +238,21 @@ OPTIONS:
Note that maturin invokes cargo twice: Once as `cargo metadata` and then as `cargo rustc`. maturin tries to
pass only the shared subset of options to cargo metadata, but this is may be a bit flaky.
-i, --interpreter <interpreter>...
The python versions to build wheels for, given as the names of the interpreters. Uses autodiscovery if not
explicitly set
--manylinux <manylinux>
Control the platform tag on linux. Options are `2010`/`2_12` (for manylinux2010), `2014`/`2_17` (for
manylinux2014), `2_24` (for manylinux_2_24), `2_27` (for manylinux_2_27) and `off` (for the native linux
tag). Note that manylinux1 is unsupported by the rust compiler. Wheels with the native `linux` tag will be
--compatibility <compatibility>
Control the platform tag on linux.
Options are `manylinux` tags (for example `manylinux2014`/`manylinux_2_24`) or `musllinux` tags (for example
`musllinux_1_2`) and `linux` for the native linux tag.
Note that `manylinux1` is unsupported by the rust compiler. Wheels with the native `linux` tag will be
rejected by pypi, unless they are separately validated by `auditwheel`.
The default is the lowest compatible, of plain `linux` if nothing matched
The default is the lowest compatible `manylinux` tag, or plain `linux` if nothing matched
This option is ignored on all non-linux platforms [possible values: 2010, 2014, 2_12, 2_17, 2_24, 2_27, off]
This option is ignored on all non-linux platforms
-i, --interpreter <interpreter>...
The python versions to build wheels for, given as the names of the interpreters. Uses autodiscovery if not
explicitly set
-o, --out <out>
The directory to store the built wheels in. Defaults to a new "wheels" directory in the project's target
directory
@@ -307,18 +310,21 @@ OPTIONS:
Note that maturin invokes cargo twice: Once as `cargo metadata` and then as `cargo rustc`. maturin tries to
pass only the shared subset of options to cargo metadata, but this is may be a bit flaky.
-i, --interpreter <interpreter>...
The python versions to build wheels for, given as the names of the interpreters. Uses autodiscovery if not
explicitly set
--manylinux <manylinux>
Control the platform tag on linux. Options are `2010`/`2_12` (for manylinux2010), `2014`/`2_17` (for
manylinux2014), `2_24` (for manylinux_2_24), `2_27` (for manylinux_2_27) and `off` (for the native linux
tag). Note that manylinux1 is unsupported by the rust compiler. Wheels with the native `linux` tag will be
--compatibility <compatibility>
Control the platform tag on linux.
Options are `manylinux` tags (for example `manylinux2014`/`manylinux_2_24`) or `musllinux` tags (for example
`musllinux_1_2`) and `linux` for the native linux tag.
Note that `manylinux1` is unsupported by the rust compiler. Wheels with the native `linux` tag will be
rejected by pypi, unless they are separately validated by `auditwheel`.
The default is the lowest compatible, of plain `linux` if nothing matched
The default is the lowest compatible `manylinux` tag, or plain `linux` if nothing matched
This option is ignored on all non-linux platforms [possible values: 2010, 2014, 2_12, 2_17, 2_24, 2_27, off]
This option is ignored on all non-linux platforms
-i, --interpreter <interpreter>...
The python versions to build wheels for, given as the names of the interpreters. Uses autodiscovery if not
explicitly set
-o, --out <out>
The directory to store the built wheels in. Defaults to a new "wheels" directory in the project's target
directory
1 change: 1 addition & 0 deletions maturin/__init__.py
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
available_options = [
"bindings",
"cargo-extra-args",
"compatibility",
"manylinux",
"rustc-extra-args",
"skip-auditwheel",
65 changes: 37 additions & 28 deletions src/auditwheel/audit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::policy::{Policy, POLICIES};
use crate::auditwheel::Manylinux;
use crate::auditwheel::PlatformTag;
use crate::Target;
use anyhow::Result;
use fs_err::File;
@@ -13,33 +13,31 @@ use std::io::Read;
use std::path::Path;
use thiserror::Error;

/// Error raised during auditing an elf file for manylinux compatibility
/// Error raised during auditing an elf file for manylinux/musllinux compatibility
#[derive(Error, Debug)]
#[error("Ensuring manylinux compliance failed")]
#[error("Ensuring manylinux/musllinux compliance failed")]
pub enum AuditWheelError {
/// The wheel couldn't be read
#[error("Failed to read the wheel")]
IoError(#[source] io::Error),
/// Reexports goblin parsing errors
#[error("Goblin failed to parse the elf file")]
GoblinError(#[source] goblin::error::Error),
/// The elf file isn't manylinux compatible. Contains the list of offending
/// The elf file isn't manylinux/musllinux compatible. Contains the list of offending
/// libraries.
#[error(
"Your library links libpython ({0}), which libraries must not do. Have you forgotten to activate the extension-module feature?",
)]
LinksLibPythonError(String),
/// The elf file isn't manylinux compatible. Contains the list of offending
/// The elf file isn't manylinux/musllinux compatible. Contains the list of offending
/// libraries.
#[error(
"Your library is not {0} compliant because it links the following forbidden libraries: {1:?}",
)]
ManylinuxValidationError(Policy, Vec<String>),
/// The elf file isn't manylinux compaible. Contains unsupported architecture
#[error(
"Your library is not manylinux compliant because it has unsupported architecture: {0}"
)]
UnsupportedArchitecture(String),
PlatformTagValidationError(Policy, Vec<String>),
/// The elf file isn't manylinux/musllinux compaible. Contains unsupported architecture
#[error("Your library is not {0} compliant because it has unsupported architecture: {1}")]
UnsupportedArchitecture(Policy, String),
}

/// Structure of "version needed" entries is documented in
@@ -172,10 +170,9 @@ fn policy_is_satisfied(
deps: &[String],
versioned_libraries: &[VersionedLibrary],
) -> Result<(), AuditWheelError> {
let arch_versions = &policy
.symbol_versions
.get(arch)
.ok_or_else(|| AuditWheelError::UnsupportedArchitecture(arch.to_string()))?;
let arch_versions = &policy.symbol_versions.get(arch).ok_or_else(|| {
AuditWheelError::UnsupportedArchitecture(policy.clone(), arch.to_string())
})?;
let mut offenders = HashSet::new();
for dep in deps {
// Skip dynamic linker/loader
@@ -238,31 +235,43 @@ fn policy_is_satisfied(
[lib] if is_libpython.is_match(lib) => {
Err(AuditWheelError::LinksLibPythonError(lib.clone()))
}
offenders => Err(AuditWheelError::ManylinuxValidationError(
offenders => Err(AuditWheelError::PlatformTagValidationError(
policy.clone(),
offenders.to_vec(),
)),
}
}

/// An reimplementation of auditwheel, which checks elf files for
/// manylinux compliance.
/// manylinux/musllinux compliance.
///
/// If `manylinux`, is None, it returns the the highest matching manylinux policy, or `linux`
/// If `platform_tag`, is None, it returns the the highest matching manylinux/musllinux policy, or `linux`
/// if nothing else matches. It will error for bogus cases, e.g. if libpython is linked.
///
/// If a specific manylinux version is given, compliance is checked and a warning printed if
/// If a specific manylinux/musllinux version is given, compliance is checked and a warning printed if
/// a higher version would be possible.
///
/// Does nothing for manylinux set to off or non-linux platforms.
/// Does nothing for `platform_tag` set to `Off`/`Linux` or non-linux platforms.
pub fn auditwheel_rs(
path: &Path,
target: &Target,
manylinux: Option<Manylinux>,
platform_tag: Option<PlatformTag>,
) -> Result<Policy, AuditWheelError> {
if !target.is_linux() || manylinux == Some(Manylinux::Off) {
if !target.is_linux() || platform_tag == Some(PlatformTag::Linux) {
return Ok(Policy::default());
}
if let Some(musl_tag @ PlatformTag::Musllinux { .. }) = platform_tag {
// TODO: add support for musllinux: https://github.com/pypa/auditwheel/issues/305
eprintln!("⚠ Warning: no auditwheel support for musllinux yet");
// HACK: fake a musllinux policy
return Ok(Policy {
name: musl_tag.to_string(),
aliases: Vec::new(),
priority: 0,
symbol_versions: Default::default(),
lib_whitelist: Default::default(),
});
}
let arch = target.target_arch().to_string();
let mut file = File::open(path).map_err(AuditWheelError::IoError)?;
let mut buffer = Vec::new();
@@ -273,7 +282,7 @@ pub fn auditwheel_rs(
let deps: Vec<String> = elf.libraries.iter().map(ToString::to_string).collect();
let versioned_libraries = find_versioned_libraries(&elf, &buffer)?;

// Find the highest possible manylinux policy, if any
// Find the highest possible policy, if any
let mut highest_policy = None;
for policy in POLICIES.iter() {
let result = policy_is_satisfied(&policy, &elf, &arch, &deps, &versioned_libraries);
@@ -283,16 +292,16 @@ pub fn auditwheel_rs(
break;
}
// UnsupportedArchitecture happens when trying 2010 with aarch64
Err(AuditWheelError::ManylinuxValidationError(_, _))
| Err(AuditWheelError::UnsupportedArchitecture(_)) => continue,
Err(AuditWheelError::PlatformTagValidationError(_, _))
| Err(AuditWheelError::UnsupportedArchitecture(..)) => continue,
// If there was an error parsing the symbols or libpython was linked,
// we error no matter what the requested policy was
Err(err) => return Err(err),
}
}

if let Some(manylinux) = manylinux {
let policy = Policy::from_name(&manylinux.to_string()).unwrap();
if let Some(platform_tag) = platform_tag {
let policy = Policy::from_name(&platform_tag.to_string()).unwrap();

if let Some(highest_policy) = highest_policy {
if policy.priority < highest_policy.priority {
@@ -312,7 +321,7 @@ pub fn auditwheel_rs(
Ok(policy)
} else {
println!(
"⚠ Warning: No compatible manylinux tag found, using the linux tag instead. \
"⚠ Warning: No compatible platform tag found, using the linux tag instead. \
You won't be able to upload those wheels to pypi."
);

88 changes: 0 additions & 88 deletions src/auditwheel/manylinux.rs

This file was deleted.

4 changes: 2 additions & 2 deletions src/auditwheel/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod audit;
mod manylinux;
mod platform_tag;
mod policy;

pub use self::audit::*;
pub use manylinux::Manylinux;
pub use platform_tag::PlatformTag;
pub use policy::{Policy, POLICIES};
Loading

0 comments on commit 606db17

Please sign in to comment.