From 14da102d854f5f36a456b93059826f772ec7c8cd Mon Sep 17 00:00:00 2001 From: Matt Valentine-House Date: Tue, 26 Nov 2024 15:03:14 +0000 Subject: [PATCH] wip --- .github/actions/setup/directories/action.yml | 176 +++++++++++++++ .github/actions/setup/macos/action.yml | 29 +++ .github/actions/setup/ubuntu/action.yml | 53 +++++ .github/workflows/modgc.yml | 214 +++++++++++++++++++ 4 files changed, 472 insertions(+) create mode 100644 .github/actions/setup/directories/action.yml create mode 100644 .github/actions/setup/macos/action.yml create mode 100644 .github/actions/setup/ubuntu/action.yml create mode 100644 .github/workflows/modgc.yml diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml new file mode 100644 index 0000000..8e382d9 --- /dev/null +++ b/.github/actions/setup/directories/action.yml @@ -0,0 +1,176 @@ +name: Setup directories etc. +description: >- + Set up the source code and build directories (plus some + environmental tweaks) + +inputs: + srcdir: + required: false + default: ${{ github.workspace }} + description: >- + Directory to (re-)checkout source codes. This will be created + if absent. If there is no `configure` file that is also + generated inside. + + builddir: + required: false + default: ${{ github.workspace }} + description: >- + Where binaries and other generated contents go. This will be + created if absent. + + makeup: + required: false + type: boolean + # Note that `default: false` evaluates to a string constant + # `'false'`, which is a truthy value :sigh: + # https://github.com/actions/runner/issues/2238 + default: '' + description: >- + If set to true, additionally runs `make up`. + + checkout: + required: false + type: boolean + default: true + description: >- + If set to '' (false), skip running actions/checkout. This is useful when + you don't want to overwrite a GitHub token that is already set up. + + dummy-files: + required: false + type: boolean + default: '' + description: >- + If set to true, creates dummy files in build dir. + + fetch-depth: + required: false + default: '1' + description: The depth of commit history fetched from the remote repository + + clean: + required: false + type: boolean + default: '' + description: >- + If set to true, clean build directory. + +outputs: {} # nothing? + +runs: + using: composite + + steps: + # Note that `shell: bash` works on both Windows and Linux, but not + # `shell: sh`. This is because GitHub hosted Windows runners have + # their bash manually installed. + - shell: bash + run: | + mkdir -p ${{ inputs.srcdir }} + mkdir -p ${{ inputs.builddir }} + + # Did you know that actions/checkout works without git(1)? We are + # checking that here. + - id: which + shell: bash + run: | + echo "git=`command -v git`" >> "$GITHUB_OUTPUT" + echo "sudo=`command -v sudo`" >> "$GITHUB_OUTPUT" + echo "autoreconf=`command -v autoreconf`" >> "$GITHUB_OUTPUT" + + - if: steps.which.outputs.git + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.eol lf + git config --global advice.detachedHead 0 + git config --global init.defaultBranch garbage + + - if: inputs.checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + path: ${{ inputs.srcdir }} + fetch-depth: ${{ inputs.fetch-depth }} + + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + with: + path: ${{ inputs.srcdir }}/.downloaded-cache + key: downloaded-cache + + - if: steps.which.outputs.autoreconf + shell: bash + working-directory: ${{ inputs.srcdir }} + run: ./autogen.sh --install + + # This is for MinGW. + - if: runner.os == 'Windows' + shell: bash + run: echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV + + - if: runner.os == 'Linux' + shell: bash + run: echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> "$GITHUB_ENV" + + # macOS' GNU make is so old that they doesn't understand `GNUMAKEFLAGS`. + - if: runner.os == 'macOS' + shell: bash + run: echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> "$GITHUB_ENV" + + - if: inputs.makeup + shell: bash + working-directory: ${{ inputs.srcdir }} + run: | + touch config.status + touch .rbconfig.time + sed -f tool/prereq.status template/Makefile.in > Makefile + sed -f tool/prereq.status template/GNUmakefile.in > GNUmakefile + make up + + # Cleanup, runs even on failure + - if: always() && inputs.makeup + shell: bash + working-directory: ${{ inputs.srcdir }} + run: rm -f config.status Makefile rbconfig.rb .rbconfig.time + + - if: steps.which.outputs.sudo + shell: bash + run: | + sudo chmod -R go-w /usr/share + chmod -v go-w $HOME $HOME/.config || : + SAVE_IFS="$IFS" IFS=:; set $PATH; dirs=() IFS="$SAVE_IFS" + for d do [ ! -d "$d" ] || dirs+=("$d"); done + sudo chmod -v go-w "${dirs[@]}" || : + + - if: inputs.dummy-files == 'true' + shell: bash + id: dummy-files + working-directory: ${{ inputs.builddir }} + run: | + : Create dummy files in build dir + set {{a..z},{A..Z},{0..9},foo,bar,test,zzz}.rb + for file; do \ + echo > $file "raise 'do not load $file'"; \ + done + # drop {a..z}.rb if case-insensitive filesystem + grep -F A.rb a.rb > /dev/null && set "${@:27}" + echo clean="cd ${{ inputs.builddir }} && rm $*" >> $GITHUB_OUTPUT + + - if: inputs.clean == 'true' + shell: bash + id: clean + run: | + echo distclean='make -C ${{ inputs.builddir }} distclean' >> $GITHUB_OUTPUT + echo remained-files='find ${{ inputs.builddir }} -ls' >> $GITHUB_OUTPUT + [ "${{ inputs.builddir }}" = "${{ inputs.srcdir }}" ] || + echo final='rmdir ${{ inputs.builddir }}' >> $GITHUB_OUTPUT + + - name: clean + uses: gacts/run-and-post-run@4683764dd706df847f57b9bed39d08164bcd2690 # v1.4.1 + with: + working-directory: + post: | + ${{ steps.dummy-files.outputs.clean }} + ${{ steps.clean.outputs.distclean }} + ${{ steps.clean.outputs.remained-files }} + ${{ steps.clean.outputs.final }} diff --git a/.github/actions/setup/macos/action.yml b/.github/actions/setup/macos/action.yml new file mode 100644 index 0000000..fd7b5e7 --- /dev/null +++ b/.github/actions/setup/macos/action.yml @@ -0,0 +1,29 @@ +name: Setup macOS environment +description: >- + Installs necessary packages via Homebrew. + +inputs: {} # nothing? + +outputs: {} # nothing? + +runs: + using: composite + + steps: + - name: brew + shell: bash + run: | + brew install --quiet gmp libffi openssl@3 zlib autoconf automake libtool + + - name: Set ENV + shell: bash + run: | + dir_config() { + local args=() lib var="$1"; shift + for lib in "$@"; do + args+="--with-${lib%@*}-dir=$(brew --prefix $lib)" + done + echo "$var=${args[*]}" >> $GITHUB_ENV + } + dir_config ruby_configure_args gmp + dir_config CONFIGURE_ARGS openssl@3 diff --git a/.github/actions/setup/ubuntu/action.yml b/.github/actions/setup/ubuntu/action.yml new file mode 100644 index 0000000..a9e5b41 --- /dev/null +++ b/.github/actions/setup/ubuntu/action.yml @@ -0,0 +1,53 @@ +name: Setup ubuntu environment +description: >- + At the beginning there was no way but to copy & paste `apt-get` + everywhere. But now that we have composite actions, it seems better + merge them into one. + +inputs: + arch: + required: false + default: '' + description: >- + Architecture. Because we run this on a GitHub-hosted runner + acceptable value for this input is very limited. + +outputs: + arch: + value: ${{ steps.uname.outputs.uname }} + description: >- + Actual architecture. This could be different from the one + passed to the `inputs.arch`. For instance giving `i386` to this + action yields `i686`. + +runs: + using: composite + + steps: + - name: set SETARCH + shell: bash + run: echo "SETARCH=${setarch}" >> "$GITHUB_ENV" + env: + setarch: ${{ inputs.arch && format('setarch {0} --', inputs.arch) }} + + - id: uname + name: uname + shell: bash + run: | + echo uname=`${SETARCH} uname -m` >> "$GITHUB_OUTPUT" + echo dpkg=`${SETARCH} uname -m | sed s/686/386/` >> "$GITHUB_OUTPUT" + + - name: apt-get + shell: bash + env: + arch: ${{ inputs.arch && format(':{0}', steps.uname.outputs.dpkg) || '' }} + run: | + set -x + ${arch:+sudo dpkg --add-architecture ${arch#:}} + sudo apt-get update -qq || : + sudo apt-get install --no-install-recommends -qq -y -o=Dpkg::Use-Pty=0 \ + ${arch:+cross}build-essential${arch/:/-} \ + libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \ + zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \ + autoconf ruby + sudo apt-get install -qq -y pkg-config${arch} || : diff --git a/.github/workflows/modgc.yml b/.github/workflows/modgc.yml new file mode 100644 index 0000000..9890fb6 --- /dev/null +++ b/.github/workflows/modgc.yml @@ -0,0 +1,214 @@ +name: ModGC +on: + push: + paths-ignore: + - 'doc/**' + - '**/man/*' + - '**.md' + - '**.rdoc' + - '**/.document' + - '.*.yml' + pull_request: + # Do not use paths-ignore for required status checks + # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks + merge_group: + +concurrency: + group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} + +permissions: + contents: read + +jobs: + check: + strategy: + matrix: + gc: + - name: mmtk + mmtk_plan: MarkSweep + mmtk_build: release + - name: mmtk + mmtk_plan: MarkSweep + mmtk_build: debug + timeout: 60 + os: [macos-latest, ubuntu-latest] + include: + - test_task: check + fail-fast: false + + env: + GITPULLOPTIONS: --no-tags origin ${{ github.ref }} + RUBY_DEBUG: ci + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + with: + repository: 'ruby/ruby' + sparse-checkout-cone-mode: false + sparse-checkout: /.github + + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + with: + path: 'gc/mmtk' + sparse-checkout-cone-mode: false + sparse-checkout: /.github + + - name: Install libraries (macOS) + uses: ./.github/actions/setup/macos + if: ${{ contains(matrix.os, 'macos') }} + + - name: Install libraries (Ubuntu) + uses: ./.github/actions/setup/ubuntu + if: ${{ contains(matrix.os, 'ubuntu') }} + + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 + with: + ruby-version: '3.0' + bundler: none + if: ${{ contains(matrix.os, 'ubuntu') }} + + - uses: ./.github/actions/setup/directories + with: + srcdir: src + builddir: build + makeup: true + clean: true + dummy-files: ${{ matrix.test_task == 'check' }} + # Set fetch-depth: 10 so that Launchable can receive commits information. + fetch-depth: 10 + + - name: make sure that kern.coredump=1 + run: | + sysctl -n kern.coredump + sudo sysctl -w kern.coredump=1 + sudo chmod -R +rwx /cores/ + if: ${{ contains(matrix.os, 'macos') }} + + - name: Delete unused SDKs + # To free up disk space to not run out during the run + run: | + sudo rm -rf ~/.dotnet + sudo rm -rf /Library/Android + sudo rm -rf /Library/Developer/CoreSimulator + continue-on-error: true + if: ${{ contains(matrix.os, 'macos') }} + + - name: Setup Ruby GC Directory + run: | + if [ "${{ contains(matrix.os, 'macos') }}" == "true" ]; then + echo "SHARED_GC_DIR=/Users/runner/ruby_gc" >> $GITHUB_ENV + else + echo "SHARED_GC_DIR=/home/runner/ruby_gc" >> $GITHUB_ENV + fi + + - name: Run configure + env: + arch: ${{ matrix.arch }} + run: >- + $SETARCH ../src/configure -C --disable-install-doc --with-shared-gc=${{ env.SHARED_GC_DIR }} + ${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE} + + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Set MMTk environment variables + run: | + if [[ ${{ matrix.gc.mmtk_build }} == debug ]]; then + echo 'RUST_LOG=' >> $GITHUB_ENV + # Debug builds run much slower so we should increase the timeout + echo 'RUBY_TEST_TIMEOUT_SCALE=10' >> $GITHUB_ENV + # SYNTAX_SUGGEST_TIMEOUT defaults to 1 second + echo 'SYNTAX_SUGGEST_TIMEOUT=60' >> $GITHUB_ENV + fi + echo 'MMTK_PLAN=${{ matrix.gc.mmtk_plan }}' >> $GITHUB_ENV + echo 'EXCLUDES=../src/test/.excludes-mmtk' >> $GITHUB_ENV + echo 'MSPECOPT=-B../src/spec/mmtk.mspec' >> $GITHUB_ENV + if: ${{ matrix.gc.name == 'mmtk' }} + + - run: $SETARCH make + + - name: Build shared GC + run: | + echo "RUBY_GC_LIBRARY=${{ matrix.gc.name }}" >> $GITHUB_ENV + make shared-gc SHARED_GC=${{ matrix.gc.name }} MMTK_BUILD=${{ matrix.gc.mmtk_build }} + + - name: Verify MMTk bindgen + run: | + cd ../src/gc/mmtk + cargo install --force cbindgen + cbindgen --config cbindgen.toml --output ../mmtk.h + if read -n1 -d '' < <(git diff ../mmtk.h); then + git diff ../mmtk.h + exit 1 + fi + if: ${{ matrix.gc.name == 'mmtk' }} + + - run: | + $SETARCH make golf + case "${{ matrix.configure }}" in + *'--enable-shared'*) + $SETARCH make runnable + ./bin/goruby -veh + ;; + *) + ./goruby -veh + ;; + esac + + - name: Set test options for skipped tests + run: | + set -x + TESTS="$(echo "${{ matrix.skipped_tests }}" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|')" + echo "TESTS=${TESTS}" >> $GITHUB_ENV + if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }} + + - name: Set up Launchable + uses: ./.github/actions/launchable/setup + with: + os: ${{ matrix.os || 'ubuntu-22.04' }} + test-opts: ${{ matrix.configure }} + launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }} + builddir: build + srcdir: src + continue-on-error: true + + - name: make ${{ matrix.test_task }} + run: >- + $SETARCH make -s ${{ matrix.test_task }} + ${TESTS:+TESTS="$TESTS"} + ${{ !contains(matrix.test_task, 'bundle') && 'RUBYOPT=-w' || '' }} + timeout-minutes: ${{ matrix.gc.timeout || 40 }} + env: + RUBY_TESTOPTS: '-q --tty=no' + TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof' + PRECHECK_BUNDLED_GEMS: 'no' + + - name: make skipped tests + run: | + $SETARCH make -s test-all TESTS="${TESTS//-n!\//-n/}" + env: + GNUMAKEFLAGS: '' + RUBY_TESTOPTS: '-v --tty=no' + if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }} + continue-on-error: ${{ matrix.continue-on-skipped_tests || false }} + + - uses: ./.github/actions/slack + with: + label: ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }} + SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot + if: ${{ failure() }} + + result: + if: ${{ always() }} + name: ${{ github.workflow }} result + runs-on: ubuntu-latest + needs: [check] + steps: + - run: exit 1 + working-directory: + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + +defaults: + run: + working-directory: build