From 79436a7fb53d158c6c68c7140c70288d51a6a19d Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Thu, 15 Oct 2026 23:39:35 +0000 Subject: [PATCH 1/5] [CI] Build and consume source wheel artifacts across Jenkins platforms --- .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 | 33 +- ci/jenkins/generated/common.groovy | 12 + ci/jenkins/generated/cpu_jenkinsfile.groovy | 42 +- ci/jenkins/generated/gpu_jenkinsfile.groovy | 42 +- ci/jenkins/generated/wasm_jenkinsfile.groovy | 39 +- cmake/modules/contrib/CLML.cmake | 9 +- 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 +- .../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 + .../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 ++ ...d_gpu_other.sh => task_build_gpu_other.sh} | 17 +- tests/scripts/task_build_wasm.sh | 48 ++ tests/scripts/task_config_build_gpu.sh | 43 -- ....sh => task_install_build_dependencies.sh} | 15 +- ...u.sh => task_install_docs_dependencies.sh} | 30 +- 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 - .../scripts/test_task_install_dist_wheels.sh | 26 + 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 +- tirx/tests/scripts/task_build_cpu.sh | 25 + tirx/tests/scripts/task_build_gpu.sh | 30 + 56 files changed, 992 insertions(+), 1163 deletions(-) delete mode 160000 3rdparty/OpenCL-Headers create mode 100644 python/tvm/config.py create mode 100644 tests/python/runtime/rpc/test_minrpc_source.py create mode 100644 tests/python/runtime/test_config.py delete mode 100755 tests/scripts/ci.py delete mode 100755 tests/scripts/task_build.py create mode 100755 tests/scripts/task_build_arm.sh create mode 100755 tests/scripts/task_build_cpu.sh create mode 100755 tests/scripts/task_build_gpu.sh rename tests/scripts/{task_config_build_gpu_other.sh => task_build_gpu_other.sh} (68%) create mode 100755 tests/scripts/task_build_wasm.sh delete mode 100755 tests/scripts/task_config_build_gpu.sh rename tests/scripts/{task_config_build_arm.sh => task_install_build_dependencies.sh} (69%) rename tests/scripts/{task_config_build_cpu.sh => task_install_docs_dependencies.sh} (51%) create mode 100755 tests/scripts/test_task_install_dist_wheels.sh create mode 100644 tirx/tests/python/runtime/test_tirx_config.py rename tests/scripts/task_config_build_wasm.sh => tirx/tests/scripts/task_build_arm.sh (69%) create mode 100755 tirx/tests/scripts/task_build_cpu.sh create mode 100755 tirx/tests/scripts/task_build_gpu.sh diff --git a/.gitignore b/.gitignore index 0348a0a1b886..e5ea924a3d36 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 8a2a3ef81d6f..85109f24d276 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 b590a6bfe034..000000000000 --- a/3rdparty/OpenCL-Headers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b590a6bfe034ea3a418b7b523e3490956bcb367a diff --git a/CMakeLists.txt b/CMakeLists.txt index 101c1507bd8c..d0bcae3f1b3c 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 627aef16eaf0..1ce63e056a89 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 f74942e1487a..12c2783b7365 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 d5a7ee042fac..17559b7cc1ca 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 e617d7af918c..6cbc1a32265c 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_.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 1429442a03b1..47abd8661c48 100644 --- a/ci/jenkins/generated/arm_jenkinsfile.groovy +++ b/ci/jenkins/generated/arm_jenkinsfile.groovy @@ -31,26 +31,17 @@ 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 + sccache --show-stats + '""", + 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 1e9ce344be4d..ba40d0587d02 100644 --- a/ci/jenkins/generated/common.groovy +++ b/ci/jenkins/generated/common.groovy @@ -22,6 +22,8 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 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 +env.SCCACHE_BUCKET = 'tvm-sccache-prod' +env.SCCACHE_REGION = 'us-west-2' max_time = 180 docker_run = 'docker/bash.sh --env CI --env PLATFORM --env TEST_STEP_NAME' docker_build = 'docker/build.sh' @@ -388,4 +390,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 44aceb116da6..cbcfa17fd4f1 100644 --- a/ci/jenkins/generated/cpu_jenkinsfile.groovy +++ b/ci/jenkins/generated/cpu_jenkinsfile.groovy @@ -31,27 +31,18 @@ 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 + sccache --show-stats + '""", + label: 'Build cpu wheels and C++ tests', + ) + } + ci.upload_artifacts('cpu', '--path "dist/*.whl"') + } } @@ -63,11 +54,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 ac46fa4de15b..1a3e01ab5a50 100644 --- a/ci/jenkins/generated/gpu_jenkinsfile.groovy +++ b/ci/jenkins/generated/gpu_jenkinsfile.groovy @@ -33,17 +33,26 @@ 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 + sccache --show-stats + '""", + 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 +65,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 +81,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 593da51094a8..468dea2a5adc 100644 --- a/ci/jenkins/generated/wasm_jenkinsfile.groovy +++ b/ci/jenkins/generated/wasm_jenkinsfile.groovy @@ -31,32 +31,21 @@ 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 + sccache --show-stats + '""", + 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 e085fdcbda46..2e2e2d841033 100644 --- a/cmake/modules/contrib/CLML.cmake +++ b/cmake/modules/contrib/CLML.cmake @@ -18,7 +18,14 @@ 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) + else() + 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 fe0b1eeb2bed..dd39c784bfe1 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 777f122da517..6b45aa1f4bff 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 1d4ec578f79a..5b1d4f23be3c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,8 +50,6 @@ 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 a93f55b5461c..2fe4aef40928 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 ...``. 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 d7e5c1b92ff6..14ad21e875bd 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_.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 d28c935ae414..1e7afdf499fe 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 7345c496e97b..ea7396f8dc21 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. false - ../../../build/lib/libtvm_runtime.so + ${tvm.lib.dir}/libtvm_runtime.so + lib/native + 0644 + + + ${tvm.ffi.lib.dir}/libtvm_ffi.so lib/native 0644 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 739852e47472..70f575498318 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. false - ../../../build/lib/libtvm_runtime.dylib + ${tvm.lib.dir}/libtvm_runtime.dylib + lib/native + 0644 + + + ${tvm.ffi.lib.dir}/libtvm_ffi.dylib lib/native 0644 diff --git a/jvm/core/pom.xml b/jvm/core/pom.xml index eeb55f5cfd89..26ba6dcd782a 100644 --- a/jvm/core/pom.xml +++ b/jvm/core/pom.xml @@ -104,7 +104,7 @@ under the License. 1 -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 diff --git a/jvm/native/linux-x86_64/pom.xml b/jvm/native/linux-x86_64/pom.xml index ce6e98e7f83a..dcda4c6ce15a 100644 --- a/jvm/native/linux-x86_64/pom.xml +++ b/jvm/native/linux-x86_64/pom.xml @@ -117,8 +117,8 @@ under the License. -std=c++17 - -I../../../include - -I../../../tirx/3rdparty/tvm-ffi/include + -I${tvm.include.dir} + -I${tvm.ffi.include.dir} -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux ${cflags} @@ -127,7 +127,7 @@ under the License. -shared - -L${project.parent.basedir}/../../build/lib/ + -L${tvm.lib.dir} -ltvm_runtime ${ldflags} diff --git a/jvm/native/osx-x86_64/pom.xml b/jvm/native/osx-x86_64/pom.xml index 5d6c3dcf7503..ccb79f5bbb4a 100644 --- a/jvm/native/osx-x86_64/pom.xml +++ b/jvm/native/osx-x86_64/pom.xml @@ -118,8 +118,8 @@ under the License. -std=c++17 - -I../../../include - -I../../../tirx/3rdparty/tvm-ffi/include + -I${tvm.include.dir} + -I${tvm.ffi.include.dir} -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin ${cflags} @@ -134,7 +134,7 @@ under the License. -Wl,-x - -L${project.parent.basedir}/../../build/lib/ + -L${tvm.lib.dir} -ltvm_runtime ${ldflags} diff --git a/jvm/pom.xml b/jvm/pom.xml index e5efce0bf5bf..fce2b6a510f4 100644 --- a/jvm/pom.xml +++ b/jvm/pom.xml @@ -44,6 +44,10 @@ UTF-8 + ${maven.multiModuleProjectDirectory}/../include + ${maven.multiModuleProjectDirectory}/../tirx/3rdparty/tvm-ffi/include + ${maven.multiModuleProjectDirectory}/../build/lib + ${tvm.lib.dir} pom diff --git a/pyproject.toml b/pyproject.toml index 8305d6dd8827..af0f60575d68 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 7320967ffb64..8952ca68b3e5 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 000000000000..c76ef9000617 --- /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 5885f7500545..2b5ba261a218 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 000000000000..0512d1cfa5bc --- /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 + + +@pytest.mark.parametrize("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 000000000000..c039e08bd2ef --- /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 fa027c150a30..000000000000 --- 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 617e8b1a1b0b..000000000000 --- 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 = "" - 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 000000000000..c385fe023048 --- /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 000000000000..732cedd3601a --- /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 000000000000..15df597dff8f --- /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 de5b94b5dc13..59f33eaeac40 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 000000000000..0ea957c5261d --- /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_gpu.sh b/tests/scripts/task_config_build_gpu.sh deleted file mode 100755 index 2f929a9e203b..000000000000 --- 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 60778f5c11eb..fe1484ab7b72 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_config_build_cpu.sh b/tests/scripts/task_install_docs_dependencies.sh similarity index 51% rename from tests/scripts/task_config_build_cpu.sh rename to tests/scripts/task_install_docs_dependencies.sh index 6a8586db5b17..2ec999876fbf 100755 --- a/tests/scripts/task_config_build_cpu.sh +++ b/tests/scripts/task_install_docs_dependencies.sh @@ -18,20 +18,16 @@ 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 +# 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 29342c70c8a3..b76a54ad31aa 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 60c0c725cb51..ae2587e49498 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 57544bee2bcc..bdd960321d90 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 9b319b7a6744..a865d363e3ac 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 b891fdb3cd02..67a9be585cb8 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 f901d13fee74..472263de07ce 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/test_task_install_dist_wheels.sh b/tests/scripts/test_task_install_dist_wheels.sh new file mode 100755 index 000000000000..f1fc0a105ee9 --- /dev/null +++ b/tests/scripts/test_task_install_dist_wheels.sh @@ -0,0 +1,26 @@ +#!/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 + +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 88d9f7068717..6628c464e3b8 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 c3397d5736c9..234f0d47768b 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 a1d4aa3eb8e0..7ff52202c190 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 000000000000..648ef0309a82 --- /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 a73464ef2145..cb85fea9eaf1 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/tirx/tests/scripts/task_build_cpu.sh b/tirx/tests/scripts/task_build_cpu.sh new file mode 100755 index 000000000000..3583a06391ec --- /dev/null +++ b/tirx/tests/scripts/task_build_cpu.sh @@ -0,0 +1,25 @@ +#!/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 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/tirx/tests/scripts/task_build_gpu.sh b/tirx/tests/scripts/task_build_gpu.sh new file mode 100755 index 000000000000..4eb7a17385a8 --- /dev/null +++ b/tirx/tests/scripts/task_build_gpu.sh @@ -0,0 +1,30 @@ +#!/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 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 From ec5a6f6877910f7d19dbd7a03d6044dd2541d2d4 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Fri, 16 Oct 2026 00:30:02 +0000 Subject: [PATCH 2/5] [CI] Use the locally built TIRx wheel for parent builds --- .github/workflows/main.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9f017ee51ce2..7624a81f4db5 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 ) From 5e6d98e0ae27e0099be84fdcd6023f303dddc8d4 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Fri, 16 Oct 2026 00:34:19 +0000 Subject: [PATCH 3/5] [CMAKE] Resolve CLML headers for installed core targets --- cmake/modules/contrib/CLML.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/modules/contrib/CLML.cmake b/cmake/modules/contrib/CLML.cmake index 2e2e2d841033..73ab85db09d8 100644 --- a/cmake/modules/contrib/CLML.cmake +++ b/cmake/modules/contrib/CLML.cmake @@ -20,7 +20,8 @@ if(USE_CLML) file(GLOB CLML_RUNTIME_MODULE src/runtime/extra/contrib/clml/clml_runtime.cc) if(TARGET tvm_runtime_opencl) get_target_property(_clml_opencl_headers tvm_runtime_opencl INTERFACE_INCLUDE_DIRECTORIES) - else() + 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) From 3ad9d0e3482819c073a23b78fea6064252bf1d92 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Fri, 16 Oct 2026 01:02:05 +0000 Subject: [PATCH 4/5] [CI] Temporarily bypass sccache compiler wrappers --- ci/jenkins/generated/arm_jenkinsfile.groovy | 1 - ci/jenkins/generated/common.groovy | 7 ++++--- ci/jenkins/generated/cpu_jenkinsfile.groovy | 1 - ci/jenkins/generated/gpu_jenkinsfile.groovy | 1 - ci/jenkins/generated/wasm_jenkinsfile.groovy | 1 - 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ci/jenkins/generated/arm_jenkinsfile.groovy b/ci/jenkins/generated/arm_jenkinsfile.groovy index 47abd8661c48..19703571e782 100644 --- a/ci/jenkins/generated/arm_jenkinsfile.groovy +++ b/ci/jenkins/generated/arm_jenkinsfile.groovy @@ -36,7 +36,6 @@ stage('Build') { script: """${docker_run} ${images.ci_arm} bash -euo pipefail -c ' source ./tests/scripts/task_install_build_dependencies.sh ./tests/scripts/task_build_arm.sh - sccache --show-stats '""", label: 'Build arm wheels and C++ tests', ) diff --git a/ci/jenkins/generated/common.groovy b/ci/jenkins/generated/common.groovy index ba40d0587d02..4270fa991d20 100644 --- a/ci/jenkins/generated/common.groovy +++ b/ci/jenkins/generated/common.groovy @@ -22,10 +22,11 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 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 -env.SCCACHE_BUCKET = 'tvm-sccache-prod' -env.SCCACHE_REGION = 'us-west-2' 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' diff --git a/ci/jenkins/generated/cpu_jenkinsfile.groovy b/ci/jenkins/generated/cpu_jenkinsfile.groovy index cbcfa17fd4f1..d487846a0b8b 100644 --- a/ci/jenkins/generated/cpu_jenkinsfile.groovy +++ b/ci/jenkins/generated/cpu_jenkinsfile.groovy @@ -36,7 +36,6 @@ stage('Build') { script: """${docker_run} ${images.ci_cpu} bash -euo pipefail -c ' source ./tests/scripts/task_install_build_dependencies.sh ./tests/scripts/task_build_cpu.sh - sccache --show-stats '""", label: 'Build cpu wheels and C++ tests', ) diff --git a/ci/jenkins/generated/gpu_jenkinsfile.groovy b/ci/jenkins/generated/gpu_jenkinsfile.groovy index 1a3e01ab5a50..c98c4c2a01b4 100644 --- a/ci/jenkins/generated/gpu_jenkinsfile.groovy +++ b/ci/jenkins/generated/gpu_jenkinsfile.groovy @@ -38,7 +38,6 @@ stage('Build') { 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 - sccache --show-stats '""", label: 'Build gpu', ) diff --git a/ci/jenkins/generated/wasm_jenkinsfile.groovy b/ci/jenkins/generated/wasm_jenkinsfile.groovy index 468dea2a5adc..a079192d8ad7 100644 --- a/ci/jenkins/generated/wasm_jenkinsfile.groovy +++ b/ci/jenkins/generated/wasm_jenkinsfile.groovy @@ -36,7 +36,6 @@ stage('Build') { script: """${docker_run} ${images.ci_wasm} bash -euo pipefail -c ' source ./tests/scripts/task_install_build_dependencies.sh ./tests/scripts/task_build_wasm.sh - sccache --show-stats '""", label: 'Build wasm wheels and C++ tests', ) From 002d6bae29bf30fdac6a8dfca1b919965c8e02bd Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Fri, 16 Oct 2026 01:09:07 +0000 Subject: [PATCH 5/5] [CI] Refresh cached compiler paths when bypassing sccache --- ci/jenkins/generated/common.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/jenkins/generated/common.groovy b/ci/jenkins/generated/common.groovy index 4270fa991d20..f7e39e01a78d 100644 --- a/ci/jenkins/generated/common.groovy +++ b/ci/jenkins/generated/common.groovy @@ -351,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: """