From 2c427093a2503e3be848a2339dd76a8441055451 Mon Sep 17 00:00:00 2001 From: Cyberhan123 <255542417@qq.com> Date: Thu, 11 Jun 2026 12:35:28 +0800 Subject: [PATCH] Add support for using prebuilt GGML SDK - Introduced option SD_USE_PREBUILT_GGML to allow the use of a prebuilt GGML SDK. - Added CMake variables for configuring the prebuilt GGML SDK, including GGML_ROOT_DIR, GGML_RELEASE_BASE_URL, GGML_RELEASE_TAG, GGML_ASSET_NAME, GGML_DOWNLOAD_URL, and GGML_CACHE_DIR. - Implemented logic in CMake to handle downloading and extracting the prebuilt GGML SDK. - Updated documentation to include instructions for building with a prebuilt GGML SDK. - Added a new CMake script (fetch_prebuilt_ggml.cmake) to manage the fetching and configuration of the prebuilt GGML SDK. - Updated CONTRIBUTING.md to specify that prebuilt GGML SDKs must match the validated revision and ABI-sensitive build options. --- .github/workflows/build.yml | 786 +++++++------------------------- CMakeLists.txt | 49 +- CONTRIBUTING.md | 2 + cmake/fetch_prebuilt_ggml.cmake | 342 ++++++++++++++ docs/build.md | 23 + 5 files changed, 586 insertions(+), 616 deletions(-) create mode 100644 cmake/fetch_prebuilt_ggml.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df4c07d45..02ffefecb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,12 +1,17 @@ name: CI on: - workflow_dispatch: # allows manual triggering + workflow_dispatch: inputs: create_release: description: "Create new release" required: true type: boolean + ggml_release_tag: + description: "Prebuilt ggml SDK release tag" + required: false + default: "v0.14.0" + type: string push: branches: - master @@ -14,6 +19,7 @@ on: paths: [ ".github/workflows/**", + "cmake/**", ".dockerignore", "Dockerfile*", "**/CMakeLists.txt", @@ -31,6 +37,7 @@ on: paths: [ ".github/workflows/**", + "cmake/**", ".dockerignore", "Dockerfile*", "**/CMakeLists.txt", @@ -46,19 +53,20 @@ on: env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + GGML_RELEASE_BASE_URL: https://github.com/seasonjs/ggml.cpp-build/releases/download + GGML_RELEASE_TAG: ${{ github.event.inputs.ggml_release_tag || 'v0.14.0' }} concurrency: group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} cancel-in-progress: true jobs: - ubuntu-latest-cmake: - runs-on: ubuntu-latest + build-linux-x86_64: + runs-on: ubuntu-22.04 steps: - name: Clone - id: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -73,110 +81,64 @@ jobs: version: 10.15.1 - name: Dependencies - id: depends run: | sudo apt-get update - sudo apt-get install build-essential - - - name: Build - id: cmake_build - run: | - mkdir build - cd build - cmake .. -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON - cmake --build . --config Release - - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: prompt/actions-commit-hash@v2 - - - name: Fetch system info - id: system-info - run: | - echo "CPU_ARCH=`uname -m`" >> "$GITHUB_OUTPUT" - echo "OS_NAME=`lsb_release -s -i`" >> "$GITHUB_OUTPUT" - echo "OS_VERSION=`lsb_release -s -r`" >> "$GITHUB_OUTPUT" - echo "OS_TYPE=`uname -s`" >> "$GITHUB_OUTPUT" - - - name: Pack artifacts - id: pack_artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - cp ggml/LICENSE ./build/bin/ggml.txt - cp LICENSE ./build/bin/stable-diffusion.cpp.txt - zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip ./build/bin/* + sudo apt-get install -y build-essential cmake ninja-build zip - - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip - - ubuntu-latest-cmake-vulkan: - runs-on: ubuntu-latest - - steps: - - name: Clone - id: checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 + - name: Cache prebuilt ggml SDK + uses: actions/cache@v4 with: - version: 10.15.1 + path: .cache/ggml + key: ggml-prebuilt-${{ runner.os }}-${{ runner.arch }}-${{ env.GGML_RELEASE_TAG }} - - name: Dependencies - id: depends + - name: Configure CMake run: | - sudo apt-get update - sudo apt-get install build-essential libvulkan-dev glslc spirv-headers + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ + -DCMAKE_INSTALL_RPATH='$ORIGIN' \ + -DSD_BUILD_SHARED_LIBS=ON \ + -DSD_USE_PREBUILT_GGML=ON \ + -DGGML_BACKEND_DL=ON \ + -DGGML_RELEASE_BASE_URL="${GGML_RELEASE_BASE_URL}" \ + -DGGML_RELEASE_TAG="${GGML_RELEASE_TAG}" \ + -DGGML_CACHE_DIR="${GITHUB_WORKSPACE}/.cache/ggml" - name: Build - id: cmake_build - run: | - mkdir build - cd build - cmake .. -DSD_BUILD_SHARED_LIBS=ON -DSD_VULKAN=ON - cmake --build . --config Release - - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: prompt/actions-commit-hash@v2 - - - name: Fetch system info - id: system-info - run: | - echo "CPU_ARCH=`uname -m`" >> "$GITHUB_OUTPUT" - echo "OS_NAME=`lsb_release -s -i`" >> "$GITHUB_OUTPUT" - echo "OS_VERSION=`lsb_release -s -r`" >> "$GITHUB_OUTPUT" - echo "OS_TYPE=`uname -s`" >> "$GITHUB_OUTPUT" + run: cmake --build build --config Release -j "$(nproc)" - name: Pack artifacts - id: pack_artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - cp ggml/LICENSE ./build/bin/ggml.txt - cp LICENSE ./build/bin/stable-diffusion.cpp.txt - zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip ./build/bin/* + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} + shell: bash + run: | + set -euo pipefail + + short="$(git rev-parse --short HEAD)" + safe_branch="${BRANCH_NAME//\//-}" + stage="artifact/sd-linux-x86_64" + archive="artifact/sd-${safe_branch}-${short}-bin-linux-x86_64.zip" + + rm -rf artifact + mkdir -p "$stage" + cp -a build/bin/. "$stage/" + cp ggml/LICENSE "$stage/ggml.txt" + cp LICENSE "$stage/stable-diffusion.cpp.txt" + printf '%s\n' \ + "platform=linux-x86_64" \ + "ggml_release_base_url=${GGML_RELEASE_BASE_URL}" \ + "ggml_release_tag=${GGML_RELEASE_TAG}" \ + "ggml_linkage=prebuilt-sdk" \ + > "$stage/SDK-METADATA.txt" + + (cd artifact && zip -y -r "$(basename "$archive")" sd-linux-x86_64) - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} uses: actions/upload-artifact@v4 with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip + name: sd-linux-x86_64 + path: artifact/*.zip + if-no-files-found: error build-and-push-docker-images: name: Build and push container images @@ -269,13 +231,12 @@ jobs: annotations: ${{ steps.meta.outputs.annotations }} build-args: ${{ matrix.build-args }} - macOS-latest-cmake: - runs-on: macos-latest + build-macos-arm64: + runs-on: macos-14 steps: - name: Clone - id: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -290,188 +251,70 @@ jobs: version: 10.15.1 - name: Dependencies - id: depends - run: | - brew install zip - - - name: Build - id: cmake_build - run: | - sysctl -a - mkdir build - cd build - cmake .. -DGGML_AVX2=ON -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DSD_BUILD_SHARED_LIBS=ON - cmake --build . --config Release - - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: prompt/actions-commit-hash@v2 - - - name: Fetch system info - id: system-info - run: | - echo "CPU_ARCH=`uname -m`" >> "$GITHUB_OUTPUT" - echo "OS_NAME=`sw_vers -productName`" >> "$GITHUB_OUTPUT" - echo "OS_VERSION=`sw_vers -productVersion`" >> "$GITHUB_OUTPUT" - echo "OS_TYPE=`uname -s`" >> "$GITHUB_OUTPUT" - - - name: Pack artifacts - id: pack_artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - cp ggml/LICENSE ./build/bin/ggml.txt - cp LICENSE ./build/bin/stable-diffusion.cpp.txt - zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip ./build/bin/* - - - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip - - windows-latest-cmake: - runs-on: windows-2022 + run: brew install cmake ninja zip - env: - VULKAN_VERSION: 1.4.328.1 - - strategy: - matrix: - include: - - build: "noavx" - defines: "-DGGML_NATIVE=OFF -DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON" - - build: "avx2" - defines: "-DGGML_NATIVE=OFF -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON" - - build: "avx" - defines: "-DGGML_NATIVE=OFF -DGGML_AVX=ON -DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON" - - build: "avx512" - defines: "-DGGML_NATIVE=OFF -DGGML_AVX512=ON -DGGML_AVX=ON -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON" - - build: "cuda12" - defines: "-DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_CUDA_ARCHITECTURES='61;70;75;80;86;89;90;100;120' -DCMAKE_CUDA_FLAGS='-Xcudafe \"--diag_suppress=177\" -Xcudafe \"--diag_suppress=550\"'" - - build: "vulkan" - defines: "-DSD_VULKAN=ON -DSD_BUILD_SHARED_LIBS=ON" - steps: - - name: Clone - id: checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.15.1 - - - name: Install cuda-toolkit - id: cuda-toolkit - if: ${{ matrix.build == 'cuda12' }} - uses: Jimver/cuda-toolkit@v0.2.22 + - name: Cache prebuilt ggml SDK + uses: actions/cache@v4 with: - cuda: "12.8.1" - method: "network" - sub-packages: '["nvcc", "cudart", "cublas", "cublas_dev", "thrust", "visual_studio_integration"]' + path: .cache/ggml + key: ggml-prebuilt-${{ runner.os }}-${{ runner.arch }}-${{ env.GGML_RELEASE_TAG }} - - name: Install Vulkan SDK - id: get_vulkan - if: ${{ matrix.build == 'vulkan' }} + - name: Configure CMake run: | - curl.exe -o $env:RUNNER_TEMP/VulkanSDK-Installer.exe -L "https://sdk.lunarg.com/sdk/download/${env:VULKAN_VERSION}/windows/vulkansdk-windows-X64-${env:VULKAN_VERSION}.exe" - & "$env:RUNNER_TEMP\VulkanSDK-Installer.exe" --accept-licenses --default-answer --confirm-command install - Add-Content $env:GITHUB_ENV "VULKAN_SDK=C:\VulkanSDK\${env:VULKAN_VERSION}" - Add-Content $env:GITHUB_PATH "C:\VulkanSDK\${env:VULKAN_VERSION}\bin" - - - name: Activate MSVC environment - id: msvc_dev_cmd - uses: ilammy/msvc-dev-cmd@v1 + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_OSX_ARCHITECTURES=arm64 \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ + -DCMAKE_INSTALL_RPATH="@loader_path" \ + -DSD_BUILD_SHARED_LIBS=ON \ + -DSD_USE_PREBUILT_GGML=ON \ + -DGGML_BACKEND_DL=ON \ + -DGGML_RELEASE_BASE_URL="${GGML_RELEASE_BASE_URL}" \ + -DGGML_RELEASE_TAG="${GGML_RELEASE_TAG}" \ + -DGGML_CACHE_DIR="${GITHUB_WORKSPACE}/.cache/ggml" - name: Build - id: cmake_build - run: | - mkdir build - cd build - cmake .. -DCMAKE_CXX_FLAGS='/bigobj' -G Ninja -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_BUILD_TYPE=Release ${{ matrix.defines }} - cmake --build . - - - name: Check AVX512F support - id: check_avx512f - if: ${{ matrix.build == 'avx512' }} - continue-on-error: true - run: | - cd build - $vcdir = $(vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath) - $msvc = $(join-path $vcdir $('VC\Tools\MSVC\'+$(gc -raw $(join-path $vcdir 'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt')).Trim())) - $cl = $(join-path $msvc 'bin\Hostx64\x64\cl.exe') - echo 'int main(void){unsigned int a[4];__cpuid(a,7);return !(a[1]&65536);}' >> avx512f.c - & $cl /O2 /GS- /kernel avx512f.c /link /nodefaultlib /entry:main - .\avx512f.exe && echo "AVX512F: YES" && ( echo HAS_AVX512F=1 >> $env:GITHUB_ENV ) || echo "AVX512F: NO" - - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: prompt/actions-commit-hash@v2 + run: cmake --build build --config Release -j "$(sysctl -n hw.ncpu)" - name: Pack artifacts - id: pack_artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - $filePath = ".\build\bin\Release\*" - if (Test-Path $filePath) { - echo "Exists at path $filePath" - Copy-Item ggml/LICENSE .\build\bin\Release\ggml.txt - Copy-Item LICENSE .\build\bin\Release\stable-diffusion.cpp.txt - } elseif (Test-Path ".\build\bin\stable-diffusion.dll") { - $filePath = ".\build\bin\*" - echo "Exists at path $filePath" - Copy-Item ggml/LICENSE .\build\bin\ggml.txt - Copy-Item LICENSE .\build\bin\stable-diffusion.cpp.txt - } else { - ls .\build\bin - throw "Can't find stable-diffusion.dll" - } - 7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip $filePath - - - name: Copy and pack Cuda runtime - id: pack_cuda_runtime - if: ${{ matrix.build == 'cuda12' && (github.event_name == 'push' && github.ref == 'refs/heads/master' || github.event.inputs.create_release == 'true') }} - run: | - echo "Cuda install location: ${{steps.cuda-toolkit.outputs.CUDA_PATH}}" - $dst='.\build\bin\cudart\' - robocopy "${{steps.cuda-toolkit.outputs.CUDA_PATH}}\bin" $dst cudart64_*.dll cublas64_*.dll cublasLt64_*.dll - 7z a cudart-sd-bin-win-cu12-x64.zip $dst\* - - - name: Upload Cuda runtime - if: ${{ matrix.build == 'cuda12' && (github.event_name == 'push' && github.ref == 'refs/heads/master' || github.event.inputs.create_release == 'true') }} - uses: actions/upload-artifact@v4 - with: - name: sd-cudart-sd-bin-win-cu12-x64.zip - path: | - cudart-sd-bin-win-cu12-x64.zip + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} + shell: bash + run: | + set -euo pipefail + + short="$(git rev-parse --short HEAD)" + safe_branch="${BRANCH_NAME//\//-}" + stage="artifact/sd-macos-arm64" + archive="artifact/sd-${safe_branch}-${short}-bin-macos-arm64.zip" + + rm -rf artifact + mkdir -p "$stage" + cp -a build/bin/. "$stage/" + cp ggml/LICENSE "$stage/ggml.txt" + cp LICENSE "$stage/stable-diffusion.cpp.txt" + printf '%s\n' \ + "platform=macos-arm64" \ + "ggml_release_base_url=${GGML_RELEASE_BASE_URL}" \ + "ggml_release_tag=${GGML_RELEASE_TAG}" \ + "ggml_linkage=prebuilt-sdk" \ + > "$stage/SDK-METADATA.txt" + + (cd artifact && zip -y -r "$(basename "$archive")" sd-macos-arm64) - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} uses: actions/upload-artifact@v4 with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip + name: sd-macos-arm64 + path: artifact/*.zip + if-no-files-found: error - windows-latest-rocm: + build-windows-x86_64: runs-on: windows-2022 - env: - ROCM_VERSION: "7.13.0" - GPU_TARGETS: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201" - steps: - - uses: actions/checkout@v3 + - name: Clone + uses: actions/checkout@v4 with: submodules: recursive @@ -485,392 +328,106 @@ jobs: with: version: 10.15.1 - - name: Cache ROCm Installation - id: cache-rocm - uses: actions/cache@v4 - with: - path: C:\TheRock\build - key: rocm-${{ env.ROCM_VERSION }}-gfx1151-${{ runner.os }} + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 - - name: ccache - uses: ggml-org/ccache-action@v1.2.16 + - name: Cache prebuilt ggml SDK + uses: actions/cache@v4 with: - key: windows-latest-rocm-${{ env.ROCM_VERSION }}-x64 - evict-old-files: 1d - - - name: Install ROCm - if: steps.cache-rocm.outputs.cache-hit != 'true' - run: | - $ErrorActionPreference = "Stop" - write-host "Downloading AMD ROCm ${{ env.ROCM_VERSION }} tarball" - Invoke-WebRequest -Uri "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx1151-${{ env.ROCM_VERSION }}.tar.gz" -OutFile "${env:RUNNER_TEMP}\rocm.tar.gz" - write-host "Extracting ROCm tarball" - mkdir C:\TheRock\build -Force - tar -xzf "${env:RUNNER_TEMP}\rocm.tar.gz" -C C:\TheRock\build --strip-components=1 - write-host "Completed ROCm extraction" - - - name: Setup ROCm Environment - run: | - $rocmPath = "C:\TheRock\build" - echo "HIP_PATH=$rocmPath" >> $env:GITHUB_ENV - echo "HIP_DEVICE_LIB_PATH=$rocmPath\lib\llvm\amdgcn\bitcode" >> $env:GITHUB_ENV - echo "HIP_PLATFORM=amd" >> $env:GITHUB_ENV - echo "LLVM_PATH=$rocmPath\lib\llvm" >> $env:GITHUB_ENV - echo "$rocmPath\bin" >> $env:GITHUB_PATH - echo "$rocmPath\lib\llvm\bin" >> $env:GITHUB_PATH + path: .cache/ggml + key: ggml-prebuilt-${{ runner.os }}-${{ runner.arch }}-${{ env.GGML_RELEASE_TAG }} - - name: Build + - name: Configure CMake + shell: pwsh run: | - mkdir build - cd build - cmake .. ` - -G "Unix Makefiles" ` - -DCMAKE_PREFIX_PATH="${env:HIP_PATH}" ` - -DSD_HIPBLAS=ON ` + cmake -S . -B build -A x64 ` + -DCMAKE_CXX_FLAGS="/bigobj" ` -DSD_BUILD_SHARED_LIBS=ON ` - -DGGML_NATIVE=OFF ` - -DCMAKE_C_COMPILER="${env:HIP_PATH}\lib\llvm\bin\clang.exe" ` - -DCMAKE_CXX_COMPILER="${env:HIP_PATH}\lib\llvm\bin\clang++.exe" ` - -DCMAKE_HIP_COMPILER="${env:HIP_PATH}\lib\llvm\bin\clang.exe" ` - -DHIP_PATH="${env:HIP_PATH}" ` - -DCMAKE_BUILD_TYPE=Release ` - -DGPU_TARGETS="${{ env.GPU_TARGETS }}" - cmake --build . --config Release --parallel ${env:NUMBER_OF_PROCESSORS} + -DSD_USE_PREBUILT_GGML=ON ` + -DGGML_BACKEND_DL=ON ` + -DGGML_RELEASE_BASE_URL="${env:GGML_RELEASE_BASE_URL}" ` + -DGGML_RELEASE_TAG="${env:GGML_RELEASE_TAG}" ` + -DGGML_CACHE_DIR="${env:GITHUB_WORKSPACE}\.cache\ggml" - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: pr-mpt/actions-commit-hash@v2 + - name: Build + shell: pwsh + run: cmake --build build --config Release --parallel $env:NUMBER_OF_PROCESSORS - name: Pack artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} + shell: pwsh run: | $ErrorActionPreference = "Stop" - $dst = "build\bin" - $rocmBin = Join-Path "${env:HIP_PATH}" "bin" - $requiredRocmPaths = @( - (Join-Path $rocmBin "rocblas.dll"), - (Join-Path $rocmBin "rocblas\library") - ) - foreach ($path in $requiredRocmPaths) { - if (!(Test-Path $path)) { - throw "Missing ROCm runtime dependency: $path" - } - } - foreach ($pattern in @("rocblas*.dll", "hipblas*.dll", "libhipblas*.dll")) { - Copy-Item -Path (Join-Path $rocmBin $pattern) -Destination $dst -Force -ErrorAction SilentlyContinue + $short = git rev-parse --short HEAD + $safeBranch = $env:BRANCH_NAME -replace '[\\/:"*?<>| ]', '-' + $binDir = "build\bin\Release" + if (!(Test-Path $binDir)) { + $binDir = "build\bin" } - - foreach ($dir in @("rocblas", "hipblaslt")) { - $src = Join-Path $rocmBin $dir - if (Test-Path $src) { - Copy-Item -Path $src -Destination $dst -Recurse -Force - } + if (!(Test-Path $binDir)) { + throw "Could not find build output directory" } - 7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip .\build\bin\* - - - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip + $stage = "artifact\sd-windows-x86_64" + $archive = "artifact\sd-$safeBranch-$short-bin-windows-x86_64.zip" - windows-latest-cmake-hip: - runs-on: windows-2022 - - env: - HIPSDK_INSTALLER_VERSION: "26.Q1" - ROCM_VERSION: "7.1.1" - GPU_TARGETS: "gfx1150;gfx1151;gfx1200;gfx1201;gfx1100;gfx1101;gfx1102;gfx1030;gfx1031;gfx1032" - - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.15.1 - - - name: Cache ROCm Installation - id: cache-rocm - uses: actions/cache@v4 - with: - path: C:\Program Files\AMD\ROCm - key: rocm-${{ env.HIPSDK_INSTALLER_VERSION }}-${{ runner.os }} - - - name: ccache - uses: ggml-org/ccache-action@v1.2.16 - with: - key: windows-latest-cmake-hip-${{ env.HIPSDK_INSTALLER_VERSION }}-x64 - evict-old-files: 1d - - - name: Install ROCm - if: steps.cache-rocm.outputs.cache-hit != 'true' - run: | - $ErrorActionPreference = "Stop" - write-host "Downloading AMD HIP SDK Installer" - Invoke-WebRequest -Uri "https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-${{ env.HIPSDK_INSTALLER_VERSION }}-Win11-For-HIP.exe" -OutFile "${env:RUNNER_TEMP}\rocm-install.exe" - write-host "Installing AMD HIP SDK" - $proc = Start-Process "${env:RUNNER_TEMP}\rocm-install.exe" -ArgumentList '-install' -NoNewWindow -PassThru - $completed = $proc.WaitForExit(600000) - if (-not $completed) { - Write-Error "ROCm installation timed out after 10 minutes. Killing the process" - $proc.Kill() - exit 1 + if (Test-Path "artifact") { + Remove-Item -LiteralPath "artifact" -Recurse -Force } - if ($proc.ExitCode -ne 0) { - Write-Error "ROCm installation failed with exit code $($proc.ExitCode)" - exit 1 - } - write-host "Completed AMD HIP SDK installation" - - - name: Verify ROCm - run: | - # Find and test ROCm installation - $clangPath = Get-ChildItem 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | Select-Object -First 1 - if (-not $clangPath) { - Write-Error "ROCm installation not found" - exit 1 - } - & $clangPath.FullName --version - # Set HIP_PATH environment variable for later steps - echo "HIP_PATH=$(Resolve-Path 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | split-path | split-path)" >> $env:GITHUB_ENV - - - name: Build - run: | - mkdir build - cd build - $env:CMAKE_PREFIX_PATH="${env:HIP_PATH}" - cmake .. ` - -G "Unix Makefiles" ` - -DSD_HIPBLAS=ON ` - -DSD_BUILD_SHARED_LIBS=ON ` - -DGGML_NATIVE=OFF ` - -DCMAKE_C_COMPILER=clang ` - -DCMAKE_CXX_COMPILER=clang++ ` - -DCMAKE_BUILD_TYPE=Release ` - -DGPU_TARGETS="${{ env.GPU_TARGETS }}" - cmake --build . --config Release --parallel ${env:NUMBER_OF_PROCESSORS} - - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: prompt/actions-commit-hash@v2 - - - name: Pack artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - md "build\bin\rocblas\library\" - md "build\bin\hipblaslt\library" - cp "${env:HIP_PATH}\bin\libhipblas.dll" "build\bin\" - cp "${env:HIP_PATH}\bin\libhipblaslt.dll" "build\bin\" - cp "${env:HIP_PATH}\bin\rocblas.dll" "build\bin\" - cp "${env:HIP_PATH}\bin\rocblas\library\*" "build\bin\rocblas\library\" - cp "${env:HIP_PATH}\bin\hipblaslt\library\*" "build\bin\hipblaslt\library\" - 7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip .\build\bin\* - - - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip - - ubuntu-latest-rocm: - runs-on: ubuntu-24.04 - - env: - UBUNTU_VERSION: "24.04" - - strategy: - matrix: - include: - - ROCM_VERSION: "7.2.1" - gpu_targets: "gfx908;gfx90a;gfx942;gfx1030;gfx1031;gfx1032;gfx1100;gfx1101;gfx1102;gfx1151;gfx1150;gfx1200;gfx1201" - build: 'x64' - - ROCM_VERSION: "7.13.0" - gpu_targets: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201" - build: x64 - - steps: - - name: Clone - id: checkout - uses: actions/checkout@v6 - with: - submodules: recursive - - - name: ccache - uses: ggml-org/ccache-action@v1.2.16 - with: - key: ubuntu-rocm-cmake-${{ matrix.ROCM_VERSION }}-${{ matrix.build }} - evict-old-files: 1d - - - name: Dependencies - id: depends - run: | - sudo apt install -y build-essential cmake wget zip ninja-build - - - name: Setup Legacy ROCm - if: matrix.ROCM_VERSION == '7.2.1' - id: legacy_env - run: | - sudo mkdir --parents --mode=0755 /etc/apt/keyrings - wget https://repo.radeon.com/rocm/rocm.gpg.key -O - | \ - gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null - - sudo tee /etc/apt/sources.list.d/rocm.list << EOF - deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/${{ matrix.ROCM_VERSION }} noble main - EOF - - sudo tee /etc/apt/preferences.d/rocm-pin-600 << EOF - Package: * - Pin: release o=repo.radeon.com - Pin-Priority: 600 - EOF - - sudo apt update - sudo apt-get install -y libssl-dev rocm-hip-sdk - - - name: Free disk space - run: | - # Remove preinstalled SDKs and caches not needed for this job - sudo rm -rf /usr/share/dotnet || true - sudo rm -rf /usr/local/lib/android || true - sudo rm -rf /opt/ghc || true - sudo rm -rf /usr/local/.ghcup || true - sudo rm -rf /opt/hostedtoolcache || true - - # Remove old package lists and caches - sudo rm -rf /var/lib/apt/lists/* || true - sudo apt clean - - - name: Setup TheRock - if: matrix.ROCM_VERSION != '7.2.1' - id: therock_env - run: | - wget https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx1151-${{ matrix.ROCM_VERSION }}.tar.gz - mkdir install - tar -xf *.tar.gz -C install - export ROCM_PATH=$(pwd)/install - echo ROCM_PATH=$ROCM_PATH >> $GITHUB_ENV - echo PATH=$PATH:$ROCM_PATH/bin >> $GITHUB_ENV - echo LD_LIBRARY_PATH=$ROCM_PATH/lib:$ROCM_PATH/llvm/lib:$ROCM_PATH/lib/rocprofiler-systems >> $GITHUB_ENV - - # setup-node installs into /opt/hostedtoolcache, which is removed above. - # Keep Node/pnpm setup after disk cleanup so the server frontend can be embedded. - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.15.1 - - - name: Build - id: cmake_build - run: | - mkdir build - cd build - cmake .. -G Ninja \ - -DCMAKE_HIP_COMPILER="$(hipconfig -l)/clang" \ - -DCMAKE_HIP_FLAGS="-mllvm --amdgpu-unroll-threshold-local=600" \ - -DCMAKE_BUILD_TYPE=Release \ - -DSD_HIPBLAS=ON \ - -DHIP_PLATFORM=amd \ - -DGPU_TARGETS="${{ matrix.gpu_targets }}" \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ - -DSD_BUILD_SHARED_LIBS=ON - cmake --build . --config Release - - - name: Get commit hash - id: commit - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - uses: prompt/actions-commit-hash@v2 - - - name: Prepare artifacts - id: prepare_artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - # Copy licenses - cp ggml/LICENSE ./build/bin/ggml.txt - cp LICENSE ./build/bin/stable-diffusion.cpp.txt - - - name: Fetch system info - id: system-info - run: | - echo "CPU_ARCH=`uname -m`" >> "$GITHUB_OUTPUT" - echo "OS_NAME=`lsb_release -s -i`" >> "$GITHUB_OUTPUT" - echo "OS_VERSION=`lsb_release -s -r`" >> "$GITHUB_OUTPUT" - echo "OS_TYPE=`uname -s`" >> "$GITHUB_OUTPUT" - - - name: Pack artifacts - id: pack_artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} - run: | - cp ggml/LICENSE ./build/bin/ggml.txt - cp LICENSE ./build/bin/stable-diffusion.cpp.txt - zip -y -r sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip ./build/bin + New-Item -ItemType Directory -Force -Path $stage | Out-Null + Copy-Item -Path "$binDir\*" -Destination $stage -Recurse -Force + Copy-Item -Path "ggml\LICENSE" -Destination "$stage\ggml.txt" -Force + Copy-Item -Path "LICENSE" -Destination "$stage\stable-diffusion.cpp.txt" -Force + + $metadata = @( + "platform=windows-x86_64", + "ggml_release_base_url=$env:GGML_RELEASE_BASE_URL", + "ggml_release_tag=$env:GGML_RELEASE_TAG", + "ggml_linkage=prebuilt-sdk" + ) -join "`n" + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + [System.IO.File]::WriteAllText((Join-Path (Resolve-Path $stage) "SDK-METADATA.txt"), $metadata, $utf8NoBom) + + Compress-Archive -Path $stage -DestinationPath $archive -Force - name: Upload artifacts - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} uses: actions/upload-artifact@v4 with: - name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip - path: | - sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip + name: sd-windows-x86_64 + path: artifact/*.zip + if-no-files-found: error release: - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} + if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event.inputs.create_release == 'true' }} runs-on: ubuntu-latest needs: - - ubuntu-latest-cmake - - ubuntu-latest-cmake-vulkan - - ubuntu-latest-rocm + - build-linux-x86_64 - build-and-push-docker-images - - macOS-latest-cmake - - windows-latest-cmake - - windows-latest-cmake-hip - - windows-latest-rocm + - build-macos-arm64 + - build-windows-x86_64 steps: - name: Clone - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Download artifacts - id: download-artifact uses: actions/download-artifact@v4 with: path: ./artifact pattern: sd-* merge-multiple: true - - name: Get commit count - id: commit_count + - name: Get release version + id: version run: | - echo "count=$(git rev-list --count HEAD)" >> $GITHUB_OUTPUT - - - name: Get commit hash - id: commit - uses: prompt/actions-commit-hash@v2 + echo "count=$(git rev-list --count HEAD)" >> "$GITHUB_OUTPUT" + echo "short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" - name: Create release id: create_release @@ -879,25 +436,24 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ format('{0}-{1}-{2}', env.BRANCH_NAME, steps.commit_count.outputs.count, steps.commit.outputs.short) }} + tag_name: ${{ format('{0}-{1}-{2}', env.BRANCH_NAME, steps.version.outputs.count, steps.version.outputs.short) }} - name: Upload release - id: upload_release if: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'master' }} uses: actions/github-script@v3 with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{ secrets.GITHUB_TOKEN }} script: | const path = require('path'); const fs = require('fs'); const release_id = '${{ steps.create_release.outputs.id }}'; - for (let file of await fs.readdirSync('./artifact')) { + for (const file of await fs.readdirSync('./artifact')) { if (path.extname(file) === '.zip') { console.log('uploadReleaseAsset', file); await github.repos.uploadReleaseAsset({ owner: context.repo.owner, repo: context.repo.repo, - release_id: release_id, + release_id, name: file, data: await fs.readFileSync(`./artifact/${file}`) }); diff --git a/CMakeLists.txt b/CMakeLists.txt index 150ecde1c..a31af90c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,8 +94,20 @@ option(SD_MUSA "sd: musa backend" OFF) option(SD_BUILD_SHARED_LIBS "sd: build shared libs" OFF) option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" OFF) option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF) +option(SD_USE_PREBUILT_GGML "sd: use a downloaded or unpacked prebuilt GGML SDK" OFF) #option(SD_BUILD_SERVER "sd: build server example" ON) +set(GGML_ROOT_DIR "" CACHE PATH "Path to an unpacked prebuilt GGML SDK, used when SD_USE_PREBUILT_GGML=ON") +set(GGML_RELEASE_BASE_URL "" CACHE STRING "Base URL for prebuilt GGML SDK release assets, for example https://github.com/seasonjs/ggml.cpp-build/releases/download") +set(GGML_RELEASE_TAG "" CACHE STRING "Release tag containing the prebuilt GGML SDK archive, used with GGML_RELEASE_BASE_URL") +set(GGML_ASSET_NAME "" CACHE STRING "Prebuilt GGML SDK archive name. If empty, CMake derives it from the host platform") +set(GGML_DOWNLOAD_URL "" CACHE STRING "Direct URL to a prebuilt GGML SDK archive") +set(GGML_CACHE_DIR "" CACHE PATH "Directory used to cache downloaded prebuilt GGML SDK archives") + +if(SD_USE_SYSTEM_GGML AND SD_USE_PREBUILT_GGML) + message(FATAL_ERROR "SD_USE_SYSTEM_GGML and SD_USE_PREBUILT_GGML are mutually exclusive.") +endif() + set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED true) @@ -312,7 +324,37 @@ endif() # deps # Only add ggml if it hasn't been added yet if (NOT TARGET ggml) - if (SD_USE_SYSTEM_GGML) + if (SD_USE_PREBUILT_GGML) + include("${CMAKE_CURRENT_LIST_DIR}/cmake/fetch_prebuilt_ggml.cmake") + + set(_ggml_prebuilt_args OUT_ROOT_DIR GGML_PREBUILT_ROOT_DIR) + if (GGML_ROOT_DIR) + list(APPEND _ggml_prebuilt_args ROOT_DIR "${GGML_ROOT_DIR}") + endif() + if (GGML_RELEASE_BASE_URL) + list(APPEND _ggml_prebuilt_args RELEASE_BASE_URL "${GGML_RELEASE_BASE_URL}") + endif() + if (GGML_RELEASE_TAG) + list(APPEND _ggml_prebuilt_args RELEASE_TAG "${GGML_RELEASE_TAG}") + endif() + if (GGML_ASSET_NAME) + list(APPEND _ggml_prebuilt_args ASSET_NAME "${GGML_ASSET_NAME}") + endif() + if (GGML_DOWNLOAD_URL) + list(APPEND _ggml_prebuilt_args DOWNLOAD_URL "${GGML_DOWNLOAD_URL}") + endif() + if (GGML_CACHE_DIR) + list(APPEND _ggml_prebuilt_args CACHE_DIR "${GGML_CACHE_DIR}") + endif() + + ggml_import_prebuilt(${_ggml_prebuilt_args}) + if (NOT TARGET ggml AND TARGET ggml::ggml) + add_library(ggml ALIAS ggml::ggml) + endif() + if (NOT TARGET ggml) + message(FATAL_ERROR "Prebuilt GGML package did not provide a ggml or ggml::ggml CMake target.") + endif() + elseif (SD_USE_SYSTEM_GGML) find_package(ggml REQUIRED) if (NOT ggml_FOUND) message(FATAL_ERROR "System-installed GGML library not found.") @@ -331,6 +373,11 @@ target_include_directories(${SD_LIB} PRIVATE src/core) target_include_directories(${SD_LIB} PUBLIC . thirdparty) target_compile_features(${SD_LIB} PUBLIC c_std_11 cxx_std_17) +if (SD_USE_PREBUILT_GGML AND COMMAND ggml_copy_runtime_binaries AND GGML_PREBUILT_ROOT_DIR) + ggml_copy_runtime_binaries(${SD_LIB} + ROOT_DIR "${GGML_PREBUILT_ROOT_DIR}") +endif() + if (SD_BUILD_EXAMPLES) add_subdirectory(examples) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f94e39049..ec5a975fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,6 +60,8 @@ Please do not submit AI-generated code that you do not understand, and do not in Do not submit PRs that update `ggml`. `ggml` updates are performed only after local validation by the maintainer. +Prebuilt `ggml` SDKs used with `SD_USE_PREBUILT_GGML` must match the validated `ggml` revision and ABI-sensitive build options used by this repository, including `GGML_MAX_NAME=128`. + Other third-party dependencies are not updated unless necessary. If you want to update a dependency, please open an issue first instead of submitting a direct PR. ## Security & Configuration diff --git a/cmake/fetch_prebuilt_ggml.cmake b/cmake/fetch_prebuilt_ggml.cmake new file mode 100644 index 000000000..2f8e3366f --- /dev/null +++ b/cmake/fetch_prebuilt_ggml.cmake @@ -0,0 +1,342 @@ +include_guard(GLOBAL) + +include(CMakeParseArguments) + +function(_ggml_prebuilt_platform out_platform out_archive_ext) + string(TOLOWER "${CMAKE_SYSTEM_NAME}" _ggml_system_name) + string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _ggml_system_processor) + + if (_ggml_system_name STREQUAL "linux" AND _ggml_system_processor MATCHES "^(x86_64|amd64)$") + set(_ggml_platform "linux-x86_64") + set(_ggml_archive_ext ".tar.gz") + elseif (_ggml_system_name STREQUAL "darwin" AND _ggml_system_processor MATCHES "^(arm64|aarch64)$") + set(_ggml_platform "macos-arm64") + set(_ggml_archive_ext ".tar.gz") + elseif (WIN32 AND _ggml_system_processor MATCHES "^(x86_64|amd64)$") + set(_ggml_platform "windows-x86_64") + set(_ggml_archive_ext ".zip") + else() + message(FATAL_ERROR + "Unsupported prebuilt ggml package for ${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}. " + "Provide ROOT_DIR explicitly or extend cmake/fetch_prebuilt_ggml.cmake.") + endif() + + set(${out_platform} "${_ggml_platform}" PARENT_SCOPE) + set(${out_archive_ext} "${_ggml_archive_ext}" PARENT_SCOPE) +endfunction() + +function(_ggml_locate_package_root out_root_dir search_root) + if (NOT EXISTS "${search_root}") + set(${out_root_dir} "" PARENT_SCOPE) + return() + endif() + + set(_ggml_package_root "") + set(_ggml_search_roots "${search_root}") + + # Some archives unpack into an extra top-level directory such as + # ggml-windows-x86_64/..., even when the archive file itself includes a tag. + file(GLOB _ggml_child_entries + LIST_DIRECTORIES true + RELATIVE "${search_root}" + "${search_root}/*") + set(_ggml_child_dirs "") + foreach(_ggml_child_entry IN LISTS _ggml_child_entries) + if (IS_DIRECTORY "${search_root}/${_ggml_child_entry}") + list(APPEND _ggml_child_dirs "${search_root}/${_ggml_child_entry}") + endif() + endforeach() + list(LENGTH _ggml_child_dirs _ggml_child_dir_count) + if (_ggml_child_dir_count EQUAL 1) + list(APPEND _ggml_search_roots ${_ggml_child_dirs}) + endif() + + foreach(_ggml_search_dir IN LISTS _ggml_search_roots) + file(GLOB_RECURSE _ggml_config_candidates + LIST_DIRECTORIES false + "${_ggml_search_dir}/ggml-config.cmake") + + foreach(_ggml_config IN LISTS _ggml_config_candidates) + if (_ggml_config MATCHES [[[/\\]lib[/\\]cmake[/\\]ggml[/\\]ggml-config\.cmake$]]) + get_filename_component(_ggml_config_dir "${_ggml_config}" DIRECTORY) + get_filename_component(_ggml_package_root "${_ggml_config_dir}/../../.." ABSOLUTE) + break() + endif() + endforeach() + + if (_ggml_package_root) + break() + endif() + endforeach() + + set(${out_root_dir} "${_ggml_package_root}" PARENT_SCOPE) +endfunction() + +function(_ggml_archive_stem out_stem archive_name) + get_filename_component(_ggml_archive_name "${archive_name}" NAME) + string(REGEX REPLACE "(\\.tar\\.gz|\\.zip)$" "" _ggml_archive_stem "${_ggml_archive_name}") + set(${out_stem} "${_ggml_archive_stem}" PARENT_SCOPE) +endfunction() + +function(_ggml_download_archive download_url archive_path) + set(_ggml_temp_archive_path "${archive_path}.tmp") + file(REMOVE "${_ggml_temp_archive_path}") + + message(STATUS "Downloading prebuilt ggml package from ${download_url}") + file(DOWNLOAD + "${download_url}" + "${_ggml_temp_archive_path}" + SHOW_PROGRESS + STATUS _ggml_download_status + LOG _ggml_download_log + TLS_VERIFY ON) + + list(GET _ggml_download_status 0 _ggml_download_code) + list(GET _ggml_download_status 1 _ggml_download_message) + if (NOT _ggml_download_code EQUAL 0) + file(REMOVE "${_ggml_temp_archive_path}") + message(FATAL_ERROR + "Failed to download ${download_url}: ${_ggml_download_message}\n${_ggml_download_log}") + endif() + + if (NOT EXISTS "${_ggml_temp_archive_path}") + message(FATAL_ERROR + "Download completed but did not create ${_ggml_temp_archive_path}.") + endif() + + file(SIZE "${_ggml_temp_archive_path}" _ggml_downloaded_size) + if (_ggml_downloaded_size EQUAL 0) + file(REMOVE "${_ggml_temp_archive_path}") + message(FATAL_ERROR + "Downloaded ${download_url} but received an empty archive. " + "Check GGML_RELEASE_TAG/GGML_ASSET_NAME/GGML_DOWNLOAD_URL and retry.") + endif() + + file(REMOVE "${archive_path}") + file(RENAME "${_ggml_temp_archive_path}" "${archive_path}") +endfunction() + +function(ggml_import_prebuilt) + set(options) + set(one_value_args + ROOT_DIR + OUT_ROOT_DIR + RELEASE_BASE_URL + RELEASE_TAG + VERSION + ASSET_NAME + DOWNLOAD_URL + CACHE_DIR) + cmake_parse_arguments(GGML "${options}" "${one_value_args}" "" ${ARGN}) + + if (GGML_RELEASE_TAG AND GGML_VERSION) + message(FATAL_ERROR "ggml_import_prebuilt accepts RELEASE_TAG or VERSION, but not both.") + endif() + + if (EXISTS "${GGML_ROOT_DIR}") + get_filename_component(_ggml_input_root "${GGML_ROOT_DIR}" ABSOLUTE) + _ggml_locate_package_root(_ggml_package_root "${_ggml_input_root}") + + if (NOT _ggml_package_root) + message(FATAL_ERROR + "Could not locate lib/cmake/ggml/ggml-config.cmake under ROOT_DIR=${_ggml_input_root}.") + endif() + else() + _ggml_prebuilt_platform(_ggml_platform _ggml_archive_ext) + + if (GGML_DOWNLOAD_URL) + set(_ggml_download_url "${GGML_DOWNLOAD_URL}") + else() + if (NOT GGML_RELEASE_BASE_URL) + message(FATAL_ERROR + "ggml_import_prebuilt requires ROOT_DIR, DOWNLOAD_URL, or RELEASE_BASE_URL + RELEASE_TAG.") + endif() + + if (GGML_RELEASE_TAG) + set(_ggml_release_tag "${GGML_RELEASE_TAG}") + elseif (GGML_VERSION) + set(_ggml_release_tag "${GGML_VERSION}") + else() + message(FATAL_ERROR + "ggml_import_prebuilt requires RELEASE_TAG or VERSION when RELEASE_BASE_URL is used.") + endif() + + string(REGEX REPLACE "/$" "" _ggml_release_base_url "${GGML_RELEASE_BASE_URL}") + endif() + + if (GGML_ASSET_NAME) + set(_ggml_asset_name "${GGML_ASSET_NAME}") + elseif (GGML_DOWNLOAD_URL) + string(REGEX REPLACE "[?#].*$" "" _ggml_download_url_path "${_ggml_download_url}") + get_filename_component(_ggml_asset_name "${_ggml_download_url_path}" NAME) + elseif (DEFINED _ggml_release_tag) + set(_ggml_asset_name "ggml-${_ggml_release_tag}-${_ggml_platform}${_ggml_archive_ext}") + else() + set(_ggml_asset_name "ggml-${_ggml_platform}${_ggml_archive_ext}") + endif() + + if (NOT GGML_DOWNLOAD_URL) + set(_ggml_download_url "${_ggml_release_base_url}/${_ggml_release_tag}/${_ggml_asset_name}") + endif() + + if (GGML_CACHE_DIR) + get_filename_component(_ggml_cache_dir "${GGML_CACHE_DIR}" ABSOLUTE) + else() + set(_ggml_cache_dir "${CMAKE_BINARY_DIR}/_deps/ggml-prebuilt") + endif() + + set(_ggml_archive_dir "${_ggml_cache_dir}/archives") + set(_ggml_archive_path "${_ggml_archive_dir}/${_ggml_asset_name}") + _ggml_archive_stem(_ggml_extract_dir_name "${_ggml_asset_name}") + set(_ggml_extract_dir "${_ggml_cache_dir}/packages/${_ggml_extract_dir_name}") + + file(MAKE_DIRECTORY "${_ggml_archive_dir}") + + set(_ggml_use_cached_archive FALSE) + if (EXISTS "${_ggml_archive_path}") + file(SIZE "${_ggml_archive_path}" _ggml_archive_size) + if (_ggml_archive_size GREATER 0) + set(_ggml_use_cached_archive TRUE) + message(STATUS "Using cached prebuilt ggml archive: ${_ggml_archive_path}") + else() + message(STATUS "Removing empty cached prebuilt ggml archive: ${_ggml_archive_path}") + file(REMOVE "${_ggml_archive_path}") + endif() + endif() + + if (NOT _ggml_use_cached_archive) + _ggml_download_archive("${_ggml_download_url}" "${_ggml_archive_path}") + endif() + + _ggml_locate_package_root(_ggml_package_root "${_ggml_extract_dir}") + if (NOT _ggml_package_root) + foreach(_ggml_extract_attempt RANGE 1 2) + file(REMOVE_RECURSE "${_ggml_extract_dir}") + file(MAKE_DIRECTORY "${_ggml_extract_dir}") + + if (_ggml_extract_attempt EQUAL 1) + message(STATUS "Extracting prebuilt ggml package to ${_ggml_extract_dir}") + else() + message(STATUS "Retrying ggml extraction with a refreshed archive") + endif() + + file(ARCHIVE_EXTRACT + INPUT "${_ggml_archive_path}" + DESTINATION "${_ggml_extract_dir}") + + _ggml_locate_package_root(_ggml_package_root "${_ggml_extract_dir}") + if (_ggml_package_root) + break() + endif() + + if (_ggml_extract_attempt EQUAL 1) + message(STATUS + "Archive ${_ggml_archive_path} did not produce a usable ggml package layout; refreshing cache.") + file(REMOVE "${_ggml_archive_path}") + _ggml_download_archive("${_ggml_download_url}" "${_ggml_archive_path}") + endif() + endforeach() + endif() + + if (NOT _ggml_package_root) + message(FATAL_ERROR + "Archive ${_ggml_archive_path} did not produce lib/cmake/ggml/ggml-config.cmake under " + "${_ggml_extract_dir}. Check GGML_RELEASE_TAG/GGML_ASSET_NAME/GGML_DOWNLOAD_URL.") + endif() + endif() + + if (NOT GGML_OUT_ROOT_DIR) + set(GGML_OUT_ROOT_DIR GGML_ROOT_DIR) + endif() + + find_package(ggml CONFIG REQUIRED + PATHS "${_ggml_package_root}" + NO_DEFAULT_PATH) + + # Some prebuilt ggml packages do not export public include directories or + # the GGML_BACKEND_DL usage requirement on ggml::ggml-base. Patch the + # imported targets locally so downstream targets (for example llama) can + # resolve #include "ggml.h" correctly and inherit the backend loader define. + if (TARGET ggml::ggml) + get_target_property(_ggml_interface_includes ggml::ggml INTERFACE_INCLUDE_DIRECTORIES) + if (NOT _ggml_interface_includes) + set_target_properties(ggml::ggml PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_ggml_package_root}/include") + endif() + endif() + + if (TARGET ggml::ggml-base) + get_target_property(_ggml_base_interface_includes ggml::ggml-base INTERFACE_INCLUDE_DIRECTORIES) + if (NOT _ggml_base_interface_includes) + set_target_properties(ggml::ggml-base PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_ggml_package_root}/include") + endif() + + if (GGML_BACKEND_DL) + get_target_property(_ggml_base_interface_compile_definitions ggml::ggml-base INTERFACE_COMPILE_DEFINITIONS) + if (_ggml_base_interface_compile_definitions MATCHES "-NOTFOUND$") + set(_ggml_base_interface_compile_definitions "") + endif() + + if (NOT _ggml_base_interface_compile_definitions MATCHES "GGML_BACKEND_DL") + list(APPEND _ggml_base_interface_compile_definitions GGML_BACKEND_DL) + set_target_properties(ggml::ggml-base PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "${_ggml_base_interface_compile_definitions}") + endif() + endif() + endif() + + set(GGML_INCLUDE_DIR "${_ggml_package_root}/include" PARENT_SCOPE) + set(GGML_LIB_DIR "${_ggml_package_root}/lib" PARENT_SCOPE) + set(GGML_BIN_DIR "${_ggml_package_root}/bin" PARENT_SCOPE) + set(${GGML_OUT_ROOT_DIR} "${_ggml_package_root}" PARENT_SCOPE) + set(GGML_PREBUILT_ROOT_DIR "${_ggml_package_root}" PARENT_SCOPE) +endfunction() + +function(ggml_copy_runtime_binaries target_name) + set(options) + set(one_value_args ROOT_DIR) + cmake_parse_arguments(GGML "${options}" "${one_value_args}" "" ${ARGN}) + + if (NOT TARGET "${target_name}") + message(FATAL_ERROR "ggml_copy_runtime_binaries expected an existing target, got '${target_name}'.") + endif() + + if (NOT GGML_ROOT_DIR) + message(FATAL_ERROR "ggml_copy_runtime_binaries requires ROOT_DIR.") + endif() + + get_filename_component(_ggml_runtime_root "${GGML_ROOT_DIR}" ABSOLUTE) + set(_ggml_runtime_bin_dir "${_ggml_runtime_root}/bin") + set(_ggml_runtime_lib_dir "${_ggml_runtime_root}/lib") + set(_ggml_runtime_files "") + + if (EXISTS "${_ggml_runtime_lib_dir}") + file(GLOB _ggml_runtime_lib_files + LIST_DIRECTORIES false + "${_ggml_runtime_lib_dir}/*.dll" + "${_ggml_runtime_lib_dir}/*.so" + "${_ggml_runtime_lib_dir}/*.so.*" + "${_ggml_runtime_lib_dir}/*.dylib" + "${_ggml_runtime_lib_dir}/*.dylib.*") + list(APPEND _ggml_runtime_files ${_ggml_runtime_lib_files}) + endif() + + if (EXISTS "${_ggml_runtime_bin_dir}") + add_custom_command(TARGET "${target_name}" POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${_ggml_runtime_bin_dir}" + "$" + COMMENT "Copying ggml runtime binaries to build directory...") + elseif(NOT _ggml_runtime_files) + message(STATUS "No ggml runtime files found under ${_ggml_runtime_root}; skipping copy step.") + endif() + + if (_ggml_runtime_files) + add_custom_command(TARGET "${target_name}" POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${_ggml_runtime_files} + "$" + COMMENT "Copying ggml runtime libraries to build directory...") + endif() +endfunction() diff --git a/docs/build.md b/docs/build.md index d33f9329a..ccadfa414 100644 --- a/docs/build.md +++ b/docs/build.md @@ -46,6 +46,29 @@ cmake .. cmake --build . --config Release ``` +## Build with a prebuilt GGML SDK + +By default, CMake builds the bundled `ggml` submodule from source. To speed up packaging or downstream builds, you can use an unpacked prebuilt GGML SDK instead: + +```shell +mkdir build && cd build +cmake .. -DSD_USE_PREBUILT_GGML=ON -DGGML_ROOT_DIR=/path/to/ggml-sdk +cmake --build . --config Release +``` + +CMake can also download an SDK archive from a release. The archive name is derived from the host platform as `ggml--.tar.gz` on Linux/macOS and `ggml--.zip` on Windows: + +```shell +mkdir build && cd build +cmake .. \ + -DSD_USE_PREBUILT_GGML=ON \ + -DGGML_RELEASE_BASE_URL=https://github.com/seasonjs/ggml.cpp-build/releases/download \ + -DGGML_RELEASE_TAG= +cmake --build . --config Release +``` + +Replace `` with a prebuilt SDK release tag that matches this repository's validated `ggml` revision. The SDK must contain `lib/cmake/ggml/ggml-config.cmake` and must be built from a compatible `ggml` revision with the same ABI-sensitive options expected by `stable-diffusion.cpp`, including `GGML_MAX_NAME=128`. + ## Build with OpenBLAS ```shell