Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 110 additions & 38 deletions .github/workflows/amdsmi-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,91 @@ defaults:
shell: bash

jobs:
manylinux-build:
name: Build (Manylinux Gate)
runs-on: ubuntu-24.04
container:
image: quay.io/pypa/manylinux_2_28_x86_64
env:
BUILD_DIR: /tmp/amdsmi-build

steps:
# rocm-systems is a large monorepo; the wheel gate only needs the amdsmi
# project and this workflow, so sparse-checkout keeps the fetch cheap.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
sparse-checkout: |
projects/amdsmi
.github/workflows

- name: Mark workspace safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Select build interpreter
run: |
set -euo pipefail
# The manylinux image periodically drops EOL interpreters
# (e.g. cp38 was removed once Python 3.8 reached end-of-life),
# so pick the newest available stable CPython rather than
# hardcoding a version that may vanish on the next image bump.
PY_BUILD=""
for _py in cp313 cp312 cp311 cp310 cp39; do
_cand="/opt/python/${_py}-${_py}/bin/python3"
if [ -x "$_cand" ]; then PY_BUILD="$_cand"; break; fi
done
if [ -z "$PY_BUILD" ]; then
echo "ERROR: no suitable CPython interpreter found under /opt/python" >&2
exit 1
fi
echo "Using build interpreter: $PY_BUILD ($("$PY_BUILD" --version 2>&1))"
echo "PY_BUILD=$PY_BUILD" >> "$GITHUB_ENV"

- name: Install build prerequisites
run: |
set -euo pipefail
dnf -y install git make gcc gcc-c++ cmake ninja-build openssl-devel libnl3-devel libmnl-devel
"$PY_BUILD" -m pip install --upgrade pip

- name: Configure
run: |
set -euo pipefail
rm -rf "$BUILD_DIR"
cmake -S "$GITHUB_WORKSPACE/projects/amdsmi" -B "$BUILD_DIR" \
-DENABLE_ESMI_LIB=ON \
-DBUILD_PYTHON_WHEEL=ON \
-DCMAKE_BUILD_TYPE=Release \
-DPython3_EXECUTABLE="$PY_BUILD"

- name: Build library and wheel
# The CMake `python_wheel` target is part of ALL (gated on
# BUILD_PYTHON_WHEEL), so this single build produces both the
# SONAME-isolated libamd_smi_python.so and the .whl that bundles it --
# no separate `pip wheel` invocation, which would bypass the target's
# disable_system_fallback.py loader-flag step.
run: make -C "$BUILD_DIR" -j"$(nproc)"

