From 96873ccc6bbb0f9b7fe37e36322c064110dba624 Mon Sep 17 00:00:00 2001 From: Branden Akana Date: Tue, 14 Jan 2025 00:30:00 -1000 Subject: [PATCH] Add GitHub Actions --- .github/actions/build/action.yml | 136 +++++++++++++ .github/actions/sign/action.yml | 176 +++++++++++++++++ .github/workflows/builds.yml | 329 +++++++++++++++++++++++++++++++ .gitignore | 2 +- build.py | 21 +- 5 files changed, 662 insertions(+), 2 deletions(-) create mode 100644 .github/actions/build/action.yml create mode 100644 .github/actions/sign/action.yml create mode 100644 .github/workflows/builds.yml diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml new file mode 100644 index 0000000..df45f34 --- /dev/null +++ b/.github/actions/build/action.yml @@ -0,0 +1,136 @@ +name: libm8gd Build +description: Build GDExtension + +inputs: + platform: + required: true + description: Target platform. + arch: + required: true + description: Target architecture. + host: + default: "" + description: libserialport cross-compilation prefix. + float-precision: + default: "single" + description: Float precision (single or double). + build-target-type: + default: "template_debug" + description: Build type (template_debug or template_release). + scons-cache: + default: ".scons-cache/" + description: Scons cache folder name, relative to each scons directory. Must not contain relative path signifiers (. or ..). Must be a transparent path part (empty or 'path/to/directory/', ending in a slash). + em_version: + default: 3.1.62 + description: Emscripten version. + em-cache-directory: + default: emsdk-cache + description: Emscripten cache directory. + gdextension-directory: + default: "" + description: Location of the gdextension project within the repository. Must not contain relative path signifiers (. or ..). Must be a transparent path part (empty or 'path/to/directory/', ending in a slash). + +runs: + using: composite + steps: + # Android only + - name: Android - Set up Java 17 + uses: actions/setup-java@v4 + if: ${{ inputs.platform == 'android' }} + with: + distribution: temurin + java-version: 17 + + - name: Android - Remove existing Android SDK, and set up ENV vars + if: ${{ inputs.platform == 'android' }} + shell: sh + run: | + sudo rm -r /usr/local/lib/android/sdk/** + export ANDROID_HOME=/usr/local/lib/android/sdk + export ANDROID_SDK_ROOT=$ANDROID_HOME + export ANDROID_NDK_VERSION=23.2.8568313 + export ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION} + echo "ANDROID_HOME=$ANDROID_HOME" >> "$GITHUB_ENV" + echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT" >> "$GITHUB_ENV" + echo "ANDROID_NDK_VERSION=$ANDROID_NDK_VERSION" >> "$GITHUB_ENV" + echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT" >> "$GITHUB_ENV" + + - name: Android - Set up Android SDK + if: ${{ inputs.platform == 'android' }} + uses: android-actions/setup-android@v3 + with: + packages: "ndk;${{ env.ANDROID_NDK_VERSION }} cmdline-tools;latest build-tools;34.0.0 platforms;android-34 cmake;3.22.1" + # Linux only + - name: Linux - dependencies + if: ${{ inputs.platform == 'linux' }} + shell: sh + run: | + sudo apt-get update -qq + sudo apt-get install -qqq build-essential pkg-config + # MacOS only + - name: MacOS - Set up Homebrew + if: ${{ inputs.platform == 'macos' }} + uses: Homebrew/actions/setup-homebrew@master + - name: MacOS - Install dependencies + shell: sh + if: ${{ inputs.platform == 'macos' }} + run: brew install autoconf automake libtool + # Web only + - name: Web - Set up Emscripten latest + if: ${{ inputs.platform == 'web' }} + uses: mymindstorm/setup-emsdk@v14 + with: + version: ${{ inputs.em_version }} + actions-cache-folder: ${{ inputs.em-cache-directory }}.${{ inputs.float-precision }}.${{ inputs.build-target-type }} + - name: Web - Verify Emscripten setup + if: ${{ inputs.platform == 'web' }} + shell: sh + run: | + emcc -v + # Windows only + - name: Windows - Install mingw64 + if: ${{ inputs.platform == 'windows' }} + shell: sh + run: | + sudo apt-get install mingw-w64 + sudo apt-get install autotools-dev autoconf libtool-bin + sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix + sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix + # Dependencies of godot + # Use python 3.x release (works cross platform) + - name: Set up Python 3.x + uses: actions/setup-python@v5 + with: + # Semantic version range syntax or exact version of a Python version + python-version: "3.x" + # Optional - x64 or x86 architecture, defaults to x64 + architecture: "x64" + - name: Setup scons + shell: bash + run: | + python -c "import sys; print(sys.version)" + python -m pip install scons + scons --version + # Build + - name: Cache .scons_cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/.scons-cache/ + ${{ github.workspace }}/**/.sconsign.dblite + ${{ github.workspace }}/godot-cpp/gen + key: ${{ inputs.platform }}_${{ inputs.arch }}_${{ inputs.float-precision }}_${{ inputs.build-target-type }}_cache + restore-keys: | + ${{ inputs.platform }}_${{ inputs.arch }}_${{ inputs.float-precision }}_${{ inputs.build-target-type }}_cache + ${{ inputs.platform }}_${{ inputs.arch }}_${{ inputs.float-precision }}_ + ${{ inputs.platform }}_${{ inputs.arch }}_ + ${{ inputs.platform }}_ + # Build gdextension + - name: Build GDExtension Debug Build + shell: bash + env: + SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ + SCONS_CACHE_LIMIT: 4096 + run: | + python build.py --host=${{ inputs.host }} --target=${{ inputs.build-target-type }} --platform=${{ inputs.platform }} --arch=${{ inputs.arch }} --extension-only + working-directory: ${{ inputs.gdextension-directory }} diff --git a/.github/actions/sign/action.yml b/.github/actions/sign/action.yml new file mode 100644 index 0000000..900e165 --- /dev/null +++ b/.github/actions/sign/action.yml @@ -0,0 +1,176 @@ +# This file incorporates work covered by the following copyright and permission notice: +# +# Copyright (c) Mikael Hermansson and Godot Jolt contributors. +# Copyright (c) Dragos Daian. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +name: GDExtension Sign +description: Sign Mac GDExtension + +inputs: + FRAMEWORK_PATH: + description: The path of the artifact. Eg. bin/addons/my_addon/bin/libmy_addon.macos.template_release.universal.framework + required: true + SIGN_FLAGS: + description: The extra flags to use. Eg. --deep + required: false + APPLE_CERT_BASE64: + required: true + description: Base64 file from p12 certificate. + APPLE_CERT_PASSWORD: + required: true + description: Password set when creating p12 certificate from .cer certificate. + APPLE_DEV_PASSWORD: + required: true + description: Apple App-Specific Password. Eg. abcd-abcd-abcd-abcd + APPLE_DEV_ID: + required: true + description: Email used for Apple Id. Eg. email@provider.com + APPLE_DEV_TEAM_ID: + required: true + description: Apple Team Id. Eg. 1ABCD23EFG + APPLE_DEV_APP_ID: + required: true + description: | + Certificate name from get info -> Common name . Eg. Developer ID Application: Common Name (1ABCD23EFG) +outputs: + zip_path: + value: ${{ steps.sign.outputs.path }} + +runs: + using: composite + steps: + - name: Sign + id: sign + shell: pwsh + run: | + #!/usr/bin/env pwsh + + # Copyright (c) Mikael Hermansson and Godot Jolt contributors. + + # Permission is hereby granted, free of charge, to any person obtaining a copy of + # this software and associated documentation files (the "Software"), to deal in + # the Software without restriction, including without limitation the rights to + # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + # the Software, and to permit persons to whom the Software is furnished to do so, + # subject to the following conditions: + + # The above copyright notice and this permission notice shall be included in all + # copies or substantial portions of the Software. + + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + # Taken from https://github.com/godot-jolt/godot-jolt/blob/master/scripts/ci_sign_macos.ps1 + + Set-StrictMode -Version Latest + $ErrorActionPreference = "Stop" + + $CodesignPath = Get-Command codesign | Resolve-Path + + $CertificateBase64 = "${{inputs.APPLE_CERT_BASE64}}" + $CertificatePassword = "${{inputs.APPLE_CERT_PASSWORD}}" + $CertificatePath = [IO.Path]::ChangeExtension((New-TemporaryFile), "p12") + + $Keychain = "ephemeral.keychain" + $KeychainPassword = (New-Guid).ToString().Replace("-", "") + + $DevId = "${{ inputs.APPLE_DEV_ID }}" + $DevTeamId = "${{ inputs.APPLE_DEV_TEAM_ID }}" + $DevPassword = "${{ inputs.APPLE_DEV_PASSWORD }}" + $DeveloperIdApplication = "${{ inputs.APPLE_DEV_APP_ID }}" + + if (!$CertificateBase64) { throw "No certificate provided" } + if (!$CertificatePassword) { throw "No certificate password provided" } + if (!$DevId) { throw "No Apple Developer ID provided" } + if (!$DeveloperIdApplication) { throw "No Apple Developer ID Application provided" } + if (!$DevTeamId) { throw "No Apple Team ID provided" } + if (!$DevPassword) { throw "No Apple Developer password provided" } + + Write-Output "Decoding certificate..." + + $Certificate = [Convert]::FromBase64String($CertificateBase64) + + Write-Output "Writing certificate to disk..." + + [IO.File]::WriteAllBytes($CertificatePath, $Certificate) + + Write-Output "Creating keychain..." + + security create-keychain -p $KeychainPassword $Keychain + + Write-Output "Setting keychain as default..." + + security default-keychain -s $Keychain + + Write-Output "Importing certificate into keychain..." + security import $CertificatePath ` + -k ~/Library/Keychains/$Keychain ` + -P $CertificatePassword ` + -T $CodesignPath + Write-Output "Check identities..." + + security find-identity + + Write-Output "Granting access to keychain..." + + security set-key-partition-list -S "apple-tool:,apple:" -s -k $KeychainPassword $Keychain + + $Framework = "${{ inputs.FRAMEWORK_PATH }}" + $SignFlags = "${{ inputs.SIGN_FLAGS }}" + $Archive = [IO.Path]::ChangeExtension((New-TemporaryFile), "zip") + + Write-Output "Signing '$Framework'..." + + & $CodesignPath --verify --timestamp --verbose "$SignFlags" --sign $DeveloperIdApplication "$Framework" + + Write-Output "Verifying signing..." + + & $CodesignPath --verify -dvvv "$Framework" + + Get-ChildItem -Force -Recurse -Path "$Framework" + + Write-Output "Archiving framework to '$Archive'..." + + ditto -ck -rsrc --sequesterRsrc --keepParent "$Framework" "$Archive" + + Write-Output "Submitting archive for notarization..." + + $output = xcrun notarytool submit "$Archive" ` + --apple-id $DevId ` + --team-id $DevTeamId ` + --password $DevPassword ` + --wait + echo $output + $matches = $output -match '((\d|[a-z])+-(\d|[a-z])+-(\d|[a-z])+-(\d|[a-z])+-(\d|[a-z])+)' + if ($output) { + $id_res = $matches[0].Substring(6) + } + xcrun notarytool log $id_res ` + --apple-id $DevId ` + --team-id $DevTeamId ` + --password $DevPassword ` + developer_log.json + get-content developer_log.json + + echo "path=$Archive" >> $env:GITHUB_OUTPUT diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml new file mode 100644 index 0000000..55103ad --- /dev/null +++ b/.github/workflows/builds.yml @@ -0,0 +1,329 @@ +name: Build GDExtension +on: + workflow_call: + push: + pull_request: + merge_group: + +env: + LIBNAME: example + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + # Debug templates + # - platform: linux + # float-precision: single + # arch: x86_64 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: windows + # float-precision: single + # arch: x86_32 + # target-type: template_debug + # os: windows-latest + + # - platform: windows + # float-precision: single + # arch: x86_64 + # target-type: template_debug + # os: windows-latest + + # - platform: macos + # float-precision: single + # arch: universal + # target-type: template_debug + # os: macos-latest + + # - platform: android + # float-precision: single + # arch: arm64 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: android + # float-precision: single + # arch: arm32 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: android + # float-precision: single + # arch: x86_64 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: android + # float-precision: single + # arch: x86_32 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: ios + # float-precision: single + # arch: arm64 + # target-type: template_debug + # os: macos-latest + + # - platform: web + # float-precision: single + # arch: wasm32 + # target-type: template_debug + # os: ubuntu-20.04 + + # # Release templates + # - platform: linux + # float-precision: single + # arch: x86_64 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: windows + # float-precision: single + # arch: x86_32 + # target-type: template_release + # os: windows-latest + + # - platform: windows + # float-precision: single + # arch: x86_64 + # target-type: template_release + # os: windows-latest + + # - platform: macos + # float-precision: single + # arch: universal + # target-type: template_release + # os: macos-latest + + # - platform: android + # float-precision: single + # arch: arm64 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: android + # float-precision: single + # arch: arm32 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: android + # float-precision: single + # arch: x86_64 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: android + # float-precision: single + # arch: x86_32 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: ios + # float-precision: single + # arch: arm64 + # target-type: template_release + # os: macos-latest + + # - platform: web + # float-precision: single + # arch: wasm32 + # target-type: template_release + # os: ubuntu-20.04 + + # Double precision templates + # Double precision debug templates + - platform: linux + float-precision: double + arch: x86_64 + target-type: template_debug + os: ubuntu-20.04 + + # - platform: windows + # float-precision: double + # arch: x86_32 + # target-type: template_debug + # os: windows-latest + + - platform: windows + float-precision: double + arch: x86_64 + target-type: template_debug + os: ubuntu-22.04 + host: x86_64-w64-mingw32 + + - platform: macos + float-precision: double + arch: universal + target-type: template_debug + os: macos-latest + + # - platform: android + # float-precision: double + # arch: arm64 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: android + # float-precision: double + # arch: arm32 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: android + # float-precision: double + # arch: x86_64 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: android + # float-precision: double + # arch: x86_32 + # target-type: template_debug + # os: ubuntu-20.04 + + # - platform: ios + # float-precision: double + # arch: arm64 + # target-type: template_debug + # os: macos-latest + + # - platform: web + # float-precision: double + # arch: wasm32 + # target-type: template_debug + # os: ubuntu-20.04 + + # Double precision release templates + - platform: linux + float-precision: double + arch: x86_64 + target-type: template_release + os: ubuntu-20.04 + + # - platform: windows + # float-precision: double + # arch: x86_32 + # target-type: template_release + # os: windows-latest + + - platform: windows + float-precision: double + arch: x86_64 + target-type: template_release + os: ubuntu-22.04 + host: x86_64-w64-mingw32 + + - platform: macos + float-precision: double + arch: universal + target-type: template_release + os: macos-latest + + # - platform: android + # float-precision: double + # arch: arm64 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: android + # float-precision: double + # arch: arm32 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: android + # float-precision: double + # arch: x86_64 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: android + # float-precision: double + # arch: x86_32 + # target-type: template_release + # os: ubuntu-20.04 + + # - platform: ios + # float-precision: double + # arch: arm64 + # target-type: template_release + # os: macos-latest + + # - platform: web + # float-precision: double + # arch: wasm32 + # target-type: template_release + # os: ubuntu-20.04 + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + # Lint + #- name: Setup clang-format + # shell: bash + # run: | + # python -m pip install clang-format + #- name: Run clang-format + # shell: bash + # run: | + # clang-format src/** --dry-run --Werror + + # Build + - name: 🔗 GDExtension Debug Build + uses: ./.github/actions/build + with: + platform: ${{ matrix.platform }} + arch: ${{ matrix.arch }} + float-precision: ${{ matrix.float-precision }} + build-target-type: ${{ matrix.target-type }} + host: ${{ matrix.host }} + + # Sign + - name: Mac Sign + # Disable sign if secrets are not set + if: ${{ matrix.platform == 'macos' && env.APPLE_CERT_BASE64 }} + env: + APPLE_CERT_BASE64: ${{ secrets.APPLE_CERT_BASE64 }} + uses: ./.github/actions/sign + with: + FRAMEWORK_PATH: bin/macos/macos.framework + APPLE_CERT_BASE64: ${{ secrets.APPLE_CERT_BASE64 }} + APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }} + APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }} + APPLE_DEV_ID: ${{ secrets.APPLE_DEV_ID }} + APPLE_DEV_TEAM_ID: ${{ secrets.APPLE_DEV_TEAM_ID }} + APPLE_DEV_APP_ID: ${{ secrets.APPLE_DEV_APP_ID }} + + # - name: Windows - Delete compilation files + # if: ${{ matrix.platform == 'windows' }} + # shell: pwsh + # run: | + # Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: libm8gd-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.float-precision }}-${{ matrix.target-type }} + path: | + ${{ github.workspace }}/project/addons/libm8gd/** + + # Merges all the build artifacts together into a single godot-cpp-template artifact. + # If you comment out this step, all the builds will be uploaded individually. + merge: + runs-on: ubuntu-20.04 + needs: build + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: libm8gd + pattern: libm8gd-* + delete-merged: true diff --git a/.gitignore b/.gitignore index b7d0ac5..5345191 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Godot 4+ specific ignores .godot/ -build/ +/build/ # library/object/executable files *.dll diff --git a/build.py b/build.py index 0c68ed4..848f08c 100644 --- a/build.py +++ b/build.py @@ -46,6 +46,12 @@ default="", help="enable building with OSXCross and specify OSXCross SDK", ) +parser.add_argument( + "--host", + type=str, + default="", + help="set C compiler to use to compile libserialport", +) parser.add_argument( "--target", type=str, @@ -139,6 +145,11 @@ def find_godot() -> str | None: _println_info(f"Could not find godot in {file_path}!") +def chmod_x(path: str) -> None: + file_path = Path(path) + file_path.chmod(file_path.stat().st_mode | stat.S_IEXEC) + + def find_command(cmd: str) -> str | None: path: str = which(cmd) if path != None: @@ -246,8 +257,16 @@ def _println_err(text: str) -> None: _println("Compiling libserialport...") try: + chmod_x("thirdparty/libserialport/autogen.sh") run("./autogen.sh", "thirdparty/libserialport") - run("./configure", "thirdparty/libserialport") + chmod_x("thirdparty/libserialport/configure") + if args.host != "": + run( + "./configure --prefix=/usr/{0} --host={0}".format(args.host), + "thirdparty/libserialport", + ) + else: + run("./configure", "thirdparty/libserialport") run("%s CFLAGS=-fPIC" % make_path, "thirdparty/libserialport") except subprocess.CalledProcessError: _println_err("Errors occured while compiling libserialport. Exiting.")