diff --git a/.github/ci_config.yml b/.github/ci_config.yml index df124563e..ec55f4ea9 100644 --- a/.github/ci_config.yml +++ b/.github/ci_config.yml @@ -15,7 +15,7 @@ platforms: BASE_IMAGE: nvcr.io/nvidia/pytorch:24.10-py3 SKIP_APT: "1" PIP_INDEX_URL: https://pypi.tuna.tsinghua.edu.cn/simple - setup: pip install scikit-build-core pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_NVIDIA=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_NVIDIA=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: gpu: type: unittest @@ -51,7 +51,7 @@ platforms: - /lib/firmware:/lib/firmware - /usr/src:/usr/src - /lib/modules:/lib/modules - setup: python -m pip install scikit-build-core packaging exceptiongroup typing-extensions pygments pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_ILUVATAR=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix -DCMAKE_CUDA_ARCHITECTURES=OFF -DCMAKE_CUDA_COMPILER=/usr/local/corex-4.3.0.20250624/bin/clang++ -DCUDAToolkit_ROOT=/usr/local/corex-4.3.0.20250624 && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install . --no-build-isolation --no-deps --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' exceptiongroup typing-extensions pygments pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_ILUVATAR=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix -DCMAKE_CUDA_ARCHITECTURES=OFF -DCMAKE_CUDA_COMPILER=/usr/local/corex-4.3.0.20250624/bin/clang++ -DCUDAToolkit_ROOT=/usr/local/corex-4.3.0.20250624 && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install . --no-build-isolation --no-deps --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: gpu: type: unittest @@ -81,7 +81,7 @@ platforms: - "--privileged" - "--ulimit=memlock=-1" - "--ulimit=stack=67108864" - setup: pip install scikit-build-core pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_METAX=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_METAX=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: gpu: type: unittest @@ -109,7 +109,7 @@ platforms: PIP_INDEX_URL: https://pypi.org/simple docker_args: - "--privileged" - setup: pip install scikit-build-core pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_MOORE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_MOORE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: gpu: type: unittest @@ -136,7 +136,7 @@ platforms: PIP_INDEX_URL: https://pypi.org/simple docker_args: - "--privileged" - setup: pip install scikit-build-core pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_CAMBRICON=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_CAMBRICON=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: gpu: type: unittest @@ -172,7 +172,7 @@ platforms: - "--group-add=video" volumes: - /opt/hyhal:/opt/hyhal:ro - setup: pip install scikit-build-core pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_HYGON=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_HYGON=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: gpu: type: unittest @@ -207,7 +207,7 @@ platforms: - /usr/local/bin/npu-smi:/usr/local/bin/npu-smi:ro env: ASCEND_HOME_PATH: /usr/local/Ascend/ascend-toolkit/latest - setup: pip install scikit-build-core pybind11 libclang pyyaml && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_ASCEND=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix + setup: python -m pip install 'scikit-build-core>=1.1.1' pybind11 libclang packaging pyyaml jinja2 && rm -rf /tmp/infinirt-src /tmp/infinirt-build /tmp/infinirt-prefix && git clone --depth 1 ${INFINI_RT_GIT_URL:-https://gh-proxy.com/https://github.com/InfiniTensor/InfiniRT.git} /tmp/infinirt-src && cmake -S /tmp/infinirt-src -B /tmp/infinirt-build -DWITH_CPU=ON -DWITH_ASCEND=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/infinirt-prefix && cmake --build /tmp/infinirt-build -j$(nproc) && cmake --install /tmp/infinirt-build && python -m pip install .[dev] --no-build-isolation --config-settings=cmake.define.INFINI_OPS_SMOKE_BUILD=ON --config-settings=cmake.define.INFINI_RT_ROOT=/tmp/infinirt-prefix jobs: npu: type: unittest diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d4305c09..0b6e72703 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1549,7 +1549,9 @@ if(GENERATE_PYTHON_BINDINGS) set(_iluvatar_dispatch_include_flags "-I${CMAKE_CURRENT_SOURCE_DIR}" "-I${PROJECT_SOURCE_DIR}" + "-I${PROJECT_SOURCE_DIR}/include" "-I${PROJECT_SOURCE_DIR}/generated" + "-I${PROJECT_SOURCE_DIR}/generated/include" ${INFINI_RT_INCLUDE_FLAGS}) foreach(_dir IN LISTS TORCH_INCLUDE_DIRS CUDAToolkit_INCLUDE_DIRS) list(APPEND _iluvatar_dispatch_include_flags "-I${_dir}") diff --git a/src/native/ascend/custom/cmake/config_ascend.cmake b/src/native/ascend/custom/cmake/config_ascend.cmake index 29bbee85f..bfbb07a6a 100644 --- a/src/native/ascend/custom/cmake/config_ascend.cmake +++ b/src/native/ascend/custom/cmake/config_ascend.cmake @@ -6,7 +6,7 @@ endif() set(ASCEND_CANN_PACKAGE_PATH ${ASCEND_HOME_PATH}) -# Auto-detect `SOC_VERSION` from `npu-smi info` if not set externally. +# Auto-detect `SOC_VERSION` from the runtime or `npu-smi` if not set externally. # Required by `CANN`'s `ascendc.cmake` for `AscendC` kernel compilation. if(NOT DEFINED SOC_VERSION OR "${SOC_VERSION}" STREQUAL "") include(${CMAKE_CURRENT_LIST_DIR}/detect_soc.cmake) diff --git a/src/native/ascend/custom/cmake/detect_soc.cmake b/src/native/ascend/custom/cmake/detect_soc.cmake index 548ead0aa..d0feb3a7d 100644 --- a/src/native/ascend/custom/cmake/detect_soc.cmake +++ b/src/native/ascend/custom/cmake/detect_soc.cmake @@ -1,9 +1,4 @@ -# Auto-detect the Ascend SOC version from `npu-smi info`. -# -# `infiniops_detect_soc()` parses the first `910*` / `310*` entry -# in `npu-smi info` and writes `Ascend` into the named variable in -# the caller's scope. Falls back to `Ascend910B4` when detection fails -# (no NPU on the host, `npu-smi` missing, output format mismatch). +# Query the runtime for the exact SOC name before consulting `npu-smi`. # # Called from both `src/CMakeLists.txt` (outer `pip install` build, to # forward `SOC_VERSION` to the standalone `build.sh` invocation) and @@ -11,14 +6,52 @@ # by that `build.sh`). function(infiniops_detect_soc out_var) - execute_process( - COMMAND bash -c "npu-smi info 2>/dev/null | awk '/910B|910A|310/ {for (i=1;i<=NF;i++) if ($i ~ /^(910|310)/) {print \"Ascend\" $i; exit}}'" - OUTPUT_VARIABLE _detected - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(_detected) - set(${out_var} "${_detected}" PARENT_SCOPE) + set(_soc_pattern "910(B[1-4](-1)?|A|ProA|ProB|PremiumA|_[0-9][0-9A-Za-z]*)|310[BP][0-9]+(vir[0-9]+)?") + if(_TORCH_PYTHON) + set(_soc_python "${_TORCH_PYTHON}") + elseif(Python_EXECUTABLE) + set(_soc_python "${Python_EXECUTABLE}") else() - set(${out_var} "Ascend910B4" PARENT_SCOPE) + find_program(_soc_python NAMES python3 python) + endif() + + if(_soc_python) + execute_process( + COMMAND "${_soc_python}" -c + "import torch, torch_npu; print(torch.npu.get_device_name(0))" + OUTPUT_VARIABLE _runtime_soc + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _runtime_result + ERROR_QUIET + TIMEOUT 30) + if(_runtime_result STREQUAL "0") + string(REGEX MATCH "^(Ascend)?(${_soc_pattern})$" + _runtime_match "${_runtime_soc}") + if(_runtime_match) + set(${out_var} "Ascend${CMAKE_MATCH_2}" PARENT_SCOPE) + return() + endif() + endif() endif() + + execute_process( + COMMAND npu-smi info + OUTPUT_VARIABLE _smi_output + RESULT_VARIABLE _smi_result + ERROR_QUIET + TIMEOUT 10) + if(_smi_result STREQUAL "0") + # Require model-qualified tokens so HBM usage such as `3103` is ignored. + string(REGEX MATCH "(^|[ \t\r\n|])(Ascend)?(${_soc_pattern})([ \t\r\n|]|$)" + _smi_match "${_smi_output}") + if(_smi_match) + set(${out_var} "Ascend${CMAKE_MATCH_3}" PARENT_SCOPE) + return() + endif() + endif() + + message(FATAL_ERROR + "Cannot determine an exact Ascend SOC version. " + "Set SOC_VERSION explicitly (pip: --config-settings=cmake.define.SOC_VERSION=). " + "Generic model names and numeric npu-smi fields are not valid SOC versions.") endfunction() diff --git a/tests/build_config/test_ci_build.py b/tests/build_config/test_ci_build.py new file mode 100644 index 000000000..483575dcc --- /dev/null +++ b/tests/build_config/test_ci_build.py @@ -0,0 +1,219 @@ +"""Hardware-independent regressions for CI build setup and SOC discovery.""" + +import os +import pathlib +import shlex +import shutil +import subprocess +import sys + +import pytest +import yaml +from packaging.requirements import Requirement + +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib + + +ROOT = pathlib.Path(__file__).resolve().parents[2] +SOC_DIR = ROOT / "src/native/ascend/custom/cmake" +CMAKE = shutil.which("cmake") + + +@pytest.mark.parametrize( + "platform", ("nvidia", "iluvatar", "metax", "moore", "cambricon", "hygon", "ascend") +) +def test_ci_installs_declared_build_requirements(platform): + project = tomllib.loads((ROOT / "pyproject.toml").read_text()) + config = yaml.safe_load((ROOT / ".github/ci_config.yml").read_text()) + commands = config["platforms"][platform]["setup"].split("&&") + install = shlex.split(commands[0]) + assert install[:4] == ["python", "-m", "pip", "install"] + requirements = {Requirement(arg).name: Requirement(arg) for arg in install[4:]} + + for declared in project["build-system"]["requires"]: + required = Requirement(declared) + assert required.name in requirements + assert requirements[required.name].specifier == required.specifier + + for command in commands: + tokens = shlex.split(command) + + if "install" in tokens and "pip" in tokens: + assert tokens[:4] == ["python", "-m", "pip", "install"] + + +def test_wheel_metadata_accepts_sdist_configuration(tmp_path, monkeypatch): + from scikit_build_core.build import prepare_metadata_for_build_wheel + + monkeypatch.chdir(ROOT) + dist_info = prepare_metadata_for_build_wheel(str(tmp_path)) + metadata = (tmp_path / dist_info / "METADATA").read_text() + assert "Name: InfiniOps" in metadata + assert "Version: 0.1.0" in metadata + + +@pytest.fixture +def soc_probe(tmp_path, monkeypatch, fake_smi): + if not CMAKE: + pytest.skip("cmake is unavailable") + + fake_modules = tmp_path / "python" + fake_modules.mkdir() + (fake_modules / "torch_npu.py").write_text("") + (fake_modules / "torch.py").write_text( + "import os\n" + "class npu:\n" + " @staticmethod\n" + " def get_device_name(index):\n" + " name = os.environ.get('TEST_RUNTIME_SOC')\n" + " if not name:\n" + " raise RuntimeError('No NPU runtime is available.')\n" + " return name\n" + ) + smi_output = tmp_path / "smi.txt" + monkeypatch.setenv("TEST_SMI_OUTPUT", str(smi_output)) + monkeypatch.setenv("PATH", str(fake_smi) + os.pathsep + os.environ["PATH"]) + monkeypatch.setenv("PYTHONPATH", str(fake_modules)) + output = tmp_path / "soc.txt" + script = tmp_path / "probe.cmake" + script.write_text( + f'include("{SOC_DIR.as_posix()}/detect_soc.cmake")\n' + "infiniops_detect_soc(DETECTED_SOC)\n" + f'file(WRITE "{output.as_posix()}" "${{DETECTED_SOC}}")\n' + ) + + def run(runtime_soc, smi_text, *, torch_python=True): + monkeypatch.setenv("TEST_RUNTIME_SOC", runtime_soc) + smi_output.write_text(smi_text) + interpreter = "_TORCH_PYTHON" if torch_python else "Python_EXECUTABLE" + command = [CMAKE, f"-D{interpreter}={sys.executable}"] + + if torch_python: + command.append("-DPython_EXECUTABLE=/missing/python") + + result = subprocess.run( + [*command, "-P", str(script)], capture_output=True, text=True, timeout=45 + ) + name = output.read_text() if output.exists() else None + + return result, name + + return run + + +@pytest.fixture(scope="session") +def fake_smi(tmp_path_factory): + directory = tmp_path_factory.mktemp("npu-smi") + smi = directory / ("npu-smi.exe" if os.name == "nt" else "npu-smi") + + if os.name == "nt": + compiler = shutil.which("g++") or shutil.which("clang++") + if not compiler: + pytest.skip("A C++ compiler is required for the Windows npu-smi fixture") + source = directory / "smi.cc" + source.write_text( + "#include \n#include \n#include \n" + 'int main() { std::ifstream input(std::getenv("TEST_SMI_OUTPUT")); ' + "std::cout << input.rdbuf(); return 0; }\n" + ) + subprocess.run([compiler, str(source), "-o", str(smi)], check=True, timeout=30) + else: + smi.write_text('#!/bin/sh\ncat "$TEST_SMI_OUTPUT"\n') + smi.chmod(0o755) + + return directory + + +@pytest.mark.parametrize("torch_python", (True, False)) +def test_runtime_soc_takes_priority_over_smi(soc_probe, torch_python): + result, name = soc_probe( + "Ascend910_9362", "| 0 | 910B4 | 3103 |", torch_python=torch_python + ) + assert result.returncode == 0, result.stderr + assert name == "Ascend910_9362" + + +@pytest.mark.parametrize( + "model", ("910B4", "910B4-1", "Ascend910_9382", "310P3", "310P3vir02") +) +def test_smi_accepts_qualified_models(soc_probe, model): + result, name = soc_probe("", f"| 0 | {model} | OK |\n| 3103 | 3109 |") + assert result.returncode == 0, result.stderr + assert name == (model if model.startswith("Ascend") else f"Ascend{model}") + + +@pytest.mark.parametrize( + "smi_text", ("", "| Ascend910 | 3103 | 3109 |", "| 3103 |", "| 910B40garbage |") +) +def test_ambiguous_soc_requires_explicit_configuration(soc_probe, smi_text): + result, name = soc_probe("Ascend910", smi_text) + assert result.returncode != 0 + assert name is None + assert "Set SOC_VERSION explicitly" in result.stderr + + +def test_explicit_soc_bypasses_detection(tmp_path): + if not CMAKE: + pytest.skip("cmake is unavailable") + + toolkit = tmp_path / "toolkit" + ascendc = toolkit / "tools/tikcpp/ascendc_kernel_cmake" + ascendc.mkdir(parents=True) + (ascendc / "ascendc.cmake").write_text("") + script = tmp_path / "explicit.cmake" + script.write_text( + 'set(SOC_VERSION "Ascend910_9382")\n' + f'set(ASCEND_HOME_PATH "{toolkit.as_posix()}")\n' + f'include("{SOC_DIR.as_posix()}/config_ascend.cmake")\n' + 'if(NOT SOC_VERSION STREQUAL "Ascend910_9382")\n' + ' message(FATAL_ERROR "Explicit SOC version was changed.")\n' + "endif()\n" + ) + result = subprocess.run( + [CMAKE, "-P", str(script)], capture_output=True, text=True, timeout=15 + ) + assert result.returncode == 0, result.stderr + + +def test_iluvatar_dispatch_can_include_public_and_generated_headers(tmp_path): + compiler = shutil.which("g++") or shutil.which("clang++") + + if not CMAKE or not compiler: + pytest.skip("cmake and a C++ compiler are required") + + source = (ROOT / "src/CMakeLists.txt").read_text(encoding="utf-8") + start = source.index(" set(_iluvatar_dispatch_include_flags") + end = source.index(" foreach(", start) + include_flags = source[start:end] + + for directory in ("include", "generated/include"): + headers = tmp_path / directory / "infini" + headers.mkdir(parents=True) + header = ( + "public.h" if directory == "include" else "operator_call_instantiations.h" + ) + (headers / header).write_text("#pragma once\n") + + dispatch = tmp_path / "dispatch.cc" + dispatch.write_text( + '#include "infini/public.h"\n#include "infini/operator_call_instantiations.h"\n' + ) + script = tmp_path / "compile.cmake" + script.write_text( + f'set(PROJECT_SOURCE_DIR "{tmp_path.as_posix()}")\n' + f'set(CMAKE_CURRENT_SOURCE_DIR "{tmp_path.as_posix()}/src")\n' + f"{include_flags}\n" + f'execute_process(COMMAND "{pathlib.Path(compiler).as_posix()}" ' + f'${{_iluvatar_dispatch_include_flags}} -fsyntax-only "{dispatch.as_posix()}" ' + "RESULT_VARIABLE result)\n" + 'if(NOT result STREQUAL "0")\n' + ' message(FATAL_ERROR "Dispatch header lookup failed.")\n' + "endif()\n" + ) + result = subprocess.run( + [CMAKE, "-P", str(script)], capture_output=True, text=True, timeout=30 + ) + assert result.returncode == 0, result.stderr