diff --git a/server/pypi/README.md b/server/pypi/README.md index 184f440d0b..3bbdef2752 100644 --- a/server/pypi/README.md +++ b/server/pypi/README.md @@ -63,7 +63,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 02d198700f..65ee00e2d9 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])) @@ -507,9 +512,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 @@ -522,7 +529,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: @@ -538,7 +545,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" @@ -552,8 +559,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" @@ -672,7 +681,8 @@ def get_rust_env_vars(self, env): # 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_NO_PYTHON": "1", + "PYO3_CROSS_LIB_DIR": self.chaquopy_dir, "PYO3_CROSS": "1", "PYO3_CROSS_PYTHON_VERSION": self.python, }) @@ -691,8 +701,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. @@ -712,6 +720,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..18eefa21ad --- /dev/null +++ b/server/pypi/packages/pydantic-core/meta.yaml @@ -0,0 +1,9 @@ +package: + name: pydantic-core + version: "2.41.5" + +requirements: + build: + - rust + host: + - python