diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml new file mode 100644 index 0000000..028c79d --- /dev/null +++ b/.github/workflows/release-binaries.yml @@ -0,0 +1,208 @@ +name: Release binaries + +# Builds release ZIPs on GitHub-hosted runners. MATLAB comes from +# matlab-actions/setup-matlab; CUDA comes from Jimver/cuda-toolkit. These builds +# compile CUDA but do not run GPU tests because hosted runners have no NVIDIA GPU. + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: Release tag to upload assets to + required: true + overwrite: + description: Replace assets with the same name + type: boolean + default: false + +permissions: + contents: write + +concurrency: + group: release-binaries-${{ github.event.release.tag_name || inputs.tag }} + cancel-in-progress: false + +env: + VCPKG_ROOT: "C:\\vcpkg" + +jobs: + windows: + runs-on: windows-2022 + timeout-minutes: 180 + strategy: + fail-fast: false + matrix: + cuda: + - { label: "12.8", version: "12.8.1", arch: "61;75;86;89;90" } + - { label: "12.9", version: "12.9.1", arch: "61;75;86;89;90" } + - { label: "13.1", version: "13.1.0", arch: "75;80;86;89;90;100;103;121" } + matlab: + - { label: "2022a", release: "R2022a", cmake: "9.12" } + - { label: "2024a", release: "R2024a", cmake: "24.1" } + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set up CUDA + uses: Jimver/cuda-toolkit@v0.2.36 + with: + cuda: ${{ matrix.cuda.version }} + log-file-suffix: windows-${{ matrix.cuda.label }}-${{ matrix.matlab.label }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v3 + with: + release: ${{ matrix.matlab.release }} + + - name: Set MATLAB root + shell: pwsh + run: | + $matlabExe = (Get-Command matlab).Source + $matlabRoot = Split-Path (Split-Path $matlabExe -Parent) -Parent + "Matlab_ROOT_DIR=$matlabRoot" >> $env:GITHUB_ENV + + - name: Set up vcpkg + shell: pwsh + run: .\build_scripts\setup_vcpkg.ps1 + + - name: Configure + shell: pwsh + run: | + $args = @( + "-S", "echoframe/cpp/src", + "-B", "build", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "cuda=$env:CUDA_PATH", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake", + "-DVCPKG_TARGET_TRIPLET=x64-windows-static-md", + "-DCMAKE_PREFIX_PATH=$env:VCPKG_ROOT/installed/x64-windows-static-md", + "-DCMAKE_CUDA_ARCHITECTURES=${{ matrix.cuda.arch }}", + "-DCUDAToolkit_ROOT=$env:CUDA_PATH", + "-DMatlab_ROOT_DIR=$env:Matlab_ROOT_DIR", + "-DMATLAB_VERSION=${{ matrix.matlab.cmake }}", + "-DEF_BUILD_CLI=ON", + "-DEF_BUILD_MEX=ON", + "-DEF_BUILD_PYTHON=ON", + "-DPython_ROOT_DIR=$env:pythonLocation", + "-DPython_EXECUTABLE=$env:pythonLocation/python.exe", + "-DPython_FIND_STRATEGY=LOCATION" + ) + cmake @args + + - name: Build + shell: pwsh + run: cmake --build build --config Release -j $env:NUMBER_OF_PROCESSORS + + - name: Package + shell: pwsh + run: | + New-Item -ItemType Directory -Force release-assets | Out-Null + $name = "build_CUDA_${{ matrix.cuda.label }}_MATLAB_${{ matrix.matlab.label }}" + pwsh -NoProfile -ExecutionPolicy Bypass -File build_scripts/pack_build.ps1 ` + -ReleaseDir build/Release -Out "release-assets/$name.zip" + + - name: Upload release asset + shell: pwsh + env: + GITHUB_TOKEN: ${{ github.token }} + TAG_NAME: ${{ github.event.release.tag_name || inputs.tag }} + OVERWRITE: ${{ inputs.overwrite || false }} + run: .github/workflows/scripts/upload-release-assets.ps1 release-assets + + linux: + runs-on: ubuntu-22.04 + timeout-minutes: 180 + env: + VCPKG_ROOT: ${{ github.workspace }}/vcpkg + strategy: + fail-fast: false + matrix: + cuda: + - { label: "12.8", version: "12.8.1", arch: "61;75;86;89;90" } + - { label: "12.9", version: "12.9.1", arch: "61;75;86;89;90" } + - { label: "13.1", version: "13.1.0", arch: "75;80;86;89;90;100;103;121" } + matlab: + - { label: "2022a", release: "R2022a" } + - { label: "2024a", release: "R2024a" } + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y build-essential cmake ninja-build zip curl unzip tar pkg-config + + - name: Set up CUDA + uses: Jimver/cuda-toolkit@v0.2.36 + with: + cuda: ${{ matrix.cuda.version }} + log-file-suffix: linux-${{ matrix.cuda.label }}-${{ matrix.matlab.label }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v3 + with: + release: ${{ matrix.matlab.release }} + + - name: Set MATLAB root + run: | + matlab_bin="$(command -v matlab)" + echo "Matlab_ROOT_DIR=$(dirname "$(dirname "$matlab_bin")")" >> "$GITHUB_ENV" + + - name: Set up vcpkg + run: | + git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT" + "$VCPKG_ROOT/bootstrap-vcpkg.sh" -disableMetrics + + - name: Configure + run: | + cmake -S echoframe/cpp/src -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \ + -DCMAKE_CUDA_ARCHITECTURES="${{ matrix.cuda.arch }}" \ + -DCUDAToolkit_ROOT="$CUDA_PATH" \ + -DMatlab_ROOT_DIR="$Matlab_ROOT_DIR" \ + -DEF_BUILD_CLI=ON \ + -DEF_BUILD_MEX=ON \ + -DEF_BUILD_PYTHON=ON \ + -DPython_EXECUTABLE="$pythonLocation/bin/python" \ + -DPython_FIND_STRATEGY=LOCATION \ + -DCMAKE_CUDA_HOST_COMPILER=/usr/bin/g++ + + - name: Build + run: cmake --build build -j "$(nproc)" + + - name: Package + run: | + name="build_linux_CUDA_${{ matrix.cuda.label }}_MATLAB_${{ matrix.matlab.label }}" + mkdir -p "stage/build/Release" release-assets + find build -maxdepth 2 -type f \( \ + -name 'echoframe_mex.mexa64' -o \ + -name 'storage.mexa64' -o \ + -name 'echoframe*.so' -o \ + -name 'echoframe_cli' \ + \) -exec cp {} stage/build/Release/ \; + test "$(find stage/build/Release -type f | wc -l)" -gt 0 + (cd stage && zip -r "../release-assets/$name.zip" build) + + - name: Upload release asset + shell: pwsh + env: + GITHUB_TOKEN: ${{ github.token }} + TAG_NAME: ${{ github.event.release.tag_name || inputs.tag }} + OVERWRITE: ${{ inputs.overwrite || false }} + run: .github/workflows/scripts/upload-release-assets.ps1 release-assets diff --git a/.github/workflows/scripts/upload-release-assets.ps1 b/.github/workflows/scripts/upload-release-assets.ps1 new file mode 100644 index 0000000..f8f8108 --- /dev/null +++ b/.github/workflows/scripts/upload-release-assets.ps1 @@ -0,0 +1,39 @@ +param( + [Parameter(Mandatory = $true)] + [string] $AssetDir +) + +$ErrorActionPreference = "Stop" + +$assets = Get-ChildItem $AssetDir -Filter *.zip +if (-not $assets) { throw "No release assets found in $AssetDir." } + +$headers = @{ + Authorization = "Bearer $env:GITHUB_TOKEN" + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" +} + +$release = Invoke-RestMethod ` + -Headers $headers ` + -Uri "https://api.github.com/repos/$env:GITHUB_REPOSITORY/releases/tags/$env:TAG_NAME" +$uploadUrl = $release.upload_url.Split("{")[0] + +foreach ($asset in $assets) { + $existing = $release.assets | Where-Object { $_.name -eq $asset.Name } + if ($existing) { + if ($env:OVERWRITE -ne "true") { + throw "Release asset already exists: $($asset.Name). Re-run with overwrite=true." + } + Invoke-RestMethod -Method Delete -Headers $headers -Uri $existing.url | Out-Null + } + + $name = [uri]::EscapeDataString($asset.Name) + Invoke-RestMethod ` + -Method Post ` + -Headers $headers ` + -ContentType "application/zip" ` + -InFile $asset.FullName ` + -Uri "$uploadUrl?name=$name" | Out-Null + Write-Host "Uploaded $($asset.Name)" +} diff --git a/.gitignore b/.gitignore index 37fcf6c..33e99e9 100644 --- a/.gitignore +++ b/.gitignore @@ -40,9 +40,10 @@ echoframe/cpp/src/py3.10_wheel_build echoframe/cpp/src/py3.11_wheel_build echoframe/cpp/src/py3.12_wheel_build echoframe/cpp/src/tempbuild -# Build trees from build_scripts/build_echoframe.bat. Unanchored: the script -# writes them at the repository root by default, and EF_BUILD_ROOT can move -# them anywhere. +# Build trees and packaged zips from build_scripts/build_echoframe.bat. +# Unanchored build_CUDA_*: the script writes them at the repository root by +# default, and EF_BUILD_ROOT can move them anywhere. +/binaries/ build_CUDA_*/ echoframe/cpp/src/testing documentation/_build/ diff --git a/README.md b/README.md index 16eccdf..ae60047 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ EchoFrame delivers high‑throughput **Fourier‑domain beamforming** and **Powe * Real‑time performance (> 10 kFPS on RTX 40‑series) * Self‑contained build: CMake ≥ 3.21 (used to build the project) + a recent CUDA toolkit are all you need -**Prebuilt binaries**: +**Prebuilt binaries**: available from [GitHub Releases](https://github.com/BrainEchoLab/EchoFrame/releases/latest). | Versions | CUDA 12.8 | CUDA 12.9 | CUDA 13.1 | |----------|-----------|-----------|-----------| @@ -59,9 +59,9 @@ We offer a range of pre-built binaries for EchoFrame. This means you can simply **⚠️ IMPORTANT:** If you do not have one of the CUDA and Matlab combinations mentioned in the **Prebuilt binaries** table in [Overview](#overview), then you may run into issues using EchoFrame with them! -You can find these binaries in the `binaries` directory. There you can pick from the available builds whichever matches your CUDA and Matlab setup. Each folder in `binaries` contains a compressed `build` folder. +Download the matching `build_CUDA__MATLAB_.zip` from [GitHub Releases](https://github.com/BrainEchoLab/EchoFrame/releases/latest). Each asset contains a compressed `build` folder. -Once you have the EchoFrame repository in your local machine, you can navigate to `EchoFrame\echoframe\cpp\src` and paste the **uncompressed** `build` folder inside `src`. After that, you should be good to go. +Once you have the EchoFrame repository on your local machine, unzip the asset, navigate to `EchoFrame\echoframe\cpp\src`, and paste the **uncompressed** `build` folder inside `src`. After that, you should be good to go. You can then explore [how to use EchoFrame](#using-echoframe). @@ -483,8 +483,6 @@ Each folder has its own README with the details. - **build_scripts/**: developer helper scripts — the Windows build, vcpkg setup, and the build-verification harness. See [build_scripts/README.md](./build_scripts/README.md). -- **binaries/**: prebuilt builds, one zip per CUDA/MATLAB combination. See [Plug and Play](#plug-play). - - **docker/**: the Ubuntu/CUDA development image. - **justfile**: task runner recipes. Stays at the repository root so `just` can find it. diff --git a/binaries/build_CUDA_12.8_MATLAB_2022a/build.zip b/binaries/build_CUDA_12.8_MATLAB_2022a/build.zip deleted file mode 100644 index a28e18d..0000000 Binary files a/binaries/build_CUDA_12.8_MATLAB_2022a/build.zip and /dev/null differ diff --git a/binaries/build_CUDA_12.8_MATLAB_2024a/build.zip b/binaries/build_CUDA_12.8_MATLAB_2024a/build.zip deleted file mode 100644 index 8ea191d..0000000 Binary files a/binaries/build_CUDA_12.8_MATLAB_2024a/build.zip and /dev/null differ diff --git a/binaries/build_CUDA_12.9_MATLAB_2022a/build.zip b/binaries/build_CUDA_12.9_MATLAB_2022a/build.zip deleted file mode 100644 index e1827bb..0000000 Binary files a/binaries/build_CUDA_12.9_MATLAB_2022a/build.zip and /dev/null differ diff --git a/binaries/build_CUDA_12.9_MATLAB_2024a/build.zip b/binaries/build_CUDA_12.9_MATLAB_2024a/build.zip deleted file mode 100644 index 66be631..0000000 Binary files a/binaries/build_CUDA_12.9_MATLAB_2024a/build.zip and /dev/null differ diff --git a/binaries/build_CUDA_13.1_MATLAB_2022a/build.zip b/binaries/build_CUDA_13.1_MATLAB_2022a/build.zip deleted file mode 100644 index 589bcd0..0000000 Binary files a/binaries/build_CUDA_13.1_MATLAB_2022a/build.zip and /dev/null differ diff --git a/binaries/build_CUDA_13.1_MATLAB_2024a/build.zip b/binaries/build_CUDA_13.1_MATLAB_2024a/build.zip deleted file mode 100644 index b196ba1..0000000 Binary files a/binaries/build_CUDA_13.1_MATLAB_2024a/build.zip and /dev/null differ diff --git a/build_scripts/README.md b/build_scripts/README.md index b8294f5..11b7a4f 100644 --- a/build_scripts/README.md +++ b/build_scripts/README.md @@ -45,7 +45,8 @@ set EF_BUILD_ROOT=C:\efb ``` The packaged `binaries\build_\build.zip` is written to the repository -either way. Enabling long paths (`LongPathsEnabled` under +either way. These generated zips are ignored by git; attach them to the matching +GitHub Release instead. Enabling long paths (`LongPathsEnabled` under `HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`, needs a reboot) or using the Ninja generator also works -- see the main README. diff --git a/justfile b/justfile index 92e51bf..967fde5 100644 --- a/justfile +++ b/justfile @@ -7,7 +7,8 @@ jobs := if os() == "windows" { env_var_or_default("NUMBER_OF_PROCESSORS", "4") } make_program := if os() == "windows" { "" } else { `command -v make` } matlab_root := if os() == "windows" { env_var_or_default("Matlab_ROOT_DIR", "C:/Program Files/MATLAB/R2024a") } else { `printf '%s' "${Matlab_ROOT_DIR:-$HOME/MATLAB/R2024a}"` } cuda_host_compiler := if os() == "windows" { "" } else { env_var_or_default("CMAKE_CUDA_HOST_COMPILER", "/usr/bin/g++") } -cuda_architectures := env_var_or_default("CMAKE_CUDA_ARCHITECTURES", "61;75;86;89;90") +default_cuda_architectures := if os() == "windows" { "" } else { `if nvcc --version 2>/dev/null | grep -Eq 'release 1[3-9]\.'; then printf '75;80;86;89;90;100;103;121'; else printf '61;75;86;89;90'; fi` } +cuda_architectures := env_var_or_default("CMAKE_CUDA_ARCHITECTURES", default_cuda_architectures) vcpkg_root := env_var_or_default("VCPKG_ROOT", "C:/vcpkg") vcpkg_triplet := "x64-windows-static" test_img := env_var_or_default("HOME", "") / "ef_test.img"