This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch jenkins-wheel-test-artifacts
in repository https://gitbox.apache.org/repos/asf/tvm.git

commit 967ee635149f564509a722f2325c71783cbd0d5c
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Oct 14 23:58:25 2026 +0000

    [CI] Keep legacy build artifacts alongside wheel pipelines
    
    Retain the build entry point, raw artifact bundles, and source-mode test 
setup used by trusted main pipelines. Select wheel mode explicitly in the 
native pipelines and local wheel commands, keeping missing wheel artifacts 
fatal and dependency fetching disabled in that mode.
---
 ci/jenkins/README.md                          |  11 +++
 ci/jenkins/data.py                            |  28 ++++++-
 ci/jenkins/generated/common.groovy            |   2 +-
 ci/jenkins/generated/gpu_jenkinsfile.groovy   |   2 +-
 tests/python/ci/test_jenkins_helpers.py       |  56 +++++++++++++-
 tests/python/ci/test_wheel_artifacts.py       |  58 +++++++++++++++
 tests/scripts/ci.py                           |   1 +
 tests/scripts/task_build.py                   | 101 ++++++++++++++++++++++++++
 tests/scripts/task_python_docs.sh             |   7 +-
 tests/scripts/task_python_unittest.sh         |   9 ++-
 tests/scripts/task_python_unittest_gpuonly.sh |  14 +++-
 tests/scripts/task_web_wasm.sh                |   9 ++-
 12 files changed, 284 insertions(+), 14 deletions(-)

diff --git a/ci/jenkins/README.md b/ci/jenkins/README.md
index 5761ac113f..06099ee292 100644
--- a/ci/jenkins/README.md
+++ b/ci/jenkins/README.md
@@ -114,3 +114,14 @@ is performed by this change.
 An explicit value wins; otherwise builds use at least one CPU and divide the
 node CPU count by `CI_NUM_EXECUTORS`. Both the wheel backend and specialized
 native commands use that bound without an additional parallel flag.
+
+### Old pipeline compatibility
+
+Jenkins may select the old trusted-main Jenkinsfile while checking out a newer
+PR revision. Retain `tests/scripts/task_build.py`, the original raw-artifact
+bundles, and the source-mode test interfaces until trusted main has migrated.
+The native pipelines and local wheel commands explicitly pass
+`TVM_TEST_USE_WHEEL=1`; only that mode installs wheel artifacts without 
fetching
+dependencies. Calls without the flag retain the old source setup and dependency
+installation behavior. A missing wheel in wheel mode is an error, not a 
fallback
+to source mode.
diff --git a/ci/jenkins/data.py b/ci/jenkins/data.py
index 83773ca2ea..8b92cc808d 100644
--- a/ci/jenkins/data.py
+++ b/ci/jenkins/data.py
@@ -31,10 +31,32 @@ data-driven artifact lists.
 import sys
 
 files_to_stash = {
-    # Exact TVM wheels, installed by Python test/docs consumers.
+    # New pipelines install the exact TVM wheel and retain only JVM native 
files.
     "wheels": ["build/wheels"],
-    # Maven's native assembly still consumes these paths directly.
-    "tvm_runtime": ["build/lib/libtvm_runtime.so", "build/lib/libtvm_ffi.so"],
+    "jvm_runtime": ["build/lib/libtvm_runtime.so", "build/lib/libtvm_ffi.so"],
+    # Keep the original bundles while trusted main still runs the old pipeline.
+    # Executables and build files needed to run c++ tests
+    "cpptest": ["build/cpptest", "build/build.ninja", 
"build/CMakeFiles/rules.ninja"],
+    # runtime files
+    "tvm_runtime": ["build/lib/libtvm_runtime.so", "build/config.cmake"],
+    # compiler files
+    "tvm_lib": [
+        "build/lib/libtvm_compiler.so",
+        "build/lib/libtvm_runtime.so",
+        "build/lib/libtvm_ffi.so",
+        "build/lib/libtvm_runtime_cuda.so",
+        "build/lib/libtvm_runtime_vulkan.so",
+        "build/lib/libtvm_runtime_opencl.so",
+        "build/lib/libtvm_runtime_rocm.so",
+        "build/lib/libtvm_runtime_extra.so",
+        "build/libtvm_allvisible.so",
+        "build/config.cmake",
+    ],
+    # gpu related compiler files
+    "tvm_lib_gpu_extra": [
+        "build/3rdparty/libflash_attn/src/libflash_attn.so",
+        
"build/3rdparty/cutlass_fpA_intB_gemm/cutlass_kernels/libfpA_intB_gemm.so",
+    ],
 }
 
 
