diff --git a/.github/workflows/build-upload-conda.yml b/.github/workflows/build-upload-conda.yml index 3f3fca308fe0..75a35cc9cadd 100644 --- a/.github/workflows/build-upload-conda.yml +++ b/.github/workflows/build-upload-conda.yml @@ -42,10 +42,14 @@ jobs: with: fetch-depth: 0 - - name: Create and activate conda env - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 + - name: Setup and activate base conda env + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 with: - python-version: "3.12" + # Keep Conda and conda-build on conda-forge so its build plugin loads. + miniforge-version: "26.3.2-3" + conda-remove-defaults: true + activate-environment: "" + auto-activate: true auto-update-conda: false show-channel-urls: true @@ -53,10 +57,10 @@ jobs: # This ensures conda env is active. shell: bash -l {0} run: | - # Setup the anaconda environment. - conda remove --name base conda-anaconda-telemetry - conda install -q -y -c conda-forge conda-build conda-verify anaconda-client + # Setup the conda-forge environment. + conda install -n base -q -y --override-channels -c conda-forge conda-build anaconda-client conda config --set anaconda_upload no + conda build --version # Build the package. conda build \ @@ -70,7 +74,7 @@ jobs: buildscripts/conda-recipes/pyomp) # Upload the package. - anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload \ + conda run -n base anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload \ --user python-for-hpc --label ${{ env.CONDA_LABEL}} \ --force "$OUTPUT" diff --git a/.github/workflows/build-upload-wheels.yml b/.github/workflows/build-upload-wheels.yml index af7376c7f15e..9cb26f23168e 100644 --- a/.github/workflows/build-upload-wheels.yml +++ b/.github/workflows/build-upload-wheels.yml @@ -6,9 +6,20 @@ permissions: on: release: types: [published] + push: + branches: [main] + paths: + - "buildscripts/cibuildwheel/**" + - "buildscripts/modal/**" + - ".github/workflows/build-upload-wheels.yml" + - "src/**" + - setup.py + - MANIFEST.in + - pyproject.toml pull_request: paths: - "buildscripts/cibuildwheel/**" + - "buildscripts/modal/**" - ".github/workflows/build-upload-wheels.yml" - "src/**" - setup.py @@ -110,9 +121,61 @@ jobs: OMP_TARGET_OFFLOAD=mandatory TEST_DEVICE=host RUN_TARGET=1 \ python -m numba.runtests -v -- numba.openmp.tests.test_openmp.TestOpenmpTarget + # Test Linux wheels on a real NVIDIA GPU. Fork PRs run after merging to main, + # where Modal credentials are available to trusted code. + test-modal-gpu: + needs: build-wheels + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + concurrency: + group: modal-gpu-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: + MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }} + MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + + - name: Download Linux wheels + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 + with: + pattern: cibw-wheels-ubuntu-latest-* + path: dist + merge-multiple: true + + - name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + with: + python-version: "3.12" + + - name: Validate Modal credentials + shell: bash + run: | + if [[ -z "$MODAL_TOKEN_ID" || -z "$MODAL_TOKEN_SECRET" ]]; then + echo "::error::MODAL_TOKEN_ID and MODAL_TOKEN_SECRET must be configured" + exit 1 + fi + + - name: Run GPU tests on Modal + run: | + python -m pip install modal==1.5.3 + set +e + timeout --signal=TERM --kill-after=30s 10m \ + modal run --timestamps buildscripts/modal/test_gpu.py + modal_status=$? + set -e + + if [[ $modal_status -eq 124 ]]; then + echo "::error::Modal GPU tests exceeded the 10-minute timeout" + exit 1 + fi + exit "$modal_status" + # Only on pre-release: Publish to TestPyPI for testing. publish-testpypi: - needs: [build-wheels, test-wheels, build-sdist] + needs: [build-wheels, test-wheels, test-modal-gpu, build-sdist] if: github.event.release.prerelease runs-on: ubuntu-latest environment: testpypi @@ -133,7 +196,7 @@ jobs: # Only on full release: Publish to production PyPI. publish-pypi: - needs: [build-wheels, test-wheels, build-sdist] + needs: [build-wheels, test-wheels, test-modal-gpu, build-sdist] if: github.event_name == 'release' && !github.event.release.prerelease runs-on: ubuntu-latest environment: pypi diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 5ef586c16b75..000000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,98 +0,0 @@ -############################################################################### -# Copyright (c) 2022-23, Lawrence Livermore National Security, LLC and RADIUSS -# project contributors. See the COPYRIGHT file for details. -# -# SPDX-License-Identifier: (MIT) -############################################################################### - -# DESCRIPTION: -############################################################################### -# General GitLab pipelines configurations for supercomputers and Linux clusters -# at Lawrence Livermore National Laboratory (LLNL). -# This entire pipeline is LLNL-specific -# -# Important note: This file is a template provided by llnl/radiuss-shared-ci. -# Remains to set variable values, change the reference to the radiuss-shared-ci -# repo, opt-in and out optional features. The project can then extend it with -# additional stages. -# -# In addition, each project should copy over and complete: -# - .gitlab/custom-jobs-and-variables.yml -# - .gitlab/subscribed-pipelines.yml -# -# The jobs should be specified in a file local to the project, -# - .gitlab/jobs/${CI_MACHINE}.yml -# or generated (see LLNL/Umpire for an example). -############################################################################### - -# We define the following GitLab pipeline variables: -variables: -##### LC GITLAB CONFIGURATION -# Use an LLNL service user to run CI. This prevents from running pipelines as -# an actual user. - LLNL_SERVICE_USER: "" -# Use the service user workspace. Solves permission issues, stores everything -# at the same location whoever triggers a pipeline. - CUSTOM_CI_BUILDS_DIR: "/usr/workspace/ggeorgak/projects/pyomp/gitlab-runner" -# Tells Gitlab to recursively update the submodules when cloning the project. -# GIT_SUBMODULE_STRATEGY: recursive - -##### PROJECT VARIABLES -# We build the projects in the CI clone directory. -# Used in script/gitlab/build_and_test.sh script. -# TODO: add a clean-up mechanism. - BUILD_ROOT: ${CI_PROJECT_DIR} - -##### SHARED_CI CONFIGURATION -# Required information about GitHub repository - GITHUB_PROJECT_NAME: "PyOMP" - GITHUB_PROJECT_ORG: "Python-for-HPC" -# Set the build-and-test command. -# Nested variables are allowed and useful to customize the job command. We -# prevent variable expansion so that you can define them at job level. - JOB_CMD: - value: "buildscripts/gitlab/build-and-test.sh" - expand: false -# Override the pattern describing branches that will skip the "draft PR filter -# test". Add protected branches here. See default value in -# preliminary-ignore-draft-pr.yml. -# ALWAYS_RUN_PATTERN: "" - -# We organize the build-and-test stage with sub-pipelines. Each sub-pipeline -# corresponds to a test batch on a given machine. - -# High level stages -stages: - - prerequisites - - build-and-test - -# Template for jobs triggering a build-and-test sub-pipeline: -.build-and-test: - stage: build-and-test - trigger: - include: - - local: '.gitlab/custom-jobs-and-variables.yml' - - project: 'radiuss/radiuss-shared-ci' - ref: 'v2024.07.0' - file: 'pipelines/${CI_MACHINE}.yml' - # Add your jobs - # you can use a local file - - local: '.gitlab/jobs/${CI_MACHINE}.yml' - # or a file generated in the previous steps - # - artifact: '${CI_MACHINE}-jobs.yml' - # job: 'generate-job-file' - # (See Umpire CI setup for an example). - strategy: depend - forward: - pipeline_variables: true - -include: - # Sets ID tokens for every job using `default:` - - project: 'lc-templates/id_tokens' - file: 'id_tokens.yml' - # [Optional] checks preliminary to running the actual CI test - #- project: 'radiuss/radiuss-shared-ci' - # ref: 'v2024.07.0' - # file: 'utilities/preliminary-ignore-draft-pr.yml' - # pipelines subscribed by the project - - local: '.gitlab/subscribed-pipelines.yml' diff --git a/.gitlab/custom-jobs-and-variables.yml b/.gitlab/custom-jobs-and-variables.yml deleted file mode 100644 index 42f4f55703fb..000000000000 --- a/.gitlab/custom-jobs-and-variables.yml +++ /dev/null @@ -1,30 +0,0 @@ -############################################################################### -# Copyright (c) 2022-23, Lawrence Livermore National Security, LLC and RADIUSS -# project contributors. See the COPYRIGHT file for details. -# -# SPDX-License-Identifier: (MIT) -############################################################################### - -# We define the following GitLab pipeline variables: -variables: -# In some pipelines we create only one allocation shared among jobs in -# order to save time and resources. This allocation has to be uniquely -# named so that we are sure to retrieve it and avoid collisions. - ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID} - -# Tioga -# Arguments for top level allocation -# OPTIONAL: "-o per-resource.count=2" allows to get 2 jobs running on each node. - TIOGA_SHARED_ALLOC: "--queue=pci --exclusive --time-limit=30m --nodes=1" -# Arguments for job level allocation - TIOGA_JOB_ALLOC: "--nodes=1 --begin-time=+5s" -# Add variables that should apply to all the jobs on a machine: -# TIOGA_MY_VAR: "..." - -# Configuration shared by build and test jobs specific to this project. -# Not all configuration can be shared. Here projects can fine tune the -# CI behavior. -# See Umpire for an example (export junit test reports). -.custom_job: - variables: - JOB_TEMPLATE_CANNOT_BE_EMPTY: "True" diff --git a/.gitlab/jobs/tioga.yml b/.gitlab/jobs/tioga.yml deleted file mode 100644 index 3914eb5c310f..000000000000 --- a/.gitlab/jobs/tioga.yml +++ /dev/null @@ -1,62 +0,0 @@ -############################################################################### -# Copyright (c) 2022-23, Lawrence Livermore National Security, LLC and RADIUSS -# project contributors. See the COPYRIGHT file for details. -# -# SPDX-License-Identifier: (MIT) -############################################################################### - -# We require project to define their job command using a variable (JOB_CMD). -# In customization/gitlab-ci.yml, we encourage to define this variable as -# non-expandable, so that project can use nested variables to configure the job -# command. The caveat is that the reproducer here cannot capture the -# definition of these variables in a generic fashion. By overriding the -# following section, projects can specify the variables to define in the -# reproducer to exactly reproduce the CI build. -.tioga_reproducer_vars: - script: - - echo -e "Running on tioga\n" - -# With GitLab CI, included files cannot be empty. -# TODO: remove when you have at least on job defined. -variables: - INCLUDED_FILE_CANNOT_BE_EMPTY: "True" - -############### -# Explanations: -############### -# RADIUSS Shared CI provides a pipeline for each machine, where a template job -# is provided. Each of your jobs must extend this template to be added to the -# list of jobs running on the associated machine. -# -# The job template then expects you to define the "JOB_CMD" variable with the -# one line command used to trigger the build and test of your project. -# -# We suggest that you set your command in such a way that you can then -# customize it per job with variables. E.g.: -# "./path/to/my_ci_script ${A_VARIABLE}" - -## Adding jobs defined by the project. -## Note: placing the extends section first allows you to override part of the -## shared implementation if needed (and if you know what you are doing). -#: -# extends: .job_on_tioga -# variables: -# : "" - -.base-job: - extends: .job_on_tioga - after_script: - - rm -rf ${CI_BUILDS_DIR} ${CI_PROJECT_DIR} - -.python-variants: - parallel: - matrix: - - PYOMP_CI_PYTHON_VERSION: - - "3.10" - - "3.11" - - "3.12" - - "3.13" - - "3.14" - -build-and-test-tioga: - extends: [.base-job, .python-variants] diff --git a/.gitlab/subscribed-pipelines.yml b/.gitlab/subscribed-pipelines.yml deleted file mode 100644 index 714e5618290c..000000000000 --- a/.gitlab/subscribed-pipelines.yml +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################### -# Copyright (c) 2022-23, Lawrence Livermore National Security, LLC and RADIUSS -# project contributors. See the COPYRIGHT file for details. -# -# SPDX-License-Identifier: (MIT) -############################################################################### - -# The template job to test whether a machine is up. -# Expects CI_MACHINE defined to machine name. -.machine-check: - stage: prerequisites - tags: [shell, oslic] - variables: - GIT_STRATEGY: none - script: - - | - if [[ $(jq '.[env.CI_MACHINE].total_nodes_up' /usr/global/tools/lorenz/data/loginnodeStatus) == 0 ]] - then - echo -e "\e[31mNo node available on ${CI_MACHINE}\e[0m" - curl --url "https://api.github.com/repos/${GITHUB_PROJECT_ORG}/${GITHUB_PROJECT_NAME}/statuses/${CI_COMMIT_SHA}" \ - --header 'Content-Type: application/json' \ - --header "authorization: Bearer ${GITHUB_TOKEN}" \ - --data "{ \"state\": \"failure\", \"target_url\": \"${CI_PIPELINE_URL}\", \"description\": \"GitLab ${CI_MACHINE} down\", \"context\": \"ci/gitlab/${CI_MACHINE}\" }" - exit 1 - fi - -### -# Trigger a build-and-test pipeline for a machine. -# Comment the jobs for machines you don’t need. -### - - -## TIOGA -tioga-up-check: - variables: - CI_MACHINE: "tioga" - extends: [.machine-check] - -tioga-build-and-test: - variables: - CI_MACHINE: "tioga" - needs: [tioga-up-check] - extends: [.build-and-test] diff --git a/buildscripts/cibuildwheel/setup-miniconda3.sh b/buildscripts/cibuildwheel/setup-miniconda3.sh deleted file mode 100644 index b50cae6f3647..000000000000 --- a/buildscripts/cibuildwheel/setup-miniconda3.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -# Read LLVM_VERSION from environment and error if not set -if [ -z "${LLVM_VERSION:-}" ]; then - echo "Error: LLVM_VERSION environment variable is not set." >&2 - exit 1 -fi - -if [ "$(uname)" = "Darwin" ]; then - OS_NAME="MacOSX" -else - OS_NAME="Linux" -fi - -echo "Installing miniconda3..." -mkdir -p _downloads -curl -L https://repo.anaconda.com/miniconda/Miniconda3-py311_25.5.1-1-${OS_NAME}-$(uname -m).sh -o _downloads/mini3.sh -mkdir -p _stage -bash _downloads/mini3.sh -b -f -p "_stage/miniconda3" -echo "Miniconda installed" -source "_stage/miniconda3/bin/activate" base -export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true - -# Create conda environment with tools and libraries for the LLVM_VERSION. -echo "Installing llvmdev ${LLVM_VERSION}..." -conda create -n llvmdev-${LLVM_VERSION} -c conda-forge -q -y clang=${LLVM_VERSION} clangxx=${LLVM_VERSION} clang-tools=${LLVM_VERSION} llvmdev=${LLVM_VERSION} zstd diff --git a/buildscripts/cibuildwheel/setup-miniforge3.sh b/buildscripts/cibuildwheel/setup-miniforge3.sh new file mode 100644 index 000000000000..2142c212429f --- /dev/null +++ b/buildscripts/cibuildwheel/setup-miniforge3.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +MINIFORGE_VERSION="26.3.2-3" + +# Read LLVM_VERSION from environment and error if not set +if [ -z "${LLVM_VERSION:-}" ]; then + echo "Error: LLVM_VERSION environment variable is not set." >&2 + exit 1 +fi + +if [ "$(uname)" = "Darwin" ]; then + OS_NAME="MacOSX" +else + OS_NAME="Linux" +fi +ARCH_NAME="$(uname -m)" + +case "${OS_NAME}-${ARCH_NAME}" in + Linux-x86_64) + MINIFORGE_SHA256="848194851a98903134187fbb4ab50efe87b003e0c0f808f97644b7524a62bf2c" + ;; + Linux-aarch64) + MINIFORGE_SHA256="2c113a69297e612b01ca0f320c22a3107a11f2ab9b573d79ac868a175945ce29" + ;; + Linux-ppc64le) + MINIFORGE_SHA256="df7e80ee070ccc6031e2710eb4cf81ee0012264b587306b5aa3890d3d89edd97" + ;; + MacOSX-x86_64) + MINIFORGE_SHA256="39273e4c89a0a1af4538010615d44ae8f44e1af41007e02def593d20f316b003" + ;; + MacOSX-arm64) + MINIFORGE_SHA256="59168f1e24d0a4ad9932021170809fca836cd240e183eeeb331d5bcfc0098168" + ;; + *) + echo "Unsupported Miniforge platform: ${OS_NAME}-${ARCH_NAME}" >&2 + exit 1 + ;; +esac + +echo "Installing Miniforge..." +MINIFORGE_INSTALLER="Miniforge3-${MINIFORGE_VERSION}-${OS_NAME}-${ARCH_NAME}.sh" +MINIFORGE_RELEASE_URL="https://github.com/conda-forge/miniforge/releases/download/${MINIFORGE_VERSION}" +MINIFORGE_URL="${MINIFORGE_RELEASE_URL}/${MINIFORGE_INSTALLER}" +mkdir -p _downloads +curl -fsSL "${MINIFORGE_URL}" -o _downloads/miniforge3.sh +if command -v sha256sum >/dev/null 2>&1; then + printf '%s %s\n' "${MINIFORGE_SHA256}" _downloads/miniforge3.sh | sha256sum --check - +elif command -v shasum >/dev/null 2>&1; then + printf '%s %s\n' "${MINIFORGE_SHA256}" _downloads/miniforge3.sh | shasum -a 256 --check - +else + echo "No SHA256 verification command is available" >&2 + exit 1 +fi +mkdir -p _stage +bash _downloads/miniforge3.sh -b -f -p "_stage/miniforge3" +echo "Miniforge installed" +source "_stage/miniforge3/bin/activate" base + +# Create conda environment with tools and libraries for the LLVM_VERSION. +echo "Installing llvmdev ${LLVM_VERSION}..." +conda create -n llvmdev-${LLVM_VERSION} --override-channels -c conda-forge -q -y clang=${LLVM_VERSION} clangxx=${LLVM_VERSION} clang-tools=${LLVM_VERSION} llvmdev=${LLVM_VERSION} zstd diff --git a/buildscripts/conda-recipes/pyomp/meta.yaml b/buildscripts/conda-recipes/pyomp/meta.yaml index b46ea3b357d3..0c689c90fea6 100644 --- a/buildscripts/conda-recipes/pyomp/meta.yaml +++ b/buildscripts/conda-recipes/pyomp/meta.yaml @@ -52,7 +52,8 @@ requirements: - llvmdev {{ LLVM_VERSION }} - zlib # require llvm-openmp for the openmp cpu runtime. - - llvm-openmp {{ LLVM_VERSION }} + - llvm-openmp 21.1.8.* # [osx] + - llvm-openmp {{ LLVM_VERSION }} # [not osx] - elfutils # [linux] - libffi # [linux] run: @@ -60,7 +61,8 @@ requirements: - setuptools - numba >=0.62, <0.64 # require llvm-openmp for the openmp cpu runtime. - - llvm-openmp {{ LLVM_VERSION }} + - llvm-openmp 21.1.8.* # [osx] + - llvm-openmp {{ LLVM_VERSION }} # [not osx] - lark - cffi diff --git a/buildscripts/gitlab/build-and-test.sh b/buildscripts/gitlab/build-and-test.sh deleted file mode 100644 index eda96566c224..000000000000 --- a/buildscripts/gitlab/build-and-test.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Create a unique temporary directory for this job. -TMPDIR=/tmp/pyomp/${CI_JOB_ID} -rm -rf ${TMPDIR} -mkdir -p ${TMPDIR} -pushd ${TMPDIR} - -# Set the LLVM_VERSION to use. -export LLVM_VERSION="20.1.8" - -# Set the envs directory under the temporary directory. -export CONDA_ENVS_DIRS="${TMPDIR}/_stage/miniconda3/envs" - -# Install miniconda and llvmdev environment. -source ${CI_PROJECT_DIR}/buildscripts/cibuildwheel/setup-miniconda3.sh - -# Export environment variables for building and testing. -export ENABLE_BUNDLED_LIBOMP="1" -export ENABLE_BUNDLED_LIBOMPTARGET="1" -export LLVM_DIR="${CONDA_ENVS_DIRS}/llvmdev-${LLVM_VERSION}" -export CMAKE_PREFIX_PATH="${CONDA_PREFIX}" -export USE_CXX11_ABI="1" -export PIP_NO_INPUT="1" - -# Create and activate a conda environment with the desired Python version. -conda create -n py-${PYOMP_CI_PYTHON_VERSION} -c conda-forge -y python=${PYOMP_CI_PYTHON_VERSION} -conda activate py-${PYOMP_CI_PYTHON_VERSION} -# Add extra packages needed to build openmp libraries. -conda install -c conda-forge -y zstd libffi - -# Clone and fetch the commit with history for package versioning. -git clone https://github.com/${GITHUB_PROJECT_ORG}/${GITHUB_PROJECT_NAME}.git --single-branch -cd ${GITHUB_PROJECT_NAME} -git fetch origin ${CI_COMMIT_SHA} -git checkout ${CI_COMMIT_SHA} - -# Install pyomp. -CC=clang CXX=clang++ python -m pip install -v . - -# Run host OpenMP tests. -RUN_TARGET=0 python -m numba.runtests -v -- numba.openmp.tests.test_openmp -# Run device (cpu target) OpenMP tests. -OMP_TARGET_OFFLOAD=mandatory TEST_DEVICE=host RUN_TARGET=1 python -m numba.runtests -v -- numba.openmp.tests.test_openmp.TestOpenmpTarget - -popd diff --git a/buildscripts/gitlab/create-conda-pkgs.sh b/buildscripts/gitlab/create-conda-pkgs.sh deleted file mode 100755 index ddca46a3d42e..000000000000 --- a/buildscripts/gitlab/create-conda-pkgs.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [ -n "${CI_COMMIT_TAG}" ]; then - LABEL="main" -else - LABEL="test" -fi - -# Create a temporary directory for the build to clone the full repo for package -# versioning. -TMPDIR=/tmp/pyomp/${CI_JOB_ID} -mkdir -p ${TMPDIR} -pushd ${TMPDIR} - -# Clone and fetch the commit with history for package versioning. -git clone https://github.com/${GITHUB_PROJECT_ORG}/${GITHUB_PROJECT_NAME}.git --single-branch -cd ${GITHUB_PROJECT_NAME} -git fetch origin ${CI_COMMIT_SHA} -git checkout ${CI_COMMIT_SHA} - -# Set pkg dir per job to avoid conflicts. -export CONDA_PKGS_DIRS=/tmp/pyomp/conda-pkgs-${CI_JOB_ID} -mkdir -p "$CONDA_PKGS_DIRS" - -function deploy_conda() { - PKG="${1}" - - echo "=> Conda deploy ${PKG}" - - set -x - - export CONDA_BLD_PATH="/tmp/pyomp/conda-build-${PYOMP_CI_BUILD_PKG}-${PYOMP_CI_PYTHON_VERSION}" - conda build --no-lock --no-locking --user python-for-hpc --label ${LABEL} \ - -c python-for-hpc/label/${LABEL} -c conda-forge \ - --python ${PYOMP_CI_PYTHON_VERSION} \ - buildscripts/conda-recipes/${PKG} - - rm -rf ${CONDA_BLD_PATH} - set +x -} - -echo "=> Building ${PYOMP_CI_BUILD_PKG} Python version ${PYOMP_CI_PYTHON_VERSION} Label ${LABEL}" - - -case ${PYOMP_CI_BUILD_PKG} in - - "pyomp") - deploy_conda "pyomp" - ;; - - *) - echo "Unknown package to build ${PYOMP_CI_BUILD_PKG}" - exit 1 - ;; - -esac - -popd diff --git a/buildscripts/modal/test_gpu.py b/buildscripts/modal/test_gpu.py new file mode 100644 index 000000000000..b01a55e94d6a --- /dev/null +++ b/buildscripts/modal/test_gpu.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +import os +from pathlib import Path +import subprocess + +import modal + + +PYTHON_VERSIONS = ("3.10", "3.11", "3.12", "3.13", "3.14") +NUMBA_VERSION = "0.63.1" +MINIFORGE_VERSION = "26.3.2-3" +MINIFORGE_SHA256 = "848194851a98903134187fbb4ab50efe87b003e0c0f808f97644b7524a62bf2c" +WHEEL_DIRECTORY = ( + Path(__file__).resolve().parents[2] / "dist" + if modal.is_local() + else Path("/wheels") +) +MINIFORGE_PREFIX = "/opt/miniforge3" + + +def find_linux_wheels() -> dict[str, Path]: + if not WHEEL_DIRECTORY.is_dir(): + raise RuntimeError(f"Linux wheel directory does not exist: {WHEEL_DIRECTORY}") + + wheels = {} + for python_version in PYTHON_VERSIONS: + abi = python_version.replace(".", "") + matches = sorted(WHEEL_DIRECTORY.glob(f"*cp{abi}-*x86_64.whl")) + if len(matches) != 1: + raise RuntimeError( + f"Expected one cp{abi} Linux x86-64 wheel in {WHEEL_DIRECTORY}, " + f"found {len(matches)}: {matches}" + ) + wheels[python_version] = matches[0] + return wheels + + +LINUX_WHEELS = find_linux_wheels() + + +def image_build_commands() -> tuple[list[str], list[str]]: + environment_commands = [] + wheel_commands = [] + for python_version, wheel in LINUX_WHEELS.items(): + environment = f"py{python_version.replace('.', '')}" + python = f"{MINIFORGE_PREFIX}/envs/{environment}/bin/python" + environment_commands.extend( + [ + ( + f"{MINIFORGE_PREFIX}/bin/conda create -q -y " + f"--override-channels -c conda-forge -n {environment} " + f"python={python_version} pip" + ), + ( + f"{python} -m pip install " + f"numba=={NUMBA_VERSION} lark cffi setuptools" + ), + ( + f'{python} -c "import numba; ' + f"assert numba.__version__ == '{NUMBA_VERSION}'\"" + ), + ] + ) + wheel_commands.append( + f"{python} -m pip install --no-deps /wheels/{wheel.name}" + ) + return environment_commands, wheel_commands + + +ENVIRONMENT_COMMANDS, WHEEL_COMMANDS = image_build_commands() + + +image = ( + modal.Image.from_registry( + "nvidia/cuda:12.8.1-devel-ubuntu22.04", add_python="3.12" + ) + .entrypoint([]) + .apt_install("ca-certificates", "curl") + .run_commands( + f"curl -fsSL https://github.com/conda-forge/miniforge/releases/download/" + f"{MINIFORGE_VERSION}/Miniforge3-{MINIFORGE_VERSION}-Linux-x86_64.sh " + "-o /tmp/miniforge3.sh", + f"printf '%s %s\\n' {MINIFORGE_SHA256} /tmp/miniforge3.sh " + "| sha256sum --check -", + f"bash /tmp/miniforge3.sh -b -p {MINIFORGE_PREFIX}", + ) + .run_commands(*ENVIRONMENT_COMMANDS) + .add_local_dir(WHEEL_DIRECTORY, "/wheels", copy=True) + .run_commands(*WHEEL_COMMANDS) +) + +app = modal.App("pyomp-gpu-ci") + + +@app.function( + image=image, + gpu="T4", + timeout=60 * 60, + restrict_modal_access=True, + single_use_containers=True, + block_network=True, +) +def test_gpu_wheels() -> None: + subprocess.run(["nvidia-smi", "--list-gpus"], check=True) + + test_environment = os.environ.copy() + test_environment.update( + { + "NUMBA_DEVELOPER_MODE": "1", + "NUMBA_DISABLE_ERROR_MESSAGE_HIGHLIGHTING": "1", + "NUMBA_CAPTURED_ERRORS": "new_style", + "PYTHONFAULTHANDLER": "1", + "OMP_TARGET_OFFLOAD": "mandatory", + "TEST_DEVICE": "gpu", + "RUN_TARGET": "1", + } + ) + + for python_version in PYTHON_VERSIONS: + environment = f"py{python_version.replace('.', '')}" + python = f"{MINIFORGE_PREFIX}/envs/{environment}/bin/python" + print(f"Running GPU tests with Python {python_version} and Numba {NUMBA_VERSION}") + subprocess.run( + [ + python, + "-c", + "from numba.openmp import print_offloading_info; " + "print_offloading_info()", + ], + check=True, + env=test_environment, + ) + subprocess.run( + [ + python, + "-m", + "numba.runtests", + "-v", + "--", + "numba.openmp.tests.test_openmp.TestOpenmpTarget", + ], + check=True, + env=test_environment, + ) + + +@app.local_entrypoint() +def main() -> None: + test_gpu_wheels.remote() diff --git a/pyproject.toml b/pyproject.toml index aa6c624e2610..5069c16cff2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,15 +68,15 @@ CC = "/usr/bin/clang" CXX = "/usr/bin/clang++" [tool.cibuildwheel.macos] -before-all = ["bash buildscripts/cibuildwheel/setup-miniconda3.sh"] +before-all = ["bash buildscripts/cibuildwheel/setup-miniforge3.sh"] [tool.cibuildwheel.macos.environment] ENABLE_BUNDLED_LIBOMP = "1" -LLVM_DIR = "${PWD}/_stage/miniconda3/envs/llvmdev-${LLVM_VERSION}/lib/cmake/llvm" -CC = "${PWD}/_stage/miniconda3/envs/llvmdev-${LLVM_VERSION}/bin/clang" -CXX = "${PWD}/_stage/miniconda3/envs/llvmdev-${LLVM_VERSION}/bin/clang++" +LLVM_DIR = "${PWD}/_stage/miniforge3/envs/llvmdev-${LLVM_VERSION}/lib/cmake/llvm" +CC = "${PWD}/_stage/miniforge3/envs/llvmdev-${LLVM_VERSION}/bin/clang" +CXX = "${PWD}/_stage/miniforge3/envs/llvmdev-${LLVM_VERSION}/bin/clang++" # Set the deplioyment target to macOS 11.0. MACOSX_DEPLOYMENT_TARGET = "11.0" # Set the cmake prefix path to find libraries in the conda environment which is # compatible with the macos target. -CMAKE_PREFIX_PATH = "${PWD}/_stage/miniconda3/envs/llvmdev-${LLVM_VERSION}/" +CMAKE_PREFIX_PATH = "${PWD}/_stage/miniforge3/envs/llvmdev-${LLVM_VERSION}/"