- name: Verify wheel artifact
run: |
set -euo pipefail
wheel_dir="$BUILD_DIR/py-interface/python_package"
shopt -s nullglob
wheels=("$wheel_dir"/*.whl)
if [ "${#wheels[@]}" -eq 0 ]; then
echo "ERROR: no wheel produced under $wheel_dir" >&2
exit 1
fi
printf 'Python wheel built: %s\n' "${wheels[@]}"
echo "WHEEL=${wheels[0]}" >> "$GITHUB_ENV"

- name: Install and import the wheel
run: |
set -euo pipefail
# Prove the wheel is installable and loads its bundled library, not
# just that the file exists. No GPU needed for import.
"$PY_BUILD" -m pip install "$WHEEL"
"$PY_BUILD" -c "import amdsmi; print('import amdsmi OK from', amdsmi.__file__)"
echo "Build gate passed"

# Single job covering every distro + package format. The build-time logic
# (package manager, distro quirks) is autodetected by run_amdsmi_build.py
# from /etc/os-release, so the matrix only needs the OS label plus the
Expand All @@ -50,7 +135,6 @@ jobs:
runs-on:
- self-hosted
- ${{ vars.RUNNER_TYPE }}
continue-on-error: true
strategy:
max-parallel: 10
fail-fast: false
Expand All @@ -70,41 +154,39 @@ jobs:
options: --rm --privileged --device=/dev/kfd --device=/dev/dri --group-add video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --shm-size=64G --cap-add=SYS_MODULE -v /lib/modules:/lib/modules -u root

steps:
# rocm-systems is a large monorepo; only the amdsmi project and this
# workflow are needed, so sparse-checkout keeps the fetch cheap.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
sparse-checkout: |
projects/amdsmi
.github/workflows

- name: Mark workspace safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Set Project Directory
run: |
set -euo pipefail
# Evaluate inside the container: ${{ github.workspace }} is the host
# runner path, but these steps run in a container where the repo is
# mounted elsewhere. $GITHUB_WORKSPACE is the in-container mount.
echo "PROJECT_DIR=$GITHUB_WORKSPACE/projects/amdsmi" >> $GITHUB_ENV

- name: Set Artifact Metadata
if: github.event_name == 'pull_request'
run: |
set -euo pipefail
echo "PR_NUMBER=PR${{ github.event.pull_request.number }}" >> $GITHUB_ENV
echo "BUILD_DATE=$(date -u +%m%d%y-%H%M)" >> $GITHUB_ENV

- name: Set Project Directory
run: |
set -euo pipefail
TARGET_DIR=$(find $GITHUB_WORKSPACE -path "*/projects/amdsmi/CMakeLists.txt" -exec dirname {} \;)
if [ -z "$TARGET_DIR" ]; then
TARGET_DIR=$(find $GITHUB_WORKSPACE -maxdepth 2 -name "CMakeLists.txt" -exec dirname {} \; | head -n 1)
fi
echo "PROJECT_DIR=$TARGET_DIR" >> $GITHUB_ENV

- name: Run AMDSMI Build and Install
run: |
set -euo pipefail
SCRIPT=$(find $GITHUB_WORKSPACE -path "*/projects/amdsmi/tests/amdsmi_build/run_amdsmi_build.py" | head -1)
if [ -z "$SCRIPT" ]; then
echo "ERROR: run_amdsmi_build.py not found"
exit 1
fi
echo "Using runner script: $SCRIPT"

# The script autodetects package manager, format, and distro
# quirks (Debian10 archived repos, RHEL10/AlmaLinux8 QA_RPATHS,
# AzureLinux3 more_itertools, etc.) from /etc/os-release.
python3 "$SCRIPT" \
python3 "${{ env.PROJECT_DIR }}/tests/amdsmi_build/run_amdsmi_build.py" \
--project-dir "${{ env.PROJECT_DIR }}" \
--os-label "${{ matrix.os }}" \
--test-results-dir "/tmp/test-results-${{ matrix.os }}"
Expand All @@ -126,10 +208,14 @@ jobs:
done

- name: Upload Package Artifact
# Not consumed by any downstream job; kept so a reviewer can download
# the built .deb/.rpm from a PR run for manual testing.
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: amd-smi-lib-${{ matrix.package_format }}-${{ matrix.os }}-${{ env.PR_NUMBER }}-${{ env.BUILD_DATE }}
# 99999 is the local (non-release) package version CPack stamps when
# ROCM_LIBPATCH_VERSION is unset, so it matches every dev-built package.
path: |
${{ env.PROJECT_DIR }}/build/amd-smi-lib*99999-local${{ matrix.arch_suffix }}
${{ env.PROJECT_DIR }}/build/amd-smi-lib-*99999-local*.rpm
Expand Down Expand Up @@ -192,6 +278,7 @@ jobs:
cd /opt/rocm/share/amd_smi/tests/python_unittest
./integration_test.py -v > "$RESULTS_DIR/integration_test_output.txt" 2>&1
./unit_tests.py -v > "$RESULTS_DIR/unit_test_output.txt" 2>&1
python3 test_abi_compat.py -v > "$RESULTS_DIR/abi_compat_output.txt" 2>&1
if [ -f ./perf_tests.py ]; then
./perf_tests.py -v > "$RESULTS_DIR/perf_test_output.txt" 2>&1 || echo 'perf_tests.py failed'
else
Expand Down Expand Up @@ -222,6 +309,10 @@ jobs:
if: always()
run: cat /tmp/test-results-${{ matrix.os }}/perf_test_output.txt || echo "No perf test results"

- name: ABI Compat Test Results
if: always()
run: cat /tmp/test-results-${{ matrix.os }}/abi_compat_output.txt || echo "No ABI compat results"

- name: Example DRM Test Results
if: always()
run: cat /tmp/test-results-${{ matrix.os }}/amd_smi_drm_ex.log || echo "No DRM example results"
Expand All @@ -234,26 +325,7 @@ jobs:
if: always()
run: |
set -euo pipefail
SCRIPT=$(find $GITHUB_WORKSPACE -path "*/projects/amdsmi/tests/amdsmi_build/run_amdsmi_build.py" | head -1)
python3 "$SCRIPT" summarize \
python3 "${{ env.PROJECT_DIR }}/tests/amdsmi_build/run_amdsmi_build.py" summarize \
--results-dir "/tmp/test-results-${{ matrix.os }}" \
--os-label "${{ matrix.os }}" \
--summary-file "$GITHUB_STEP_SUMMARY"

- name: Uninstall
if: always()
run: |
set -euo pipefail
case "${{ matrix.package_format }}" in
deb) apt remove -y amd-smi-lib || true ;;
rpm)
case "${{ matrix.os }}" in
SLES) zypper remove -y amd-smi-lib || true ;;
*) dnf remove -y amd-smi-lib || true ;;
esac
;;
esac
rm -f /usr/local/bin/amd-smi
if [ -d /opt/rocm/share/amd_smi ]; then
rm -rf /opt/rocm/share/amd_smi
fi
43 changes: 32 additions & 11 deletions .github/workflows/amdsmi-manylinux-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ jobs:
name: ${{matrix.target.name}}
runs-on: ${{matrix.target.runs-on}}
container: ${{matrix.target.container}}
# Build directory for build_wheel.py's CMake tree. This mirrors
# build_wheel.py's own default (/tmp/amdsmi-build); it is set explicitly
# here so the build step and the SONAME conflict check, which reads both
# libraries back from it, stay coupled to one value.
env:
AMDSMI_WHEEL_BUILD_DIR: /tmp/amdsmi-build