diff --git a/ci/jenkins/generated/common.groovy 
b/ci/jenkins/generated/common.groovy
index cebd9a8d08..57c8e7eb57 100644
--- a/ci/jenkins/generated/common.groovy
+++ b/ci/jenkins/generated/common.groovy
@@ -24,7 +24,7 @@ built_images = [:]
 upstream_revision = null
 max_time = 180
 rebuild_docker_images = false
-docker_run = 'docker/bash.sh --env CI --env PLATFORM --env TEST_STEP_NAME'
+docker_run = 'docker/bash.sh --env CI --env PLATFORM --env TEST_STEP_NAME 
--env TVM_TEST_USE_WHEEL=1'
 docker_build = 'docker/build.sh'
 s3_bucket = 'tvm-jenkins-artifacts-prod'
 s3_prefix = "tvm/${env.BRANCH_NAME}/${env.BUILD_NUMBER}"
diff --git a/ci/jenkins/generated/gpu_jenkinsfile.groovy 
b/ci/jenkins/generated/gpu_jenkinsfile.groovy
index f5ffd7adf8..df4992f7d2 100644
--- a/ci/jenkins/generated/gpu_jenkinsfile.groovy
+++ b/ci/jenkins/generated/gpu_jenkinsfile.groovy
@@ -22,7 +22,7 @@ ci.start()
 ci.build('CPU', 'BUILD: GPU', 'tvm/build-gpu', images.ci_gpu, 'gpu') {
   sh "${docker_run} --no-gpu ${images.ci_gpu} 
./tests/scripts/task_config_build_gpu.sh build"
   ci.wheel_build("--no-gpu ${images.ci_gpu}")
-  ci.upload('gpu', '--bundle wheels --bundle tvm_runtime')
+  ci.upload('gpu', '--bundle wheels --bundle jvm_runtime')
 
   // Check the compiler-only GPU configuration after publishing the test build.
   sh 'rm -rf build'
diff --git a/tests/python/ci/test_jenkins_helpers.py 
b/tests/python/ci/test_jenkins_helpers.py
index 23c0e42383..b9d26a78a0 100644
--- a/tests/python/ci/test_jenkins_helpers.py
+++ b/tests/python/ci/test_jenkins_helpers.py
@@ -19,11 +19,13 @@
 import importlib.util
 import json
 import os
+import shutil
 import subprocess
+import sys
 
 import pytest
 
-from .test_utils import JENKINS_SCRIPT_ROOT
+from .test_utils import JENKINS_SCRIPT_ROOT, REPO_ROOT
 
 
 def load_s3():
@@ -78,3 +80,55 @@ with pathlib.Path(os.environ["SLEEP_LOG"]).open("a") as out:
     delays = (tmp_path / "sleeps").read_text().splitlines()
     assert len(delays) == 4
     assert all(30 <= int(delay) <= 200 for delay in delays)
+
+
+def test_build_environment_and_target(tmp_path):
+    # Run the entry point in a minimal checkout, capturing the tool boundary.
+    script = tmp_path / "tests/scripts/task_build.py"
+    script.parent.mkdir(parents=True)
+    shutil.copy(REPO_ROOT / "tests/scripts/task_build.py", script)
+    (tmp_path / "build").mkdir()
+    executable(
+        tmp_path / "cmake",
+        """
+import json, os, pathlib, sys
+with pathlib.Path(os.environ["COMMAND_LOG"]).open("a") as out:
+    entry = [sys.argv[1:], os.getcwd(), os.environ["SCCACHE_BUCKET"], 
os.environ["CC"]]
+    out.write(json.dumps(entry) + "\\n")
+""",
+    )
+    executable(tmp_path / "sccache", "pass\n")
+    log = tmp_path / "commands"
+    env = dict(
+        os.environ,
+        PATH=f"{tmp_path}:{os.environ['PATH']}",
+        COMMAND_LOG=str(log),
+        AWS_ACCESS_KEY_ID="test",
+        CI_NUM_EXECUTORS="2",
+        VERBOSE="false",
+    )
+    subprocess.run(
+        [
+            sys.executable,
+            script,
+            "--debug",
+            "--cmake-target",
+            "cpptest",
+            "--sccache-bucket",
+            "test-cache",
+        ],
+        cwd=tmp_path,
+        env=env,
+        check=True,
+    )
+    configure, build = [json.loads(line) for line in 
log.read_text().splitlines()]
+    assert configure == [
+        ["-GNinja", "-DCMAKE_BUILD_TYPE=Debug", ".."],
+        str(tmp_path / "build"),
+        "test-cache",
+        "/opt/sccache/cc",
+    ]
+    assert build[0][:4] == ["--build", ".", "--", "cpptest"]
+    assert build[0][4].startswith("-j") and int(build[0][4][2:]) >= 1
+    assert len(build[0]) == 5
+    assert build[1:] == configure[1:]
diff --git a/tests/python/ci/test_wheel_artifacts.py 
b/tests/python/ci/test_wheel_artifacts.py
index e764f3b27e..ae0c8c5141 100644
--- a/tests/python/ci/test_wheel_artifacts.py
+++ b/tests/python/ci/test_wheel_artifacts.py
@@ -187,3 +187,61 @@ def test_local_parallel_limit_crosses_docker_boundary(
     args = calls[0]
     index = args.index(f"CMAKE_BUILD_PARALLEL_LEVEL={expected}")
     assert args[index - 1] == "--env"
+
+
[email protected](
+    "script", ["task_python_unittest.sh", "task_python_docs.sh", 
"task_web_wasm.sh"]
+)
[email protected]("wheel_mode", [False, True])
+def test_shared_scripts_preserve_legacy_and_wheel_modes(tmp_path, script, 
wheel_mode):
+    scripts = tmp_path / "tests/scripts"
+    scripts.mkdir(parents=True)
+    shutil.copy(ROOT / "tests/scripts" / script, scripts / script)
+    # Substitute only the install boundary; execute the real caller's mode 
routing.
+    (scripts / "task_python_install_wheels.sh").write_text('echo WHEEL_SETUP 
>> "$CALL_LOG"\n')
+    for directory in ["docs", "web", "bin"]:
+        (tmp_path / directory).mkdir()
+    for tool in ["uv", "python3", "make"]:
+        binary = tmp_path / "bin" / tool
+        binary.write_text(
+            f'#!/bin/sh\necho "{tool}:$*" >> "$CALL_LOG"\n'
+            + ("exit 77\n" if tool == "make" else "")
+        )
+        binary.chmod(0o755)
+    result = subprocess.run(
+        [shutil.which("bash"), str(scripts / script)],
+        cwd=tmp_path,
+        env={
+            **os.environ,
+            "PATH": f"{tmp_path / 'bin'}:{os.environ['PATH']}",
+            "CALL_LOG": str(tmp_path / "calls"),
+            "TVM_TEST_USE_WHEEL": "1" if wheel_mode else "0",
+            "IS_LOCAL": "0",
+        },
+        capture_output=True,
+        text=True,
+    )
+    assert result.returncode == (0 if script == "task_python_unittest.sh" else 
77), result.stderr
+    calls = (tmp_path / "calls").read_text().splitlines()
+    assert ("WHEEL_SETUP" in calls) == wheel_mode
+    dependency_calls = [call for call in calls if call.startswith("uv:")]
+    if wheel_mode:
+        assert not dependency_calls
+    else:
+        assert "uv:pip install -v --target=python ./3rdparty/tvm-ffi/" in 
dependency_calls
+
+
+def test_wheel_mode_does_not_fall_back_when_install_fails(tmp_path):
+    scripts = tmp_path / "tests/scripts"
+    scripts.mkdir(parents=True)
+    shutil.copy(ROOT / "tests/scripts/task_python_unittest.sh", scripts)
+    (scripts / "task_python_install_wheels.sh").write_text("return 37\n")
+    result = subprocess.run(
+        [shutil.which("bash"), str(scripts / "task_python_unittest.sh")],
+        cwd=tmp_path,
+        env={**os.environ, "TVM_TEST_USE_WHEEL": "1"},
+        capture_output=True,
+        text=True,
+    )
+    assert result.returncode == 37
+    assert "uv pip install" not in result.stderr
diff --git a/tests/scripts/ci.py b/tests/scripts/ci.py
index ec51674b05..a39abaff49 100755
--- a/tests/scripts/ci.py
+++ b/tests/scripts/ci.py
@@ -434,6 +434,7 @@ def generate_command(
 
         docker_env = {
             "TVM_WHEEL_DIR": str(REPO_ROOT / build_dir / "wheels"),
+            "TVM_TEST_USE_WHEEL": "1",
             "VERBOSE": "true" if verbose else "false",
         }
 
diff --git a/tests/scripts/task_build.py b/tests/scripts/task_build.py
new file mode 100755
index 0000000000..c240ec51c0
--- /dev/null
+++ b/tests/scripts/task_build.py
@@ -0,0 +1,101 @@
+#!/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 shutil
+import subprocess
+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="List pytest nodeids for a 
folder")
+    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", help="optional build target")
+    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 in ["standalone_crt", "crttest"]:
+        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()
+
+    if use_sccache:
+        if args.sccache_bucket and "AWS_ACCESS_KEY_ID" in os.environ:
+            env["SCCACHE_BUCKET"] = args.sccache_bucket
+            env["SCCACHE_REGION"] = args.sccache_region if args.sccache_region 
else "us-west-2"
+            logging.info(f"Using sccache bucket: {args.sccache_bucket}")
+            logging.info(f"Using sccache region: {env['SCCACHE_REGION']}")
+        else:
+            logging.info("No sccache bucket set, using local cache")
+        if "SCCACHE_SERVER_PORT" in os.environ:
+            env["SCCACHE_SERVER_PORT"] = os.getenv("SCCACHE_SERVER_PORT")
+        env["CXX"] = "/opt/sccache/c++"
+        env["CC"] = "/opt/sccache/cc"
+
+    else:
+        if sccache_exe is None:
+            reason = "'sccache' executable not found"
+        else:
+            reason = "<unknown>"
+        logging.info(f"Not using sccache, reason: {reason}")
+
+    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)
+
+    build_type = "Debug" if args.debug else "RelWithDebInfo"
+    subprocess.run(
+        ["cmake", "-GNinja", f"-DCMAKE_BUILD_TYPE={build_type}", ".."],
+        cwd=build_dir,
+        env=env,
+        check=True,
+    )
+
+    ninja_args = [f"-j{num_cpus}"]
+    if args.cmake_target:
+        ninja_args.insert(0, args.cmake_target)
+    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_python_docs.sh 
b/tests/scripts/task_python_docs.sh
index 878996fa12..bf9533be07 100755
--- a/tests/scripts/task_python_docs.sh
+++ b/tests/scripts/task_python_docs.sh
@@ -18,12 +18,15 @@
 
 set -euxo pipefail
 
-# Local docs retain source builds; Jenkins imports the exact build-node wheels.
-if [ "${IS_LOCAL:-0}" != "1" ]; then
+# The native pipeline opts in; old main-selected jobs retain source imports.
+if [ "${TVM_TEST_USE_WHEEL:-0}" = "1" ]; then
     source "$(dirname "$0")/task_python_install_wheels.sh"
     export TVM_DOCS_USE_WHEEL=1
 else
     export PYTHONPATH="$(pwd)/python"
+    unset TVM_DOCS_USE_WHEEL
+    uv pip install -v --target=python ./3rdparty/tvm-ffi/
+    uv pip install --no-deps apache-tvm-ffi-orcjit==0.1.2
 fi
 
 # to avoid CI CPU thread throttling.
diff --git a/tests/scripts/task_python_unittest.sh 
b/tests/scripts/task_python_unittest.sh
index e5e5603429..b9b408e65d 100755
--- a/tests/scripts/task_python_unittest.sh
+++ b/tests/scripts/task_python_unittest.sh
@@ -18,7 +18,14 @@
 
 set -euxo pipefail
 
-source "$(dirname "$0")/task_python_install_wheels.sh"
+if [ "${TVM_TEST_USE_WHEEL:-0}" = "1" ]; then
+    source "$(dirname "$0")/task_python_install_wheels.sh"
+else
+    # Trusted main still invokes this script after transferring native 
artifacts.
+    export PYTHONPATH="$(pwd)/python"
+    uv pip install -v --target=python ./3rdparty/tvm-ffi/
+    uv pip install --no-deps apache-tvm-ffi-orcjit==0.1.2
+fi
 export PYTEST_ADDOPTS="${CI_PYTEST_ADD_OPTIONS:-} ${PYTEST_ADDOPTS:-}"
 
 python3 -m pytest -vvs -n auto -m "${TVM_TEST_MARKER:-not gpu}" 
"${@:-tests/python}"
diff --git a/tests/scripts/task_python_unittest_gpuonly.sh 
b/tests/scripts/task_python_unittest_gpuonly.sh
index 9aa95cb201..ca73370270 100755
--- a/tests/scripts/task_python_unittest_gpuonly.sh
+++ b/tests/scripts/task_python_unittest_gpuonly.sh
@@ -31,6 +31,14 @@ export TVM_TEST_TARGETS='cuda;metal;rocm;nvptx'
 # only minimal functionality of Vulkan runtime.
 export TVM_TEST_TARGETS='{"kind":"vulkan","from_device":0}'
 
-./tests/scripts/task_python_unittest.sh \
-    tests/python/backend/vulkan/codegen/test_vulkan.py \
-    tests/python/backend/vulkan/test_vulkan_module_export.py
+if [ "${TVM_TEST_USE_WHEEL:-0}" = "1" ]; then
+    ./tests/scripts/task_python_unittest.sh \
+        tests/python/backend/vulkan/codegen/test_vulkan.py \
+        tests/python/backend/vulkan/test_vulkan_module_export.py
+else
+    export PYTHONPATH="$(pwd)/python"
+    export PYTEST_ADDOPTS="${CI_PYTEST_ADD_OPTIONS:-} ${PYTEST_ADDOPTS:-}"
+    python3 -m pytest -vvs -n auto -m "${TVM_TEST_MARKER}" \
+        tests/python/backend/vulkan/codegen/test_vulkan.py \
+        tests/python/backend/vulkan/test_vulkan_module_export.py
+fi
diff --git a/tests/scripts/task_web_wasm.sh b/tests/scripts/task_web_wasm.sh
index e2f6d6c4db..c74596c08e 100755
--- a/tests/scripts/task_web_wasm.sh
+++ b/tests/scripts/task_web_wasm.sh
@@ -18,8 +18,13 @@
 
 set -euxo pipefail
 
-# Host Python uses the built wheel; Emscripten still builds its separate 
runtime.
-source "$(dirname "$0")/task_python_install_wheels.sh"
+# Host Python uses wheels in the native pipeline. Keep source mode for old 
main.
+if [ "${TVM_TEST_USE_WHEEL:-0}" = "1" ]; then
+    source "$(dirname "$0")/task_python_install_wheels.sh"
+else
+    export PYTHONPATH="$(pwd)/python"
+    uv pip install -v --target=python ./3rdparty/tvm-ffi/
+fi
 
 rm -rf .emscripten_cache
 cd web

Reply via email to