Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion server/pypi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ these can be installed using your distribution. Some of them have special entrie
[here](https://github.com/mzakharo/android-gfortran/releases/tag/r21e). Create a
`fortran` subdirectory in the same directory as this README, and unpack the .bz2 files
into it.
* `rust`: `rustup` must be on the PATH.
* `rust`: `rustup` must be on the PATH. One can set `PYO3_NO_PYTHON=1` in `script_env:` to build without a Python interpreter (https://pyo3.rs/main/building-and-distribution#building-abi3-extensions-without-a-python-interpreter).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap at 88 columns.

Building with a Python interpreter is supported only for Python 3.13 and above.


## Building a package
Expand Down
44 changes: 27 additions & 17 deletions server/pypi/build-wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def unpack_and_build(self):
self.src_dir = f"{self.build_dir}/src"
self.build_env = f"{self.build_dir}/env"
self.host_env = f"{self.build_dir}/requirements"
self.chaquopy_dir = f"{self.host_env}/chaquopy"

if self.no_unpack:
log("Reusing existing build directory due to --no-unpack")
Expand Down Expand Up @@ -249,6 +250,10 @@ def version_key(ver):
if len(zips) != 1:
raise CommandError(f"Found {len(zips)} {self.abi} ZIPs in {target_version_dir}")
self.target_zip = zips[0]
zips = glob(f"{target_version_dir}/target-*-stdlib.zip")
if len(zips) != 1:
raise CommandError(f"Found {len(zips)} stdlib ZIPs in {target_version_dir}")
self.std_lib_zip = zips[0]

def create_build_env(self):
python_ver = self.python or ".".join(map(str, sys.version_info[:2]))
Expand Down Expand Up @@ -502,9 +507,11 @@ def create_dummy_libs(self):
for name in ["pthread", "rt"]:
run(f"{os.environ['AR']} rc {self.host_env}/chaquopy/lib/lib{name}.a")

def extract_stdlib(self):
run(f"unzip -q -d {self.chaquopy_dir}/lib {self.std_lib_zip}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly under lib is the wrong place for the Python standard library; it should be under lib/python3.13, for example. So are you sure this is actually having any effect? Maybe the build would work fine without it.

If it's really necessary, then please do the following:

  • Change std_lib to stdlib, which is the more conventional spelling.
  • Merge extract_stdlib into extract_target, because making it a separate function is taking up more space for no benefit.


def extract_target(self):
chaquopy_dir = f"{self.host_env}/chaquopy"
run(f"unzip -q -d {chaquopy_dir} {self.target_zip} include/* jniLibs/*")
run(f"unzip -q -d {self.chaquopy_dir} {self.target_zip} include/* jniLibs/*")

# Only include OpenSSL and SQLite in the environment if the recipe requests
# them. This affects grpcio, which provides its own copy of BoringSSL, and gets
Expand All @@ -517,7 +524,7 @@ def extract_target(self):
self.meta["requirements"]["host"].remove(name)
except ValueError:
for pattern in [f"include/{includes}", f"jniLibs/{self.abi}/{libs}"]:
run(f"rm -r {chaquopy_dir}/{pattern}", shell=True)
run(f"rm -r {self.chaquopy_dir}/{pattern}", shell=True)

# When building packages that could be loaded by older versions of Chaquopy,
# i.e non-Python packages, and Python packages for 3.12 and older:
Expand All @@ -533,7 +540,7 @@ def extract_target(self):
#
# When building only for Python 3.13 and newer, we should use the _python suffix
# so our wheels are compatible with any other Python on Android distributions.
lib_dir = f"{chaquopy_dir}/jniLibs/{self.abi}"
lib_dir = f"{self.chaquopy_dir}/jniLibs/{self.abi}"
for name in ["crypto", "ssl", "sqlite3"]:
python_name = f"lib{name}_python.so"
chaquopy_name = f"lib{name}_chaquopy.so"
Expand All @@ -547,8 +554,10 @@ def extract_target(self):
run(f"patchelf --set-soname {chaquopy_name} "
f"{lib_dir}/{chaquopy_name}")

run(f"mv {chaquopy_dir}/jniLibs/{self.abi}/* {chaquopy_dir}/lib", shell=True)
run(f"rm -r {chaquopy_dir}/jniLibs")
run(f"mv {self.chaquopy_dir}/jniLibs/{self.abi}/* {self.chaquopy_dir}/lib", shell=True)
run(f"rm -r {self.chaquopy_dir}/jniLibs")

self.extract_stdlib()

def build_with_script(self, build_script):
prefix_dir = f"{self.build_dir}/prefix"
Expand Down Expand Up @@ -659,18 +668,18 @@ def get_rust_env_vars(self, env):
env.update({
"RUSTFLAGS": f"-C linker={env['CC']} -L native={self.host_env}/chaquopy/lib",
"CARGO_BUILD_TARGET": tool_prefix,

# Normally PyO3 requires sysconfig modules, which are not currently
# available in the `target` packages for Python 3.12 and older. However,
# since PyO3 0.16.4, it's possible to compile abi3 modules without sysconfig
# modules. This only requires packages to specify the minimum python
# compatibility version via one of the "abi3-py*" features (e.g.
# abi3-py310). Doing this requires the "-L native" flag in RUSTFLAGS above.
# https://pyo3.rs/main/building-and-distribution#building-abi3-extensions-without-a-python-interpreter
"PYO3_NO_PYTHON": "1",
"PYO3_CROSS": "1",
"PYO3_CROSS_PYTHON_VERSION": self.python,
})
# Normally PyO3 requires sysconfig modules, which are not currently
# available in the `target` packages for Python 3.12 and older, but are available on Python 3.13.
# However, since PyO3 0.16.4, it's possible to compile abi3 modules without sysconfig
Comment on lines +675 to +676
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap at 88 columns.

# modules. This only requires packages to specify the minimum python
# compatibility version via one of the "abi3-py*" features (e.g.
# abi3-py310). Doing this requires the "-L native" flag in RUSTFLAGS above.
# https://pyo3.rs/main/building-and-distribution#building-abi3-extensions-without-a-python-interpreter
if 'PYO3_NO_PYTHON' not in env:
env["PYO3_CROSS_LIB_DIR"] = self.chaquopy_dir

@contextmanager
def env_vars(self):
Expand All @@ -686,8 +695,6 @@ def env_vars(self):

if self.needs_python:
self.get_python_env_vars(env, pypi_env)
if "rust" in self.non_python_build_reqs:
self.get_rust_env_vars(env)

env.update({
# TODO: make everything use HOST instead, and remove this.
Expand All @@ -707,6 +714,9 @@ def env_vars(self):
key, value = var.split("=", 1)
env[key] = value

if "rust" in self.non_python_build_reqs:
self.get_rust_env_vars(env)

Comment on lines +717 to +719
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment explaining why this needs to be done after script_env.

# We do this unconditionally, because we don't know whether the package requires
# CMake (it may be listed in the pyproject.toml build-system requires).
self.generate_cmake_toolchain(env)
Expand Down
4 changes: 4 additions & 0 deletions server/pypi/packages/cryptography/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package:
name: cryptography
version: "42.0.8"

build:
script_env:
- PYO3_NO_PYTHON=1

Comment on lines +5 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the cryptography changes from this PR; we can do that in #1375.

requirements:
build:
- rust
Expand Down
9 changes: 9 additions & 0 deletions server/pypi/packages/pydantic-core/meta.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package:
name: pydantic-core
version: "2.41.4"

requirements:
build:
- rust
host:
- python
Comment on lines +8 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

host: python is unnecessary, as this is the default for all packages whose source is downloaded from PyPI.