strategy:
fail-fast: false
Expand All @@ -49,6 +55,7 @@ jobs:
python-bin: /opt/python/cp310-cp310/bin/python3
os-variant: AlmaLinux8
repair-flag: '--repair'
release-flag: '--release'
artifact-prefix: amdsmi-rpm-wheels
- id: debian
name: Build Debian Wheels (Ubuntu 22.04)
Expand All @@ -57,10 +64,17 @@ jobs:
python-bin: python3
os-variant: Ubuntu22
repair-flag: ''
release-flag: ''
artifact-prefix: amdsmi-debian-wheels

steps:
# rocm-systems is a large monorepo; the wheel build only needs the amdsmi
# project, so sparse-checkout keeps the fetch cheap.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
sparse-checkout: |
projects/amdsmi
.github/workflows

- name: Install Debian build prerequisites
if: matrix.target.id == 'debian'
Expand All @@ -82,9 +96,10 @@ jobs:
${{matrix.target.python-bin}} \
"$GITHUB_WORKSPACE/projects/amdsmi/tools/build_wheel.py" \
--project-dir "$GITHUB_WORKSPACE/projects/amdsmi" \
--build-dir "$AMDSMI_WHEEL_BUILD_DIR" \
--output-dir "$GITHUB_WORKSPACE/wheels" \
--os-variant ${{matrix.target.os-variant}} \
${{matrix.target.repair-flag}}
${{matrix.target.repair-flag}} ${{matrix.target.release-flag}}

- name: Verify wheels exist
run: |
Expand All @@ -94,24 +109,30 @@ jobs:
exit 1
fi

- name: Verify system/wheel SONAMEs are distinct
run: |
${{matrix.target.python-bin}} \
"$GITHUB_WORKSPACE/projects/amdsmi/tests/run_amdsmi_pkg_conflict_test.py" \
--build-root "$AMDSMI_WHEEL_BUILD_DIR"

