Skip to content

Commit a23a6b5

Browse files
fix: platform.system() on Android for Python 3.13+ (#2992)
Python from 3.13 onwards supports Android as an official platform. As a part of this official support, there were a bunch of changes including `platform.system()` returning "Android" and `sys.platform` being "android" instead of "Linux" and "linux" on earlier versions. This is a breaking change. Also try to be backwards compatible while running on Android for older python versions in environments like Termux This maturin bug was found as a part of testing of builds of python packages for Termux as part of Python 3.13 migration. Downstream Python distribution update PR: termux/termux-packages#27739 Ideally we would like to ensure that get_python_os() returns "linux" for Python versions <= 3.12, but that'd require additional work in supplying python version to get_python_os(), but that'd complicate some things. This works well enough, and shouldn't be necessary in future when Python >= 3.13 becomes the standard target by most (which should ideally happen soon since upstream Python is supporting Android from this version). My analysis of maturin reveal that get_python_os() returning "android" shouldn't cause any side effects for Python <= 3.12 on Android. Issue also reported to maturin in #2945 Partially based of off work by @robertkirkman in the linked issue. Further fixup was done to ensure suitability for upstream acceptance. Ref: python/cpython#116215 CC @mhsmith Android support for Python
1 parent 5e31a16 commit a23a6b5

File tree

4 files changed

+85
-54
lines changed

4 files changed

+85
-54
lines changed

src/python_interpreter/config.rs

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -372,57 +372,59 @@ impl InterpreterConfig {
372372
}),
373373
};
374374
let file_ext = if target.is_windows() { "pyd" } else { "so" };
375-
let ext_suffix = if target.is_linux() || target.is_macos() || target.is_hurd() {
376-
let target_env = target.get_python_target_env(interpreter_kind, (major, minor));
377-
match interpreter_kind {
378-
InterpreterKind::CPython => ext_suffix.unwrap_or_else(|| {
379-
// Eg: .cpython-38-x86_64-linux-gnu.so
380-
format!(
381-
".cpython-{}-{}-{}-{}.{}",
382-
abi_tag,
383-
target.get_python_ext_arch(interpreter_kind),
384-
target.get_python_os(),
385-
target_env,
386-
file_ext,
387-
)
388-
}),
389-
InterpreterKind::PyPy => ext_suffix.unwrap_or_else(|| {
390-
// Eg: .pypy38-pp73-x86_64-linux-gnu.so
375+
let ext_suffix =
376+
if target.is_linux() || target.is_android() || target.is_macos() || target.is_hurd() {
377+
let target_env = target.get_python_target_env(interpreter_kind, (major, minor));
378+
match interpreter_kind {
379+
InterpreterKind::CPython => ext_suffix.unwrap_or_else(|| {
380+
// Eg: .cpython-38-x86_64-linux-gnu.so
381+
format!(
382+
".cpython-{}-{}-{}-{}.{}",
383+
abi_tag,
384+
target.get_python_ext_arch(interpreter_kind),
385+
target.get_python_os(),
386+
target_env,
387+
file_ext,
388+
)
389+
}),
390+
InterpreterKind::PyPy => ext_suffix.unwrap_or_else(|| {
391+
// Eg: .pypy38-pp73-x86_64-linux-gnu.so
392+
format!(
393+
".pypy{}{}-{}-{}-{}-{}.{}",
394+
major,
395+
minor,
396+
abi_tag,
397+
target.get_python_ext_arch(interpreter_kind),
398+
target.get_python_os(),
399+
target_env,
400+
file_ext,
401+
)
402+
}),
403+
InterpreterKind::GraalPy => ext_suffix.unwrap_or_else(|| {
404+
// e.g. .graalpy230-310-native-x86_64-linux.so
405+
format!(
406+
".{}-{}-{}.{}",
407+
abi_tag.replace('_', "-"),
408+
target.get_python_ext_arch(interpreter_kind),
409+
target.get_python_os(),
410+
file_ext,
411+
)
412+
}),
413+
}
414+
} else if target.is_emscripten() && matches!(interpreter_kind, InterpreterKind::CPython)
415+
{
416+
ext_suffix.unwrap_or_else(|| {
391417
format!(
392-
".pypy{}{}-{}-{}-{}-{}.{}",
393-
major,
394-
minor,
418+
".cpython-{}-{}-{}.{}",
395419
abi_tag,
396420
target.get_python_ext_arch(interpreter_kind),
397421
target.get_python_os(),
398-
target_env,
399-
file_ext,
422+
file_ext
400423
)
401-
}),
402-
InterpreterKind::GraalPy => ext_suffix.unwrap_or_else(|| {
403-
// e.g. .graalpy230-310-native-x86_64-linux.so
404-
format!(
405-
".{}-{}-{}.{}",
406-
abi_tag.replace('_', "-"),
407-
target.get_python_ext_arch(interpreter_kind),
408-
target.get_python_os(),
409-
file_ext,
410-
)
411-
}),
412-
}
413-
} else if target.is_emscripten() && matches!(interpreter_kind, InterpreterKind::CPython) {
414-
ext_suffix.unwrap_or_else(|| {
415-
format!(
416-
".cpython-{}-{}-{}.{}",
417-
abi_tag,
418-
target.get_python_ext_arch(interpreter_kind),
419-
target.get_python_os(),
420-
file_ext
421-
)
422-
})
423-
} else {
424-
ext_suffix.context("missing value for ext_suffix")?
425-
};
424+
})
425+
} else {
426+
ext_suffix.context("missing value for ext_suffix")?
427+
};
426428
let gil_disabled = build_flags
427429
.map(|flags| flags.contains("Py_GIL_DISABLED"))
428430
.unwrap_or(false);

