This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new d72d4b5af9 [CI] Build and consume source wheel artifacts across
Jenkins platforms (#20750)
d72d4b5af9 is described below
commit d72d4b5af9538dd3474b0d2bf7436cee2d677e95
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Oct 15 21:48:10 2026 -0400
[CI] Build and consume source wheel artifacts across Jenkins platforms
(#20750)
Build pinned-source FFI, TIRx and TVM wheels in Jenkins and transfer the
three packages through dist/. A single build-dependency script prepares
writable backend and Z3 prerequisites in .local/python; Groovy calls the
root platform build script.
Root platform scripts own the complete platform options and three direct
wheel builds in FFI, TIRx, TVM order. All three use the same root CMake
source/build tree, native settings and source version; fixed tvm_ffi,
tvm_tirx and tvm install components select each package's payload. Run
enabled C++ tests from that shared native tree. Retain standalone TIRx
platform scripts for future independent use; Wasm orchestration remains
in parent TVM.
Source the small wheel-installation script before test commands in
Groovy. It installs the three package-prefix globs offline without
dependency resolution into .local/python, keeps the pinned ORCJIT
prerequisite, and exports installed-package paths and source resources.
Python, docs, Java and Wasm entrypoints use that caller-provided
environment. Java discovers native artifacts through package
configuration, and relative library paths keep the separate packages
relocatable.
Temporarily bypass sccache in Jenkins with real GCC compilers and
cleared compiler launchers, retaining image tooling for later
restoration. Keep existing parallel bounds and Docker environment
forwarding. Preserve Jenkins retry behavior and optional GPU
shared-library packaging. Resolve OpenCL headers through the configured
SDK or pinned CMake FetchContent when OpenCL is enabled, replacing the
header submodule. Build macOS and Windows parent wheels against the
explicitly installed local TIRx artifact. Remove redundant CI/build
wrappers and document direct development commands.
---
.github/workflows/main.yml | 14 +-
.gitignore | 1 -
.gitmodules | 3 -
3rdparty/OpenCL-Headers | 1 -
CMakeLists.txt | 13 +-
LICENSE | 2 +-
apps/android_rpc/README.md | 3 +-
apps/android_rpc/app/src/main/jni/Android.mk | 7 +-
ci/README.md | 19 +
ci/jenkins/generated/arm_jenkinsfile.groovy | 32 +-
ci/jenkins/generated/common.groovy | 17 +-
ci/jenkins/generated/cpu_jenkinsfile.groovy | 41 +-
ci/jenkins/generated/gpu_jenkinsfile.groovy | 41 +-
ci/jenkins/generated/wasm_jenkinsfile.groovy | 38 +-
cmake/modules/contrib/CLML.cmake | 10 +-
docker/bash.sh | 8 +
docs/README.md | 102 +++-
docs/conf.py | 2 -
docs/contribute/pull_request.rst | 69 ++-
docs/contribute/testing.rst | 53 +-
jvm/README.md | 18 +-
.../linux-x86_64/src/main/assembly/assembly.xml | 7 +-
.../osx-x86_64/src/main/assembly/assembly.xml | 7 +-
jvm/core/pom.xml | 2 +-
jvm/native/linux-x86_64/pom.xml | 6 +-
jvm/native/osx-x86_64/pom.xml | 6 +-
jvm/pom.xml | 4 +
pyproject.toml | 4 +-
python/tvm/__init__.py | 32 +-
python/tvm/config.py | 97 +++
python/tvm/rpc/minrpc.py | 2 +
tests/python/runtime/rpc/test_minrpc_source.py | 51 ++
tests/python/runtime/test_config.py | 74 +++
tests/scripts/ci.py | 672 ---------------------
tests/scripts/task_build.py | 152 -----
tests/scripts/task_build_arm.sh | 48 ++
tests/scripts/task_build_cpu.sh | 54 ++
tests/scripts/task_build_gpu.sh | 56 ++
..._build_gpu_other.sh => task_build_gpu_other.sh} | 17 +-
tests/scripts/task_build_wasm.sh | 48 ++
tests/scripts/task_config_build_cpu.sh | 37 --
tests/scripts/task_config_build_gpu.sh | 43 --
...d_arm.sh => task_install_build_dependencies.sh} | 15 +-
...b_wasm.sh => task_install_docs_dependencies.sh} | 29 +-
tests/scripts/task_java_unittest.sh | 3 -
tests/scripts/task_jvm_build.sh | 21 +-
tests/scripts/task_python_docs.sh | 21 +-
tests/scripts/task_python_unittest.sh | 15 +-
tests/scripts/task_python_unittest_gpuonly.sh | 5 +-
tests/scripts/task_web_wasm.sh | 5 -
...eb_wasm.sh => test_task_install_dist_wheels.sh} | 22 +-
tirx/3rdparty/tvm-ffi | 2 +-
tirx/CMakeLists.txt | 7 +
tirx/cmake/modules/OpenCL.cmake | 13 +-
tirx/tests/python/runtime/test_tirx_config.py | 61 ++
.../tests/scripts/task_build_arm.sh | 16 +-
.../tests/scripts/task_build_cpu.sh | 21 +-
.../tests/scripts/task_build_gpu.sh | 26 +-
58 files changed, 941 insertions(+), 1254 deletions(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9f017ee51c..7624a81f4d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -56,12 +56,12 @@ jobs:
CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }}
run: |
set -euxo pipefail
- pip install scikit-build-core
+ pip install "scikit-build-core>=0.11" "setuptools-scm>=8"
export CMAKE_ARGS="-DTVM_TIRX_USE_LLVM=ON -DTVM_TIRX_USE_METAL=ON
-DTVM_TIRX_BUILD_TESTS=OFF"
pip wheel --no-deps -w dist ./tirx -v
- export PIP_FIND_LINKS="$(pwd)/dist"
+ pip install --no-deps --force-reinstall dist/apache_tvm_tirx-*.whl
export CMAKE_ARGS="-DBUILD_TESTING=OFF"
- pip wheel --no-deps -w dist . -v
+ pip wheel --no-deps --no-build-isolation -w dist . -v
- name: Install TIRx and TVM from wheels
shell: bash -l {0}
run: |
@@ -121,12 +121,14 @@ jobs:
env:
CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }}
run: |
- pip install scikit-build-core || exit /b 1
+ pip install "scikit-build-core>=0.11" "setuptools-scm>=8" || exit /b
1
set CMAKE_ARGS=-DTVM_TIRX_USE_LLVM=ON -DTVM_TIRX_BUILD_TESTS=OFF
pip wheel --no-deps -w dist ./tirx -v || exit /b 1
- set PIP_FIND_LINKS=%CD%\dist
+ for %%w in (dist\apache_tvm_tirx-*.whl) do (
+ pip install --no-deps --force-reinstall "%%w" || exit /b 1
+ )
set CMAKE_ARGS=-DBUILD_TESTING=OFF
- pip wheel --no-deps -w dist . -v || exit /b 1
+ pip wheel --no-deps --no-build-isolation -w dist . -v || exit /b 1
for %%w in (dist\*.whl) do (
pip install --no-deps "%%w" || exit /b 1
)
diff --git a/.gitignore b/.gitignore
index 0348a0a1b8..e5ea924a3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -258,7 +258,6 @@ conda/pkg
_docs/
jvm/target
.config/configstore/
-.ci-py-scripts/
# Local tvm-site checkout
diff --git a/.gitmodules b/.gitmodules
index 8a2a3ef81d..85109f24d2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,6 @@
[submodule "3rdparty/cutlass"]
path = 3rdparty/cutlass
url = https://github.com/NVIDIA/cutlass.git
-[submodule "3rdparty/OpenCL-Headers"]
- path = 3rdparty/OpenCL-Headers
- url = https://github.com/KhronosGroup/OpenCL-Headers.git
[submodule "3rdparty/cutlass_fpA_intB_gemm"]
path = 3rdparty/cutlass_fpA_intB_gemm
url = https://github.com/tlc-pack/cutlass_fpA_intB_gemm
diff --git a/3rdparty/OpenCL-Headers b/3rdparty/OpenCL-Headers
deleted file mode 160000
index b590a6bfe0..0000000000
--- a/3rdparty/OpenCL-Headers
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit b590a6bfe034ea3a418b7b523e3490956bcb367a
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 101c1507bd..d0bcae3f1b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -91,9 +91,6 @@ if(TVM_USE_INSTALLED_TIRX)
message(FATAL_ERROR "TVM requires apache-tvm-tirx 0.1.0a0")
endif()
else()
- if(NOT TVM_OPENCL_HEADERS_ROOT AND EXISTS
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/OpenCL-Headers/CL/cl.h")
- set(TVM_OPENCL_HEADERS_ROOT
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/OpenCL-Headers")
- endif()
# Keep source-built core libraries beside the parent libraries.
set(_tirx_output_defaults "")
foreach(_kind ARCHIVE LIBRARY RUNTIME)
@@ -332,6 +329,13 @@ foreach(_target tvm_compiler tvm_runtime_extra)
elseif(UNIX)
set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH
"\$ORIGIN/../../tvm_tirx/lib")
endif()
+ if(TVM_BUILD_PYTHON_MODULE)
+ if(APPLE)
+ set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH
"@loader_path/../../tvm_ffi/lib")
+ elseif(UNIX)
+ set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH
"\$ORIGIN/../../tvm_ffi/lib")
+ endif()
+ endif()
tvm_ffi_add_apple_dsymutil(${_target})
endforeach()
add_subdirectory(tests/cpp)
@@ -364,6 +368,9 @@ install(TARGETS tvm_compiler tvm_runtime_extra
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT tvm
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT tvm)
if(TVM_TIRX_USE_CUDA AND USE_CUTLASS)
+ foreach(_target fpA_intB_gemm flash_attn)
+ tvm_configure_target_library(${_target})
+ endforeach()
install(TARGETS fpA_intB_gemm flash_attn
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT tvm
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT tvm
diff --git a/LICENSE b/LICENSE
index 627aef16ea..1ce63e056a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -207,7 +207,7 @@ respective component directories for their licenses.
Apache Software Foundation License 2.0
--------------------------------------
-3rdparty/OpenCL-Headers
+OpenCL-Headers (downloaded when OpenCL is enabled)
3rdparty/cutlass_fpA_intB_gemm
3rdparty/tensorrt_llm
diff --git a/apps/android_rpc/README.md b/apps/android_rpc/README.md
index f74942e148..12c2783b73 100644
--- a/apps/android_rpc/README.md
+++ b/apps/android_rpc/README.md
@@ -74,7 +74,8 @@ $ANDROID_HOME/platform-tools/adb uninstall
org.apache.tvm.tvmrpc
### Build with OpenCL
-Application is building with OpenCL support by default.
+Application is building with OpenCL support by default. Set `OPENCL_SDK` to an
+OpenCL SDK containing `include/CL/cl.h` before invoking the Android build.
[OpenCL-wrapper](../../src/runtime/opencl/opencl_wrapper) is used and will
dynamically load OpenCL library on the device.
If the device doesn't have OpenCL library on it, then you'll see in the
runtime that OpenCL library cannot be opened.
If you want to build this application without OpenCL then set `USE_OPENCL = 0`
diff --git a/apps/android_rpc/app/src/main/jni/Android.mk
b/apps/android_rpc/app/src/main/jni/Android.mk
index d5a7ee042f..17559b7cc1 100644
--- a/apps/android_rpc/app/src/main/jni/Android.mk
+++ b/apps/android_rpc/app/src/main/jni/Android.mk
@@ -38,8 +38,11 @@ LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib/ -llog
LOCAL_C_INCLUDES := $(ROOT_PATH)/include $(ROOT_PATH)/tirx/include \
$(ROOT_PATH)/tirx/3rdparty/tvm-ffi/include \
- $(ROOT_PATH)/tirx/3rdparty/tvm-ffi/3rdparty/dlpack/include
\
- $(ROOT_PATH)/3rdparty/OpenCL-Headers
+ $(ROOT_PATH)/tirx/3rdparty/tvm-ffi/3rdparty/dlpack/include
+
+ifeq ($(USE_OPENCL), 1)
+ LOCAL_C_INCLUDES += $(OPENCL_SDK)/include
+endif
LOCAL_MODULE = tvm4j_runtime_packed
diff --git a/ci/README.md b/ci/README.md
index e617d7af91..6cbc1a3226 100644
--- a/ci/README.md
+++ b/ci/README.md
@@ -93,3 +93,22 @@ Other jobs run in GitHub Actions for Windows and MacOS jobs.
The native Groovy entry points in `ci/jenkins/generated/` load shared helpers
from `common.groovy`. Edit these sources directly; no generation step is
needed.
See comments in [common.groovy](jenkins/generated/common.groovy) for shared
utility options and trust constraints.
+
+The native Jenkins pipelines source
+`tests/scripts/task_install_build_dependencies.sh` once to clean `dist/` and
+install build backend and Z3 prerequisites. They then invoke
+`tests/scripts/task_build_<platform>.sh`. Each root platform script sets the
full
+platform CMake options and directly builds the pinned-source FFI wheel from
+`tirx/3rdparty/tvm-ffi`, followed by the TIRx and TVM wheels. All three wheel
+builds use the same root CMake source and absolute `build/` directory, with
fixed
+package-specific install components. They perform normal CMake configuration
+passes over the shared native build graph. The root script also runs enabled
+C++ tests, and all three wheels are written to `dist/`.
+
+Before running a consumer script, Groovy sources
+`tests/scripts/test_task_install_dist_wheels.sh` in the same shell. This
installs
+the three transferred wheels offline without dependencies into `.local/python`
+and sets its absolute path as `PYTHONPATH`; `TVM_HOME` retains checkout
resources.
+Test and docs scripts use that caller-provided environment. Groovy sets the
+cache, parallelism and C/C++ compiler-launcher environment values;
+`docker/bash.sh` forwards them to the build scripts.
diff --git a/ci/jenkins/generated/arm_jenkinsfile.groovy
b/ci/jenkins/generated/arm_jenkinsfile.groovy
index 1429442a03..19703571e7 100644
--- a/ci/jenkins/generated/arm_jenkinsfile.groovy
+++ b/ci/jenkins/generated/arm_jenkinsfile.groovy
@@ -31,26 +31,16 @@ stage('Build') {
ci.with_node(instance: 'ARM-GRAVITON3-SPOT', retry_instance: 'ARM-GRAVITON3',
name: 'BUILD: arm', folder: 'tvm/build-arm', variables: ['PLATFORM=arm']) {
ci.docker_init(images.ci_arm)
- sh(
- script: "${docker_run} ${images.ci_arm}
./tests/scripts/task_config_build_arm.sh build",
- label: 'Create ARM CMake config',
- )
- sh (
- script: "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_arm}
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod
--sccache-region us-west-2 --build-dir build",
- label: 'Run CMake build',
- )
- sh (
- script: """
- set -eux
- ${docker_run} ${images.ci_arm} python3 ./tests/scripts/task_build.py \
- --sccache-bucket tvm-sccache-prod \
- --sccache-region us-west-2 \
- --cmake-target cpptest \
- --cmake-target tirx_cpptest \
- --build-dir build
- """,
- label: 'Make C++ tests',
- )
- ci.upload_artifacts('arm', '--artifact tvm_lib --artifact cpptest')
+ withEnv(ci.build_environment()) {
+ sh(
+ script: """${docker_run} ${images.ci_arm} bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_arm.sh
+ '""",
+ label: 'Build arm wheels and C++ tests',
+ )
+ }
+ ci.upload_artifacts('arm', '--path "dist/*.whl"')
+
}
}
diff --git a/ci/jenkins/generated/common.groovy
b/ci/jenkins/generated/common.groovy
index 1e9ce344be..f7e39e01a7 100644
--- a/ci/jenkins/generated/common.groovy
+++ b/ci/jenkins/generated/common.groovy
@@ -23,7 +23,10 @@ image_platforms = [ci_arm: 'ARM', ci_cpu: 'CPU', ci_gpu:
'GPU', ci_wasm: 'CPU']
// Resolve Hub inputs through the caller account ECR cache; false keeps public
names.
enable_docker_hub_cache = true
max_time = 180
-docker_run = 'docker/bash.sh --env CI --env PLATFORM --env TEST_STEP_NAME'
+// Temporarily bypass image sccache wrappers and inherited compiler launchers.
+docker_run = 'docker/bash.sh --env CI --env PLATFORM --env TEST_STEP_NAME' +
+ ' --env CC=/usr/bin/gcc --env CXX=/usr/bin/g++' +
+ ' --env CMAKE_C_COMPILER_LAUNCHER= --env CMAKE_CXX_COMPILER_LAUNCHER='
docker_build = 'docker/build.sh'
s3_bucket = 'tvm-jenkins-artifacts-prod'
jenkins_scripts_root = 'ci/jenkins/scripts'
@@ -348,6 +351,8 @@ def download_artifacts(tag) {
}
def docker_init(image) {
+ // Forget compiler wrappers cached before the temporary sccache bypass.
+ sh 'rm -f build/CMakeCache.txt tirx/3rdparty/tvm-ffi/build/CMakeCache.txt'
// Clear out all Docker images that aren't going to be used
sh(
script: """
@@ -388,4 +393,14 @@ def start() {
}
}
+// Preserve caller limits, otherwise share the node among its executors.
+def build_environment() {
+ def parallel_limit = env.CMAKE_BUILD_PARALLEL_LEVEL ?: sh(
+ script: 'n=$(( $(getconf _NPROCESSORS_ONLN) / ${CI_NUM_EXECUTORS:-1} ));
if [ "$n" -lt 1 ]; then n=1; fi; echo "$n"',
+ returnStdout: true,
+ label: 'Determine build parallelism',
+ ).trim()
+ return ["CMAKE_BUILD_PARALLEL_LEVEL=${parallel_limit}"]
+}
+
return this
diff --git a/ci/jenkins/generated/cpu_jenkinsfile.groovy
b/ci/jenkins/generated/cpu_jenkinsfile.groovy
index 44aceb116d..d487846a0b 100644
--- a/ci/jenkins/generated/cpu_jenkinsfile.groovy
+++ b/ci/jenkins/generated/cpu_jenkinsfile.groovy
@@ -31,27 +31,17 @@ stage('Build') {
ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
name: 'BUILD: CPU', folder: 'tvm/build-cpu', variables: ['PLATFORM=cpu']) {
ci.docker_init(images.ci_cpu)
- sh(
- script: "${docker_run} ${images.ci_cpu}
./tests/scripts/task_config_build_cpu.sh build",
- label: 'Create CPU CMake config',
- )
- sh (
- script: "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_cpu}
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod
--sccache-region us-west-2 --build-dir build",
- label: 'Run CMake build',
- )
- sh (
- script: """
- set -eux
- ${docker_run} ${images.ci_cpu} python3 ./tests/scripts/task_build.py \
- --sccache-bucket tvm-sccache-prod \
- --sccache-region us-west-2 \
- --cmake-target cpptest \
- --cmake-target tirx_cpptest \
- --build-dir build
- """,
- label: 'Make C++ tests',
- )
- ci.upload_artifacts('cpu', '--artifact tvm_lib --artifact cpptest')
+ withEnv(ci.build_environment()) {
+ sh(
+ script: """${docker_run} ${images.ci_cpu} bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_cpu.sh
+ '""",
+ label: 'Build cpu wheels and C++ tests',
+ )
+ }
+ ci.upload_artifacts('cpu', '--path "dist/*.whl"')
+
}
}
@@ -63,11 +53,10 @@ stage('Test') {
ci.docker_init(images.ci_cpu)
ci.download_artifacts('cpu')
sh (
- script: "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_cpu}
./tests/scripts/task_cpp_unittest.sh",
- label: 'Run C++ tests',
- )
- sh (
- script: "${docker_run} ${images.ci_cpu}
./tests/scripts/task_python_unittest.sh",
+ script: """${docker_run} ${images.ci_cpu} bash -euo pipefail -c '
+ source ./tests/scripts/test_task_install_dist_wheels.sh
+ ./tests/scripts/task_python_unittest.sh
+ '""",
label: 'Run Python unit tests',
)
}
diff --git a/ci/jenkins/generated/gpu_jenkinsfile.groovy
b/ci/jenkins/generated/gpu_jenkinsfile.groovy
index ac46fa4de1..c98c4c2a01 100644
--- a/ci/jenkins/generated/gpu_jenkinsfile.groovy
+++ b/ci/jenkins/generated/gpu_jenkinsfile.groovy
@@ -33,17 +33,25 @@ stage('Build') {
ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
name: 'BUILD: GPU', folder: 'tvm/build-gpu', variables: ['PLATFORM=gpu']) {
ci.docker_init(images.ci_gpu)
- sh "${docker_run} --no-gpu ${images.ci_gpu}
./tests/scripts/task_config_build_gpu.sh build"
- sh (
- script: "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_gpu} --no-gpu
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod
--sccache-region us-west-2 --build-dir build",
- label: 'Run CMake build',
- )
- ci.upload_artifacts('gpu', '--artifact tvm_lib --artifact
tvm_lib_gpu_extra')
+ withEnv(ci.build_environment()) {
+ sh(
+ script: """${docker_run} --no-gpu ${images.ci_gpu} bash -euo pipefail
-c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_gpu.sh
+ '""",
+ label: 'Build gpu',
+ )
+ }
+ ci.upload_artifacts('gpu', '--path "dist/*.whl"')
// Check the compiler-only GPU configuration after publishing the test
build.
sh 'rm -rf build'
- sh "${docker_run} --no-gpu ${images.ci_gpu}
./tests/scripts/task_config_build_gpu_other.sh build"
- sh "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_gpu} --no-gpu
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod
--sccache-region us-west-2 --cmake-target tvm_runtime --build-dir build"
+ withEnv(ci.build_environment()) {
+ sh(
+ script: "${docker_run} --no-gpu ${images.ci_gpu}
./tests/scripts/task_build_gpu_other.sh",
+ label: 'Build gpu_other',
+ )
+ }
}
}
@@ -56,12 +64,12 @@ stage('Test') {
ci.docker_init(images.ci_gpu)
ci.download_artifacts('gpu')
sh(
- script: "${docker_run} ${images.ci_gpu}
./tests/scripts/task_java_unittest.sh",
- label: 'Run Java unit tests',
- )
- sh(
- script: "${docker_run} ${images.ci_gpu}
./tests/scripts/task_python_unittest_gpuonly.sh",
- label: 'Run Python GPU unit tests',
+ script: """${docker_run} ${images.ci_gpu} bash -euo pipefail -c '
+ source ./tests/scripts/test_task_install_dist_wheels.sh
+ ./tests/scripts/task_java_unittest.sh
+ ./tests/scripts/task_python_unittest_gpuonly.sh
+ '""",
+ label: 'Run Java and Python GPU unit tests',
)
}
},
@@ -72,7 +80,10 @@ stage('Test') {
ci.docker_init(images.ci_gpu)
ci.download_artifacts('gpu')
sh(
- script: "${docker_run} ${images.ci_gpu}
./tests/scripts/task_python_docs.sh",
+ script: """${docker_run} ${images.ci_gpu} bash -euo pipefail -c '
+ source ./tests/scripts/test_task_install_dist_wheels.sh
+ ./tests/scripts/task_python_docs.sh
+ '""",
label: 'Build docs',
)
ci.upload_artifacts('docs', '--path docs.tgz')
diff --git a/ci/jenkins/generated/wasm_jenkinsfile.groovy
b/ci/jenkins/generated/wasm_jenkinsfile.groovy
index 593da51094..a079192d8a 100644
--- a/ci/jenkins/generated/wasm_jenkinsfile.groovy
+++ b/ci/jenkins/generated/wasm_jenkinsfile.groovy
@@ -31,32 +31,20 @@ stage('Build') {
ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
name: 'BUILD: WASM', folder: 'tvm/build-wasm', variables:
['PLATFORM=wasm']) {
ci.docker_init(images.ci_wasm)
+ withEnv(ci.build_environment()) {
+ sh(
+ script: """${docker_run} ${images.ci_wasm} bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_wasm.sh
+ '""",
+ label: 'Build wasm wheels and C++ tests',
+ )
+ }
sh(
- script: "${docker_run} ${images.ci_wasm}
./tests/scripts/task_config_build_wasm.sh build",
- label: 'Create WASM CMake config',
- )
- sh (
- script: "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_wasm}
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod
--sccache-region us-west-2 --build-dir build",
- label: 'Run CMake build',
- )
- sh (
- script: """
- set -eux
- ${docker_run} ${images.ci_wasm} python3 ./tests/scripts/task_build.py \
- --sccache-bucket tvm-sccache-prod \
- --sccache-region us-west-2 \
- --cmake-target cpptest \
- --cmake-target tirx_cpptest \
- --build-dir build
- """,
- label: 'Make C++ tests',
- )
- sh (
- script: "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_wasm}
./tests/scripts/task_cpp_unittest.sh",
- label: 'Run C++ tests',
- )
- sh(
- script: "${docker_run} ${images.ci_wasm}
./tests/scripts/task_web_wasm.sh",
+ script: """${docker_run} ${images.ci_wasm} bash -euo pipefail -c '
+ source ./tests/scripts/test_task_install_dist_wheels.sh
+ ./tests/scripts/task_web_wasm.sh
+ '""",
label: 'Run WASM lint and tests',
)
}
diff --git a/cmake/modules/contrib/CLML.cmake b/cmake/modules/contrib/CLML.cmake
index e085fdcbda..73ab85db09 100644
--- a/cmake/modules/contrib/CLML.cmake
+++ b/cmake/modules/contrib/CLML.cmake
@@ -18,7 +18,15 @@
if(USE_CLML)
file(GLOB CLML_RELAX_CONTRIB_SRC src/relax/backend/contrib/clml/*.cc)
file(GLOB CLML_RUNTIME_MODULE
src/runtime/extra/contrib/clml/clml_runtime.cc)
- include_directories(SYSTEM "3rdparty/OpenCL-Headers")
+ if(TARGET tvm_runtime_opencl)
+ get_target_property(_clml_opencl_headers tvm_runtime_opencl
INTERFACE_INCLUDE_DIRECTORIES)
+ endif()
+ if(NOT _clml_opencl_headers)
+ find_path(_clml_opencl_headers CL/cl.h
+ HINTS "${TVM_OPENCL_HEADERS_ROOT}" ENV OPENCL_SDK
+ PATH_SUFFIXES include REQUIRED)
+ endif()
+ include_directories(SYSTEM ${_clml_opencl_headers})
list(APPEND COMPILER_SRCS ${CLML_RELAX_CONTRIB_SRC})
if(NOT USE_CLML_GRAPH_EXECUTOR)
add_library(tvm_clml_objs OBJECT ${CLML_RUNTIME_MODULE})
diff --git a/docker/bash.sh b/docker/bash.sh
index fe0b1eeb2b..dd39c784bf 100755
--- a/docker/bash.sh
+++ b/docker/bash.sh
@@ -236,6 +236,14 @@ function validate_mount() {
break_joined_flag='if (( ${#1} == 2 )); then shift; else set -- -"${1#-i}"
"${@:2}"; fi'
DOCKER_ENV=( )
+# Forward build settings selected by the caller without overriding explicit
values.
+BUILD_ENV_VARS=(SCCACHE_BUCKET SCCACHE_REGION CMAKE_BUILD_PARALLEL_LEVEL
+ CMAKE_C_COMPILER_LAUNCHER CMAKE_CXX_COMPILER_LAUNCHER)
+for build_var in "${BUILD_ENV_VARS[@]}"; do
+ if [ "${!build_var+x}" = "x" ]; then
+ DOCKER_ENV+=( --env "$build_var=${!build_var}" )
+ fi
+done
DOCKER_FLAGS=( )
while (( $# )); do
diff --git a/docs/README.md b/docs/README.md
index 777f122da5..6b45aa1f4b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -23,23 +23,46 @@ This folder contains the source of TVM's documentation,
hosted at https://tvm.ap
### With Docker (recommended)
-1. Build TVM and the docs inside the [tlcpack/ci-gpu
image](https://hub.docker.com/r/tlcpack/ci-gpu) using the
[`ci.py`](../tests/scripts/ci.py) script.
+Run these commands from the repository root. The Docker image aliases select
+this checkout's CI images; their first download can take some time.
+
+1. Build the FFI, TIRx, and TVM wheels in dependency order and run the CPU
+ C++ tests, using the same commands as CI:
```bash
- # If this runs into errors, try cleaning your 'build' directory
- python tests/scripts/ci.py docs
+ docker/bash.sh ci_cpu bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_cpu.sh
+ sccache --show-stats
+ '
+ ```
- # See other doc building options
- python tests/scripts/ci.py docs --help
+2. Build the Python documentation without executing tutorials. Install the
+ additional CPU documentation dependencies in the same container as the
build:
+
+ ```bash
+ docker/bash.sh --env IS_LOCAL=1 \
+ --env PYTHON_DOCS_ONLY=1 --env TVM_TUTORIAL_EXEC_PATTERN=none \
+ ci_cpu bash -euo pipefail -c '
+ tests/scripts/task_install_docs_dependencies.sh
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_docs.sh
+ '
```
-2. Serve the docs and visit http://localhost:8000 in your browser
+ The sourced helper installs the wheels from `dist/` and sets `PYTHONPATH`
+ for the docs script. Rebuild the wheels after changing TVM code;
+ documentation-only edits can reuse them.
+
+3. Serve the generated docs and visit http://localhost:8000 in your browser:
```bash
- # Run an HTTP server you can visit to view the docs in your browser
- python tests/scripts/ci.py serve-docs
+ python3 -m http.server --directory _docs
```
+For the full documentation build, including GPU tutorials and non-Python APIs,
+see [Helper Scripts](#helper-scripts) below.
+
### Native
1. [Build TVM](https://tvm.apache.org/docs/install/from_source.html) first in
the repo root folder, then make it importable:
@@ -48,7 +71,7 @@ This folder contains the source of TVM's documentation,
hosted at https://tvm.ap
export TVM_HOME=/path-to-tvm
export TVM_LIBRARY_PATH=$TVM_HOME/build
pip install --target=$TVM_HOME/python $TVM_HOME/tirx/3rdparty/tvm-ffi
- export PYTHONPATH=$TVM_HOME/python:$PYTHONPATH
+ export PYTHONPATH="$TVM_HOME/tirx/python:$TVM_HOME/python:${PYTHONPATH:-}"
```
`docs/conf.py` unconditionally imports `tvm` at startup, so the build will
@@ -88,44 +111,59 @@ This will cause failure in some cases when certain
machines do not have necessar
environment. You can set `TVM_TUTORIAL_EXEC_PATTERN` to only execute
the path that matches the regular expression pattern.
-For example, to only build tutorials under `/get_started/tutorials`, run
+After building GPU wheels as shown below, execute only tutorials under
+`/get_started/tutorials` with:
```bash
-python tests/scripts/ci.py docs --tutorial-pattern=/get_started/tutorials
+docker/bash.sh --env IS_LOCAL=1 \
+ --env PYTHON_DOCS_ONLY=1 --env
TVM_TUTORIAL_EXEC_PATTERN=/get_started/tutorials \
+ ci_gpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_docs.sh
+ '
```
-To only build one specific file, do
+To execute only one file, use a regular expression matching its name. Quote the
+pattern so the shell preserves the backslash that escapes the dot:
```bash
-# The slash \ is used to get . in regular expression
-python tests/scripts/ci.py docs --tutorial-pattern=file_name\.py
+docker/bash.sh --env IS_LOCAL=1 \
+ --env PYTHON_DOCS_ONLY=1 --env 'TVM_TUTORIAL_EXEC_PATTERN=file_name\.py' \
+ ci_gpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_docs.sh
+ '
```
## Helper Scripts
-The following script mirrors the CI docs pipeline: it runs a sphinx pre-check
-(when not running locally) and then performs a full `make htmldepoly` build,
-including tutorial execution. You will need a GPU CI environment.
+`tests/scripts/task_python_docs.sh` mirrors the CI docs pipeline. It runs a
+Sphinx warning pre-check when `IS_LOCAL=0`, then builds the documentation with
+`make htmldepoly`. `IS_LOCAL=1` skips the pre-check; the final build still
checks
+warnings. `PYTHON_DOCS_ONLY=1` skips the C++, Java, and TypeScript API docs for
+local builds.
-```bash
-tests/scripts/task_python_docs.sh
-```
-
-To build docs locally without executing tutorials (fastest local iteration):
+To run the full build, including tutorial execution and all API docs, use a
+host with an NVIDIA GPU and Docker GPU support:
```bash
-cd docs && TVM_TUTORIAL_EXEC_PATTERN=none make html
+docker/bash.sh --no-gpu ci_gpu bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_gpu.sh
+ sccache --show-stats
+'
+docker/bash.sh --env IS_LOCAL=1 \
+ --env PYTHON_DOCS_ONLY=0 --env TVM_TUTORIAL_EXEC_PATTERN=.py \
+ ci_gpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_docs.sh
+ '
+python3 -m http.server --directory _docs
```
-Note: the sphinx pre-check (warning validation) only runs in CI (`IS_LOCAL=0`).
-`python tests/scripts/ci.py docs` always sets `IS_LOCAL=1` and skips the
-pre-check regardless of other flags.
-
-To run the full build including tutorial executions:
-
-```bash
-python tests/scripts/ci.py docs --full
-```
+Native builds use `build/` and wheels use `dist/`. Clean these directories
+and run the matching platform build when switching between CPU and GPU
+documentation builds.
## Define the Order of Tutorials
diff --git a/docs/conf.py b/docs/conf.py
index 1d4ec578f7..5b1d4f23be 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -50,8 +50,6 @@ if curr_path.name == "_staging":
else:
tvm_path = Path(os.pardir)
-sys.path.insert(0, str(tvm_path.resolve() / "python"))
-sys.path.insert(0, str(tvm_path.resolve() / "tirx" / "python"))
sys.path.insert(0, str(tvm_path.resolve() / "docs"))
# -- General configuration ------------------------------------------------
diff --git a/docs/contribute/pull_request.rst b/docs/contribute/pull_request.rst
index a93f55b546..2fe4aef409 100644
--- a/docs/contribute/pull_request.rst
+++ b/docs/contribute/pull_request.rst
@@ -186,31 +186,60 @@ locally beforehand to reduce reviewers' burden and
speedup review process.
Docker (recommended)
^^^^^^^^^^^^^^^^^^^^
-``tests/scripts/ci.py`` replicates the CI environment locally and provides a
user-friendly interface.
-The same Docker images and scripts used in CI are used directly to run tests.
It also deposits builds
-in different folders so you can maintain multiple test environments without
rebuilding from scratch
-each time (e.g. you can test a change in CPU and GPU while retaining
incremental rebuilds).
+Use ``docker/bash.sh`` with the same platform build and test scripts used in
CI.
+Run these commands from the repository root. The CPU and GPU Docker images are
+large and may take some time to download on their first use.
.. code:: bash
- # see all available platforms
- python tests/scripts/ci.py --help
- python tests/scripts/ci.py cpu --help
+ # show Docker options
+ docker/bash.sh --help
+
+ # build FFI, TIRx, and TVM wheels and run CPU C++ tests
+ # from the shared native build
+ docker/bash.sh ci_cpu bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_cpu.sh
+ sccache --show-stats
+ '
+
+ # install the wheels from dist/ and run Python unit tests
+ docker/bash.sh ci_cpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_unittest.sh
+ '
+
+ # reuse the wheels to run a selected test
+ docker/bash.sh ci_cpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_unittest.sh \
+
tests/python/s_tir/schedule/test_tir_schedule_rolling_buffer.py::test_upscale
+ '
+
+ # enter an interactive container and install the wheels for that shell
+ docker/bash.sh ci_cpu
+ source tests/scripts/test_task_install_dist_wheels.sh
+ python3 -m pytest
tests/python/s_tir/schedule/test_tir_schedule_rolling_buffer.py
+ exit
+
+Rebuild the wheels after changing TVM code. For GPU tests, use a host with an
+NVIDIA GPU and Docker GPU support:
- # run the CPU build in the ci_cpu docker container (build will be left in
- # the build-cpu/ folder)
- # note: the CPU and GPU Docker images are quite large and may take some
- # time to download on their first use
- python tests/scripts/ci.py cpu
-
- # run the CPU build in the ci_cpu docker container and then run unittests
- python tests/scripts/ci.py cpu --unittest
+.. code:: bash
- # quickly iterate by running a specific test and skipping the rebuild each
time
- python tests/scripts/ci.py cpu --skip-build --tests
tests/python/s_tir/schedule/test_tir_schedule_rolling_buffer.py::test_upscale
+ docker/bash.sh --no-gpu ci_gpu bash -euo pipefail -c '
+ source ./tests/scripts/task_install_build_dependencies.sh
+ ./tests/scripts/task_build_gpu.sh
+ sccache --show-stats
+ '
+ docker/bash.sh ci_gpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_unittest_gpuonly.sh
+ '
- # run the CPU build and drop into a shell in the container
- python tests/scripts/ci.py cpu --interactive
+Native builds use ``build/`` and wheels use ``dist/``. Clean these directories
+and run the matching platform build when switching test environments.
+For lint checks, use ``pre-commit run --files <changed-file>...``.
We regularly update our docker images and, over time, stale images may
unnecessarily consume disk
space. You can remove stale images that aren't used in the presently
checked-out branch plus any
@@ -258,7 +287,7 @@ If you want to run a single test:
.. code:: bash
# let python know where to find tvm related libraries
- export PYTHONPATH=python
+ export PYTHONPATH="$PWD/tirx/python:$PWD/python:${PYTHONPATH:-}"
rm -rf python/tvm/*.pyc python/tvm/*/*.pyc python/tvm/*/*/*.pyc
python -m pytest -v tirx/tests/python/tirx/transform/test_storage_rewrite.py
diff --git a/docs/contribute/testing.rst b/docs/contribute/testing.rst
index d7e5c1b92f..14ad21e875 100644
--- a/docs/contribute/testing.rst
+++ b/docs/contribute/testing.rst
@@ -237,18 +237,24 @@ Allowed image names are defined in
``ci/jenkins/docker-images.ini`` in the TVM s
and map to images at `tlcpack`_.
If no additional arguments are given, the docker image will be loaded
-with an interactive bash session. If a script is passed as an
-optional argument (e.g. ``docker/bash.sh ci_gpu
tests/scripts/task_python_unittest.sh``), then that script will be
-executed inside the docker image.
-
-Note: The docker images contain all system dependencies, but do not
-include the ``build/config.cmake`` configuration file for those
-systems. The TVM source directory is used as the home directory of
-the docker image, and so this will default to using the same
-config/build directories as the local config. One solution is to
-maintain separate ``build_local`` and ``build_docker`` directories,
-and make a symlink from ``build`` to the appropriate folder when
-entering/exiting docker.
+with an interactive bash session. Additional arguments run a command inside
+the container. First build the platform wheels as described in
+:ref:`pr-testing`, then source the installation helper before running tests:
+
+.. code-block:: bash
+
+ docker/bash.sh ci_gpu bash -euo pipefail -c '
+ source tests/scripts/test_task_install_dist_wheels.sh
+ tests/scripts/task_python_unittest_gpuonly.sh
+ '
+
+The helper installs the wheels from ``dist/`` into ``.local/python`` and sets
+``PYTHONPATH`` for subsequent commands in the same shell. In an interactive
+container, source the helper before invoking pytest or a test script.
+
+The checkout is shared with the container. Native builds use ``build/`` and
+wheel artifacts use ``dist/``; clean these directories and rebuild when
+switching platform configurations.
Running in CI
~~~~~~~~~~~~~
@@ -267,22 +273,23 @@ in which stages.
- Compile-time configuration
- The docker image does not have the ``config.cmake`` file built into
- it, so this is the first step in each of the ``BUILD`` tasks. This
- is done using the ``tests/scripts/task_config_build_*.sh`` scripts.
- Which script is used depends on the build being tested, and is
- specified in the Jenkinsfile.
+ The Jenkinsfile sources ``tests/scripts/task_install_build_dependencies.sh``
+ once to clean ``dist/`` and install build prerequisites. It then invokes
+ ``tests/scripts/task_build_<platform>.sh``, which sets the full platform
CMake
+ options and builds FFI, TIRx, and TVM wheels in that order. All three wheel
+ builds use the root CMake source and the same ``build/`` directory, with
+ package-specific install components. Enabled C++ tests run from that same
+ native build.
- Each ``BUILD`` task concludes by packing a library for use in later
- tests.
+ The three wheels in ``dist/`` are transferred to the test nodes.
- Which tests run
The ``Unit Test`` stage of the Jenkinsfile determines how ``pytest``
- is called. Each task starts by unpacking a
- compiled library that was previous compiled in the ``BUILD`` stage,
- then runs a test script
- (e.g. ``tests/scripts/task_python_unittest.sh``). These scripts set
+ is called. Each consumer downloads the wheels from the ``BUILD`` stage
+ and sources ``tests/scripts/test_task_install_dist_wheels.sh`` before
+ running a test script (e.g. ``tests/scripts/task_python_unittest.sh``).
+ These scripts set
the files/folders and command-line options that are passed to
``pytest``.
diff --git a/jvm/README.md b/jvm/README.md
index d28c935ae4..1e7afdf499 100644
--- a/jvm/README.md
+++ b/jvm/README.md
@@ -39,24 +39,30 @@ TVM4J contains three modules:
- core
* It contains all the Java interfaces.
- native
- * The JNI native library is compiled in this module. Need to expose
libtvm_runtime to LD_LIBRARY_PATH
+ * The JNI native library is compiled in this module using the installed
TIRx and FFI packages.
- assembly
* It assembles Java interfaces (core), JNI library (native) and TVM
runtime library together. The simplest way to integrate tvm4j in your project
is to rely on this module. It automatically extracts the native library to a
tempfile and load it.
### Build
-First please refer to [Installation
Guide](https://tvm.apache.org/docs/install/) and build runtime shared library
from the C++ codes (libtvm\_runtime.so for Linux and libtvm\_runtime.dylib for
OSX).
-
-Then you can compile tvm4j by
+Build the FFI, TIRx, and TVM wheels for your platform first; the
+[contributor guide](../docs/contribute/pull_request.rst) has the CI Docker
build
+commands. From the repository root, install the wheels from `dist/` and compile
+tvm4j in the same shell:
```bash
+source tests/scripts/test_task_install_dist_wheels.sh
bash tests/scripts/task_jvm_build.sh
```
-(Optional) run unit test by
+The build discovers native headers and libraries through `tvm_tirx.config`
+and `tvm_ffi.config`. The sourced helper sets `PYTHONPATH` to the installed
+packages in `.local/python`.
+
+To run the Java CI task in that shell:
```bash
-sh tests/scripts/task_java_unittest.sh
+bash tests/scripts/task_java_unittest.sh
```
After it is compiled and packaged, you can install tvm4j in your local maven
repository,
diff --git a/jvm/assembly/linux-x86_64/src/main/assembly/assembly.xml
b/jvm/assembly/linux-x86_64/src/main/assembly/assembly.xml
index 7345c496e9..ea7396f8dc 100644
--- a/jvm/assembly/linux-x86_64/src/main/assembly/assembly.xml
+++ b/jvm/assembly/linux-x86_64/src/main/assembly/assembly.xml
@@ -26,7 +26,12 @@ under the License.
<includeBaseDirectory>false</includeBaseDirectory>
<files>
<file>
- <source>../../../build/lib/libtvm_runtime.so</source>
+ <source>${tvm.lib.dir}/libtvm_runtime.so</source>
+ <outputDirectory>lib/native</outputDirectory>
+ <fileMode>0644</fileMode>
+ </file>
+ <file>
+ <source>${tvm.ffi.lib.dir}/libtvm_ffi.so</source>
<outputDirectory>lib/native</outputDirectory>
<fileMode>0644</fileMode>
</file>
diff --git a/jvm/assembly/osx-x86_64/src/main/assembly/assembly.xml
b/jvm/assembly/osx-x86_64/src/main/assembly/assembly.xml
index 739852e474..70f5754983 100644
--- a/jvm/assembly/osx-x86_64/src/main/assembly/assembly.xml
+++ b/jvm/assembly/osx-x86_64/src/main/assembly/assembly.xml
@@ -26,7 +26,12 @@ under the License.
<includeBaseDirectory>false</includeBaseDirectory>
<files>
<file>
- <source>../../../build/lib/libtvm_runtime.dylib</source>
+ <source>${tvm.lib.dir}/libtvm_runtime.dylib</source>
+ <outputDirectory>lib/native</outputDirectory>
+ <fileMode>0644</fileMode>
+ </file>
+ <file>
+ <source>${tvm.ffi.lib.dir}/libtvm_ffi.dylib</source>
<outputDirectory>lib/native</outputDirectory>
<fileMode>0644</fileMode>
</file>
diff --git a/jvm/core/pom.xml b/jvm/core/pom.xml
index eeb55f5cfd..26ba6dcd78 100644
--- a/jvm/core/pom.xml
+++ b/jvm/core/pom.xml
@@ -104,7 +104,7 @@ under the License.
<threadCount>1</threadCount>
<argLine>
-Djava.library.path=${project.parent.basedir}/native/${platform}/target
-
-Dlibtvm.so.path=${project.parent.basedir}/../build/lib/${libtvm.so.filename}
+ -Dlibtvm.so.path=${tvm.lib.dir}/${libtvm.so.filename}
-Dlog4j.configuration=file://${project.parent.basedir}/conf/log4j.properties
</argLine>
</configuration>
diff --git a/jvm/native/linux-x86_64/pom.xml b/jvm/native/linux-x86_64/pom.xml
index ce6e98e7f8..dcda4c6ce1 100644
--- a/jvm/native/linux-x86_64/pom.xml
+++ b/jvm/native/linux-x86_64/pom.xml
@@ -117,8 +117,8 @@ under the License.
<compilerStartOption>-std=c++17</compilerStartOption>
</compilerStartOptions>
<compilerEndOptions>
- <compilerEndOption>-I../../../include</compilerEndOption>
-
<compilerEndOption>-I../../../tirx/3rdparty/tvm-ffi/include</compilerEndOption>
+ <compilerEndOption>-I${tvm.include.dir}</compilerEndOption>
+ <compilerEndOption>-I${tvm.ffi.include.dir}</compilerEndOption>
<compilerEndOption>-I${JAVA_HOME}/include</compilerEndOption>
<compilerEndOption>-I${JAVA_HOME}/include/linux</compilerEndOption>
<compilerEndOption>${cflags}</compilerEndOption>
@@ -127,7 +127,7 @@ under the License.
<linkerStartOption>-shared</linkerStartOption>
</linkerStartOptions>
<linkerEndOptions>
-
<linkerEndOption>-L${project.parent.basedir}/../../build/lib/</linkerEndOption>
+ <linkerEndOption>-L${tvm.lib.dir}</linkerEndOption>
<linkerEndOption>-ltvm_runtime</linkerEndOption>
<linkerEndOption>${ldflags}</linkerEndOption>
</linkerEndOptions>
diff --git a/jvm/native/osx-x86_64/pom.xml b/jvm/native/osx-x86_64/pom.xml
index 5d6c3dcf75..ccb79f5bbb 100644
--- a/jvm/native/osx-x86_64/pom.xml
+++ b/jvm/native/osx-x86_64/pom.xml
@@ -118,8 +118,8 @@ under the License.
<compilerStartOption>-std=c++17</compilerStartOption>
</compilerStartOptions>
<compilerEndOptions>
- <compilerEndOption>-I../../../include</compilerEndOption>
-
<compilerEndOption>-I../../../tirx/3rdparty/tvm-ffi/include</compilerEndOption>
+ <compilerEndOption>-I${tvm.include.dir}</compilerEndOption>
+ <compilerEndOption>-I${tvm.ffi.include.dir}</compilerEndOption>
<compilerEndOption>-I${JAVA_HOME}/include</compilerEndOption>
<compilerEndOption>-I${JAVA_HOME}/include/darwin</compilerEndOption>
<compilerEndOption>${cflags}</compilerEndOption>
@@ -134,7 +134,7 @@ under the License.
<linkerMiddleOption>-Wl,-x</linkerMiddleOption>
</linkerMiddleOptions>
<linkerEndOptions>
-
<linkerEndOption>-L${project.parent.basedir}/../../build/lib/</linkerEndOption>
+ <linkerEndOption>-L${tvm.lib.dir}</linkerEndOption>
<linkerEndOption>-ltvm_runtime</linkerEndOption>
<linkerEndOption>${ldflags}</linkerEndOption>
</linkerEndOptions>
diff --git a/jvm/pom.xml b/jvm/pom.xml
index e5efce0bf5..fce2b6a510 100644
--- a/jvm/pom.xml
+++ b/jvm/pom.xml
@@ -44,6 +44,10 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
<tvm.include.dir>${maven.multiModuleProjectDirectory}/../include</tvm.include.dir>
+
<tvm.ffi.include.dir>${maven.multiModuleProjectDirectory}/../tirx/3rdparty/tvm-ffi/include</tvm.ffi.include.dir>
+
<tvm.lib.dir>${maven.multiModuleProjectDirectory}/../build/lib</tvm.lib.dir>
+ <tvm.ffi.lib.dir>${tvm.lib.dir}</tvm.ffi.lib.dir>
</properties>
<packaging>pom</packaging>
diff --git a/pyproject.toml b/pyproject.toml
index 8305d6dd88..af0f60575d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -51,6 +51,9 @@ classifiers = [
]
dependencies = ["apache-tvm-tirx==0.1.0a0"]
+[project.scripts]
+tvm-config = "tvm.config:__main__"
+
[project.optional-dependencies]
torch = ["torch"]
cuda = ["cuda-bindings"]
@@ -121,7 +124,6 @@ sdist.include = [
"/tirx/src/runtime/metadata.h",
"/tirx/src/runtime/pack_args.h",
"/tirx/src/runtime/workspace_pool.h",
- "/3rdparty/OpenCL-Headers/**",
"/3rdparty/cutlass/**",
"/3rdparty/cutlass_fpA_intB_gemm/**",
"/3rdparty/libflash_attn/**",
diff --git a/python/tvm/__init__.py b/python/tvm/__init__.py
index 7320967ffb..8952ca68b3 100644
--- a/python/tvm/__init__.py
+++ b/python/tvm/__init__.py
@@ -18,13 +18,27 @@
# pylint: disable=redefined-builtin, wildcard-import
"""TVM compiler dialects and runtime extensions built on TIRx."""
-from .libinfo import __version__
-from .base import _RUNTIME_ONLY
-from . import runtime
-from . import rpc
+import sys
-if not _RUNTIME_ONLY:
- from . import te
- from . import s_tir
- from .driver import compile
- from . import relax
+from typing import TYPE_CHECKING
+
+
+def _is_config_mode() -> bool:
+ """Recognize the config CLI before loading Python or native TVM
components."""
+ if sys.argv[0].endswith("tvm-config"):
+ return True
+ argv = getattr(sys, "orig_argv", [])
+ return any(arg == "-m" and argv[i + 1 : i + 2] == ["tvm.config"] for i,
arg in enumerate(argv))
+
+
+if TYPE_CHECKING or not _is_config_mode():
+ from .libinfo import __version__
+ from .base import _RUNTIME_ONLY
+ from . import runtime
+ from . import rpc
+
+ if not _RUNTIME_ONLY:
+ from . import te
+ from . import s_tir
+ from .driver import compile
+ from . import relax
diff --git a/python/tvm/config.py b/python/tvm/config.py
new file mode 100644
index 0000000000..c76ef90006
--- /dev/null
+++ b/python/tvm/config.py
@@ -0,0 +1,97 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Lightweight compiler and linker configuration for the TVM runtime."""
+
+import argparse
+import os
+import sys
+from pathlib import Path
+
+
+def _runtime_root() -> Path:
+ """Locate this package without importing its native code."""
+ return Path(__file__).resolve().parent
+
+
+def _find_include() -> str:
+ package = _runtime_root()
+ for path in (package / "include", package.parent.parent / "include"):
+ if (path / "tvm" / "runtime").is_dir():
+ return str(path.resolve())
+ raise RuntimeError("Cannot find TVM runtime headers")
+
+
+def _library_paths() -> list[Path]:
+ package = _runtime_root()
+ source = package.parent.parent
+ paths = [package / "lib", source / "build" / "lib", source / "lib"]
+ if os.environ.get("TVM_LIBRARY_PATH"):
+ paths.insert(0, Path(os.environ["TVM_LIBRARY_PATH"]))
+ for name in ("LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "PATH"):
+ paths.extend(Path(value) for value in os.environ.get(name,
"").split(os.pathsep) if value)
+ return paths
+
+
+def _find_runtime() -> str:
+ filename = {"win32": "tvm_runtime_extra.dll", "darwin":
"libtvm_runtime_extra.dylib"}.get(
+ sys.platform, "libtvm_runtime_extra.so"
+ )
+ for directory in _library_paths():
+ path = directory / filename
+ if path.is_file():
+ return str(path.resolve())
+ raise RuntimeError("Cannot find libtvm_runtime_extra")
+
+
+def __main__() -> None:
+ """Print paths and flags using the same conventions as tvm_ffi.config."""
+ parser = argparse.ArgumentParser(description="Get TVM runtime build
configuration")
+ for name, help_text in (
+ ("includedir", "Print include directory"),
+ ("libdir", "Print library directory"),
+ ("libfiles", "Fully qualified library filenames"),
+ ("libs", "Libraries to be linked"),
+ ("cflags", "Print C flags"),
+ ("cxxflags", "Print C++ flags"),
+ ("ldflags", "Print linker flags"),
+ ):
+ parser.add_argument(f"--{name}", action="store_true", help=help_text)
+ args = parser.parse_args()
+ if len(sys.argv) == 1:
+ parser.print_help()
+ return
+ if args.includedir:
+ print(_find_include())
+ if args.libdir or args.libfiles or args.libs or args.ldflags:
+ library = Path(_find_runtime())
+ link_file = library.with_suffix(".lib") if sys.platform == "win32"
else library
+ if args.libdir:
+ print(library.parent)
+ if args.libfiles:
+ print(link_file)
+ if args.cxxflags:
+ print(f"-I{_find_include()} -std=c++20")
+ if args.cflags:
+ print(f"-I{_find_include()}")
+ if args.libs:
+ print(link_file if sys.platform == "win32" else "-ltvm_runtime_extra")
+ if args.ldflags and sys.platform != "win32":
+ print(f"-L{library.parent}")
+
+
+if __name__ == "__main__":
+ __main__()
diff --git a/python/tvm/rpc/minrpc.py b/python/tvm/rpc/minrpc.py
index 5885f75005..2b5ba261a2 100644
--- a/python/tvm/rpc/minrpc.py
+++ b/python/tvm/rpc/minrpc.py
@@ -41,6 +41,8 @@ def find_minrpc_server_libpath(server="posix_popen_server"):
curr_dir = os.path.dirname(os.path.realpath(os.path.expanduser(__file__)))
source_dir = os.path.abspath(os.path.join(curr_dir, "..", "..", ".."))
roots = [os.path.join(curr_dir, ".."), source_dir]
+ if os.environ.get("TVM_HOME"):
+ roots.append(os.environ["TVM_HOME"])
candidates = []
for root in roots:
minrpc_dir = os.path.join(root, "src", "runtime", "rpc", "minrpc")
diff --git a/tests/python/runtime/rpc/test_minrpc_source.py
b/tests/python/runtime/rpc/test_minrpc_source.py
new file mode 100644
index 0000000000..0512d1cfa5
--- /dev/null
+++ b/tests/python/runtime/rpc/test_minrpc_source.py
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Find checkout-only minrpc sources while importing an installed TVM wheel."""
+
+from pathlib import Path
+
+import pytest
+
+from tvm import libinfo
+from tvm.rpc import minrpc
+
+
[email protected]("layout", ["source", "symlink", "wheel"])
+def test_minrpc_source_location(tmp_path, monkeypatch, layout):
+ checkout = tmp_path / "checkout"
+ minrpc_dir = checkout / "src/runtime/rpc/minrpc"
+ server = minrpc_dir / "posix_popen_server/posix_popen_server.cc"
+ server.parent.mkdir(parents=True)
+ server.write_text("// source fixture\n")
+ # A wheel's package location is unrelated to the source checkout.
+ package = tmp_path / "site-packages/tvm" if layout == "wheel" else
checkout / "python/tvm"
+ if layout == "symlink":
+ package.mkdir(parents=True)
+ alias = tmp_path / "python-alias"
+ alias.symlink_to(checkout / "python", target_is_directory=True)
+ package = alias / "tvm"
+ monkeypatch.setattr(libinfo, "__file__", str(package / "libinfo.py"))
+ monkeypatch.setattr(minrpc, "__file__", str(package / "rpc/minrpc.py"))
+ monkeypatch.delenv("TVM_HOME", raising=False)
+ if layout == "wheel":
+ monkeypatch.setenv("TVM_HOME", str(checkout))
+ directory, path = minrpc.find_minrpc_server_libpath()
+ assert Path(directory) == minrpc_dir
+ assert Path(path) == server
+ server.unlink()
+ with pytest.raises(RuntimeError, match="Cannot find minserver"):
+ minrpc.find_minrpc_server_libpath()
diff --git a/tests/python/runtime/test_config.py
b/tests/python/runtime/test_config.py
new file mode 100644
index 0000000000..c039e08bd2
--- /dev/null
+++ b/tests/python/runtime/test_config.py
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""The config command must work without importing native runtime
dependencies."""
+
+import os
+import subprocess
+import sys
+from pathlib import Path
+
+import tvm
+from tvm import config
+
+
+def test_config_without_native_imports(tmp_path):
+ # A startup hook rejects native loads and dependencies even when they are
installed.
+ (tmp_path / "sitecustomize.py").write_text(
+ "import sys\n"
+ "class BlockNative:\n"
+ " def find_spec(self, fullname, path=None, target=None):\n"
+ " if fullname.split('.')[0] in ('tvm_ffi', 'torch', 'numpy') or
"
+ "fullname in ('tvm.base', 'tvm.runtime', 'tvm.backend', 'tvm.driver',
'tvm_tirx.base'):\n"
+ " raise AssertionError(fullname)\n"
+ "sys.meta_path.insert(0, BlockNative())\n"
+ "def audit(event, args):\n"
+ " if event == 'ctypes.dlopen': raise AssertionError(args)\n"
+ "sys.addaudithook(audit)\n"
+ )
+ env = dict(os.environ)
+ env["PYTHONPATH"] = os.pathsep.join(
+ (
+ str(tmp_path),
+ str(Path(tvm.__file__).parent.parent),
+ )
+ )
+ command = [sys.executable, "-m", "tvm.config"]
+ include = subprocess.check_output([*command, "--includedir"], env=env,
text=True).strip()
+ assert (Path(include) / "tvm" / "runtime").is_dir()
+ library = subprocess.check_output([*command, "--libfiles"], env=env,
text=True).strip()
+ assert Path(library).is_file()
+ flags = subprocess.check_output(
+ [*command, "--cflags", "--cxxflags", "--libs", "--ldflags"], env=env,
text=True
+ )
+ assert f"-I{include}" in flags
+ assert "-std=c++20" in flags
+
+
+def test_runtime_library_path_fallback(tmp_path, monkeypatch):
+ library = tmp_path / Path(config._find_runtime()).name
+ library.touch()
+ monkeypatch.setattr(config, "_runtime_root", lambda: tmp_path / "tvm")
+ monkeypatch.delenv("LD_LIBRARY_PATH", raising=False)
+ monkeypatch.delenv("DYLD_LIBRARY_PATH", raising=False)
+ monkeypatch.setenv("PATH", str(tmp_path))
+ assert config._find_runtime() == str(library)
+
+
+if __name__ == "__main__":
+ import tvm_tirx.testing
+
+ tvm_tirx.testing.main()
diff --git a/tests/scripts/ci.py b/tests/scripts/ci.py
deleted file mode 100755
index fa027c150a..0000000000
--- a/tests/scripts/ci.py
+++ /dev/null
@@ -1,672 +0,0 @@
-#!/usr/bin/env python3
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-# ruff: noqa: E501, RUF005
-
-import argparse
-import getpass
-import grp
-import inspect
-import json
-import multiprocessing
-import os
-import platform
-import random
-import shutil
-import string
-import subprocess
-import sys
-import textwrap
-import typing
-from collections.abc import Callable
-from pathlib import Path
-from typing import Any
-
-REPO_ROOT = Path(__file__).resolve().parent.parent.parent
-SCRIPT_DIR = REPO_ROOT / ".ci-py-scripts"
-NPROC = multiprocessing.cpu_count()
-
-
-class col:
- BLUE = "\033[94m"
- CYAN = "\033[96m"
- GREEN = "\033[92m"
- YELLOW = "\033[93m"
- RED = "\033[91m"
- RESET = "\033[0m"
- BOLD = "\033[1m"
- UNDERLINE = "\033[4m"
-
-
-def print_color(color: str, msg: str, bold: bool, **kwargs: Any) -> None:
- if hasattr(sys.stdout, "isatty") and sys.stdout.isatty():
- bold_code = col.BOLD if bold else ""
- print(bold_code + color + msg + col.RESET, **kwargs)
- else:
- print(msg, **kwargs)
-
-
-warnings: list[str] = []
-
-
-def clean_exit(msg: str) -> None:
- print_color(col.RED, msg, bold=True, file=sys.stderr)
-
- for warning in warnings:
- print_color(col.YELLOW, warning, bold=False, file=sys.stderr)
-
- exit(1)
-
-
-def cmd(commands: list[Any], **kwargs: Any):
- commands = [str(s) for s in commands]
- command_str = " ".join(commands)
- print_color(col.BLUE, command_str, bold=True)
- proc = subprocess.run(commands, **kwargs)
- if proc.returncode != 0:
- raise RuntimeError(f"Command failed: '{command_str}'")
- return proc
-
-
-def get_build_dir(name: str) -> str:
- build_dir = REPO_ROOT / f"build-{name}"
- return str(build_dir.relative_to(REPO_ROOT))
-
-
-def check_docker():
- executable = shutil.which("docker")
- if executable is None:
- clean_exit("'docker' executable not found, install it first (e.g. 'apt
install docker.io')")
-
- if sys.platform == "linux":
- # Check that the user is in the docker group before running
- try:
- group = grp.getgrnam("docker")
- if getpass.getuser() not in group.gr_mem:
- warnings.append(
- f"Note: User '{getpass.getuser()}' is not in the 'docker'
group, either:\n"
- " * run with 'sudo'\n"
- " * add user to 'docker': sudo usermod -aG docker
$(whoami), then log out and back in",
- )
- except KeyError:
- warnings.append("Note: 'docker' group does not exist")
-
-
-def check_gpu():
- if not (sys.platform == "linux" and shutil.which("lshw")):
- # Can't check GPU on non-Linux platforms
- return
-
- # See if we can check if a GPU is present in case of later failures,
- # but don't block on execution since this isn't critical
- try:
- proc = cmd(
- ["lshw", "-json", "-C", "display"],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- encoding="utf-8",
- )
- stdout = proc.stdout.strip().strip(",")
- stdout = json.loads(stdout)
- except (subprocess.CalledProcessError, json.decoder.JSONDecodeError):
- # Do nothing if any step failed
- return
-
- if isinstance(stdout, dict):
- # Sometimes lshw outputs a single item as a dict instead of a list of
- # dicts, so wrap it up if necessary
- stdout = [stdout]
- if not isinstance(stdout, list):
- return
-
- vendors = [s.get("vendor", "").lower() for s in stdout]
- if not any("nvidia" in vendor for vendor in vendors):
- warnings.append(
- "nvidia GPU not found in 'lshw', maybe use --cpu flag when running
'docs' command?"
- )
-
-
-def gen_name(s: str) -> str:
- # random 4 letters
- suffix = "".join([random.choice(string.ascii_lowercase) for i in range(5)])
- return f"{s}-{suffix}"
-
-
-def docker(
- name: str,
- image: str,
- scripts: list[str],
- env: dict[str, str],
- interactive: bool,
- additional_flags: dict[str, str] | None = None,
-):
- """
- Invoke a set of bash scripts through docker/bash.sh
-
- name: container name
- image: docker image name
- scripts: list of bash commands to run
- env: environment to set
- """
- check_docker()
-
- # As sccache is added to these images these can be uncommented
- sccache_images = {
- "ci_gpu",
- "ci_cpu",
- # "ci_wasm",
- "ci_cortexm",
- "ci_arm",
- "ci_riscv",
- "ci_adreno",
- }
-
- if image in sccache_images and os.getenv("USE_SCCACHE", "1") == "1":
- scripts = [
- "sccache --start-server",
- ] + scripts
- # Set the C/C++ compiler so CMake picks them up in the build
- env["CC"] = "/opt/sccache/cc"
- env["CXX"] = "/opt/sccache/c++"
- env["SCCACHE_CACHE_SIZE"] = os.getenv("SCCACHE_CACHE_SIZE", "50G")
- env["SCCACHE_SERVER_PORT"] = os.getenv("SCCACHE_SERVER_PORT", "4226")
-
- env["PLATFORM"] = name
-
- docker_bash = REPO_ROOT / "docker" / "bash.sh"
-
- command = [docker_bash]
- if sys.stdout.isatty():
- command.append("-t")
-
- command.append("--name")
- command.append(name)
- if interactive:
- command.append("-i")
- scripts = ["interact() {", " bash", "}", "trap interact 0", ""] +
scripts
-
- for key, value in env.items():
- command.append("--env")
- command.append(f"{key}={value}")
-
- if additional_flags is not None:
- for key, value in additional_flags.items():
- command.append(key)
- command.append(value)
-
- SCRIPT_DIR.mkdir(exist_ok=True)
-
- script_file = SCRIPT_DIR / f"{name}.sh"
- with open(script_file, "w") as f:
- f.write("set -eux\n\n")
- f.write("\n".join(scripts))
- f.write("\n")
-
- command += [image, "bash", str(script_file.relative_to(REPO_ROOT))]
-
- try:
- cmd(command)
- except RuntimeError as e:
- clean_exit(f"Error invoking Docker: {e}")
- except KeyboardInterrupt:
- cmd(["docker", "stop", "--time", "1", name])
- finally:
- if os.getenv("DEBUG", "0") != "1":
- script_file.unlink()
-
-
-def docs(
- tutorial_pattern: str | None = None,
- cpu: bool = False,
- full: bool = False,
- interactive: bool = False,
- skip_build: bool = False,
- docker_image: str | None = None,
-) -> None:
- """
- Build the documentation from gallery/ and docs/. By default this builds
only
- the Python docs without any tutorials.
-
- arguments:
- full -- Build all language docs, not just Python (cannot be used with
--cpu)
- cpu -- Use the 'ci_cpu' Docker image (useful for building docs on a
machine without a GPU)
- tutorial-pattern -- Regex for which tutorials to execute when building
docs (cannot be used with --cpu)
- skip_build -- skip build and setup scripts
- interactive -- start a shell after running build / test scripts
- docker-image -- manually specify the docker image to use
- """
- build_dir = get_build_dir("gpu")
-
- extra_setup = []
- image = "ci_gpu" if docker_image is None else docker_image
- if cpu:
- # TODO: Change this to tlcpack/docs once that is uploaded
- image = "ci_cpu" if docker_image is None else docker_image
- build_dir = get_build_dir("cpu")
- config_script = " && ".join(
- [
- f"mkdir -p {build_dir}",
- f"pushd {build_dir}",
- "cp ../cmake/config.cmake .",
- "popd",
- ]
- )
-
- # These are taken from the ci-gpu image via pip freeze, consult that
- # if there are any changes:
https://github.com/apache/tvm/tree/main/docs#native
- requirements = [
- "Sphinx==8.1.3",
- "sphinx_autodoc_annotation~=1.0",
- "sphinx-gallery==0.20.0",
- "sphinx-book-theme==1.1.4",
- "pydata-sphinx-theme==0.15.4",
- "autodocsumm==0.2.14",
- "image==1.5.33",
- "matplotlib==3.10.8",
- "commonmark==0.9.1",
- "docutils==0.21.2",
- "Pillow==12.1.1",
- ]
-
- extra_setup = [
- "uv pip install " + " ".join(requirements),
- ]
- else:
- check_gpu()
- config_script = f"./tests/scripts/task_config_build_gpu.sh {build_dir}"
-
- scripts = extra_setup + [
- config_script,
- f"./tests/scripts/task_build.py --build-dir {build_dir}",
- ]
-
- if skip_build:
- scripts = []
-
- scripts.append("./tests/scripts/task_python_docs.sh")
-
- if tutorial_pattern is None:
- tutorial_pattern = os.getenv("TVM_TUTORIAL_EXEC_PATTERN", ".py" if
full else "none")
-
- env = {
- "TVM_TUTORIAL_EXEC_PATTERN": tutorial_pattern,
- "PYTHON_DOCS_ONLY": "0" if full else "1",
- "IS_LOCAL": "1",
- "TVM_LIBRARY_PATH": str(REPO_ROOT / build_dir),
- }
- docker(name=gen_name("docs"), image=image, scripts=scripts, env=env,
interactive=interactive)
- print_color(
- col.GREEN,
- "Done building the docs. You can view them by running "
- "'python3 tests/scripts/ci.py serve-docs' and visiting:"
- " http://localhost:8000 in your browser.",
- bold=True,
- )
-
-
-def serve_docs(directory: str = "_docs") -> None:
- """
- Serve the docs using Python's http server
-
- arguments:
- directory -- Directory to serve from
- """
- directory_path = Path(directory)
- if not directory_path.exists():
- clean_exit("Docs have not been built, run 'ci.py docs' first")
- cmd([sys.executable, "-m", "http.server"], cwd=directory_path)
-
-
-def lint(interactive: bool = False, docker_image: str | None = None) -> None:
- """
- Run lint checks locally.
-
- arguments:
- interactive -- start a shell after running build / test scripts when using
--docker-image
- docker-image -- manually specify a docker image to use
- """
- scripts = ["pre-commit run --all-files"]
- if docker_image is None:
- if interactive:
- clean_exit("--interactive requires --docker-image")
- cmd(scripts[0].split())
- return
-
- docker(
- name=gen_name("lint"),
- image=docker_image,
- scripts=scripts,
- env={},
- interactive=interactive,
- )
-
-
-Option = tuple[str, list[str]]
-
-
-def generate_command(
- name: str,
- options: dict[str, Option],
- help: str,
- precheck: Callable[[], None] | None = None,
- post_build: list[str] | None = None,
- additional_flags: dict[str, str] | None = None,
- env: dict[str, str] | None = None,
-):
- """
- Helper to generate CLIs that:
- 1. Build a with a config matching a specific CI Docker image (e.g. 'cpu')
- 2. Run tests (either a pre-defined set from scripts or manually via
invoking
- pytest)
- 3. (optional) Drop down into a terminal into the Docker container
- """
-
- def fn(
- tests: list[str] | None = None,
- skip_build: bool = False,
- interactive: bool = False,
- docker_image: str | None = None,
- verbose: bool = False,
- **kwargs,
- ) -> None:
- """
- arguments:
- tests -- pytest test IDs (e.g. tests/python or
tests/python/a_file.py::a_test[param=1])
- skip_build -- skip build and setup scripts
- interactive -- start a shell after running build / test scripts
- docker-image -- manually specify the docker image to use
- verbose -- run verbose build
- """
- if precheck is not None:
- precheck()
-
- build_dir = get_build_dir(name)
-
- if skip_build:
- scripts = []
- else:
- scripts = [
- f"./tests/scripts/task_config_build_{name}.sh {build_dir}",
- f"./tests/scripts/task_build.py --build-dir {build_dir}",
- ]
-
- if post_build is not None:
- scripts += post_build
-
- # Check that a test suite was not used alongside specific test names
- if any(v for v in kwargs.values()) and tests is not None:
- option_flags = ", ".join([f"--{k}" for k in options.keys()])
- clean_exit(f"{option_flags} cannot be used with --tests")
-
- if tests is not None:
- scripts.append(f"python3 -m pytest {' '.join(tests)}")
-
- # Add named test suites
- for option_name, (_, extra_scripts) in options.items():
- if kwargs.get(option_name, False):
- scripts.extend(script.format(build_dir=build_dir) for script
in extra_scripts)
-
- docker_env = {
- # Need to specify the library path manually or else TVM can't
- # determine which build directory to use (i.e. if there are
- # multiple copies of libtvm_compiler.so / libtvm_runtime.so laying
around)
- "TVM_LIBRARY_PATH": str(REPO_ROOT / get_build_dir(name)),
- "VERBOSE": "true" if verbose else "false",
- }
-
- if env is not None:
- docker_env.update(env)
-
- docker(
- name=gen_name(f"ci-{name}"),
- image=f"ci_{name}" if docker_image is None else docker_image,
- scripts=scripts,
- env=docker_env,
- interactive=interactive,
- additional_flags=additional_flags,
- )
-
- fn.__name__ = name
-
- return fn, options, help
-
-
-def check_arm_qemu() -> None:
- """
- Check if a machine is ready to run an ARM Docker image
- """
- machine = platform.machine().lower()
- if "arm" in machine or "aarch64" in machine:
- # No need to check anything if the machine runs ARM
- return
-
- binfmt = Path("/proc/sys/fs/binfmt_misc")
- if not binfmt.exists() or len(list(binfmt.glob("qemu-*"))) == 0:
- clean_exit(
- textwrap.dedent(
- """
- You must run a one-time setup to use ARM containers on x86 via QEMU:
-
- sudo apt install -y qemu binfmt-support qemu-user-static
- docker run --rm --privileged multiarch/qemu-user-static --reset -p
yes
-
- See
https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/ for
details""".strip(
- "\n"
- )
- )
- )
-
-
-def cli_name(s: str) -> str:
- return s.replace("_", "-")
-
-
-def typing_get_origin(annotation):
- return typing.get_origin(annotation)
-
-
-def typing_get_args(annotation):
- return typing.get_args(annotation)
-
-
-def is_optional_type(annotation):
- return (
- hasattr(annotation, "__origin__")
- and (typing_get_origin(annotation) == typing.Union)
- and (type(None) in typing_get_args(annotation))
- )
-
-
-def add_subparser(
- func: Callable,
- subparsers: Any,
- options: dict[str, Option] | None = None,
- help: str | None = None,
-) -> Any:
- """
- Utility function to make it so subparser commands can be defined locally
- as a function rather than directly via argparse and manually dispatched
- out.
- """
-
- # Each function is intended follow the example for arguments in PEP257, so
- # split apart the function documentation from the arguments
- split = [s.strip() for s in func.__doc__.split("arguments:\n")]
- if len(split) == 1:
- args_help = None
- command_help = split[0]
- else:
- command_help, args_help = split
-
- if help is not None:
- command_help = help
-
- # Parse out the help text for each argument if present
- arg_help_texts = {}
- if args_help is not None:
- for line in args_help.split("\n"):
- line = line.strip()
- name, help_text = [t.strip() for t in line.split(" -- ")]
- arg_help_texts[cli_name(name)] = help_text
-
- subparser = subparsers.add_parser(cli_name(func.__name__),
help=command_help)
-
- seen_prefixes = set()
-
- # Add each parameter to the subparser
- signature = inspect.signature(func)
- for name, value in signature.parameters.items():
- if name == "kwargs":
- continue
-
- arg_cli_name = cli_name(name)
- kwargs: dict[str, str | bool] = {"help": arg_help_texts[arg_cli_name]}
-
- is_optional = is_optional_type(value.annotation)
- if is_optional:
- arg_type = typing_get_args(value.annotation)[0]
- else:
- arg_type = value.annotation
-
- # Grab the default value if present
- has_default = False
- if value.default is not value.empty:
- kwargs["default"] = value.default
- has_default = True
-
- # Check if it should be a flag
- if arg_type is bool:
- kwargs["action"] = "store_true"
- else:
- kwargs["required"] = not is_optional and not has_default
-
- if str(arg_type).startswith("typing.List"):
- kwargs["action"] = "append"
-
- if arg_cli_name[0] not in seen_prefixes:
- subparser.add_argument(f"-{arg_cli_name[0]}", f"--{arg_cli_name}",
**kwargs)
- seen_prefixes.add(arg_cli_name[0])
- else:
- subparser.add_argument(f"--{arg_cli_name}", **kwargs)
-
- if options is not None:
- for option_name, (help, _) in options.items():
- option_cli_name = cli_name(option_name)
- if option_cli_name[0] not in seen_prefixes:
- subparser.add_argument(
- f"-{option_cli_name[0]}", f"--{option_cli_name}",
action="store_true", help=help
- )
- seen_prefixes.add(option_cli_name[0])
- else:
- subparser.add_argument(f"--{option_cli_name}",
action="store_true", help=help)
-
- return subparser
-
-
-CPP_UNITTEST = ("run c++ unitests", ["./tests/scripts/task_cpp_unittest.sh
{build_dir}"])
-
-generated = [
- generate_command(
- name="gpu",
- help="Run GPU build and test(s)",
- options={
- "cpp": CPP_UNITTEST,
- "unittest": (
- "run unit tests",
- [
- "./tests/scripts/task_java_unittest.sh",
- "./tests/scripts/task_python_unittest_gpuonly.sh",
- ],
- ),
- },
- ),
- generate_command(
- name="cpu",
- help="Run CPU build and test(s)",
- options={
- "cpp": CPP_UNITTEST,
- "unittest": (
- "run unit tests",
- [
- "./tests/scripts/task_python_unittest.sh",
- ],
- ),
- },
- ),
- generate_command(
- name="wasm",
- help="Run WASM build and test(s)",
- options={
- "cpp": CPP_UNITTEST,
- "test": ("run WASM tests", ["./tests/scripts/task_web_wasm.sh"]),
- },
- ),
- generate_command(
- name="arm",
- help="Run ARM build and test(s) (native or via QEMU on x86)",
- precheck=check_arm_qemu,
- options={
- "cpp": CPP_UNITTEST,
- "python": (
- "run full Python tests",
- [
- "./tests/scripts/task_python_unittest.sh",
- ],
- ),
- },
- ),
-]
-
-
-def main():
- description = """
- Run CI jobs locally via Docker. This facilitates reproducing CI failures
for
- fast iteration. Note that many of the Docker images required are large (the
- CPU and GPU images are both over 25GB) and may take some time to download
on first use.
- """
- parser = argparse.ArgumentParser(description=description)
- subparsers = parser.add_subparsers(dest="command")
-
- commands = {}
-
- # Add manually defined commands
- for func in [docs, serve_docs, lint]:
- add_subparser(func, subparsers)
- commands[cli_name(func.__name__)] = func
-
- # Add generated commands
- for func, options, help in generated:
- add_subparser(func, subparsers, options, help)
- commands[cli_name(func.__name__)] = func
-
- args = parser.parse_args()
-
- if args.command is None:
- # Command not found in list, error out
- parser.print_help()
- exit(1)
-
- func = commands[args.command]
-
- # Extract out the parsed args and invoke the relevant function
- kwargs = {k: getattr(args, k) for k in dir(args) if not k.startswith("_")
and k != "command"}
- func(**kwargs)
-
-
-if __name__ == "__main__":
- main()
diff --git a/tests/scripts/task_build.py b/tests/scripts/task_build.py
deleted file mode 100755
index 617e8b1a1b..0000000000
--- a/tests/scripts/task_build.py
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/env python3
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-import argparse
-import logging
-import multiprocessing
-import os
-import shlex
-import shutil
-import subprocess
-import sys
-from pathlib import Path
-
-REPO_ROOT = Path(__file__).resolve().parent.parent.parent
-
-if __name__ == "__main__":
- logging.basicConfig(
- format="[%(filename)s:%(lineno)d %(levelname)s] %(message)s",
level=logging.INFO
- )
-
- parser = argparse.ArgumentParser(
- description="Build TVM with optional settings from the CMAKE_ARGS
environment variable"
- )
- parser.add_argument("--sccache-bucket", required=False, help="sccache
bucket name")
- parser.add_argument("--sccache-region", required=False, help="sccache
region")
- parser.add_argument("--build-dir", default="build", help="build folder")
- parser.add_argument(
- "--cmake-target", action="append", default=[], help="build target
(repeatable)"
- )
- parser.add_argument(
- "--ffi-only", action="store_true", help="prepare the source-pinned FFI
package"
- )
- parser.add_argument("--debug", required=False, action="store_true",
help="build in debug mode")
- args = parser.parse_args()
- sccache_exe = shutil.which("sccache")
-
- if args.cmake_target and all(
- target in {"standalone_crt", "crttest"} for target in args.cmake_target
- ):
- logging.info("Skipping standalone_crt build")
- exit(0)
-
- use_sccache = sccache_exe is not None
- build_dir = Path(os.getcwd()) / args.build_dir
- build_dir = build_dir.relative_to(REPO_ROOT)
- env = os.environ.copy()
- # CMAKE_ARGS configures TVM, not the separate source-FFI Python package.
- cmake_args = shlex.split(env.pop("CMAKE_ARGS", ""))
- env["PYTHONPATH"] = os.pathsep.join(
- [
- str(REPO_ROOT / "tirx" / "python"),
- str(REPO_ROOT / "python"),
- os.environ.get("PYTHONPATH", ""),
- ]
- )
-
- if use_sccache:
- if args.sccache_bucket and "AWS_ACCESS_KEY_ID" in os.environ:
- env["SCCACHE_BUCKET"] = args.sccache_bucket
- env["SCCACHE_REGION"] = args.sccache_region if args.sccache_region
else "us-west-2"
- logging.info(f"Using sccache bucket: {args.sccache_bucket}")
- logging.info(f"Using sccache region: {env['SCCACHE_REGION']}")
- else:
- logging.info("No sccache bucket set, using local cache")
- if "SCCACHE_SERVER_PORT" in os.environ:
- env["SCCACHE_SERVER_PORT"] = os.getenv("SCCACHE_SERVER_PORT")
- env["CXX"] = "/opt/sccache/c++"
- env["CC"] = "/opt/sccache/cc"
-
- else:
- if sccache_exe is None:
- reason = "'sccache' executable not found"
- else:
- reason = "<unknown>"
- logging.info(f"Not using sccache, reason: {reason}")
-
- # Build from this checkout's pinned submodule once, then transfer the exact
- # Python/native package together with the TVM libraries to test workers.
- ffi_commit = subprocess.check_output(
- ["git", "-C", str(REPO_ROOT / "tirx" / "3rdparty" / "tvm-ffi"),
"rev-parse", "HEAD"],
- text=True,
- ).strip()
- ffi_identity = f"{ffi_commit} {sys.implementation.cache_tag}"
- ffi_marker = REPO_ROOT / "python" / "tvm_ffi" / ".source-commit"
- if not ffi_marker.is_file() or ffi_marker.read_text().strip() !=
ffi_identity:
- subprocess.run(
- [
- "uv",
- "pip",
- "install",
- "--python",
- sys.executable,
- "-v",
- "--no-deps",
- "--reinstall",
- "--target=python",
- "./tirx/3rdparty/tvm-ffi/",
- ],
- cwd=REPO_ROOT,
- env=env,
- check=True,
- )
- ffi_marker.write_text(ffi_identity + "\n")
- if args.ffi_only:
- sys.exit(0)
-
- if use_sccache:
- subprocess.run(["sccache", "--start-server"], env=env, check=False)
- logging.info("===== sccache stats =====")
- subprocess.run(["sccache", "--show-stats"], env=env, check=True)
-
- executors = int(os.environ.get("CI_NUM_EXECUTORS", 1))
- build_platform = os.environ.get("PLATFORM", None)
-
- nproc = multiprocessing.cpu_count()
-
- available_cpus = nproc // executors
- num_cpus = max(available_cpus, 1)
-
- if "tirx_cpptest" in args.cmake_target:
- cmake_args.append("-DTVM_TIRX_BUILD_TESTS=ON")
-
- build_type = "Debug" if args.debug else "RelWithDebInfo"
- subprocess.run(
- ["cmake", "-GNinja", f"-DCMAKE_BUILD_TYPE={build_type}", *cmake_args,
".."],
- cwd=build_dir,
- env=env,
- check=True,
- )
-
- ninja_args = [*args.cmake_target, f"-j{num_cpus}"]
- if os.environ.get("VERBOSE", "true").lower() in {"1", "true", "yes"}:
- ninja_args.append("-v")
- subprocess.run(["cmake", "--build", ".", "--", *ninja_args],
cwd=build_dir, env=env, check=True)
-
- if use_sccache:
- logging.info("===== sccache stats =====")
- subprocess.run(["sccache", "--show-stats"], env=env, check=True)
diff --git a/tests/scripts/task_build_arm.sh b/tests/scripts/task_build_arm.sh
new file mode 100755
index 0000000000..c385fe0230
--- /dev/null
+++ b/tests/scripts/task_build_arm.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euxo pipefail
+
+export SKBUILD_CMAKE_BUILD_TYPE=RelWithDebInfo
+export SKBUILD_CMAKE_SOURCE_DIR="$PWD"
+export SKBUILD_BUILD_DIR="$PWD/build"
+export SKBUILD_BUILD_TARGETS="all;cpptest"
+export CMAKE_ARGS="
+ -DTVM_VERSION=$(python3 -m setuptools_scm)
+ -DTVM_BUILD_PYTHON_MODULE=ON
+ -DTVM_TIRX_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_TESTS=OFF
+ -DTVM_FFI_ATTACH_DEBUG_SYMBOLS=ON
+ -DUSE_SORT=ON
+ -DUSE_RPC=ON
+ -DCMAKE_CXX_FLAGS=-Werror
+ -DUSE_CCACHE=OFF
+ -DSUMMARIZE=ON
+ -DTVM_USE_INSTALLED_TIRX=OFF
+ -DUSE_GTEST=ON
+ -DBUILD_TESTING=ON
+ -DTVM_TIRX_BUILD_TESTS=ON
+ -DTVM_TIRX_USE_LLVM=llvm-config-17
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx/3rdparty/tvm-ffi
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx
+uv build --wheel --no-build-isolation --offline --python python3
+
+./build/tirx_cpptest
+./build/cpptest
diff --git a/tests/scripts/task_build_cpu.sh b/tests/scripts/task_build_cpu.sh
new file mode 100755
index 0000000000..732cedd360
--- /dev/null
+++ b/tests/scripts/task_build_cpu.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euxo pipefail
+
+export SKBUILD_CMAKE_BUILD_TYPE=RelWithDebInfo
+export SKBUILD_CMAKE_SOURCE_DIR="$PWD"
+export SKBUILD_BUILD_DIR="$PWD/build"
+export SKBUILD_BUILD_TARGETS="all;cpptest"
+export CMAKE_ARGS="
+ -DTVM_VERSION=$(python3 -m setuptools_scm)
+ -DTVM_BUILD_PYTHON_MODULE=ON
+ -DTVM_TIRX_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_TESTS=OFF
+ -DTVM_FFI_ATTACH_DEBUG_SYMBOLS=ON
+ -DUSE_SORT=ON
+ -DUSE_DNNL=ON
+ -DUSE_EXAMPLE_NPU_CODEGEN=ON
+ -DUSE_EXAMPLE_NPU_RUNTIME=ON
+ -DCMAKE_CXX_FLAGS='-Wno-error=range-loop-construct -Wno-error=comment'
+ -DHIDE_PRIVATE_SYMBOLS=ON
+ -DUSE_TENSORFLOW_PATH=/tensorflow
+ -DUSE_TFLITE=/opt/tflite
+ -DUSE_FLATBUFFERS_PATH=/flatbuffers
+ -DUSE_CCACHE=OFF
+ -DSUMMARIZE=ON
+ -DTVM_USE_INSTALLED_TIRX=OFF
+ -DUSE_GTEST=ON
+ -DBUILD_TESTING=ON
+ -DTVM_TIRX_BUILD_TESTS=ON
+ -DTVM_TIRX_USE_LLVM='/usr/bin/llvm-config-17 --link-static'
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx/3rdparty/tvm-ffi
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx
+uv build --wheel --no-build-isolation --offline --python python3
+
+./build/tirx_cpptest
+./build/cpptest
diff --git a/tests/scripts/task_build_gpu.sh b/tests/scripts/task_build_gpu.sh
new file mode 100755
index 0000000000..15df597dff
--- /dev/null
+++ b/tests/scripts/task_build_gpu.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euxo pipefail
+
+export SKBUILD_CMAKE_BUILD_TYPE=RelWithDebInfo
+export SKBUILD_CMAKE_SOURCE_DIR="$PWD"
+export SKBUILD_BUILD_DIR="$PWD/build"
+export SKBUILD_BUILD_TARGETS=all
+export CMAKE_ARGS="
+ -DTVM_VERSION=$(python3 -m setuptools_scm)
+ -DTVM_BUILD_PYTHON_MODULE=ON
+ -DTVM_TIRX_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_TESTS=OFF
+ -DTVM_FFI_ATTACH_DEBUG_SYMBOLS=ON
+ -DUSE_CUBLAS=ON
+ -DUSE_CUDNN=ON
+ -DTVM_TIRX_USE_CUDA=ON
+ -DUSE_RPC=ON
+ -DUSE_SORT=ON
+ -DUSE_BLAS=openblas
+ -DCMAKE_CXX_FLAGS=-Werror
+ -DUSE_TENSORRT_CODEGEN=ON
+ -DHIDE_PRIVATE_SYMBOLS=ON
+ -DUSE_CUTLASS=ON
+ -DCMAKE_CUDA_ARCHITECTURES=75
+ -DUSE_CLML=ON
+ -DUSE_CCACHE=OFF
+ -DSUMMARIZE=ON
+ -DTVM_USE_INSTALLED_TIRX=OFF
+ -DBUILD_TESTING=OFF
+ -DTVM_TIRX_BUILD_TESTS=OFF
+ -DTVM_TIRX_USE_VULKAN=ON
+ -DTVM_TIRX_USE_OPENCL=ON
+ -DTVM_TIRX_USE_OPENCL_GTEST=/googletest
+ -DTVM_TIRX_USE_LLVM='/usr/bin/llvm-config-15 --link-static'
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx/3rdparty/tvm-ffi
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx
+uv build --wheel --no-build-isolation --offline --python python3
diff --git a/tests/scripts/task_config_build_gpu_other.sh
b/tests/scripts/task_build_gpu_other.sh
similarity index 68%
rename from tests/scripts/task_config_build_gpu_other.sh
rename to tests/scripts/task_build_gpu_other.sh
index de5b94b5dc..59f33eaeac 100755
--- a/tests/scripts/task_config_build_gpu_other.sh
+++ b/tests/scripts/task_build_gpu_other.sh
@@ -20,13 +20,10 @@
# correctly, even if they aren't actively tested in the CI.
set -euxo pipefail
-BUILD_DIR=$1
-mkdir -p "$BUILD_DIR"
-cd "$BUILD_DIR"
-cp ../cmake/config.cmake .
-
-echo set\(TVM_TIRX_USE_OPENCL ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_ROCM ON\) >> config.cmake
-echo set\(CMAKE_CXX_FLAGS -Werror\) >> config.cmake
-echo set\(USE_CCACHE OFF\) >> config.cmake
-echo set\(SUMMARIZE ON\) >> config.cmake
+# Preserve the separate runtime-only GPU compile check.
+uv pip install --python python3 --no-deps --target build/python
./tirx/3rdparty/tvm-ffi
+export PYTHONPATH="$PWD/build/python"
+cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+ -DTVM_TIRX_USE_OPENCL=ON -DTVM_TIRX_USE_ROCM=ON -DCMAKE_CXX_FLAGS=-Werror \
+ -DUSE_CCACHE=OFF -DSUMMARIZE=ON
+cmake --build build --target tvm_runtime tvm_runtime_opencl tvm_runtime_rocm
diff --git a/tests/scripts/task_build_wasm.sh b/tests/scripts/task_build_wasm.sh
new file mode 100755
index 0000000000..0ea957c526
--- /dev/null
+++ b/tests/scripts/task_build_wasm.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euxo pipefail
+
+export SKBUILD_CMAKE_BUILD_TYPE=RelWithDebInfo
+export SKBUILD_CMAKE_SOURCE_DIR="$PWD"
+export SKBUILD_BUILD_DIR="$PWD/build"
+export SKBUILD_BUILD_TARGETS="all;cpptest"
+export CMAKE_ARGS="
+ -DTVM_VERSION=$(python3 -m setuptools_scm)
+ -DTVM_BUILD_PYTHON_MODULE=ON
+ -DTVM_TIRX_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_PYTHON_MODULE=ON
+ -DTVM_FFI_BUILD_TESTS=OFF
+ -DTVM_FFI_ATTACH_DEBUG_SYMBOLS=ON
+ -DUSE_SORT=ON
+ -DTVM_TIRX_USE_LLVM=llvm-config-15
+ -DCMAKE_CXX_FLAGS=-Werror
+ -DHIDE_PRIVATE_SYMBOLS=ON
+ -DUSE_CCACHE=OFF
+ -DSUMMARIZE=ON
+ -DTVM_USE_INSTALLED_TIRX=OFF
+ -DUSE_GTEST=ON
+ -DBUILD_TESTING=ON
+ -DTVM_TIRX_BUILD_TESTS=ON
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx/3rdparty/tvm-ffi
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx
+uv build --wheel --no-build-isolation --offline --python python3
+
+./build/tirx_cpptest
+./build/cpptest
diff --git a/tests/scripts/task_config_build_cpu.sh
b/tests/scripts/task_config_build_cpu.sh
deleted file mode 100755
index 6a8586db5b..0000000000
--- a/tests/scripts/task_config_build_cpu.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-set -euxo pipefail
-
-BUILD_DIR=$1
-mkdir -p "$BUILD_DIR"
-cd "$BUILD_DIR"
-cp ../cmake/config.cmake .
-
-echo set\(USE_SORT ON\) >> config.cmake
-echo set\(USE_DNNL ON\) >> config.cmake
-echo set\(USE_EXAMPLE_NPU_CODEGEN ON\) >> config.cmake
-echo set\(USE_EXAMPLE_NPU_RUNTIME ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_LLVM \"/usr/bin/llvm-config-17 --link-static\"\) >>
config.cmake
-echo set\(CMAKE_CXX_FLAGS \"-Wno-error=range-loop-construct
-Wno-error=comment\"\) >> config.cmake
-echo set\(HIDE_PRIVATE_SYMBOLS ON\) >> config.cmake
-echo set\(USE_TENSORFLOW_PATH \"/tensorflow\"\) >> config.cmake
-echo set\(USE_TFLITE \"/opt/tflite\"\) >> config.cmake
-echo set\(USE_FLATBUFFERS_PATH \"/flatbuffers\"\) >> config.cmake
-echo set\(USE_CCACHE OFF\) >> config.cmake
-echo set\(SUMMARIZE ON\) >> config.cmake
diff --git a/tests/scripts/task_config_build_gpu.sh
b/tests/scripts/task_config_build_gpu.sh
deleted file mode 100755
index 2f929a9e20..0000000000
--- a/tests/scripts/task_config_build_gpu.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-set -euxo pipefail
-
-BUILD_DIR=$1
-mkdir -p "$BUILD_DIR"
-cd "$BUILD_DIR"
-cp ../cmake/config.cmake .
-
-echo set\(USE_CUBLAS ON\) >> config.cmake
-echo set\(USE_CUDNN ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_CUDA ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_VULKAN ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_OPENCL ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_OPENCL_GTEST \"/googletest\"\) >> config.cmake
-echo set\(TVM_TIRX_USE_LLVM \"/usr/bin/llvm-config-15 --link-static\"\) >>
config.cmake
-echo set\(USE_RPC ON\) >> config.cmake
-echo set\(USE_SORT ON\) >> config.cmake
-echo set\(USE_BLAS openblas\) >> config.cmake
-echo set\(CMAKE_CXX_FLAGS -Werror\) >> config.cmake
-echo set\(USE_TENSORRT_CODEGEN ON\) >> config.cmake
-echo set\(USE_CCACHE OFF\) >> config.cmake
-echo set\(SUMMARIZE ON\) >> config.cmake
-echo set\(HIDE_PRIVATE_SYMBOLS ON\) >> config.cmake
-echo set\(USE_CUTLASS ON\) >> config.cmake
-echo set\(CMAKE_CUDA_ARCHITECTURES 75\) >> config.cmake
-echo set\(USE_CLML ON\) >> config.cmake
diff --git a/tests/scripts/task_config_build_arm.sh
b/tests/scripts/task_install_build_dependencies.sh
similarity index 69%
rename from tests/scripts/task_config_build_arm.sh
rename to tests/scripts/task_install_build_dependencies.sh
index 60778f5c11..fe1484ab7b 100755
--- a/tests/scripts/task_config_build_arm.sh
+++ b/tests/scripts/task_install_build_dependencies.sh
@@ -18,14 +18,7 @@
set -euxo pipefail
-BUILD_DIR=$1
-mkdir -p "$BUILD_DIR"
-cd "$BUILD_DIR"
-cp ../cmake/config.cmake .
-
-echo set\(USE_SORT ON\) >> config.cmake
-echo set\(USE_RPC ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_LLVM llvm-config-17\) >> config.cmake
-echo set\(CMAKE_CXX_FLAGS -Werror\) >> config.cmake
-echo set\(USE_CCACHE OFF\) >> config.cmake
-echo set\(SUMMARIZE ON\) >> config.cmake
+rm -rf dist
+uv pip install --python python3 --target .local/python \
+ "scikit-build-core>=0.11,!=1.1.0" "setuptools-scm>=8" "cython>=3.2.8"
"mlc-z3-static>=4.16.0"
+export PYTHONPATH="$PWD/.local/python"
diff --git a/tests/scripts/task_web_wasm.sh
b/tests/scripts/task_install_docs_dependencies.sh
similarity index 67%
copy from tests/scripts/task_web_wasm.sh
copy to tests/scripts/task_install_docs_dependencies.sh
index f901d13fee..2ec999876f 100755
--- a/tests/scripts/task_web_wasm.sh
+++ b/tests/scripts/task_install_docs_dependencies.sh
@@ -18,19 +18,16 @@
set -euxo pipefail
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-
-# setup tvm-ffi into python folder
-uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
-rm -rf .emscripten_cache
-cd web
-export EM_CACHE=`pwd`/.emscripten_cache
-make clean
-npm install
-npm run lint
-npm run prepwasm
-npm run bundle
-npm run test
-npm run typedoc
-cd ..
+# Additional documentation dependencies for the CPU CI image.
+uv pip install \
+ 'Sphinx==8.1.3' \
+ 'sphinx_autodoc_annotation~=1.0' \
+ 'sphinx-gallery==0.20.0' \
+ 'sphinx-book-theme==1.1.4' \
+ 'pydata-sphinx-theme==0.15.4' \
+ 'autodocsumm==0.2.14' \
+ 'image==1.5.33' \
+ 'matplotlib==3.10.8' \
+ 'commonmark==0.9.1' \
+ 'docutils==0.21.2' \
+ 'Pillow==12.1.1'
diff --git a/tests/scripts/task_java_unittest.sh
b/tests/scripts/task_java_unittest.sh
index 29342c70c8..b76a54ad31 100755
--- a/tests/scripts/task_java_unittest.sh
+++ b/tests/scripts/task_java_unittest.sh
@@ -18,9 +18,6 @@
set -euxo pipefail
-export PYTHONPATH=tirx/python:python
-export LD_LIBRARY_PATH="lib:${LD_LIBRARY_PATH:-}"
-
# to avoid CI CPU thread throttling.
export TVM_BIND_THREADS=0
export OMP_NUM_THREADS=1
diff --git a/tests/scripts/task_jvm_build.sh b/tests/scripts/task_jvm_build.sh
index 60c0c725cb..ae2587e494 100755
--- a/tests/scripts/task_jvm_build.sh
+++ b/tests/scripts/task_jvm_build.sh
@@ -33,13 +33,21 @@ fi
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
TVM_BUILD_PATH="${TVM_BUILD_PATH:-${ROOTDIR}/build}"
-TVM_BUILD_PATH="$(realpath "${TVM_BUILD_PATH}")"
-DLPACK_PATH="${DLPACK_PATH:-${ROOTDIR}/tirx/3rdparty/tvm-ffi/3rdparty/dlpack}"
-
-INCLUDE_FLAGS="-I${ROOTDIR}/include -I${DLPACK_PATH}/include"
-PKG_CFLAGS="-Wall -O3 ${INCLUDE_FLAGS} -fPIC"
-PKG_LDFLAGS=""
+TVM_INCLUDE_DIR="$(python3 -m tvm_tirx.config --includedir)"
+FFI_INCLUDE_DIR="$(python3 -m tvm_ffi.config --includedir)"
+TVM_LIB_DIR="$(python3 -m tvm_tirx.config --libdir)"
+FFI_LIB_DIR="$(python3 -m tvm_ffi.config --libdir)"
+PKG_CFLAGS="-Wall -O3 -fPIC -I${TVM_INCLUDE_DIR} $(python3 -m tvm_ffi.config
--cflags)"
+PKG_LDFLAGS="-L${TVM_LIB_DIR} -ltvm_runtime $(python3 -m tvm_ffi.config
--ldflags --libs)"
+# Each config flag prints one line; Maven expects one compiler/linker argument
list.
+PKG_LDFLAGS="${PKG_LDFLAGS//$'\n'/ }"
+NATIVE_ARGS=(
+ "-Dtvm.include.dir=${TVM_INCLUDE_DIR}"
+ "-Dtvm.ffi.include.dir=${FFI_INCLUDE_DIR}"
+ "-Dtvm.lib.dir=${TVM_LIB_DIR}"
+ "-Dtvm.ffi.lib.dir=${FFI_LIB_DIR}"
+)
if [ "$(uname -s)" = "Darwin" ]; then
JVM_PKG_PROFILE="osx-x86_64"
@@ -64,5 +72,6 @@ mvn ${GOAL} \
"-Dcflags=${PKG_CFLAGS}" \
"-Dldflags=${PKG_LDFLAGS}" \
"-Dcurrent_libdir=${TVM_BUILD_PATH}" \
+ "${NATIVE_ARGS[@]}" \
${JVM_TEST_ARGS} \
"$@"
diff --git a/tests/scripts/task_python_docs.sh
b/tests/scripts/task_python_docs.sh
index 57544bee2b..bdd960321d 100755
--- a/tests/scripts/task_python_docs.sh
+++ b/tests/scripts/task_python_docs.sh
@@ -18,8 +18,6 @@
set -euxo pipefail
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-
# to avoid CI CPU thread throttling.
export TVM_BIND_THREADS=0
export OMP_NUM_THREADS=1
@@ -47,13 +45,10 @@ sphinx_precheck() {
clean_files
echo "PreCheck sphinx doc generation WARNINGS.."
- # setup tvm-ffi into python folder
- uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
pushd docs
make clean
TVM_TUTORIAL_EXEC_PATTERN=none make html 2>&1 | tee /tmp/$$.log.txt
- check_sphinx_warnings "docs"
+ check_sphinx_warnings
popd
}
@@ -106,7 +101,10 @@ check_sphinx_warnings() {
if grep --quiet -E "WARN" < /tmp/$$.logclean.txt; then
echo "Lines with 'WARNING' found in the log, please fix them:"
grep -E "WARN" < /tmp/$$.logclean.txt
- echo "You can reproduce locally by running 'python tests/scripts/ci.py
$1'"
+ echo "First follow the GPU wheel build steps in docs/README.md (Helper
Scripts)."
+ echo "Then reproduce from the repository root with:"
+ echo "docker/bash.sh --env IS_LOCAL=1 --env PYTHON_DOCS_ONLY=1 --env
TVM_TUTORIAL_EXEC_PATTERN=none ci_gpu bash -euo pipefail -c 'source
tests/scripts/test_task_install_dist_wheels.sh;
tests/scripts/task_python_docs.sh'"
+ echo "For tutorial failures, replace TVM_TUTORIAL_EXEC_PATTERN=none
with TVM_TUTORIAL_EXEC_PATTERN=.py."
exit 1
fi
echo "No WARNINGS to be fixed."
@@ -129,21 +127,16 @@ clean_files
find . -type f -path "*.log" | xargs rm -f
find . -type f -path "*.pyc" | xargs rm -f
-# setup tvm-ffi into python folder
-uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
-# LLVMModule JIT execution, needed by the tutorials this build runs.
-uv pip install --no-deps apache-tvm-ffi-orcjit==0.1.2
cd docs
-PYTHONPATH=$(pwd)/../tirx/python:$(pwd)/../python make htmldepoly
SPHINXOPTS='-j auto' |& tee /tmp/$$.log.txt
+make htmldepoly SPHINXOPTS='-j auto' |& tee /tmp/$$.log.txt
if grep -E "failed to execute|Segmentation fault" < /tmp/$$.log.txt; then
echo "Some of sphinx-gallery item example failed to execute."
exit 1
fi
-check_sphinx_warnings "docs --tutorial-pattern=.*"
+check_sphinx_warnings
cd ..
diff --git a/tests/scripts/task_python_unittest.sh
b/tests/scripts/task_python_unittest.sh
index 9b319b7a67..a865d363e3 100755
--- a/tests/scripts/task_python_unittest.sh
+++ b/tests/scripts/task_python_unittest.sh
@@ -17,16 +17,9 @@
# under the License.
set -euxo pipefail
-
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
export PYTEST_ADDOPTS="${CI_PYTEST_ADD_OPTIONS:-} ${PYTEST_ADDOPTS:-}"
-# Reuse the exact source-pinned FFI package transferred by the native build.
-python3 tests/scripts/task_build.py --ffi-only
-
-# LLVMModule JIT execution. Installed here as well as in the CI image because
a docker/
-# change does not reach the test containers in the same run (they use the
pinned tag).
-# --no-deps keeps the submodule-built tvm-ffi above from being replaced by a
PyPI build.
-uv pip install --no-deps apache-tvm-ffi-orcjit==0.1.2
-
-python3 -m pytest -vvs -n auto -o faulthandler_timeout=120 -m
"${TVM_TEST_MARKER:-not gpu}" tests/python tirx/tests/python
+if [ "$#" -eq 0 ]; then
+ set -- tests/python tirx/tests/python
+fi
+python3 -m pytest -vvs -n auto -o faulthandler_timeout=120 -m
"${TVM_TEST_MARKER:-not gpu}" "$@"
diff --git a/tests/scripts/task_python_unittest_gpuonly.sh
b/tests/scripts/task_python_unittest_gpuonly.sh
index b891fdb3cd..67a9be585c 100755
--- a/tests/scripts/task_python_unittest_gpuonly.sh
+++ b/tests/scripts/task_python_unittest_gpuonly.sh
@@ -31,9 +31,6 @@ export TVM_TEST_TARGETS='cuda;metal;rocm;nvptx'
# only minimal functionality of Vulkan runtime.
export TVM_TEST_TARGETS='{"kind":"vulkan","from_device":0}'
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-export PYTEST_ADDOPTS="${CI_PYTEST_ADD_OPTIONS:-} ${PYTEST_ADDOPTS:-}"
-
-python3 -m pytest -vvs -n auto -m "${TVM_TEST_MARKER}" \
+./tests/scripts/task_python_unittest.sh \
tirx/tests/python/backend/vulkan/codegen/test_basic.py \
tirx/tests/python/backend/vulkan/test_module_export.py
diff --git a/tests/scripts/task_web_wasm.sh b/tests/scripts/task_web_wasm.sh
index f901d13fee..472263de07 100755
--- a/tests/scripts/task_web_wasm.sh
+++ b/tests/scripts/task_web_wasm.sh
@@ -18,11 +18,6 @@
set -euxo pipefail
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-
-# setup tvm-ffi into python folder
-uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
rm -rf .emscripten_cache
cd web
export EM_CACHE=`pwd`/.emscripten_cache
diff --git a/tests/scripts/task_web_wasm.sh
b/tests/scripts/test_task_install_dist_wheels.sh
similarity index 66%
copy from tests/scripts/task_web_wasm.sh
copy to tests/scripts/test_task_install_dist_wheels.sh
index f901d13fee..f1fc0a105e 100755
--- a/tests/scripts/task_web_wasm.sh
+++ b/tests/scripts/test_task_install_dist_wheels.sh
@@ -18,19 +18,9 @@
set -euxo pipefail
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-
-# setup tvm-ffi into python folder
-uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
-rm -rf .emscripten_cache
-cd web
-export EM_CACHE=`pwd`/.emscripten_cache
-make clean
-npm install
-npm run lint
-npm run prepwasm
-npm run bundle
-npm run test
-npm run typedoc
-cd ..
+unset PYTHONPATH TVM_LIBRARY_PATH TVM_FFI_LIBRARY_PATH TVM_TIRX_LIBRARY_PATH
+export PYTHONNOUSERSITE=1 TVM_HOME="$PWD"
+uv pip install --python python3 --target .local/python --no-deps
apache-tvm-ffi-orcjit==0.1.2
+uv pip install --python python3 --target .local/python --no-deps --offline
--reinstall \
+ dist/apache_tvm-*.whl dist/apache_tvm_ffi-*.whl dist/apache_tvm_tirx-*.whl
+export PYTHONPATH="$PWD/.local/python"
diff --git a/tirx/3rdparty/tvm-ffi b/tirx/3rdparty/tvm-ffi
index 88d9f70687..6628c464e3 160000
--- a/tirx/3rdparty/tvm-ffi
+++ b/tirx/3rdparty/tvm-ffi
@@ -1 +1 @@
-Subproject commit 88d9f7068717a8dc0df7d2ad06e1567dfc24472f
+Subproject commit 6628c464e3b824d09c9cf65ac0c6c24f973f6d56
diff --git a/tirx/CMakeLists.txt b/tirx/CMakeLists.txt
index c3397d5736..234f0d4776 100644
--- a/tirx/CMakeLists.txt
+++ b/tirx/CMakeLists.txt
@@ -257,6 +257,13 @@ foreach(_target IN LISTS TVM_RUNTIME_BACKEND_LIBS)
tvm_ffi_add_prefix_map(${_target} "${CMAKE_CURRENT_SOURCE_DIR}")
endforeach()
foreach(_target tvm_runtime tvm_tirx ${TVM_RUNTIME_BACKEND_LIBS})
+ if(TVM_TIRX_BUILD_PYTHON_MODULE)
+ if(APPLE)
+ set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH
"@loader_path/../../tvm_ffi/lib")
+ elseif(UNIX)
+ set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH
"\$ORIGIN/../../tvm_ffi/lib")
+ endif()
+ endif()
if(APPLE)
target_link_options(${_target} PRIVATE "-Wl,-undefined,error")
elseif(UNIX)
diff --git a/tirx/cmake/modules/OpenCL.cmake b/tirx/cmake/modules/OpenCL.cmake
index a1d4aa3eb8..7ff52202c1 100644
--- a/tirx/cmake/modules/OpenCL.cmake
+++ b/tirx/cmake/modules/OpenCL.cmake
@@ -26,7 +26,18 @@ if(TVM_TIRX_USE_OPENCL)
list(APPEND RUNTIME_OPENCL_SRCS ${OPENCL_WRAPPER_SRCS})
find_path(TVM_OPENCL_HEADERS_DIR CL/cl.h
HINTS "${TVM_OPENCL_HEADERS_ROOT}" ENV OPENCL_SDK
- PATH_SUFFIXES include REQUIRED)
+ PATH_SUFFIXES include)
+ if(NOT TVM_OPENCL_HEADERS_DIR)
+ include(FetchContent)
+ FetchContent_Declare(tvm_opencl_headers
+ GIT_REPOSITORY https://github.com/KhronosGroup/OpenCL-Headers.git
+ GIT_TAG b590a6bfe034ea3a418b7b523e3490956bcb367a
+ # Only consume headers; do not add the upstream build/install rules.
+ SOURCE_SUBDIR headers-only
+ )
+ FetchContent_MakeAvailable(tvm_opencl_headers)
+ set(TVM_OPENCL_HEADERS_DIR "${tvm_opencl_headers_SOURCE_DIR}")
+ endif()
set(_opencl_include_dirs "${TVM_OPENCL_HEADERS_DIR}")
else()
find_opencl(${TVM_TIRX_USE_OPENCL})
diff --git a/tirx/tests/python/runtime/test_tirx_config.py
b/tirx/tests/python/runtime/test_tirx_config.py
new file mode 100644
index 0000000000..648ef0309a
--- /dev/null
+++ b/tirx/tests/python/runtime/test_tirx_config.py
@@ -0,0 +1,61 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""The config command must work without importing native runtime
dependencies."""
+
+import os
+import subprocess
+import sys
+from pathlib import Path
+
+import tvm_tirx
+
+
+def test_config_without_native_imports(tmp_path):
+ # A startup hook rejects native loads and dependencies even when they are
installed.
+ (tmp_path / "sitecustomize.py").write_text(
+ "import sys\n"
+ "class BlockNative:\n"
+ " def find_spec(self, fullname, path=None, target=None):\n"
+ " if fullname.split('.')[0] in ('tvm_ffi', 'torch', 'numpy') or
"
+ "fullname in ('tvm.base', 'tvm.runtime', 'tvm.backend', 'tvm.driver',
'tvm_tirx.base'):\n"
+ " raise AssertionError(fullname)\n"
+ "sys.meta_path.insert(0, BlockNative())\n"
+ "def audit(event, args):\n"
+ " if event == 'ctypes.dlopen': raise AssertionError(args)\n"
+ "sys.addaudithook(audit)\n"
+ )
+ env = dict(os.environ)
+ env["PYTHONPATH"] = os.pathsep.join(
+ (
+ str(tmp_path),
+ str(Path(tvm_tirx.__file__).parent.parent),
+ )
+ )
+ command = [sys.executable, "-m", "tvm_tirx.config"]
+ include = subprocess.check_output([*command, "--includedir"], env=env,
text=True).strip()
+ assert (Path(include) / "tvm" / "runtime").is_dir()
+ libraries = subprocess.check_output([*command, "--libfiles"], env=env,
text=True).splitlines()
+ assert len(libraries) == 2
+ assert all(Path(library).is_file() for library in libraries)
+ cmake_dir = subprocess.check_output([*command, "--cmakedir"], env=env,
text=True).strip()
+ assert (Path(cmake_dir) / "tvm_tirx-config.cmake").is_file()
+
+
+if __name__ == "__main__":
+ import tvm_tirx.testing
+
+ tvm_tirx.testing.main()
diff --git a/tests/scripts/task_config_build_wasm.sh
b/tirx/tests/scripts/task_build_arm.sh
similarity index 69%
rename from tests/scripts/task_config_build_wasm.sh
rename to tirx/tests/scripts/task_build_arm.sh
index a73464ef21..cb85fea9ea 100755
--- a/tests/scripts/task_config_build_wasm.sh
+++ b/tirx/tests/scripts/task_build_arm.sh
@@ -18,14 +18,8 @@
set -euxo pipefail
-BUILD_DIR=$1
-mkdir -p "$BUILD_DIR"
-cd "$BUILD_DIR"
-cp ../cmake/config.cmake .
-
-echo set\(USE_SORT ON\) >> config.cmake
-echo set\(TVM_TIRX_USE_LLVM llvm-config-15\) >> config.cmake
-echo set\(CMAKE_CXX_FLAGS -Werror\) >> config.cmake
-echo set\(HIDE_PRIVATE_SYMBOLS ON\) >> config.cmake
-echo set\(USE_CCACHE OFF\) >> config.cmake
-echo set\(SUMMARIZE ON\) >> config.cmake
+export CMAKE_ARGS="
+ -DTVM_TIRX_USE_LLVM=llvm-config-17
+ -DCMAKE_CXX_FLAGS=-Werror
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx
diff --git a/tests/scripts/task_web_wasm.sh
b/tirx/tests/scripts/task_build_cpu.sh
similarity index 71%
copy from tests/scripts/task_web_wasm.sh
copy to tirx/tests/scripts/task_build_cpu.sh
index f901d13fee..3583a06391 100755
--- a/tests/scripts/task_web_wasm.sh
+++ b/tirx/tests/scripts/task_build_cpu.sh
@@ -18,19 +18,8 @@
set -euxo pipefail
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-
-# setup tvm-ffi into python folder
-uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
-rm -rf .emscripten_cache
-cd web
-export EM_CACHE=`pwd`/.emscripten_cache
-make clean
-npm install
-npm run lint
-npm run prepwasm
-npm run bundle
-npm run test
-npm run typedoc
-cd ..
+export CMAKE_ARGS="
+ -DTVM_TIRX_USE_LLVM='/usr/bin/llvm-config-17 --link-static'
+ -DCMAKE_CXX_FLAGS='-Wno-error=range-loop-construct -Wno-error=comment'
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx
diff --git a/tests/scripts/task_web_wasm.sh
b/tirx/tests/scripts/task_build_gpu.sh
similarity index 69%
copy from tests/scripts/task_web_wasm.sh
copy to tirx/tests/scripts/task_build_gpu.sh
index f901d13fee..4eb7a17385 100755
--- a/tests/scripts/task_web_wasm.sh
+++ b/tirx/tests/scripts/task_build_gpu.sh
@@ -18,19 +18,13 @@
set -euxo pipefail
-export PYTHONPATH="$(pwd)/tirx/python:$(pwd)/python"
-
-# setup tvm-ffi into python folder
-uv pip install -v --target=python ./tirx/3rdparty/tvm-ffi/
-
-rm -rf .emscripten_cache
-cd web
-export EM_CACHE=`pwd`/.emscripten_cache
-make clean
-npm install
-npm run lint
-npm run prepwasm
-npm run bundle
-npm run test
-npm run typedoc
-cd ..
+export CMAKE_ARGS="
+ -DTVM_TIRX_USE_CUDA=ON
+ -DTVM_TIRX_USE_VULKAN=ON
+ -DTVM_TIRX_USE_OPENCL=ON
+ -DTVM_TIRX_USE_OPENCL_GTEST=/googletest
+ -DTVM_TIRX_USE_LLVM='/usr/bin/llvm-config-15 --link-static'
+ -DCMAKE_CXX_FLAGS=-Werror
+ -DCMAKE_CUDA_ARCHITECTURES=75
+"
+uv build --wheel --no-build-isolation --offline --python python3 --out-dir
dist ./tirx