- name: Smoke test (install + import)
run: |
${{matrix.target.python-bin}} -m pip install --upgrade pip
${{matrix.target.python-bin}} -m pip install "$GITHUB_WORKSPACE"/wheels/*.whl
${{matrix.target.python-bin}} -c "import amdsmi; print('amdsmi import OK')"
# Prove the installed wheel is what imports (resolves under pip's
# location), not some other amdsmi on the path.
${{matrix.target.python-bin}} -c "import os, subprocess, amdsmi; out = subprocess.check_output(['${{matrix.target.python-bin}}', '-m', 'pip', 'show', 'amdsmi'], text=True); loc = next((l.split(':', 1)[1].strip() for l in out.splitlines() if l.startswith('Location:')), ''); p = os.path.realpath(amdsmi.__file__); print('import from', p, 'pip loc', loc); assert loc and p.startswith(os.path.realpath(loc)), 'wheel is not the imported amdsmi'; print('amdsmi import OK')"
# The wheel must ship with the system-library fallback disabled, so a
# build-step ordering regression that leaves it enabled fails here
# rather than shipping a wheel that can silently load a system .so.
${{matrix.target.python-bin}} -c "import amdsmi.amdsmi_wrapper as w; assert not w._AMDSMI_ALLOW_SYSTEM_FALLBACK, 'wheel shipped with system fallback enabled'; print('fallback disabled OK')"

- name: Upload wheels (by SHA)
# Not consumed by any downstream job; kept so a reviewer can download the
# built wheel from a run. Named by commit SHA for traceability.
- name: Upload wheels
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: ${{matrix.target.artifact-prefix}}-${{github.sha}}
path: ${{github.workspace}}/wheels/*.whl
if-no-files-found: error
retention-days: 30

- name: Upload wheels (downloadable)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: ${{matrix.target.artifact-prefix}}
path: ${{github.workspace}}/wheels/*.whl
if-no-files-found: warn
retention-days: 90
5 changes: 5 additions & 0 deletions projects/amdsmi/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ device/
# misc
esmi_ib_library/

# editor / tooling backup files (keep accidental cruft out of the index)
*.bak
*.backup
*.orig

# do NOT ignore these files
!.clang-format
!.clang-tidy
Expand Down
5 changes: 5 additions & 0 deletions projects/amdsmi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ Full documentation for amd_smi_lib is available at [https://rocm.docs.amd.com/pr

### Changed

- **Reworked Python package install layout for system packages and pip wheels**.
- System `.deb` / `.rpm` packages no longer run `pip install` during postinst; the `amdsmi` Python package is installed directly into the default system Python's `site-packages`/`dist-packages` (detected from the distro-default interpreter) as part of the package payload, so plain `import amdsmi` works after `apt install` / `dnf install`. Removing the package removes those files. `pip list` will no longer show `amdsmi` for the system package.
- The module is additionally staged under `share/amd_smi` (its historical location) for consumers that resolve it via `ROCM_PATH/share/amd_smi` rather than `site-packages` (e.g. rocprofiler-compute, omnistat) and for the TheRock artifact layout, whose manifest captures `share/amd_smi` but not the interpreter's `site-packages`. `site-packages` remains the primary importable location.
- Added new CMake option `-DBUILD_PYTHON_WHEEL=ON` (default `OFF`) which builds the standalone Python wheel and an isolated `libamd_smi_python.so` (distinct SONAME) bundled inside it, so the wheel-shipped library can coexist in-process with the system `libamd_smi.so` without symbol collisions. With `-DBUILD_PYTHON_WHEEL=OFF` (the default used by ROCm CI) only the system-package layout is built; no wheel artifact is produced.
- `py-interface/amdsmi_wrapper.py` now loads the shared library in this order: the `AMDSMI_LIB_OVERRIDE` env var (development / ABI-test escape hatch), a `libamd_smi_python.so` bundled next to the wrapper (pip wheel), then the system `libamd_smi.so` via the dynamic linker. A `_MissingLibrary` sentinel defers `OSError` to the first API call when no candidate is loadable, so import-time tooling (docs, lint) still works without a runtime library.
- **Normalized JSON/CSV key casing in `amd-smi metric` clock and temperature sections**.
- The `uclk_aid`, `socclks_mid`, and temperature `xcd` keys are now lowercase (`aid_<N>`, `mid_<N>`, `xcp_<N>`) in JSON and CSV output, matching the existing `xcp_<N>` usage keys; they were previously uppercase (`AID_<N>`, `MID_<N>`, `XCP_<N>`).
- Human-readable output is unchanged, since it uppercases all keys.
Expand Down
Loading
Loading