This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch jenkins-wheel-test-artifacts
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/jenkins-wheel-test-artifacts
by this push:
new d0b4f744e5 [CI] Install wheel consumers into the checkout Python target
d0b4f744e5 is described below
commit d0b4f744e50cf5b65e60355bd07dd37260df1ba2
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Oct 15 15:58:14 2026 +0000
[CI] Install wheel consumers into the checkout Python target
---
ci/jenkins/README.md | 26 +++++----
tests/scripts/task_python_install_wheels.sh | 91 +++++------------------------
2 files changed, 30 insertions(+), 87 deletions(-)
diff --git a/ci/jenkins/README.md b/ci/jenkins/README.md
index dbb68032b7..081650703f 100644
--- a/ci/jenkins/README.md
+++ b/ci/jenkins/README.md
@@ -132,8 +132,8 @@ existing platform CMake configuration. `uv build` does not
resolve runtime depen
requirements. CPU, ARM and Wasm host builds request `all;cpptest`
and run C++ tests in that same native tree before uploading anything. Python
consumers install the exact TVM and FFI wheels with `--no-deps --offline`,
clear
-checkout path overrides, and verify artifact hashes and package/native-library
-paths. Only `build/wheels/*.whl` is uploaded. GPU Java consumers discover
headers,
+checkout path overrides, and verify package/native-library paths. Only
+`build/wheels/*.whl` is uploaded. GPU Java consumers discover headers,
linker paths and packaged native libraries through `tvm.config` and
`tvm_ffi.config`; no separate native files are transferred. The configuration
commands avoid loading TVM's Python compiler, backends or native libraries.
@@ -149,16 +149,18 @@ modifying the image environment or exposing the build
overlay to test consumers.
`uv build` then runs offline without isolation.
The selected images must already provide compatible runtime and test
dependencies,
-apart from core FFI and its ORCJIT addon. Consumers install the explicit
-`apache-tvm-ffi-orcjit==0.1.2 --no-deps` prerequisite used by legacy Python
tests,
-then install exactly one transferred wheel for each of TVM and FFI with
-`--no-deps --offline`. Consumers do not compile FFI.
-The checkout remains available for tests and resources through `TVM_HOME`,
including
-Wasm assets under `web/dist`, without adding its Python package ahead of the
-wheel. Provenance checks bind installed distributions to the transferred wheel
-filenames and the loaded files' SHA-256 hashes from each wheel RECORD, and
verify
-TVM/FFI Python and native libraries load
-from the test environment. Other missing runtime dependencies remain errors.
+apart from core FFI and its ORCJIT addon. Consumers use the image's existing
Python
+and install into the checkout-local `.local/python` directory with `uv pip
--target`.
+They install the explicit `apache-tvm-ffi-orcjit==0.1.2 --no-deps`
prerequisite,
+then exactly one transferred TVM wheel and one FFI wheel with `--no-deps
--offline`
+and `--reinstall`. Repeated setup replaces these packages in the same writable
target.
+No additional virtualenv or consumer-side FFI compilation is needed.
+
+`PYTHONPATH` points to the absolute target directory, keeping image
dependencies
+available while preventing checkout imports from shadowing the wheels. A short
+smoke check verifies that the loaded TVM/FFI Python and native libraries come
from
+that target. `TVM_HOME` retains checkout resources, including Wasm assets under
+`web/dist`. The generated `.local` directory is ignored by Git.
`CMAKE_BUILD_PARALLEL_LEVEL` is passed through Docker as an environment
variable.
An explicit value wins; otherwise builds use at least one CPU and divide the
diff --git a/tests/scripts/task_python_install_wheels.sh
b/tests/scripts/task_python_install_wheels.sh
index 6791f36780..b0ff9e9851 100644
--- a/tests/scripts/task_python_install_wheels.sh
+++ b/tests/scripts/task_python_install_wheels.sh
@@ -16,25 +16,14 @@
# specific language governing permissions and limitations
# under the License.
-# Source this file so subsequent Python invocations use the wheel environment.
-# Docker defaults PYTHONPATH to the checkout; never let it shadow the
artifacts.
+# Source this file so subsequent Python invocations use the installed wheels.
+# Docker defaults PYTHONPATH to the checkout; replace it with the explicit
target.
unset PYTHONPATH TVM_LIBRARY_PATH TVM_FFI_LIBRARY_PATH
export PYTHONNOUSERSITE=1
-# Installed TVM still needs checkout resources such as web/dist for Wasm
linking.
export TVM_HOME="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
-export TVM_WHEEL_DIR="$(realpath "${TVM_WHEEL_DIR:-$(pwd)/build/wheels}")"
-# Preserve the image's active environment (often itself a venv), including when
-# local ci.py sources this setup again for a named test suite.
-export TVM_IMAGE_PYTHON="${TVM_IMAGE_PYTHON:-$(command -v python3)}"
-export
TVM_IMAGE_SITE_PACKAGES="${TVM_IMAGE_SITE_PACKAGES:-$("$TVM_IMAGE_PYTHON" -I -c
'import site; print("\n".join(site.getsitepackages()))')}"
-export VIRTUAL_ENV="${TVM_WHEEL_DIR%/}/../wheel-venv"
-uv venv --offline --python "$TVM_IMAGE_PYTHON" --clear "$VIRTUAL_ENV"
-# Append the image dependencies after our wheels without executing its .pth
files:
-# system-site-packages can install editable-import hooks ahead of the wheel.
-TVM_WHEEL_SITE="$("$VIRTUAL_ENV/bin/python3" -c 'import sysconfig;
print(sysconfig.get_path("purelib"))')"
-printf '%s\n' "$TVM_IMAGE_SITE_PACKAGES" >
"$TVM_WHEEL_SITE/image-dependencies.pth"
-export PATH="$VIRTUAL_ENV/bin:$PATH"
-# Require one artifact for each distribution before creating an installation.
+TVM_WHEEL_DIR="$(realpath "${TVM_WHEEL_DIR:-$TVM_HOME/build/wheels}")"
+TVM_PYTHON_TARGET="$TVM_HOME/.local/python"
+
shopt -s nullglob
tvm_wheels=("$TVM_WHEEL_DIR"/apache_tvm-*.whl)
ffi_wheels=("$TVM_WHEEL_DIR"/apache_tvm_ffi-*.whl)
@@ -44,72 +33,24 @@ if [ "${#tvm_wheels[@]}" -ne 1 ] || [ "${#ffi_wheels[@]}"
-ne 1 ]; then
return 1
fi
# Match the explicit prerequisite in the legacy Python test setup.
-uv pip install --python "$VIRTUAL_ENV/bin/python3" --no-deps
apache-tvm-ffi-orcjit==0.1.2
-uv pip install --python "$VIRTUAL_ENV/bin/python3" --no-deps --offline
--reinstall \
+uv pip install --python python3 --target "$TVM_PYTHON_TARGET" --no-deps
apache-tvm-ffi-orcjit==0.1.2
+uv pip install --python python3 --target "$TVM_PYTHON_TARGET" --no-deps
--offline --reinstall \
"${tvm_wheels[0]}" "${ffi_wheels[0]}"
+export PYTHONPATH="$TVM_PYTHON_TARGET"
-# Fail before pytest if a source/editable install or native override leaked in.
+# Keep source resources through TVM_HOME, but load Python and native code from
the target.
python3 - <<'PYTHON'
-import base64
-import csv
-import hashlib
-import io
-import json
import os
-import sys
-from importlib.metadata import distribution, requires, version
from pathlib import Path
-from urllib.parse import unquote, urlparse
-from zipfile import ZipFile
-from packaging.requirements import Requirement
-
-# Core FFI comes from the build-stage wheel and ORCJIT is pinned explicitly.
-# Check the wheel requirements without invoking a resolver or hardcoding a
floor.
-for text in requires("apache-tvm") or []:
- requirement = Requirement(text)
- if requirement.marker and not requirement.marker.evaluate({"extra": ""}):
- continue
- installed = version(requirement.name)
- assert requirement.specifier.contains(installed, prereleases=True), (text,
installed)
-
import tvm
import tvm_ffi
-prefix = Path(sys.prefix).resolve()
-path = Path(tvm.__file__).resolve()
-assert path.is_relative_to(prefix), path
-print(f"Installed tvm: {path}")
-# Bind each distribution and its loaded Python/native files to the exact wheel.
-def verify_wheel(name, pattern, paths):
- wheel, = Path(os.environ["TVM_WHEEL_DIR"]).glob(pattern)
- dist = distribution(name)
- source = json.loads(dist.read_text("direct_url.json"))
- assert Path(unquote(urlparse(source["url"]).path)).resolve() ==
wheel.resolve(), source
- assert "archive_info" in source and "dir_info" not in source, source
- with ZipFile(wheel) as archive:
- record, = (name for name in archive.namelist() if
name.endswith(".dist-info/RECORD"))
- hashes = {row[0]: row[1] for row in
csv.reader(io.StringIO(archive.read(record).decode()))}
- for path in paths:
- path = Path(path).resolve()
- relative =
path.relative_to(Path(dist.locate_file("")).resolve()).as_posix()
- digest = hashlib.sha256()
- with path.open("rb") as stream:
- for chunk in iter(lambda: stream.read(1024 * 1024), b""):
- digest.update(chunk)
- actual = "sha256=" +
base64.urlsafe_b64encode(digest.digest()).decode().rstrip("=")
- assert actual == hashes[relative], (name, path)
- print(f"Installed {name} wheel: {wheel.name}")
-
-verify_wheel("apache-tvm", "apache_tvm-*.whl",
- [tvm.__file__, *(lib._name for lib in
tvm.base._LOADED_LIBS.values())])
-verify_wheel("apache-tvm-ffi", "apache_tvm_ffi-*.whl",
- [tvm_ffi.__file__, tvm_ffi.core.__file__, tvm_ffi.LIB._name])
-for path in (Path(tvm_ffi.__file__).resolve(),
Path(tvm_ffi.LIB._name).resolve()):
- assert path.is_relative_to(prefix), path
- print(f"Installed FFI: {path}")
+target = Path(os.environ["PYTHONPATH"]).resolve()
+paths = [tvm.__file__, tvm_ffi.__file__, tvm_ffi.core.__file__,
tvm_ffi.LIB._name]
assert {"tvm_runtime", "tvm_compiler"} <= tvm.base._LOADED_LIBS.keys()
-for name, library in tvm.base._LOADED_LIBS.items():
- path = Path(library._name).resolve()
- assert path.is_relative_to(prefix), (name, path)
- print(f"Installed {name}: {path}")
+paths.extend(library._name for library in tvm.base._LOADED_LIBS.values())
+for filename in paths:
+ path = Path(filename).resolve()
+ assert path.is_relative_to(target), path
+ print(f"Installed wheel artifact: {path}")
PYTHON