src/python_interpreter/mod.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,20 @@ fn fun_with_abiflags(
295295
&& !(target.get_python_os() == "cygwin"
296296
&& message.system.to_lowercase().starts_with("cygwin"))
297297
{
298-
bail!(
299-
"platform.system() in python, {}, and the rust target, {:?}, don't match ಠ_ಠ",
300-
message.system,
301-
target,
302-
)
298+
// Python versions <= 3.12 used to report platform.system() as "linux". Only on Python versions
299+
// >= 3.13, platform.system() reports as "android". So maintain backwards compatibility with
300+
// Python 3.12 when compiling on Android environment (for e.g. Termux)
301+
let is_android_compat = target.get_python_os() == "android"
302+
&& message.system == "linux"
303+
&& message.major == 3
304+
&& message.minor <= 12;
305+
if !is_android_compat {
306+
bail!(
307+
"platform.system() in python, {}, and the rust target, {:?}, don't match ಠ_ಠ",
308+
message.system,
309+
target,
310+
)
311+
}
303312
}
304313

305314
if message.major != 3 || message.minor < 7 {

src/target/mod.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub enum Os {
4545
Aix,
4646
Hurd,
4747
Cygwin,
48+
Android,
4849
}
4950

5051
impl fmt::Display for Os {
@@ -66,6 +67,7 @@ impl fmt::Display for Os {
6667
Os::Aix => write!(f, "AIX"),
6768
Os::Hurd => write!(f, "Hurd"),
6869
Os::Cygwin => write!(f, "Cygwin"),
70+
Os::Android => write!(f, "Android"),
6971
}
7072
}
7173
}
@@ -161,6 +163,14 @@ impl Arch {
161163
// Returns the set of supported architectures for each operating system
162164
fn get_supported_architectures(os: &Os) -> Vec<Arch> {
163165
match os {
166+
Os::Android => vec![
167+
Arch::Aarch64,
168+
Arch::Armv5teL,
169+
Arch::Armv6L,
170+
Arch::Armv7L,
171+
Arch::X86,
172+
Arch::X86_64,
173+
],
164174
Os::Linux => vec![
165175
Arch::Aarch64,
166176
Arch::Armv5teL,
@@ -278,7 +288,10 @@ impl Target {
278288
.map_err(|_| format_err!("Unknown target triple {}", target_triple))?;
279289

280290
let os = match platform.operating_system {
281-
OperatingSystem::Linux => Os::Linux,
291+
OperatingSystem::Linux => match platform.environment {
292+
Environment::Android | Environment::Androideabi => Os::Android,
293+
_ => Os::Linux,
294+
},
282295
OperatingSystem::Windows => Os::Windows,
283296
OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_) => Os::Macos,
284297
OperatingSystem::IOS(_) => Os::Ios,
@@ -458,6 +471,7 @@ impl Target {
458471
/// Returns the name python uses in `sys.platform` for this os
459472
pub fn get_python_os(&self) -> &str {
460473
match self.os {
474+
Os::Android => "android",
461475
Os::Windows => "windows",
462476
Os::Linux => "linux",
463477
Os::Macos => "darwin",
@@ -571,7 +585,8 @@ impl Target {
571585
| Os::Wasi
572586
| Os::Aix
573587
| Os::Hurd
574-
| Os::Cygwin => true,
588+
| Os::Cygwin
589+
| Os::Android => true,
575590
}
576591
}
577592

@@ -671,6 +686,11 @@ impl Target {
671686
self.os == Os::Aix
672687
}
673688

689+
/// Returns true if we're building a binary for Android
690+
pub fn is_android(&self) -> bool {
691+
self.os == Os::Android
692+
}
693+
674694
/// Returns true if the current platform's target env is Musl
675695
#[inline]
676696
pub fn is_musl_libc(&self) -> bool {

src/target/pypi_tags.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub fn is_arch_supported_by_pypi(target: &Target) -> bool {
5151
// PyPI allows iOS with arm64 and x86_64 (simulator)
5252
matches!(normalized_arch, "arm64" | "x86_64")
5353
}
54-
Os::Linux if target.target_triple().contains("android") => {
54+
Os::Android => {
5555
// Android target triples map to specific platform tag architectures
5656
let android_arch = match arch.as_str() {
5757
"armv7l" => "armeabi_v7a", // armv7 little-endian

0 commit comments

Comments
 (0)