From b4ed5fc5e68f85ff84fad245c4eb39718347cda8 Mon Sep 17 00:00:00 2001 From: Peter Heywood Date: Fri, 29 Nov 2024 11:37:55 +0000 Subject: [PATCH] Add initial CI workflows - Lint workflow which just does linting, separateley to compilation workflows - Ubuntu workflow which tests different combinations of CUDA, GCC and Ubuntu --- .github/scripts/install_cuda_ubuntu.sh | 196 +++++++++++++++++++++++++ .github/workflows/Lint.yml | 66 +++++++++ .github/workflows/Ubuntu.yml | 120 +++++++++++++++ 3 files changed, 382 insertions(+) create mode 100755 .github/scripts/install_cuda_ubuntu.sh create mode 100644 .github/workflows/Lint.yml create mode 100644 .github/workflows/Ubuntu.yml diff --git a/.github/scripts/install_cuda_ubuntu.sh b/.github/scripts/install_cuda_ubuntu.sh new file mode 100755 index 0000000..6a5dfa0 --- /dev/null +++ b/.github/scripts/install_cuda_ubuntu.sh @@ -0,0 +1,196 @@ +# @todo - better / more robust parsing of inputs from env vars. +## ------------------- +## Constants +## ------------------- + +# List of sub-packages to install. +# @todo - pass this in from outside the script? +# @todo - check the specified subpackages exist via apt pre-install? apt-rdepends cuda-9-0 | grep "^cuda-"? + +# Ideally choose from the list of meta-packages to minimise variance between cuda versions (although it does change too). Some of these packages may not be availble in older CUDA releases +CUDA_PACKAGES_IN=( + "cuda-compiler" + "cuda-cudart-dev" + "cuda-nvtx" + "cuda-nvrtc-dev" + "libcurand-dev" # 11-0+ + "cuda-cccl" # 11.4+, provides cub and thrust. On 11.3 knwon as cuda-thrust-11-3 +) + +## ------------------- +## Bash functions +## ------------------- +# returns 0 (true) if a >= b +function version_ge() { + [ "$#" != "2" ] && echo "${FUNCNAME[0]} requires exactly 2 arguments." && exit 1 + [ "$(printf '%s\n' "$@" | sort -V | head -n 1)" == "$2" ] +} +# returns 0 (true) if a > b +function version_gt() { + [ "$#" != "2" ] && echo "${FUNCNAME[0]} requires exactly 2 arguments." && exit 1 + [ "$1" = "$2" ] && return 1 || version_ge $1 $2 +} +# returns 0 (true) if a <= b +function version_le() { + [ "$#" != "2" ] && echo "${FUNCNAME[0]} requires exactly 2 arguments." && exit 1 + [ "$(printf '%s\n' "$@" | sort -V | head -n 1)" == "$1" ] +} +# returns 0 (true) if a < b +function version_lt() { + [ "$#" != "2" ] && echo "${FUNCNAME[0]} requires exactly 2 arguments." && exit 1 + [ "$1" = "$2" ] && return 1 || version_le $1 $2 +} + +## ------------------- +## Select CUDA version +## ------------------- + +# Get the cuda version from the environment as $cuda. +CUDA_VERSION_MAJOR_MINOR=${cuda} + +# Split the version. +# We (might/probably) don't know PATCH at this point - it depends which version gets installed. +CUDA_MAJOR=$(echo "${CUDA_VERSION_MAJOR_MINOR}" | cut -d. -f1) +CUDA_MINOR=$(echo "${CUDA_VERSION_MAJOR_MINOR}" | cut -d. -f2) +CUDA_PATCH=$(echo "${CUDA_VERSION_MAJOR_MINOR}" | cut -d. -f3) +# use lsb_release to find the OS. +UBUNTU_VERSION=$(lsb_release -sr) +UBUNTU_VERSION="${UBUNTU_VERSION//.}" + +echo "CUDA_MAJOR: ${CUDA_MAJOR}" +echo "CUDA_MINOR: ${CUDA_MINOR}" +echo "CUDA_PATCH: ${CUDA_PATCH}" +# echo "UBUNTU_NAME: ${UBUNTU_NAME}" +echo "UBUNTU_VERSION: ${UBUNTU_VERSION}" + +# If we don't know the CUDA_MAJOR or MINOR, error. +if [ -z "${CUDA_MAJOR}" ] ; then + echo "Error: Unknown CUDA Major version. Aborting." + exit 1 +fi +if [ -z "${CUDA_MINOR}" ] ; then + echo "Error: Unknown CUDA Minor version. Aborting." + exit 1 +fi +# If we don't know the Ubuntu version, error. +if [ -z ${UBUNTU_VERSION} ]; then + echo "Error: Unknown Ubuntu version. Aborting." + exit 1 +fi + + +## ------------------------------- +## Select CUDA packages to install +## ------------------------------- +CUDA_PACKAGES="" +for package in "${CUDA_PACKAGES_IN[@]}" +do : + # @todo This is not perfect. Should probably provide a separate list for diff versions + # cuda-compiler-X-Y if CUDA >= 9.1 else cuda-nvcc-X-Y + if [[ "${package}" == "cuda-nvcc" ]] && version_ge "$CUDA_VERSION_MAJOR_MINOR" "9.1" ; then + package="cuda-compiler" + elif [[ "${package}" == "cuda-compiler" ]] && version_lt "$CUDA_VERSION_MAJOR_MINOR" "9.1" ; then + package="cuda-nvcc" + # CUB/Thrust are packages in cuda-thrust in 11.3, but cuda-cccl in 11.4+ + elif [[ "${package}" == "cuda-thrust" || "${package}" == "cuda-cccl" ]]; then + # CUDA cuda-thrust >= 11.4 + if version_ge "$CUDA_VERSION_MAJOR_MINOR" "11.4" ; then + package="cuda-cccl" + # Use cuda-thrust > 11.2 + elif version_ge "$CUDA_VERSION_MAJOR_MINOR" "11.3" ; then + package="cuda-thrust" + # Do not include this pacakge < 11.3 + else + continue + fi + fi + # CUDA 11+ includes lib* / lib*-dev packages, which if they existed previously where cuda-cu*- / cuda-cu*-dev- + if [[ ${package} == libcu* ]] && version_lt "$CUDA_VERSION_MAJOR_MINOR" "11.0" ; then + package="${package/libcu/cuda-cu}" + fi + # Build the full package name and append to the string. + CUDA_PACKAGES+=" ${package}-${CUDA_MAJOR}-${CUDA_MINOR}" +done +echo "CUDA_PACKAGES ${CUDA_PACKAGES}" + +## ----------------- +## Prepare to install +## ----------------- +CPU_ARCH="x86_64" +PIN_FILENAME="cuda-ubuntu${UBUNTU_VERSION}.pin" +PIN_URL="https://developer.download.nvidia.com/compute/cuda/repos/ubuntu${UBUNTU_VERSION}/${CPU_ARCH}/${PIN_FILENAME}" +# apt keyring package now available https://developer.nvidia.com/blog/updating-the-cuda-linux-gpg-repository-key/ +KERYRING_PACKAGE_FILENAME="cuda-keyring_1.0-1_all.deb" +KEYRING_PACKAGE_URL="https://developer.download.nvidia.com/compute/cuda/repos/ubuntu${UBUNTU_VERSION}/${CPU_ARCH}/${KERYRING_PACKAGE_FILENAME}" +REPO_URL="https://developer.download.nvidia.com/compute/cuda/repos/ubuntu${UBUNTU_VERSION}/${CPU_ARCH}/" + +echo "PIN_FILENAME ${PIN_FILENAME}" +echo "PIN_URL ${PIN_URL}" +echo "KEYRING_PACKAGE_URL ${KEYRING_PACKAGE_URL}" +echo "APT_KEY_URL ${APT_KEY_URL}" + +## ----------------- +## Check for root/sudo +## ----------------- + +# Detect if the script is being run as root, storing true/false in is_root. +is_root=false +if (( $EUID == 0)); then + is_root=true +fi +# Find if sudo is available +has_sudo=false +if command -v sudo &> /dev/null ; then + has_sudo=true +fi +# Decide if we can proceed or not (root or sudo is required) and if so store whether sudo should be used or not. +if [ "$is_root" = false ] && [ "$has_sudo" = false ]; then + echo "Root or sudo is required. Aborting." + exit 1 +elif [ "$is_root" = false ] ; then + USE_SUDO=sudo +else + USE_SUDO= +fi + +## ----------------- +## Install +## ----------------- +echo "Adding CUDA Repository" +wget ${PIN_URL} +$USE_SUDO mv ${PIN_FILENAME} /etc/apt/preferences.d/cuda-repository-pin-600 +wget ${KEYRING_PACKAGE_URL} && ${USE_SUDO} dpkg -i ${KERYRING_PACKAGE_FILENAME} && rm ${KERYRING_PACKAGE_FILENAME} +$USE_SUDO add-apt-repository "deb ${REPO_URL} /" +$USE_SUDO apt-get update + +echo "Installing CUDA packages ${CUDA_PACKAGES}" +$USE_SUDO apt-get -y install ${CUDA_PACKAGES} + +if [[ $? -ne 0 ]]; then + echo "CUDA Installation Error." + exit 1 +fi + +## ----------------- +## Set environment vars / vars to be propagated +## ----------------- + +CUDA_PATH=/usr/local/cuda-${CUDA_MAJOR}.${CUDA_MINOR} +echo "CUDA_PATH=${CUDA_PATH}" +export CUDA_PATH=${CUDA_PATH} +export PATH="$CUDA_PATH/bin:$PATH" +export LD_LIBRARY_PATH="$CUDA_PATH/lib:$LD_LIBRARY_PATH" +export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$LD_LIBRARY_PATH" +# Check nvcc is now available. +nvcc -V + + +# If executed on github actions, make the appropriate echo statements to update the environment +if [[ $GITHUB_ACTIONS ]]; then + # Set paths for subsequent steps, using ${CUDA_PATH} + echo "Adding CUDA to CUDA_PATH, PATH and LD_LIBRARY_PATH" + echo "CUDA_PATH=${CUDA_PATH}" >> $GITHUB_ENV + echo "${CUDA_PATH}/bin" >> $GITHUB_PATH + echo "LD_LIBRARY_PATH=${CUDA_PATH}/lib:${LD_LIBRARY_PATH}" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=${CUDA_PATH}/lib64:${LD_LIBRARY_PATH}" >> $GITHUB_ENV +fi diff --git a/.github/workflows/Lint.yml b/.github/workflows/Lint.yml new file mode 100644 index 0000000..71c772d --- /dev/null +++ b/.github/workflows/Lint.yml @@ -0,0 +1,66 @@ +# Lint the project using cpplint, this currently requires CUDA for CMake configuration to succeed +name: Lint + +on: + # Branch pushes that do not only modify other workflow files + push: + branches: + - '**' + paths: + - "**" + - "!.github/**" + - ".github/scripts/install_cuda_ubuntu.sh" + - ".github/workflows/Lint.yml" + # Allow manual invocation. + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + cpplint: + runs-on: ${{ matrix.cudacxx.os }} + name: "cpplint (${{ matrix.cudacxx.cuda }}, ${{ matrix.cudacxx.os }})" + strategy: + fail-fast: false + # Multiplicative build matrix + # optional exclude: can be partial, include: must be specific + matrix: + cudacxx: + - cuda: "12.0" + os: ubuntu-22.04 + env: + # Define constants + BUILD_DIR: "build" + CUDA: ${{ matrix.cudacxx.cuda }} + OS: ${{ matrix.cudacxx.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Install CUDA + if: ${{ startswith(env.OS, 'ubuntu') && env.CUDA != '' }} + env: + cuda: ${{ env.CUDA }} + run: .github/scripts/install_cuda_ubuntu.sh + + - name: Install cpplint + run: | + python3 -m pip install cpplint + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Add custom problem matchers for annotations + run: echo "::add-matcher::.github/problem-matchers.json" + + - name: Configure cmake + run: > + cmake . -B "${{ env.BUILD_DIR }}" + -Wno-dev + -DCMAKE_WARN_DEPRECATED="OFF" + -DFLAMEGPU_WARNINGS_AS_ERRORS="ON" + -DBUILD_TESTING="ON" + + - name: Lint + working-directory: ${{ env.BUILD_DIR }} + run: cmake --build . --target lint --verbose -j `nproc` diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml new file mode 100644 index 0000000..7cb565f --- /dev/null +++ b/.github/workflows/Ubuntu.yml @@ -0,0 +1,120 @@ +# Compile project on Ubuntu +name: Ubuntu + +on: + # Branch pushes that do not only modify other workflow files + push: + branches: + - '**' + paths: + - "**" + - "!.github/**" + - ".github/scripts/install_cuda_ubuntu.sh" + - ".github/workflows/Ubuntu.yml" + # Allow manual invocation. + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + build: + runs-on: ${{ matrix.cudacxx.os }} + strategy: + fail-fast: false + # Multiplicative build matrix + # optional exclude: can be partial, include: must be specific + matrix: + cudacxx: + # CUDA 12.6 with gcc 12 on 24.04 + - cuda: "12.6" + cuda_arch: "52" + hostcxx: gcc-12 + os: ubuntu-24.04 + # CUDA 12.0 with gcc 11 on 22.04 + - cuda: "12.6" + cuda_arch: "52" + hostcxx: gcc-11 + os: ubuntu-22.04 + # CUDA 11.8 with GCC 10 + - cuda: "11.8" + cuda_arch: "52" + hostcxx: gcc-10 + os: ubuntu-22.04 + # CUDA 11.6 with GCC 9 on 22.04 + - cuda: "11.6" + cuda_arch: "35" + hostcxx: gcc-9 + os: ubuntu-22.04 + # CUDA 11.2, with GCC 8 on Ubuntu 20.04 to try and prevent use of C++17 features not present in gcc 8 + - cuda: "11.2" + cuda_arch: "35" + hostcxx: gcc-8 + os: ubuntu-20.04 + + # Name the job based on matrix/env options + name: "build (${{ matrix.cudacxx.cuda }}, ${{ matrix.cudacxx.hostcxx }}, ${{ matrix.cudacxx.os }})" + + # Define job-wide env constants, and promote matrix elements to env constants for portable steps. + env: + # Define constants + BUILD_DIR: "build" + # Port matrix options to environment, for more portability. + CUDA: ${{ matrix.cudacxx.cuda }} + CUDA_ARCH: ${{ matrix.cudacxx.cuda_arch }} + HOSTCXX: ${{ matrix.cudacxx.hostcxx }} + OS: ${{ matrix.cudacxx.os }} + CONFIG: ${{ matrix.config.config }} + + steps: + - uses: actions/checkout@v3 + + - name: Install CUDA + if: ${{ startswith(env.OS, 'ubuntu') && env.CUDA != '' }} + env: + cuda: ${{ env.CUDA }} + run: .github/scripts/install_cuda_ubuntu.sh + + - name: Install/Select gcc and g++ + if: ${{ startsWith(env.HOSTCXX, 'gcc-') }} + run: | + gcc_version=${HOSTCXX//gcc-/} + sudo apt-get install -y gcc-${gcc_version} g++-${gcc_version} + echo "CC=/usr/bin/gcc-${gcc_version}" >> $GITHUB_ENV + echo "CXX=/usr/bin/g++-${gcc_version}" >> $GITHUB_ENV + echo "CUDAHOSTCXX=/usr/bin/g++-${gcc_version}" >> $GITHUB_ENV + + - name: Add custom problem matchers for annotations + run: echo "::add-matcher::.github/problem-matchers.json" + + - name: Enable git safe-directory + run: git config --global --add safe.directory $GITHUB_WORKSPACE + + - name: Configure cmake + run: > + cmake . -B "${{ env.BUILD_DIR }}" + -DCMAKE_BUILD_TYPE="Release" + -Wno-dev + -DCMAKE_WARN_DEPRECATED="OFF" + -DFLAMEGPU_WARNINGS_AS_ERRORS="ON" + -DCMAKE_CUDA_ARCHITECTURES="${{ env.CUDA_ARCH }}" + -DBUILD_TESTING="ON" + + - name: Build flamegpu + working-directory: ${{ env.BUILD_DIR }} + run: cmake --build . --target flamegpu --verbose -j `nproc` + + - name: Build the simulation binary + if: ${{ env.FLAMEGPU_BUILD_PYTHON == 'ON' }} + working-directory: ${{ env.BUILD_DIR }} + run: cmake --build . --target exatepp_abm --verbose -j `nproc` + + - name: Build the c++ test suite + if: ${{ env.FLAMEGPU_BUILD_TESTS == 'ON' }} + working-directory: ${{ env.BUILD_DIR }} + run: cmake --build . --target tests --verbose -j `nproc` + + - name: Build any remaining targets + working-directory: ${{ env.BUILD_DIR }} + run: cmake --build . --target all --verbose -j `nproc`