From f143b124e5f2808fb4e3bc1f77b6dc56a7778481 Mon Sep 17 00:00:00 2001 From: Denis Kudelin <15978569+denis-kudelin@users.noreply.github.com> Date: Sun, 27 Apr 2025 13:45:31 +0300 Subject: [PATCH 1/4] CI: enable multi-RID build and automated NuGet pack/publish (#1) --- .github/workflows/multi-rid-build.yml | 259 ++++++++++++++++++ .github/workflows/nuget.yml | 204 ++++++++++++++ nuget/SimpleCpp.Native.csproj | 37 +++ nuget/buildTransitive/SimpleCpp.Native.props | 26 ++ .../buildTransitive/SimpleCpp.Native.targets | 13 + 5 files changed, 539 insertions(+) create mode 100644 .github/workflows/multi-rid-build.yml create mode 100644 .github/workflows/nuget.yml create mode 100644 nuget/SimpleCpp.Native.csproj create mode 100644 nuget/buildTransitive/SimpleCpp.Native.props create mode 100644 nuget/buildTransitive/SimpleCpp.Native.targets diff --git a/.github/workflows/multi-rid-build.yml b/.github/workflows/multi-rid-build.yml new file mode 100644 index 00000000..a66092d8 --- /dev/null +++ b/.github/workflows/multi-rid-build.yml @@ -0,0 +1,259 @@ +name: multi-rid-build +on: [pull_request, workflow_dispatch] + +env: + PROJECT_NAME: simplecpp + BUILD_TARGETS: "simplecpp" + BUILD_TESTING: OFF + DEBUG_SYMBOLS: true + CMAKE_BUILD_TYPE: Release + CMAKE_CXX_STANDARD: 17 + CMAKE_CXX_STANDARD_REQUIRED: ON + C_FLAGS_COMMON: "-w -fexceptions" + CXX_FLAGS_COMMON: "-w -fexceptions" + CMAKE_EXTRA_ARGS: "" + +jobs: + windows: + runs-on: windows-latest + strategy: + matrix: + arch: + - x64 + - ARM64 + steps: + - uses: actions/checkout@v4 + + - name: prepare-dirs + shell: pwsh + run: New-Item -ItemType Directory -Force -Path artifacts + + - name: install dlfcn-win32 + shell: pwsh + run: | + $triplet = ("${{ matrix.arch }}-windows").ToLower() + vcpkg install dlfcn-win32 --triplet $triplet + + "VCPKG_INSTALLATION_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + "VCPKG_TRIPLET=$triplet" | Out-File -FilePath $env:GITHUB_ENV -Append + "VCPKG_INCLUDE=$env:VCPKG_INSTALLATION_ROOT\installed\$triplet\include" | Out-File -FilePath $env:GITHUB_ENV -Append + "VCPKG_LIB=$env:VCPKG_INSTALLATION_ROOT\installed\$triplet\lib" | Out-File -FilePath $env:GITHUB_ENV -Append + + - id: detect-mingw + shell: pwsh + run: | + if ( + (Test-Path 'C:\msys64\usr\bin\bash.exe') -and + (Get-ChildItem -Recurse 'C:\msys64' -Filter llvm-objcopy.exe | Select-Object -First 1) -and + (Get-ChildItem -Recurse 'C:\msys64' -Filter clang.exe | Select-Object -First 1) + ) { + "full=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + } else { + "full=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + } + + - name: build-mingw + if: steps.detect-mingw.outputs.full == 'true' + run: | + C:\msys64\usr\bin\bash.exe -lc ' + set -euo pipefail + export PATH="$PATH:/c/Program Files/CMake/bin:/c/msys64/usr/bin:/c/msys64/mingw64/bin:/c/msys64/clang-arm64/bin:/c/msys64/ucrt64/bin" + mkdir -p artifacts + cmake -S . -B build/mingw/${{ matrix.arch }} -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_TARGET_TRIPLET=$VCPKG_TRIPLET \ + -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} \ + -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} \ + -DCMAKE_INCLUDE_PATH="$VCPKG_INCLUDE" \ + -DCMAKE_LIBRARY_PATH="$VCPKG_LIB" \ + -DCMAKE_C_FLAGS="$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g") ${{ env.C_FLAGS_COMMON }} -I$VCPKG_INCLUDE" \ + -DCMAKE_CXX_FLAGS="$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g") ${{ env.CXX_FLAGS_COMMON }} -std=c++${{ env.CMAKE_CXX_STANDARD }} -I$VCPKG_INCLUDE" \ + -DCMAKE_EXE_LINKER_FLAGS="-L$VCPKG_LIB -ldlfcn-win32" \ + -DCMAKE_SHARED_LINKER_FLAGS="-L$VCPKG_LIB -ldlfcn-win32" \ + -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DBUILD_TESTING=${{ env.BUILD_TESTING }} \ + ${{ env.CMAKE_EXTRA_ARGS }} + for tgt in ${{ env.BUILD_TARGETS }}; do + cmake --build build/mingw/${{ matrix.arch }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $tgt + done + if [ "${{ env.DEBUG_SYMBOLS }}" = "true" ]; then + OBJCOPY=$(command -v llvm-objcopy || command -v objcopy) + for f in artifacts/*.dll; do + [ -f "$f" ] && "$OBJCOPY" --only-keep-debug "$f" "$f.debug" && "$OBJCOPY" --strip-debug "$f" + done + fi + ' + + - name: check-lib-files + if: steps.detect-mingw.outputs.full == 'false' + shell: pwsh + run: | + Write-Host "Library files in VCPKG_LIB directory:" + Get-ChildItem -Path $env:VCPKG_LIB -Filter "*.lib" | Select-Object -ExpandProperty Name + + - name: configure-vs + if: steps.detect-mingw.outputs.full == 'false' + shell: pwsh + run: | + $out = "$(Get-Location)\artifacts" + $dbg = if ($env:DEBUG_SYMBOLS -eq 'true') { '/Zi' } else { '' } + $linker = if ($env:DEBUG_SYMBOLS -eq 'true') { '/DEBUG' } else { '' } + $defs = '/D_CRT_DECLARE_NONSTDC_NAMES=1 /D_CRT_NONSTDC_NO_DEPRECATE=1 /D_CRT_SECURE_NO_WARNINGS=1 /Dpopen=_popen /Dpclose=_pclose' + + $dlfcnLib = "$env:VCPKG_LIB\dlfcn-win32.lib" + if (-not (Test-Path $dlfcnLib)) { + $dlfcnLib = (Get-ChildItem -Path $env:VCPKG_LIB -Filter "dlfcn*.lib" | Select-Object -First 1).FullName + if (-not $dlfcnLib) { + $dlfcnLib = (Get-ChildItem -Path $env:VCPKG_LIB -Filter "*.lib" | Select-Object -First 1).FullName + } + } + + cmake -S . -B build/${{ matrix.arch }} -G "Visual Studio 17 2022" -A ${{ matrix.arch }} -T ClangCL ` + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" ` + -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TRIPLET" ` + -DCMAKE_PREFIX_PATH="$env:VCPKG_INSTALLATION_ROOT\installed\$env:VCPKG_TRIPLET" ` + -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} ` + -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} ` + -DCMAKE_INCLUDE_PATH="$env:VCPKG_INCLUDE" ` + -DCMAKE_LIBRARY_PATH="$env:VCPKG_LIB" ` + -DCMAKE_C_FLAGS="/w /EHsc $dbg $defs /I$env:VCPKG_INCLUDE" ` + -DCMAKE_CXX_FLAGS="/w /EHsc $dbg /std:c++${{ env.CMAKE_CXX_STANDARD }} $defs /I$env:VCPKG_INCLUDE" ` + -DCMAKE_EXE_LINKER_FLAGS="/LIBPATH:$env:VCPKG_LIB $dlfcnLib $linker" ` + -DCMAKE_SHARED_LINKER_FLAGS="/LIBPATH:$env:VCPKG_LIB $dlfcnLib $linker" ` + -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE="$out" ` + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE="$out" ` + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE="$out" ` + -DBUILD_TESTING=${{ env.BUILD_TESTING }} ` + -DVCPKG_APPLOCAL_DEPS=ON ` + ${{ env.CMAKE_EXTRA_ARGS }} + + - name: build-vs + if: steps.detect-mingw.outputs.full == 'false' + shell: pwsh + run: | + foreach ($t in $env:BUILD_TARGETS.Split()) { + cmake --build build/${{ matrix.arch }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $t + } + + - uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-win-${{ matrix.arch }} + path: artifacts/** + + linux: + runs-on: ubuntu-latest + strategy: + matrix: + triple: + - x86_64-linux-gnu + - aarch64-linux-gnu + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v1 + with: + version: 0.12.0 + use-cache: true + - name: create-ccc + run: | + mkdir -p $HOME/bin + printf '#!/bin/sh\nexec zig cc "$@"\n' > $HOME/bin/zigcc + printf '#!/bin/sh\nexec zig c++ "$@"\n' > $HOME/bin/zigcxx + chmod +x $HOME/bin/zigcc $HOME/bin/zigcxx + echo "$HOME/bin" >> $GITHUB_PATH + - name: configure + run: | + set -euo pipefail + mkdir -p artifacts + DBG=$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g" || echo "-g0") + export CFLAGS="-O2 $DBG -ffunction-sections -fdata-sections ${{ env.C_FLAGS_COMMON }}" + export CXXFLAGS="-O2 $DBG -ffunction-sections -fdata-sections ${{ env.CXX_FLAGS_COMMON }} -std=c++${{ env.CMAKE_CXX_STANDARD }}" + cmake -S . -B build/${{ matrix.triple }} -G Ninja \ + -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \ + -DCMAKE_C_COMPILER=zigcc \ + -DCMAKE_CXX_COMPILER=zigcxx \ + -DCMAKE_C_COMPILER_TARGET=${{ matrix.triple }} \ + -DCMAKE_CXX_COMPILER_TARGET=${{ matrix.triple }} \ + -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} \ + -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} \ + -DCMAKE_EXE_LINKER_FLAGS="-Wl,-z,origin,-rpath,'$$ORIGIN'" \ + -DCMAKE_INSTALL_RPATH="$$ORIGIN" \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ + -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--gc-sections" \ + -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DBUILD_TESTING=${{ env.BUILD_TESTING }} \ + -DDISABLE_CPP03_SYNTAX_CHECK=ON + ${{ env.CMAKE_EXTRA_ARGS }} + - name: build + run: | + for tgt in ${{ env.BUILD_TARGETS }}; do + cmake --build build/${{ matrix.triple }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $tgt + done + - name: split-debug + if: env.DEBUG_SYMBOLS == 'true' + run: | + set -euo pipefail + shopt -s nullglob globstar + OBJCOPY=$(command -v llvm-objcopy-18 || command -v llvm-objcopy || command -v objcopy) + for lib in **/*.so; do + "$OBJCOPY" --only-keep-debug "$lib" "$lib.debug" + "$OBJCOPY" --add-gnu-debuglink="$lib.debug" "$lib" + done + - uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-linux-${{ matrix.triple }} + path: artifacts/** + + macos: + runs-on: macos-latest + strategy: + matrix: + arch: + - x86_64 + - arm64 + steps: + - uses: actions/checkout@v4 + - name: configure + run: | + set -euo pipefail + mkdir -p artifacts + DBG=$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g" || echo "-g0") + cmake -S . -B build/${{ matrix.arch }} -G Ninja \ + -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \ + -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ + -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} \ + -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} \ + -DCMAKE_C_FLAGS="$DBG ${{ env.C_FLAGS_COMMON }}" \ + -DCMAKE_CXX_FLAGS="$DBG ${{ env.CXX_FLAGS_COMMON }} -std=c++${{ env.CMAKE_CXX_STANDARD }}" \ + -DCMAKE_MACOSX_RPATH=ON \ + -DCMAKE_INSTALL_RPATH="@loader_path" \ + -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ + -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath,@loader_path" \ + -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(pwd)/artifacts \ + -DBUILD_TESTING=${{ env.BUILD_TESTING }} \ + ${{ env.CMAKE_EXTRA_ARGS }} + - name: build + run: | + for tgt in ${{ env.BUILD_TARGETS }}; do + cmake --build build/${{ matrix.arch }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $tgt + done + - name: dsym + if: env.DEBUG_SYMBOLS == 'true' + run: | + shopt -s nullglob + for dylib in artifacts/*.dylib; do + dsymutil "$dylib" + done + - uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-osx-${{ matrix.arch }} + path: artifacts/** \ No newline at end of file diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml new file mode 100644 index 00000000..56f0e0e1 --- /dev/null +++ b/.github/workflows/nuget.yml @@ -0,0 +1,204 @@ +# NuGet package publishing workflow +name: nuget +on: + workflow_run: + workflows: [multi-rid-build] + types: [completed] + workflow_dispatch: + +env: + NUGET_SOURCE: https://api.nuget.org/v3/index.json + CSPROJ_NAME: SimpleCpp.Native.csproj + ARTIFACT_PREFIX: simplecpp + +jobs: + pack: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + + steps: + - name: Set project path + run: echo "CSPROJ_PATH=nuget/${{ env.CSPROJ_NAME }}" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: determine artifact run id + id: run_id + env: + GH_TOKEN: ${{ github.token }} + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + RID=$(gh api "/repos/${{ github.repository }}/actions/workflows/multi-rid-build.yml/runs?status=success&per_page=1" --jq '.workflow_runs[0].id') + if [ -z "$RID" ]; then + echo "::error::No successful multi-rid-build runs found" + RID="none" + fi + else + RID=${{ github.event.workflow_run.id }} + fi + echo "run_id=$RID" >> $GITHUB_OUTPUT + echo "Using artifacts from run ID: $RID" + + - uses: actions/download-artifact@v4 + if: steps.run_id.outputs.run_id != 'none' + continue-on-error: true + with: + path: artifacts + run-id: ${{ steps.run_id.outputs.run_id }} + github-token: ${{ github.token }} + + - name: Create artifacts directory if it doesn't exist + run: mkdir -p artifacts + + - name: Validate artifacts + id: validate_artifacts + run: | + ARTIFACT_COUNT=$(find artifacts -type f | wc -l) + if [ "$ARTIFACT_COUNT" -eq 0 ]; then + echo "::error::No artifacts found from the build" + exit 1 + fi + echo "has_artifacts=true" >> $GITHUB_OUTPUT + + - name: Check version change + id: version_check + continue-on-error: true + run: | + if [ ! -f "${{ env.CSPROJ_PATH }}" ]; then + echo "::error::Project file not found at ${{ env.CSPROJ_PATH }}" + echo "is_master=false" >> $GITHUB_OUTPUT + echo "can_publish=false" >> $GITHUB_OUTPUT + echo "version_specified=false" >> $GITHUB_OUTPUT + exit 1 + fi + CURRENT_VERSION=$(grep -o '.*' ${{ env.CSPROJ_PATH }} | sed 's/\(.*\)<\/Version>/\1/') + if [ -z "$CURRENT_VERSION" ]; then + echo "::warning::Version tag not found in csproj file" + echo "can_publish=false" >> $GITHUB_OUTPUT + echo "version_specified=false" >> $GITHUB_OUTPUT + exit 0 + fi + echo "version_specified=true" >> $GITHUB_OUTPUT + echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT + if [[ "${{ github.ref_name }}" == "master" || "${{ github.ref_name }}" == "main" ]]; then + echo "is_master=true" >> $GITHUB_OUTPUT + git fetch origin + if ! git ls-remote --exit-code --quiet origin refs/heads/master || ! git cat-file -e origin/master:${{ env.CSPROJ_PATH }} 2>/dev/null; then + echo "version_changed=true" >> $GITHUB_OUTPUT + echo "can_publish=true" >> $GITHUB_OUTPUT + exit 0 + fi + PREV_VERSION=$(git show origin/master:${{ env.CSPROJ_PATH }} 2>/dev/null | grep -o '.*' | sed 's/\(.*\)<\/Version>/\1/' || echo "") + if [ -z "$PREV_VERSION" ] || [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then + echo "version_changed=true" >> $GITHUB_OUTPUT + else + echo "version_changed=false" >> $GITHUB_OUTPUT + fi + if [ -n "${{ secrets.NUGET_API_KEY }}" ] && [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then + echo "api_key_available=true" >> $GITHUB_OUTPUT + echo "can_publish=true" >> $GITHUB_OUTPUT + else + echo "api_key_available=false" >> $GITHUB_OUTPUT + echo "can_publish=false" >> $GITHUB_OUTPUT + fi + else + echo "is_master=false" >> $GITHUB_OUTPUT + echo "can_publish=false" >> $GITHUB_OUTPUT + fi + + - name: prepare + run: | + set -e + shopt -s nullglob + mkdir -p pkg/runtimes + mkdir -p pkg/buildTransitive + prefix="${{ env.ARTIFACT_PREFIX }}" + map() { + case "$1" in + ${prefix}-linux-aarch64*) echo linux-arm64 ;; + ${prefix}-linux-x86_64*) echo linux-x64 ;; + ${prefix}-osx-arm64*) echo osx-arm64 ;; + ${prefix}-osx-x86_64*) echo osx-x64 ;; + ${prefix}-win-*ARM64*) echo win-arm64 ;; + ${prefix}-win-*x64*) echo win-x64 ;; + *) return 1;; + esac + } + if [ -d "artifacts" ]; then + for dir in artifacts/*; do + [ -d "$dir" ] || continue + rid=$(map "$(basename "$dir")") || continue + native="pkg/runtimes/$rid/native" + mkdir -p "$native" + if [ -d "$dir/Release" ]; then + cp -R "$dir/Release"/. "$native/" + else + cp -R "$dir"/. "$native/" + fi + done + fi + cp ${{ env.CSPROJ_PATH }} pkg/ 2>/dev/null || echo "Project file not found" + cp LICENSE pkg/ 2>/dev/null || echo "LICENSE not found" + cp README.md pkg/ 2>/dev/null || echo "README.md not found" + if [ -d "docs" ]; then + mkdir -p pkg/docs + cp -R docs/* pkg/docs/ 2>/dev/null || echo "docs not found or empty" + fi + if [ -d "nuget/buildTransitive" ]; then + cp nuget/buildTransitive/*.props pkg/buildTransitive/ 2>/dev/null || echo "Props files not found" + cp nuget/buildTransitive/*.targets pkg/buildTransitive/ 2>/dev/null || echo "Targets files not found" + fi + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + + - name: pack + working-directory: pkg + run: | + if [ ! -f ${{ env.CSPROJ_NAME }} ]; then + echo "::error::Project file not copied to package directory" + exit 1 + fi + dotnet pack -c Release -o ../out + + - name: Publish to NuGet + if: >- + steps.version_check.outputs.can_publish == 'true' && + steps.version_check.outputs.is_master == 'true' && + steps.version_check.outputs.version_changed == 'true' && + steps.version_check.outputs.api_key_available == 'true' && + steps.version_check.outputs.version_specified == 'true' + run: | + set -e + if [ -d "out" ] && [ "$(ls -A out 2>/dev/null)" ]; then + for pkg in out/*.nupkg; do + echo "Publishing package: $pkg" + dotnet nuget push "$pkg" --api-key ${{ secrets.NUGET_API_KEY }} --source ${{ env.NUGET_SOURCE }} --skip-duplicate + done + else + echo "::error::No packages found to publish" + exit 1 + fi + + - name: Report publish skip reason + if: steps.version_check.outputs.can_publish != 'true' + run: | + if [ "${{ steps.version_check.outputs.version_specified }}" != "true" ]; then + echo "::warning::Publish skipped: version not specified" + elif [ "${{ steps.version_check.outputs.is_master }}" != "true" ]; then + echo "::warning::Publish skipped: not on master/main branch" + elif [ "${{ steps.version_check.outputs.version_changed }}" != "true" ]; then + echo "::warning::Publish skipped: version unchanged" + elif [ "${{ steps.version_check.outputs.api_key_available }}" != "true" ]; then + echo "::warning::Publish skipped: NuGet API key missing" + else + echo "::warning::Publish skipped: can_publish flag is false" + fi + + - uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_PREFIX }}-nuget-packages + path: out/*.nupkg \ No newline at end of file diff --git a/nuget/SimpleCpp.Native.csproj b/nuget/SimpleCpp.Native.csproj new file mode 100644 index 00000000..1bd2e513 --- /dev/null +++ b/nuget/SimpleCpp.Native.csproj @@ -0,0 +1,37 @@ + + + SimpleCpp.Native + netstandard2.0 + 1.3.2.0 + Cross-platform native binaries for SimpleCpp. + Denis Kudelin + LICENSE + https://github.com/Itexoft/simplecpp + https://github.com/Itexoft/simplecpp + git + README.md + true + false + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nuget/buildTransitive/SimpleCpp.Native.props b/nuget/buildTransitive/SimpleCpp.Native.props new file mode 100644 index 00000000..135caeb7 --- /dev/null +++ b/nuget/buildTransitive/SimpleCpp.Native.props @@ -0,0 +1,26 @@ + + + + <_SimpleCpp_Rid Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier) + + + <_SimpleCpp_Rid Condition="'$(_SimpleCpp_Rid)'=='' and '$(OS)'=='Windows_NT' and '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)'=='X64'">win-x64 + <_SimpleCpp_Rid Condition="'$(_SimpleCpp_Rid)'=='' and '$(OS)'=='Windows_NT' and '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)'=='Arm64'">win-arm64 + + + <_SimpleCpp_Rid Condition="'$(_SimpleCpp_Rid)'=='' and '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' and '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)'=='X64'">linux-x64 + <_SimpleCpp_Rid Condition="'$(_SimpleCpp_Rid)'=='' and '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' and '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)'=='Arm64'">linux-arm64 + + + <_SimpleCpp_Rid Condition="'$(_SimpleCpp_Rid)'=='' and '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' and '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)'=='X64'">osx-x64 + <_SimpleCpp_Rid Condition="'$(_SimpleCpp_Rid)'=='' and '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' and '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)'=='Arm64'">osx-arm64 + + + <_SimpleCpp_NativeRelativeLibPath>runtimes\$(_SimpleCpp_Rid)\native + <_SimpleCpp_NativeLibPath>$(MSBuildThisFileDirectory)\..\$(_SimpleCpp_NativeRelativeLibPath) + + + + + + \ No newline at end of file diff --git a/nuget/buildTransitive/SimpleCpp.Native.targets b/nuget/buildTransitive/SimpleCpp.Native.targets new file mode 100644 index 00000000..8939efbb --- /dev/null +++ b/nuget/buildTransitive/SimpleCpp.Native.targets @@ -0,0 +1,13 @@ + + + <_SimpleCpp_NativeLibs Include="$(_SimpleCpp_NativeLibPath)\*" /> + + + + + + + + + + \ No newline at end of file From 410e716b84438357cee808125e964228f6878b7c Mon Sep 17 00:00:00 2001 From: Denis Kudelin <15978569+denis-kudelin@users.noreply.github.com> Date: Mon, 28 Apr 2025 05:36:20 +0300 Subject: [PATCH 2/4] CI: enable multi-RID build and automated NuGet pack/publish - fix nuget version --- .github/workflows/nuget.yml | 54 +++++++++++++++++++---------------- nuget/SimpleCpp.Native.csproj | 2 +- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 56f0e0e1..8ad039d8 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -64,7 +64,8 @@ jobs: - name: Check version change id: version_check - continue-on-error: true + env: + BRANCH_NAME: ${{ github.ref_name }} run: | if [ ! -f "${{ env.CSPROJ_PATH }}" ]; then echo "::error::Project file not found at ${{ env.CSPROJ_PATH }}" @@ -73,39 +74,42 @@ jobs: echo "version_specified=false" >> $GITHUB_OUTPUT exit 1 fi + CURRENT_VERSION=$(grep -o '.*' ${{ env.CSPROJ_PATH }} | sed 's/\(.*\)<\/Version>/\1/') if [ -z "$CURRENT_VERSION" ]; then - echo "::warning::Version tag not found in csproj file" - echo "can_publish=false" >> $GITHUB_OUTPUT echo "version_specified=false" >> $GITHUB_OUTPUT + echo "can_publish=false" >> $GITHUB_OUTPUT exit 0 fi echo "version_specified=true" >> $GITHUB_OUTPUT echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - if [[ "${{ github.ref_name }}" == "master" || "${{ github.ref_name }}" == "main" ]]; then - echo "is_master=true" >> $GITHUB_OUTPUT - git fetch origin - if ! git ls-remote --exit-code --quiet origin refs/heads/master || ! git cat-file -e origin/master:${{ env.CSPROJ_PATH }} 2>/dev/null; then - echo "version_changed=true" >> $GITHUB_OUTPUT - echo "can_publish=true" >> $GITHUB_OUTPUT - exit 0 - fi - PREV_VERSION=$(git show origin/master:${{ env.CSPROJ_PATH }} 2>/dev/null | grep -o '.*' | sed 's/\(.*\)<\/Version>/\1/' || echo "") - if [ -z "$PREV_VERSION" ] || [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then - echo "version_changed=true" >> $GITHUB_OUTPUT - else - echo "version_changed=false" >> $GITHUB_OUTPUT - fi - if [ -n "${{ secrets.NUGET_API_KEY }}" ] && [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then - echo "api_key_available=true" >> $GITHUB_OUTPUT - echo "can_publish=true" >> $GITHUB_OUTPUT - else - echo "api_key_available=false" >> $GITHUB_OUTPUT - echo "can_publish=false" >> $GITHUB_OUTPUT - fi - else + + if [ "$BRANCH_NAME" != "master" ]; then echo "is_master=false" >> $GITHUB_OUTPUT echo "can_publish=false" >> $GITHUB_OUTPUT + exit 0 + fi + echo "is_master=true" >> $GITHUB_OUTPUT + + git fetch --quiet + if git cat-file -e HEAD~1:${{ env.CSPROJ_PATH }} 2>/dev/null; then + PREV_VERSION=$(git show HEAD~1:${{ env.CSPROJ_PATH }} | grep -o '.*' | sed 's/\(.*\)<\/Version>/\1/') + else + PREV_VERSION="" + fi + + if [ -z "$PREV_VERSION" ] || [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then + echo "version_changed=true" >> $GITHUB_OUTPUT + else + echo "version_changed=false" >> $GITHUB_OUTPUT + fi + + if [ -n "${{ secrets.NUGET_API_KEY }}" ] && [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then + echo "api_key_available=true" >> $GITHUB_OUTPUT + echo "can_publish=true" >> $GITHUB_OUTPUT + else + echo "api_key_available=false" >> $GITHUB_OUTPUT + echo "can_publish=false" >> $GITHUB_OUTPUT fi - name: prepare diff --git a/nuget/SimpleCpp.Native.csproj b/nuget/SimpleCpp.Native.csproj index 1bd2e513..8262be9d 100644 --- a/nuget/SimpleCpp.Native.csproj +++ b/nuget/SimpleCpp.Native.csproj @@ -2,7 +2,7 @@ SimpleCpp.Native netstandard2.0 - 1.3.2.0 + 1.3.2.1 Cross-platform native binaries for SimpleCpp. Denis Kudelin LICENSE From ea398010260cb9b909101b8c49fd753f5826c86e Mon Sep 17 00:00:00 2001 From: Denis Kudelin <15978569+denis-kudelin@users.noreply.github.com> Date: Mon, 28 Apr 2025 09:19:53 +0300 Subject: [PATCH 3/4] build refactoring --- .github/workflows/build-artifacts.yml | 13 ++ .github/workflows/multi-rid-build.yml | 259 -------------------------- .github/workflows/nuget-artifacts.yml | 20 ++ .github/workflows/nuget.yml | 208 --------------------- 4 files changed, 33 insertions(+), 467 deletions(-) create mode 100644 .github/workflows/build-artifacts.yml delete mode 100644 .github/workflows/multi-rid-build.yml create mode 100644 .github/workflows/nuget-artifacts.yml delete mode 100644 .github/workflows/nuget.yml diff --git a/.github/workflows/build-artifacts.yml b/.github/workflows/build-artifacts.yml new file mode 100644 index 00000000..bc05c482 --- /dev/null +++ b/.github/workflows/build-artifacts.yml @@ -0,0 +1,13 @@ +name: build-artifacts + +on: + pull_request: + workflow_dispatch: + +jobs: + build: + uses: Itexoft/DevOpsKit/.github/workflows/cpp-multi-rid-build.yml@master + with: + project_name: simplecpp + build_targets: simplecpp + cmake_cxx_standard: "17" \ No newline at end of file diff --git a/.github/workflows/multi-rid-build.yml b/.github/workflows/multi-rid-build.yml deleted file mode 100644 index a66092d8..00000000 --- a/.github/workflows/multi-rid-build.yml +++ /dev/null @@ -1,259 +0,0 @@ -name: multi-rid-build -on: [pull_request, workflow_dispatch] - -env: - PROJECT_NAME: simplecpp - BUILD_TARGETS: "simplecpp" - BUILD_TESTING: OFF - DEBUG_SYMBOLS: true - CMAKE_BUILD_TYPE: Release - CMAKE_CXX_STANDARD: 17 - CMAKE_CXX_STANDARD_REQUIRED: ON - C_FLAGS_COMMON: "-w -fexceptions" - CXX_FLAGS_COMMON: "-w -fexceptions" - CMAKE_EXTRA_ARGS: "" - -jobs: - windows: - runs-on: windows-latest - strategy: - matrix: - arch: - - x64 - - ARM64 - steps: - - uses: actions/checkout@v4 - - - name: prepare-dirs - shell: pwsh - run: New-Item -ItemType Directory -Force -Path artifacts - - - name: install dlfcn-win32 - shell: pwsh - run: | - $triplet = ("${{ matrix.arch }}-windows").ToLower() - vcpkg install dlfcn-win32 --triplet $triplet - - "VCPKG_INSTALLATION_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - "VCPKG_TRIPLET=$triplet" | Out-File -FilePath $env:GITHUB_ENV -Append - "VCPKG_INCLUDE=$env:VCPKG_INSTALLATION_ROOT\installed\$triplet\include" | Out-File -FilePath $env:GITHUB_ENV -Append - "VCPKG_LIB=$env:VCPKG_INSTALLATION_ROOT\installed\$triplet\lib" | Out-File -FilePath $env:GITHUB_ENV -Append - - - id: detect-mingw - shell: pwsh - run: | - if ( - (Test-Path 'C:\msys64\usr\bin\bash.exe') -and - (Get-ChildItem -Recurse 'C:\msys64' -Filter llvm-objcopy.exe | Select-Object -First 1) -and - (Get-ChildItem -Recurse 'C:\msys64' -Filter clang.exe | Select-Object -First 1) - ) { - "full=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - } else { - "full=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - } - - - name: build-mingw - if: steps.detect-mingw.outputs.full == 'true' - run: | - C:\msys64\usr\bin\bash.exe -lc ' - set -euo pipefail - export PATH="$PATH:/c/Program Files/CMake/bin:/c/msys64/usr/bin:/c/msys64/mingw64/bin:/c/msys64/clang-arm64/bin:/c/msys64/ucrt64/bin" - mkdir -p artifacts - cmake -S . -B build/mingw/${{ matrix.arch }} -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake \ - -DVCPKG_TARGET_TRIPLET=$VCPKG_TRIPLET \ - -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} \ - -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} \ - -DCMAKE_INCLUDE_PATH="$VCPKG_INCLUDE" \ - -DCMAKE_LIBRARY_PATH="$VCPKG_LIB" \ - -DCMAKE_C_FLAGS="$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g") ${{ env.C_FLAGS_COMMON }} -I$VCPKG_INCLUDE" \ - -DCMAKE_CXX_FLAGS="$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g") ${{ env.CXX_FLAGS_COMMON }} -std=c++${{ env.CMAKE_CXX_STANDARD }} -I$VCPKG_INCLUDE" \ - -DCMAKE_EXE_LINKER_FLAGS="-L$VCPKG_LIB -ldlfcn-win32" \ - -DCMAKE_SHARED_LINKER_FLAGS="-L$VCPKG_LIB -ldlfcn-win32" \ - -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DBUILD_TESTING=${{ env.BUILD_TESTING }} \ - ${{ env.CMAKE_EXTRA_ARGS }} - for tgt in ${{ env.BUILD_TARGETS }}; do - cmake --build build/mingw/${{ matrix.arch }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $tgt - done - if [ "${{ env.DEBUG_SYMBOLS }}" = "true" ]; then - OBJCOPY=$(command -v llvm-objcopy || command -v objcopy) - for f in artifacts/*.dll; do - [ -f "$f" ] && "$OBJCOPY" --only-keep-debug "$f" "$f.debug" && "$OBJCOPY" --strip-debug "$f" - done - fi - ' - - - name: check-lib-files - if: steps.detect-mingw.outputs.full == 'false' - shell: pwsh - run: | - Write-Host "Library files in VCPKG_LIB directory:" - Get-ChildItem -Path $env:VCPKG_LIB -Filter "*.lib" | Select-Object -ExpandProperty Name - - - name: configure-vs - if: steps.detect-mingw.outputs.full == 'false' - shell: pwsh - run: | - $out = "$(Get-Location)\artifacts" - $dbg = if ($env:DEBUG_SYMBOLS -eq 'true') { '/Zi' } else { '' } - $linker = if ($env:DEBUG_SYMBOLS -eq 'true') { '/DEBUG' } else { '' } - $defs = '/D_CRT_DECLARE_NONSTDC_NAMES=1 /D_CRT_NONSTDC_NO_DEPRECATE=1 /D_CRT_SECURE_NO_WARNINGS=1 /Dpopen=_popen /Dpclose=_pclose' - - $dlfcnLib = "$env:VCPKG_LIB\dlfcn-win32.lib" - if (-not (Test-Path $dlfcnLib)) { - $dlfcnLib = (Get-ChildItem -Path $env:VCPKG_LIB -Filter "dlfcn*.lib" | Select-Object -First 1).FullName - if (-not $dlfcnLib) { - $dlfcnLib = (Get-ChildItem -Path $env:VCPKG_LIB -Filter "*.lib" | Select-Object -First 1).FullName - } - } - - cmake -S . -B build/${{ matrix.arch }} -G "Visual Studio 17 2022" -A ${{ matrix.arch }} -T ClangCL ` - -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" ` - -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TRIPLET" ` - -DCMAKE_PREFIX_PATH="$env:VCPKG_INSTALLATION_ROOT\installed\$env:VCPKG_TRIPLET" ` - -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} ` - -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} ` - -DCMAKE_INCLUDE_PATH="$env:VCPKG_INCLUDE" ` - -DCMAKE_LIBRARY_PATH="$env:VCPKG_LIB" ` - -DCMAKE_C_FLAGS="/w /EHsc $dbg $defs /I$env:VCPKG_INCLUDE" ` - -DCMAKE_CXX_FLAGS="/w /EHsc $dbg /std:c++${{ env.CMAKE_CXX_STANDARD }} $defs /I$env:VCPKG_INCLUDE" ` - -DCMAKE_EXE_LINKER_FLAGS="/LIBPATH:$env:VCPKG_LIB $dlfcnLib $linker" ` - -DCMAKE_SHARED_LINKER_FLAGS="/LIBPATH:$env:VCPKG_LIB $dlfcnLib $linker" ` - -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE="$out" ` - -DCMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE="$out" ` - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE="$out" ` - -DBUILD_TESTING=${{ env.BUILD_TESTING }} ` - -DVCPKG_APPLOCAL_DEPS=ON ` - ${{ env.CMAKE_EXTRA_ARGS }} - - - name: build-vs - if: steps.detect-mingw.outputs.full == 'false' - shell: pwsh - run: | - foreach ($t in $env:BUILD_TARGETS.Split()) { - cmake --build build/${{ matrix.arch }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $t - } - - - uses: actions/upload-artifact@v4 - with: - name: ${{ env.PROJECT_NAME }}-win-${{ matrix.arch }} - path: artifacts/** - - linux: - runs-on: ubuntu-latest - strategy: - matrix: - triple: - - x86_64-linux-gnu - - aarch64-linux-gnu - steps: - - uses: actions/checkout@v4 - - uses: mlugg/setup-zig@v1 - with: - version: 0.12.0 - use-cache: true - - name: create-ccc - run: | - mkdir -p $HOME/bin - printf '#!/bin/sh\nexec zig cc "$@"\n' > $HOME/bin/zigcc - printf '#!/bin/sh\nexec zig c++ "$@"\n' > $HOME/bin/zigcxx - chmod +x $HOME/bin/zigcc $HOME/bin/zigcxx - echo "$HOME/bin" >> $GITHUB_PATH - - name: configure - run: | - set -euo pipefail - mkdir -p artifacts - DBG=$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g" || echo "-g0") - export CFLAGS="-O2 $DBG -ffunction-sections -fdata-sections ${{ env.C_FLAGS_COMMON }}" - export CXXFLAGS="-O2 $DBG -ffunction-sections -fdata-sections ${{ env.CXX_FLAGS_COMMON }} -std=c++${{ env.CMAKE_CXX_STANDARD }}" - cmake -S . -B build/${{ matrix.triple }} -G Ninja \ - -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \ - -DCMAKE_C_COMPILER=zigcc \ - -DCMAKE_CXX_COMPILER=zigcxx \ - -DCMAKE_C_COMPILER_TARGET=${{ matrix.triple }} \ - -DCMAKE_CXX_COMPILER_TARGET=${{ matrix.triple }} \ - -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} \ - -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} \ - -DCMAKE_EXE_LINKER_FLAGS="-Wl,-z,origin,-rpath,'$$ORIGIN'" \ - -DCMAKE_INSTALL_RPATH="$$ORIGIN" \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--gc-sections" \ - -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DBUILD_TESTING=${{ env.BUILD_TESTING }} \ - -DDISABLE_CPP03_SYNTAX_CHECK=ON - ${{ env.CMAKE_EXTRA_ARGS }} - - name: build - run: | - for tgt in ${{ env.BUILD_TARGETS }}; do - cmake --build build/${{ matrix.triple }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $tgt - done - - name: split-debug - if: env.DEBUG_SYMBOLS == 'true' - run: | - set -euo pipefail - shopt -s nullglob globstar - OBJCOPY=$(command -v llvm-objcopy-18 || command -v llvm-objcopy || command -v objcopy) - for lib in **/*.so; do - "$OBJCOPY" --only-keep-debug "$lib" "$lib.debug" - "$OBJCOPY" --add-gnu-debuglink="$lib.debug" "$lib" - done - - uses: actions/upload-artifact@v4 - with: - name: ${{ env.PROJECT_NAME }}-linux-${{ matrix.triple }} - path: artifacts/** - - macos: - runs-on: macos-latest - strategy: - matrix: - arch: - - x86_64 - - arm64 - steps: - - uses: actions/checkout@v4 - - name: configure - run: | - set -euo pipefail - mkdir -p artifacts - DBG=$([ "${{ env.DEBUG_SYMBOLS }}" = "true" ] && echo "-g" || echo "-g0") - cmake -S . -B build/${{ matrix.arch }} -G Ninja \ - -DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \ - -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ - -DCMAKE_CXX_STANDARD=${{ env.CMAKE_CXX_STANDARD }} \ - -DCMAKE_CXX_STANDARD_REQUIRED=${{ env.CMAKE_CXX_STANDARD_REQUIRED }} \ - -DCMAKE_C_FLAGS="$DBG ${{ env.C_FLAGS_COMMON }}" \ - -DCMAKE_CXX_FLAGS="$DBG ${{ env.CXX_FLAGS_COMMON }} -std=c++${{ env.CMAKE_CXX_STANDARD }}" \ - -DCMAKE_MACOSX_RPATH=ON \ - -DCMAKE_INSTALL_RPATH="@loader_path" \ - -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath,@loader_path" \ - -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(pwd)/artifacts \ - -DBUILD_TESTING=${{ env.BUILD_TESTING }} \ - ${{ env.CMAKE_EXTRA_ARGS }} - - name: build - run: | - for tgt in ${{ env.BUILD_TARGETS }}; do - cmake --build build/${{ matrix.arch }} --parallel --config ${{ env.CMAKE_BUILD_TYPE }} --target $tgt - done - - name: dsym - if: env.DEBUG_SYMBOLS == 'true' - run: | - shopt -s nullglob - for dylib in artifacts/*.dylib; do - dsymutil "$dylib" - done - - uses: actions/upload-artifact@v4 - with: - name: ${{ env.PROJECT_NAME }}-osx-${{ matrix.arch }} - path: artifacts/** \ No newline at end of file diff --git a/.github/workflows/nuget-artifacts.yml b/.github/workflows/nuget-artifacts.yml new file mode 100644 index 00000000..d596f8ad --- /dev/null +++ b/.github/workflows/nuget-artifacts.yml @@ -0,0 +1,20 @@ +name: nuget-artifacts + +on: + workflow_run: + workflows: [build-artifacts] + types: [completed] + workflow_dispatch: + +jobs: + pack: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + uses: Itexoft/DevOpsKit/.github/workflows/nuget-package-publish.yml@master + secrets: inherit + with: + project_name: simplecpp + csproj_path: nuget/SimpleCpp.Native.csproj + build_workflow: build-artifacts + extra_paths: docs + publish_branch: master + publish: false \ No newline at end of file diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml deleted file mode 100644 index 8ad039d8..00000000 --- a/.github/workflows/nuget.yml +++ /dev/null @@ -1,208 +0,0 @@ -# NuGet package publishing workflow -name: nuget -on: - workflow_run: - workflows: [multi-rid-build] - types: [completed] - workflow_dispatch: - -env: - NUGET_SOURCE: https://api.nuget.org/v3/index.json - CSPROJ_NAME: SimpleCpp.Native.csproj - ARTIFACT_PREFIX: simplecpp - -jobs: - pack: - if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-latest - - steps: - - name: Set project path - run: echo "CSPROJ_PATH=nuget/${{ env.CSPROJ_NAME }}" >> $GITHUB_ENV - - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: determine artifact run id - id: run_id - env: - GH_TOKEN: ${{ github.token }} - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - RID=$(gh api "/repos/${{ github.repository }}/actions/workflows/multi-rid-build.yml/runs?status=success&per_page=1" --jq '.workflow_runs[0].id') - if [ -z "$RID" ]; then - echo "::error::No successful multi-rid-build runs found" - RID="none" - fi - else - RID=${{ github.event.workflow_run.id }} - fi - echo "run_id=$RID" >> $GITHUB_OUTPUT - echo "Using artifacts from run ID: $RID" - - - uses: actions/download-artifact@v4 - if: steps.run_id.outputs.run_id != 'none' - continue-on-error: true - with: - path: artifacts - run-id: ${{ steps.run_id.outputs.run_id }} - github-token: ${{ github.token }} - - - name: Create artifacts directory if it doesn't exist - run: mkdir -p artifacts - - - name: Validate artifacts - id: validate_artifacts - run: | - ARTIFACT_COUNT=$(find artifacts -type f | wc -l) - if [ "$ARTIFACT_COUNT" -eq 0 ]; then - echo "::error::No artifacts found from the build" - exit 1 - fi - echo "has_artifacts=true" >> $GITHUB_OUTPUT - - - name: Check version change - id: version_check - env: - BRANCH_NAME: ${{ github.ref_name }} - run: | - if [ ! -f "${{ env.CSPROJ_PATH }}" ]; then - echo "::error::Project file not found at ${{ env.CSPROJ_PATH }}" - echo "is_master=false" >> $GITHUB_OUTPUT - echo "can_publish=false" >> $GITHUB_OUTPUT - echo "version_specified=false" >> $GITHUB_OUTPUT - exit 1 - fi - - CURRENT_VERSION=$(grep -o '.*' ${{ env.CSPROJ_PATH }} | sed 's/\(.*\)<\/Version>/\1/') - if [ -z "$CURRENT_VERSION" ]; then - echo "version_specified=false" >> $GITHUB_OUTPUT - echo "can_publish=false" >> $GITHUB_OUTPUT - exit 0 - fi - echo "version_specified=true" >> $GITHUB_OUTPUT - echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - - if [ "$BRANCH_NAME" != "master" ]; then - echo "is_master=false" >> $GITHUB_OUTPUT - echo "can_publish=false" >> $GITHUB_OUTPUT - exit 0 - fi - echo "is_master=true" >> $GITHUB_OUTPUT - - git fetch --quiet - if git cat-file -e HEAD~1:${{ env.CSPROJ_PATH }} 2>/dev/null; then - PREV_VERSION=$(git show HEAD~1:${{ env.CSPROJ_PATH }} | grep -o '.*' | sed 's/\(.*\)<\/Version>/\1/') - else - PREV_VERSION="" - fi - - if [ -z "$PREV_VERSION" ] || [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then - echo "version_changed=true" >> $GITHUB_OUTPUT - else - echo "version_changed=false" >> $GITHUB_OUTPUT - fi - - if [ -n "${{ secrets.NUGET_API_KEY }}" ] && [ "$PREV_VERSION" != "$CURRENT_VERSION" ]; then - echo "api_key_available=true" >> $GITHUB_OUTPUT - echo "can_publish=true" >> $GITHUB_OUTPUT - else - echo "api_key_available=false" >> $GITHUB_OUTPUT - echo "can_publish=false" >> $GITHUB_OUTPUT - fi - - - name: prepare - run: | - set -e - shopt -s nullglob - mkdir -p pkg/runtimes - mkdir -p pkg/buildTransitive - prefix="${{ env.ARTIFACT_PREFIX }}" - map() { - case "$1" in - ${prefix}-linux-aarch64*) echo linux-arm64 ;; - ${prefix}-linux-x86_64*) echo linux-x64 ;; - ${prefix}-osx-arm64*) echo osx-arm64 ;; - ${prefix}-osx-x86_64*) echo osx-x64 ;; - ${prefix}-win-*ARM64*) echo win-arm64 ;; - ${prefix}-win-*x64*) echo win-x64 ;; - *) return 1;; - esac - } - if [ -d "artifacts" ]; then - for dir in artifacts/*; do - [ -d "$dir" ] || continue - rid=$(map "$(basename "$dir")") || continue - native="pkg/runtimes/$rid/native" - mkdir -p "$native" - if [ -d "$dir/Release" ]; then - cp -R "$dir/Release"/. "$native/" - else - cp -R "$dir"/. "$native/" - fi - done - fi - cp ${{ env.CSPROJ_PATH }} pkg/ 2>/dev/null || echo "Project file not found" - cp LICENSE pkg/ 2>/dev/null || echo "LICENSE not found" - cp README.md pkg/ 2>/dev/null || echo "README.md not found" - if [ -d "docs" ]; then - mkdir -p pkg/docs - cp -R docs/* pkg/docs/ 2>/dev/null || echo "docs not found or empty" - fi - if [ -d "nuget/buildTransitive" ]; then - cp nuget/buildTransitive/*.props pkg/buildTransitive/ 2>/dev/null || echo "Props files not found" - cp nuget/buildTransitive/*.targets pkg/buildTransitive/ 2>/dev/null || echo "Targets files not found" - fi - - - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.x - - - name: pack - working-directory: pkg - run: | - if [ ! -f ${{ env.CSPROJ_NAME }} ]; then - echo "::error::Project file not copied to package directory" - exit 1 - fi - dotnet pack -c Release -o ../out - - - name: Publish to NuGet - if: >- - steps.version_check.outputs.can_publish == 'true' && - steps.version_check.outputs.is_master == 'true' && - steps.version_check.outputs.version_changed == 'true' && - steps.version_check.outputs.api_key_available == 'true' && - steps.version_check.outputs.version_specified == 'true' - run: | - set -e - if [ -d "out" ] && [ "$(ls -A out 2>/dev/null)" ]; then - for pkg in out/*.nupkg; do - echo "Publishing package: $pkg" - dotnet nuget push "$pkg" --api-key ${{ secrets.NUGET_API_KEY }} --source ${{ env.NUGET_SOURCE }} --skip-duplicate - done - else - echo "::error::No packages found to publish" - exit 1 - fi - - - name: Report publish skip reason - if: steps.version_check.outputs.can_publish != 'true' - run: | - if [ "${{ steps.version_check.outputs.version_specified }}" != "true" ]; then - echo "::warning::Publish skipped: version not specified" - elif [ "${{ steps.version_check.outputs.is_master }}" != "true" ]; then - echo "::warning::Publish skipped: not on master/main branch" - elif [ "${{ steps.version_check.outputs.version_changed }}" != "true" ]; then - echo "::warning::Publish skipped: version unchanged" - elif [ "${{ steps.version_check.outputs.api_key_available }}" != "true" ]; then - echo "::warning::Publish skipped: NuGet API key missing" - else - echo "::warning::Publish skipped: can_publish flag is false" - fi - - - uses: actions/upload-artifact@v4 - with: - name: ${{ env.ARTIFACT_PREFIX }}-nuget-packages - path: out/*.nupkg \ No newline at end of file From 7d9aa0cb7e260ad08cd59bf832bb1382be048220 Mon Sep 17 00:00:00 2001 From: Denis Kudelin <15978569+denis-kudelin@users.noreply.github.com> Date: Thu, 1 May 2025 05:49:42 +0300 Subject: [PATCH 4/4] add capi-bundle --- .github/workflows/build-shim.yml | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/build-shim.yml diff --git a/.github/workflows/build-shim.yml b/.github/workflows/build-shim.yml new file mode 100644 index 00000000..3f225f44 --- /dev/null +++ b/.github/workflows/build-shim.yml @@ -0,0 +1,43 @@ +name: capi-bundle +on: + workflow_run: + workflows: ["nuget-artifacts"] + types: [completed] + +jobs: + bundle: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + env: + CAPI_BUNDLE_DIR: capi-bundle + + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + path: _all_artifacts + + - name: assemble + shell: bash + run: | + set -euo pipefail + root="$CAPI_BUNDLE_DIR" + mkdir -p "$root/include" + cp -r include/. "$root/include/" + find _all_artifacts -name 'compile_commands.json' | while read json; do + rid=$(basename "$(dirname "$json")") + cp "$json" "$root/${rid}-compile_commands.json" + jq -r '.[].file' "$json" | grep -E '\.(h|hpp)$' | while read hdr; do + [ -f "$hdr" ] || continue + abs=$(realpath "$hdr") + rel=${abs#$(pwd)/} + mkdir -p "$root/include/$(dirname "$rel")" + cp -f "$abs" "$root/include/$rel" + done + done + + - uses: actions/upload-artifact@v4 + with: + name: capi-bundle + path: ${{ env.CAPI_BUNDLE_DIR }}/** \ No newline at end of file