diff --git a/server/pypi/README.md b/server/pypi/README.md index 6fe44262af..996c57e860 100644 --- a/server/pypi/README.md +++ b/server/pypi/README.md @@ -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). + Building with a Python interpreter is supported only for Python 3.13 and above. ## Building a package diff --git a/server/pypi/build-wheel.py b/server/pypi/build-wheel.py index c3f15b7e2d..d9f4225ad2 100755 --- a/server/pypi/build-wheel.py +++ b/server/pypi/build-wheel.py @@ -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") @@ -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])) @@ -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}") + 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 @@ -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: @@ -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" @@ -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" @@ -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 + # 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): @@ -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. @@ -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) + # 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) diff --git a/server/pypi/packages/cryptography/meta.yaml b/server/pypi/packages/cryptography/meta.yaml index 09e89d5a17..d626cd9d0b 100644 --- a/server/pypi/packages/cryptography/meta.yaml +++ b/server/pypi/packages/cryptography/meta.yaml @@ -2,6 +2,10 @@ package: name: cryptography version: "42.0.8" +build: + script_env: + - PYO3_NO_PYTHON=1 + requirements: build: - rust diff --git a/server/pypi/packages/pydantic-core/meta.yaml b/server/pypi/packages/pydantic-core/meta.yaml new file mode 100644 index 0000000000..4eb21d93b2 --- /dev/null +++ b/server/pypi/packages/pydantic-core/meta.yaml @@ -0,0 +1,9 @@ +package: + name: pydantic-core + version: "2.41.4" + +requirements: + build: + - rust + host: + - python