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..1c7ea64 --- /dev/null +++ b/.github/workflows/modgc.yml @@ -0,0 +1,97 @@ +name: ModGC +on: + push: + paths-ignore: + - '**.md' + 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: + fail-fast: false + 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] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + + - 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: 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 + + if: ${{ matrix.gc.name == 'mmtk' }} + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 + with: + ruby-version: '3.3' + bundler: none + if: ${{ contains(matrix.os, 'ubuntu') }} + + - 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: Install Bundler dependencies + run: bundle install + + - name: build Ruby & MMTk shared GC + run: rake compile + + - name: Verify MMTk bindgen + run: | + cd 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 + + - name: make check + run: RUST_LOG= make -s check + timeout-minutes: 40 + working-directory: ./res/ruby + env: + RUBY_TESTOPTS: '-q --tty=no' + RUBY_GC_LIBRARY: mmtk + TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof' + PRECHECK_BUNDLED_GEMS: 'no' +