diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 00000000..ddefd44e --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,344 @@ +# Continuous integration tasks running on Cirrus CI. +# + +env: + WXRUBY_TEST_EXCLUDE: 'test_intl:test_media_ctrl' + GITHUB_TOKEN: ENCRYPTED[2ce3914266dec78118dd9706c25d2e52fbd164f86713692efde9b8af5f0159099cb299934a8d6279e524da7a0790e025] + +verify_task: + skip: "changesIncludeOnly('.yardopts','*.md','.circleci/**','.github/**','lib/wx/doc/**','assets/**','lib/wx/version.rb')" + only_if: $CIRRUS_BRANCH =~ 'master' + matrix: + - name: Cirrus CI / Fedora AMD64 Test + container: + image: fedora:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: fedora + - name: Cirrus CI / Fedora ARM64 Test + arm_container: + image: fedora:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: fedora + - name: Cirrus CI / OpenSuSE Leap AMD64 Test + container: + image: opensuse/leap:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: opensuse + - name: Cirrus CI / OpenSuSE Leap ARM64 Test + arm_container: + image: opensuse/leap:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: opensuse + - name: Cirrus CI / Ubuntu AMD64 Test + container: + image: ubuntu:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: ubuntu + - name: Cirrus CI / Ubuntu ARM64 Test + arm_container: + image: ubuntu:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: ubuntu + - name: Cirrus CI / Debian AMD64 Test + container: + image: debian:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: debian + - name: Cirrus CI / Debian ARM64 Test + arm_container: + image: debian:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: debian + + before_script: | + ./tools/scripts/cirrus/setup-$distro.sh + + # Show some information about the system. + uname -a + locale + locale -a + + system_build_script: | + ./tools/scripts/cirrus/setup-$distro-system-ruby.sh + + ./tools/scripts/cirrus/build-wxruby3.sh 2>&1 | tee -a build-wxruby3.log + + system_test_script: + ./tools/scripts/cirrus/test-wxruby3.sh + + system_cleanup_script: | + ./tools/scripts/cirrus/cleanup-wxruby3.sh + + ./tools/scripts/cirrus/setup-$distro-system-ruby.sh remove + + latest_build_script: + ./tools/scripts/cirrus/setup-ruby-install-latest.sh + + ./tools/scripts/cirrus/build-wxruby3.sh --latest 2>&1 | tee -a build-wxruby3.log + + latest_test_script: + ./tools/scripts/cirrus/test-wxruby3.sh + +release_task: + only_if: $CIRRUS_BUILD_SOURCE == 'api' + matrix: + - name: Cirrus CI / Fedora AMD64 Release + container: + image: fedora:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: fedora + - name: Cirrus CI / Fedora ARM64 Release + arm_container: + image: fedora:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: fedora + - name: Cirrus CI / OpenSuSE Leap AMD64 Release + container: + image: opensuse/leap:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: opensuse + - name: Cirrus CI / OpenSuSE Leap ARM64 Release + arm_container: + image: opensuse/leap:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: opensuse + - name: Cirrus CI / Ubuntu AMD64 Release + container: + image: ubuntu:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: ubuntu + - name: Cirrus CI / Ubuntu ARM64 Release + arm_container: + image: ubuntu:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: ubuntu + - name: Cirrus CI / Debian AMD64 Release + container: + image: debian:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: debian + - name: Cirrus CI / Debian ARM64 Release + arm_container: + image: debian:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: debian + - name: Cirrus CI / MacOSX Monterey M1 Release + macos_instance: + image: ghcr.io/cirruslabs/macos-monterey-xcode:latest + env: + osname: darwin + distro: macosx + latest_only: true + - name: Cirrus CI / MacOSX Ventura M1 Release + macos_instance: + image: ghcr.io/cirruslabs/macos-ventura-xcode:latest + env: + osname: darwin + distro: macosx + latest_only: true + - name: Cirrus CI / MacOSX Sonoma M1 Release + macos_instance: + image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest + env: + osname: darwin + distro: macosx + latest_only: true + + before_script: | + ./tools/scripts/cirrus/setup-$distro.sh + + # Show some information about the system. + uname -a + locale + locale -a + + system_build_script: | + ./tools/scripts/cirrus/setup-$distro-system-ruby.sh + + ./tools/scripts/cirrus/build-wxruby3.sh --binpkg 2>&1 | tee -a build-wxruby3.log + + system_cleanup_script: | + ./tools/scripts/cirrus/cleanup-wxruby3.sh + + ./tools/scripts/cirrus/setup-$distro-system-ruby.sh remove + + latest_build_script: + ./tools/scripts/cirrus/setup-ruby-install-latest.sh + + ./tools/scripts/cirrus/build-wxruby3.sh --latest --binpkg 2>&1 | tee -a build-wxruby3.log + + release_script: + + ruby tools/scripts/cirrus/upload-release-pkg.rb + +test_release_task: + only_if: $CIRRUS_BUILD_SOURCE == 'api' + depends_on: + - Cirrus CI / Fedora AMD64 Release + - Cirrus CI / OpenSuSE Leap AMD64 Release + - Cirrus CI / Ubuntu AMD64 Release + - Cirrus CI / Debian AMD64 Release + - Cirrus CI / Fedora ARM64 Release + - Cirrus CI / OpenSuSE Leap ARM64 Release + - Cirrus CI / Ubuntu ARM64 Release + - Cirrus CI / Debian ARM64 Release + - Cirrus CI / MacOSX Monterey M1 Release + - Cirrus CI / MacOSX Ventura M1 Release + - Cirrus CI / MacOSX Sonoma M1 Release + matrix: + - name: Cirrus CI / Fedora AMD64 Release Test + container: + image: fedora:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: fedora + - name: Cirrus CI / Fedora ARM64 Release Test + arm_container: + image: fedora:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: fedora + - name: Cirrus CI / OpenSuSE Leap AMD64 Release Test + container: + image: opensuse/leap:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: opensuse + - name: Cirrus CI / OpenSuSE Leap ARM64 Release Test + arm_container: + image: opensuse/leap:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: opensuse + - name: Cirrus CI / Ubuntu AMD64 Release Test + container: + image: ubuntu:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: ubuntu + - name: Cirrus CI / Ubuntu ARM64 Release Test + arm_container: + image: ubuntu:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: ubuntu + - name: Cirrus CI / Debian AMD64 Release Test + container: + image: debian:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: debian + - name: Cirrus CI / Debian ARM64 Release Test + arm_container: + image: debian:latest + cpu: 4 + memory: 8G + env: + osname: linux + distro: debian + - name: Cirrus CI / MacOSX Monterey M1 Release Test + macos_instance: + image: ghcr.io/cirruslabs/macos-monterey-xcode:latest + env: + osname: darwin + distro: macosx + latest_only: true + - name: Cirrus CI / MacOSX Ventura M1 Release Test + macos_instance: + image: ghcr.io/cirruslabs/macos-ventura-xcode:latest + env: + osname: darwin + distro: macosx + latest_only: true + - name: Cirrus CI / MacOSX Sonoma M1 Release Test + macos_instance: + image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest + env: + osname: darwin + distro: macosx + latest_only: true + + before_script: | + ./tools/scripts/cirrus/setup-$distro.sh test + + # Show some information about the system. + uname -a + locale + locale -a + + system_test_script: | + ./tools/scripts/cirrus/setup-$distro-system-ruby.sh + + ./tools/scripts/cirrus/test-wxruby3-release.sh + + system_cleanup_script: | + ./tools/scripts/cirrus/cleanup-wxruby3.sh + + ./tools/scripts/cirrus/setup-$distro-system-ruby.sh remove + + latest_test_script: + ./tools/scripts/cirrus/setup-ruby-install-latest.sh + + ./tools/scripts/cirrus/test-wxruby3-release.sh diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ff5a8dcc..efbbed8e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -12,6 +12,7 @@ on: - 'lib/wx/doc/**' - 'lib/wx/version.rb' - '**.md' + - '.cirrus.yml' pull_request: branches: - master @@ -26,6 +27,7 @@ on: - '.yardopts' - '.gitignore' - 'LICENSE' + - '.cirrus.yml' workflow_dispatch: concurrency: @@ -60,7 +62,7 @@ jobs: gtk_version: 3 CC: gcc-10 CXX: g++-10 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' configure_flags: @@ -93,7 +95,7 @@ jobs: gtk_version: 3 CC: gcc-12 CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' configure_flags: @@ -115,7 +117,7 @@ jobs: gtk_version: 3 CC: gcc-12 CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' configure_flags: @@ -126,13 +128,13 @@ jobs: gtk_version: 3 CC: gcc-12 CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' configure_flags: use_xvfb: true wxw_type: embed - wxr_type: bingem + wxr_type: binpkg runs-on: ${{ matrix.os }} name: ${{ matrix.os }} wxRuby(${{ matrix.wxr_type }}) ${{ matrix.CXX }} ruby-${{ matrix.ruby }} wxWidgets-${{ matrix.wxWidgets }}(${{ matrix.wxw_type }}) SWIG${{ matrix.swig }} env: @@ -252,37 +254,33 @@ jobs: ruby tools/check_swig_types.rb - name: Build wxRuby3 gem - if: matrix.wxr_type == 'gem' || matrix.wxr_type == 'bingem' + if: matrix.wxr_type == 'gem' || matrix.wxr_type == 'binpkg' run: | - if [ "${{ matrix.wxr_type }}" == "gem" ]; then - bundle exec rake gem - else - BUILD_CMD="bundle exec rake bingem" - /bin/bash -o pipefail -c "xvfb-run -a -s '-screen 0 1600x1200x24' $BUILD_CMD 2>&1 | tee -a gembuild.out" || rc=$? - if [ -n "$rc" ]; then - if fgrep -q '(core dumped)' gembuild.out; then - echo '*** Test crashed, trying to get more information ***' - gdb --quiet --core=core -ex 'where' -ex 'thread apply all bt' -ex 'q' --args $BUILD_CMD - fi - exit $rc - fi + bundle exec rake gem + if [ "${{ matrix.wxr_type }}" == "binpkg" ]; then + bundle exec rake binpkg fi - name: Install wxRuby3 gem if: matrix.wxr_type != 'develop' run: | - if [ "${{ matrix.wxr_type }}" == "bingem" ]; then - gem install $(echo pkg/*.gem) + if [ "${{ matrix.wxr_type }}" == "binpkg" ]; then + gem install $(echo pkg/*.gem) -- package=`pwd`/$(echo pkg/*.pkg) else if [ "${{ matrix.wxw_type }}" == "system" ]; then - gem install $(echo pkg/*.gem) && wxruby setup + gem install $(echo pkg/*.gem) -- prebuilt=none && wxruby setup -- --wxwin=@system elif [ "${{ matrix.wxw_type }}" == "embed" ]; then - gem install $(echo pkg/*.gem) && wxruby setup --autoinstall + gem install $(echo pkg/*.gem) -- prebuilt=none && wxruby setup --autoinstall else - gem install $(echo pkg/*.gem) && wxruby setup --wxwin=$WXWIN_INSTALL + gem install $(echo pkg/*.gem) -- prebuilt=none && wxruby setup --wxwin=$WXWIN_INSTALL fi fi + - name: Check wxRuby3 gem install + if: matrix.wxr_type != 'develop' + run: | + wxruby check + - name: Run wxRuby3 regression tests run: | ulimit -c unlimited diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index ae7a35e1..8b1b1212 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -12,6 +12,7 @@ on: - 'lib/wx/doc/**' - 'lib/wx/version.rb' - '**.md' + - '.cirrus.yml' pull_request: branches: - master @@ -26,6 +27,7 @@ on: - '.yardopts' - '.gitignore' - 'LICENSE' + - '.cirrus.yml' workflow_dispatch: concurrency: @@ -53,7 +55,7 @@ jobs: runner: macos-11 arch: x86_64 wxWidgets: '3.2.4' - ruby: '3.2' + ruby: 'ruby' swig: '4' configure_flags: --disable-sys-libs wxw_type: develop @@ -62,7 +64,7 @@ jobs: runner: macos-11 arch: x86_64 wxWidgets: '3.2.4' - ruby: '3.2' + ruby: 'ruby' swig: '4' configure_flags: --disable-sys-libs wxw_type: embed @@ -71,7 +73,7 @@ jobs: runner: macos-13 arch: x86_64 wxWidgets: '3.2.4' - ruby: '3.2' + ruby: 'ruby' swig: '4' configure_flags: --disable-sys-libs wxw_type: develop @@ -80,16 +82,16 @@ jobs: runner: macos-13 arch: x86_64 wxWidgets: '3.2.4' - ruby: '3.2' + ruby: 'ruby' swig: '4' configure_flags: --disable-sys-libs wxw_type: embed - wxr_type: bingem + wxr_type: binpkg - name: wxMac macOS 13 runner: macos-13 arch: x86_64 wxWidgets: '3.2.4' - ruby: '3.2' + ruby: 'ruby' swig: '4' configure_flags: --disable-sys-libs wxw_type: embed @@ -98,7 +100,7 @@ jobs: runner: macos-13 arch: x86_64 wxWidgets: '3.2.4' - ruby: '3.2' + ruby: 'ruby' swig: '4' configure_flags: --disable-sys-libs wxw_type: system @@ -293,33 +295,37 @@ jobs: ruby tools/check_swig_types.rb - name: Build wxRuby3 gem - if: matrix.wxr_type == 'gem' || matrix.wxr_type == 'bingem' + if: matrix.wxr_type == 'gem' || matrix.wxr_type == 'binpkg' run: | - if [ "${{ matrix.wxr_type }}" == "gem" ]; then - bundle exec rake gem - else - bundle exec rake bingem + bundle exec rake gem + if [ "${{ matrix.wxr_type }}" == "binpkg" ]; then + bundle exec rake binpkg fi - name: Remove wxWidgets - if: matrix.wxw_type == 'develop' && matrix.wxr_type == 'bingem' + if: matrix.wxw_type == 'develop' && matrix.wxr_type == 'binpkg' run: rm -rf $WXWIDGETS_ROOT - name: Install wxRuby3 gem if: matrix.wxr_type != 'develop' run: | - if [ "${{ matrix.wxr_type }}" == "bingem" ]; then - gem install $(echo pkg/*.gem) + if [ "${{ matrix.wxr_type }}" == "binpkg" ]; then + gem install $(echo pkg/*.gem) -- package=`pwd`/$(echo pkg/*.pkg) else if [ "${{ matrix.wxw_type }}" == "system" ]; then - gem install $(echo pkg/*.gem) && wxruby setup + gem install $(echo pkg/*.gem) -- prebuilt=none && wxruby setup --wxwin=@system elif [ "${{ matrix.wxw_type }}" == "embed" ]; then - gem install $(echo pkg/*.gem) && wxruby setup --autoinstall + gem install $(echo pkg/*.gem) -- prebuilt=none && wxruby setup --autoinstall else - gem install $(echo pkg/*.gem) && wxruby setup --wxwin=$WXWIN_INSTALL + gem install $(echo pkg/*.gem) -- prebuilt=none && wxruby setup --wxwin=$WXWIN_INSTALL fi fi + - name: Check wxRuby3 gem install + if: matrix.wxr_type != 'develop' + run: | + wxruby check + - name: Run wxRuby3 regression tests run: | if [ "${{ matrix.wxr_type }}" == "develop" ]; then diff --git a/.github/workflows/msw.yml b/.github/workflows/msw.yml index 5fcba111..48215d16 100644 --- a/.github/workflows/msw.yml +++ b/.github/workflows/msw.yml @@ -13,6 +13,7 @@ on: - 'lib/wx/doc/**' - 'lib/wx/version.rb' - '**.md' + - '.cirrus.yml' pull_request: branches: - master @@ -27,6 +28,7 @@ on: - '.yardopts' - '.gitignore' - 'LICENSE' + - '.cirrus.yml' workflow_dispatch: concurrency: @@ -61,7 +63,7 @@ jobs: - os: windows-2022 platform: 'x64' CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' wxw_type: embed @@ -69,7 +71,7 @@ jobs: - os: windows-2022 platform: 'x64' CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' wxw_type: develop @@ -77,15 +79,15 @@ jobs: - os: windows-2022 platform: 'x64' CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' wxw_type: embed - wxr_type: bingem + wxr_type: binpkg - os: windows-2022 platform: 'x64' CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' wxw_type: embed @@ -93,7 +95,7 @@ jobs: - os: windows-2022 platform: 'x64' CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' wxw_type: develop @@ -187,23 +189,31 @@ jobs: - name: Build wxRuby3 gem if: matrix.wxr_type != 'develop' run: | - bundle exec rake ${{ matrix.wxr_type }} + bundle exec rake gem + If ("${{ matrix.wxr_type }}" -eq "binpkg") { + bundle exec rake binpkg + } - name: Install wxRuby3 gem if: matrix.wxr_type != 'develop' run: | If ("${{ matrix.wxw_type }}" -eq "embed") { If ("${{ matrix.wxr_type }}" -eq "gem") { - foreach ($f in Get-ChildItem -Path pkg\*.gem) { gem install $f && wxruby setup --autoinstall } + foreach ($f in Get-ChildItem -Path pkg\*.gem) { gem install $f -- prebuilt=none && wxruby setup --autoinstall } } Else { - foreach ($f in Get-ChildItem -Path pkg\*.gem) { gem install $f } + foreach ($f in Get-ChildItem -Path pkg\*.gem) { foreach ($p in Get-ChildItem -Path pkg\*.pkg) { gem install $f -- package=$p } } } } Else { - foreach ($f in Get-ChildItem -Path pkg\*.gem) { gem install $f && wxruby setup --wxwin=$env:WXWIN_ROOT } + foreach ($f in Get-ChildItem -Path pkg\*.gem) { gem install $f -- prebuilt=none && wxruby setup --wxwin=$env:WXWIN_ROOT } } + - name: Check wxRuby3 gem install + if: matrix.wxr_type != 'develop' + run: | + wxruby check + - name: Run wxRuby3 regression tests run: | If ("${{ matrix.wxr_type }}" -eq "develop") { diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 24b7d071..145b49a0 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -39,7 +39,7 @@ jobs: - name: Install Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.2' + ruby-version: 'ruby' - name: Setup Ruby gems run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d2d40b4e..5f4cf8f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,41 +17,44 @@ permissions: contents: write jobs: - release: + release-gem: strategy: fail-fast: false matrix: include: - os: ubuntu-22.04 - ruby: '3.2' + ruby: 'ruby' runs-on: ${{ matrix.os }} name: ${{ matrix.os }} wxRuby Release (${{ github.ref_name }}) env: GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} + CIRRUS_TOKEN: ${{ secrets.CIRRUS_TOKEN }} outputs: - version: ${{ steps.version_var.outputs.WXRUBY_VERSION }} + version: ${{ steps.version_var.outputs.WXRUBY_RELEASE }} prerelease: ${{ steps.version_var.outputs.WXRUBY_PRERELEASE }} steps: - name: Checkout wxRuby3 uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 - if: ${{ github.event_name != 'workflow_dispatch' }} with: ruby-version: ${{ matrix.ruby }} - name: Setup Ruby gems - if: ${{ github.event_name != 'workflow_dispatch' }} run: | bundle install - name: Build wxRuby3 gem - if: ${{ github.event_name != 'workflow_dispatch' }} run: | bundle exec rake gem + - name: Create GPG signature for wxRuby3 gem + run: | + echo -n "${{ secrets.GPG_KEY }}" | base64 --decode | gpg --pinentry-mode loopback --batch --passphrase ${{ secrets.GPG_PASSPHRASE }} --import + gpg --detach-sign --pinentry-mode loopback --batch --passphrase ${{ secrets.GPG_PASSPHRASE }} --armor pkg/*.gem + - name: Upload gem to release - if: ${{ github.event_name != 'workflow_dispatch' }} + if: github.event_name != 'workflow_dispatch' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} @@ -60,41 +63,265 @@ jobs: overwrite: true file_glob: true + - name: Upload gem GPG signature to release + if: github.event_name != 'workflow_dispatch' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: pkg/*.gem.asc + tag: ${{ github.ref }} + overwrite: true + file_glob: true + - name: Publish Gem on RubyGems - if: ${{ github.event_name != 'workflow_dispatch' }} + if: github.event_name != 'workflow_dispatch' run: | - gem push pkg/*.gem + gem push pkg/*.gem + + - name: Trigger Cirrus CI Release Tasks + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + ruby tools/scripts/cirrus/start_release_tasks.rb ${{ github.ref }} + else + ruby tools/scripts/cirrus/start_release_tasks.rb + fi + + - name: Upload Gem as release test artifact + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: wxruby3_release_test_gem + path: pkg/*.gem - name: Set version variables + if: github.event_name != 'workflow_dispatch' id: version_var run: | - echo "WXRUBY_VERSION=${GITHUB_REF_NAME/#v/}" >> "$GITHUB_OUTPUT" + echo "WXRUBY_RELEASE=${GITHUB_REF_NAME/#v/}" >> "$GITHUB_OUTPUT" if grep -q "\-[a-zA-Z]" <<< "$GITHUB_REF_NAME" ; then echo "WXRUBY_PRERELEASE=1" >> "$GITHUB_OUTPUT" else echo "WXRUBY_PRERELEASE=0" >> "$GITHUB_OUTPUT" fi + + release-windows-binpkg: + needs: release-gem + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} wxRuby Binpkg Release (${{ github.ref_name }}) + env: + DOXYGEN_ROOT: ${{ github.workspace }}\doxygen + strategy: + fail-fast: false + matrix: + include: + - name: Windows2022 + os: windows-2022 + platform: 'x64' + CXX: g++-12 + ruby: 'ruby' + swig: '4' + + steps: + - name: Checkout wxRuby3 + uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: Setup Ruby gems + run: | + bundle install + + - name: Cache Doxygen + id: cache-doxygen + uses: actions/cache@v4 + with: + path: doxygen-1.10.0.windows.x64.bin.zip + key: ${{ runner.os }}-doxygen + + - name: Download Doxygen tool + if: steps.cache-doxygen.outputs.cache-hit != 'true' + run: | + wget.exe https://www.doxygen.nl/files/doxygen-1.10.0.windows.x64.bin.zip + $hash = '2135c1d5bdd6e067b3d0c40a4daac5d63d0fee1b3f4d6ef1e4f092db0d632d5b' + if ((Get-FileHash doxygen-1.10.0.windows.x64.bin.zip -Algorithm SHA256).Hash -ne $hash) { + del doxygen-1.10.0.windows.x64.bin.zip + throw "Doxygen Hash doesn't match!" + } + + - name: Install Doxygen package + run: | + Expand-Archive -LiteralPath '.\doxygen-1.10.0.windows.x64.bin.zip' -DestinationPath $env:DOXYGEN_ROOT -Force + echo "$env:DOXYGEN_ROOT" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Display build environment + run: | + echo "$env:PATH" + ruby -v + doxygen -v + ridk exec bash -c 'gcc -v' + ridk exec bash -c 'g++ -v' + + - name: Configure wxRuby3 + run: | + bundle exec rake configure[--with-wxwin,--autoinstall] + + - name: Build wxRuby3 + run: | + bundle exec rake build + + - name: Build wxRuby3 binpkg + run: | + bundle exec rake binpkg + + - name: Upload bin pkg as release asset + if: github.event_name != 'workflow_dispatch' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: pkg/*.pkg + tag: v0.9.7 + overwrite: true + file_glob: true + + - name: Upload bin pkg digest signature as release asset + if: github.event_name != 'workflow_dispatch' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: pkg/*.sha + tag: v0.9.7 + overwrite: true + file_glob: true + + - name: Upload bin pkg as release test artifacts + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: wxruby3_release_test_bin_${{ matrix.name }} + path: pkg/* + + release-macosx-binpkg: + needs: release-gem + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} wxRuby Binpkg Release (${{ github.ref_name }}) + strategy: + fail-fast: false + matrix: + include: + - name: macOS12 + os: macos-12 + arch: x86_64 + ruby: 'ruby' + - name: macOS13 + os: macos-13 + arch: x86_64 + ruby: 'ruby' + + steps: + - name: Checkout wxRuby3 + uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: Setup Ruby gems + run: | + bundle install + + - name: Display build environment + run: | + echo "$env:PATH" + ruby -v + + - name: Configure wxRuby3 + run: | + bundle exec rake configure[--with-wxwin,--autoinstall] + + - name: Build wxRuby3 + run: | + bundle exec rake build + + - name: Build wxRuby3 binpkg + run: | + bundle exec rake binpkg + + - name: Upload bin pkg as release asset + if: github.event_name != 'workflow_dispatch' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: pkg/*.pkg + tag: v0.9.7 + overwrite: true + file_glob: true + + - name: Upload bin pkg digest signature as release asset + if: github.event_name != 'workflow_dispatch' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: pkg/*.sha + tag: v0.9.7 + overwrite: true + file_glob: true + + - name: Upload bin pkg as release test artifacts + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: wxruby3_release_test_bin_${{ matrix.name }} + path: pkg/* + verify-gem: - needs: release + needs: + - release-macosx-binpkg strategy: fail-fast: false matrix: include: - - os: ubuntu-22.04 + - name: Ubuntu2204 + os: ubuntu-22.04 + gtk_version: 3 + CC: gcc-12 + CXX: g++-12 + ruby: 'ruby' + wxWidgets: '3.2.4' + swig: '4' + configure_flags: + use_xvfb: true + build: true + wxw_type: system + - name: Ubuntu2204 + os: ubuntu-22.04 gtk_version: 3 CC: gcc-12 CXX: g++-12 - ruby: '3.2' + ruby: 'ruby' wxWidgets: '3.2.4' swig: '4' configure_flags: use_xvfb: true + build: true + wxw_type: embed + - name: macOS12 + os: macos-12 + arch: x86_64 + ruby: 'ruby' + build: false + - name: macOS13 + os: macos-13 + arch: x86_64 + ruby: 'ruby' + build: false + runs-on: ${{ matrix.os }} name: ${{ matrix.os }} wxRuby Gem Test (${{ github.ref_name }}) env: WXWIDGETS_ROOT: ${{ github.workspace }}/ext/wxWidgets - WXWIN_INSTALL: ${{ github.workspace }}/ext/wxWidgets/install wxGTK_VERSION: ${{ matrix.gtk_version && matrix.gtk_version || 3 }} wxCONFIGURE_FLAGS: ${{ matrix.configure_flags }} wxUSE_ASAN: ${{ matrix.use_asan && 1 || 0 }} @@ -102,8 +329,8 @@ jobs: WX_EXTRA_PACKAGES: doxygen patchelf CC: ${{ matrix.CC }} CXX: ${{ matrix.CXX }} - WXRUBY_VERSION: ${{ needs.release.outputs.version }} - WXRUBY_PRERELEASE: ${{ needs.release.outputs.prerelease }} + WXRUBY_RELEASE: ${{ needs.release-gem.outputs.version }} + WXRUBY_PRERELEASE: ${{ needs.release-gem.outputs.prerelease }} steps: - name: Install Ruby uses: ruby/setup-ruby@v1 @@ -111,6 +338,7 @@ jobs: ruby-version: ${{ matrix.ruby }} - name: checkout wxWidgets + if: ${{ matrix.build && matrix.wxw_type != 'embed' }} uses: actions/checkout@v4 with: repository: wxWidgets/wxWidgets @@ -119,6 +347,7 @@ jobs: submodules: 'recursive' - name: Set up build environment + if: ${{ matrix.build && matrix.wxw_type != 'embed' }} run: | # Install locales used by our tests to run all the tests instead of # skipping them. @@ -130,11 +359,9 @@ jobs: run: | echo $PATH ruby -v - doxygen -v - gcc -v - g++ -v - name: Configuring wxWidgets + if: ${{ matrix.build && matrix.wxw_type != 'embed' }} working-directory: ${{ env.WXWIDGETS_ROOT }} run: | wxCONFIGURE_OPTIONS="$wxCONFIGURE_FLAGS" @@ -151,245 +378,200 @@ jobs: fi - name: Build and install wxWidgets + if: ${{ matrix.build && matrix.wxw_type != 'embed' }} working-directory: ${{ env.WXWIDGETS_ROOT }} run: | - make && sudo make install && sudo ldconfig + sudo make install && sudo ldconfig - - name: Remove wxWidgets + - name: Install wxRuby3 gem with binary package + if: ${{ !matrix.build && github.event_name != 'workflow_dispatch' }} run: | - rm -rf $WXWIDGETS_ROOT + if [ "$WXRUBY_PRERELEASE" == "1" ]; then + gem install wxruby3 -v $WXRUBY_RELEASE --pre -- prebuilt=only + else + gem install wxruby3 -v $WXRUBY_RELEASE -- prebuilt=only + fi - - name: Install wxRuby3 gem + - name: Install wxRuby3 gem for local build + if: ${{ matrix.build && github.event_name != 'workflow_dispatch' }} run: | if [ "$WXRUBY_PRERELEASE" == "1" ]; then - gem install wxruby3 -v $WXRUBY_VERSION --pre && wxruby setup --autoinstall + gem install wxruby3 -v $WXRUBY_RELEASE --pre -- prebuilt=none else - gem install wxruby3 -v $WXRUBY_VERSION && wxruby setup --autoinstall + gem install wxruby3 -v $WXRUBY_RELEASE -- prebuilt=none + fi + if wxruby check || [ "$?" != "1" ]; then + echo "ERROR: 'wxruby check' returned unexpected result!" fi - - name: Run wxRuby3 regression tests + - name: Setup wxRuby3 gem with local build + if: ${{ matrix.build && github.event_name != 'workflow_dispatch' }} run: | - ulimit -c unlimited - TEST_CMD="wxruby test" - /bin/bash -o pipefail -c "xvfb-run -a -s '-screen 0 1600x1200x24' $TEST_CMD 2>&1 | tee -a wxtest.out" || rc=$? - if [ -n "$rc" ]; then - if fgrep -q '(core dumped)' wxtest.out; then - echo '*** Test crashed, trying to get more information ***' - gdb --quiet --core=core -ex 'where' -ex 'thread apply all bt' -ex 'q' --args $TEST_CMD - fi - exit $rc + if [ "${{ matrix.wxw_type }}" == "embed" ]; then + wxruby setup --autoinstall + else + wxruby setup --wxwin=@system --autoinstall fi - verify-gem-mac: - needs: release - strategy: - fail-fast: false - matrix: - include: - - name: wxMac macOS 13 - runner: macos-13 - arch: x86_64 - wxWidgets: '3.2.4' - ruby: '3.2' - swig: '4' - configure_flags: --disable-sys-libs - runs-on: ${{ matrix.runner }} - name: ${{ matrix.name }} wxRuby Gem Test (${{ github.ref_name }}) - env: - NSUnbufferedIO: YES - WXWIDGETS_ROOT: ${{ github.workspace }}/ext/wxWidgets - WXWIN_INSTALL: ${{ github.workspace }}/ext/wxWidgets/install - WXRUBY_VERSION: ${{ needs.release.outputs.version }} - WXRUBY_PRERELEASE: ${{ needs.release.outputs.prerelease }} - - steps: - - name: Install Ruby - uses: ruby/setup-ruby@v1 + - name: Download release test gem artifact + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: actions/download-artifact@v4 with: - ruby-version: ${{ matrix.ruby }} + name: wxruby3_release_test_gem - - name: checkout wxWidgets - uses: actions/checkout@v4 + - name: Download bin pkg artifacts + if: ${{ !matrix.build && github.event_name == 'workflow_dispatch' }} + uses: actions/download-artifact@v4 with: - repository: wxWidgets/wxWidgets - path: ${{ env.WXWIDGETS_ROOT }} - ref: v${{ matrix.wxWidgets }} - - - name: Set environment variables - run: | - echo TZ=UTC >> $GITHUB_ENV - echo LD_LIBRARY_PATH=`pwd`/lib >> $GITHUB_ENV + name: wxruby3_release_test_bin_${{ matrix.name }} - - name: Before install - working-directory: ${{ env.WXWIDGETS_ROOT }} + - name: Install wxRuby3 gem with binary package (release test) + if: ${{ !matrix.build && github.event_name == 'workflow_dispatch' }} run: | - ./build/tools/before_install.sh + gem install ./$(echo wxruby3*.gem) -- package=`pwd`/$(echo wxruby3*.pkg) - - name: Install SWIG + - name: Install wxRuby3 gem for local build (release test) + if: ${{ matrix.build && github.event_name == 'workflow_dispatch' }} run: | - brew install swig - - - name: Install doxygen - run: | - brew install doxygen + gem install ./$(echo wxruby3*.gem) -- prebuilt=none + if wxruby check || [ "$?" != "1" ]; then + echo "ERROR: 'wxruby check' returned unexpected result!" + fi - - name: Show build environment + - name: Setup wxRuby3 gem with local build (release test) + if: ${{ matrix.build && github.event_name == 'workflow_dispatch' }} run: | - echo "Environment:" - env | sort - echo - - echo "Ruby version:" - ruby -v - echo - - echo "SWIG version:" - swig -version - echo - - echo "Doxygen version:" - doxygen -v - echo - - echo "Compiler version:" - ${CXX-g++} --version - echo + if [ "${{ matrix.wxw_type }}" == "embed" ]; then + wxruby setup --autoinstall + else + wxruby setup --wxwin=@system --autoinstall + fi - - name: Remove wxWidgets + - name: Check wxRuby3 gem install + if: matrix.wxr_type != 'develop' run: | - rm -rf $WXWIDGETS_ROOT + wxruby check - - name: Install wxRuby3 gem + - name: Run wxRuby3 regression tests (XVFB) + if: ${{ matrix.use_xvfb }} run: | - if [ "$WXRUBY_PRERELEASE" == "1" ]; then - gem install wxruby3 -v $WXRUBY_VERSION --pre && wxruby setup --autoinstall - else - gem install wxruby3 -v $WXRUBY_VERSION && wxruby setup --autoinstall + ulimit -c unlimited + TEST_CMD="wxruby test" + /bin/bash -o pipefail -c "xvfb-run -a -s '-screen 0 1600x1200x24' $TEST_CMD 2>&1 | tee -a wxtest.out" || rc=$? + if [ -n "$rc" ]; then + if fgrep -q '(core dumped)' wxtest.out; then + echo '*** Test crashed, trying to get more information ***' + gdb --quiet --core=core -ex 'where' -ex 'thread apply all bt' -ex 'q' --args $TEST_CMD + fi + exit $rc fi - name: Run wxRuby3 regression tests + if: ${{ !matrix.use_xvfb }} run: | wxruby test - release-bingem: - needs: release + + verify-gem-windows: + needs: + - release-windows-binpkg runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} wxRuby Bingem Release (${{ github.ref_name }}) + name: ${{ matrix.os }} wxRuby Gem Test (${{ github.ref_name }}) env: - DOXYGEN_ROOT: ${{ github.workspace }}\doxygen - GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} + WXRUBY_RELEASE: ${{ needs.release-gem.outputs.version }} + WXRUBY_PRERELEASE: ${{ needs.release-gem.outputs.prerelease }} strategy: fail-fast: false matrix: include: - - os: windows-2022 + - name: Windows2022 + os: windows-2022 platform: 'x64' CXX: g++-12 - ruby: '3.2' - wxWidgets: '3.2.4' + ruby: 'ruby' + swig: '4' + build: false + - name: Windows2022 + os: windows-2022 + platform: 'x64' + CXX: g++-12 + ruby: 'ruby' swig: '4' + build: true steps: - - name: Checkout wxRuby3 - uses: actions/checkout@v4 - - - uses: ruby/setup-ruby@v1 + - name: Install Ruby + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - name: Setup Ruby gems - if: ${{ github.event_name != 'workflow_dispatch' }} + - name: Install wxRuby3 gem with binary package + if: ${{ !matrix.build && github.event_name != 'workflow_dispatch' }} run: | - bundle install - - - name: Cache Doxygen - id: cache-doxygen - uses: actions/cache@v4 - with: - path: doxygen-1.10.0.windows.x64.bin.zip - key: ${{ runner.os }}-doxygen + If ("$env:WXRUBY_PRERELEASE" -eq "1") { + gem install wxruby3 -v $env:WXRUBY_RELEASE --pre -- prebuilt=only + } + Else { + gem install wxruby3 -v $env:WXRUBY_RELEASE -- prebuilt=only + } - - name: Download Doxygen tool - if: steps.cache-doxygen.outputs.cache-hit != 'true' + - name: Install wxRuby3 gem for local build + if: ${{ matrix.build && github.event_name != 'workflow_dispatch' }} run: | - wget.exe https://www.doxygen.nl/files/doxygen-1.10.0.windows.x64.bin.zip - $hash = '2135c1d5bdd6e067b3d0c40a4daac5d63d0fee1b3f4d6ef1e4f092db0d632d5b' - if ((Get-FileHash doxygen-1.10.0.windows.x64.bin.zip -Algorithm SHA256).Hash -ne $hash) { - del doxygen-1.10.0.windows.x64.bin.zip - throw "Doxygen Hash doesn't match!" + If ("$env:WXRUBY_PRERELEASE" -eq "1") { + gem install wxruby3 -v $env:WXRUBY_RELEASE --pre -- prebuilt=none + } + Else { + gem install wxruby3 -v $env:WXRUBY_RELEASE -- prebuilt=none } + wxruby check + If ($LastExitCode -ne 1) { + echo "ERROR: 'wxruby check' returned unexpected result!" + Exit 1 + } + Exit 0 - - name: Install Doxygen package - if: ${{ github.event_name != 'workflow_dispatch' }} + - name: Setup wxRuby3 gem with local build + if: ${{ matrix.build && github.event_name != 'workflow_dispatch' }} run: | - Expand-Archive -LiteralPath '.\doxygen-1.10.0.windows.x64.bin.zip' -DestinationPath $env:DOXYGEN_ROOT -Force - echo "$env:DOXYGEN_ROOT" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + wxruby setup --autoinstall - - name: Display build environment - if: ${{ github.event_name != 'workflow_dispatch' }} - run: | - echo "$env:PATH" - ruby -v - doxygen -v - ridk exec bash -c 'gcc -v' - ridk exec bash -c 'g++ -v' - - - name: Configure wxRuby3 - if: ${{ github.event_name != 'workflow_dispatch' }} - run: | - bundle exec rake configure[--with-wxwin] + - name: Download release test gem artifact + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: actions/download-artifact@v4 + with: + name: wxruby3_release_test_gem - - name: Build wxRuby3 - if: ${{ github.event_name != 'workflow_dispatch' }} - run: | - bundle exec rake build + - name: Download bin pkg artifacts + if: ${{ !matrix.build && github.event_name == 'workflow_dispatch' }} + uses: actions/download-artifact@v4 + with: + name: wxruby3_release_test_bin_${{ matrix.name }} - - name: Build wxRuby3 gem - if: ${{ github.event_name != 'workflow_dispatch' }} + - name: Install wxRuby3 gem with binary package (release test) + if: ${{ !matrix.build && github.event_name == 'workflow_dispatch' }} run: | - bundle exec rake bingem + foreach ($f in Get-ChildItem -Path wxruby3*.gem) { foreach ($p in Get-ChildItem -Path wxruby3*.pkg) { gem install $f -- package=$p } } - - name: Publish Gem on RubyGems - if: ${{ github.event_name != 'workflow_dispatch' }} + - name: Install wxRuby3 gem for local build (release test) + if: ${{ matrix.build && github.event_name == 'workflow_dispatch' }} run: | - gem push pkg/*.gem - - verify-bingem: - needs: [release, release-bingem] - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} wxRuby Bingem Test (${{ github.ref_name }}) - env: - WXRUBY_VERSION: ${{ needs.release.outputs.version }} - WXRUBY_PRERELEASE: ${{ needs.release.outputs.prerelease }} - strategy: - fail-fast: false - matrix: - include: - - os: windows-2022 - platform: 'x64' - CXX: g++-12 - ruby: '3.2' - wxWidgets: '3.2.4' - swig: '4' - - steps: - - name: Install Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} + foreach ($f in Get-ChildItem -Path wxruby3*.gem) { gem install $f -- prebuilt=none } + wxruby check + If ($LastExitCode -ne 1) { + echo "ERROR: 'wxruby check' returned unexpected result!" + Exit 1 + } + Exit 0 - - name: Wait for RubyGems + - name: Setup wxRuby3 gem with local build (release test) + if: ${{ matrix.build && github.event_name == 'workflow_dispatch' }} run: | - Start-Sleep -Seconds 300 + foreach ($f in Get-ChildItem -Path wxruby3*.gem) { wxruby setup --autoinstall } - - name: Install wxRuby3 gem + - name: Check wxRuby3 gem install if: matrix.wxr_type != 'develop' run: | - If ("$env:WXRUBY_PRERELEASE" -eq "1") { - gem install wxruby3 -v $env:WXRUBY_VERSION --pre - } - Else { - gem install wxruby3 -v $env:WXRUBY_VERSION - } + wxruby check - name: Run wxRuby3 regression tests run: | diff --git a/Gemfile b/Gemfile index cf205047..e616ea46 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ gem 'rake' gem 'nokogiri', '~> 1.12' gem 'minitest', '~> 5.15' gem 'test-unit', '~> 3.5' +gem 'plat4m', '~> 1.0' group :develop, optional: true do gem 'ruby_memcheck', '~> 1.2' end diff --git a/INSTALL.md b/INSTALL.md index ec66c38e..90c20e13 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -8,25 +8,26 @@ The wxRuby3 gem provides a **worry-free** installation for all supported platforms. -The default gem installation commands +The default gem installation command + ```shell gem install wxruby3 ``` -for Windows and +and the setup command ```shell -gem install wxruby3 && wxruby setup +wxruby setup ``` -for Linux and MacOSX (see Note below) and Windows (if installing for Ruby releases older than the latest stable release) -should always result in a successfully installed wxRuby3 version.
-Just follow the (very few and in some cases none) prompts at the start of the setup procedure and sit back and relax. + +for installations without prebuilt binary packages should always result in a successfully installed wxRuby3 version. > **NOTE**
> Currently installing the wxRuby3 gem for the system supplied Ruby on MacOSX systems does not work.
> The user is therefor required to install a Ruby interpreter using either [MacPorts](https://www.macports.org/) (both > privileged and user installations are supported) or [Homebrew](https://brew.sh/) or Ruby installers/version managers -> like [ruby-install](https://github.com/postmodern/ruby-install) or [RVM](https://rvm.io) (only user installations supported) . +> like [ruby-install](https://github.com/postmodern/ruby-install) or [RVM](https://rvm.io) (only user installations +> supported) . [Below](INSTALL.md#installing-software-requirements) more details regarding the software requirements for wxRuby3, the setup procedure and various options to tweak and customize the installation (including platform specific details for @@ -36,16 +37,65 @@ Linux, Windows and MacOSX) are described. Installing the wxRuby3 gem will also install the bundled `wxruby` CLI binary. -For source gem installations the CLI will initially only provide the *setup* command. +For source gem installations the CLI will initially only provide the `check` and `setup` commands. -For binary gem installations and successfully set up source gem installations the *setup* command is replaced by other +For finalized installations (either from binary packages or source builds) the *setup* command is replaced by other utility commands providing the ability to run the bundled regression tests and access (run or copy) the bundled examples.
Run the following command to see the available options at any time: ```shell -wxruby --help +wxruby -h ``` +## Binary packages + +The wxRuby3 gem installation process will by default attempt to match the current platform to any standard available +binary packages and if found install the matched package. + +Binary packages are archives (custom format) containing prebuilt (extension) library artifacts for a single specific +platform. Any such platform is identified by: + +- CPU architecture (x86_64, ARM64, etc.) +- Operating system type (linux, darwin, windows, etc.) +- OS distribution and release number (except for windows) +- Ruby ABI version (i.e. {major}.{minor}) + +The standard available binary packages provide both the wxRuby3 extension libraries as well as the embedded wxWidgets +libraries the extension libraries were built for.
+This is however not mandatory. User created binary packages can be built for separately installed (either distribution +or user provided) wxWidgets libraries. + +### Standard packages + +The standard release artifacts at [Github](https://github.com/mcorino/wxRuby3/releases) provide a selection of binary +packages for all supported OS platforms which are automatically built and uploaded for every release.
+The following tables lists the packages provided by the current wxRuby3 release process: + +| OS | Distributions | Architectures | Rubies | +|---------|-------------------------------|-------------------------|----------------------------------------------------| +| Linux | OpenSuSE Leap (latest stable) | x86_64 and ARM64 | Distro provided Ruby and Latest stable Ruby | +| Linux | Fedora (latest stable) | x86_64 and ARM64 | Distro provided Ruby and Latest stable Ruby | +| Linux | Debian (latest stable) | x86_64 and ARM64 | Distro provided Ruby and Latest stable Ruby | +| Linux | Ubuntu (latest stable) | x86_64 and ARM64 | Distro provided Ruby and Latest stable Ruby | +| Windows | NA | x86_64 | Latest stable Ruby | +| OSX | MacOSX 12 | x86_64 and ARM64 | Latest stable Ruby | +| OSX | MacOSX 13 | x86_64 and ARM64 | Latest stable Ruby | +| OSX | MacOSX 14 | ARM64 | Latest stable Ruby | + +### User created packages + +Users can create their own wxRuby3 binary packages by building from source ([see here](#building-from-source)) and after +successfully having built the wxWidgets extension libraries execute the `rake binpkg` command.
+This creates two files in the `pkg` folder with names like
+`wxruby3_{distribution}_ruby{abi version}_v{wxruby version}_{os}_{arch}.pkg`
+and
+`wxruby3_{distribution}_ruby{abi version}_v{wxruby version}_{os}_{arch}.sha`
+where the `.pkg` file is the actual binary archive and the `.sha` file is the associated SHA256 digest signature of the +archive contents. + +Both files are required for installation and should be located at the same path (either local path or http(s) url).
+[See here](#the-package-option) for information on how to use user created binary packages with the wxRuby3 gem installation process. + ## Software requirements for wxRuby3 The software requirements for setting up a wxRuby3 runtime environment are: @@ -90,63 +140,129 @@ wxRuby3 can be built and installed for 3 different types of wxWidgets installati In this case the libraries and development files are most likely not found in standard locations and the wxRuby3 installation procedure will require specific options to have these locations provided. 3. An 'embedded' installation of wxWidgets setup by the wxRuby3 installation procedure.
- This is the default when the installation procedure does not detect a (compatible!) system installation or if an - option has been provided explicitly specifying to install an embedded wxWidgets version. + This is the default when using a standard binary package or when installing from source and the setup procedure does + not detect a (compatible!) system installation or if an option has been provided explicitly specifying to install an + embedded wxWidgets version. Please note that in case of option **2** the user is responsible to make sure the wxWidgets shared libraries can be found by the system's dynamic loader at runtime. -As described with option **3** a wxWidgets system installation must be compatible (>= version 3.2) to be selected. In case -the installed version does not meet this requirement it is ignored as if not installed. +As described with option **3** a wxWidgets system installation must be compatible (>= version 3.2) to be selected for +source installation. In case the installed version does not meet this requirement it is ignored as if not installed. For more information on how to install wxWidgets see the [Installing software requirements](INSTALL.md#installing-software-requirements) section below. ## wxRuby3 gem installation details -The wxRuby3 project provides gems on [RubyGems](https://rubygems.org) which can be installed with the +The wxRuby3 project provides a gem on [RubyGems](https://rubygems.org) which can be installed with the standard `gem install` command line this: ```shell gem install wxruby3 ``` -On Linux and MacOSX systems this will install the source based gem which will require a follow-up command to build the native wxruby3 extension -for the platform on which wxRuby3 is being installed.
-On Windows systems a prebuilt binary gem is available for the latest stable release(s) of [RubyInstaller](https://rubyinstaller.org) -installed rubies that will be installed by default if installing for that platform (including an embedded, latest -stable version, wxWidgets installation).
-Alternatively the source gem can be installed on Windows by installing with an explicit platform specification like this: +Alternatively the gem can be downloaded from the [Github release assets](https://github.com/mcorino/wxRuby3/releases) and +stored locally. This local gem can than be installed like this: ```shell -gem install wxruby3 --platform=ruby +gem install /path/to/local/wxruby3.gem ``` -> The result of installing the gem on a Linux platform should be something like this: +This default installation command will allow the wxRuby3 installation process to scan available standard binary packages +([see here](#standard-packages)) for a match to the platform being installed on and install any matched package or revert +to a source install if none matched.
+This command will therefor succeed if: +- a matching binary package could be successfully downloaded and installed; +- the installation reverted to source install. + +This command only fails when: +- a matching digest signature for the downloaded binary package could not be downloaded; +- the digest signature did not match the downloaded package contents. + +> The result of successfully installing the gem on a Linux platform should be something like this: > ``` > $ gem install wxruby3 +> Building native extensions. This could take a while... > -> The wxRuby3 Gem has been successfully installed. -> Before being able to use wxRuby3 you need to run the post-install setup process -> by executing the command 'wxruby setup'. +> The wxRuby3 Gem has been successfully installed including the 'wxruby' utility. > -> Run 'wxruby setup -h' to see information on the available commandline options. -> -> Successfully installed wxruby3-0.9.5 -> Parsing documentation for wxruby3-0.9.5 -> Installing ri documentation for wxruby3-0.9.5 -> Done installing documentation for wxruby3 after 10 seconds +> In case no suitable binary release package was available for your platform you +> will need to run the post-install setup process by executing: +> +> $ wxruby setup +> +> To check whether wxRuby3 is ready to run or not you can at any time execute the +> following command: +> +> $ wxruby check +> +> Run 'wxruby check -h' for more information. +> +> When the wxRuby3 setup has been fully completed you can start using wxRuby3. +> +> You can run the regression tests to verify the installation by executing: +> +> $ wxruby test +> +> The wxRuby3 sample explorer can be run by executing: +> +> $ wxruby sampler +> +> Have fun using wxRuby3. +> +> Run 'wxruby -h' to see information on the available commands. +> +> Successfully installed wxruby3-0.9.8 +> Parsing documentation for wxruby3-0.9.8 +> Installing ri documentation for wxruby3-0.9.8 +> Done installing documentation for wxruby3 after 2 seconds > 1 gem installed > ``` -As said, on Linux and MacOSX systems (also on Windows when installing the source gem) an additional command is required -to build the actual wxRuby3 extension libraries for the platform which is a wxRuby3 CLI command installed by the gem: +### Gem installation options + +Two options are available to control the wxRuby3 gem installation process. + +#### The `prebuilt` option + +The `prebuilt=none|only` option can be used to either prevent binary package matching and installation (`prebuilt=none`) +or make binary package installation mandatory (`prebuilt=only`). + +The following command therefor forces a wxRuby3 source installation and will never fail: + +```shell +gem install wxruby3 -- prebuilt=none +``` + +And the following command will force binary package installation and fails if no matching package could be installed: + +```shell +gem install wxruby3 -- prebuilt=only +``` + +#### The `package` option + +The `package=URL` option can be used to explicitly specify a binary package to install. This option implies `prebuilt=only`. +No package matching will be performed so mismatched binary packages will cause wxRuby3 to fail after installation.
+The `URL` can be specified as: +- an absolute local path like `/path/to/binary/package.pkg` +- an absolute `file://` URI like `file:///path/to/binary/package.pkg` +- an `http://` or `https://` URL + +In all cases the associated `.sha` file must be located at the same path as the package file itself. If not the +installation will fail as well as when the signature does not match the digest of the package contents. + +### Gem source setup + +As said a gem-based source installation requires an additional command is to build the actual wxRuby3 extension libraries +for the platform installing on which is a wxRuby3 CLI command installed by the gem: ```shell wxruby setup ``` The wxRuby3 CLI `wxruby` is installed by all wxRuby3 gems. In case of the source gem initially the CLI will provide only -a single command `wxruby setup` to finish wxRuby3 extension (build and) installation. +the commands `wxruby setup` (to finish wxRuby3 extension installation) and `wxruby check`. For most (user) installations the default setup command as shown above will suffice nicely. In this case the setup (or installation) procedure will analyze the system to see if it meets the software requirements described above and if not @@ -184,7 +300,7 @@ The initial message shown (between lines starting with '---' ) is indicative of on options passed to the setup command.
Building the wxRuby3 native extensions and generating reference documentation will always happen. -### Disable prompting for automatic install +#### Disable prompting for automatic install To prevent having the setup procedure asking consent the setup procedure can be started with the `--autoinstall` option like this: @@ -195,7 +311,7 @@ wxruby setup --autoinstall Note that on Linux that may still present a prompt in case the `sudo` command requires a password. -### Prevent automatic installation of software requirements +#### Prevent automatic installation of software requirements To prevent the setup procedure from considering to automatically install (with or without prompting) any missing software requirements the setup procedure can be started with the `--no-autoinstall` option like this: @@ -207,7 +323,7 @@ wxruby setup --no-autoinstall The setup procedure will still analyze the system for available software requirements and if it finds any missing it will end the procedure and show a message of what it found missing. -### Force embedded wxWidgets installation +#### Force embedded wxWidgets installation To prevent the setup procedure of using any system installed wxWidgets version the setup procedure can be started with the `--with-wxwin` option like this: @@ -218,7 +334,7 @@ wxruby setup --with-wxwin This will force the setup procedure to build and install an embedded wxWidgets version for wxRuby3. -### Setup with user installed wxWidgets +#### Setup with user installed wxWidgets In case of a (custom) user installation of wxWidgets the `--wxwin` (and optionally `--wxxml`) option(s) can be used to start the setup procedure to build for this installation like this: @@ -241,7 +357,7 @@ wxruby setup --wxwin=/my/custom/wxWidgets --wxxml=/my/alternate/wxWidgets/xml > responsible for making sure the wxRuby3 extension library can find the wxWidgets libraries at runtime (normally this > requires updating the standard shared library search path for the platform). -### Setup with customized tool paths +#### Setup with customized tool paths If for whatever reason the required development tools `doxygen`, `swig` and/or `git` have been installed in a location not in the standard executable search path the full path to these tools can be passed on the setup procedure using the @@ -251,7 +367,7 @@ not in the standard executable search path the full path to these tools can be p wxruby setup --doxygen=/my/path/to/doxygen ``` -### Redirect log to customized path +#### Redirect log to customized path The setup procedure will log full build results to a file setup.log at the location where the gem contents is stored. If the setup fails the error message will display the log file location and by default if the setup succeeds the log @@ -364,7 +480,7 @@ Checkout the wxRuby3 sources from [GitHub](https://github.com/mcorino/wxRuby3) o Requirements are the same as for installing the source gem. Gem dependencies are listed in the Gemfile in the root of the wxRuby3 tree and should be installed by executing `bundle install`.
To be able to generate HTML documentation the optional `:documentation` group should be included.
-To be able to run the Rake memory check task the option `:develop` group should be included. +To be able to run the Rake memory check task the optional `:develop` group should be included. The wxRuby3 project provides a Rake based build system. Call `rake help` to get an overview of the available commands. As mentioned there the `rake configure` command is required as the very first command. Call `rake configure[--help]` to @@ -376,4 +492,7 @@ commands are executed using parallel task execution by default. When the build has finished without errors the regression tests can be run by calling `rake test`. +After successfully building the wxRuby3 extension libraries (and possibly embedded wxWidgets libraries) a binary package +can be created by call `rake binpkg`. + For more details concerning the wxRuby3 development strategy and build options see [here](TODO). diff --git a/README.md b/README.md index 3911c6af..3ccc4850 100644 --- a/README.md +++ b/README.md @@ -96,11 +96,11 @@ of these products. Currently the following are fully supported: -| Platform | Ruby version(s) | wxWidgets version(s) | -|-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|----------------------| -| Windows 10 (tested)
(most likely also Windows 11) | Ruby >= 2.5
(RubyInstaller MSYS2-DevKit) | wxWidgets >= 3.2 | -| Linux (tested; all major AMD-64 distributions: Ubuntu, Debian, Fedora, OpenSuSE and ArchLinux)
(most likely also i686 and ARM) | Ruby >= 2.5 | wxWidgets >= 3.2 | -| MacOS >= 10.10 using Cocoa (tested on AMD-64 and ARM64 M2 Chip) | Ruby >= 2.5 (MacPorts, Homebrew, ruby-install, RVM) | wxWidgets >= 3.2 | +| Platform | Ruby version(s) | wxWidgets version(s) | +|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|----------------------| +| Windows 10 (tested)
(most likely also Windows 11) | Ruby >= 2.5
(RubyInstaller MSYS2-DevKit) | wxWidgets >= 3.2 | +| Linux (tested; all major AMD64 and ARM64 distributions: Ubuntu, Debian, Fedora, OpenSuSE and ArchLinux)
(most likely also i686) | Ruby >= 2.5 | wxWidgets >= 3.2 | +| MacOS >= 10.10 using Cocoa (tested on AMD64 and ARM64 M1/M2 Chip) | Ruby >= 2.5 (MacPorts, Homebrew, ruby-install, RVM) | wxWidgets >= 3.2 | Support for other platforms is not being actively developed at present, but patches are welcome. It is likely to be much simpler to get wxRuby @@ -109,43 +109,49 @@ on legacy systems (eg Windows 98, Mac OS 9). ### How can I install wxRuby3? -wxRuby3 is distributed as a Ruby gem on [RubyGems](https://rubygems.org).
-Apart from a regular source-only version of the gem there is also a binary gem version for Windows 10 provided (for the -latest stable Ruby release at the time of publishing) which includes an embedded wxWidgets installation (also latest -stable version). +wxRuby3 is distributed as a Ruby gem on [RubyGems](https://rubygems.org). This gem can also be downloaded from the release +assets on [Github](https://github.com/mcorino/wxRuby3/releases). The wxRuby3 gem provides a **worry-free** installation procedure for all supported platforms. -Installing the binary gem version on Windows (which is the default when installing for the latest stable Ruby release for -that platform) requires no additional installation steps and/or additional software to be installed except for a supported -version of the Ruby interpreter. So for this platform the following command is all it takes to get up and running: +Installing the gem requires no additional installation steps and/or additional software to be installed except for a +supported version of the Ruby interpreter. So the following command is all it takes to install: ```shell gem install wxruby3 ``` -For the platforms requiring a source based installation (Linux and MacOSX as well as Windows for older Ruby releases -or when explicitly selecting source based install) an additional (simple) setup step is required to finish installation. -For these platforms the full installation sequence would be: +The wxRuby3 installation procedure will check the availability of a, prebuilt, binary package matching the platform +being installed on and if found will download and install that package resulting in a ready-to-run wxRuby3 installation.
+If no matching package is found the installation reverts to a source installation which will require an additional setup +step to finalize the wxRuby3 installation by executing the following command: ```shell -gem install wxruby3 && wxruby setup +wxruby setup ``` -The second part of this command sequence is a fully automated setup procedure provided by the wxRuby3 **CLI** installed with -the gem. This procedure (by default) will analyze your system and install (after asking your consent) any missing software -requirements and build the wxRuby3 extension libraries (including a embedded copy of wxWidgets if necessary). It may take -quite a while depending on your system but you can mostly sit back and relax. +This last command is a fully automated setup procedure provided by the wxRuby3 **CLI** installed with the gem. This +procedure (by default) will analyze your system and install (after asking your consent) any missing software +requirements and build the wxRuby3 extension libraries (including a embedded copy of wxWidgets if necessary). It may +take quite a while depending on your system but you can mostly sit back and relax. > **NOTE**
> A source based installation requires the availability of the Ruby development headers. User installed Rubies in most cases > will already include those but (especially on Linux) system installed Rubies may require having an additional '-dev/-devel' -> package installed. +> package installed (although actually you may already have needed those to install the gems that the wxRuby3 gem depends +> on like the nokogiri gem). + +The wxRuby3 CLI also provides a 'check' command with which the runtime status of the wxRuby3 installation can be checked +at any time. By default running `wxruby check` will display a message reporting the runtime and suggestions on finalizing +the installation if not finalized yet. No message is displayed if wxRuby3 is ready to run. Run `wxruby check -h` for +details concerning this command. + +A selection of (prebuilt) binary packages is provided as release assets on [Github](https://github.com/mcorino/wxRuby3/releases). +See the [INSTALL](INSTALL.md#binary-packages) document for more details. This install procedure can of course be tweaked and customized with commandline arguments. See the [INSTALL](INSTALL.md) document for more details. - ### Where can I ask a question, or report a bug? Use GitHUb Issues. diff --git a/ext/mkrf_conf_bingem.rb b/ext/mkrf_conf_bingem.rb deleted file mode 100644 index 2133852e..00000000 --- a/ext/mkrf_conf_bingem.rb +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2023 M.J.N. Corino, The Netherlands -# -# This software is released under the MIT license. - -### -# wxRuby3 extension configuration file for binary gem -### - -# generate Rakefile with appropriate default task (all actual task in rakelib) -File.open('../Rakefile', 'w') do |f| - f.puts < These classes include: -* classes considered POD types like Wx::Size, Wx::Point, Wx::RealPoint, Wx::Rect, Wx::GBSpan, Wx::GBPosition, Wx::BusyInfoFlags, +- classes considered POD types like Wx::Size, Wx::Point, Wx::RealPoint, Wx::Rect, Wx::GBSpan, Wx::GBPosition, Wx::BusyInfoFlags, Wx::AboutDialogInfo -* final non-instantiatable classes like the Wx::DC (Device Context) class family, Wx::GraphicsContext, Wx::WindowsDisabler, +- final non-instantiatable classes like the Wx::DC (Device Context) class family, Wx::GraphicsContext, Wx::WindowsDisabler, Wx::EventBlocker, Wx::BusyInfo -* classes with native singleton objects like Wx::Clipboard -* the reference counted GDI objects like Wx::Pen, Wx::Brush, Wx::Colour, Wx::Cursor, Wx::Bitmap, Wx::Icon and similar +- classes with native singleton objects like Wx::Clipboard +- the reference counted GDI objects like Wx::Pen, Wx::Brush, Wx::Colour, Wx::Cursor, Wx::Bitmap, Wx::Icon and similar reference counted objects like Wx::Font The reference documentation will note untracked object classes. diff --git a/lib/wx/doc/extra/14_config.md b/lib/wx/doc/extra/14_config.md index dd37eee4..27f75c62 100644 --- a/lib/wx/doc/extra/14_config.md +++ b/lib/wx/doc/extra/14_config.md @@ -87,7 +87,7 @@ Another difference is that {Wx::Config} will not automatically create missing gr happen when writing configuration values. A last difference is that the default support is by default backed up by persistent storage (windows registry or file) and -the wxRuby enhanced support only provides in-memory storage (`Hash` instance) by default. + +the wxRuby enhanced support only provides in-memory storage (`Hash` instance) by default.
Persisting configuration data from {Wx::Config} will require coding customized storage and retrieval operations (which is trivial using standard YAML or JSON support). diff --git a/lib/wx/doc/secret_store.rb b/lib/wx/doc/secret_store.rb new file mode 100644 index 00000000..838c0f86 --- /dev/null +++ b/lib/wx/doc/secret_store.rb @@ -0,0 +1,55 @@ +# :stopdoc: +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. +# :startdoc: + + +module Wx + + class SecretStore + + # Wipes the secret data. + # @param [String] secret string containing secret data + # @return [void] + def self.wipe(secret); end + + end + + class SecretValue + + # @overload initialize() + # Creates an empty secret value (not the same as an empty password). + # @return [Wx::SecretValue] + # @overload initialize(secret) + # Creates a secret value from the given string. + # The secret argument may contain NUL bytes. + # Any UTF-8 encoded (or encodable; wxRuby will attempt re-encoding as UTF-8 for any string not encoded UTF-8 or ASCII-8BIT) string will be stored as UTF-8 encoded string. + # In these cases use {#get_as_string} if needing to compare the original string to a restored string. + # Otherwise the string will be stored as ASCII-8BIT encoded string. + # In these cases use {#get_data} if needing to compare the original string to a restored string. + # See {#==} for comparing secret values opaquely. + # @param secret [String] + # @return [Wx::SecretValue] + # @overload initialize(other) + # Creates a copy of an existing secret. + # @param other [Wx::SecretValue] + # @return [Wx::SecretValue] + def initialize(*args) end + + # Returns a copy of the secret data as an ASCII-8BIT encoded String. + # Be aware this could be binary data and may contain embedded NUL characters. + # For more security {Wx::SecretStore.wipe} should be used to wipe the secret data after use. + # @return [String] secret data + def get_data; end + + # Returns a copy of the secret data as an UTF-8 encoded String. + # Make sure to use this method only if sure that the secret originally stored was indeed + # UTF-8 data as otherwise the returned string will not match the stored data. + # For more security {Wx::SecretStore.wipe} should be used to wipe the secret data after use. + # @return [String] secret data + def get_as_string; end + + end + +end diff --git a/lib/wx/version.rb b/lib/wx/version.rb index 688ce705..6c4f707e 100644 --- a/lib/wx/version.rb +++ b/lib/wx/version.rb @@ -3,5 +3,5 @@ # This software is released under the MIT license. module Wx - WXRUBY_VERSION = '0.9.7' + WXRUBY_VERSION = '0.9.8' end diff --git a/lib/wx/wxruby/base.rb b/lib/wx/wxruby/base.rb index 1354cb2c..bb9f60cb 100644 --- a/lib/wx/wxruby/base.rb +++ b/lib/wx/wxruby/base.rb @@ -20,7 +20,7 @@ class << self def commands @commands ||= ::Hash.new do |hash, key| STDERR.puts "Unknown command #{key} specified." - exit(1) + exit(127) end end private :commands @@ -73,7 +73,7 @@ def parse_args(args) describe_all exit(0) end - opts.order!(args) + opts.order!(args) rescue ($stderr.puts $!.message; exit(127)) end end end @@ -81,9 +81,7 @@ def parse_args(args) def self.run(argv = ARGV) # parse global options (upto first command) argv = WxRuby::Commands.parse_args(argv) - while !argv.empty? - WxRuby::Commands.run(argv.shift, argv) - end + WxRuby::Commands.run(argv.shift, argv) unless argv.empty? end end diff --git a/lib/wx/wxruby/cmd/check.rb b/lib/wx/wxruby/cmd/check.rb new file mode 100644 index 00000000..a7841c8b --- /dev/null +++ b/lib/wx/wxruby/cmd/check.rb @@ -0,0 +1,182 @@ +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. + +# wxruby check command handler +#-------------------------------------------------------------------- + +require 'fileutils' +require 'plat4m' + +module WxRuby + module Commands + + class Check + + LOAD_ERRORS = { + linux: /cannot\s+open\s+shared\s+object/i, + darwin: /library\s+not\s+loaded/i, + windows: /specified\s+module\s+could\s+not\s+be\s+found/i + } + + DESC = 'Run wxRuby3 runtime readiness check.' + + def self.description + " check -h|[options]\t\t\t#{DESC}" + end + + def self.options + Commands.options['check'] ||= { verbose: Commands.options[:verbose] } + end + + def self.parse_args(args) + opts = OptionParser.new + opts.banner = "#{DESC}\n\nUsage: wxruby check -h|--help OR wxruby check [options]\n\n" + + "Returns:\n"+ + " 0 if wxRuby3 is ready to run\n"+ + " 1 if wxRuby3 does not seem to be built yet\n"+ + " 2 if wxRuby3 has problems loading extension libraries\n"+ + " 3 if an unexpected Ruby error occurred\n\n"+ + "Unless '-q|--quiet' has been specified a description of the possible problem cause will\n"+ + "be shown on failure.\n\n" + opts.separator '' + opts.on('-q', '--quiet', + "Do not show problem analysis messages on failures.") do |v| + Check.options[:quiet] = true + Check.options[:verbose] = false + end + opts.on('-v', '--verbose', + 'Show verbose output') do |v| + Check.options[:verbose] = true + Check.options[:quiet] = false + end + opts.on('-h', '--help', + 'Show this message.') do |v| + puts opts + puts + exit(0) + end + opts.parse!(args) rescue ($stderr.puts $!.message; exit(127)) + end + + def self.show_error(msg) + $stderr.puts(msg) unless options[:quiet] + end + + def self.show_log(msg) + $stdout.puts(msg) if options[:verbose] + end + + def self.run(argv) + return description if argv == :describe + + parse_args(argv) + + show_log('Checking build (or binary package installation) completion...') + # check if the binary setup (packages or built) has been completed successfully + unless Commands.setup_done? + $stderr.puts <<~__INFO_TXT + + wxRuby3 requires the post-install setup cmd to be run to build and finish installing + the required runtime binaries. Execute the command like: + + $ wxruby setup + + To see the available options for the command execute: + + $ wxruby setup -h + + __INFO_TXT + exit(1) + end + + # check runtime + show_log('Attempting to load wxRuby3 libraries...') + sysinfo = Plat4m.current + begin + require 'wx' + rescue LoadError => ex + if ex.message =~ LOAD_ERRORS[sysinfo.os.id] + # error loading shared libraries + show_log("Captured LoadError: #{ex.message}") + # check if wxWidgets libs can be located + show_log('Checking wxWidgets availability...') + wx_found = if Dir[File.join(WxRuby::ROOT, 'ext', "*.#{RbConfig::CONFIG['SOEXT']}")].empty? + # no embedded wxWidgets -> if system installed than 'wx-config' should be in the path + if system("wx-config --version>#{sysinfo.dev_null} 2>&1") + true # system installed + else + # no system installed wxWidgets + # check the system dependent load paths if any wxWidgets libs can be found + case sysinfo.os.id + when :linux + (ENV['LD_LIBRARY_PATH']||'').split(':').any? { |p| !Dir[File.join(p, 'libwx_base*.so')].empty? } + when :darwin + (ENV['DYLD_LIBRARY_PATH']||'').split(':').any? { |p| !Dir[File.join(p, 'libwx_base*.dylib')].empty? } + when :windows + (ENV['PATH']||'').split(';').any? { |p| !Dir[File.join(p, 'wxbase*.dll')].empty? } + else + true # do not know how to search so assume wxWidgets found + end + end + else + true # embedded wxWidgets + end + if wx_found + show_log('wxWidgets found') + show_error <<~__INFO_TXT + + The runtime environment of this system seems to be missing some required libraries for + executing wxRuby3 apps. + Please be aware wxRuby3 requires a properly configured GUI based system to function. + See the documentation for more information on the required runtime environment. + + __INFO_TXT + else + show_log('NO wxWidgets found') + show_error <<~__INFO_TXT + + It seems wxRuby3 is not able to load any of the required wxWidgets libraries it was built + for. + Please make sure these (shared) libraries are available in the appropriate search path + for this system. + + __INFO_TXT + end + else + show_error <<~__INFO_TXT + + There is an unexpected problem loading the wxRuby3 extension libraries. + Please check the problem report below for a probable cause analysis. If you have reason + to suspect a bug to be the cause of this problem please file an issue at Github and attach + the problem report. + + #{ex.message} + #{ex.backtrace.join("\n")} + + __INFO_TXT + end + exit(2) + rescue Exception => ex + show_log("Captured Exception: #{ex.message}") + show_error <<~__INFO_TXT + + There is an unexpected problem loading the wxRuby3 libraries. + Please check the problem report below for a probable cause analysis. If you have reason + to suspect a bug to be the cause of this problem please file an issue at Github and attach + the problem report. + + #{ex.message} + #{ex.backtrace.join("\n")} + + __INFO_TXT + exit(3) + end + end + + end + + self.register('check', Check) + + end +end diff --git a/lib/wx/wxruby/cmd/sampler.rb b/lib/wx/wxruby/cmd/sampler.rb index 28dba98a..111ef37c 100644 --- a/lib/wx/wxruby/cmd/sampler.rb +++ b/lib/wx/wxruby/cmd/sampler.rb @@ -34,7 +34,7 @@ def self.parse_args(args) puts exit(0) end - opts.parse!(args) + opts.parse!(args) rescue ($stderr.puts $!.message; exit(127)) end def self.run(argv) diff --git a/lib/wx/wxruby/cmd/setup.rb b/lib/wx/wxruby/cmd/setup.rb index d5201ead..285d5775 100644 --- a/lib/wx/wxruby/cmd/setup.rb +++ b/lib/wx/wxruby/cmd/setup.rb @@ -14,7 +14,7 @@ class Setup DESC = 'Run wxRuby3 post-install setup.' def self.description - " setup -h|[options]\t\t#{DESC}" + " setup -h|[options]\t\t\t#{DESC}" end def self.options @@ -26,7 +26,10 @@ def self.parse_args(args) opts.banner = "#{DESC}\n\nUsage: wxruby setup -h|--help OR wxruby setup [options]\n\n" opts.separator '' opts.on('--wxwin=path', - "the installation root for the wxWidgets libraries and headers if not using the system default") {|v| Setup.options['wxwin'] = File.expand_path(v)} + "the installation root for the wxWidgets libraries and headers if not using the system default", + "(use '@system' to force using system default only)") do |v| + Setup.options['wxwin'] = (v.downcase == '@system') ? v : File.expand_path(v) + end opts.on('--wxxml=path', "the path to the doxygen generated wxWidgets XML interface specs if not using bootstrap") {|v| Setup.options['wxxml'] = File.expand_path(v)} opts.on('--with-wxwin', @@ -47,7 +50,7 @@ def self.parse_args(args) puts exit(0) end - opts.parse!(args) + opts.parse!(args) rescue ($stderr.puts $!.message; exit(127)) end def self.run(argv) diff --git a/lib/wx/wxruby/cmd/test.rb b/lib/wx/wxruby/cmd/test.rb index 8f4f9d4e..0f37b03a 100644 --- a/lib/wx/wxruby/cmd/test.rb +++ b/lib/wx/wxruby/cmd/test.rb @@ -45,7 +45,7 @@ def self.parse_args(args) puts exit(0) end - opts.parse!(args) + opts.parse!(args) rescue ($stderr.puts $!.message; exit(127)) end def self.run(argv) diff --git a/rakelib/configure.rb b/rakelib/configure.rb index 3151a6cf..8198f2aa 100644 --- a/rakelib/configure.rb +++ b/rakelib/configure.rb @@ -57,13 +57,26 @@ def self.define(task, args) opts.on('--sodir=path', "the directory for ruby extensions [#{get_config('sodir')}]") {|v| CONFIG['sodir'] = v} opts.on('--wxwin=path', - "the installation root for the wxWidgets libraries and headers if not using the system default") {|v| CONFIG['wxwin'] = File.expand_path(v)} + "the installation root for the wxWidgets libraries and headers if not using the system default", + "(use '@system' to force using system default only)") { |v| + if v.downcase == '@system' + CONFIG[WXW_SYS_KEY] = true + CONFIG['wxwin'] = nil + CONFIG['with-wxwin'] = false + else + CONFIG['wxwin'] = File.expand_path(v) + CONFIG[WXW_SYS_KEY] = false + end + } opts.on('--wxxml=path', "the path to the doxygen generated wxWidgets XML interface specs if not using bootstrap") {|v| CONFIG['wxxml'] = File.expand_path(v)} opts.on('--wxwininstdir=path', - "the directory where the wxWidgets dlls are to be installed for wxRuby [#{instance.get_config('wxwininstdir')}]") {|v| CONFIG['wxwininstdir'] = v} + "the directory where the wxWidgets dlls are installed (do not change if not absolutely needed) [#{instance.get_config('wxwininstdir')}]") {|v| CONFIG['wxwininstdir'] = v} opts.on('--with-wxwin', - "build a local copy of wxWidgets for use with wxRuby [false]") {|v| CONFIG['with-wxwin'] = true} + "build a local copy of wxWidgets for use with wxRuby [false]") { |v| + CONFIG['with-wxwin'] = true + CONFIG[WXW_SYS_KEY] = false + } opts.on('--with-debug', "build with debugger support [#{instance.get_config('with-debug')}]") {|v| CONFIG['with-debug'] = true} opts.on('--swig=path', @@ -84,69 +97,64 @@ def self.define(task, args) def self.check instance.init # re-initialize - if Dir[File.join('ext', 'wxruby_*.so')].empty? # Don't check for wxWidgets installation when executed for binary gem install + # should we try to use a system or user defined wxWidgets installation? + if !get_config('with-wxwin') - if !get_config('with-wxwin') - # check if a user defined wxWidgets location is specified or we're using a system standard install - if get_cfg_string('wxwin').empty? - # assume system standard install; will be checked below - set_config('wxwininstdir', get_cfg_string('libdir')) if get_cfg_string('wxwininstdir').empty? - elsif get_cfg_string('wxwininstdir').empty? - if instance.windows? - set_config('wxwininstdir', File.join(get_cfg_string('wxwin'), 'bin')) - else - set_config('wxwininstdir', File.join(get_cfg_string('wxwin'), 'lib')) - end - end - elsif !get_cfg_string('wxwin').empty? - if get_cfg_string('wxwininstdir').empty? - if instance.windows? - set_config('wxwininstdir', File.join(get_cfg_string('wxwin'), 'bin')) - else - set_config('wxwininstdir', File.join(get_cfg_string('wxwin'), 'lib')) - end + # check if a user defined wxWidgets location is specified or should be using a system standard install + if get_cfg_string('wxwin').empty? + # assume/force system standard install; will be checked below + set_config('wxwininstdir', get_cfg_string('libdir')) if get_cfg_string('wxwininstdir').empty? + elsif get_cfg_string('wxwininstdir').empty? # if not explicitly specified derive from 'wxwin' + if instance.windows? + set_config('wxwininstdir', File.join(get_cfg_string('wxwin'), 'bin')) + else + set_config('wxwininstdir', File.join(get_cfg_string('wxwin'), 'lib')) end - else - set_config('wxwininstdir', get_cfg_string('sodir')) if get_cfg_string('wxwininstdir').empty? end - if !get_cfg_string('wxwin').empty? || !get_config('with-wxwin') - # check wxWidgets availability through 'wx-config' command - if instance.check_wx_config - if instance.wx_config("--version") < '3.2.0' - if get_cfg_string('wxwin').empty? && get_cfg_string('wxxml').empty? - # no custom wxWidgets build specified so switch to assuming we should include building wxWidgets ourselves - set_config('with-wxwin', true) - else - # if someone wants to customize they HAVE to do it right - STDERR.puts "ERROR: Incompatible wxWidgets version. wxRuby requires a wxWidgets >= 3.2.0 release." - exit(1) - end - end - else - if get_cfg_string('wxwin').empty? && get_cfg_string('wxxml').empty? - # no custom wxWidgets build specified so switch to assuming we should include building wxWidgets ourselves + # or should we use an embedded (automatically built) wxWidgets installation + else + + set_config('wxwininstdir', instance.ext_dir) + + end + + if !get_cfg_string('wxwin').empty? || !get_config('with-wxwin') + # check wxWidgets availability through 'wx-config' command + if instance.check_wx_config + if instance.wx_config("--version") < '3.2.0' + if get_cfg_string('wxwin').empty? && get_cfg_string('wxxml').empty? && !get_config(WXW_SYS_KEY) + # no custom (or forced system) wxWidgets build specified so switch to assuming we should include building wxWidgets ourselves set_config('with-wxwin', true) else # if someone wants to customize they HAVE to do it right - STDERR.puts "ERROR: Cannot find wxWidgets. wxRuby requires a wxWidgets >= 3.2.0 release." + STDERR.puts "ERROR: Incompatible wxWidgets version. wxRuby requires a wxWidgets >= 3.2.0 release." exit(1) end end - # else we're are assumed to build wxWidgets ourselves so cannot test anything yet - end - - if get_cfg_string('wxxml').empty? && !get_cfg_string('wxwin').empty? - # in case of a custom wxWidgets build and no explicit xml path check if the custom build holds this - xml_path = File.join(get_cfg_string('wxwin'), 'docs', 'doxygen', 'out', 'xml') - # if not there see if the standard setup 'wxw_root/' was used - xml_path = File.join(get_cfg_string('wxwin'), '..', 'docs', 'doxygen', 'out', 'xml') unless File.directory?(xml_path) - if File.directory?(xml_path) && !Dir.glob(File.join(xml_path, '*.xml')).empty? - set_config('wxxml', xml_path) + else + if get_cfg_string('wxwin').empty? && get_cfg_string('wxxml').empty? && !get_config(WXW_SYS_KEY) + # no custom (or forced system) wxWidgets build specified so switch to assuming we should include building wxWidgets ourselves + set_config('with-wxwin', true) + else + # if someone wants to customize they HAVE to do it right + STDERR.puts "ERROR: Cannot find wxWidgets. wxRuby requires a wxWidgets >= 3.2.0 release." + exit(1) end end + # else we're assumed to build wxWidgets ourselves so cannot test anything yet + end + if get_cfg_string('wxxml').empty? && !get_cfg_string('wxwin').empty? + # in case of a custom wxWidgets build and no explicit xml path check if the custom build holds this + xml_path = File.join(get_cfg_string('wxwin'), 'docs', 'doxygen', 'out', 'xml') + # if not there see if the standard setup 'wxw_root/' was used + xml_path = File.join(get_cfg_string('wxwin'), '..', 'docs', 'doxygen', 'out', 'xml') unless File.directory?(xml_path) + if File.directory?(xml_path) && !Dir.glob(File.join(xml_path, '*.xml')).empty? + set_config('wxxml', xml_path) + end end + end end diff --git a/rakelib/gem.rake b/rakelib/gem.rake index 95bf3363..3b9d289c 100644 --- a/rakelib/gem.rake +++ b/rakelib/gem.rake @@ -12,14 +12,10 @@ namespace :wxruby do namespace :gem do - task :srcgem => ['bin:build', WXRuby3::Gem.gem_file('wxruby3', WXRuby3::WXRUBY_VERSION)] + task :srcgem => ['bin:build', WXRuby3::Gem.gem_file] - if WXRuby3.is_bootstrapped? - task :bingem => ['bin:build', File.join(WXRuby3.config.rb_docgen_path, 'window.rb'), WXRuby3::Gem.gem_file('wxruby3', WXRuby3::WXRUBY_VERSION, :bin)] - end - - # this task only exists for installed source gems (where package tasks have been removed) - unless File.file?(File.join(__dir__, 'package.rake')) + # this task only exists for installed (source) gems (where run tasks have been removed) + unless File.file?(File.join(__dir__, 'run.rake')) task :setup => 'config:bootstrap' do |_t, args| begin @@ -51,7 +47,7 @@ namespace :wxruby do $ ./wxruby test - The wxRuby3 sample selector can be run by executing: + The wxRuby3 sample explorer can be run by executing: $ ./wxruby sampler @@ -65,13 +61,14 @@ namespace :wxruby do end # source gem file -file WXRuby3::Gem.gem_file('wxruby3', WXRuby3::WXRUBY_VERSION) => WXRuby3::Gem.manifest do - gemspec = WXRuby3::Gem.define_spec('wxruby3', WXRuby3::WXRUBY_VERSION) do |gem| +file WXRuby3::Gem.gem_file => WXRuby3::Gem.manifest do + gemspec = WXRuby3::Gem.define_spec do |gem| gem.summary = %Q{wxWidgets extension for Ruby} gem.description = %Q{wxRuby3 is a Ruby library providing an extension for the wxWidgets C++ UI framework} gem.email = 'mcorino@m2c-software.nl' gem.homepage = "https://github.com/mcorino/wxRuby3" gem.authors = ['Martin Corino'] + gem.extensions = ['ext/mkrf_conf_ext.rb'] gem.files = WXRuby3::Gem.manifest gem.require_paths = %w{lib} gem.bindir = 'bin' @@ -82,6 +79,7 @@ file WXRuby3::Gem.gem_file('wxruby3', WXRuby3::WXRUBY_VERSION) => WXRuby3::Gem.m gem.add_dependency 'rake' gem.add_dependency 'minitest', '~> 5.15' gem.add_dependency 'test-unit', '~> 3.5' + gem.add_dependency 'plat4m', '~> 1.0' gem.rdoc_options << '--exclude=\\.dll' << '--exclude=\\.so' << @@ -90,17 +88,39 @@ file WXRuby3::Gem.gem_file('wxruby3', WXRuby3::WXRUBY_VERSION) => WXRuby3::Gem.m "'--exclude=lib/wx/(aui|core|grid|html|pg|prt|rbn|rtc|stc|wxruby)/.*'" gem.metadata = { "bug_tracker_uri" => "https://github.com/mcorino/wxRuby3/issues", - "source_code_uri" => "https://github.com/mcorino/wxRuby3", - "documentation_uri" => "https://mcorino.github.io/wxRuby3", "homepage_uri" => "https://github.com/mcorino/wxRuby3", + "documentation_uri" => "https://mcorino.github.io/wxRuby3", + "github_repo" => "https://github.com/mcorino/wxRuby3" } gem.post_install_message = <<~__MSG - The wxRuby3 Gem has been successfully installed. - Before being able to use wxRuby3 you need to run the post-install setup process - by executing the command 'wxruby setup'. + The wxRuby3 Gem has been successfully installed including the 'wxruby' utility. + + In case no suitable binary release package was available for your platform you + will need to run the post-install setup process by executing: + + $ wxruby setup + + To check whether wxRuby3 is ready to run or not you can at any time execute the + following command: + + $ wxruby check + + Run 'wxruby check -h' for more information. + + When the wxRuby3 setup has been fully completed you can start using wxRuby3. + + You can run the regression tests to verify the installation by executing: - Run 'wxruby setup -h' to see information on the available commandline options. + $ wxruby test + + The wxRuby3 sample explorer can be run by executing: + + $ wxruby sampler + + Have fun using wxRuby3. + + Run 'wxruby -h' to see information on the available commands. __MSG end @@ -110,60 +130,70 @@ end desc 'Build wxRuby 3 gem' task :gem => 'wxruby:gem:srcgem' -if WXRuby3.is_bootstrapped? - - # binary gem file - file WXRuby3::Gem.gem_file('wxruby3', WXRuby3::WXRUBY_VERSION, :bin) => WXRuby3::Gem.manifest(:bin) + ['ext/mkrf_conf_bingem.rb'] do - WXRuby3::Install.install_wxwin_shlibs - begin - # create gemspec - gemspec = WXRuby3::Gem.define_spec('wxruby3', WXRuby3::WXRUBY_VERSION, :bin) do |gem| - gem.summary = %Q{wxWidgets extension for Ruby} - gem.description = %Q{wxRuby3 is a Ruby library providing an extension for the wxWidgets C++ UI framework} - gem.email = 'mcorino@m2c-software.nl' - gem.homepage = "https://github.com/mcorino/wxRuby3" - gem.authors = ['Martin Corino'] - gem.files = WXRuby3::Gem.manifest(:bin) - gem.require_paths = %w{lib} - gem.require_paths << 'ext' if WXRuby3.config.get_config('with-wxwin') - gem.bindir = 'bin' - gem.executables = WXRuby3::Bin.binaries - gem.extensions = ['ext/mkrf_conf_bingem.rb'] - gem.required_ruby_version = ">= #{WXRuby3::Config.rb_ver_major}.#{WXRuby3::Config.rb_ver_minor}", - "< #{WXRuby3::Config.rb_ver_major}.#{WXRuby3::Config.rb_ver_minor+1}" - gem.licenses = ['MIT'] - gem.add_dependency 'rake' - gem.add_dependency 'minitest', '~> 5.15' - gem.add_dependency 'test-unit', '~> 3.5' - gem.rdoc_options << '--exclude=\\.dll' << '--exclude=\\.so' - gem.metadata = { - "bug_tracker_uri" => "https://github.com/mcorino/wxRuby3/issues", - "source_code_uri" => "https://github.com/mcorino/wxRuby3", - "documentation_uri" => "https://mcorino.github.io/wxRuby3", - "homepage_uri" => "https://github.com/mcorino/wxRuby3", - } - gem.post_install_message = <<~__MSG - - wxRuby3 has been successfully installed including the 'wxruby' utility. - - You can run the regression tests to verify the installation by executing: - - $ ./wxruby test - - The wxRuby3 sample selector can be run by executing: - - $ ./wxruby sampler - - Have fun using wxRuby3. - __MSG +# these tasks do not exist for installed (source) gems (where run tasks have been removed) +if File.file?(File.join(__dir__, 'run.rake')) + + if WXRuby3.is_bootstrapped? + + namespace :wxruby do + + namespace :gem do + task :binpkg => ['wxruby:build', 'wxruby:doc', 'bin:build', WXRuby3::Gem.bin_pkg_file] + end + + end + + # binary package file + file WXRuby3::Gem.bin_pkg_file => WXRuby3::Gem.bin_pkg_manifest do |t| + WXRuby3::Install.install_wxwin_shlibs + begin + # create bin package + WXRuby3::Gem.build_bin_pkg + ensure + # cleanup + WXRuby3::Install.remove_wxwin_shlibs end - WXRuby3::Gem.build_gem(gemspec) - ensure - WXRuby3::Install.remove_wxwin_shlibs end + + desc 'Build wxRuby 3 binary release package' + task :binpkg => 'wxruby:gem:binpkg' + end - desc 'Build wxRuby 3 binary gem' - task :bingem => 'wxruby:gem:bingem' +else # in case of installed source gem the following tasks exist + + namespace :wxruby do + + namespace :gem do + kwargs = {} + no_prebuilt = false + task :install do |_, args| + argv = args.extras + until argv.empty? + switch = argv.shift + case switch + when '--prebuilt' + kwargs[:prebuilt_only] = true + when '--no-prebuilt' + no_prebuilt = true unless kwargs[:package] + when '--package' + fail "Missing value for '--package' argument for wxruby:gem:install." if argv.empty? + kwargs[:package] = argv.shift + no_prebuilt = false + else + fail "Invalid argument #{switch} for wxruby:gem:install." + end + end + unless no_prebuilt # do not even try to find&install a binary package + if WXRuby3::Gem.install_gem(**kwargs) + # binaries have been installed -> finish install + Rake::Task['wxruby:post:binpkg'].invoke + end + end + end + + end + + end end diff --git a/rakelib/gem.rb b/rakelib/gem.rb index 5875382f..ea423d6d 100644 --- a/rakelib/gem.rb +++ b/rakelib/gem.rb @@ -13,6 +13,13 @@ require 'rubygems/builder' rescue LoadError end +require 'zlib' +require 'tempfile' +require 'json' +require 'uri' +require 'net/https' +require 'fileutils' +require 'digest/sha2' require_relative './lib/config' require_relative './install' @@ -21,60 +28,305 @@ module WXRuby3 module Gem - def self.manifest(gemtype = :src) - # create MANIFEST list with included files - manifest = Rake::FileList.new - manifest.include %w[bin/*] # *nix executables in bin/ - manifest.exclude %w[bin/*.bat] unless WXRuby3.config.windows? - manifest.include %w[assets/**/* lib/**/* samples/**/* tests/**/*] - if gemtype == :bin - if WXRuby3.config.get_config('with-wxwin') - manifest.include "ext/*.#{WXRuby3.config.dll_mask}" - end - manifest.include 'ext/mkrf_conf_bingem.rb' - manifest.include %w[rakelib/prepost.* rakelib/install.rb rakelib/lib/config.rb rakelib/lib/config/**/* rakelib/lib/ext/**/* rakelib/yard/**/*] - manifest.include WXRuby3::BUILD_CFG - else + BINPKG_EXT = '.pkg' + DIGEST_EXT = '.sha' + + class << self + + # Gem helpers + + def manifest + # create MANIFEST list with included files + manifest = Rake::FileList.new + manifest.include %w[bin/*] # *nix executables in bin/ + manifest.exclude %w[bin/*.bat] unless WXRuby3.config.windows? + manifest.include %w[assets/**/* lib/**/* samples/**/* tests/**/*] manifest.exclude "lib/*.#{WXRuby3.config.dll_mask}" - manifest.include 'ext/wxruby3/wxruby.ico', 'ext/wxruby3/swig/**/*', 'ext/wxruby3/include/**/*' + manifest.include 'ext/mkrf_conf_ext.rb', 'ext/wxruby3/wxruby.ico', 'ext/wxruby3/swig/**/*', 'ext/wxruby3/include/**/*' manifest.exclude 'ext/wxruby3/swig/classes/**/*' manifest.include 'rakelib/**/*' manifest.exclude %w[rakelib/run.* rakelib/help.* rakelib/package.* rakelib/memcheck.* rakelib/memcheck/**/*] - manifest.include 'rakefile' + manifest.include %w{LICENSE README.md CREDITS.md INSTALL.md .yardopts} + manifest end - manifest.include %w{LICENSE README.md CREDITS.md INSTALL.md .yardopts} - manifest - end - def self.define_spec(name, version, gemtype = :src, &block) - gemspec = ::Gem::Specification.new(name, version) - if gemtype == :bin - platform = ::Gem::Platform.local.to_s - gemspec.platform = platform + def define_spec(&block) + gemspec = ::Gem::Specification.new('wxruby3', WXRuby3::WXRUBY_VERSION) + gemspec.required_rubygems_version = ::Gem::Requirement.new(">= 0") if gemspec.respond_to? :required_rubygems_version= + block.call(gemspec) if block_given? + gemspec end - gemspec.required_rubygems_version = ::Gem::Requirement.new(">= 0") if gemspec.respond_to? :required_rubygems_version= - block.call(gemspec) if block_given? - gemspec - end - def self.gem_name(name, version, gemtype = :src) - define_spec(name, version, gemtype).full_name - end + def gem_name + define_spec.full_name + end + private :gem_name - def self.gem_file(name, version, gemtype = :src) - File.join('pkg', "#{WXRuby3::Gem.gem_name(name, version, gemtype)}.gem") - end + def gem_file + File.join('pkg', "#{gem_name}.gem") + end + + def build_gem(gemspec) + if defined?(::Gem::Package) && ::Gem::Package.respond_to?(:build) + gem_file_name = ::Gem::Package.build(gemspec) + else + gem_file_name = ::Gem::Builder.new(gemspec).build + end + + FileUtils.mkdir_p('pkg') - def self.build_gem(gemspec) - if defined?(::Gem::Package) && ::Gem::Package.respond_to?(:build) - gem_file_name = ::Gem::Package.build(gemspec) - else - gem_file_name = ::Gem::Builder.new(gemspec).build + FileUtils.mv(gem_file_name, 'pkg') end - FileUtils.mkdir_p('pkg') + # Binary package helpers + + def bin_pkg_manifest + # create MANIFEST list with included files + manifest = Rake::FileList.new + manifest.include "lib/*.#{WXRuby3.config.dll_mask}" + manifest.include 'lib/wx/**/events/*.rb', 'lib/wx/**/ext/*.rb', 'lib/wx/core/font/*.rb' + manifest.include "lib/wx/doc/gen/**/*.rb" + if WXRuby3.config.get_config('with-wxwin') + manifest.include "ext/*.#{WXRuby3.config.dll_mask}" + end + manifest + end + + def make_bin_name + os = WXRuby3.config.sysinfo.os + case os.id + when :windows + "wxruby3_#{os.distro}_ruby#{WXRuby3::Config.rb_ver_major}#{WXRuby3::Config.rb_ver_minor}" + else + "wxruby3_#{os.distro}_#{os.release || '0'}_ruby#{WXRuby3::Config.rb_ver_major}#{WXRuby3::Config.rb_ver_minor}" + end + end + private :make_bin_name + + def bin_pkg_name + gemspec = ::Gem::Specification.new(make_bin_name, WXRuby3::WXRUBY_VERSION) + platform = ::Gem::Platform.new(RB_CONFIG["arch"]) + if platform.os == 'darwin' + # loose the version for darwin kernels as that does not seem to affect wxRuby runtime compatibility + # (until proven otherwise) + platform.version = nil + end + gemspec.platform = platform.to_s + gemspec.full_name + end + private :bin_pkg_name + + def bin_pkg_file + File.join('pkg', bin_pkg_name+BINPKG_EXT) + end + + def build_bin_pkg + # make sure pkg directory exists + FileUtils.mkdir_p('pkg') + + fname = bin_pkg_file + + # package registry and essential config + registry = [] + config = %w{wxwininstdir with-wxwin}.reduce({}) { |h, k| h[k] = WXRuby3.config.get_config(k); h } + # package temp deflate stream + deflate_stream = Tempfile.new(File.basename(fname, '.*'), binmode: true) + begin + # pack binaries into temp deflate stream + bin_pkg_manifest.each do |path| + registry << pack_file(deflate_stream, path) + end + # convert registry and config to deflated json string + registry_json_z = Zlib::Deflate.deflate(registry.to_json) + config_json_z = Zlib::Deflate.deflate(config.to_json) + + # create final package archive + deflate_stream.rewind + digest = Digest::SHA256.new + File.open(fname, 'w', binmode: true) do |fout| + # pack config + data = [config_json_z.size].pack('Q') + digest << data + fout.write(data) + digest << config_json_z + fout.write(config_json_z) + # pack registry + data = [registry_json_z.size].pack('Q') + digest << data + fout.write(data) + digest << registry_json_z + fout.write(registry_json_z) + # pack files + registry.each do |entry| + if entry[2] > 0 + data = deflate_stream.read(entry[2]) + digest << data + fout.write(data) + end + end + end + sha_file = File.join('pkg', bin_pkg_name+DIGEST_EXT) + File.open(sha_file, 'w') { |fsha| fsha << digest.hexdigest! } + ensure + deflate_stream.close(true) + end + end + + def pack_file(os, path) + pack = true + entry = [path, File.stat(path).mode, 0] + unless WXRuby3.config.windows? + if File.symlink?(path) + pack = false + entry << File.readlink(path) + end + end + if pack + offs = os.tell + os.write(Zlib::Deflate.deflate(File.read(path, binmode: true))) + entry[2] = os.tell - offs # packed data size + end + entry + end + private :pack_file + + # Gem installation helpers + + def install_gem(prebuilt_only: false, package: nil) + # check if a user specified binary package is to be used + if package + uri = File.file?(package) ? nil : URI(package) + if uri.nil? || uri.scheme == 'file' + filename = package + if uri + filename = uri.host ? "#{uri.host}:#{uri.path}" : uri.path + filename = nil unless File.file?(filename) + end + if filename + $stdout.puts "Installing user package #{filename}..." + exit(1) unless install_bin_pkg(filename) + $stdout.puts 'Done!' + true + else + $stderr.puts "ERROR: Cannot access file #{package}." + exit(1) + end + elsif uri.scheme == 'http' || uri.scheme == 'https' + # download the binary release package + $stdout.puts "Downloading #{uri.path}..." + filename = File.basename(uri.path) + if WXRuby3.config.download_file(uri.path, filename) + sha_file = File.basename(filename, '.*')+DIGEST_EXT + uri.path = File.join(File.dirname(uri.path), sha_file) + unless WXRuby3.config.download_file(uri.path, sha_file) + $stderr.puts "ERROR: Unable to download digest signature for binary release package : #{package}" + exit(1) + end + exit(1) unless install_bin_pkg(filename) + true + else + $stderr.puts "ERROR: Unable to download binary release package (#{package})!" + exit(1) + end + else + end + # check if there exists a pre-built binary release package for the current platform + elsif has_release_package? + # download the binary release package + $stdout.puts "Downloading #{bin_pkg_url(BINPKG_EXT)}..." + if WXRuby3.config.download_file(bin_pkg_url(BINPKG_EXT), bin_pkg_name+BINPKG_EXT) + unless WXRuby3.config.download_file(bin_pkg_url(DIGEST_EXT), bin_pkg_name+DIGEST_EXT) + $stderr.puts "ERROR: Unable to download digest signature for binary release package : #{bin_pkg_name}" + exit(1) + end + exit(1) unless install_bin_pkg(bin_pkg_name+BINPKG_EXT) + true + else + if prebuilt_only + $stderr.puts "ERROR: Unable to download binary release package (#{bin_pkg_name})!" + exit(1) + end + $stdout.puts "WARNING: Unable to download binary release package (#{bin_pkg_name})! Reverting to source install." + false + end + else + if prebuilt_only + $stderr.puts "ERROR: No binary release package available!" + exit(1) + end + false + end + end + + def bin_pkg_url(ext) + # which package are we looking for + pkg_name = bin_pkg_name + "https://github.com/mcorino/wxRuby3/releases/download/v#{WXRuby3::WXRUBY_VERSION}/#{pkg_name}#{ext}" + end + private :bin_pkg_url + + def has_release_package? + # check if the release package exists on Github + uri = URI(bin_pkg_url(BINPKG_EXT)) + $stdout.print "Checking #{uri.to_s}..." if WXRuby3.config.verbose? + response = Net::HTTP.start('github.com', use_ssl: true) do |http| + request = Net::HTTP::Head.new(uri) + http.request(request) + end + $stdout.puts "response #{response}" if WXRuby3.config.verbose? + # directly found or with redirect + Net::HTTPOK === response || Net::HTTPRedirection === response + end + private :has_release_package? + + def install_bin_pkg(fname) + # first get digest signature (if available) + sha_file = File.join(File.dirname(fname), File.basename(fname, '.*')+DIGEST_EXT) + unless File.file?(sha_file) + $stderr.puts "ERROR: Cannot access package digest signature file : #{sha_file}." + return false + end + sha_sig = File.read(sha_file) + File.open(fname, 'r', binmode: true) do |fin| + # check digest signature + digest = Digest::SHA256.new + while (data = fin.read(1024*1024)) + digest << data + end + if sha_sig != digest.hexdigest! + $stderr.puts 'ERROR: Package digest signature does NOT match.' + return false + end + fin.rewind + # get packed config size + config_size = fin.read(8).unpack('Q').shift + # unpack config + config = JSON.parse!(Zlib::Inflate.inflate(fin.read(config_size))) + # get packed registry size + registry_size = fin.read(8).unpack('Q').shift + # unpack registry + registry = JSON.parse!(Zlib::Inflate.inflate(fin.read(registry_size))) + # unpack and create binaries + registry.each do |entry| + path, mode, size, symlink = entry + if symlink + FileUtils.mkdir_p(File.dirname(symlink)) + FileUtils.ln_s(symlink, path) + else + FileUtils.mkdir_p(File.dirname(path)) + File.open(path, 'w', binmode: true) do |fbin| + fbin << Zlib::Inflate.inflate(fin.read(size)) + end + File.chmod(mode, path) + end + end + # merge config + config.each_pair { |k,v| WXRuby3.config.set_config(k, v) } + end + true + end + private :install_bin_pkg - FileUtils.mv(gem_file_name, 'pkg') end end diff --git a/rakelib/install.rb b/rakelib/install.rb index 0a48bd37..f5f9e88e 100644 --- a/rakelib/install.rb +++ b/rakelib/install.rb @@ -67,7 +67,7 @@ def install_wxwin_shlibs FileUtils.ln_s(File.join('.', File.basename(src_shlib)), File.join('ext', File.basename(shlib))) else FileUtils.cp(shlib, inshlib = File.join('ext', File.basename(shlib))) - unless WXRuby3.config.patch_rpath(inshlib, WXRuby3.config.get_rpath_origin) + unless WXRuby3.config.update_shlib_loadpaths(inshlib) # cleanup and exit FileUtils.rm_f(Dir["ext/*.#{WXRuby3.config.dll_mask}"]) exit(1) @@ -77,14 +77,14 @@ def install_wxwin_shlibs end # prepare wxRuby shared libs Dir["lib/*.#{WXRuby3.config.dll_mask}"].each do |shlib| - unless WXRuby3.config.patch_rpath(shlib, WXRuby3.config.get_rpath_origin, "#{WXRuby3.config.get_rpath_origin}/../ext") + unless WXRuby3.config.update_shlib_loadpaths(shlib) && WXRuby3.config.update_shlib_ruby_libpath(shlib) # cleanup and exit FileUtils.rm_f(Dir["ext/*.#{WXRuby3.config.dll_mask}"]) exit(1) end end (wxwin_inshlibs + Dir["lib/*.#{WXRuby3.config.dll_mask}"]).each do |shlib| - unless WXRuby3.config.update_shlib_loadpaths(shlib, WXRuby3::Install.wxwin_shlibs) + unless WXRuby3.config.update_shlib_wxwin_libpaths(shlib, WXRuby3::Install.wxwin_shlibs) # cleanup and exit FileUtils.rm_f(Dir["ext/*.#{WXRuby3.config.dll_mask}"]) exit(1) diff --git a/rakelib/lib/config.rb b/rakelib/lib/config.rb index 8cda856e..cce3c575 100644 --- a/rakelib/lib/config.rb +++ b/rakelib/lib/config.rb @@ -11,6 +11,7 @@ require 'json' require 'open3' require 'monitor' +require 'plat4m' module FileUtils # add convenience methods @@ -67,7 +68,8 @@ module WXRuby3 'sodir' => '$siterubyverarch', } - CFG_KEYS.concat(%w{wxwin wxxml wxwininstdir with-wxwin with-debug swig doxygen}) + CFG_KEYS.concat(%w{wxwin wxxml wxwininstdir with-wxwin with-debug swig doxygen git}) + WXW_SYS_KEY = 'with-system-wxwin' CONFIG.merge!({ 'wxwin' => ENV['WXWIN'] || '', 'wxxml' => ENV['WXXML'] || '', @@ -280,7 +282,7 @@ def do_run(*cmd, capture: nil) end begin # setup ENV for child execution - ENV.merge!(Config.instance.exec_env) + ENV.update(Config.instance.exec_env) output = `#{cmd.join(' ')}` ensure # restore ENV @@ -350,7 +352,7 @@ def sh(*cmd, fail_on_error: false, **kwargs) def test(*tests, **options) errors = 0 - excludes = (ENV['WXRUBY_TEST_EXCLUDE'] || '').split(';') + excludes = (ENV['WXRUBY_TEST_EXCLUDE'] || '').split(':') tests = Dir.glob(File.join(Config.instance.test_dir, '*.rb')) if tests.empty? tests.each do |test| unless excludes.include?(File.basename(test, '.*')) @@ -381,6 +383,10 @@ def check_tool_pkgs [] end + def download_file(_url, _dest) + raise NoMethodError + end + def install_prerequisites pkg_deps = check_tool_pkgs if get_config('autoinstall') == false @@ -443,16 +449,22 @@ def do_link(_pkg) def get_rpath_origin '' end + protected :get_rpath_origin - def check_rpath_patch + def patch_rpath(_shlib, *) true end + protected :patch_rpath - def patch_rpath(_shlib, *) + def update_shlib_loadpaths(_shlib) + true + end + + def update_shlib_ruby_libpath(_shlib) true end - def update_shlib_loadpaths(_shlib, _deplibs) + def update_shlib_wxwin_libpaths(_shlib, _deplibs) true end @@ -494,15 +506,23 @@ def build_cfg end def save + cfg = WXRuby3::CONFIG.dup + wxw_system = !!cfg.delete(WXW_SYS_KEY) + cfg['wxwin'] = '@system' if wxw_system File.open(build_cfg, 'w') do |f| - f << JSON.pretty_generate(WXRuby3::CONFIG) + f << JSON.pretty_generate(cfg) end end def load if File.file?(build_cfg) File.open(build_cfg, 'r') do |f| - WXRuby3::CONFIG.merge!(JSON.load(f.read)) + cfg = JSON.load(f.read) + if cfg['wxwin'] == '@system' + cfg[WXW_SYS_KEY] = true + cfg.delete('wxwin') + end + WXRuby3::CONFIG.merge!(cfg) end end end @@ -515,10 +535,8 @@ def platform case RUBY_PLATFORM when /mingw/ :mingw - when /cygwin/ - :cygwin - when /netbsd/ - :netbsd + when /freebsd/ + :freebsd when /darwin/ :macosx when /linux/ @@ -538,15 +556,26 @@ def create def initialize @ruby_exe = RB_CONFIG["ruby_install_name"] - @extmk = /extmk\.rb/ =~ $0 - @platform = Config.platform - require File.join(File.dirname(__FILE__), 'config', @platform.to_s) + @sysinfo = Plat4m.current rescue nil + @platform = if @sysinfo + case @sysinfo.os.id + when :darwin + :macosx + when :windows + RUBY_PLATFORM =~ /mingw/ ? :mingw : :unknown + else + @sysinfo.os.id + end + else + :unknown + end + require_relative File.join('config', @platform.to_s) self.class.include(WXRuby3::Config::Platform) init # initialize settings end - attr_reader :ruby_exe, :extmk, :platform, :helper_modules, :helper_inits, :include_modules, :verbosity + attr_reader :ruby_exe, :sysinfo, :platform, :helper_modules, :helper_inits, :include_modules, :verbosity attr_reader :release_build, :debug_build, :verbose_debug, :no_deprecate attr_reader :ruby_cppflags, :ruby_ldflags, :ruby_libs, :extra_cflags, :extra_cppflags, :extra_ldflags, :extra_libs, :cpp_out_flag, :link_output_flag, :obj_ext, :dll_ext, @@ -593,11 +622,6 @@ def init # Extra swig helper files to be built @helper_modules = %w|RubyStockObjects| - # if macosx? - # %w|RubyStockObjects Mac| - # else - # %w|RubyStockObjects| - # end # helper to initialize on startup (stock objects can only be initialized after App creation) @helper_inits = @helper_modules - %w|RubyStockObjects| @@ -640,7 +664,7 @@ def init @obj_ext = RB_CONFIG["OBJEXT"] @dll_ext = RB_CONFIG['DLEXT'] - # Exclude certian classes from being built, even if they are present + # Exclude certain classes from being built, even if they are present # in the configuration of wxWidgets. if ENV['WXRUBY_EXCLUDED'] ENV['WXRUBY_EXCLUDED'].split(",").each { |classname| exclude_module(classname) } @@ -704,16 +728,12 @@ def wx_abi_version @wx_abi_version || '' end - def cygwin? - @platform == :cygwin - end - def mingw? @platform == :mingw end - def netbsd? - @platform == :netbsd + def freebsd? + @platform == :freebsd end def macosx? @@ -725,7 +745,7 @@ def linux? end def windows? - mingw? || cygwin? + mingw? end def ldflags(_target) diff --git a/rakelib/lib/config/cygwin.rb b/rakelib/lib/config/freebsd.rb similarity index 75% rename from rakelib/lib/config/cygwin.rb rename to rakelib/lib/config/freebsd.rb index ba956e6e..fc804a46 100644 --- a/rakelib/lib/config/cygwin.rb +++ b/rakelib/lib/config/freebsd.rb @@ -6,4 +6,4 @@ # wxRuby3 buildtools configuration ### -raise "Cygwin platform is unsupported as yet." +raise "Freebsd platform is unsupported as yet." diff --git a/rakelib/lib/config/linux.rb b/rakelib/lib/config/linux.rb index af7babac..26056051 100644 --- a/rakelib/lib/config/linux.rb +++ b/rakelib/lib/config/linux.rb @@ -7,7 +7,7 @@ ### require_relative './unixish' -require_relative 'pkgman/base' +require_relative 'pkgman/linux' module WXRuby3 @@ -42,6 +42,7 @@ def check_rpath_patch end true end + protected :check_rpath_patch def patch_rpath(shlib, *rpath) if check_rpath_patch @@ -50,6 +51,7 @@ def patch_rpath(shlib, *rpath) end false end + protected :patch_rpath def check_tool_pkgs pkg_deps = super diff --git a/rakelib/lib/config/macosx.rb b/rakelib/lib/config/macosx.rb index b44000eb..a06e03a7 100644 --- a/rakelib/lib/config/macosx.rb +++ b/rakelib/lib/config/macosx.rb @@ -9,6 +9,8 @@ require_relative './unixish' require_relative 'pkgman/macosx' +require 'pathname' + module WXRuby3 module Config @@ -33,12 +35,7 @@ def dll_mask def get_rpath_origin "@loader_path" end - - def check_rpath_patch - # no need to check anything; install_name_tool is part of XCode cmdline tools - # and without these we couldn't build anything - true - end + protected :get_rpath_origin def patch_rpath(shlib, *rpath) # don't leave old rpath-s behind @@ -47,13 +44,44 @@ def patch_rpath(shlib, *rpath) sh("install_name_tool #{rpath.collect {|rp| "-add_rpath '#{rp}'"}.join(' ')} #{shlib} 2>/dev/null", verbose: false) { |_,_| } true end - - def update_shlib_loadpaths(shlib, deplibs) - changes = deplibs.collect { |dl| "-change '#{dl}' '@rpath/#{File.basename(dl)}'"} - sh("install_name_tool #{changes.join(' ')} #{shlib} 2>/dev/null", verbose: false) { |_,_| } + protected :patch_rpath + + # add Ruby library path for wxruby shared libraries + def update_shlib_ruby_libpath(shlib) + # fix lookup for the Ruby shared library + # on MacOSX the Ruby library will be linked with it's full path from the **development** environment + # which is no use after binary deployment so we change that to be relative to the executable's path + # loading the shared libs (which is always going to be the Ruby exe) + + # get the development folder holding ruby lib + ruby_libdir = Pathname.new(RB_CONFIG['libdir']) + # determine the relative path to the lib directory from the executable dir + # (this remains constant for any similar deployed Ruby for this platform) + rel_ruby_libdir = ruby_libdir.relative_path_from(RB_CONFIG['bindir']) + # get the Ruby library name used for linking + ld_ruby_lib = (RB_CONFIG['LIBRUBYARG_SHARED'].split.find { |s| s =~ /^-lruby/ }).sub(/^-l/,'') + # match the full shared library name that will be linked + ruby_so = [RB_CONFIG['LIBRUBY_SO'], RB_CONFIG['LIBRUBY_SONAME'], *RB_CONFIG['LIBRUBY_ALIASES'].split].find do |soname| + soname =~ /^lib#{ld_ruby_lib}\./ + end + # form the full path of the shared Ruby library linked + ruby_lib = File.join(ruby_libdir.to_s, RB_CONFIG['LIBRUBY_SO']) + # change the full path to a path relative to the Ruby executable + sh("install_name_tool -change #{ruby_lib} '@executable_path/#{rel_ruby_libdir.to_s}/#{ruby_so}' #{shlib}") true end + # add deployment lookup paths for wxwidgets shared libraries + def update_shlib_wxwin_libpaths(shlib, deplibs) + if super + changes = deplibs.collect { |dl| "-change '#{dl}' '@rpath/#{File.basename(dl)}'"} + sh("install_name_tool #{changes.join(' ')} #{shlib} 2>/dev/null", verbose: false) { |_,_| } + true + else + false + end + end + def check_tool_pkgs pkg_deps = super # need g++ to build wxRuby3 extensions in any case @@ -104,7 +132,7 @@ def do_link(pkg) private def wx_configure - bash('./configure --disable-optimise --disable-sys-libs --without-liblzma --prefix=`pwd`/install --disable-tests --without-subdirs --disable-debug_info CFLAGS="-Wno-unused-but-set-variable"') + bash('./configure --disable-optimise --disable-sys-libs --without-liblzma --without-regex --prefix=`pwd`/install --disable-tests --without-subdirs --disable-debug_info CFLAGS="-Wno-unused-but-set-variable"') end def wx_make diff --git a/rakelib/lib/config/mingw.rb b/rakelib/lib/config/mingw.rb index 6c61b478..2f789800 100644 --- a/rakelib/lib/config/mingw.rb +++ b/rakelib/lib/config/mingw.rb @@ -171,7 +171,7 @@ def download_and_install(url, zip, exe, unpack_to=nil) mkdir(tmp_tool_root) unless File.directory?(tmp_tool_root) # download chdir(tmp_tool_root) do - unless sh("curl -L #{url} --output #{zip}") + unless download_file(url, zip) STDERR.puts "ERROR: Failed to download installation package for #{exe}" exit(1) end diff --git a/rakelib/lib/config/pkgman/arch.rb b/rakelib/lib/config/pkgman/arch.rb deleted file mode 100644 index e8bb4ce7..00000000 --- a/rakelib/lib/config/pkgman/arch.rb +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2023 M.J.N. Corino, The Netherlands -# -# This software is released under the MIT license. - -### -# wxRuby3 buildtools platform pkg manager for Arch Linux type systems -### - -module WXRuby3 - - module Config - - module Platform - - module PkgManager - - PLATFORM_DEPS = %w[pkg-config gtk3 webkit2gtk gspell libunwind gstreamer curl libsecret libnotify libpng12] - - class << self - - private - - def do_install(distro, pkgs) - run_pacman(make_install_cmd(pkgs)) - end - - def add_platform_pkgs(pkgs) - if pkgs.include?('g++') - pkgs.delete('g++') - pkgs << 'gcc' - end - # find pkgs we need - PLATFORM_DEPS.inject(pkgs) { |list, pkg| list << pkg unless system("pacman -Qq #{pkg} >/dev/null 2>&1"); list } - end - - def run_pacman(cmd) - run("pacman -q --noconfirm #{cmd}") - end - - def make_install_cmd(pkgs) - # create install command - "-S --needed #{ pkgs.join(' ') }" - end - - end - - end - - end - - end - -end diff --git a/rakelib/lib/config/pkgman/debian.rb b/rakelib/lib/config/pkgman/debian.rb deleted file mode 100644 index 3b590b4f..00000000 --- a/rakelib/lib/config/pkgman/debian.rb +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2023 M.J.N. Corino, The Netherlands -# -# This software is released under the MIT license. - -### -# wxRuby3 buildtools platform pkg manager for Debian type systems -### - -module WXRuby3 - - module Config - - module Platform - - module PkgManager - - PLATFORM_DEPS = %w[libgtk-3-dev libwebkit2gtk-4.0-dev libgspell-1-dev libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcurl4-openssl-dev libsecret-1-dev libnotify-dev] - - class << self - - private - - def do_install(distro, pkgs) - run_apt(make_install_cmd(pkgs)) - end - - def add_platform_pkgs(pkgs) - # get architecture - arch = expand('dpkg --print-architecture').strip - # find pkgs we need - PLATFORM_DEPS.inject(pkgs) do |list, pkg| - list << pkg unless (system("dpkg-query -s \"#{pkg}:#{arch}\" >/dev/null 2>&1") || - system("dpkg-query -s \"#{pkg}:all\" >/dev/null 2>&1") || - (expand("dpkg-query -s \"#{pkg}\" 2>/dev/null").strip =~ /Architecture: (#{arch}|all)/)) - list - end - end - - def run_apt(cmd) - run("DEBIAN_FRONTEND=noninteractive apt-get -q -o=Dpkg::Use-Pty=0 #{cmd}") - end - - def update_pkgs - run_apt('update') - end - - def make_install_cmd(pkgs) - # update cache - update_pkgs - # get list of available packages - apt_cache = `apt-cache pkgnames`.chomp.split("\n").collect { |s| s.strip } - # remove un-installables - pkgs = pkgs.select { |pkg| apt_cache.include?(pkg) } - # create install command - "install -y #{ pkgs.join(' ') }" - end - - end - - end - - end - - end - -end diff --git a/rakelib/lib/config/pkgman/base.rb b/rakelib/lib/config/pkgman/linux.rb similarity index 56% rename from rakelib/lib/config/pkgman/base.rb rename to rakelib/lib/config/pkgman/linux.rb index 7a87380d..2bde10c1 100644 --- a/rakelib/lib/config/pkgman/base.rb +++ b/rakelib/lib/config/pkgman/linux.rb @@ -14,6 +14,17 @@ module Platform module PkgManager + PLATFORM_DEPS = { + debian: %w[libgtk-3-dev libwebkit2gtk-4.0-dev libgspell-1-dev libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libcurl4-openssl-dev libsecret-1-dev libnotify-dev], + rhel: %w[expat-devel findutils gspell-devel gstreamer1-plugins-base-devel gtk3-devel libcurl-devel libjpeg-devel libnotify-devel libpng-devel libSM-devel libsecret-devel libtiff-devel SDL-devel webkit2gtk4.1-devel zlib-devel], + suse: %w[gtk3-devel webkit2gtk3-devel gspell-devel gstreamer-devel gstreamer-plugins-base-devel libcurl-devel libsecret-devel libnotify-devel libSDL-devel zlib-devel libjpeg-devel libpng-devel], + arch: %w[pkg-config gtk3 webkit2gtk gspell libunwind gstreamer curl libsecret libnotify libpng12] + } + PLATFORM_ALTS = { + suse: { 'g++' => 'gcc-c++' }, + rhel: { 'git' => 'git-core' }, + arch: { 'g++' => 'gcc' } + } MIN_GENERIC_PKGS = %w[gtk3-devel patchelf g++ make git webkit2gtk3-devel gspell-devel gstreamer-devel gstreamer-plugins-base-devel libcurl-devel libsecret-devel libnotify-devel libSDL-devel zlib-devel] class << self @@ -21,25 +32,21 @@ class << self def install(pkgs) # do we need to install anything? if !pkgs.empty? || builds_wxwidgets? - # determine the linux distro specs - distro = get_distro - begin - # load distro installation support - require_relative "./#{distro[:type]}" - rescue LoadError + # check linux distro compatibility + unless no_autoinstall? || pkgman # do we need to build wxWidgets? pkgs.concat(MIN_GENERIC_PKGS) if builds_wxwidgets? $stderr.puts <<~__ERROR_TXT - ERROR: Do not know how to install required packages for distro type '#{distro[:type]}'. + ERROR: Do not know how to install required packages for distro type '#{WXRuby3.config.sysinfo.os.variant}'. - Make sure the following packages (or equivalent) are installed and than try again with `WXRUBY_NO_AUTOINSTALL=1`: + Make sure the following packages (or equivalent) are installed and than try again with `--no-autoinstall`: #{pkgs.join(', ')} __ERROR_TXT exit(1) end # can we install? unless no_autoinstall? || has_sudo? || is_root? - $stderr.puts 'ERROR: Cannot check for or install required packages. Please install sudo or run as root and try again.' + $stderr.puts 'ERROR: Cannot check for or install required packages. Please install sudo and try again.' exit(1) end # do we need to build wxWidgets? @@ -60,7 +67,7 @@ def install(pkgs) exit(1) end # do the actual install - unless do_install(distro, pkgs) + unless run(pkgman.make_install_command(*pkgs)) $stderr.puts <<~__ERROR_TXT ERROR: Failed to install all or some of the following required software packages: #{pkgs.join(', ')} @@ -78,6 +85,23 @@ def install(pkgs) private + def pkgman + @pkgman ||= WXRuby3.config.sysinfo.os.pkgman + end + + def platform_pkgs + PLATFORM_DEPS[WXRuby3.config.sysinfo.os.variant.to_sym] || [] + end + + def add_platform_pkgs(pkgs) + # transform any platform specific package alternatives + (PLATFORM_ALTS[WXRuby3.config.sysinfo.os.variant.to_sym] || {}).each_pair do |org, alt| + pkgs << alt if pkgs.delete(org) + end + # add any other platform specific package dependencies + pkgs.concat(pkgman.select_uninstalled(platform_pkgs)) + end + def builds_wxwidgets? Config.get_config('with-wxwin') && Config.get_cfg_string('wxwin').empty? end @@ -100,8 +124,8 @@ def is_root? def run(cmd) $stdout.print "Running #{cmd}..." - rc = WXRuby3.config.sh("#{is_root? ? '' : 'sudo '}#{cmd}") - $stderr.puts (rc ? 'done!' : 'FAILED!') + rc = WXRuby3.config.sh(cmd) + $stderr.puts(rc ? 'done!' : 'FAILED!') rc end @@ -109,55 +133,6 @@ def expand(cmd) `#{is_root? ? '' : 'sudo '}#{cmd}` end - def get_distro - if File.file?('/etc/os-release') # works with most (if not all) recent distro releases - data = File.readlines('/etc/os-release').reduce({}) do |hash, line| - val, var = line.split('=') - hash[val] = var.strip.gsub(/(\A")|("\Z)/, '') - hash - end - { - type: if data['ID_LIKE'] - data['ID_LIKE'].split.first.to_sym - elsif File.file?('/etc/redhat-release') - :rhel - elsif File.file?('/etc/SUSE-brand') || File.file?('/etc/SuSE-release') - :suse - elsif File.file?('/etc/debian_version') - :debian - else - data['ID'].to_sym - end, - distro: data['ID'].downcase, - release: data['VERSION_ID'] - } - elsif File.file?('/etc/redhat-release') - data = File.read('/etc/redhat-release').strip - { - type: :rhel, - distro: data.split.shift.downcase, - release: data =~ /\d+(\.\d+)*/ ? $~[0] : '' - } - elsif File.file?('/etc/SUSE-brand') || File.file?('/etc/SuSE-release') - data = File.readlines(File.file?('/etc/SUSE-brand') ? '/etc/SUSE-brand' : '/etc/SuSE-release') - { - type: :suse, - distro: data.shift.split.shift.downcase, - release: (data.find { |s| s.strip =~ /\AVERSION\s*=/ } || '').split('=').last || '' - } - elsif File.file?('/etc/debian_version') - { - type: :debian, - distro: 'generic', - release: File.read('/etc/debian_version').strip - } - else - { - type: :unknown - } - end - end - end end diff --git a/rakelib/lib/config/pkgman/macosx.rb b/rakelib/lib/config/pkgman/macosx.rb index 0e8fb7c3..97feaa96 100644 --- a/rakelib/lib/config/pkgman/macosx.rb +++ b/rakelib/lib/config/pkgman/macosx.rb @@ -18,7 +18,7 @@ class << self def install(pkgs) # do we need to install anything? - if !pkgs.empty? + unless pkgs.empty? # can we install XCode commandline tools? unless no_autoinstall? || !pkgs.include?('xcode') || has_sudo? || is_root? STDERR.puts 'ERROR: Cannot check for or install required packages. Please install sudo or run as root and try again.' @@ -50,18 +50,20 @@ def install(pkgs) private + def pkgman + @pkgman ||= WXRuby3.config.sysinfo.os.pkgman + end + def do_install(pkgs) rc = true # first see if we need to install XCode commandline tools if pkgs.include?('xcode') pkgs.delete('xcode') - rc = run('xcode-select --install') + rc = auth_run('xcode-select --install') end - # now check if we need any other packages (which need Homebrew or MacPorts) + # now check if we need any other packages if rc && !pkgs.empty? - # Has Ruby been installed through MacPorts? - if has_macports? && - (ruby_info = expand('port -q installed installed').strip.split("\n").find { |ln| ln.strip =~ /\Aruby\d+\s/ }) + if pkgman.macports? # this is really crap; with MacPorts we need to install swig-ruby instead of simply swig # which for whatever nonsensical reason will pull in another (older) Ruby version (probably 2.3 or such) @@ -70,59 +72,15 @@ def do_install(pkgs) pkgs.delete('swig') pkgs << 'swig-ruby' end - # in case MacPorts was installed with root privileges this install would also have to be run - # with root privileges (otherwise it would fail early on with access problems) so we can - # just run without sudo as we either have root privileges for root-installed MacPorts or - # we're running without root privileges for user-installed MacPorts - pkgs.each { |pkg| rc &&= sh("port install #{pkg}") } - - # or are we running without root privileges and have Homebrew installed? - # (Ruby may be installed using Homebrew itself or using a Ruby version manager like RVM) - elsif !is_root? && has_homebrew? - pkgs.each { |pkg| rc &&= sh("brew install #{pkg}") } - - # or do we have MacPorts (running either privileged or not) and - # a Ruby installed using a Ruby version manager. - elsif has_macports? - - # same crap as above - if pkgs.include?('swig') - pkgs.delete('swig') - pkgs << 'swig-ruby' - end - # in case MacPorts was installed with root privileges this install would also have to be run - # with root privileges (otherwise it would fail early on with access problems) so we can - # just run without sudo as we either have root privileges for root-installed MacPorts or - # we're running without root privileges for user-installed MacPorts - pkgs.each { |pkg| rc &&= sh("port install #{pkg}") } - - else - if has_homebrew? || is_root? - $stderr.puts <<~__ERROR_TXT - ERROR: Unsupported Ruby installation. wxRuby3 can only be installed for Ruby with root privileges - in case Ruby was installed with MacPorts. Homebrew should not be run with root privileges. - - Re-install a supported Ruby setup and try again. - __ERROR_TXT - else - $stderr.puts <<~__ERROR_TXT - ERROR: Unsupported Ruby installation. wxRuby3 requires either a MacPorts installed Ruby version - or a non-privileged Ruby installation and have Homebrew installed. - - Install either Homebrew or MacPorts and try again. - __ERROR_TXT - end - exit(1) end + + # actually install packages + pkgs.each { |pkg| rc &&= run(pkgman.make_install_command(pkg)); break unless rc } end rc end - def builds_wxwidgets? - Config.get_config('with-wxwin') && Config.get_cfg_string('wxwin').empty? - end - def no_autoinstall? Config.get_config('autoinstall') == false end @@ -132,46 +90,27 @@ def wants_autoinstall? end def has_sudo? - system('command -v sudo > /dev/null') + WXRuby3.config.sysinfo.os.has_sudo? end def is_root? - if @is_root.nil? - @is_root = (`id -u 2>/dev/null`.chomp == '0') - end - @is_root - end - - def has_macports? - if @has_macports.nil? - @has_macports = system('command -v port>/dev/null') - end + WXRuby3.config.sysinfo.os.is_root? end - def has_homebrew? - if @has_homebrew.nil? - @has_homebrew = system('command -v brew>/dev/null') - end - end - - def run(cmd) + def auth_run(cmd) $stdout.print "Running #{cmd}..." rc = WXRuby3.config.sh("#{is_root? ? '' : 'sudo '}#{cmd}") - $stderr.puts (rc ? 'done!' : 'FAILED!') + $stderr.puts(rc ? 'done!' : 'FAILED!') rc end - def sh(*cmd, title: nil) + def run(*cmd, title: nil) $stdout.print(title ? title : "Running #{cmd}...") rc = WXRuby3.config.sh(*cmd) - $stderr.puts (rc ? 'done!' : 'FAILED!') + $stderr.puts(rc ? 'done!' : 'FAILED!') rc end - def expand(cmd) - WXRuby3.config.expand(cmd) - end - end end diff --git a/rakelib/lib/config/pkgman/rhel.rb b/rakelib/lib/config/pkgman/rhel.rb deleted file mode 100644 index d048f75e..00000000 --- a/rakelib/lib/config/pkgman/rhel.rb +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2023 M.J.N. Corino, The Netherlands -# -# This software is released under the MIT license. - -### -# wxRuby3 buildtools platform pkg manager for RHEL type systems -### - -module WXRuby3 - - module Config - - module Platform - - module PkgManager - - PLATFORM_DEPS = %w[expat-devel findutils gspell-devel gstreamer1-plugins-base-devel gtk3-devel libcurl-devel libjpeg-devel libnotify-devel libpng-devel libSM-devel libsecret-devel libtiff-devel SDL-devel webkit2gtk4.1-devel zlib-devel] - - class << self - - private - - def do_install(distro, pkgs) - run_dnf(make_install_cmd(pkgs)) - end - - def add_platform_pkgs(pkgs) - # add build tools - if pkgs.include?('git') - pkgs.delete('git') - pkgs << 'git-core' - end - # find pkgs we need - PLATFORM_DEPS.inject(pkgs) { |list, pkg| list << pkg unless system("dnf list installed #{pkg} >/dev/null 2>&1"); list } - end - - def run_dnf(cmd) - run("dnf #{cmd}") - end - - def make_install_cmd(pkgs) - # create install command - "install -y #{ pkgs.join(' ') }" - end - - end - - end - - end - - end - -end diff --git a/rakelib/lib/config/pkgman/suse.rb b/rakelib/lib/config/pkgman/suse.rb deleted file mode 100644 index badf6958..00000000 --- a/rakelib/lib/config/pkgman/suse.rb +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2023 M.J.N. Corino, The Netherlands -# -# This software is released under the MIT license. - -### -# wxRuby3 buildtools platform pkg manager for SuSE type systems -### - -module WXRuby3 - - module Config - - module Platform - - module PkgManager - - PLATFORM_DEPS = %w[gtk3-devel webkit2gtk3-devel gspell-devel gstreamer-devel gstreamer-plugins-base-devel libcurl-devel libsecret-devel libnotify-devel libSDL-devel zlib-devel libjpeg-devel libpng-devel] - - class << self - - private - - def do_install(distro, pkgs) - run_zypper(make_install_cmd(pkgs)) - end - - def add_platform_pkgs(pkgs) - # add build tools - if pkgs.include?('g++') - pkgs.delete('g++') - pkgs << 'gcc-c++' - end - # find pkgs we need - PLATFORM_DEPS.inject(pkgs) { |list, pkg| list << pkg unless system("rpm -q --whatprovides #{pkg} >/dev/null 2>&1"); list } - end - - def run_zypper(cmd) - run("zypper -t -i #{cmd}") - end - - def make_install_cmd(pkgs) - # create install command - "install -y #{ pkgs.join(' ') }" - end - - end - - end - - end - - end - -end diff --git a/rakelib/lib/config/unixish.rb b/rakelib/lib/config/unixish.rb index b6aed2ba..dd9d4546 100644 --- a/rakelib/lib/config/unixish.rb +++ b/rakelib/lib/config/unixish.rb @@ -66,6 +66,12 @@ def check_tool_pkgs def get_rpath_origin "$ORIGIN" end + protected :get_rpath_origin + + # add deployment lookup paths for wxruby shared libraries + def update_shlib_loadpaths(shlib) + WXRuby3.config.patch_rpath(shlib, WXRuby3.config.get_rpath_origin, "#{WXRuby3.config.get_rpath_origin}/../ext") + end def expand(cmd) STDERR.puts "> sh: #{cmd}" if verbose? @@ -74,6 +80,10 @@ def expand(cmd) s end + def download_file(url, dest) + sh("curl -L #{url} --output #{dest}") + end + private def wx_checkout @@ -101,7 +111,7 @@ def wx_checkout end def wx_configure - bash('./configure --prefix=`pwd`/install --disable-tests --without-subdirs --disable-debug_info') + bash('./configure --prefix=`pwd`/install --disable-tests --without-subdirs --without-regex --disable-debug_info') end def wx_make diff --git a/rakelib/lib/config/netbsd.rb b/rakelib/lib/config/unknown.rb similarity index 53% rename from rakelib/lib/config/netbsd.rb rename to rakelib/lib/config/unknown.rb index 45201fc3..f650d727 100644 --- a/rakelib/lib/config/netbsd.rb +++ b/rakelib/lib/config/unknown.rb @@ -3,7 +3,8 @@ # This software is released under the MIT license. ### -# wxRuby3 buildtools configuration +# wxRuby3 buildtools configuration fallback ### -raise "NETBSD platform is unsupported as yet." +$stderr.puts 'ERROR: Unsupported Ruby platform!' +exit(1) diff --git a/rakelib/lib/director/aui_manager.rb b/rakelib/lib/director/aui_manager.rb index 0d9d395f..008bda8e 100644 --- a/rakelib/lib/director/aui_manager.rb +++ b/rakelib/lib/director/aui_manager.rb @@ -53,7 +53,7 @@ class WXRubyAuiManager : public wxAuiManager m_actionWindow = NULL; m_hoverButton = NULL; m_art = new wxAuiDefaultDockArt; - m_hintWnd = NULL; + #{Config.instance.wx_version < '3.3.0' ? 'm_hintWnd = NULL;' : ''} m_flags = wxAUI_MGR_DEFAULT; m_hasMaximized = false; m_dockConstraintX = 0.3; diff --git a/rakelib/lib/director/secret_store.rb b/rakelib/lib/director/secret_store.rb new file mode 100644 index 00000000..205f455a --- /dev/null +++ b/rakelib/lib/director/secret_store.rb @@ -0,0 +1,117 @@ +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. + +### +# wxRuby3 wxWidgets interface director +### + +module WXRuby3 + + class Director + + class SecretStore < Director + + def setup + super + spec.items << 'wxSecretValue' + spec.gc_as_untracked # don't even track SecretStore and SecretValue objects + # there is no possibility of SecretStore derivatives + # not least because wxRuby only ever allows a single global SecretStore + spec.disable_proxies + spec.make_abstract 'wxSecretStore' + + spec.include 'ruby/encoding.h' + + spec.ignore 'wxSecretValue::GetAsString', + 'wxSecretValue::GetSize', + 'wxSecretValue::GetData', + 'wxSecretValue::Wipe', + 'wxSecretValue::WipeString', + 'wxSecretValue::wxSecretValue(const wxString&)', + 'wxSecretValue::wxSecretValue(size_t, const void *)' + spec.regard 'wxSecretValue::wxSecretValue()', + 'wxSecretValue::wxSecretValue(const wxSecretValue&)', + regard_doc: false + # customize string arg ctor + spec.add_extend_code 'wxSecretValue', <<~__HEREDOC + wxSecretValue(VALUE secret_string) + { + if (RTEST(secret_string) && TYPE(secret_string) == T_STRING) + { + if (ENCODING_GET(secret_string) == rb_utf8_encindex()) + { + return new wxSecretValue(RSTR_TO_WXSTR(secret_string)); + } + else + { + return new wxSecretValue(RSTRING_LEN(secret_string), (void*)StringValuePtr(secret_string)); + } + } + else + { + rb_raise(rb_eArgError, "Expected String or Wx::SecretValue for #0"); + } + } + __HEREDOC + + # customize GetData + spec.map 'const void*' => 'String', swig: false do + map_out code: '' + end + spec.add_extend_code 'wxSecretValue', <<~__HEREDOC + VALUE get_data() + { + size_t sz = $self->GetSize(); + const void* data = $self->GetData(); + return rb_enc_str_new(static_cast(data), sz, rb_ascii8bit_encoding()); + } + + VALUE get_as_string() + { + size_t sz = $self->GetSize(); + const void* data = $self->GetData(); + return rb_utf8_str_new(static_cast(data), sz); + } + __HEREDOC + + # customize GetDefault + spec.ignore 'wxSecretStore::GetDefault', ignore_doc: false + spec.add_extend_code 'wxSecretStore', <<~__HEREDOC + static VALUE get_default() + { + wxSecretStore* result = new wxSecretStore(wxSecretStore::GetDefault()); + return SWIG_NewPointerObj(result, SWIGTYPE_p_wxSecretStore, SWIG_POINTER_OWN); + } + __HEREDOC + spec.map 'wxString *errmsg' => 'String' do + map_in ignore: true, temp: 'wxString tmp', code: '$1 = &tmp;' + map_argout code: <<~__CODE + if ($result == Qfalse) + { + $result = SWIG_Ruby_AppendOutput($result, WXSTR_TO_RSTR(tmp$argnum)); + } + __CODE + end + spec.map 'wxString& username' => 'String' do + map_in ignore: true, temp: 'wxString tmp', code: '$1 = &tmp;' + map_argout code: <<~__CODE + if ($result == Qtrue) + { + $result = SWIG_Ruby_AppendOutput($result, WXSTR_TO_RSTR(tmp$argnum)); + } + __CODE + end + # the type matching of the username argument is tricky here since there only is the const difference + # have to explicitly overrule here for Save() incl. explicitly negating the argout mapping + spec.map 'const wxString& username' => 'String' do + map_in temp: 'wxString tmp', code: 'tmp = RSTR_TO_WXSTR($input); $1 = &tmp;' + map_argout by_ref: true + end + end + + end # class SecretStore + + end # class Director + +end # module WXRuby3 diff --git a/rakelib/lib/generate/doc.rb b/rakelib/lib/generate/doc.rb index c8fe645a..f50b9ee6 100644 --- a/rakelib/lib/generate/doc.rb +++ b/rakelib/lib/generate/doc.rb @@ -713,9 +713,9 @@ def run gen_class_doc(fdoc) unless no_gen?(:classes) end package.all_modules.each do |_| + mod_indent -= 1 fdoc.puts fdoc.iputs('end', mod_indent) - mod_indent -= 1 end end end diff --git a/rakelib/lib/generate/doc/secret_store.yaml b/rakelib/lib/generate/doc/secret_store.yaml new file mode 100644 index 00000000..0f8d3ee7 --- /dev/null +++ b/rakelib/lib/generate/doc/secret_store.yaml @@ -0,0 +1,55 @@ +--- +:wxSecretStore: + :detail: + :pre: + :programlisting: + - :pattern: !ruby/regexp /\!store\..*Save.*username.*password\)/ + :replace: | + + ```ruby + store = Wx::SecretStore.get_default + rc, err = store.ok? + if rc + unless store.save('MyApp/MyService', username, password) + Wx.log_warning('Failed to save credentials to the system secret store.') + end + else + Wx.log_warning("This system doesn't support storing passwords securely (#{err}).") + end + ``` + - :pattern: !ruby/regexp /store\..*Load.*username.*password\)/ + :replace: | + + ```ruby + store = Wx::SecretStore.get_default + rc, _ = store.ok? + if rc + password = Wx::SecretValue.new + rc, username = store.load('MyApp/MyService', password) + if rc + # ... use the password ... + end + end + ``` +:wxSecretStore.IsOk: + :brief: + :replace: + :text: | + Check if this object can actually be used. + Returns true if the object can be used. + Returns false and an error message describing the reason if not. + +:wxSecretStore.Load: + :detail: + :post: + - :pattern: !ruby/regexp /Otherwise\s+the\s+function.*arguments\./ + :subst: | + Otherwise the function returns true and the username and updates the provided password argument. + +:wxSecretValue: + :detail: + :post: + - :pattern: !ruby/regexp /\s+\Z/ + :subst: | + + @note Due to a bug using binary secrets will not work for WXGTK wxWidgets<=3.2.4 (WXOSX and WXMSW work fine). This has been fixed for later versions. diff --git a/rakelib/lib/specs/interfaces.rb b/rakelib/lib/specs/interfaces.rb index aa2e0335..e56ba944 100644 --- a/rakelib/lib/specs/interfaces.rb +++ b/rakelib/lib/specs/interfaces.rb @@ -233,6 +233,7 @@ module WXRuby3 Director.Spec(pkg, 'wxPersistenceManager', requirements: %w[USE_CONFIG]) Director.Spec(pkg, 'wxPersistentObject', requirements: %w[USE_CONFIG]) Director.Spec(pkg, 'wxPersistentWindow', requirements: %w[USE_CONFIG]) + Director.Spec(pkg, 'wxSecretStore', requirements: %w[USE_SECRETSTORE]) } Director.Package('Wx::PRT', 'USE_PRINTING_ARCHITECTURE') do |pkg| diff --git a/rakelib/lib/typemap/common.rb b/rakelib/lib/typemap/common.rb index 2e88fe58..7e1f1800 100644 --- a/rakelib/lib/typemap/common.rb +++ b/rakelib/lib/typemap/common.rb @@ -262,6 +262,16 @@ module Common end + # output typemaps for common value objects like wxPoint, wxSize, wxRect and wxRealPoint, + # making sure to ALWAYS create managed copies + %w[Point Size Rect RealPoint].each do |klass| + map "const wx#{klass}&", "const wx#{klass}*", as: "Wx::#{klass}" do + map_out code: <<~__CODE + $result = SWIG_NewPointerObj((new wx#{klass}(*static_cast< const wx#{klass}* >($1))), SWIGTYPE_p_wx#{klass}, SWIG_POINTER_OWN); + __CODE + end + end + # Integer <> wxItemKind type mappings map 'wxItemKind' => 'Integer' do diff --git a/rakelib/prepost.rake b/rakelib/prepost.rake index bd2bf785..cb1d6e8b 100644 --- a/rakelib/prepost.rake +++ b/rakelib/prepost.rake @@ -29,14 +29,21 @@ namespace 'wxruby' do $stdout.puts 'done!' if WXRuby3.config.run_silent? # cleanup rm_rf('rakelib', verbose: !WXRuby3.config.run_silent?) + rm_f('Rakefile', verbose: !WXRuby3.config.run_silent?) + rm_f('ext/mkrf_conf_ext.rb', verbose: !WXRuby3.config.run_silent?) rm_rf('ext/wxruby3', verbose: !WXRuby3.config.run_silent?) WXRuby3.config.cleanup_bootstrap File.open(File.join(WXRuby3::Config.wxruby_root, 'ext', 'wxruby.setup.done'), 'w') { |f| f << '1' } end - task :bingem => 'gem:install' do + task :binpkg => 'gem:install' do # cleanup rm_rf('rakelib') + rm_f('Rakefile') + rm_f('ext/mkrf_conf_ext.rb') + rm_rf('ext/wxruby3') + rm_f('*.pkg') + rm_f('*.sha') File.open(File.join(WXRuby3::Config.wxruby_root, 'ext', 'wxruby.setup.done'), 'w') { |f| f << '1' } end diff --git a/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css b/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css index e4743239..eac76bb8 100644 --- a/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +++ b/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css @@ -90,4 +90,8 @@ div.wxrb-logo table td { #toc { position: fixed; + overflow-y: scroll; + overflow-x: hidden; + top: 1em; + bottom: 0; } diff --git a/samples/sampler/sample.rb b/samples/sampler/sample.rb index 83737c3b..98383a65 100644 --- a/samples/sampler/sample.rb +++ b/samples/sampler/sample.rb @@ -6,6 +6,8 @@ # wxRuby3 sampler application ### +require 'set' + module WxRuby ART_FOLDER = File.join(__dir__, '..', 'art') diff --git a/tests/lib/wxapp_runner.rb b/tests/lib/wxapp_runner.rb index ccc37d5c..b287ed25 100644 --- a/tests/lib/wxapp_runner.rb +++ b/tests/lib/wxapp_runner.rb @@ -67,7 +67,7 @@ def initialize(*args) class TestCase def self.is_ci_build? - !!ENV['GITHUB_ACTION'] + (ENV['GITHUB_ACTION'] || ENV['CI']) end def is_ci_build? diff --git a/tests/test_config.rb b/tests/test_config.rb index 0d2308b2..796054ad 100644 --- a/tests/test_config.rb +++ b/tests/test_config.rb @@ -203,13 +203,16 @@ def run_auto_accessor_tests(cfg) def run_env_var_tests(cfg) # by default expansion is on + # Cirrus CI Linux builds run in privileged container without proper user env + has_user = Wx::PLATFORM == 'WXMSW' || ENV['USER'] + # add a number of entries for env var in new group 'Environment' cfg['/Environment/HOME'] = '$HOME' - cfg['Environment'].USER = Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}' + cfg['Environment'].USER = Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}' if has_user cfg['/Environment/PATH'] = '$(PATH)' assert_equal(ENV['HOME'], cfg.Environment['HOME']) - assert_equal(ENV[Wx::PLATFORM == 'WXMSW' ? 'USERNAME' : 'USER'], cfg['/Environment/USER']) + assert_equal(ENV[Wx::PLATFORM == 'WXMSW' ? 'USERNAME' : 'USER'], cfg['/Environment/USER']) if has_user assert_equal(ENV['PATH'], cfg.Environment.PATH) # test escaping @@ -225,9 +228,9 @@ def run_env_var_tests(cfg) assert_equal('${NonExistingLongNonsenseVariable}', cfg.Environment['NONSENSE']) - cfg['/Environment/MULTIPLE'] = "$HOME / #{Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}'}" + cfg['/Environment/MULTIPLE'] = "$HOME / #{Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}'}" if has_user - assert_equal("#{ENV['HOME']} / #{Wx::PLATFORM == 'WXMSW' ? ENV['USERNAME'] : ENV['USER']}", cfg.Environment['MULTIPLE']) + assert_equal("#{ENV['HOME']} / #{Wx::PLATFORM == 'WXMSW' ? ENV['USERNAME'] : ENV['USER']}", cfg.Environment['MULTIPLE']) if has_user # disable env var expansion cfg.expand_env_vars = false diff --git a/tests/test_secret_store.rb b/tests/test_secret_store.rb new file mode 100644 index 00000000..82e521ea --- /dev/null +++ b/tests/test_secret_store.rb @@ -0,0 +1,81 @@ +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. + +require_relative './lib/wxapp_runner' + +require 'digest' + +class TestSecretStore < Test::Unit::TestCase + + unless is_ci_build? + + def test_store + if Wx.has_feature?(:USE_SECRETSTORE) + state, err = Wx::SecretStore.get_default.ok? + if state + puts "SecretStore OK" + + if Wx::WXWIDGETS_VERSION > '3.2.4' + password = Digest::SHA256.digest('My Secret Password!') + else + password = Digest::SHA256.hexdigest('My Secret Password!') # binary secrets does not work + end + secret_val = Wx::SecretValue.new(password) + assert_true(Wx::SecretStore.get_default.save('My/Service', 'a_user', secret_val)) + + secret_val2 = Wx::SecretValue.new + rc, user = Wx::SecretStore.get_default.load('My/Service', secret_val2) + assert_true(rc) + assert_equal('a_user', user) + assert_equal(secret_val, secret_val2) + assert_equal(password, secret_val2.get_data) + + password = 'My Secret Password!' + secret_val = Wx::SecretValue.new(password) + assert_true(Wx::SecretStore.get_default.save('My/Service', 'a_user', secret_val)) + + secret_val2 = Wx::SecretValue.new + rc, user = Wx::SecretStore.get_default.load('My/Service', secret_val2) + assert_true(rc) + assert_equal('a_user', user) + assert_equal(secret_val, secret_val2) + assert_equal(password, secret_val2.get_as_string) + + password = 'My Secret Password!'.encode('UTF-16') + secret_val = Wx::SecretValue.new(password) + assert_true(Wx::SecretStore.get_default.save('My/Service', 'a_user', secret_val)) + + secret_val2 = Wx::SecretValue.new + rc, user = Wx::SecretStore.get_default.load('My/Service', secret_val2) + assert_true(rc) + assert_equal('a_user', user) + assert_equal(secret_val, secret_val2) + assert_equal(password, secret_val2.get_as_string) + assert_not_equal('My Secret Password!'.encode('UTF-16'), secret_val2.get_as_string) + assert_equal('My Secret Password!'.encode('UTF-16'), secret_val2.get_as_string.encode('UTF-16')) + + password = 'My Secret Password!'.encode('UTF-32') + secret_val = Wx::SecretValue.new(password) + assert_true(Wx::SecretStore.get_default.save('My/Service', 'a_user', secret_val)) + + secret_val2 = Wx::SecretValue.new + rc, user = Wx::SecretStore.get_default.load('My/Service', secret_val2) + assert_true(rc) + assert_equal('a_user', user) + assert_equal(secret_val, secret_val2) + assert_equal(password, secret_val2.get_as_string) + assert_not_equal('My Secret Password!'.encode('UTF-32'), secret_val2.get_as_string) + assert_equal('My Secret Password!'.encode('UTF-32'), secret_val2.get_as_string.encode('UTF-32')) + + else + puts "Default SecretStore not usable : #{err}" + end + else + puts 'Wx::SecretStore not available' + end + end + + end + +end diff --git a/tools/scripts/cirrus/build-wxruby3.sh b/tools/scripts/cirrus/build-wxruby3.sh new file mode 100755 index 00000000..f66d6508 --- /dev/null +++ b/tools/scripts/cirrus/build-wxruby3.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +_ruby="system" +_binpkg=0 + +for a in "$@" +do +case $a in + --latest) + _ruby="latest" + ;; + --binpkg) + _binpkg=1 + ;; + *) + # unknown option + ;; +esac +done + +if [ "$latest_only" == "" ] || [ "$_ruby" == "latest" ]; then + ruby -v + + bundle install + bundle exec rake 'configure[--with-wxwin,--autoinstall]' + bundle exec rake build + + if [ "$_binpkg" == "1" ]; then + bundle exec rake binpkg + fi +fi diff --git a/tools/scripts/cirrus/cleanup-wxruby3.sh b/tools/scripts/cirrus/cleanup-wxruby3.sh new file mode 100755 index 00000000..45e29a48 --- /dev/null +++ b/tools/scripts/cirrus/cleanup-wxruby3.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if [ "$latest_only" == "" ]; then + bundle exec rake clean + rm -rf ext/wxWidgets + rm -f .wxconfig + rm -f Gemfile.lock +fi diff --git a/tools/scripts/cirrus/setup-debian-system-ruby.sh b/tools/scripts/cirrus/setup-debian-system-ruby.sh new file mode 100755 index 00000000..56dba9a5 --- /dev/null +++ b/tools/scripts/cirrus/setup-debian-system-ruby.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +if [ "$1" == "remove" ]; then + apt-get remove -y ruby ruby-dev +else + apt-get install -y ruby ruby-dev ruby-bundler +fi diff --git a/tools/scripts/cirrus/setup-debian.sh b/tools/scripts/cirrus/setup-debian.sh new file mode 100755 index 00000000..7cc3a8a3 --- /dev/null +++ b/tools/scripts/cirrus/setup-debian.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +apt-get update +apt-get install -y git make gcc gpg xvfb xfonts-75dpi curl procps +if [ "$1" == "test" ]; then + apt-get install -y 'libgtk-3-[0-9]+' 'libwebkit2gtk-4.0-[0-9]+' 'libgspell-1-[0-9]+' libnotify4 'libsecret-1-[0-9]+' curl +fi diff --git a/tools/scripts/cirrus/setup-fedora-system-ruby.sh b/tools/scripts/cirrus/setup-fedora-system-ruby.sh new file mode 100755 index 00000000..f2a0e65b --- /dev/null +++ b/tools/scripts/cirrus/setup-fedora-system-ruby.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +if [ "$1" == "remove" ]; then + dnf remove -y ruby ruby-devel +else + dnf install -y ruby ruby-devel rubygem-bundler +fi diff --git a/tools/scripts/cirrus/setup-fedora.sh b/tools/scripts/cirrus/setup-fedora.sh new file mode 100755 index 00000000..89a842c3 --- /dev/null +++ b/tools/scripts/cirrus/setup-fedora.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +dnf install -y git xorg-x11-server-Xvfb xorg-x11-fonts-75dpi which gcc make procps-ng +if [ "$1" == "test" ]; then + dnf install -y gtk3 webkit2gtk4.1 mesa-libGLU gspell libnotify libsecret curl +fi diff --git a/tools/scripts/cirrus/setup-macosx-system-ruby.sh b/tools/scripts/cirrus/setup-macosx-system-ruby.sh new file mode 100755 index 00000000..5a71cecc --- /dev/null +++ b/tools/scripts/cirrus/setup-macosx-system-ruby.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +# system Ruby not supported for MacOSX so do nothing to install or remove diff --git a/tools/scripts/cirrus/setup-macosx.sh b/tools/scripts/cirrus/setup-macosx.sh new file mode 100755 index 00000000..b394cd2f --- /dev/null +++ b/tools/scripts/cirrus/setup-macosx.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" diff --git a/tools/scripts/cirrus/setup-opensuse-system-ruby.sh b/tools/scripts/cirrus/setup-opensuse-system-ruby.sh new file mode 100755 index 00000000..56abc4ea --- /dev/null +++ b/tools/scripts/cirrus/setup-opensuse-system-ruby.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if [ "$1" == "remove" ]; then + zypper remove -y ruby ruby-devel ruby2.5-rubygem-bundler + zypper install -y libyaml-devel libopenssl-devel +else + zypper install -y ruby ruby-devel ruby2.5-rubygem-bundler zlib-devel +fi diff --git a/tools/scripts/cirrus/setup-opensuse.sh b/tools/scripts/cirrus/setup-opensuse.sh new file mode 100755 index 00000000..65c804c9 --- /dev/null +++ b/tools/scripts/cirrus/setup-opensuse.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +zypper install -y git xorg-x11-server-Xvfb xvfb-run xorg-x11-fonts curl gcc make tar gzip +if [ "$1" == "test" ]; then + zypper install -y gtk3 webkit2gtk3-devel libGLU1 gspell libnotify4 libsecret curl +fi diff --git a/tools/scripts/cirrus/setup-ruby-install-latest.sh b/tools/scripts/cirrus/setup-ruby-install-latest.sh new file mode 100755 index 00000000..308d4fa2 --- /dev/null +++ b/tools/scripts/cirrus/setup-ruby-install-latest.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +if [ "$distro" == "macosx" ]; then + brew install ruby +else + curl -s -L https://raw.github.com/postmodern/postmodern.github.io/main/postmodern.asc --output postmodern.asc + gpg --import postmodern.asc + + RUBY_INSTALL_LATEST_URL=$(curl -s https://api.github.com/repos/postmodern/ruby-install/releases/latest | grep browser_download_url | cut -d\" -f4 | egrep '.tar.gz$') + RUBY_INSTALL_LATEST_FILE=$(basename $RUBY_INSTALL_LATEST_URL) + RUBY_INSTALL_LATEST_DIR=$(basename -s .tar.gz $RUBY_INSTALL_LATEST_FILE) + + curl -s -L $RUBY_INSTALL_LATEST_URL --output $RUBY_INSTALL_LATEST_FILE + curl -s -L $RUBY_INSTALL_LATEST_URL.asc --output $RUBY_INSTALL_LATEST_FILE.asc + + gpg --verify $RUBY_INSTALL_LATEST_FILE.asc $RUBY_INSTALL_LATEST_FILE || exit 1 + + tar -xzf $RUBY_INSTALL_LATEST_FILE + cd $RUBY_INSTALL_LATEST_DIR + make install + cd .. + + ruby-install --system ruby -- --disable-install-rdoc --enable-shared +fi diff --git a/tools/scripts/cirrus/setup-ubuntu-system-ruby.sh b/tools/scripts/cirrus/setup-ubuntu-system-ruby.sh new file mode 100755 index 00000000..56dba9a5 --- /dev/null +++ b/tools/scripts/cirrus/setup-ubuntu-system-ruby.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +if [ "$1" == "remove" ]; then + apt-get remove -y ruby ruby-dev +else + apt-get install -y ruby ruby-dev ruby-bundler +fi diff --git a/tools/scripts/cirrus/setup-ubuntu.sh b/tools/scripts/cirrus/setup-ubuntu.sh new file mode 100755 index 00000000..7cc3a8a3 --- /dev/null +++ b/tools/scripts/cirrus/setup-ubuntu.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +apt-get update +apt-get install -y git make gcc gpg xvfb xfonts-75dpi curl procps +if [ "$1" == "test" ]; then + apt-get install -y 'libgtk-3-[0-9]+' 'libwebkit2gtk-4.0-[0-9]+' 'libgspell-1-[0-9]+' libnotify4 'libsecret-1-[0-9]+' curl +fi diff --git a/tools/scripts/cirrus/start_release_tasks.rb b/tools/scripts/cirrus/start_release_tasks.rb new file mode 100644 index 00000000..dafa9a76 --- /dev/null +++ b/tools/scripts/cirrus/start_release_tasks.rb @@ -0,0 +1,49 @@ + +require 'uri' +require 'net/https' +require 'json' + +$CIRRUS_TOKEN = ENV['CIRRUS_TOKEN'] || '' + +# for an actual release we can just run the Cirrus tasks off the master branch since +# the release tag has just be created against that +$branch = ARGV.empty? ? 'master' : ARGV[0].split('/').last + +$cirrus_uri = URI('https://api.cirrus-ci.com/graphql') + +$query = %q[{ ownerRepository(platform: "github", owner: "mcorino", name: "wxRuby3") { id } }] + +$headers = { + 'Authorization' => "Bearer #{$CIRRUS_TOKEN}", + 'Content-Type' => 'application/json' +} +$response = Net::HTTP.post($cirrus_uri, { query: $query }.to_json, $headers) + +if Net::HTTPOK === $response + $repository_id = JSON.parse($response.body)['data']['ownerRepository']['id'] + + $mutation = %Q[mutation StartReleaseBuild( $input: RepositoryCreateBuildInput! ) { createBuild(input: $input) { build { id } } }] + vars = { + input: { + repositoryId: "#{$repository_id}", + branch: $branch, + clientMutationId: "wxRuby3" + } + } + $response = Net::HTTP.post($cirrus_uri, { query: $mutation, variables: vars }.to_json, $headers) + if Net::HTTPOK === $response + response_data = JSON.parse($response.body) + if response_data['errors'] + $stderr.puts "Error starting release build: #{response_data['errors'].collect { |err| err['message'] }.join(',')}" + else + $build_id = response_data['data']['createBuild']['build']['id'] + puts "Started release build [#{$build_id}]" + end + else + $stderr.puts "Failed to start release build." + exit(1) + end +else + $stderr.puts "Failed to retrieve repository id." + exit(1) +end diff --git a/tools/scripts/cirrus/test-wxruby3-release.sh b/tools/scripts/cirrus/test-wxruby3-release.sh new file mode 100755 index 00000000..1b689664 --- /dev/null +++ b/tools/scripts/cirrus/test-wxruby3-release.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +if [ "$CIRRUS_BUILD_SOURCE" == "api" ]; then + bundle install + bundle exec rake gem + if [ "$distro" == "macosx" ]; then + gem install $(echo pkg/*.gem) --no-format-executable && wxruby test --exclude=test_intl --exclude=test_media_ctrl + else + gem install $(echo pkg/*.gem) --no-format-executable && xvfb-run -a -s '-screen 0 1600x1200x24' wxruby test --exclude=test_intl --exclude=test_media_ctrl + fi +else + WXRUBY_VERSION=${CIRRUS_TAG/#v/} + if grep -q "\-[a-zA-Z]" <<< "$CIRRUS_TAG" ; then + WXRUBY_PRERELEASE="--pre" + else + WXRUBY_PRERELEASE="" + fi + if [ "$distro" == "macosx" ]; then + gem install wxruby3 -v "$WXRUBY_VERSION" ${WXRUBY_PRERELEASE} --no-format-executable && wxruby test --exclude=test_intl --exclude=test_media_ctrl + else + gem install wxruby3 -v "$WXRUBY_VERSION" ${WXRUBY_PRERELEASE} --no-format-executable && xvfb-run -a -s '-screen 0 1600x1200x24' wxruby test --exclude=test_intl --exclude=test_media_ctrl + fi +fi diff --git a/tools/scripts/cirrus/test-wxruby3.sh b/tools/scripts/cirrus/test-wxruby3.sh new file mode 100755 index 00000000..6e94394a --- /dev/null +++ b/tools/scripts/cirrus/test-wxruby3.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +if [ "$distro" == "macosx" ]; then + bundle exec rake test +else + xvfb-run -a -s '-screen 0 1600x1200x24' bundle exec rake test +fi diff --git a/tools/scripts/cirrus/upload-release-pkg.rb b/tools/scripts/cirrus/upload-release-pkg.rb new file mode 100644 index 00000000..cd881eb7 --- /dev/null +++ b/tools/scripts/cirrus/upload-release-pkg.rb @@ -0,0 +1,59 @@ + +require 'uri' +require 'net/https' +require 'json' + +$GITHUB_TOKEN = ENV['GITHUB_TOKEN'] || '' +if $GITHUB_TOKEN.empty? + $stderr.puts 'Please provide GitHub access token via GITHUB_TOKEN environment variable!' + exit(1) +end + +$CIRRUS_RELEASE = ENV['CIRRUS_RELEASE'] || '' +if $CIRRUS_RELEASE.empty? + uri = URI('https://api.github.com/repos/mcorino/wxruby3/releases/latest') + headers = { + 'Accept' => 'application/vnd.github+json', + 'X-GitHub-Api-Version' => '2022-11-28', + 'Authorization' => "Bearer #{ENV['GITHUB_TOKEN']}" + } + rest_response = Net::HTTP.get_response(uri, headers) + if rest_response.code.to_i == 200 + data = JSON.parse!(rest_response.body) + $CIRRUS_RELEASE = data['id'] + else + $stderr.puts "Cannot determine latest release [#{rest_response}]!" + exit(1) + end +end + +file_content_type="application/octet-stream" +Dir.glob(File.join('pkg', '*.pkg')).each do |fpath| + name = File.basename(fpath) + url_to_upload = "https://uploads.github.com/repos/mcorino/wxruby3/releases/#{$CIRRUS_RELEASE}/assets?name=#{name}" + puts "Uploading #{fpath} for release #{$CIRRUS_RELEASE} to #{url_to_upload}..." + cmd = "curl -L -X POST -H \"Accept: application/vnd.github+json\" " + + "-H \"Authorization: token #{$GITHUB_TOKEN}\" " + + "-H \"X-GitHub-Api-Version: 2022-11-28\" " + + "-H \"Content-Type: #{file_content_type}\" " + + "#{url_to_upload} --data-binary @#{fpath}" + result = `#{cmd}` + rc = $?.success? && JSON.parse!(result)['browser_download_url'] + if rc + name = File.basename(name, '.*')+'.sha' + fpath = File.join(File.dirname(fpath), name) + url_to_upload = "https://uploads.github.com/repos/mcorino/wxruby3/releases/#{$CIRRUS_RELEASE}/assets?name=#{name}" + puts "Uploading #{fpath} for release #{$CIRRUS_RELEASE} to #{url_to_upload}..." + cmd = "curl -L -X POST -H \"Accept: application/vnd.github+json\" " + + "-H \"Authorization: token #{$GITHUB_TOKEN}\" " + + "-H \"X-GitHub-Api-Version: 2022-11-28\" " + + "-H \"Content-Type: #{file_content_type}\" " + + "#{url_to_upload} --data-binary @#{fpath}" + result = `#{cmd}` + rc = $?.success? && JSON.parse!(result)['browser_download_url'] + end + unless rc + $stderr.puts "Failed to upload release asset!" + exit(1) + end +end