├── .dockerignore ├── .github └── workflows │ ├── ci.yml │ ├── publish-images.yml │ └── release-images.yml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile.jruby ├── Dockerfile.mri.erb ├── Gemfile ├── History.md ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── rake-compiler-dock ├── build ├── gem_helper.rb ├── mk_i686.rb ├── mk_musl_cross.sh ├── mk_osxcross.sh ├── mk_pkg_config.sh ├── parallel_docker_build.rb ├── patches │ └── rake-compiler-1.2.9 │ │ ├── 0004-Enable-build-of-static-libruby.patch │ │ └── 0005-build-miniruby-first.patch ├── rcd-env.sh ├── runas ├── sigfw.c ├── strip_wrapper_codesign ├── strip_wrapper_vbox └── sudoers ├── lib ├── rake_compiler_dock.rb └── rake_compiler_dock │ ├── colors.rb │ ├── docker_check.rb │ ├── predefined_user_group.rb │ ├── starter.rb │ └── version.rb ├── mingw64-ucrt ├── Dockerfile ├── README.md ├── binutils-mingw-w64-ignore-check-errors.patch ├── gcc-mingw-w64-only-c-c++.patch └── mingw-w64-enable-ucrt.patch ├── rake-compiler-dock.gemspec └── test ├── env ├── Dockerfile.alpine └── Dockerfile.debian ├── fixtures └── mig_test_rpc.defs ├── rcd_test ├── Gemfile ├── Rakefile ├── ext │ ├── java │ │ ├── RcdTestExtService.java │ │ └── RubyRcdTest.java │ └── mri │ │ ├── extconf.rb │ │ ├── rcd_test_ext.c │ │ └── rcd_test_ext.h ├── lib │ └── rcd_test.rb ├── rcd_test.gemspec └── test │ └── test_basic.rb ├── test_environment_variables.rb ├── test_mig.rb ├── test_parallel_docker_build.rb ├── test_starter.rb └── test_versions.rb /.dockerignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | cache*.tar 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | concurrency: 3 | group: "${{github.workflow}}-${{github.ref}}" 4 | cancel-in-progress: true 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 5 * * 3" # At 05:00 on Wednesday # https://crontab.guru/#0_5_*_*_3 9 | push: 10 | branches: 11 | - main 12 | tags: 13 | - "*.*.*" 14 | pull_request: 15 | types: [opened, synchronize] 16 | branches: 17 | - "*" 18 | 19 | jobs: 20 | build_source_gem: 21 | name: build source 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: ruby/setup-ruby@v1 26 | with: 27 | ruby-version: "3.3" 28 | working-directory: test/rcd_test 29 | bundler-cache: true 30 | 31 | - name: Build source gem 32 | run: | 33 | cd test/rcd_test/ 34 | bundle exec rake gem 35 | 36 | - name: Upload source gem 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: gem-ruby 40 | path: test/rcd_test/pkg/rcd_test-?.?.?.gem # e.g. rcd_test-1.0.0.gem 41 | 42 | build_native_gem: 43 | name: build native 44 | strategy: 45 | fail-fast: false 46 | matrix: 47 | include: 48 | - platform: aarch64-linux-gnu 49 | alias: aarch64-linux 50 | - platform: aarch64-linux-musl 51 | - platform: aarch64-mingw-ucrt 52 | - platform: arm-linux-gnu 53 | alias: arm-linux 54 | - platform: arm-linux-musl 55 | - platform: arm64-darwin 56 | - platform: jruby 57 | - platform: x64-mingw-ucrt 58 | static: true 59 | - platform: x64-mingw32 60 | static: true 61 | - platform: x86-linux-gnu 62 | alias: x86-linux 63 | - platform: x86-linux-musl 64 | - platform: x86-mingw32 65 | - platform: x86_64-darwin 66 | - platform: x86_64-linux-gnu 67 | alias: x86_64-linux 68 | - platform: x86_64-linux-musl 69 | runs-on: ubuntu-latest 70 | steps: 71 | - uses: actions/checkout@v4 72 | - uses: ruby/setup-ruby@v1 73 | with: 74 | ruby-version: "3.3" 75 | bundler-cache: true 76 | 77 | - name: Fetch docker buildx layer cache 78 | uses: actions/cache@v4 79 | with: 80 | path: tmp/build-cache 81 | key: ${{ runner.os }}-${{ matrix.platform }}-buildx-${{ github.sha }} 82 | restore-keys: ${{ runner.os }}-${{ matrix.platform }}-buildx 83 | - name: Build docker image 84 | run: | 85 | docker buildx create --driver docker-container --use 86 | bundle exec rake build:${{ matrix.platform }} RCD_DOCKER_BUILD="docker buildx build --cache-from=type=local,src=tmp/build-cache --cache-to=type=local,dest=tmp/build-cache-new --load" 87 | docker images 88 | - name: Update and prune docker buildx layer cache 89 | run: | 90 | rm -rf tmp/build-cache 91 | mv tmp/build-cache-new tmp/build-cache 92 | 93 | - name: Test the generated image 94 | run: bundle exec rake test TEST_PLATFORM=${{ matrix.platform }} 95 | 96 | - name: Build native gem 97 | run: | 98 | cd test/rcd_test/ 99 | bundle install 100 | bundle exec rake clean clobber 101 | bundle exec rake gem:${{ matrix.platform }} 102 | 103 | - name: Upload native gem 104 | uses: actions/upload-artifact@v4 105 | with: 106 | name: gem-${{ matrix.platform }} 107 | path: test/rcd_test/pkg/*-*-*.gem 108 | 109 | - if: matrix.static 110 | name: Build static native gem 111 | env: 112 | RCD_TEST_CONFIG: "--link-static" 113 | run: | 114 | cd test/rcd_test/ 115 | bundle install 116 | bundle exec rake clean clobber 117 | bundle exec rake gem:${{ matrix.platform }} 118 | 119 | - if: matrix.static 120 | name: Upload static native gem 121 | uses: actions/upload-artifact@v4 122 | with: 123 | name: gem-${{ matrix.platform }}-static 124 | path: test/rcd_test/pkg/*-*-*.gem 125 | 126 | - if: matrix.alias 127 | name: Build native gem ${{ matrix.alias }} 128 | run: | 129 | cd test/rcd_test/ 130 | bundle install 131 | bundle exec rake clean clobber 132 | bundle exec rake gem:${{ matrix.alias }} 133 | 134 | - if: matrix.alias 135 | name: Upload native gem ${{ matrix.alias }} 136 | uses: actions/upload-artifact@v4 137 | with: 138 | name: gem-${{ matrix.alias }} 139 | path: test/rcd_test/pkg/*-*-*.gem 140 | 141 | test_source_gem: 142 | name: source gem 143 | needs: build_source_gem 144 | strategy: 145 | fail-fast: false 146 | matrix: 147 | os: [ubuntu-latest] 148 | ruby: ["3.4", "3.3", "3.2", "3.1", "3.0", "2.7"] 149 | runs-on: ${{ matrix.os }} 150 | steps: 151 | - uses: actions/checkout@v4 152 | - uses: ruby/setup-ruby@v1 153 | with: 154 | ruby-version: ${{ matrix.ruby }} 155 | - name: Download source gem 156 | uses: actions/download-artifact@v4 157 | with: 158 | name: gem-ruby 159 | - name: Test source gem 160 | run: | 161 | gem install --local *.gem --verbose 162 | cd test/rcd_test/ 163 | bundle install 164 | ruby -rrcd_test -S rake test 165 | 166 | test-x86_64-linux-setup-ruby: 167 | name: "${{ matrix.platform }} setup-ruby(${{ matrix.ruby }})" 168 | needs: build_native_gem 169 | strategy: 170 | fail-fast: false 171 | matrix: 172 | platform: [x86_64-linux, x86_64-linux-gnu] 173 | ruby: ["3.4", "3.3", "3.2", "3.1", "3.0", "2.7"] 174 | include: 175 | # declare rubygems for each ruby version 176 | - { ruby: "3.0", rubygems: "3.5.23" } 177 | - { ruby: "2.7", rubygems: "3.4.22" } 178 | runs-on: ubuntu-latest 179 | steps: 180 | - uses: actions/checkout@v4 181 | - uses: ruby/setup-ruby@v1 182 | with: 183 | ruby-version: ${{ matrix.ruby }} 184 | - name: Download gem-${{ matrix.platform }} 185 | uses: actions/download-artifact@v4 186 | with: 187 | name: gem-${{ matrix.platform }} 188 | - if: matrix.rubygems 189 | run: gem update --system ${{ matrix.rubygems }} 190 | - name: Test gem-${{ matrix.platform }} 191 | run: | 192 | gem install --local *.gem --verbose 193 | cd test/rcd_test/ 194 | bundle install 195 | ruby -rrcd_test -S rake test 196 | 197 | test_architecture_matrix: 198 | name: "${{ matrix.platform }} ${{ matrix.ruby }}" 199 | needs: build_native_gem 200 | strategy: 201 | fail-fast: false 202 | matrix: 203 | platform: 204 | - aarch64-linux 205 | - aarch64-linux-gnu 206 | - aarch64-linux-musl 207 | - arm-linux 208 | - arm-linux-gnu 209 | - arm-linux-musl 210 | - x86-linux 211 | - x86-linux-gnu 212 | - x86-linux-musl 213 | - x86_64-linux 214 | - x86_64-linux-gnu 215 | - x86_64-linux-musl 216 | ruby: ["3.4", "3.3", "3.2", "3.1", "3.0", "2.7"] 217 | include: 218 | # declare rubygems for each ruby version 219 | - { ruby: "3.0", rubygems: "3.5.23" } 220 | - { ruby: "2.7", rubygems: "3.4.22" } 221 | # declare docker image for each platform 222 | - { platform: aarch64-linux-musl, docker_tag: "-alpine" } 223 | - { platform: arm-linux-musl, docker_tag: "-alpine" } 224 | - { platform: x86-linux-musl, docker_tag: "-alpine" } 225 | - { platform: x86_64-linux-musl, docker_tag: "-alpine" } 226 | # declare docker platform for each platform 227 | - { platform: aarch64-linux, docker_platform: "--platform=linux/arm64" } 228 | - { platform: aarch64-linux-gnu, docker_platform: "--platform=linux/arm64" } 229 | - { platform: aarch64-linux-musl, docker_platform: "--platform=linux/arm64" } 230 | - { platform: arm-linux, docker_platform: "--platform=linux/arm/v7" } 231 | - { platform: arm-linux-gnu, docker_platform: "--platform=linux/arm/v7" } 232 | - { platform: arm-linux-musl, docker_platform: "--platform=linux/arm/v7" } 233 | - { platform: x86-linux, docker_platform: "--platform=linux/386" } 234 | - { platform: x86-linux-gnu, docker_platform: "--platform=linux/386" } 235 | - { platform: x86-linux-musl, docker_platform: "--platform=linux/386" } 236 | runs-on: ubuntu-latest 237 | steps: 238 | - uses: actions/checkout@v4 239 | - name: Download gem-${{ matrix.platform }} 240 | uses: actions/download-artifact@v4 241 | with: 242 | name: gem-${{ matrix.platform }} 243 | - name: Run tests 244 | run: | 245 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 246 | docker run --rm -v $PWD:/work -w /work \ 247 | ${{ matrix.docker_platform}} ruby:${{ matrix.ruby }}${{ matrix.docker_tag }} \ 248 | sh -c " 249 | if test -n '${{ matrix.rubygems }}' ; then gem update --system ${{ matrix.rubygems }} ; fi && 250 | gem install --local *.gem --verbose && 251 | cd test/rcd_test/ && 252 | bundle install && 253 | ruby -rrcd_test -S rake test 254 | " 255 | 256 | test_the_rest: 257 | name: "${{ matrix.platform }} ${{ matrix.ruby }}" 258 | needs: build_native_gem 259 | strategy: 260 | fail-fast: false 261 | matrix: 262 | os: [macos-13, macos-14] 263 | ruby: ["3.4", "3.3", "3.2", "3.1", "3.0", "2.7"] 264 | include: 265 | - os: macos-13 266 | platform: x86_64-darwin 267 | - os: macos-14 268 | platform: arm64-darwin 269 | - os: ubuntu-latest 270 | ruby: jruby 271 | platform: jruby 272 | - os: windows-latest 273 | ruby: "2.7" 274 | platform: x64-mingw32 275 | - os: windows-latest 276 | ruby: "3.0" 277 | platform: x64-mingw32 278 | - os: windows-latest 279 | ruby: "3.1" 280 | platform: x64-mingw-ucrt 281 | - os: windows-latest 282 | ruby: "3.2" 283 | platform: x64-mingw-ucrt 284 | - os: windows-latest 285 | ruby: "3.3" 286 | platform: x64-mingw-ucrt 287 | - os: windows-latest 288 | ruby: "3.4" 289 | platform: x64-mingw-ucrt 290 | runs-on: ${{ matrix.os }} 291 | steps: 292 | - uses: actions/checkout@v4 293 | - uses: ruby/setup-ruby@v1 294 | with: 295 | ruby-version: ${{ matrix.ruby }} 296 | - name: Print ruby version and gem env 297 | run: | 298 | ruby --version 299 | gem env 300 | - name: Download gem-${{ matrix.platform }} 301 | uses: actions/download-artifact@v4 302 | with: 303 | name: gem-${{ matrix.platform }} 304 | - name: Test gem-${{ matrix.platform }} 305 | run: | 306 | gem install --local *.gem --verbose 307 | cd test/rcd_test/ 308 | bundle install 309 | ruby -rrcd_test -S rake test 310 | 311 | test_windows_static: 312 | name: "static ${{ matrix.platform }} ${{ matrix.ruby }}" 313 | needs: build_native_gem 314 | strategy: 315 | fail-fast: false 316 | matrix: 317 | include: 318 | - os: windows-latest 319 | ruby: "2.7" 320 | platform: x64-mingw32 321 | - os: windows-latest 322 | ruby: "3.0" 323 | platform: x64-mingw32 324 | - os: windows-latest 325 | ruby: "3.1" 326 | platform: x64-mingw-ucrt 327 | - os: windows-latest 328 | ruby: "3.2" 329 | platform: x64-mingw-ucrt 330 | - os: windows-latest 331 | ruby: "3.3" 332 | platform: x64-mingw-ucrt 333 | - os: windows-latest 334 | ruby: "3.4" 335 | platform: x64-mingw-ucrt 336 | runs-on: ${{ matrix.os }} 337 | steps: 338 | - uses: actions/checkout@v4 339 | - uses: ruby/setup-ruby@v1 340 | with: 341 | ruby-version: ${{ matrix.ruby }} 342 | - name: Print ruby version and gem env 343 | run: | 344 | ruby --version 345 | gem env 346 | - name: Download gem-${{ matrix.platform }}-static 347 | uses: actions/download-artifact@v4 348 | with: 349 | name: gem-${{ matrix.platform }}-static 350 | - name: Test gem-${{ matrix.platform }}-static 351 | run: | 352 | gem install --local *.gem --verbose 353 | cd test/rcd_test/ 354 | bundle install 355 | ruby -rrcd_test -S rake test 356 | 357 | test_ad_hoc: 358 | name: "${{ matrix.platform }} on ${{ matrix.from_image }}" 359 | needs: build_native_gem 360 | strategy: 361 | fail-fast: false 362 | matrix: 363 | include: 364 | - from_image: navikey/raspbian-bullseye 365 | image_platform: linux/arm/v7 366 | platform: arm-linux # bullseye ships ruby 2.7, rubygems won't recognize -gnu suffix 367 | dockerfile: debian 368 | - from_image: arm64v8/ubuntu 369 | image_platform: linux/aarch64 370 | platform: aarch64-linux # arm64v8 ships ruby 3.0, rubygems won't recognize -gnu suffix 371 | dockerfile: debian 372 | - from_image: i386/alpine 373 | image_platform: linux/386 374 | platform: x86-linux-musl 375 | dockerfile: alpine 376 | - from_image: arm32v6/alpine 377 | image_platform: linux/arm/v6 378 | platform: arm-linux-musl 379 | dockerfile: alpine 380 | - from_image: alpine 381 | image_platform: linux/amd64 382 | platform: x86_64-linux-musl 383 | dockerfile: alpine 384 | runs-on: ubuntu-latest 385 | steps: 386 | - uses: actions/checkout@v4 387 | - name: Download gem-${{ matrix.platform }} 388 | uses: actions/download-artifact@v4 389 | with: 390 | name: gem-${{ matrix.platform }} 391 | - name: Build ${{ matrix.from_image }} image 392 | run: | 393 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 394 | docker build --rm --build-arg from_image=${{ matrix.from_image }} --platform=${{ matrix.image_platform }} -t ruby-test -f test/env/Dockerfile.${{ matrix.dockerfile }} . 395 | - name: Run tests 396 | run: docker run --rm -t --network=host -v `pwd`:/build ruby-test 397 | -------------------------------------------------------------------------------- /.github/workflows/publish-images.yml: -------------------------------------------------------------------------------- 1 | name: Publish docker images to GHCR 2 | concurrency: 3 | group: "${{github.workflow}}-${{github.ref}}" 4 | cancel-in-progress: true 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 3 * * 3" # At 03:00 on Wednesday # https://crontab.guru/#0_3_*_*_3 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | platform: 16 | - aarch64-linux-gnu 17 | - aarch64-linux-musl 18 | - aarch64-mingw-ucrt 19 | - arm-linux-gnu 20 | - arm-linux-musl 21 | - arm64-darwin 22 | - jruby 23 | - x64-mingw-ucrt 24 | - x64-mingw32 25 | - x86-linux-gnu 26 | - x86-linux-musl 27 | - x86-mingw32 28 | - x86_64-darwin 29 | - x86_64-linux-gnu 30 | - x86_64-linux-musl 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: Use cache from primary pipeline 35 | uses: actions/cache@v4 36 | with: 37 | path: tmp/build-cache 38 | key: ${{runner.os}}-${{matrix.platform}}-buildx-${{github.sha}} 39 | restore-keys: | 40 | ${{runner.os}}-${{matrix.platform}}-buildx 41 | - uses: ruby/setup-ruby@v1 42 | with: 43 | ruby-version: "3.3" 44 | bundler-cache: true 45 | - name: Generate docker image names 46 | id: rcd_config 47 | run: | 48 | bundle exec ruby -e ' \ 49 | require "rake_compiler_dock"; \ 50 | print "image_name="; \ 51 | puts RakeCompilerDock::Starter.container_image_name(:platform => %q(${{matrix.platform}})); \ 52 | print "snapshot_name="; \ 53 | puts RakeCompilerDock::Starter.container_image_name(:platform => %q(${{matrix.platform}}), :version => %q(snapshot)); \ 54 | if %q(${{matrix.platform}}).end_with?("-gnu"); \ 55 | print "generic_linux_snapshot_name="; \ 56 | puts RakeCompilerDock::Starter.container_image_name(:platform => %q(${{matrix.platform}}), :version => %q(snapshot)).chomp("-gnu"); \ 57 | end \ 58 | ' | tee -a $GITHUB_OUTPUT 59 | - name: Build docker image 60 | env: 61 | RCD_DOCKER_BUILD: docker buildx build --cache-from=type=local,src=tmp/build-cache --cache-to=type=local,dest=tmp/build-cache-new --load 62 | run: | 63 | docker buildx create --driver docker-container --use 64 | bundle exec rake build:${{matrix.platform}} 65 | # move build cache and remove outdated layers 66 | rm -rf tmp/build-cache 67 | mv tmp/build-cache-new tmp/build-cache 68 | - uses: docker/login-action@v3 69 | with: 70 | registry: ghcr.io 71 | username: ${{github.actor}} 72 | password: ${{secrets.GITHUB_TOKEN}} 73 | - name: Push the docker image 74 | run: | 75 | docker images 76 | docker tag ${{steps.rcd_config.outputs.image_name}} ${{steps.rcd_config.outputs.snapshot_name}} 77 | docker push ${{steps.rcd_config.outputs.snapshot_name}} 78 | - name: Push a generic linux image 79 | if: ${{ steps.rcd_config.outputs.generic_linux_snapshot_name }} 80 | run: | 81 | docker tag ${{steps.rcd_config.outputs.image_name}} ${{steps.rcd_config.outputs.generic_linux_snapshot_name}} 82 | docker push ${{steps.rcd_config.outputs.generic_linux_snapshot_name}} 83 | -------------------------------------------------------------------------------- /.github/workflows/release-images.yml: -------------------------------------------------------------------------------- 1 | name: Release docker images to GHCR 2 | concurrency: 3 | group: "${{github.workflow}}-${{github.ref}}" 4 | cancel-in-progress: true 5 | 6 | # 7 | # This workflow assumes the maintainer has chosen the appropriate tag in the workflow dispatch UI. 8 | # 9 | on: 10 | workflow_dispatch: 11 | inputs: 12 | tag: 13 | description: "Tag name to release" 14 | required: true 15 | 16 | jobs: 17 | build: 18 | name: "build ${{ inputs.tag }} ${{ matrix.platform }}" 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | platform: 23 | - aarch64-linux-gnu 24 | - aarch64-linux-musl 25 | - aarch64-mingw-ucrt 26 | - arm-linux-gnu 27 | - arm-linux-musl 28 | - arm64-darwin 29 | - jruby 30 | - x64-mingw-ucrt 31 | - x64-mingw32 32 | - x86-linux-gnu 33 | - x86-linux-musl 34 | - x86-mingw32 35 | - x86_64-darwin 36 | - x86_64-linux-gnu 37 | - x86_64-linux-musl 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v4 41 | with: 42 | ref: ${{ inputs.tag }} 43 | - name: Use cache from primary pipeline 44 | uses: actions/cache@v4 45 | with: 46 | path: tmp/build-cache 47 | key: ${{runner.os}}-${{matrix.platform}}-buildx-${{github.sha}} 48 | restore-keys: | 49 | ${{runner.os}}-${{matrix.platform}}-buildx 50 | - uses: ruby/setup-ruby@v1 51 | with: 52 | ruby-version: "3.3" 53 | bundler-cache: true 54 | - name: Generate docker image names 55 | id: rcd_config 56 | run: | 57 | bundle exec ruby -e ' \ 58 | require "rake_compiler_dock"; \ 59 | print "image_name="; \ 60 | puts RakeCompilerDock::Starter.container_image_name(:platform => %q(${{matrix.platform}})); \ 61 | if %q(${{matrix.platform}}).end_with?("-gnu"); \ 62 | print "generic_linux_image_name="; \ 63 | puts RakeCompilerDock::Starter.container_image_name(:platform => %q(${{matrix.platform}})).chomp("-gnu"); \ 64 | end \ 65 | ' | tee -a $GITHUB_OUTPUT 66 | - name: Build docker image 67 | env: 68 | RCD_DOCKER_BUILD: docker buildx build --cache-from=type=local,src=tmp/build-cache --cache-to=type=local,dest=tmp/build-cache-new --load 69 | run: | 70 | docker buildx create --driver docker-container --use 71 | bundle exec rake build:${{matrix.platform}} 72 | # move build cache and remove outdated layers 73 | rm -rf tmp/build-cache 74 | mv tmp/build-cache-new tmp/build-cache 75 | - uses: docker/login-action@v3 76 | with: 77 | registry: ghcr.io 78 | username: ${{github.actor}} 79 | password: ${{secrets.GITHUB_TOKEN}} 80 | - name: Push the docker image 81 | run: | 82 | docker images 83 | docker push ${{steps.rcd_config.outputs.image_name}} 84 | - name: Push a generic linux image 85 | if: ${{ steps.rcd_config.outputs.generic_linux_image_name }} 86 | run: | 87 | docker tag ${{steps.rcd_config.outputs.image_name}} ${{steps.rcd_config.outputs.generic_linux_image_name}} 88 | docker push ${{steps.rcd_config.outputs.generic_linux_image_name}} 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Dockerfile.mri.aarch64-linux-gnu 4 | /Dockerfile.mri.aarch64-linux-musl 5 | /Dockerfile.mri.arm-linux-gnu 6 | /Dockerfile.mri.arm-linux-musl 7 | /Dockerfile.mri.arm64-darwin 8 | /Dockerfile.mri.x64-mingw-ucrt 9 | /Dockerfile.mri.x64-mingw32 10 | /Dockerfile.mri.x86-linux-gnu 11 | /Dockerfile.mri.x86-linux-musl 12 | /Dockerfile.mri.x86-mingw32 13 | /Dockerfile.mri.x86_64-darwin 14 | /Dockerfile.mri.x86_64-linux-gnu 15 | /Dockerfile.mri.x86_64-linux-musl 16 | /Gemfile.lock 17 | /_yardoc/ 18 | /cache/ 19 | /coverage/ 20 | /doc/ 21 | /pkg/ 22 | /spec/reports/ 23 | /tmp/ 24 | *.bundle 25 | *.so 26 | *.o 27 | *.a 28 | mkmf.log 29 | .byebug_history 30 | test/rcd_test/Gemfile.lock 31 | test/rcd_test/pkg/ 32 | test/rcd_test/tmp/ 33 | test/rcd_test/vendor/ 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This document is intended for the rake-compiler-dock contributors. 4 | 5 | ## Cutting a release 6 | 7 | - prep 8 | - [ ] make sure CI is green! 9 | - [ ] update `History.md` and `lib/rake_compiler_dock/version.rb` 10 | - [ ] commit and create a git tag 11 | - option 1: build locally 12 | - build 13 | - [ ] run the steps below to generate the images locally 14 | - [ ] run `gem build rake-compiler-dock` 15 | - push 16 | - [ ] run `bundle exec rake release:images` 17 | - [ ] run `gem push pkg/rake-compiler-dock*gem` 18 | - [ ] run `git push && git push --tags` 19 | - option 2: build with github actions 20 | - build and push images from github actions 21 | - [ ] run `git push && git push --tags` 22 | - [ ] wait for CI to go green on the tag 23 | - [ ] go to the [release-images pipeline][] and run the workflow on the tag 24 | - build the gem locally and push it 25 | - [ ] locally, run `gem build rake-compiler-dock` 26 | - [ ] run `gem push pkg/rake-compiler-dock*gem` 27 | - announce 28 | - [ ] create a release at https://github.com/rake-compiler/rake-compiler-dock/releases 29 | 30 | [release-images pipeline]: https://github.com/rake-compiler/rake-compiler-dock/actions/workflows/release-images.yml 31 | 32 | 33 | ## Building a versioned image 34 | 35 | We want to preserve the cache if we can, so patch releases don't change _all_ the layers. There are a few ways to do this. 36 | 37 | 38 | ### Using local docker 39 | 40 | If you're going to keep your local docker cache, around, you can run things in parallel: 41 | 42 | ``` 43 | bundle exec rake build 44 | ``` 45 | 46 | 47 | ### Use a custom docker command 48 | 49 | If you're a pro and want to run a custom command and still run things in parallel: 50 | 51 | ``` 52 | export RCD_DOCKER_BUILD="docker build --arg1 --arg2" 53 | bundle exec rake build 54 | ``` 55 | 56 | 57 | ### Using the buildx backend and cache 58 | 59 | Here's one way to leverage the buildx cache, which will turn off parallel builds but generates an external cache directory that can be saved and re-used: 60 | 61 | ``` 62 | export RCD_USE_BUILDX_CACHE=t 63 | docker buildx create --use --driver=docker-container 64 | bundle exec rake build 65 | ``` 66 | 67 | -------------------------------------------------------------------------------- /Dockerfile.jruby: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | RUN apt-get -y update && \ 5 | apt-get install -y sudo wget curl git-core build-essential xz-utils unzip dirmngr && \ 6 | apt-get install -y openjdk-11-jdk-headless maven && \ 7 | rm -rf /var/lib/apt/lists/* 8 | 9 | ## 10 | ## install rbenv and ruby-build 11 | ## 12 | RUN groupadd -r rubyuser && useradd -r -g rubyuser -G sudo -p "" --create-home rubyuser 13 | 14 | ENV RBENV_ROOT=/usr/local/rbenv RBENV_RUBIES="jruby-9.4.0.0" 15 | 16 | # chown after running `rbenv init` because that command creates some subdirectories 17 | RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \ 18 | git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \ 19 | \ 20 | echo "export RBENV_ROOT=/usr/local/rbenv" >> /etc/rubybashrc && \ 21 | echo "export PATH=$RBENV_ROOT/bin:\$PATH" >> /etc/rubybashrc && \ 22 | $RBENV_ROOT/bin/rbenv init - --no-rehash bash >> /etc/rubybashrc && \ 23 | echo "source /etc/rubybashrc" >> /etc/bashrc && \ 24 | echo "source /etc/rubybashrc" >> /etc/bash.bashrc && \ 25 | \ 26 | chown -R rubyuser:rubyuser ${RBENV_ROOT} && \ 27 | find ${RBENV_ROOT} -type d -print0 | sudo xargs -0 chmod g+sw 28 | ENV BASH_ENV=/etc/rubybashrc 29 | 30 | 31 | ## 32 | ## set up rake-compiler and install bootstrap rubies 33 | ## 34 | USER rubyuser 35 | 36 | # Install the bootstrap rubies 37 | RUN bash -c " \ 38 | echo 'gem: --no-ri --no-rdoc --no-document' >> ~/.gemrc && \ 39 | export CFLAGS='-s -O3 -fno-fast-math -fPIC' && \ 40 | for v in ${RBENV_RUBIES} ; do \ 41 | rbenv install \$v -- --disable-install-doc ; \ 42 | done && \ 43 | find ${RBENV_ROOT} -type d -print0 | sudo xargs -0 chmod g+w \ 44 | " 45 | 46 | # Install rake-compiler and patch it to build and install static libraries for Linux rubies 47 | COPY build/patches /home/rubyuser/patches 48 | RUN bash -c " \ 49 | for v in ${RBENV_RUBIES} ; do \ 50 | rbenv shell \$v && \ 51 | gem install rake-compiler -v1.2.9 && \ 52 | cd ${RBENV_ROOT}/versions/\$v/lib/ruby/gems/*/gems/rake-compiler-1.2.9 && \ 53 | for patch in /home/rubyuser/patches/rake-compiler-1.2.9/*.patch ; do \ 54 | patch -p1 < \$patch ; \ 55 | done \ 56 | done \ 57 | " 58 | 59 | # Install rake-compiler's cross rubies in global dir instead of /root 60 | RUN sudo mkdir -p /usr/local/rake-compiler && \ 61 | sudo chown rubyuser.rubyuser /usr/local/rake-compiler && \ 62 | ln -s /usr/local/rake-compiler ~/.rake-compiler 63 | 64 | 65 | USER root 66 | 67 | # Install SIGINT forwarder 68 | COPY build/sigfw.c /root/ 69 | RUN gcc $HOME/sigfw.c -o /usr/local/bin/sigfw 70 | 71 | # Install user mapper 72 | COPY build/runas /usr/local/bin/ 73 | 74 | # Install sudoers configuration 75 | COPY build/sudoers /etc/sudoers.d/rake-compiler-dock 76 | 77 | RUN bash -c "rbenv global jruby-9.4.0.0" 78 | 79 | CMD bash 80 | -------------------------------------------------------------------------------- /Dockerfile.mri.erb: -------------------------------------------------------------------------------- 1 | <% image = "ubuntu:20.04" %> 2 | FROM <%= image %> 3 | 4 | ## 5 | ## Install required base packages for compiling ruby 6 | ## 7 | ENV DEBIAN_FRONTEND=noninteractive 8 | RUN apt-get -y update && \ 9 | apt-get install -y sudo wget curl git-core build-essential xz-utils unzip dirmngr && \ 10 | apt-get install -y autoconf cmake pkg-config zlib1g-dev libreadline-dev libsqlite0-dev libssl-dev libyaml-dev libffi-dev && \ 11 | rm -rf /var/lib/apt/lists/* 12 | 13 | ## 14 | ## Install cross compilers 15 | ## 16 | <% if platform =~ /x64-mingw-ucrt/ %> 17 | COPY --from=larskanis/mingw64-ucrt:20.04 \ 18 | /build/binutils-mingw-w64-x86-64_*.deb \ 19 | /build/g++-mingw-w64-x86-64_*.deb \ 20 | /build/gcc-mingw-w64-base_*.deb \ 21 | /build/gcc-mingw-w64-x86-64_*.deb \ 22 | /build/mingw-w64-common_*.deb \ 23 | /build/mingw-w64-x86-64-dev_*.deb \ 24 | /debs/ 25 | RUN dpkg -i /debs/*.deb 26 | 27 | <% elsif platform =~ /aarch64-mingw-ucrt/ %> 28 | RUN wget https://github.com/mstorsjo/llvm-mingw/releases/download/20250114/llvm-mingw-20250114-ucrt-ubuntu-20.04-<%= RUBY_PLATFORM[/^\w+/] %>.tar.xz && \ 29 | tar xf llvm-mingw*.tar.xz && \ 30 | mv `ls -d llvm-mingw-*/` /llvm-mingw && \ 31 | echo "export PATH=/llvm-mingw/bin:\$PATH" >> /etc/rubybashrc && \ 32 | rm -r /llvm-mingw/bin/i686-w64* /llvm-mingw/bin/armv7-w64* /llvm-mingw/bin/x86_64-w64* /llvm-mingw/i686-w64* /llvm-mingw/armv7-w64* /llvm-mingw/x86_64-w64* 33 | 34 | <% elsif platform =~ /linux-musl/ %> 35 | COPY build/mk_musl_cross.sh /tmp 36 | RUN /tmp/mk_musl_cross.sh <%= target %> 37 | 38 | <% else %> 39 | RUN apt-get -y update && \ 40 | apt-get install -y <% 41 | if platform =~ /darwin/ %> clang python lzma-dev libxml2-dev libssl-dev libc++-10-dev <% end %><% 42 | if platform =~ /aarch64-linux-gnu/ %> gcc-aarch64-linux-gnu g++-aarch64-linux-gnu <% end %><% 43 | if platform =~ /arm-linux-gnu/ %> gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf <% end %><% 44 | if platform =~ /x86-linux-gnu/ %> gcc-i686-linux-gnu g++-i686-linux-gnu <% end %><% 45 | if platform =~ /x86_64-linux-gnu/ %> gcc-x86-64-linux-gnu g++-x86-64-linux-gnu <% end %><% 46 | if platform =~ /x86-mingw32/ %> gcc-mingw-w64-i686 g++-mingw-w64-i686 <% end %><% 47 | if platform =~ /x64-mingw32/ %> gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 <% end %> && \ 48 | rm -rf /var/lib/apt/lists/* 49 | <% end %> 50 | 51 | <% if platform =~ /darwin/ %> 52 | COPY build/mk_osxcross.sh /tmp 53 | RUN /tmp/mk_osxcross.sh 54 | <% end %> 55 | 56 | 57 | ## 58 | ## install rbenv and ruby-build 59 | ## 60 | RUN groupadd -r rubyuser && useradd -r -g rubyuser -G sudo -p "" --create-home rubyuser 61 | 62 | ENV RBENV_ROOT=/usr/local/rbenv 63 | 64 | # chown after running `rbenv init` because that command creates some subdirectories 65 | RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \ 66 | git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \ 67 | \ 68 | echo "export RBENV_ROOT=/usr/local/rbenv" >> /etc/rubybashrc && \ 69 | echo "export PATH=$RBENV_ROOT/bin:\$PATH" >> /etc/rubybashrc && \ 70 | $RBENV_ROOT/bin/rbenv init - --no-rehash bash >> /etc/rubybashrc && \ 71 | echo "source /etc/rubybashrc" >> /etc/bashrc && \ 72 | echo "source /etc/rubybashrc" >> /etc/bash.bashrc && \ 73 | \ 74 | chown -R rubyuser:rubyuser ${RBENV_ROOT} && \ 75 | find ${RBENV_ROOT} -type d -print0 | sudo xargs -0 chmod g+sw 76 | ENV BASH_ENV=/etc/rubybashrc 77 | 78 | 79 | ## 80 | ## set up rake-compiler and install bootstrap rubies 81 | ## 82 | USER rubyuser 83 | 84 | ENV RBENV_RUBIES="3.1.6 3.4.1" 85 | 86 | # Install the bootstrap rubies 87 | RUN bash -c " \ 88 | echo 'gem: --no-ri --no-rdoc --no-document' >> ~/.gemrc && \ 89 | export CFLAGS='-s -O3 -fno-fast-math -fPIC' && \ 90 | for v in ${RBENV_RUBIES} ; do \ 91 | rbenv install \$v -- --disable-install-doc ; \ 92 | done && \ 93 | find ${RBENV_ROOT} -type d -print0 | sudo xargs -0 chmod g+w \ 94 | " 95 | 96 | # Install rake-compiler and patch it to build and install static libraries for Linux rubies 97 | COPY build/patches /home/rubyuser/patches 98 | RUN bash -c " \ 99 | for v in ${RBENV_RUBIES} ; do \ 100 | rbenv shell \$v && \ 101 | gem install rake-compiler -v1.2.9 && \ 102 | cd ${RBENV_ROOT}/versions/\$v/lib/ruby/gems/*/gems/rake-compiler-1.2.9 && \ 103 | for patch in /home/rubyuser/patches/rake-compiler-1.2.9/*.patch ; do \ 104 | patch -p1 < \$patch ; \ 105 | done \ 106 | done \ 107 | " 108 | 109 | # Install rake-compiler's cross rubies in global dir instead of /root 110 | RUN sudo mkdir -p /usr/local/rake-compiler && \ 111 | sudo chown rubyuser.rubyuser /usr/local/rake-compiler && \ 112 | ln -s /usr/local/rake-compiler ~/.rake-compiler 113 | 114 | <% 115 | # 116 | # Build ruby versions with ruby2_keywords using ruby-3.x 117 | # 118 | # Note that parallel builds of ruby with make flag `-j` are often flaky, see 119 | # https://bugs.ruby-lang.org/issues/18506 120 | # 121 | xrubies_build_plan = if platform =~ /x64-mingw-ucrt/ 122 | [ 123 | # Rubyinstaller-3.1+ is platform x64-mingw-ucrt 124 | ["3.4.1:3.3.7:3.2.6:3.1.6", "3.4.1"], 125 | ] 126 | elsif platform =~ /aarch64-mingw-ucrt/ 127 | [ 128 | ["3.4.1", "3.1.6"], 129 | ] 130 | elsif platform =~ /x64-mingw32/ 131 | [ 132 | # Rubyinstaller prior to 3.1 is platform x64-mingw32 133 | ["3.0.7:2.7.8", "3.1.6"], 134 | ] 135 | else 136 | [ 137 | ["2.7.8", "3.1.6"], 138 | ["3.4.1:3.3.7:3.2.6:3.1.6:3.0.7", "3.4.1"], 139 | ] 140 | end 141 | 142 | strip = '-s' if platform !~ /darwin|aarch64-mingw/ 143 | 144 | xrubies_build_plan.each do |xrubies, bootstrap_ruby_version| %> 145 | RUN bash -c " \ 146 | rbenv shell <%= bootstrap_ruby_version %> && \ 147 | export CPPFLAGS='<%= "-D__USE_MINGW_ANSI_STDIO=1" if platform =~ /x64-mingw-ucrt/ %>' && \ 148 | export CFLAGS='-O1 -fno-omit-frame-pointer -fno-fast-math -fstack-protector-strong <%= strip %>' && \ 149 | export LDFLAGS='-pipe <%= strip %>' && \ 150 | <%= "export LIBS='-l:libssp.a' &&" if platform =~ /mingw/ %> \ 151 | <%= "export CC=#{target}-clang &&" if platform =~ /darwin/ %> \ 152 | export MAKE='make V=1' && \ 153 | rake-compiler cross-ruby VERSION=<%= xrubies %> HOST=<%= target %> && \ 154 | rm -rf ~/.rake-compiler/builds ~/.rake-compiler/sources \ 155 | " 156 | <% end %> 157 | # " 158 | 159 | <% if platform =~ /linux/ %> 160 | # Avoid linking against libruby shared object. 161 | # See also https://github.com/rake-compiler/rake-compiler-dock/issues/13 162 | RUN find /usr/local/rake-compiler/ruby/*linux*/ -name libruby.so | xargs rm 163 | RUN find /usr/local/rake-compiler/ruby/*linux*/ -name libruby-static.a | while read f ; do cp $f `echo $f | sed s/-static//` ; done 164 | RUN find /usr/local/rake-compiler/ruby/*linux*/ -name libruby.a | while read f ; do ar t $f | xargs ar d $f ; done 165 | RUN find /usr/local/rake-compiler/ruby/*linux*/ -name mkmf.rb | while read f ; do sed -i ':a;N;$!ba;s/TRY_LINK = [^\n]*\n[^\n]*\n[^\n]*LOCAL_LIBS)/& -lruby-static -lpthread -lrt -ldl/' $f ; done 166 | <% end %> 167 | 168 | <% if platform =~ /mingw/ %> 169 | # RubyInstaller doesn't install libgcc -> link it static. 170 | RUN find /usr/local/rake-compiler/ruby/*mingw*/ -name rbconfig.rb | while read f ; do sed -i 's/."LDFLAGS". = "/&-static-libgcc /' $f ; done 171 | # Don't link to static libruby 172 | RUN find /usr/local/rake-compiler/ruby -name lib*-ruby*.dll.a | while read f ; do n=`echo $f | sed s/.dll//` ; mv $f $n ; done 173 | <% end %> 174 | 175 | <% if platform =~ /darwin/ %> 176 | # for rubies which use `-bundle_loader` on darwin 177 | # - the upstream change: https://github.com/ruby/ruby/pull/6193 178 | # - how we got to this solution: https://github.com/rake-compiler/rake-compiler-dock/issues/87 179 | # 180 | # note that ruby/ruby#6193 was backported to 2.7.7, 3.0.5, and 3.1.3 181 | # - see https://github.com/rake-compiler/rake-compiler-dock/issues/134 for more notes 182 | RUN find /usr/local/rake-compiler/ruby/*/*/lib/ruby -name rbconfig.rb | while read f ; do \ 183 | sed -E -i 's/(\["EXTDLDFLAGS"\] = ".*)(-bundle_loader)/\1-Wl,-flat_namespace \2/' $f ; \ 184 | done 185 | <% end %> 186 | 187 | 188 | ## 189 | ## Final adjustments 190 | ## 191 | USER root 192 | 193 | <% if platform =~ /(x64|x86)-mingw/ %> 194 | # Install wrappers for strip commands as a workaround for "Protocol error" in boot2docker. 195 | COPY build/strip_wrapper_vbox /root/ 196 | RUN mv /usr/bin/<%= target %>-strip /usr/bin/<%= target %>-strip.bin && \ 197 | ln /root/strip_wrapper_vbox /usr/bin/<%= target %>-strip 198 | 199 | # Use posix pthread for mingw so that C++ standard library for thread could be 200 | # available such as std::thread, std::mutex, so on. 201 | # https://sourceware.org/pthreads-win32/ 202 | RUN printf "1\n" | update-alternatives --config <%= target %>-gcc && \ 203 | printf "1\n" | update-alternatives --config <%= target %>-g++ 204 | <% end %> 205 | 206 | <% if platform =~ /aarch64-mingw/ %> 207 | # Fool libtool to allow building a shared library although linking to libclang_rt.builtins-aarch64.a 208 | # 209 | # Linker option "-llibclang_rt.builtins-aarch64.a" is required on Windows on ARM when compiling C++ and when clang is called with -nostdlib (due to be invoked by libtool). 210 | # Unfortunately libtool then forces a static build. 211 | # This is a ugly hack to make libtool beleave that the library is a DLL instead of an archive and to allow building a DLL instand. 212 | # 213 | RUN sudo cp /llvm-mingw/aarch64-w64-mingw32/bin/libc++.dll /llvm-mingw/aarch64-w64-mingw32/lib/libclang_rt.builtins-aarch64.0 214 | <% end %> 215 | 216 | <% if platform =~ /darwin/ %> 217 | # Install wrapper around strip to re-sign binaries (ad-hoc signature) 218 | COPY build/strip_wrapper_codesign /root/ 219 | RUN mv /opt/osxcross/target/bin/<%= target %>-strip /opt/osxcross/target/bin/<%= target %>-strip.bin && \ 220 | ln /root/strip_wrapper_codesign /opt/osxcross/target/bin/<%= target %>-strip 221 | 222 | # Install mig which is a Macos specific RPC code generator, which is part of xcode 223 | RUN apt-get -y update && \ 224 | apt-get install -y bison flex && \ 225 | rm -rf /var/lib/apt/lists/* 226 | RUN git clone --branch=cross_platform https://github.com/markmentovai/bootstrap_cmds && \ 227 | cd bootstrap_cmds && \ 228 | autoreconf --install && \ 229 | sh configure && \ 230 | make && \ 231 | sed -E -i 's/^cppflags=(.*)/cppflags=(\1 "-D<%= platform =~ /arm64/ ? "__arm64__" : "__x86_64__" %>" "-I\/opt\/osxcross\/target\/SDK\/MacOSX11.1.sdk\/usr\/include")/' migcom.tproj/mig.sh && \ 232 | sudo make install 233 | <% end %> 234 | 235 | <% if platform =~ /arm64-darwin/ %> 236 | # Add a arm64 darwin target as alternative to aarch64 237 | RUN grep -E 'rbconfig-aarch64-darwin' /usr/local/rake-compiler/config.yml | sed 's/rbconfig-[a-z0-9_]*-darwin/rbconfig-<%= platform %>/' >> /usr/local/rake-compiler/config.yml 238 | <% end %> 239 | 240 | # Install SIGINT forwarder 241 | COPY build/sigfw.c /root/ 242 | RUN gcc $HOME/sigfw.c -o /usr/bin/sigfw 243 | 244 | # Install user mapper 245 | COPY build/runas /usr/bin/ 246 | COPY build/rcd-env.sh /etc/profile.d/ 247 | RUN echo 'source /etc/profile.d/rcd-env.sh' >> /etc/rubybashrc 248 | 249 | # Install sudoers configuration 250 | COPY build/sudoers /etc/sudoers.d/rake-compiler-dock 251 | 252 | RUN bash -c "rbenv global 3.4.1" 253 | 254 | ENV RUBY_CC_VERSION=3.4.1:3.3.7:3.2.6:3.1.6:3.0.7:2.7.8 255 | 256 | CMD bash 257 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in rake-compiler-dock.gemspec 4 | gemspec 5 | 6 | group :nice do 7 | gem "test-unit-notify", "~> 1.0", :platform => :ruby 8 | end 9 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # `rake-compiler/rake-compiler-dock` Changelog 2 | 3 | ## Unreleased 4 | 5 | - Drop support for Ruby 2.4, 2.5, and 2.6. #153 @ianks @flavorjones 6 | - Remove `rbenv` ruby version 2.5.9 from the build container. #153 @ianks @flavorjones 7 | 8 | ### CRuby container summary 9 | 10 | - native rubies: 3.4.1 (default), 3.1.6 11 | - `RUBY_CC_VERSION=3.4.1:3.3.7:3.2.6:3.1.6:3.0.7:2.7.8` 12 | 13 | 14 | ## v1.9.1 / 2025-01-20 15 | 16 | ### Features 17 | 18 | - Introduce `RakeCompilerDock.set_ruby_cc_version` to make it easier to set `RUBY_CC_VERSION` without hardcoding specific patch-level ruby versions. See the README for more deteails. #149, #150 @flavorjones 19 | 20 | 21 | ## v1.9.0 / 2025-01-19 22 | 23 | ### Changes 24 | 25 | - Bump Ruby 3.3 to v3.3.7 (from v3.3.5). #147 @flavorjones 26 | - The default `rbenv` ruby in the container is now Ruby v3.4.1 (previously the default was 3.1.6), which allows gems to drop Ruby 3.1 from their gemspecs. Fixes #145. #146 @flavorjones 27 | - Ruby 3.4.x, 3.3.x, 3.2.x, 3.1.x, and 3.0.x are cross-compiled using Ruby v3.4.1. #146 @flavorjones 28 | 29 | ### CRuby container summary 30 | 31 | - native rubies: 3.4.1 (default), 3.1.6, 2.5.9 32 | - `RUBY_CC_VERSION=3.4.1:3.3.7:3.2.6:3.1.6:3.0.7:2.7.8:2.6.10:2.5.9:2.4.10` 33 | 34 | 35 | ## v1.8.0 / 2025-01-05 36 | 37 | ### Notable changes 38 | 39 | All versions of Ruby in the build containers are the latest patch version. #135 @flavorjones 40 | - `RUBY_CC_VERSION=3.4.1:3.3.5:3.2.6:3.1.6:3.0.7:2.7.8:2.6.10:2.5.9:2.4.10` 41 | 42 | ### Other changes 43 | 44 | - Base ruby updated to v3.1.6 (from v3.1.3), which is now the default `rbenv` ruby. 45 | - (Darwin) Set `OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1` to successfully build the 3.0 series. 46 | - (Darwin) The linker flags include `-Wl,-flat_namespace` for 2.7, 3.0, 3.1, 3.2, 3.3, and 3.4 rubies. Previously this was only needed for 3.2+, but code backported to 2.7.7, 3.0.5, and 3.1.3 required it for those versions as well. 47 | 48 | ### CRuby container summary 49 | 50 | - native rubies: 3.1.6 (default), 2.5.9 51 | - `RUBY_CC_VERSION=3.4.1:3.3.5:3.2.6:3.1.6:3.0.7:2.7.8:2.6.10:2.5.9:2.4.10` 52 | 53 | 54 | ## v1.7.1 / 2025-01-03 55 | 56 | - Bump rake-compiler dependency to v1.2.9 (from v1.2.5). 57 | - Bump Ruby to v3.4.1 (from v3.4.0) to address `ruby_abi_version` issues. 58 | 59 | 60 | ## v1.7.0 / 2024-12-25 61 | 62 | ### Ruby 3.4 support 63 | 64 | - Add Ruby 3.4.0 cross-compilation support. 65 | 66 | 67 | ## v1.7.0.rc1 / 2024-12-13 68 | 69 | ### Ruby 3.4 support 70 | 71 | - Add Ruby 3.4.0-rc1 cross-compilation support. 72 | 73 | 74 | ## v1.6.0 / 2024-12-13 75 | 76 | ### Notable changes 77 | 78 | #### Standardizing all Linux build images on Ubuntu 79 | 80 | In this release, we upgraded the base image for the `x86_64-linux-gnu` and `x86-linux-gnu` images from `manylinux2014` to `ubuntu:20.04`. (`manylinux2014` reached end-of-life earlier this year.) As a result, all of the build images are using the same base image, simplifying things considerably. (#122, #126) @flavorjones 81 | 82 | ⚠ **Note** there are two important changes due to this upgrade: 83 | 84 | 1. The minimum supported version of GLIBC for the `x86*-linux-gnu` images has increased from 2.17 to 2.29 for `x86_64` and `x86` architectures. (Note that GLIBC was already pinned to 2.29 for ARM architectures.) 85 | 2. Precompiled gems built with the `x86*linux-gnu` images are less likely to be compatible with Musl libc, and may segfault when run on Musl systems. 86 | 87 | For (2) above, if you have been shipping a single `x86_64-linux` native gem for both GNU and Musl systems, **please make sure you test your gems on a Musl system before shipping them**. See the [actions workflow in flavorjones/ruby-c-extensions-explained](https://github.com/flavorjones/ruby-c-extensions-explained/blob/6619a0d94e627897838a79144704387db65a03bc/.github/workflows/precompiled.yml#L137) for an example of how to do this rigorously. 88 | 89 | 90 | #### Ruby 3.3 support upgraded to `3.3.5` 91 | 92 | Update Ruby 3.3 support from 3.3.0-rc1 to 3.3.5. Note that the 3.3.x releases are not usable until 3.3.5 because of https://bugs.ruby-lang.org/issues/20088. 93 | 94 | ⚠ **Note** that if you were specifying `3.3.0` in your `RUBY_CC_VERSION` environment variable, that string must be updated to `3.3.5`. 95 | 96 | 97 | ### Added 98 | 99 | - Add support for the `SOURCE_DATE_EPOCH` environment variable, which can be used to create reproducible builds. (#128) @segiddins 100 | 101 | 102 | ## 1.5.2 / 2024-07-30 103 | 104 | With CentOS 7 becoming EOL as of 2024-06-30, `yum` will no longer work in the `x86_64-linux-gnu` and 105 | `x86-linux-gnu` images shipped with v1.5.1 and earlier. 106 | 107 | In this release, we've updated the `manylinux2014` base image for the `x86_64-linux-gnu` and 108 | `x86-linux-gnu` images to point at a version patched by `pypa` to use valid yum mirrors. See 109 | https://github.com/pypa/manylinux/pull/1628 for more information. 110 | 111 | 112 | ## 1.5.1 / 2024-06-03 113 | 114 | ### Improvements 115 | 116 | The `x86_64-linux-gnu` and `x86-linux-gnu` containers (based on `manylinux_2014`) now have a modern version of `pkg-config`, v0.29.2, installed on them in `/usr/local/bin/pkg-config`. The distro's version (v0.27.1) is still in `/usr/bin/pkg-config` if you need to fall back for some reason, but the newer version will be used by default and should be backwards-compatible. (#121, #123 by @flavorjones) 117 | 118 | 119 | ## 1.5.0 / 2024-02-25 120 | 121 | ### Notable changes 122 | 123 | #### First-class Linux `musl` libc support 124 | 125 | * Add Linux musl cross build targets `aarch64-linux-musl`, `arm-linux-musl`, `x86-linux-musl`, and `x86_64-linux-musl`. #75, #111 (@flavorjones) 126 | * Add Linux cross build targets `aarch64-linux-gnu`, `arm-linux-gnu`, `x86-linux-gnu`, and `x86_64-linux-gnu`. #111 (@flavorjones) 127 | * The cross build targets `aarch64-linux`, `arm-linux`, `x86-linux`, and `x86_64-linux` are now aliases for the `*-linux-gnu` targets. #111 (@flavorjones) 128 | 129 | **Please read the README for details and caveats!** 130 | 131 | 132 | ### Improvements 133 | 134 | * Predefined user and group list is more complete, and represents the union of users and groups across all RCD images. 135 | 136 | 137 | ### Changes to build containers 138 | 139 | * Replace `rvm` with `rbenv` and `ruby-build` in the build containers. 140 | - `rvm` has been replaced by `rbenv` and `ruby-build` 141 | - no longer applying sendfile patches to bootstrap rubies 142 | - no longer updating gems belonging to the bootstrap rubies 143 | - user `rvm` no longer exists, replaced by `rubyuser` 144 | 145 | Users of the `rake-compiler-dock` gem should not be affected by this change. However, users of the raw containers may be affected if there are assumptions about `rvm`. 146 | 147 | 148 | 1.4.0 / 2023-12-27 149 | ------------------ 150 | 151 | * Add Ruby 3.3.0-rc1 cross-compilation support. #109, #105 (@flavorjones) 152 | * Update to rake-compiler 1.2.5. #108 (@flavorjones) 153 | 154 | 155 | 1.3.1 / 2023-10-14 156 | ------------------ 157 | 158 | * In the container, `strip` will re-codesign binaries on darwin. #104 (@flavorjones) 159 | * Update to rake-compiler 1.2.2. #101 (@flavorjones) 160 | 161 | 162 | 1.3.0 / 2023-01-11 163 | ------------------ 164 | 165 | * Add Ruby 3.2 cross-compilation support. 166 | * Update RVM installations to latest rubygems. 167 | * Update to rake-compiler 1.2.1. 168 | * Reduce pre-installed gems to only rake-compiler and bundler. 169 | * Install yaml and ffi development headers in the base images, for psych and ffi gem compilation. 170 | * Ensure autoconf is installed in the base iamges. 171 | * Bump JRuby to 9.4.0.0 172 | * Move docker images to ghcr.io/rake-compiler: 173 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-jruby` 174 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-aarch64-linux` 175 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-arm-linux` 176 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-arm64-darwin` 177 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-x64-mingw-ucrt` 178 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-x64-mingw32` 179 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-x86-linux` 180 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-x86-mingw32` 181 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-x86_64-darwin` 182 | * `ghcr.io/rake-compiler/rake-compiler-dock-image:1.3.0-mri-x86_64-linux` 183 | * Start publishing weekly image snapshots. 184 | 185 | 186 | 1.2.2 / 2022-06-27 187 | ------------------ 188 | 189 | * Fix ucrt libruby file naming for static build. #69 190 | 191 | 192 | 1.2.1 / 2022-03-17 193 | ------------------ 194 | 195 | * Fix testing for ruby C-API functions in mkmf. #65, #67 196 | * Use openjdk 11 to make maven work on ubuntu 20.04. #64 197 | * Remove x86_64-w64-mingw32-pkg-config from the x64-mingw-ucrt image. #63 198 | * Add a patch for math.h to use gcc builtins and to improve compat with `musl` libc-based systems. #42 199 | 200 | 201 | 1.2.0 / 2022-01-04 202 | ------------------ 203 | 204 | * Add Ruby-3.1 as native rvm and cross ruby versions. 205 | * Remove Ruby-2.2 and 2.3 from RUBY_CC_VERSION and cross rubies. 206 | * Add Linux cross build targets "arm-linux" and "aarch64-linux". 207 | * Add cross build target "x64-mingw-ucrt" for ruby-3.1 on Windows. 208 | * Add `wget` and `git` to Linux image. 209 | * Add `maven` to JRuby image. 210 | * Update JRuby to 9.3.2.0 211 | * Fix default value of platform for JRuby. #50 212 | * Update openjdk version from 14 to 17 213 | * Add a test gem and build and run it on all supported platforms on Github Actions. 214 | * Allow alternative build command like `docker buildx build` 215 | * Provide compiler-rt libraries for darwin builds. #60 216 | * Extract build environment from `runas` into `rcd-env.sh` and loaded it in non-interactive non-login shells. #57 217 | 218 | 219 | 1.1.0 / 2020-12-29 220 | ------------------ 221 | 222 | * Use ManyLinux2014 as docker base image for linux targets. 223 | That way compatibility to very old linux dists can be provided, while still using modern compilers. 224 | * Add macOS cross build targets "x86_64-darwin" and "arm64-darwin". 225 | They are based on MacOSX-SDK-11.1 226 | * Add Ruby-3.0 as native rvm and cross ruby version. 227 | * Remove Ruby-2.2 from RUBY_CC_VERSION and cross rubies. 228 | * Update to Ubuntu-20.04 for Mingw and JRuby targets. 229 | * Set `ExtensionTask#no_native=true` to fix issues with cross_compiling callback not being called. 230 | See discussion here: https://github.com/rake-compiler/rake-compiler/pull/171 231 | * Allow setting an alternative dockerhub user per `DOCKERHUB_USER`. 232 | 233 | 234 | 1.0.1 / 2020-01-13 235 | ------------------ 236 | 237 | * Fix error when using rake-compiler-dock on non-tty STDIN. #31 238 | 239 | 240 | 1.0.0 / 2020-01-04 241 | ------------------ 242 | * Add ruby-2.7.0 cross ruby. 243 | * Use per-target docker images. 244 | There are currently 5 docker images: 245 | * larskanis/rake-compiler-dock-mri-x86-mingw32 246 | * larskanis/rake-compiler-dock-mri-x64-mingw32 247 | * larskanis/rake-compiler-dock-mri-x86-linux 248 | * larskanis/rake-compiler-dock-mri-x86_64-linux 249 | * larskanis/rake-compiler-dock-jruby 250 | 251 | Since every image has only the taget specific compilers and rubies, care has to be taken, that only rake tasks are triggered, that belong to the specific target. 252 | See the README.md for more information. 253 | * Ensure the separate docker images always use as much as possible common docker images layers to avoid increased download sizes. 254 | * Pass GEM_PRIVATE_KEY_PASSPHRASE to the container. 255 | * Update JRuby to 9.2.9.0 and native CRuby to 2.5.7. 256 | * Create empty ~/.gem to be used for gem signing key. 257 | * Ensure terminal is in cooked mode after build. 258 | This fixes terminal misconfiguration after parallel builds. 259 | * Raise Windows-API to Vista (affects ruby < 2.6 only) 260 | * Use posix pthread for mingw so that C++ standard library for thread could be available such as std::thread, std::mutex, so on. 261 | * Make the system to have GLIBC 2.12 instead of 2.23 so that generated ruby package can run on CentOS 6 with GLIBC 2.12 262 | 263 | 264 | 0.7.2 / 2019-03-18 265 | ------------------ 266 | * Fix missing libruby.a of cross rubies. Fixes #26 267 | * Downgrade to Ubuntu-16.04 for MRI base image. Fixes #25 268 | 269 | 270 | 0.7.1 / 2019-01-27 271 | ------------------ 272 | * Add bundler-2.x to image and keep bundler-1.x for compatibility with projects using a `Gemfile.lock` generated by bundler-1.x . 273 | 274 | 275 | 0.7.0 / 2018-12-26 276 | ------------------ 277 | * Add docker image for JRuby extensions, new option `:rubyvm` and `RCD_RUBYVM` environment variable 278 | * Add ruby-2.6.0 cross ruby 279 | * Remove ruby-2.0 and ruby-2.1 cross ruby 280 | * Fix compatibility with JRuby on the host 281 | * Update base image to Ubuntu-18.04. 282 | * Update native ruby version to 2.5.3 283 | 284 | 285 | 0.6.3 / 2018-02-06 286 | ------------------ 287 | * Update base image to Ubuntu-17.10. Fixes #19, #17 288 | * Update native ruby version to 2.5.0 289 | 290 | 291 | 0.6.2 / 2017-12-25 292 | ------------------ 293 | * Add ruby-2.5.0 cross ruby 294 | * Update native ruby version to 2.4.3 295 | 296 | 297 | 0.6.1 / 2017-06-03 298 | ------------------ 299 | * Update base image from Ubuntu-16.10 to 17.04 300 | * Update perinstalled gems (this solves an version conflict between hoe and rake) 301 | * Update native ruby version 302 | 303 | 304 | 0.6.0 / 2016-12-26 305 | ------------------ 306 | * Remove Windows cross build environments for Ruby < 2.0. 307 | * Add Windows cross build environment for Ruby-2.4. 308 | * Add Linux cross build environment for Ruby-2.0 to 2.4. 309 | * Update Windows gcc to 6.2. 310 | * Update docker base image to Ubuntu-16.10. 311 | * Update native rvm based ruby to 2.4.0. 312 | * Respect MACHINE_DRIVER environment variable for docker-machine. #15 313 | * Add RCD_WORKDIR and RCD_MOUNTDIR environment variables. #11 314 | * Ignore non-unique UID or GID. #10 315 | 316 | 317 | 0.5.3 / 2016-09-02 318 | ------------------ 319 | * Fix 'docker-machine env' when running in Windows cmd shell 320 | 321 | 322 | 0.5.2 / 2016-03-16 323 | ------------------ 324 | * Fix exception when group id can not be retrieved. #14 325 | 326 | 327 | 0.5.1 / 2015-01-30 328 | ------------------ 329 | * Fix compatibility issue with bundler. #8 330 | * Add a check for the current working directory on boot2docker / docker-machine. #7 331 | 332 | 333 | 0.5.0 / 2015-12-25 334 | ------------------ 335 | * Add cross ruby version 2.3.0. 336 | * Replace RVM ruby version 2.2.2 with 2.3.0 and set it as default. 337 | * Add support for docker-machine in addition to deprecated boot2docker. 338 | * Drop runtime support for ruby-1.8. Cross build for 1.8 is still supported. 339 | 340 | 341 | 0.4.3 / 2015-07-09 342 | ------------------ 343 | * Ensure the user and group names doesn't clash with names in the image. 344 | 345 | 346 | 0.4.2 / 2015-07-04 347 | ------------------ 348 | * Describe use of environment variables in README. 349 | * Provide RCD_IMAGE, RCD_HOST_RUBY_PLATFORM and RCD_HOST_RUBY_VERSION to the container. 350 | * Add unzip tool to docker image. 351 | 352 | 353 | 0.4.1 / 2015-07-01 354 | ------------------ 355 | * Make rake-compiler-dock compatible to ruby-1.8 to ease the usage in gems still supporting 1.8. 356 | * Use separate VERSION and IMAGE_VERSION, to avoid unnecessary image downloads. 357 | * Finetune help texts and add FAQ links. 358 | 359 | 360 | 0.4.0 / 2015-06-29 361 | ------------------ 362 | * Add support for OS-X. 363 | * Try boot2docker init and start, when docker is not available. 364 | * Add colorized terminal output. 365 | * Fix usage of STDIN for sending data/commands into the container. 366 | * Limit gemspec to ruby-1.9.3 or newer. 367 | * Allow spaces in user name and path on the host side. 368 | 369 | 370 | 0.3.1 / 2015-06-24 371 | ------------------ 372 | * Add :sigfw and :runas options. 373 | * Don't stop the container on Ctrl-C when running interactively. 374 | * Workaround an issue with sendfile() leading to broken files when using boot2docker on Windows. 375 | 376 | 377 | 0.3.0 / 2015-06-17 378 | ------------------ 379 | * Workaround an issue with broken DLLs when building on Windows. 380 | * Print docker command line based on verbose flag of rake. 381 | * Add check for docker and instructions for install. 382 | 383 | 384 | 0.2.0 / 2015-06-08 385 | ------------------ 386 | * Add a simple API for running commands within the rake-compiler-dock environment. 387 | * Respect ftp, http and https_proxy settings from the host. 388 | * Add wget to the image 389 | 390 | 391 | 0.1.0 / 2015-05-27 392 | ------------------ 393 | * first public release 394 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Lars Kanis 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rake-compiler-dock 2 | 3 | **Easy to use and reliable cross compiler environment for building Windows, Linux, Mac and JRuby binary gems.** 4 | 5 | It provides cross compilers and Ruby environments for 2.6 and newer versions of the [RubyInstaller](http://rubyinstaller.org/) and Linux runtime environments. 6 | They are prepared for use with [rake-compiler](https://github.com/rake-compiler/rake-compiler). 7 | It is used by [many gems with C or JRuby extentions](https://github.com/rake-compiler/rake-compiler-dock/wiki/Projects-using-rake-compiler-dock). 8 | 9 | This is kind of successor of [rake-compiler-dev-box](https://github.com/tjschuck/rake-compiler-dev-box). 10 | It is wrapped as a gem for easier setup, usage and integration and is based on lightweight Docker containers. 11 | It is also more reliable, since the underlying docker images are versioned and immutable. 12 | 13 | ## Supported platforms 14 | 15 | The following platforms are supported for cross-compilation by rake-compiler-dock: 16 | 17 | - `aarch64-linux` and `aarch64-linux-gnu` 18 | - `aarch64-linux-musl` 19 | - `aarch64-mingw-ucrt` 20 | - `arm-linux` and `arm-linux-gnu` 21 | - `arm-linux-musl` 22 | - `arm64-darwin` 23 | - `jruby` 24 | - `x64-mingw-ucrt` 25 | - `x64-mingw32` 26 | - `x86-linux` and `x86-linux-gnu` 27 | - `x86-linux-musl` 28 | - `x86-mingw32` 29 | - `x86_64-darwin` 30 | - `x86_64-linux` and `x86_64-linux-gnu` 31 | - `x86_64-linux-musl` 32 | 33 | ### Windows 34 | 35 | `x64-mingw-ucrt` should be used for Ruby 3.1 and later on windows. `x64-mingw32` should be used for Ruby 3.0 and earlier. This is to match the [changed platform of RubyInstaller-3.1](https://rubyinstaller.org/2021/12/31/rubyinstaller-3.1.0-1-released.html). 36 | 37 | ### Linux GNU and Musl 38 | 39 | Platform names with a `*-linux` suffix are aliases for `*-linux-gnu`, since the Rubygems default is to assume `gnu` if no libc is specified. 40 | 41 | Some C extensions may not require separate GNU and Musl builds, in which case it's acceptable to ship a single `*-linux` gem to cover both platforms. We recommend you set up automated testing for both GNU and Musl to determine whether you need to ship distinct native gems for these platforms (see [`flavorjones/ruby-c-extensions-explained`](https://github.com/flavorjones/ruby-c-extensions-explained/blob/main/.github/workflows/precompiled.yml) for an example of how to set up testing pipelines on Github Actions). 42 | 43 | 44 | ## Linux GNU and Musl: important details 45 | 46 | ### Summary 47 | 48 | If you ship `-linux-gnu` and `-linux-musl` gems: 49 | 50 | - Use rake-compiler `>= 1.2.7` to build your gems 51 | - Recommend your musl users have bundler `>= 2.5.6` 52 | - Require your linux users to have rubygems `>= 3.3.22` and bundler `>= 2.3.21` 53 | 54 | ⚠ DO NOT ship a `-linux-musl` gem with a `-linux` gem. See https://github.com/rake-compiler/rake-compiler-dock/issues/117 for more context. 55 | 56 | 57 | ### Warning about combining `-linux-musl` with other linux native platforms 58 | 59 | After [evaluating many ruby, rubygems, and bundler versions](https://github.com/rake-compiler/rake-compiler-dock/issues/117), we strongly recommend that gem maintainers choose one of the following two options: 60 | 61 | - ship a `-linux` platform gem that works for both gnu and musl systems if you can, 62 | - or ship both `-linux-gnu` and `-linux-musl` platform gems 63 | 64 | Do NOT ship `-linux` and `-linux-musl` gems together, some versions of bundler or rubygems will not work properly for your users. 65 | 66 | 67 | ### Warning about required versions of rubygems 68 | 69 | The `*-linux-gnu` and `*-linux-musl` platform gems require Rubygems 3.3.22 or later (or Bundler 2.3.21 or later) at installation time. 70 | 71 | Ruby version 3.1 and later ship with a sufficient Rubygems version. For earlier versions of Ruby, here are the versions of Rubygems you should recommend to your users: 72 | 73 | - ruby: "3.0", rubygems: "3.5.5" # or possibly higher 74 | - ruby: "2.7", rubygems: "3.4.22" 75 | - ruby: "2.6", rubygems: "3.4.22" 76 | 77 | **It's strongly suggested that you use rake-compiler v1.2.7 or later to build `linux-musl` and/or `linux-gnu` native gems.** That version of rake-compiler sets `required_rubygems_version` automatically in the native platform gems' gemspecs. 78 | 79 | 80 | ### Warning about required versions of bundler 81 | 82 | Finally, there is a known bug in bundler `< 2.5.6` that may make it difficult for users on musl systems to resolve their `linux-musl` dependencies correctly. You can read a description of this problem at https://github.com/rubygems/rubygems/issues/7432, but in summary **you should recommend your musl users have bundler v2.5.6 or later**. 83 | 84 | 85 | ## Installation 86 | 87 | Install docker [following the instructions on the docker website](https://docs.docker.com/engine/install/) ... or install [docker-toolbox for Windows and OSX](https://github.com/docker/toolbox/releases) or boot2docker on [Windows](https://github.com/boot2docker/windows-installer/releases) or [OS X](https://github.com/boot2docker/osx-installer/releases) . 88 | 89 | Install rake-compiler-dock as a gem. The docker image is downloaded later on demand: 90 | 91 | $ gem install rake-compiler-dock 92 | 93 | ... or build your own gem and docker image: 94 | 95 | $ git clone https://github.com/rake-compiler/rake-compiler-dock 96 | $ rake install 97 | 98 | 99 | ## Usage 100 | 101 | Rake-compiler-dock provides the necessary tools to build Ruby extensions for Windows and Linux written in C and C++ and JRuby written in Java. 102 | It is intended to be used in conjunction with [rake-compiler's](https://github.com/rake-compiler/rake-compiler) cross build capability. 103 | Your Rakefile should enable cross compilation like so: 104 | 105 | ```ruby 106 | exttask = Rake::ExtensionTask.new('my_extension', my_gem_spec) do |ext| 107 | ext.cross_compile = true 108 | ext.cross_platform = %w[x86-mingw32 x64-mingw-ucrt x64-mingw32 x86-linux x86_64-linux x86_64-darwin arm64-darwin] 109 | end 110 | ``` 111 | 112 | where you should choose your platforms from the list in the "Supported platforms" section. 113 | 114 | See below, how to invoke cross builds in your Rakefile. 115 | 116 | Additionally it may also be used to build ffi based binary gems like [libusb](https://github.com/larskanis/libusb), but currently doesn't provide any additional build helpers for this use case, beyond docker invocation and cross compilers. 117 | 118 | ### Interactive Usage 119 | 120 | Rake-compiler-dock offers the shell command `rake-compiler-dock` and a [ruby API](http://www.rubydoc.info/gems/rake-compiler-dock/RakeCompilerDock) for issuing commands within the docker image, described below. 121 | 122 | `rake-compiler-dock` without arguments starts an interactive shell session. 123 | This is best suited to try out and debug a build. 124 | It mounts the current working directory into the docker environment. 125 | All changes below the current working directory are shared with the host. 126 | But note, that all other changes to the file system of the container are dropped at the end of the session - the docker image is static for a given version. 127 | `rake-compiler-dock` can also take the build command(s) from STDIN or as command arguments. 128 | 129 | All commands are executed with the same user and group of the host. 130 | This is done by copying user account data into the container and sudo to it. 131 | 132 | To build x86 Windows and x86_64 Linux binary gems interactively, it can be called like this: 133 | 134 | user@host:$ cd your-gem-dir/ 135 | user@host:$ rake-compiler-dock # this enters a container with an interactive shell for x86 Windows (default) 136 | user@5b53794ada92:$ bundle 137 | user@5b53794ada92:$ rake cross native gem 138 | user@5b53794ada92:$ exit 139 | user@host:$ ls pkg/*.gem 140 | your-gem-1.0.0.gem your-gem-1.0.0-x86-mingw32.gem 141 | 142 | user@host:$ RCD_PLATFORM=x86_64-linux-gnu rake-compiler-dock # this enters a container for amd64 Linux GNU target 143 | user@adc55b2b92a9:$ bundle 144 | user@adc55b2b92a9:$ rake cross native gem 145 | user@adc55b2b92a9:$ exit 146 | user@host:$ ls pkg/*.gem 147 | your-gem-1.0.0.gem your-gem-1.0.0-x86_64-linux-gnu.gem 148 | 149 | Or non-interactive: 150 | 151 | user@host:$ rake-compiler-dock bash -c "bundle && rake cross native gem" 152 | 153 | The environment variable `RUBY_CC_VERSION` is predefined as described [below](#environment-variables). 154 | 155 | If necessary, additional software can be installed, prior to the build command. 156 | This is local to the running session, only. 157 | 158 | For Windows and Mac: 159 | 160 | sudo apt-get update && sudo apt-get install your-package 161 | 162 | For Linux: 163 | 164 | sudo yum install your-package 165 | 166 | You can also choose between different executable ruby versions by `rbenv shell ` . 167 | The current default is 3.1. 168 | 169 | ### JRuby support 170 | 171 | Rake-compiler-dock offers a dedicated docker image for JRuby. 172 | JRuby doesn't need a complicated cross build environment like C-ruby, but using Rake-compiler-dock for JRuby makes building binary gems more consistent. 173 | 174 | To build java binary gems interactively, it can be called like this: 175 | 176 | user@host:$ cd your-gem-dir/ 177 | user@host:$ RCD_RUBYVM=jruby rake-compiler-dock # this enters a container with an interactive shell 178 | user@5b53794ada92:$ ruby -v 179 | jruby 9.2.5.0 (2.5.0) 2018-12-06 6d5a228 OpenJDK 64-Bit Server VM 10.0.2+13-Ubuntu-1ubuntu0.18.04.4 on 10.0.2+13-Ubuntu-1ubuntu0.18.04.4 +jit [linux-x86_64] 180 | user@5b53794ada92:$ bundle 181 | user@5b53794ada92:$ rake java gem 182 | user@5b53794ada92:$ exit 183 | user@host:$ ls pkg/*.gem 184 | your-gem-1.0.0.gem your-gem-1.0.0-java.gem 185 | 186 | ### Add to your Rakefile 187 | 188 | To make the build process reproducible for other parties, it is recommended to add rake-compiler-dock to your Rakefile. 189 | This can be done like this: 190 | 191 | ```ruby 192 | PLATFORMS = %w[ 193 | aarch64-linux-gnu 194 | aarch64-linux-musl 195 | aarch64-mingw-ucrt 196 | arm-linux-gnu 197 | arm-linux-musl 198 | arm64-darwin 199 | x64-mingw-ucrt 200 | x64-mingw32 201 | x86-linux-gnu 202 | x86-linux-musl 203 | x86-mingw32 204 | x86_64-darwin 205 | x86_64-linux-gnu 206 | x86_64-linux-musl 207 | ] 208 | task 'gem:native' do 209 | require 'rake_compiler_dock' 210 | sh "bundle config set cache_all true" # Avoid repeated downloads of gems by using gem files from the host. 211 | sh "bundle package" 212 | PLATFORMS.each do |plat| 213 | RakeCompilerDock.sh "bundle --local && rake native:#{plat} gem", platform: plat 214 | end 215 | RakeCompilerDock.sh "bundle --local && rake java gem", rubyvm: :jruby 216 | end 217 | ``` 218 | 219 | This runs the `bundle` and `rake` commands once for each platform. 220 | That is once for the jruby gems and 6 times for the specified MRI platforms. 221 | 222 | ### Run builds in parallel 223 | 224 | rake-compiler-dock uses dedicated docker images per build target (since rake-compiler-dock-1.0). 225 | Because each target runs in a separate docker container, it is simple to run all targets in parallel. 226 | The following example defines `rake gem:native` as a multitask and separates the preparation which should run only once. 227 | It also shows how gem signing can be done with parallel builds. 228 | Please note, that parallel builds only work reliable, if the specific platform gem is requested (instead of just "rake gem"). 229 | 230 | ```ruby 231 | namespace "gem" do 232 | task 'prepare' do 233 | require 'rake_compiler_dock' 234 | require 'io/console' 235 | sh "bundle config set cache_all true" 236 | sh "bundle package" 237 | sh "cp ~/.gem/gem-*.pem build/gem/ || true" 238 | ENV["GEM_PRIVATE_KEY_PASSPHRASE"] = STDIN.getpass("Enter passphrase of gem signature key: ") 239 | end 240 | 241 | exttask.cross_platform.each do |plat| 242 | desc "Build all native binary gems in parallel" 243 | multitask 'native' => plat 244 | 245 | desc "Build the native gem for #{plat}" 246 | task plat => 'prepare' do 247 | RakeCompilerDock.sh <<-EOT, platform: plat 248 | (cp build/gem/gem-*.pem ~/.gem/ || true) && 249 | bundle --local && 250 | rake native:#{plat} pkg/#{exttask.gem_spec.full_name}-#{plat}.gem 251 | EOT 252 | end 253 | end 254 | end 255 | ``` 256 | 257 | ### Add to your Gemfile 258 | 259 | Rake-compiler-dock uses [semantic versioning](http://semver.org/), so you should add it into your Gemfile, to make sure, that future changes will not break your build. 260 | 261 | ```ruby 262 | gem 'rake-compiler-dock', '~> 1.2' 263 | ``` 264 | 265 | See [the wiki](https://github.com/rake-compiler/rake-compiler-dock/wiki/Projects-using-rake-compiler-dock) for projects which make use of rake-compiler-dock. 266 | 267 | ### As a CI System Container 268 | 269 | The OCI images provided by rake-compiler-dock can be used without the `rake-compiler-dock` gem or wrapper. This may be useful if your CI pipeline is building native gems. 270 | 271 | For example, a Github Actions job might look like this: 272 | 273 | ``` yaml 274 | jobs: 275 | native-gem: 276 | name: "native-gem" 277 | runs-on: ubuntu-latest 278 | container: 279 | image: "ghcr.io/rake-compiler/rake-compiler-dock-image:1.2.2-mri-x86_64-linux-gnu" 280 | steps: 281 | - uses: actions/checkout@v2 282 | - run: bundle install && bundle exec rake gem:x86_64-linux-gnu:rcd 283 | - uses: actions/upload-artifact@v2 284 | with: 285 | name: native-gem 286 | path: gems 287 | retention-days: 1 288 | ``` 289 | 290 | Where the referenced rake task might be defined by something like: 291 | 292 | ``` ruby 293 | cross_platforms = ["x64-mingw32", "x86_64-linux", "x86_64-darwin", "arm64-darwin"] 294 | 295 | namespace "gem" do 296 | cross_platforms.each do |platform| 297 | namespace platform do 298 | task "rcd" do 299 | Rake::Task["native:#{platform}"].invoke 300 | Rake::Task["pkg/#{rcee_precompiled_spec.full_name}-#{Gem::Platform.new(platform)}.gem"].invoke 301 | end 302 | end 303 | end 304 | end 305 | 306 | ``` 307 | 308 | For an example of rake tasks that support this style of invocation, visit https://github.com/flavorjones/ruby-c-extensions-explained/tree/main/precompiled 309 | 310 | 311 | ### Living on the edge: using weekly snapshots 312 | 313 | OCI images snapshotted from `main` are published weekly to Github Container Registry with the string "snapshot" in place of the version number in the tag name, e.g.: 314 | 315 | - `ghcr.io/rake-compiler/rake-compiler-dock-image:snapshot-mri-x86_64-linux-gnu` 316 | 317 | These images are intended for integration testing. They may not work properly and should not be considered production ready. 318 | 319 | 320 | ## Environment Variables 321 | 322 | Rake-compiler-dock makes use of several environment variables. 323 | 324 | The following variables are recognized by rake-compiler-dock: 325 | 326 | * `RCD_RUBYVM` - The ruby VM and toolchain to be used. 327 | Must be one of `mri`, `jruby`. 328 | * `RCD_PLATFORM` - The target rubygems platform. 329 | Must be a space separated list out of the platforms listed under "Supported platforms" above. 330 | It is ignored when `rubyvm` is set to `:jruby`. 331 | * `RCD_IMAGE` - The docker image that is downloaded and started. 332 | Defaults to "ghcr.io/rake-compiler/rake-compiler-dock-image:IMAGE_VERSION-PLATFORM" with an image version that is determined by the gem version. 333 | 334 | The following variables are passed through to the docker container without modification: 335 | 336 | * `http_proxy`, `https_proxy`, `ftp_proxy` - See [Frequently asked questions](https://github.com/rake-compiler/rake-compiler-dock/wiki/FAQ) for more details. 337 | * `GEM_PRIVATE_KEY_PASSPHRASE` - To avoid interactive password prompts in the container. 338 | 339 | The following variables are provided to the running docker container: 340 | 341 | * `RCD_IMAGE` - The full docker image name the container is running on. 342 | * `RCD_HOST_RUBY_PLATFORM` - The `RUBY_PLATFORM` of the host ruby. 343 | * `RCD_HOST_RUBY_VERSION` - The `RUBY_VERSION` of the host ruby. 344 | * `RUBY_CC_VERSION` - The target ruby versions for rake-compiler. 345 | The default is defined in the [Dockerfile](https://github.com/rake-compiler/rake-compiler-dock/blob/94770238d68d71df5f70abe76097451a575ce46c/Dockerfile.mri.erb#L229), but can be changed as a parameter to rake. 346 | * `RCD_MOUNTDIR` - The directory which is mounted into the docker container. 347 | Defaults to pwd. 348 | * `RCD_WORKDIR` - The working directory within the docker container. 349 | Defaults to pwd. 350 | 351 | Other environment variables can be set or passed through to the container like this: 352 | 353 | ```ruby 354 | RakeCompilerDock.sh "rake cross native gem OPENSSL_VERSION=#{ENV['OPENSSL_VERSION']}" 355 | ``` 356 | 357 | ### Choosing specific Ruby versions to support 358 | 359 | If you only want to precompile for certain Ruby versions, you can specify those versions by overwriting the `RUBY_CC_VERSION` environment variable. 360 | 361 | For example, if you wanted to only support Ruby 3.4 and 3.3, you might set this variable to: 362 | 363 | ``` ruby 364 | ENV["RUBY_CC_VERSION"] = "3.4.1:3.3.7" 365 | ``` 366 | 367 | In practice, though, hardcoding these versions is brittle because the patch versions in the container may vary from release to release. 368 | 369 | A more robust way to do this is to use `RakeCompilerDock.ruby_cc_version` which accepts an array of Ruby minor versions or patch version requirements. 370 | 371 | ``` ruby 372 | RakeCompilerDock.ruby_cc_version("3.3", "3.4") 373 | # => "3.4.1:3.3.7" 374 | 375 | RakeCompilerDock.ruby_cc_version("~> 3.4.0", "~> 3.3.0") 376 | # => "3.4.1:3.3.7" 377 | 378 | RakeCompilerDock.ruby_cc_version("~> 3.3") 379 | # => "3.4.1:3.3.7" 380 | ``` 381 | 382 | So you can either set the environment variable directly: 383 | 384 | ``` ruby 385 | ENV["RUBY_CC_VERSION"] = RakeCompilerDock.ruby_cc_version("~> 3.1") 386 | ``` 387 | 388 | or do the same thing using the `set_ruby_cc_version` convenience method: 389 | 390 | ``` ruby 391 | RakeCompilerDock.set_ruby_cc_version("~> 3.1") # equivalent to the above assignment 392 | ``` 393 | 394 | 395 | 396 | ## More information 397 | 398 | See [Frequently asked questions](https://github.com/rake-compiler/rake-compiler-dock/wiki/FAQ) and [![Join the chat at https://gitter.im/larskanis/rake-compiler-dock](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/larskanis/rake-compiler-dock?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 399 | 400 | 401 | ## Contributing 402 | 403 | 1. Fork it ( https://github.com/rake-compiler/rake-compiler-dock/fork ) 404 | 2. Create your feature branch (`git checkout -b my-new-feature`) 405 | 3. Commit your changes (`git commit -am 'Add some feature'`) 406 | 4. Push to the branch (`git push origin my-new-feature`) 407 | 5. Create a new Pull Request 408 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require "rake/clean" 3 | require "rake_compiler_dock" 4 | require_relative "build/gem_helper" 5 | require_relative "build/parallel_docker_build" 6 | 7 | CLEAN.include("tmp") 8 | 9 | RakeCompilerDock::GemHelper.install_tasks 10 | 11 | platforms = [ 12 | # tuple is [platform, target] 13 | ["aarch64-linux-gnu", "aarch64-linux-gnu"], 14 | ["aarch64-linux-musl", "aarch64-linux-musl"], 15 | ["aarch64-mingw-ucrt", "aarch64-w64-mingw32"], 16 | ["arm-linux-gnu", "arm-linux-gnueabihf"], 17 | ["arm-linux-musl", "arm-linux-musleabihf"], 18 | ["arm64-darwin", "aarch64-apple-darwin"], 19 | ["x64-mingw-ucrt", "x86_64-w64-mingw32"], 20 | ["x64-mingw32", "x86_64-w64-mingw32"], 21 | ["x86-linux-gnu", "i686-linux-gnu"], 22 | ["x86-linux-musl", "i686-unknown-linux-musl"], 23 | ["x86-mingw32", "i686-w64-mingw32"], 24 | ["x86_64-darwin", "x86_64-apple-darwin"], 25 | ["x86_64-linux-gnu", "x86_64-linux-gnu"], 26 | ["x86_64-linux-musl", "x86_64-unknown-linux-musl"], 27 | ] 28 | 29 | namespace :build do 30 | 31 | platforms.each do |platform, target| 32 | sdf = "Dockerfile.mri.#{platform}" 33 | 34 | desc "Build image for platform #{platform}" 35 | task platform => sdf 36 | task sdf do 37 | image_name = RakeCompilerDock::Starter.container_image_name(platform: platform) 38 | sh(*RakeCompilerDock.docker_build_cmd(platform), "-t", image_name, "-f", "Dockerfile.mri.#{platform}", ".") 39 | if image_name.include?("linux-gnu") 40 | sh("docker", "tag", image_name, image_name.sub("linux-gnu", "linux")) 41 | end 42 | end 43 | 44 | df = ERB.new(File.read("Dockerfile.mri.erb"), trim_mode: ">").result(binding) 45 | File.write(sdf, df) 46 | CLEAN.include(sdf) 47 | end 48 | 49 | desc "Build image for JRuby" 50 | task :jruby => "Dockerfile.jruby" 51 | task "Dockerfile.jruby" do 52 | image_name = RakeCompilerDock::Starter.container_image_name(rubyvm: "jruby") 53 | sh(*RakeCompilerDock.docker_build_cmd("jruby"), "-t", image_name, "-f", "Dockerfile.jruby", ".") 54 | end 55 | 56 | RakeCompilerDock::ParallelDockerBuild.new(platforms.map{|pl, _| "Dockerfile.mri.#{pl}" } + ["Dockerfile.jruby"], workdir: "tmp/docker") 57 | 58 | desc "Build images for all MRI platforms in parallel" 59 | if ENV['RCD_USE_BUILDX_CACHE'] 60 | task :mri => platforms.map(&:first) 61 | else 62 | multitask :mri => platforms.map(&:first) 63 | end 64 | 65 | desc "Build images for all platforms in parallel" 66 | if ENV['RCD_USE_BUILDX_CACHE'] 67 | task :all => platforms.map(&:first) + ["jruby"] 68 | else 69 | multitask :all => platforms.map(&:first) + ["jruby"] 70 | end 71 | end 72 | 73 | task :build => "build:all" 74 | 75 | namespace :prepare do 76 | desc "Build cross compiler for x64-mingw-ucrt aka RubyInstaller-3.1+" 77 | task "mingw64-ucrt" do 78 | sh(*RakeCompilerDock.docker_build_cmd, "-t", "larskanis/mingw64-ucrt:20.04", ".", 79 | chdir: "mingw64-ucrt") 80 | end 81 | end 82 | 83 | desc "Run tests" 84 | task :test do 85 | sh %Q{ruby -w -W2 -I. -Ilib -e "#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}" -- -v #{ENV['TESTOPTS']}} 86 | end 87 | 88 | desc "Update predefined_user_group.rb" 89 | task :update_lists do 90 | def get_user_list(platform) 91 | puts "getting user list from #{platform} ..." 92 | `RCD_PLATFORM=#{platform} rake-compiler-dock bash -c "getent passwd"`.each_line.map do |line| 93 | line.chomp.split(":")[0] 94 | end.compact.reject(&:empty?) - [RakeCompilerDock::Starter.make_valid_user_name(`id -nu`.chomp)] 95 | end 96 | 97 | def get_group_list(platform) 98 | puts "getting group list from #{platform} ..." 99 | `RCD_PLATFORM=#{platform} rake-compiler-dock bash -c "getent group"`.each_line.map do |line| 100 | line.chomp.split(":")[0] 101 | end.compact.reject(&:empty?) - [RakeCompilerDock::Starter.make_valid_group_name(`id -ng`.chomp)] 102 | end 103 | 104 | users = platforms.flat_map { |platform, _| get_user_list(platform) }.uniq.sort 105 | groups = platforms.flat_map { |platform, _| get_group_list(platform) }.uniq.sort 106 | 107 | File.open("lib/rake_compiler_dock/predefined_user_group.rb", "w") do |fd| 108 | fd.puts <<-EOT 109 | # DO NOT EDIT - This file is generated per 'rake update_lists' 110 | module RakeCompilerDock 111 | PredefinedUsers = #{users.inspect} 112 | PredefinedGroups = #{groups.inspect} 113 | end 114 | EOT 115 | end 116 | end 117 | 118 | namespace :release do 119 | desc "push all docker images" 120 | task :images do 121 | image_name = RakeCompilerDock::Starter.container_image_name(rubyvm: "jruby") 122 | sh("docker", "push", image_name) 123 | 124 | platforms.each do |platform, _| 125 | image_name = RakeCompilerDock::Starter.container_image_name(platform: platform) 126 | sh("docker", "push", image_name) 127 | 128 | if image_name.include?("linux-gnu") 129 | sh("docker", "push", image_name.sub("linux-gnu", "linux")) 130 | end 131 | end 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /bin/rake-compiler-dock: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rake_compiler_dock' 4 | 5 | args = ARGV.empty? ? ['bash'] : ARGV.dup 6 | 7 | if $stdin.tty? 8 | # An interactive session should not leave the container on Ctrl-C 9 | args << {:sigfw => false} 10 | end 11 | 12 | begin 13 | RakeCompilerDock.exec(*args) do |ok, res| 14 | exit(res.exitstatus) 15 | end 16 | rescue RakeCompilerDock::DockerIsNotAvailable 17 | exit(-1) 18 | end 19 | -------------------------------------------------------------------------------- /build/gem_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/gem_helper" 2 | 3 | module RakeCompilerDock 4 | class GemHelper < Bundler::GemHelper 5 | def install 6 | super 7 | 8 | task "release:guard_clean" => ["release:update_history"] 9 | 10 | task "release:update_history" do 11 | update_history 12 | end 13 | end 14 | 15 | def hfile 16 | "History.md" 17 | end 18 | 19 | def headline 20 | '([^\w]*)(\d+\.\d+\.\d+)([^\w]+)([2Y][0Y][0-9Y][0-9Y]-[0-1M][0-9M]-[0-3D][0-9D])([^\w]*|$)' 21 | end 22 | 23 | def reldate 24 | Time.now.strftime("%Y-%m-%d") 25 | end 26 | 27 | def version_tag 28 | "#{version}" 29 | end 30 | 31 | def update_history 32 | hin = File.read(hfile) 33 | hout = hin.sub(/#{headline}/) do 34 | raise "#{hfile} isn't up-to-date for version #{version}" unless $2==version.to_s 35 | $1 + $2 + $3 + reldate + $5 36 | end 37 | if hout != hin 38 | Bundler.ui.confirm "Updating #{hfile} for release." 39 | File.write(hfile, hout) 40 | Rake::FileUtilsExt.sh "git", "commit", hfile, "-m", "Update release date in #{hfile}" 41 | end 42 | end 43 | 44 | def tag_version 45 | Bundler.ui.confirm "Tag release with annotation:" 46 | m = File.read(hfile).match(/(?#{headline}.*?)#{headline}/m) || raise("Unable to find release notes in #{hfile}") 47 | Bundler.ui.info(m[:annotation].gsub(/^/, " ")) 48 | IO.popen(["git", "tag", "--file=-", version_tag], "w") do |fd| 49 | fd.write m[:annotation] 50 | end 51 | yield if block_given? 52 | rescue 53 | Bundler.ui.error "Untagging #{version_tag} due to error." 54 | sh_with_code "git tag -d #{version_tag}" 55 | raise 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /build/mk_i686.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | 3 | require "fileutils" 4 | 5 | Dir["/usr/bin/x86_64-linux-gnu-*"].each do |file| 6 | case File.basename(file) 7 | when /^x86_64-linux-gnu-(gcc|g\+\+|ld)$/ 8 | e = $1 9 | puts "create /usr/bin/i686-linux-gnu-#{e}" 10 | File.write("/usr/bin/i686-linux-gnu-#{e}", <<-EOT) 11 | #!/bin/sh 12 | x86_64-linux-gnu-#{e} -m32 "$@" 13 | EOT 14 | File.chmod(0755, "/usr/bin/i686-linux-gnu-#{e}") 15 | when /^x86_64-linux-gnu-([a-z+\.]+)$/ 16 | FileUtils.ln_s(file, "/usr/bin/i686-linux-gnu-#{$1}", verbose: true) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /build/mk_musl_cross.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -x 6 | 7 | # check if target is provided as the first argument, and exit if not 8 | if [ -z "$1" ]; then 9 | echo "No target provided" 10 | exit 1 11 | fi 12 | TARGET=$1 13 | 14 | git clone --depth 1 https://github.com/richfelker/musl-cross-make.git 15 | pushd musl-cross-make 16 | 17 | cat > config.mak <> /etc/rubybashrc 28 | echo "export MACOSX_DEPLOYMENT_TARGET=10.13" >> /etc/rubybashrc 29 | echo "export OSXCROSS_MP_INC=1" >> /etc/rubybashrc 30 | echo "export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1" >> /etc/rubybashrc 31 | 32 | 33 | # Add links to build tools without target version kind of: 34 | # arm64-apple-darwin-clang => arm64-apple-darwin20.1-clang 35 | rm -f /opt/osxcross/target/bin/*-apple-darwin-* 36 | find /opt/osxcross/target/bin/ -name '*-apple-darwin[0-9]*' | sort | while read f ; do d=`echo $f | sed s/darwin[0-9\.]*/darwin/`; echo $f '"$@"' | tee $d && chmod +x $d ; done 37 | 38 | # There's no objdump in osxcross but we can use llvm's 39 | ln -s /usr/lib/llvm-10/bin/llvm-objdump /opt/osxcross/target/bin/x86_64-apple-darwin-objdump 40 | ln -s /usr/lib/llvm-10/bin/llvm-objdump /opt/osxcross/target/bin/aarch64-apple-darwin-objdump 41 | 42 | # install /usr/bin/codesign and make a symlink for codesign_allocate (the architecture doesn't matter) 43 | git clone -q --depth=1 https://github.com/flavorjones/sigtool --branch flavorjones-fix-link-line-library-order 44 | make -C sigtool install 45 | ln -s /opt/osxcross/target/bin/x86_64-apple-darwin[0-9]*-codesign_allocate /usr/bin/codesign_allocate 46 | -------------------------------------------------------------------------------- /build/mk_pkg_config.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -x 6 | 7 | # sha256 from https://lists.freedesktop.org/archives/pkg-config/2017-March/001084.html 8 | TARFILE=pkg-config-0.29.2.tar.gz 9 | DIR=pkg-config-0.29.2 10 | CHECKSUM=6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591 11 | 12 | cd /tmp 13 | 14 | wget https://pkgconfig.freedesktop.org/releases/${TARFILE} 15 | sha256sum ${TARFILE} | grep "${CHECKSUM}" 16 | 17 | tar -xzvf ${TARFILE} 18 | cd $DIR 19 | 20 | ./configure --prefix=/usr/local 21 | make install 22 | 23 | pkg-config --version 24 | echo "OK" 25 | -------------------------------------------------------------------------------- /build/parallel_docker_build.rb: -------------------------------------------------------------------------------- 1 | require "fileutils" 2 | require "rake" 3 | require "digest/sha1" 4 | 5 | module RakeCompilerDock 6 | class << self 7 | def docker_build_cmd(platform=nil) 8 | cmd = if ENV['RCD_USE_BUILDX_CACHE'] 9 | if platform 10 | cache_version = RakeCompilerDock::IMAGE_VERSION.split(".").take(2).join(".") 11 | cache = File.join("cache", cache_version, platform) 12 | "docker buildx build --cache-to=type=local,dest=#{cache},mode=max --cache-from=type=local,src=#{cache} --load" 13 | else 14 | return nil 15 | end 16 | else 17 | ENV['RCD_DOCKER_BUILD'] || "docker build" 18 | end 19 | Shellwords.split(cmd) 20 | end 21 | end 22 | 23 | # Run docker builds in parallel, but ensure that common docker layers are reused 24 | class ParallelDockerBuild 25 | include Rake::DSL 26 | 27 | def initialize(dockerfiles, workdir: "tmp/docker", inputdir: ".", task_prefix: "common-") 28 | FileUtils.mkdir_p(workdir) 29 | 30 | files = parse_dockerfiles(dockerfiles, inputdir) 31 | # pp files 32 | 33 | vcs = find_commons(files) 34 | # pp vcs 35 | 36 | define_common_tasks(vcs, workdir, task_prefix) 37 | end 38 | 39 | # Read given dockerfiles from inputdir and split into a list of commands. 40 | # 41 | # Returns: 42 | # {"File0"=>[" FROM a\n", " RUN a\n", " RUN d\n"], 43 | # "File1"=>[" FROM a\n", 44 | # ... 45 | def parse_dockerfiles(dockerfiles, inputdir) 46 | dockerfiles.map do |fn| 47 | [fn, File.read(File.join(inputdir, fn))] 48 | end.map do |fn, f| 49 | # Split file contant in lines unless line ends with backslash 50 | fc = f.each_line.with_object([]) do |line, lines| 51 | if lines.last=~/\\\n\z/ 52 | lines.last << line 53 | else 54 | lines << line 55 | end 56 | end 57 | [fn, fc] 58 | end.to_h 59 | end 60 | 61 | # Build a tree of common parts of given files. 62 | # 63 | # Returns: 64 | # {["File0", "File1", "File2", "File3"]=> 65 | # [[" FROM a\n"], 66 | # {["File0", "File1"]=> 67 | # [[" RUN a\n", " RUN d\n"], {["File1"]=>[[" RUN f\n"], {}]}], 68 | # ["File2", "File3"]=>[[" RUN b\n", " RUN c\n", " RUN d\n"], {}]}]} 69 | def find_commons(files, vmask=nil, li=0) 70 | vmask ||= files.keys 71 | vcs = Hash.new { [] } 72 | files.each do |fn, lines| 73 | next unless vmask.include?(fn) 74 | vcs[lines[li]] += [fn] 75 | end 76 | 77 | vcs.map do |line, vc| 78 | next unless line 79 | nvcs = find_commons(files, vc, li+1) 80 | if nvcs.first && nvcs.first[0] == vc 81 | # Append lines that are equal between file(s) 82 | nl = [[line] + nvcs.first[1][0], nvcs.first[1][1]] 83 | else 84 | nl = [[line], nvcs] 85 | end 86 | [vc, nl] 87 | end.compact.to_h 88 | end 89 | 90 | # Write intermediate dockerfiles to workdir and define rake tasks 91 | # 92 | # The rake tasks are named after the dockerfiles given to #new . 93 | # This also adds dependant intermediate tasks as prerequisites. 94 | def define_common_tasks(vcs, workdir, task_prefix, plines=[]) 95 | vcs.map do |files, (lines, nvcs)| 96 | fn = "#{task_prefix}#{Digest::SHA1.hexdigest(files.join)}" 97 | File.write(File.join(workdir, fn), (plines + lines).join) 98 | task fn do 99 | docker_build(fn, workdir) 100 | end 101 | 102 | nfn = define_common_tasks(nvcs, workdir, task_prefix, plines + lines) 103 | nfn.each do |file| 104 | task file => fn 105 | end 106 | files.each do |file| 107 | task file => fn 108 | end 109 | fn 110 | end 111 | end 112 | 113 | # Run an intermediate dockerfile without tag 114 | # 115 | # The layers will be reused in subsequent builds, even if they run in parallel. 116 | def docker_build(filename, workdir) 117 | cmd = RakeCompilerDock.docker_build_cmd 118 | return if cmd.nil? 119 | sh(*RakeCompilerDock.docker_build_cmd, "-f", File.join(workdir, filename), ".") 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /build/patches/rake-compiler-1.2.9/0004-Enable-build-of-static-libruby.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tasks/bin/cross-ruby.rake b/tasks/bin/cross-ruby.rake 2 | index 8317a2a3..8ed21718 100644 3 | --- a/tasks/bin/cross-ruby.rake 4 | +++ b/tasks/bin/cross-ruby.rake 5 | @@ -116,11 +116,31 @@ 6 | "--host=#{mingw_host}", 7 | "--target=#{mingw_target}", 8 | "--build=#{RUBY_BUILD}", 9 | - '--enable-shared', 10 | + '--enable-install-static-library', 11 | + '--disable-jit-support', 12 | + 'ac_cv_lib_z_uncompress=no', 13 | + 'ac_cv_lib_crypt_crypt=no', 14 | + 'ac_cv_func_crypt_r=no', 15 | + 'extra_warnflags=-Wno-shorten-64-to-32 -Wno-dll-attribute-on-redeclaration', 16 | '--disable-install-doc', 17 | '--with-ext=', 18 | ] 19 | 20 | + if mingw_host =~ /darwin/ 21 | + options += [ 22 | + '--enable-static', 23 | + '--disable-shared', 24 | + ] 25 | + else 26 | + options += [ 27 | + '--enable-static', 28 | + '--enable-shared', 29 | + ] 30 | + end 31 | + 32 | + # https://github.com/docker-library/ruby/issues/308 33 | + options << "--with-coroutine=arm32" if major == "2.7" && mingw_target =~ /arm-linux-musl/ 34 | + 35 | # Force Winsock2 for Ruby 1.8, 1.9 defaults to it 36 | options << "--with-winsock2" if major == "1.8" 37 | options << "--prefix=#{install_dir}" 38 | -------------------------------------------------------------------------------- /build/patches/rake-compiler-1.2.9/0005-build-miniruby-first.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tasks/bin/cross-ruby.rake b/tasks/bin/cross-ruby.rake 2 | index d37ab97b..0df44b30 100644 3 | --- a/tasks/bin/cross-ruby.rake 4 | +++ b/tasks/bin/cross-ruby.rake 5 | @@ -129,6 +129,12 @@ 6 | 7 | # make 8 | file "#{build_dir}/ruby.exe" => ["#{build_dir}/Makefile"] do |t| 9 | + puts "MIKE: #{ruby_cc_version}: #{mingw_target}" 10 | + if ruby_cc_version.start_with?("ruby-3.1") && mingw_target =~ /darwin/ 11 | + # for later 3.1.x releases, we need to explicitly build miniruby 12 | + # see https://bugs.ruby-lang.org/issues/19239 13 | + sh "#{MAKE} miniruby", chdir: File.dirname(t.prerequisites.first) 14 | + end 15 | sh MAKE, chdir: File.dirname(t.prerequisites.first) 16 | end 17 | 18 | -------------------------------------------------------------------------------- /build/rcd-env.sh: -------------------------------------------------------------------------------- 1 | # set up a working RCD build environment 2 | export RAKE_EXTENSION_TASK_NO_NATIVE=true 3 | if ! test -e "$HOME"/.rake-compiler ; then 4 | ln -s /usr/local/rake-compiler "$HOME"/.rake-compiler 5 | fi 6 | mkdir -p "$HOME"/.gem 7 | -------------------------------------------------------------------------------- /build/runas: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | groupadd -o -g "$GID" "$GROUP" 4 | mkdir -p /tmp/home 5 | useradd -o -g "$GID" -u "$UID" -G rubyuser,sudo -p "" -b /tmp/home -m "$USER" 6 | 7 | /usr/bin/sudo -u "$USER" -H BASH_ENV=/etc/rubybashrc -- "$@" 8 | -------------------------------------------------------------------------------- /build/sigfw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program handles SIGINT and forwards it to another process. 3 | * It is intended to be run as PID 1. 4 | * 5 | * Docker starts processes with "docker run" as PID 1. 6 | * On Linux, the default signal handler for PID 1 ignores any signals. 7 | * Therefore Ctrl-C aka SIGINT is ignored per default. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int pid = 0; 17 | 18 | void 19 | handle_sigint (int signum) 20 | { 21 | if(pid) 22 | kill(pid, SIGINT); 23 | } 24 | 25 | int main(int argc, char *argv[]){ 26 | struct sigaction new_action; 27 | int status = -1; 28 | 29 | /* Set up the structure to specify the new action. */ 30 | new_action.sa_handler = handle_sigint; 31 | sigemptyset (&new_action.sa_mask); 32 | new_action.sa_flags = 0; 33 | 34 | sigaction (SIGINT, &new_action, (void*)0); 35 | 36 | pid = fork(); 37 | if(pid){ 38 | wait(&status); 39 | return WEXITSTATUS(status); 40 | }else{ 41 | status = execvp(argv[1], &argv[1]); 42 | perror("exec"); 43 | return status; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /build/strip_wrapper_codesign: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Stripping a signed Mach-O binary will invalidate the signature. To mimic what strip does on native 4 | # Darwin, we install this wrapper to re-sign the binary after stripping it. 5 | 6 | files = ARGV.reject { |f| f=~/^-/ } 7 | 8 | strip = "#{File.basename($0)}.bin" 9 | strip_options = ARGV.select{|f| f=~/^-/ } 10 | strip_arguments = [strip] + strip_options + files 11 | 12 | codesign = "codesign" # installed into /usr/bin by mk_osxcross.sh 13 | codesign_options = ["-f", "-s-"] 14 | codesign_arguments = [codesign] + codesign_options + files 15 | 16 | system(*strip_arguments, exception: true) 17 | system(*codesign_arguments, exception: true) 18 | -------------------------------------------------------------------------------- /build/strip_wrapper_vbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Strip file on local folder instead of a Virtualbox shared folder 4 | # to work around this bug: https://www.virtualbox.org/ticket/8463 5 | 6 | require 'tempfile' 7 | require 'fileutils' 8 | 9 | strip = "#{File.basename($0)}.bin" 10 | 11 | files = ARGV.reject{|f| f=~/^-/ }.map do |arg| 12 | tmp = Tempfile.new 'strip' 13 | tmp.close 14 | FileUtils.cp arg, tmp.path 15 | [tmp, arg] 16 | end 17 | 18 | options = ARGV.select{|f| f=~/^-/ } + files.map{|t,o| t.path } 19 | 20 | unless system( strip, *options ) 21 | exit 127 22 | end 23 | code = $?.exitstatus 24 | 25 | files.each do |tmp, orig| 26 | FileUtils.rm orig 27 | FileUtils.cp tmp.path, orig 28 | end 29 | 30 | exit code 31 | -------------------------------------------------------------------------------- /build/sudoers: -------------------------------------------------------------------------------- 1 | Defaults env_keep += "http_proxy https_proxy ftp_proxy GEM_PRIVATE_KEY_PASSPHRASE RCD_HOST_RUBY_PLATFORM RCD_HOST_RUBY_VERSION RCD_IMAGE RUBY_CC_VERSION LD_LIBRARY_PATH DEVTOOLSET_ROOTPATH SOURCE_DATE_EPOCH" 2 | -------------------------------------------------------------------------------- /lib/rake_compiler_dock.rb: -------------------------------------------------------------------------------- 1 | 2 | # We do ruby version check at runtime, so this file should be kept ruby-1.8 compatible. 3 | if (m=RUBY_VERSION.match(/^(\d+)\.(\d+)\.(\d+)/)) && 4 | m.captures.map(&:to_i).pack("N*") < [1,9,2].pack("N*") 5 | raise "rake-compiler-dock requires at least RUBY_VERSION >= 1.9.2" 6 | end 7 | 8 | require "rake_compiler_dock/colors" 9 | require "rake_compiler_dock/docker_check" 10 | require "rake_compiler_dock/starter" 11 | require "rake_compiler_dock/version" 12 | require "rake_compiler_dock/predefined_user_group" 13 | 14 | module RakeCompilerDock 15 | 16 | # Run the command cmd within a fresh rake-compiler-dock container and within a shell. 17 | # 18 | # If a block is given, upon command completion the block is called with an OK flag (true on a zero exit status) and a Process::Status object. 19 | # Without a block a RuntimeError is raised when the command exits non-zero. 20 | # 21 | # Option +:verbose+ can be set to enable printing of the command line. 22 | # If not set, rake's verbose flag is used. 23 | # 24 | # Option +:rubyvm+ can be set to +:mri+ or +:jruby+ . 25 | # It selects the docker image with an appropriate toolchain. 26 | # 27 | # Option +:platform+ can be set to a list of space separated values. 28 | # It selects the docker image(s) with an appropriate toolchain. 29 | # Allowed values are +aarch64-linux-gnu+, +arm-linux-gnu+, +arm64-darwin+, +x64-mingw-ucrt+, 30 | # +x64-mingw32+, +x86-linux-gnu+, +x86-mingw32+, +x86_64-darwin+, +x86_64-linux-gnu+. 31 | # If the list contains multiple values, +cmd+ is consecutively executed in each of the docker images, 32 | # Option +:platform+ is ignored when +:rubyvm+ is set to +:jruby+. 33 | # Default is "x86-mingw32 x64-mingw32" . 34 | # 35 | # Examples: 36 | # 37 | # RakeCompilerDock.sh 'bundle && rake cross native gem' 38 | # 39 | # Check exit status after command runs: 40 | # 41 | # sh %{bundle && rake cross native gem}, verbose: false do |ok, res| 42 | # if ! ok 43 | # puts "windows cross build failed (status = #{res.exitstatus})" 44 | # end 45 | # end 46 | def sh(cmd, options={}, &block) 47 | Starter.sh(cmd, options, &block) 48 | end 49 | 50 | def image_name 51 | Starter.image_name 52 | end 53 | 54 | # Run the command cmd within a fresh rake-compiler-dock container. 55 | # The command is run directly, without the shell. 56 | # 57 | # If a block is given, upon command completion the block is called with an OK flag (true on a zero exit status) and a Process::Status object. 58 | # Without a block a RuntimeError is raised when the command exits non-zero. 59 | # 60 | # * Option +:verbose+ can be set to enable printing of the command line. 61 | # If not set, rake's verbose flag is used. 62 | # * Option +:check_docker+ can be set to false to disable the docker check. 63 | # * Option +:sigfw+ can be set to false to not stop the container on Ctrl-C. 64 | # * Option +:runas+ can be set to false to execute the command as user root. 65 | # * Option +:options+ can be an Array of additional options to the 'docker run' command. 66 | # * Option +:username+ can be used to overwrite the user name in the container 67 | # * Option +:groupname+ can be used to overwrite the group name in the container 68 | # 69 | # Examples: 70 | # 71 | # RakeCompilerDock.exec 'bash', '-c', 'echo $RUBY_CC_VERSION' 72 | def exec(*args, &block) 73 | Starter.exec(*args, &block) 74 | end 75 | 76 | # Retrieve the cross-rubies that are available in the docker image. This can be used to construct 77 | # a custom `RUBY_CC_VERSION` string that is valid. 78 | # 79 | # Returns a Hash corresponding_patch_version> 80 | # 81 | # For example: 82 | # 83 | # RakeCompilerDock.cross_rubies 84 | # # => { 85 | # # "3.4" => "3.4.1", 86 | # # "3.3" => "3.3.5", 87 | # # "3.2" => "3.2.6", 88 | # # "3.1" => "3.1.6", 89 | # # "3.0" => "3.0.7", 90 | # # "2.7" => "2.7.8", 91 | # # } 92 | # 93 | def cross_rubies 94 | { 95 | "3.4" => "3.4.1", 96 | "3.3" => "3.3.7", 97 | "3.2" => "3.2.6", 98 | "3.1" => "3.1.6", 99 | "3.0" => "3.0.7", 100 | "2.7" => "2.7.8", 101 | } 102 | end 103 | 104 | # Returns a valid RUBY_CC_VERSION string for the given requirements, 105 | # where each `requirement` may be: 106 | # 107 | # - a String that matches the minor version exactly 108 | # - a String that can be used as a Gem::Requirement constructor argument 109 | # - a Gem::Requirement object 110 | # 111 | # Note that the returned string will contain versions sorted in descending order. 112 | # 113 | # For example: 114 | # RakeCompilerDock.ruby_cc_version("2.7", "3.4") 115 | # # => "3.4.1:2.7.8" 116 | # 117 | # RakeCompilerDock.ruby_cc_version("~> 3.2") 118 | # # => "3.4.1:3.3.7:3.2.6" 119 | # 120 | # RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.2")) 121 | # # => "3.4.1:3.3.7:3.2.6" 122 | # 123 | def ruby_cc_version(*requirements) 124 | cross = cross_rubies 125 | output = [] 126 | 127 | if requirements.empty? 128 | output += cross.values 129 | else 130 | requirements.each do |requirement| 131 | if cross[requirement] 132 | output << cross[requirement] 133 | else 134 | requirement = Gem::Requirement.new(requirement) unless requirement.is_a?(Gem::Requirement) 135 | versions = cross.values.find_all { |v| requirement.satisfied_by?(Gem::Version.new(v)) } 136 | raise("No matching ruby version for requirement: #{requirement.inspect}") if versions.empty? 137 | output += versions 138 | end 139 | end 140 | end 141 | 142 | output.uniq.sort.reverse.join(":") 143 | end 144 | 145 | # Set the environment variable `RUBY_CC_VERSION` to the value returned by `ruby_cc_version`, 146 | # for the given requirements. 147 | def set_ruby_cc_version(*requirements) 148 | ENV["RUBY_CC_VERSION"] = ruby_cc_version(*requirements) 149 | end 150 | 151 | module_function :exec, :sh, :image_name, :cross_rubies, :ruby_cc_version, :set_ruby_cc_version 152 | end 153 | -------------------------------------------------------------------------------- /lib/rake_compiler_dock/colors.rb: -------------------------------------------------------------------------------- 1 | module RakeCompilerDock 2 | module Colors 3 | # Start an escape sequence 4 | ESC = "\e[" 5 | 6 | # End the escape sequence 7 | NND = "#{ESC}0m" 8 | 9 | ColorMap = { 10 | black: 0, 11 | red: 1, 12 | green: 2, 13 | yellow: 3, 14 | blue: 4, 15 | magenta: 5, 16 | cyan: 6, 17 | white: 7, 18 | } 19 | 20 | ColorMap.each do |color, code| 21 | define_method(color) do |string| 22 | colored(code, string) 23 | end 24 | end 25 | 26 | def colored(color, string) 27 | if @colors_on 28 | c = ColorMap[color] || color 29 | "#{ESC}#{30+c}m#{string}#{NND}" 30 | else 31 | string.dup 32 | end 33 | end 34 | 35 | def enable_colors 36 | @colors_on = true 37 | end 38 | 39 | def disable_colors 40 | @colors_on = false 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/rake_compiler_dock/docker_check.rb: -------------------------------------------------------------------------------- 1 | require "uri" 2 | require "rbconfig" 3 | require "rake_compiler_dock/colors" 4 | 5 | module RakeCompilerDock 6 | class DockerCheck 7 | include Colors 8 | 9 | attr_reader :io 10 | attr_reader :pwd 11 | attr_reader :docker_command 12 | attr_accessor :machine_name 13 | 14 | def initialize(io, pwd, machine_name="rake-compiler-dock") 15 | @io = io 16 | @pwd = pwd 17 | @machine_name = machine_name 18 | 19 | if !io.tty? || (RUBY_PLATFORM=~/mingw|mswin/ && RUBY_VERSION[/^\d+/] < '2') 20 | disable_colors 21 | else 22 | enable_colors 23 | end 24 | 25 | docker_version 26 | 27 | unless ok? 28 | doma_version 29 | if doma_avail? 30 | io.puts 31 | io.puts yellow("docker-machine is available, but not ready to use. Trying to start.") 32 | 33 | doma_create 34 | if doma_create_ok? 35 | doma_start 36 | 37 | docker_version 38 | end 39 | else 40 | b2d_version 41 | 42 | if b2d_avail? 43 | io.puts 44 | io.puts yellow("boot2docker is available, but not ready to use. Trying to start.") 45 | 46 | b2d_init 47 | if b2d_init_ok? 48 | b2d_start 49 | 50 | docker_version 51 | end 52 | end 53 | end 54 | end 55 | end 56 | 57 | COMMANDS = %w[docker podman] 58 | def docker_version 59 | COMMANDS.find do |command| 60 | @docker_version_text, @docker_version_status = run("#{command} version") 61 | @docker_command = command 62 | @docker_version_status == 0 63 | end 64 | end 65 | 66 | def ok? 67 | @docker_version_status == 0 && @docker_version_text =~ /version/i && doma_pwd_ok? 68 | end 69 | 70 | def docker_client_avail? 71 | @docker_version_text =~ /version/ 72 | end 73 | 74 | def doma_version 75 | @doma_version_text, @doma_version_status = run("docker-machine --version") 76 | end 77 | 78 | def doma_avail? 79 | @doma_version_status == 0 && @doma_version_text =~ /version/ 80 | end 81 | 82 | def add_env_options(options, names) 83 | names.each do |name| 84 | if (v=ENV[name]) && !v.empty? 85 | options << ["--engine-env", "#{name}=#{ENV[name]}"] 86 | end 87 | end 88 | options 89 | end 90 | 91 | def doma_create 92 | options = add_env_options([], %w[ftp_proxy http_proxy https_proxy]) 93 | driver = ENV['MACHINE_DRIVER'] || 'virtualbox' 94 | @doma_create_text, @doma_create_status = run("docker-machine create --driver #{driver.inspect} #{options.join(" ")} #{machine_name}", cmd: :visible, output: :visible) 95 | end 96 | 97 | def doma_create_ok? 98 | @doma_create_status == 0 || @doma_create_text =~ /already exists/ 99 | end 100 | 101 | def doma_start 102 | @doma_start_text, @doma_start_status = run("docker-machine start #{machine_name}", cmd: :visible, output: :visible) 103 | @doma_env_set = false 104 | 105 | if doma_start_ok? 106 | @doma_env_text, @doma_env_status = run("docker-machine env #{machine_name} --shell bash --no-proxy") 107 | if @doma_env_status == 0 && set_env(@doma_env_text) 108 | @doma_env_set = true 109 | end 110 | end 111 | end 112 | 113 | def doma_start_ok? 114 | @doma_start_status == 0 || @doma_start_text =~ /already running/ 115 | end 116 | 117 | def doma_env_ok? 118 | @doma_env_status == 0 119 | end 120 | 121 | def doma_has_env? 122 | @doma_env_set 123 | end 124 | 125 | def b2d_version 126 | @b2d_version_text = `boot2docker version 2>&1` rescue SystemCallError 127 | @b2d_version_status = $?.exitstatus 128 | end 129 | 130 | def b2d_avail? 131 | @b2d_version_status == 0 && @b2d_version_text =~ /version/ 132 | end 133 | 134 | def b2d_init 135 | system("boot2docker init") rescue SystemCallError 136 | @b2d_init_status = $?.exitstatus 137 | end 138 | 139 | def b2d_init_ok? 140 | @b2d_init_status == 0 141 | end 142 | 143 | def b2d_start 144 | @b2d_start_text = `boot2docker start` rescue SystemCallError 145 | @b2d_start_status = $?.exitstatus 146 | @b2d_start_envset = false 147 | 148 | if @b2d_start_status == 0 149 | io.puts @b2d_start_text 150 | if set_env(@b2d_start_text) 151 | @b2d_start_envset = true 152 | io.puts yellow("Using above environment variables for starting #{machine_name}.") 153 | end 154 | end 155 | end 156 | 157 | def b2d_start_ok? 158 | @b2d_start_status == 0 159 | end 160 | 161 | def b2d_start_has_env? 162 | @b2d_start_envset 163 | end 164 | 165 | def host_os 166 | RbConfig::CONFIG['host_os'] 167 | end 168 | 169 | def doma_pwd_ok? 170 | case host_os 171 | when /mingw|mswin/ 172 | pwd =~ /^\/c\/users/i 173 | when /linux/ 174 | true 175 | when /darwin/ 176 | pwd =~ /^\/users/i 177 | end 178 | end 179 | 180 | def set_env(text) 181 | set = false 182 | text.scan(/(unset |Remove-Item Env:\\)(.+?)$/) do |_, key| 183 | ENV.delete(key) 184 | set = true 185 | end 186 | text.scan(/(export |\$Env:)(.+?)(="|=| = ")(.*?)(|\")$/) do |_, key, _, val, _| 187 | ENV[key] = val 188 | set = true 189 | end 190 | set 191 | end 192 | 193 | def help_text 194 | help = [] 195 | if !ok? && docker_client_avail? && !doma_avail? && !b2d_avail? 196 | help << red("Docker client tools work, but connection to the local docker server failed.") 197 | case host_os 198 | when /linux/ 199 | help << yellow("Please make sure the docker daemon is running.") 200 | help << "" 201 | help << yellow("On Ubuntu/Debian:") 202 | help << " sudo service docker start" 203 | help << yellow("or") 204 | help << " sudo service docker.io start" 205 | help << "" 206 | help << yellow("On Fedora/Centos/RHEL") 207 | help << " sudo systemctl start docker" 208 | help << "" 209 | help << yellow("On SuSE") 210 | help << " sudo systemctl start docker" 211 | help << "" 212 | help << yellow("Then re-check with '") + white("docker version") + yellow("'") 213 | help << yellow("or have a look at our FAQs: http://git.io/vm8AL") 214 | else 215 | help << yellow(" Please check why '") + white("docker version") + yellow("' fails") 216 | help << yellow(" or have a look at our FAQs: http://git.io/vm8AL") 217 | end 218 | elsif !ok? && !doma_avail? && !b2d_avail? 219 | case host_os 220 | when /mingw|mswin/ 221 | help << red("Docker is not available.") 222 | help << yellow("Please download and install the docker-toolbox:") 223 | help << yellow(" https://www.docker.com/docker-toolbox") 224 | when /linux/ 225 | help << red("Neither Docker nor Podman is available.") 226 | help << "" 227 | help << yellow("Install Docker on Ubuntu/Debian:") 228 | help << " sudo apt-get install docker.io" 229 | help << "" 230 | help << yellow("Install Docker on Fedora/Centos/RHEL") 231 | help << " sudo yum install docker" 232 | help << " sudo systemctl start docker" 233 | help << "" 234 | help << yellow("Install Docker on SuSE") 235 | help << " sudo zypper install docker" 236 | help << " sudo systemctl start docker" 237 | when /darwin/ 238 | help << red("Docker is not available.") 239 | help << yellow("Please install docker-machine per homebrew:") 240 | help << " brew cask install virtualbox" 241 | help << " brew install docker" 242 | help << " brew install docker-machine" 243 | help << "" 244 | help << yellow("or download and install the official docker-toolbox:") 245 | help << yellow(" https://www.docker.com/docker-toolbox") 246 | else 247 | help << red("Docker is not available.") 248 | end 249 | elsif doma_avail? 250 | if !ok? && !doma_create_ok? 251 | help << red("docker-machine is installed but machine couldn't be created.") 252 | help << "" 253 | help << yellow(" Please check why '") + white("docker-machine create") + yellow("' fails") 254 | help << yellow(" or have a look at our FAQs: http://git.io/vRzIg") 255 | elsif !ok? && !doma_start_ok? 256 | help << red("docker-machine is installed but couldn't be started.") 257 | help << "" 258 | help << yellow(" Please check why '") + white("docker-machine start") + yellow("' fails.") 259 | help << yellow(" You might need to re-init with '") + white("docker-machine rm") + yellow("'") 260 | help << yellow(" or have a look at our FAQs: http://git.io/vRzIg") 261 | elsif !ok? && !doma_env_ok? 262 | help << red("docker-machine is installed and started, but 'docker-machine env' failed.") 263 | help << "" 264 | help << yellow("You might try to regenerate TLS certificates with:") 265 | help << " docker-machine regenerate-certs #{machine_name}" 266 | elsif !ok? && !doma_pwd_ok? 267 | help << red("docker-machine can not mount the current working directory.") 268 | help << "" 269 | case host_os 270 | when /mingw|mswin/ 271 | help << yellow(" Please move to a diretory below C:\\Users") 272 | when /darwin/ 273 | help << yellow(" Please move to a diretory below /Users") 274 | end 275 | elsif !ok? 276 | help << red("docker-machine is installed and started, but 'docker version' failed.") 277 | help << "" 278 | 279 | if doma_has_env? 280 | help << yellow(" Please copy and paste following environment variables to your terminal") 281 | help += @doma_env_text.each_line.reject{|l| l=~/\s*#/ }.map{|l| " #{l.chomp}" } 282 | help << yellow(" and check why '") + white("docker version") + yellow("' fails.") 283 | else 284 | help << yellow(" Please check why '") + white("docker version") + yellow("' fails.") 285 | end 286 | help << yellow(" You might also have a look at our FAQs: http://git.io/vRzIg") 287 | end 288 | elsif b2d_avail? 289 | if !ok? && !b2d_init_ok? 290 | help << red("boot2docker is installed but couldn't be initialized.") 291 | help << "" 292 | help << yellow(" Please check why '") + white("boot2docker init") + yellow("' fails") 293 | help << yellow(" or have a look at our FAQs: http://git.io/vm8Nr") 294 | elsif !ok? && !b2d_start_ok? 295 | help << red("boot2docker is installed but couldn't be started.") 296 | help << "" 297 | help << yellow(" Please check why '") + white("boot2docker start") + yellow("' fails.") 298 | help << yellow(" You might need to re-init with '") + white("boot2docker delete") + yellow("'") 299 | help << yellow(" or have a look at our FAQs: http://git.io/vm8Nr") 300 | elsif !ok? && !doma_pwd_ok? 301 | help << red("boot2docker can not mount the current working directory.") 302 | help << "" 303 | case host_os 304 | when /mingw|mswin/ 305 | help << yellow(" Please move to a diretory below C:\\Users") 306 | when /darwin/ 307 | help << yellow(" Please move to a diretory below /Users") 308 | end 309 | elsif !ok? && b2d_start_ok? 310 | help << red("boot2docker is installed and started, but 'docker version' failed.") 311 | help << "" 312 | if b2d_start_has_env? 313 | help << yellow(" Please copy and paste above environment variables to your terminal") 314 | help << yellow(" and check why '") + white("docker version") + yellow("' fails.") 315 | else 316 | help << yellow(" Please check why '") + white("docker version") + yellow("' fails.") 317 | end 318 | help << yellow(" You might need to re-init with '") + white("boot2docker delete") + yellow("'") 319 | help << yellow(" or have a look at our FAQs: http://git.io/vm8Nr") 320 | end 321 | end 322 | 323 | help.join("\n") 324 | end 325 | 326 | def print_help_text 327 | io.puts(help_text) 328 | end 329 | 330 | private 331 | 332 | def run(cmd, options={}) 333 | if options[:cmd] == :visible 334 | io.puts "$ #{green(cmd)}" 335 | end 336 | 337 | if options[:output] == :visible 338 | text = String.new 339 | begin 340 | IO.popen("#{cmd} 2>&1") do |fd| 341 | while !fd.eof? 342 | t = fd.read_nonblock(1024) 343 | io.write t 344 | text << t 345 | end 346 | end 347 | rescue SystemCallError 348 | text = nil 349 | end 350 | else 351 | text = `#{cmd} 2>&1` rescue SystemCallError 352 | end 353 | [text, $?.exitstatus] 354 | end 355 | end 356 | end 357 | -------------------------------------------------------------------------------- /lib/rake_compiler_dock/predefined_user_group.rb: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This file is generated per 'rake update_lists' 2 | module RakeCompilerDock 3 | PredefinedUsers = ["_apt", "adm", "backup", "bin", "daemon", "dbus", "ftp", "games", "gnats", "halt", "irc", "list", "lp", "mail", "man", "news", "nobody", "operator", "proxy", "root", "rubyuser", "shutdown", "sync", "sys", "systemd-network", "uucp", "www-data"] 4 | PredefinedGroups = ["adm", "audio", "backup", "bin", "cdrom", "cgred", "daemon", "dbus", "dialout", "dip", "disk", "fax", "floppy", "ftp", "games", "gnats", "input", "irc", "kmem", "list", "lock", "lp", "mail", "man", "mem", "news", "nobody", "nogroup", "operator", "plugdev", "proxy", "root", "rubyuser", "sasl", "shadow", "src", "ssh", "ssh_keys", "staff", "sudo", "sys", "systemd-journal", "systemd-network", "tape", "tty", "users", "utempter", "utmp", "uucp", "video", "voice", "wheel", "www-data"] 5 | end 6 | -------------------------------------------------------------------------------- /lib/rake_compiler_dock/starter.rb: -------------------------------------------------------------------------------- 1 | require "shellwords" 2 | require "etc" 3 | require "io/console" 4 | require "rake_compiler_dock/version" 5 | 6 | module RakeCompilerDock 7 | class DockerIsNotAvailable < RuntimeError 8 | end 9 | 10 | class Starter 11 | class << self 12 | def sh(cmd, options={}, &block) 13 | begin 14 | exec('bash', '-c', cmd, options, &block) 15 | ensure 16 | STDIN.cooked! if STDIN.tty? 17 | end 18 | end 19 | 20 | def exec(*args) 21 | options = (Hash === args.last) ? args.pop : {} 22 | 23 | mountdir = options.fetch(:mountdir){ ENV['RCD_MOUNTDIR'] || Dir.pwd } 24 | workdir = options.fetch(:workdir){ ENV['RCD_WORKDIR'] || Dir.pwd } 25 | case RUBY_PLATFORM 26 | when /mingw|mswin/ 27 | mountdir = sanitize_windows_path(mountdir) 28 | workdir = sanitize_windows_path(workdir) 29 | # Virtualbox shared folders don't care about file permissions, so we use generic ids. 30 | uid = 1000 31 | gid = 1000 32 | when /darwin/ 33 | uid = 1000 34 | gid = 1000 35 | else 36 | # Docker mounted volumes also share file uid/gid and permissions with the host. 37 | # Therefore we use the same attributes inside and outside the container. 38 | uid = Process.uid 39 | gid = Process.gid 40 | end 41 | user = options.fetch(:username){ current_user } 42 | group = options.fetch(:groupname){ current_group } 43 | 44 | platforms(options).split(" ").each do |platform| 45 | image_name = container_image_name(options.merge(platform: platform)) 46 | 47 | check = check_docker(mountdir) if options.fetch(:check_docker){ true } 48 | docker_opts = options.fetch(:options) do 49 | opts = ["--rm", "-i"] 50 | opts << "-t" if $stdin.tty? 51 | opts 52 | end 53 | 54 | if verbose_flag(options) && args.size == 3 && args[0] == "bash" && args[1] == "-c" 55 | $stderr.puts "rake-compiler-dock bash -c #{ args[2].inspect }" 56 | end 57 | 58 | runargs = args.dup 59 | runargs.unshift("sigfw") if options.fetch(:sigfw){ true } 60 | runargs.unshift("runas") if options.fetch(:runas){ true } 61 | 62 | cmd = [check.docker_command, "run", 63 | "-v", "#{mountdir}:#{make_valid_path(mountdir)}", 64 | "-e", "UID=#{uid}", 65 | "-e", "GID=#{gid}", 66 | "-e", "USER=#{user}", 67 | "-e", "GROUP=#{group}", 68 | "-e", "GEM_PRIVATE_KEY_PASSPHRASE", 69 | "-e", "SOURCE_DATE_EPOCH", 70 | "-e", "ftp_proxy", 71 | "-e", "http_proxy", 72 | "-e", "https_proxy", 73 | "-e", "RCD_HOST_RUBY_PLATFORM=#{RUBY_PLATFORM}", 74 | "-e", "RCD_HOST_RUBY_VERSION=#{RUBY_VERSION}", 75 | "-e", "RCD_IMAGE=#{image_name}", 76 | "-w", make_valid_path(workdir), 77 | *docker_opts, 78 | image_name, 79 | *runargs] 80 | 81 | cmdline = Shellwords.join(cmd) 82 | if verbose_flag(options) == true 83 | $stderr.puts cmdline 84 | end 85 | 86 | ok = system(*cmd) 87 | if block_given? 88 | yield(ok, $?) 89 | elsif !ok 90 | fail "Command failed with status (#{$?.exitstatus}): " + 91 | "[#{cmdline}]" 92 | end 93 | end 94 | end 95 | 96 | def verbose_flag(options) 97 | options.fetch(:verbose) do 98 | Object.const_defined?(:Rake) && Rake.const_defined?(:FileUtilsExt) ? Rake::FileUtilsExt.verbose_flag : false 99 | end 100 | end 101 | 102 | def current_user 103 | make_valid_user_name(Etc.getlogin) 104 | end 105 | 106 | def current_group 107 | group_obj = Etc.getgrgid rescue nil 108 | make_valid_group_name(group_obj ? group_obj.name : "dummygroup") 109 | end 110 | 111 | def make_valid_name(name) 112 | name = name.to_s.downcase 113 | name = "_" if name.empty? 114 | # Convert disallowed characters 115 | if name.length > 1 116 | name = name[0..0].gsub(/[^a-z_]/, "_") + name[1..-2].to_s.gsub(/[^a-z0-9_-]/, "_") + name[-1..-1].to_s.gsub(/[^a-z0-9_$-]/, "_") 117 | else 118 | name = name.gsub(/[^a-z_]/, "_") 119 | end 120 | 121 | # Limit to 32 characters 122 | name.sub( /^(.{16}).{2,}(.{15})$/ ){ $1+"-"+$2 } 123 | end 124 | 125 | def make_valid_user_name(name) 126 | name = make_valid_name(name) 127 | PredefinedUsers.include?(name) ? make_valid_name("_#{name}") : name 128 | end 129 | 130 | def make_valid_group_name(name) 131 | name = make_valid_name(name) 132 | PredefinedGroups.include?(name) ? make_valid_name("_#{name}") : name 133 | end 134 | 135 | def make_valid_path(name) 136 | # Convert problematic characters 137 | name = name.gsub(/[ ]/i, "_") 138 | end 139 | 140 | @@docker_checked_lock = Mutex.new 141 | @@docker_checked = {} 142 | 143 | def check_docker(pwd) 144 | @@docker_checked_lock.synchronize do 145 | @@docker_checked[pwd] ||= begin 146 | check = DockerCheck.new($stderr, pwd) 147 | unless check.ok? 148 | at_exit do 149 | check.print_help_text 150 | end 151 | raise DockerIsNotAvailable, "Docker is not available" 152 | end 153 | check 154 | end 155 | end 156 | end 157 | 158 | # Change Path from "C:\Path" to "/c/Path" as used by boot2docker 159 | def sanitize_windows_path(path) 160 | path.gsub(/^([a-z]):/i){ "/#{$1.downcase}" } 161 | end 162 | 163 | def container_image_name(options={}) 164 | options.fetch(:image) do 165 | image_name = ENV['RCD_IMAGE'] || ENV['RAKE_COMPILER_DOCK_IMAGE'] 166 | return image_name unless image_name.nil? 167 | 168 | "%s/rake-compiler-dock-image:%s-%s%s" % [ 169 | container_registry, 170 | options.fetch(:version) { IMAGE_VERSION }, 171 | container_rubyvm(options), 172 | container_jrubyvm?(options) ? "" : "-#{options.fetch(:platform)}", 173 | ] 174 | end 175 | end 176 | 177 | def container_registry 178 | ENV['CONTAINER_REGISTRY'] || "ghcr.io/rake-compiler" 179 | end 180 | 181 | def container_rubyvm(options={}) 182 | return "jruby" if options[:platform] == "jruby" 183 | options.fetch(:rubyvm) { ENV['RCD_RUBYVM'] } || "mri" 184 | end 185 | 186 | def container_jrubyvm?(options={}) 187 | container_rubyvm(options).to_s == "jruby" 188 | end 189 | 190 | def platforms(options={}) 191 | options.fetch(:platform) { ENV['RCD_PLATFORM'] } || 192 | (container_jrubyvm?(options) ? "jruby" : "x86-mingw32 x64-mingw32") 193 | end 194 | end 195 | end 196 | end 197 | -------------------------------------------------------------------------------- /lib/rake_compiler_dock/version.rb: -------------------------------------------------------------------------------- 1 | module RakeCompilerDock 2 | VERSION = "1.9.1" 3 | IMAGE_VERSION = "1.9.1" 4 | end 5 | -------------------------------------------------------------------------------- /mingw64-ucrt/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | RUN set -ex \ 6 | && sed -i -- 's/# deb-src/deb-src/g' /etc/apt/sources.list \ 7 | && apt-get update \ 8 | && apt-get install -y --no-install-recommends \ 9 | build-essential \ 10 | cdbs \ 11 | devscripts \ 12 | equivs \ 13 | fakeroot \ 14 | pkg-config \ 15 | && apt-get clean \ 16 | && rm -rf /tmp/* /var/tmp/* 17 | 18 | WORKDIR /build 19 | 20 | # Download mingw sources 21 | RUN apt-get source mingw-w64-common && \ 22 | cd mingw-w64-* && \ 23 | mk-build-deps -ir -t "apt-get -o Debug::pkgProblemResolver=yes -y --no-install-recommends" 24 | 25 | # Patch build for UCRT and build UCRT enabled deb-packages 26 | ADD mingw-w64-*.patch ./ 27 | RUN cd mingw-w64-* && \ 28 | cat debian/rules && \ 29 | ls ../mingw-w64-*.patch | xargs -n1 patch --verbose -p1 -i && \ 30 | dpkg-buildpackage -b && \ 31 | rm -rf ../*.ddeb ../*i686* `pwd` 32 | 33 | # Install UCRT enabled deb-packages 34 | RUN dpkg -i mingw-w64-common_7.0.0-2_all.deb \ 35 | mingw-w64-tools_7.0.0-2_*.deb \ 36 | mingw-w64-x86-64-dev_7.0.0-2_all.deb 37 | 38 | # Download gcc-binutils sources for mingw 39 | RUN apt-get source binutils-mingw-w64 && \ 40 | cd binutils-mingw-w64-* && \ 41 | mk-build-deps -ir -t "apt-get -o Debug::pkgProblemResolver=yes -y --no-install-recommends" 42 | 43 | # Build gcc-binutils based on UCRT headers and crt 44 | ADD binutils-mingw-w64-*.patch ./ 45 | RUN cd binutils-mingw-w64-* && \ 46 | ls ../binutils-mingw-w64-*.patch | xargs -n1 patch --verbose -p1 -i && \ 47 | dpkg-buildpackage -b --jobs=auto && \ 48 | rm -rf ../*.ddeb ../*i686* `pwd` 49 | 50 | # Download gcc sources for mingw 51 | RUN apt-get source gcc-mingw-w64 && \ 52 | cd gcc-mingw-w64-* && \ 53 | mk-build-deps -ir -t "apt-get -o Debug::pkgProblemResolver=yes -y --no-install-recommends" 54 | 55 | # Add version based alias 56 | RUN ln -s `which autoconf` `which autoconf`2.69 && \ 57 | ln -s `which autom4te` `which autom4te`2.69 58 | 59 | # Build gcc (only C and C++) based on UCRT headers and crt 60 | ADD gcc-mingw-w64-*.patch ./ 61 | RUN cd gcc-mingw-w64-* && \ 62 | ls ../gcc-mingw-w64-*.patch | xargs -n1 patch --verbose -p1 -i && \ 63 | dpkg-buildpackage -b --jobs=auto && \ 64 | rm -rf ../*.ddeb ../*i686* `pwd` 65 | 66 | RUN ls -l *.deb 67 | -------------------------------------------------------------------------------- /mingw64-ucrt/README.md: -------------------------------------------------------------------------------- 1 | Docker image with compilers for ruby platform x64-mingw-ucrt 2 | ------------------ 3 | 4 | This Dockerfile builds compilers for Windows UCRT target. 5 | It takes the mingw compiler provided by Debian/Ubuntu and configures and compiles them for UCRT. 6 | Outputs are *.deb files of binutils, gcc and g++. 7 | Rake-compiler-dock reads them from this image as part of its build process for the x64-mingw-ucrt platform. 8 | 9 | The image is provided for arm64 and amd64 architectures. 10 | They are built by the following command: 11 | 12 | ```sh 13 | docker buildx build . -t larskanis/mingw64-ucrt:20.04 --platform linux/arm64,linux/amd64 --push 14 | ``` 15 | 16 | 17 | Create builder instance for two architectures 18 | ------------------ 19 | 20 | Building with qemu emulation fails currently with a segfault, so that it must be built by a builder instance with at least one remote node for the other architecture. 21 | Building on native hardware is also much faster (~30 minutes) than on qemu. 22 | A two-nodes builder requires obviously a ARM and a Intel/AMD device. 23 | It can be created like this: 24 | 25 | ```sh 26 | # Make sure the remote instance can be connected 27 | $ docker -H ssh://isa info 28 | 29 | # Create a new builder with the local instance 30 | $ docker buildx create --name isayoga 31 | 32 | # Add the remote instance 33 | $ docker buildx create --name isayoga --append ssh://isa 34 | 35 | # They are inactive from the start 36 | $ docker buildx ls 37 | NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS 38 | isayoga docker-container 39 | \_ isayoga0 \_ unix:///var/run/docker.sock inactive 40 | \_ isayoga1 \_ ssh://isa inactive 41 | default* docker 42 | \_ default \_ default running v0.13.2 linux/arm64 43 | 44 | # Bootstrap the instances 45 | $ docker buildx inspect --bootstrap --builder isayoga 46 | 47 | # Set the new builder as default 48 | $ docker buildx use isayoga 49 | 50 | # Now it should be default and in state "running" 51 | $ docker buildx ls 52 | NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS 53 | isayoga* docker-container 54 | \_ isayoga0 \_ unix:///var/run/docker.sock running v0.18.2 linux/arm64 55 | \_ isayoga1 \_ ssh://isa running v0.18.2 linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386 56 | default docker 57 | \_ default \_ default running v0.13.2 linux/arm64 58 | ``` 59 | -------------------------------------------------------------------------------- /mingw64-ucrt/binutils-mingw-w64-ignore-check-errors.patch: -------------------------------------------------------------------------------- 1 | diff --git a/debian/rules b/debian/rules 2 | index 4401493..62c8fe4 100755 3 | --- a/debian/rules 4 | +++ b/debian/rules 5 | @@ -91,7 +91,7 @@ override_dh_auto_test-arch: 6 | unset CFLAGS CPPFLAGS LDFLAGS; \ 7 | env > $(build_dir)/env; \ 8 | for target in $(targets); do \ 9 | - dh_auto_test -B$(build_dir)/$$target; \ 10 | + dh_auto_test -B$(build_dir)/$$target || true; \ 11 | done 12 | 13 | override_dh_auto_install-arch: 14 | -------------------------------------------------------------------------------- /mingw64-ucrt/gcc-mingw-w64-only-c-c++.patch: -------------------------------------------------------------------------------- 1 | diff --git a/debian/rules b/debian/rules 2 | index 9e7e40c..fa9a8a2 100755 3 | --- a/debian/rules 4 | +++ b/debian/rules 5 | @@ -58,7 +58,7 @@ ifneq ($(filter stage1,$(DEB_BUILD_PROFILES)),) 6 | INSTALL_TARGET := install-gcc 7 | else 8 | # Build the full GCC. 9 | - languages := c,c++,fortran,objc,obj-c++,ada 10 | + languages := c,c++ 11 | BUILD_TARGET := 12 | INSTALL_TARGET := install install-lto-plugin 13 | endif 14 | -------------------------------------------------------------------------------- /mingw64-ucrt/mingw-w64-enable-ucrt.patch: -------------------------------------------------------------------------------- 1 | diff --git a/debian/rules b/debian/rules 2 | index 9fb970c..5473839 100755 3 | --- a/debian/rules 4 | +++ b/debian/rules 5 | @@ -94,7 +94,7 @@ build-arch-stamp: $(patsubst %,$(host)~$(host)~tools-%-install-stamp,$(HOST_TOOL 6 | %~headers-configure-stamp: autoreconf-stamp 7 | mkdir -p $(call buildfolder,$*~headers) && \ 8 | cd $(call buildfolder,$*~headers) && \ 9 | - $(call sourcefolder,$*~headers)/configure --prefix=/usr/$(call ruletarget,$*~headers) --host=$(call rulehost,$*~headers) --enable-sdk=all --enable-secure-api 10 | + $(call sourcefolder,$*~headers)/configure --prefix=/usr/$(call ruletarget,$*~headers) --host=$(call rulehost,$*~headers) --enable-sdk=all --with-default-msvcrt=ucrt 11 | touch $*-headers-configure-stamp 12 | 13 | # Override CRT configuration to avoid multilib builds 14 | @@ -108,7 +108,7 @@ target64crt := $(target64)~$(target64)~crt 15 | $(target64crt)-configure-stamp: $(target64)~$(target64)~headers-install-stamp autoreconf-stamp 16 | mkdir -p $(call buildfolder,$(target64crt)) && \ 17 | cd $(call buildfolder,$(target64crt)) && \ 18 | - $(call sourcefolder,$(target64crt))/configure --prefix=/usr/$(call ruletarget,$(target64crt)) --host=$(call rulehost,$(target64crt)) --target=$(call ruletarget,$(target64crt)) --disable-lib32 --enable-lib64 CPPFLAGS="$(CPPFLAGS) -I$(top_dir)/debian/tmp/usr/$(call ruletarget,$(target64crt))/include" 19 | + $(call sourcefolder,$(target64crt))/configure --prefix=/usr/$(call ruletarget,$(target64crt)) --host=$(call rulehost,$(target64crt)) --target=$(call ruletarget,$(target64crt)) --disable-lib32 --enable-lib64 --with-default-msvcrt=ucrt CPPFLAGS="$(CPPFLAGS) -I$(top_dir)/debian/tmp/usr/$(call ruletarget,$(target64crt))/include" 20 | touch $@ 21 | 22 | build-indep-stamp: $(foreach target,$(targets),$(patsubst %,$(target)~$(target)~%-install-stamp,$(TARGET_PROJECTS))) $(patsubst %,$(target32)~$(target32)~%-install-stamp,$(TARGET32_PROJECTS)) 23 | -------------------------------------------------------------------------------- /rake-compiler-dock.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'rake_compiler_dock/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "rake-compiler-dock" 8 | spec.version = RakeCompilerDock::VERSION 9 | spec.authors = ["Lars Kanis"] 10 | spec.email = ["lars@greiz-reinsdorf.de"] 11 | spec.summary = %q{Easy to use and reliable cross compiler environment for building Windows and Linux binary gems.} 12 | spec.description = %q{Easy to use and reliable cross compiler environment for building Windows and Linux binary gems. 13 | Use rake-compiler-dock to enter an interactive shell session or add a task to your Rakefile to automate your cross build.} 14 | spec.homepage = "https://github.com/rake-compiler/rake-compiler-dock" 15 | spec.license = "MIT" 16 | # We do not set a ruby version in the gemspec, to allow addition to the Gemfile of gems that still support ruby-1.8. 17 | # However we do the version check at runtime. 18 | # spec.required_ruby_version = '>= 1.9.2' 19 | 20 | spec.files = begin 21 | `git ls-files -z`.split("\x0") 22 | rescue StandardError => e 23 | warn "WARNING: Could not discover files for gemspec: #{e}" 24 | [] 25 | end 26 | 27 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 28 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 29 | spec.require_paths = ["lib"] 30 | 31 | spec.add_development_dependency "bundler", ">= 1.7", "< 3.0" 32 | spec.add_development_dependency "rake", ">= 12" 33 | spec.add_development_dependency "test-unit", "~> 3.0" 34 | end 35 | -------------------------------------------------------------------------------- /test/env/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | ARG from_image 2 | FROM ${from_image} 3 | 4 | RUN uname -a 5 | RUN apk add ruby ruby-rake git 6 | 7 | RUN ruby --version 8 | RUN gem env 9 | RUN gem inst bundler 10 | 11 | WORKDIR /build 12 | 13 | CMD ruby -e "puts Gem::Platform.local.to_s" && \ 14 | gem install --local *.gem --verbose --no-document && \ 15 | cd test/rcd_test/ && \ 16 | bundle install && \ 17 | rake test 18 | -------------------------------------------------------------------------------- /test/env/Dockerfile.debian: -------------------------------------------------------------------------------- 1 | ARG from_image 2 | FROM ${from_image} 3 | 4 | # To prevent installed tzdata deb package to show interactive dialog. 5 | ENV DEBIAN_FRONTEND noninteractive 6 | 7 | RUN uname -a 8 | RUN apt-get update -qq && \ 9 | apt-get install -yq \ 10 | -o Dpkg::Options::='--force-confnew' \ 11 | ruby \ 12 | git 13 | 14 | RUN ruby --version 15 | RUN gem env 16 | RUN gem inst bundler 17 | 18 | WORKDIR /build 19 | 20 | CMD ruby -e "puts Gem::Platform.local.to_s" && \ 21 | gem install --local *.gem --verbose --no-document && \ 22 | cd test/rcd_test/ && \ 23 | bundle install && \ 24 | rake test 25 | -------------------------------------------------------------------------------- /test/fixtures/mig_test_rpc.defs: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | subsystem mig_test_ipc 1; 5 | 6 | routine mig_test_call( 7 | server_port : mach_port_t; 8 | out returnvalue : int); 9 | -------------------------------------------------------------------------------- /test/rcd_test/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | # Specify your gem's dependencies in rcd_test.gemspec 6 | gemspec 7 | 8 | gem "rake", "~> 13.0" 9 | gem "rake-compiler" 10 | gem "rake-compiler-dock", path: "../.." 11 | gem "minitest", "~> 5.0" 12 | -------------------------------------------------------------------------------- /test/rcd_test/Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rubygems/package_task" 3 | require "rake/testtask" 4 | 5 | rcd_test_spec = Bundler.load_gemspec("rcd_test.gemspec") 6 | 7 | if RUBY_ENGINE == "jruby" 8 | require "rake/javaextensiontask" 9 | 10 | Rake::JavaExtensionTask.new("rcd_test_ext", rcd_test_spec) do |ext| 11 | ext.ext_dir = 'ext/java' 12 | ext.lib_dir = 'lib/rcd_test' 13 | end 14 | else 15 | require "rake/extensiontask" 16 | 17 | exttask = Rake::ExtensionTask.new('rcd_test_ext', rcd_test_spec) do |ext| 18 | ext.ext_dir = 'ext/mri' 19 | ext.lib_dir = 'lib/rcd_test' 20 | ext.cross_compile = true 21 | ext.cross_platform = %w[ 22 | aarch64-linux 23 | aarch64-linux-gnu 24 | aarch64-linux-musl 25 | aarch64-mingw-ucrt 26 | arm-linux 27 | arm-linux-gnu 28 | arm-linux-musl 29 | arm64-darwin 30 | x64-mingw-ucrt 31 | x64-mingw32 32 | x86-linux 33 | x86-linux-gnu 34 | x86-linux-musl 35 | x86-mingw32 36 | x86_64-darwin 37 | x86_64-linux 38 | x86_64-linux-gnu 39 | x86_64-linux-musl 40 | ] 41 | end 42 | end 43 | 44 | namespace "gem" do 45 | if exttask 46 | task 'prepare' do 47 | require 'rake_compiler_dock' 48 | require 'io/console' 49 | sh "bundle package" 50 | sh "mkdir -p tmp/gem" 51 | sh "cp ~/.gem/gem-*.pem tmp/gem/ || true" 52 | unless ENV['CI'] 53 | ENV["GEM_PRIVATE_KEY_PASSPHRASE"] = STDIN.getpass("Enter passphrase of gem signature key: ") 54 | end 55 | end 56 | 57 | exttask.cross_platform.each do |plat| 58 | desc "Build all native binary gems" 59 | multitask 'native' => plat 60 | 61 | desc "Build the native gem for #{plat}" 62 | task plat => 'prepare' do 63 | config = "-- #{ENV['RCD_TEST_CONFIG']}" 64 | # Set mountdir of the directory where .git resides, 65 | # - so that git ls-files in the gemspec works 66 | # - and to bundle the rake-compiler-dock under test 67 | RakeCompilerDock.sh <<-EOT, platform: plat, mountdir: Dir.pwd + "/../..", verbose: true 68 | (cp tmp/gem/gem-*.pem ~/.gem/ || true) && 69 | bundle --local && 70 | rake native:#{plat} pkg/#{exttask.gem_spec.full_name}-#{plat}.gem "MAKE=make V=1" #{config} 71 | EOT 72 | end 73 | end 74 | end 75 | 76 | desc "Build a jruby gem" 77 | task "jruby" do 78 | require 'rake_compiler_dock' 79 | sh "bundle package" 80 | RakeCompilerDock.sh <<-EOT, rubyvm: "jruby", platform: "jruby", mountdir: Dir.pwd + "/../..", verbose: true 81 | mvn archetype:generate -DartifactId=test -DarchetypeVersion=1.4 -DinteractiveMode=false -DgroupId=test -Dpackage=test && 82 | bundle --local && 83 | bundle exec rake java gem 84 | EOT 85 | end 86 | end 87 | 88 | Rake::TestTask.new(:test) do |t| 89 | t.libs << "test" 90 | t.libs << "lib" 91 | t.test_files = FileList["test/**/test_*.rb"] 92 | end 93 | 94 | task default: [:clobber, :compile, :test] 95 | task gem: :build 96 | 97 | CLEAN.add("{ext,lib}/**/*.{o,so}", "pkg", "tmp") 98 | -------------------------------------------------------------------------------- /test/rcd_test/ext/java/RcdTestExtService.java: -------------------------------------------------------------------------------- 1 | package rcd_test; 2 | 3 | import org.jruby.Ruby; 4 | import org.jruby.RubyClass; 5 | import org.jruby.RubyModule; 6 | import org.jruby.runtime.ObjectAllocator; 7 | import org.jruby.runtime.builtin.IRubyObject; 8 | import org.jruby.runtime.load.BasicLibraryService; 9 | 10 | import java.io.IOException; 11 | 12 | public class RcdTestExtService implements BasicLibraryService { 13 | @Override 14 | public boolean basicLoad(final Ruby ruby) throws IOException { 15 | RubyModule rb_mRcdTest = ruby.defineModule("RcdTest"); 16 | rb_mRcdTest.defineAnnotatedMethods(RubyRcdTest.class); 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/rcd_test/ext/java/RubyRcdTest.java: -------------------------------------------------------------------------------- 1 | package rcd_test; 2 | 3 | import org.jruby.Ruby; 4 | import org.jruby.anno.JRubyMethod; 5 | import org.jruby.runtime.builtin.IRubyObject; 6 | import org.jruby.anno.JRubyModule; 7 | import org.jruby.runtime.ThreadContext; 8 | 9 | @JRubyModule(name = "RcdTest") 10 | public class RubyRcdTest { 11 | @JRubyMethod(name = "do_something", module = true, meta = true) 12 | public static IRubyObject buildSelfString(ThreadContext context, IRubyObject recv) { 13 | Ruby runtime = context.getRuntime(); 14 | return runtime.newString("something has been done"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/rcd_test/ext/mri/extconf.rb: -------------------------------------------------------------------------------- 1 | if RUBY_ENGINE == "jruby" 2 | File.open("Makefile", "w") do |mf| 3 | mf.puts "# Dummy makefile for JRuby" 4 | mf.puts "all install::\n" 5 | end 6 | else 7 | require "mkmf" 8 | 9 | include RbConfig 10 | 11 | puts "-"*70 12 | puts "CONFIG['arch']: #{CONFIG['arch'].inspect}" 13 | puts "CONFIG['sitearch']: #{CONFIG['sitearch'].inspect}" 14 | puts "CONFIG['host']: #{CONFIG['host'].inspect}" 15 | puts "CONFIG['RUBY_SO_NAME']: #{CONFIG['RUBY_SO_NAME'].inspect}" 16 | puts "RUBY_PLATFORM: #{RUBY_PLATFORM.inspect}" 17 | puts "Gem::Platform.local.to_s: #{Gem::Platform.local.to_s.inspect}" 18 | puts "cc --version: #{ %x[#{CONFIG['CC']} --version].lines.first}" 19 | puts "-"*70 20 | 21 | $CFLAGS << " -Wall -Werror" 22 | 23 | have_func('rb_thread_call_without_gvl', 'ruby/thread.h') || 24 | raise("rb_thread_call_without_gvl() not found") 25 | 26 | if arg_config("--link-static", false) 27 | # https://github.com/rake-compiler/rake-compiler-dock/issues/69 28 | puts "Linking with '-static' flag" 29 | $LDFLAGS << ' -static' 30 | else 31 | if RbConfig::CONFIG["target_os"].include?("darwin") 32 | ## In ruby 3.2, symbol resolution changed on Darwin, to introduce the `-bundle_loader` flag. 33 | ## 34 | ## See https://github.com/rake-compiler/rake-compiler-dock/issues/87 for a lot of context, but 35 | ## I'll try to summarize here. 36 | ## 37 | ## > -bundle_loader executable 38 | ## > This specifies the executable that will be loading the bundle output file being linked. 39 | ## > Undefined symbols from the bundle are checked against the specified executable like it 40 | ## > was one of the dynamic libraries the bundle was linked with. 41 | ## 42 | ## There are good reasons to do this, including faster initialiation/loading as the Darwin 43 | ## toolchain gets improved over time. 44 | ## 45 | ## Unfortunately, this flag prevents us from building a shared object that works with both a 46 | ## Ruby compiled with `--enable-shared` and one compiled with `--disabled-shared`. The result 47 | ## is usually an "Symbol not found" error about `_rb_cObject`, or a "dyld: missing symbol 48 | ## called" error. 49 | ## 50 | ## There are two workarounds that I know of (there may be others I don't know of), and 51 | ## they are ... 52 | 53 | ## ---------------------------------------- 54 | ## SOLUTION 1, the `-flat_namespace` flag 55 | ## 56 | ## > Two-level namespace 57 | ## > By default all references resolved to a dynamic library record the library to which 58 | ## > they were resolved. At runtime, dyld uses that information to directly resolve symbols. 59 | ## > The alternative is to use the -flat_namespace option. With flat namespace, the library 60 | ## > is not recorded. At runtime, dyld will search each dynamic library in load order when 61 | ## > resolving symbols. This is slower, but more like how other operating systems resolve 62 | ## > symbols. 63 | ## 64 | # 65 | # puts "Adding '-flat_namespace'" 66 | # $LDFLAGS << ' -flat_namespace' 67 | # 68 | ## This solution unfortunately introduces new behavior that any symbols statically linked into 69 | ## the shared object (e.g., libxml2 in nokogiri.bundle) may not be resolved locally from the 70 | ## shared object, but instead resolved from a shared object loaded in the main process. 71 | ## 72 | ## This solution might be good for you if: 73 | ## - you don't statically link things into your bundle, 74 | ## - or you don't export those symbols, 75 | ## - or you can avoid exporting those symbols (e.g., by using `-load_hidden`, or 76 | ## `-exported_symbols_list` or some other mechanism) 77 | ## 78 | 79 | ## ---------------------------------------- 80 | ## BUT ... if that is a problem, try SOLUTION 2, remove the `-bundle-loader` flag 81 | ## 82 | ## This returns us to the symbol resolution we had in previous Rubies. It feels gross but may 83 | ## be a workaround for gem maintainers until we all figure out a better way to deal with this. 84 | # 85 | # extdldflags = RbConfig::MAKEFILE_CONFIG["EXTDLDFLAGS"].split 86 | # if found = extdldflags.index("-bundle_loader") 87 | # removed_1 = extdldflags.delete_at(found) # flag 88 | # removed_2 = extdldflags.delete_at(found) # and its argument 89 | # puts "Removing '#{removed_1} #{removed_2}' from EXTDLDFLAGS" 90 | # end 91 | # RbConfig::MAKEFILE_CONFIG["EXTDLDFLAGS"] = extdldflags.join(" ") 92 | # 93 | end 94 | end 95 | 96 | create_makefile("rcd_test/rcd_test_ext") 97 | 98 | # exercise the strip command - this approach borrowed from grpc 99 | strip_tool = RbConfig::CONFIG['STRIP'] 100 | strip_tool += ' -x' if RUBY_PLATFORM =~ /darwin/ 101 | File.open('Makefile.new', 'w') do |o| 102 | o.puts 'hijack: all strip' 103 | o.puts 104 | o.write(File.read('Makefile')) 105 | o.puts 106 | o.puts 'strip: $(DLLIB)' 107 | o.puts "\t$(ECHO) Stripping $(DLLIB)" 108 | o.puts "\t$(Q) #{strip_tool} $(DLLIB)" 109 | end 110 | File.rename('Makefile.new', 'Makefile') 111 | end 112 | -------------------------------------------------------------------------------- /test/rcd_test/ext/mri/rcd_test_ext.c: -------------------------------------------------------------------------------- 1 | #include "rcd_test_ext.h" 2 | 3 | #ifndef __has_builtin 4 | #define __has_builtin(x) 0 5 | #endif 6 | 7 | VALUE rb_mRcdTest; 8 | 9 | static VALUE 10 | rcdt_isinf_eh(VALUE self, VALUE rb_float) { 11 | Check_Type(rb_float, T_FLOAT); 12 | 13 | return isinf(RFLOAT_VALUE(rb_float)) ? Qtrue : Qfalse; 14 | } 15 | 16 | static VALUE 17 | rcdt_isnan_eh(VALUE self, VALUE rb_float) { 18 | Check_Type(rb_float, T_FLOAT); 19 | 20 | return isnan(RFLOAT_VALUE(rb_float)) ? Qtrue : Qfalse; 21 | } 22 | 23 | static VALUE 24 | rcdt_do_something(VALUE self) 25 | { 26 | return rb_str_new_cstr("something has been done"); 27 | } 28 | 29 | static VALUE 30 | rcdt_darwin_builtin_available_eh(VALUE self) 31 | { 32 | #if __has_builtin(__builtin_available) 33 | // This version must be higher than MACOSX_DEPLOYMENT_TARGET to prevent clang from optimizing it away 34 | if (__builtin_available(macOS 10.14, *)) { 35 | return Qtrue; 36 | } 37 | return Qfalse; 38 | #else 39 | rb_raise(rb_eRuntimeError, "__builtin_available is not defined"); 40 | #endif 41 | } 42 | 43 | static VALUE 44 | rcdt_largefile_op_removed_from_musl(VALUE self) 45 | { 46 | // Reference a symbol that was removed in Musl 1.24 🙄 47 | // https://github.com/sparklemotion/sqlite3-ruby/issues/434 48 | #ifdef __linux__ 49 | posix_fallocate(STDERR_FILENO, 0, 0); 50 | return Qtrue; 51 | #else 52 | return Qfalse; 53 | #endif 54 | } 55 | 56 | void 57 | Init_rcd_test_ext(void) 58 | { 59 | rb_mRcdTest = rb_define_module("RcdTest"); 60 | rb_define_singleton_method(rb_mRcdTest, "do_something", rcdt_do_something, 0); 61 | rb_define_singleton_method(rb_mRcdTest, "darwin_builtin_available?", rcdt_darwin_builtin_available_eh, 0); 62 | rb_define_singleton_method(rb_mRcdTest, "isinf?", rcdt_isinf_eh, 1); 63 | rb_define_singleton_method(rb_mRcdTest, "isnan?", rcdt_isnan_eh, 1); 64 | rb_define_singleton_method(rb_mRcdTest, "largefile_op_removed_from_musl", rcdt_largefile_op_removed_from_musl, 0); 65 | } 66 | -------------------------------------------------------------------------------- /test/rcd_test/ext/mri/rcd_test_ext.h: -------------------------------------------------------------------------------- 1 | #ifndef RCD_TEST_EXT_H 2 | #define RCD_TEST_EXT_H 1 3 | 4 | // see rcdt_largefile_op_removed_from_musl 5 | #define _LARGEFILE_SOURCE 1 6 | #define _FILE_OFFSET_BITS 64 7 | #include 8 | 9 | #include "ruby.h" 10 | 11 | #endif /* RCD_TEST_EXT_H */ 12 | -------------------------------------------------------------------------------- /test/rcd_test/lib/rcd_test.rb: -------------------------------------------------------------------------------- 1 | begin 2 | RUBY_VERSION =~ /(\d+\.\d+)/ 3 | require "rcd_test/#{$1}/rcd_test_ext" 4 | rescue LoadError 5 | require 'rcd_test/rcd_test_ext' 6 | end 7 | -------------------------------------------------------------------------------- /test/rcd_test/rcd_test.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "rcd_test" 5 | spec.version = "1.0.0" 6 | spec.authors = ["Lars Kanis"] 7 | spec.email = ["lars@greiz-reinsdorf.de"] 8 | 9 | spec.summary = "C extension for testing rake-compiler-dock" 10 | spec.description = "This gem has no real use other than testing builds of binary gems." 11 | spec.homepage = "https://github.com/rake-compiler/rake-compiler-dock" 12 | spec.required_ruby_version = ">= 2.7.0" 13 | spec.license = "MIT" 14 | 15 | spec.files = [ 16 | "ext/java/RcdTestExtService.java", 17 | "ext/java/RubyRcdTest.java", 18 | "ext/mri/extconf.rb", 19 | "ext/mri/rcd_test_ext.c", 20 | "ext/mri/rcd_test_ext.h", 21 | "lib/rcd_test.rb", 22 | ] 23 | 24 | spec.bindir = "exe" 25 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 26 | spec.require_paths = ["lib"] 27 | spec.extensions = ["ext/mri/extconf.rb"] 28 | end 29 | -------------------------------------------------------------------------------- /test/rcd_test/test/test_basic.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest/autorun" 4 | require "rcd_test" 5 | 6 | class TestBasic < Minitest::Test 7 | 8 | def test_do_something 9 | assert_equal "something has been done", RcdTest.do_something 10 | end 11 | 12 | def test_check_darwin_compiler_rt_symbol_resolution 13 | skip("jruby should not run libc-specific tests") if RUBY_ENGINE == "jruby" 14 | 15 | if RUBY_PLATFORM.include?("darwin") 16 | assert(RcdTest.darwin_builtin_available?) 17 | else 18 | e = assert_raises(RuntimeError) { RcdTest.darwin_builtin_available? } 19 | assert_equal("__builtin_available is not defined", e.message) 20 | end 21 | end 22 | 23 | def test_floating_point_classification_macros 24 | skip("jruby should not run libc-specific tests") if RUBY_ENGINE == "jruby" 25 | 26 | refute(RcdTest.isinf?(42.0)) 27 | assert(RcdTest.isinf?(Float::INFINITY)) 28 | refute(RcdTest.isnan?(42.0)) 29 | assert(RcdTest.isnan?(Float::NAN)) 30 | end 31 | 32 | def test_largefile_op_removed_from_musl 33 | skip("jruby should not run libc-specific tests") if RUBY_ENGINE == "jruby" 34 | 35 | is_linux = RUBY_PLATFORM.include?("linux") 36 | assert_equal(is_linux, RcdTest.largefile_op_removed_from_musl) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/test_environment_variables.rb: -------------------------------------------------------------------------------- 1 | require 'rake_compiler_dock' 2 | require 'rbconfig' 3 | require 'test/unit' 4 | begin 5 | require 'test/unit/notify' 6 | rescue LoadError 7 | end 8 | 9 | class TestEnvironmentVariables 10 | module Common 11 | TEST_PLATFORM = ENV["TEST_PLATFORM"] || "x64-mingw-ucrt" 12 | IS_JRUBY = TEST_PLATFORM.to_s == "jruby" 13 | TEST_IMAGE_NAME = if IS_JRUBY 14 | RakeCompilerDock::Starter.container_image_name(rubyvm: "jruby") 15 | else 16 | RakeCompilerDock::Starter.container_image_name(platform: TEST_PLATFORM) 17 | end 18 | 19 | def rcd_env 20 | self.class.instance_variable_get("@rcd_env") || begin 21 | command = "env" 22 | output = %x(#{invocation(command)}) 23 | 24 | env = output.split("\n").each_with_object({}) do |line, hash| 25 | hash[Regexp.last_match(1)] = Regexp.last_match(2).chomp if line =~ /\A(\w+)=(.*)\z/ 26 | end 27 | 28 | self.class.instance_variable_set("@rcd_env", env) 29 | end 30 | end 31 | 32 | unless IS_JRUBY 33 | def test_RUBY_CC_VERSION 34 | df = File.read(File.expand_path("../../Dockerfile.mri.erb", __FILE__)) 35 | df =~ /^ENV RUBY_CC_VERSION=(.*)$/ 36 | assert_equal $1, rcd_env['RUBY_CC_VERSION'] 37 | 38 | assert_equal RakeCompilerDock.ruby_cc_version, rcd_env['RUBY_CC_VERSION'] 39 | end 40 | 41 | def test_RAKE_EXTENSION_TASK_NO_NATIVE 42 | assert_equal "true", rcd_env['RAKE_EXTENSION_TASK_NO_NATIVE'] 43 | end 44 | 45 | def test_symlink_rake_compiler 46 | cmd = invocation("if test -h $HOME/.rake-compiler ; then echo yes ; else echo no ; fi") 47 | assert_equal("yes", %x(#{cmd}).strip) 48 | end 49 | 50 | def test_gem_directory 51 | cmd = invocation("if test -d $HOME/.gem ; then echo yes ; else echo no ; fi") 52 | assert_equal("yes", %x(#{cmd}).strip) 53 | end 54 | end 55 | end 56 | 57 | class UsingWrapper < Test::Unit::TestCase 58 | include Common 59 | 60 | def invocation(command) 61 | idir = File.join(File.dirname(__FILE__), '../lib') 62 | "RCD_PLATFORM=#{TEST_PLATFORM} RCD_RUBYVM=#{IS_JRUBY ? 'jruby' : 'mri'} #{RbConfig::CONFIG['RUBY_INSTALL_NAME']} -I#{idir.inspect} bin/rake-compiler-dock bash -c '#{command}'" 63 | end 64 | 65 | def test_HOST_RUBY_PLATFORM 66 | assert_equal RUBY_PLATFORM, rcd_env['RCD_HOST_RUBY_PLATFORM'] 67 | end 68 | 69 | def test_HOST_RUBY_VERSION 70 | assert_equal RUBY_VERSION, rcd_env['RCD_HOST_RUBY_VERSION'] 71 | end 72 | 73 | def test_IMAGE 74 | assert_equal TEST_IMAGE_NAME, rcd_env['RCD_IMAGE'] 75 | end 76 | 77 | def test_PWD 78 | assert_equal Dir.pwd, rcd_env['PWD'] 79 | end 80 | 81 | def test_SOURCE_DATE_EPOCH 82 | cmd = "SOURCE_DATE_EPOCH=1234567890 " + invocation("echo \$SOURCE_DATE_EPOCH") 83 | sde = %x(#{cmd}).strip 84 | assert_equal "1234567890", sde 85 | end 86 | end 87 | 88 | class AsIfContinuousIntegration < Test::Unit::TestCase 89 | include Common 90 | 91 | def invocation(command) 92 | "docker run --rm #{TEST_IMAGE_NAME} bash -c '#{command}'" 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /test/test_mig.rb: -------------------------------------------------------------------------------- 1 | require 'rake_compiler_dock' 2 | require 'test/unit' 3 | 4 | class TestMigCompile < Test::Unit::TestCase 5 | TEST_PLATFORM = ENV["TEST_PLATFORM"] || 'arm64-darwin' 6 | 7 | def test_mig_compile 8 | omit "only on darwin platform" unless TEST_PLATFORM =~ /darwin/ 9 | 10 | RakeCompilerDock::Starter.sh "mig -header tmp/mig_test_rpc.h -user tmp/mig_test_rpc.c -sheader /dev/null -server /dev/null -I. test/fixtures/mig_test_rpc.defs ", platform: TEST_PLATFORM, verbose: false 11 | 12 | h_file = File.read("tmp/mig_test_rpc.h") 13 | assert_match /Request_mig_test_call/, h_file 14 | 15 | c_file = File.read("tmp/mig_test_rpc.c") 16 | assert_match /Reply__mig_test_call/, c_file 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/test_parallel_docker_build.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require_relative "../build/parallel_docker_build" 3 | require "tmpdir" 4 | 5 | class TestParallelDockerBuild < Test::Unit::TestCase 6 | def setup 7 | @tmpdir ||= Dir.mktmpdir 8 | 9 | Dir.chdir(@tmpdir) do 10 | File.write "File0", <<-EOT 11 | FROM a 12 | RUN a 13 | RUN d 14 | EOT 15 | File.write "File1", <<-EOT 16 | FROM a 17 | RUN a 18 | RUN d 19 | RUN f \\ 20 | g 21 | EOT 22 | File.write "File2", <<-EOT 23 | FROM a 24 | RUN b 25 | RUN c 26 | RUN d 27 | EOT 28 | File.write "File3", <<-EOT 29 | FROM a 30 | RUN b 31 | RUN c 32 | RUN d 33 | EOT 34 | end 35 | end 36 | 37 | def teardown 38 | FileUtils.rm_rf @tmpdir 39 | end 40 | 41 | private def hd(str) 42 | "y" + Digest::SHA1.hexdigest(str) 43 | end 44 | 45 | def test_tasks 46 | Dir.chdir(@tmpdir) do 47 | RakeCompilerDock::ParallelDockerBuild.new(%w[ File0 File1 File2 File3 ], task_prefix: "y") 48 | end 49 | 50 | assert_operator Rake::Task["File0"].prerequisites, :include?, hd("File0File1") 51 | assert_operator Rake::Task["File1"].prerequisites, :include?, hd("File1") 52 | assert_operator Rake::Task[hd "File1"].prerequisites, :include?, hd("File0File1") 53 | assert_operator Rake::Task[hd "File0File1"].prerequisites, :include?, hd("File0File1File2File3") 54 | 55 | assert_operator Rake::Task["File2"].prerequisites, :include?, hd("File2File3") 56 | assert_operator Rake::Task["File3"].prerequisites, :include?, hd("File2File3") 57 | assert_operator Rake::Task[hd "File2File3"].prerequisites, :include?, hd("File0File1File2File3") 58 | end 59 | 60 | def test_common_files 61 | Dir.chdir(@tmpdir) do 62 | RakeCompilerDock::ParallelDockerBuild.new(%w[ File0 File1 File2 File3 ], task_prefix: "y") 63 | end 64 | 65 | assert_equal "FROM a\nRUN a\nRUN d\nRUN f \\\ng\n", read_df(hd "File1") 66 | assert_equal "FROM a\nRUN a\nRUN d\n", read_df(hd "File0File1") 67 | assert_equal "FROM a\nRUN b\nRUN c\nRUN d\n", read_df(hd "File2File3") 68 | assert_equal "FROM a\n", read_df(hd "File0File1File2File3") 69 | end 70 | 71 | def read_df(fn) 72 | File.read(File.join(@tmpdir, "/tmp/docker", fn)).each_line.map(&:lstrip).join 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /test/test_starter.rb: -------------------------------------------------------------------------------- 1 | require 'rake_compiler_dock' 2 | require 'test/unit' 3 | begin 4 | require 'test/unit/notify' 5 | rescue LoadError 6 | end 7 | 8 | class TestStarter < Test::Unit::TestCase 9 | include RakeCompilerDock 10 | 11 | def test_make_valid_user_name 12 | assert_equal "mouse-click", Starter.make_valid_user_name("Mouse-Click") 13 | assert_equal "very_very_very_l-ame_with_spaces", Starter.make_valid_user_name("Very very very long name with spaces") 14 | assert_equal "_halt", Starter.make_valid_user_name("halt") 15 | assert_equal "_rubyuser", Starter.make_valid_user_name("rubyuser") 16 | assert_equal "staff", Starter.make_valid_user_name("staff") 17 | assert_equal "a", Starter.make_valid_user_name("a") 18 | assert_equal "_", Starter.make_valid_user_name("") 19 | assert_equal "_", Starter.make_valid_user_name(nil) 20 | end 21 | 22 | def test_make_valid_group_name 23 | assert_equal "mouse-click", Starter.make_valid_group_name("Mouse-Click") 24 | assert_equal "very_very_very_l-ame_with_spaces", Starter.make_valid_group_name("Very very very long name with spaces") 25 | assert_equal "halt", Starter.make_valid_group_name("halt") 26 | assert_equal "_rubyuser", Starter.make_valid_group_name("rubyuser") 27 | assert_equal "_staff", Starter.make_valid_group_name("staff") 28 | assert_equal "a", Starter.make_valid_group_name("a") 29 | assert_equal "_", Starter.make_valid_group_name("") 30 | assert_equal "_", Starter.make_valid_group_name(nil) 31 | end 32 | 33 | def test_container_image_name 34 | # with env vars 35 | with_env({"RCD_IMAGE" => "env-var-value"}) do 36 | assert_equal("env-var-value", Starter.container_image_name) 37 | end 38 | with_env({"RAKE_COMPILER_DOCK_IMAGE" => "env-var-value"}) do 39 | assert_equal("env-var-value", Starter.container_image_name) 40 | end 41 | 42 | # with image option 43 | assert_equal("option-value", Starter.container_image_name({:image => "option-value"})) 44 | 45 | # with env var and image option, image option wins 46 | with_env({"RCD_IMAGE" => "env-var-value"}) do 47 | assert_equal("option-value", Starter.container_image_name({:image => "option-value"})) 48 | end 49 | 50 | # mri platform arg 51 | assert_equal( 52 | "ghcr.io/rake-compiler/rake-compiler-dock-image:#{IMAGE_VERSION}-mri-platform-option-value", 53 | Starter.container_image_name({:platform => "platform-option-value"}), 54 | ) 55 | 56 | # jruby rubyvm arg 57 | assert_equal( 58 | "ghcr.io/rake-compiler/rake-compiler-dock-image:#{IMAGE_VERSION}-jruby", 59 | Starter.container_image_name({:rubyvm => "jruby"}), 60 | ) 61 | 62 | # jruby platform arg 63 | assert_equal( 64 | "ghcr.io/rake-compiler/rake-compiler-dock-image:#{IMAGE_VERSION}-jruby", 65 | Starter.container_image_name({:platform => "jruby"}), 66 | ) 67 | 68 | # container registry env var 69 | with_env({"CONTAINER_REGISTRY" => "registry-value"}) do 70 | assert_equal( 71 | "registry-value/rake-compiler-dock-image:#{IMAGE_VERSION}-mri-x86_64-darwin", 72 | Starter.container_image_name({:platform => "x86_64-darwin"}), 73 | ) 74 | end 75 | 76 | # snapshots 77 | assert_equal( 78 | "ghcr.io/rake-compiler/rake-compiler-dock-image:snapshot-mri-x86_64-darwin", 79 | Starter.container_image_name({:platform =>"x86_64-darwin", :version => "snapshot"}), 80 | ) 81 | end 82 | 83 | def test_container_registry 84 | assert_equal("ghcr.io/rake-compiler", Starter.container_registry) 85 | 86 | with_env({"CONTAINER_REGISTRY" => "env-var-value"}) do 87 | assert_equal("env-var-value", Starter.container_registry) 88 | end 89 | end 90 | 91 | def test_container_rubyvm 92 | # no args 93 | assert_equal("mri", Starter.container_rubyvm) 94 | 95 | # with env var 96 | with_env({"RCD_RUBYVM" => "env-var-value"}) do 97 | assert_equal("env-var-value", Starter.container_rubyvm) 98 | end 99 | 100 | # with rubyvm option 101 | assert_equal("option-value", Starter.container_rubyvm({:rubyvm => "option-value"})) 102 | 103 | # with rubyvm option and env var, rubyvm option wins 104 | with_env({"RCD_RUBYVM" => "env-var-value"}) do 105 | assert_equal("option-value", Starter.container_rubyvm({:rubyvm => "option-value"})) 106 | end 107 | 108 | # with jruby platform option 109 | assert_equal("jruby", Starter.container_rubyvm({:platform => "jruby"})) 110 | end 111 | 112 | def test_container_jrubyvm? 113 | assert(Starter.container_jrubyvm?({:rubyvm => "jruby"})) 114 | assert(Starter.container_jrubyvm?({:platform => "jruby"})) 115 | refute(Starter.container_jrubyvm?({:rubyvm => "mri"})) 116 | refute(Starter.container_jrubyvm?({:platform => "x86_64-linux-gnu"})) 117 | end 118 | 119 | def test_platforms 120 | # no args 121 | assert_equal("x86-mingw32 x64-mingw32", Starter.platforms) 122 | 123 | # with env var 124 | with_env({"RCD_PLATFORM" => "env-var-value"}) do 125 | assert_equal("env-var-value", Starter.platforms) 126 | end 127 | 128 | # with platform option 129 | assert_equal("option-value", Starter.platforms({:platform => "option-value"})) 130 | 131 | # with platform option and env var, platform option wins 132 | with_env({"RCD_PLATFORM" => "arm64-darwin"}) do 133 | assert_equal("option-value", Starter.platforms({:platform => "option-value"})) 134 | end 135 | 136 | # when options rubyvm is set to jruby 137 | assert_equal("jruby", Starter.platforms({:rubyvm => "jruby"})) 138 | end 139 | 140 | def with_env(env = {}) 141 | original_env = {} 142 | env.each do |k, v| 143 | original_env[k] = ENV[k] 144 | ENV[k] = v 145 | end 146 | yield 147 | ensure 148 | original_env.each do |k, v| 149 | ENV[k] = v 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /test/test_versions.rb: -------------------------------------------------------------------------------- 1 | require 'rake_compiler_dock' 2 | require 'rbconfig' 3 | require 'test/unit' 4 | 5 | class TestVersions < Test::Unit::TestCase 6 | def test_cross_rubies 7 | cross = RakeCompilerDock.cross_rubies 8 | assert_operator(cross, :is_a?, Hash) 9 | cross.each do |minor, patch| 10 | assert_match(/^\d+\.\d+$/, minor) 11 | assert_match(/^\d+\.\d+\.\d+$/, patch) 12 | end 13 | end 14 | 15 | def test_ruby_cc_versions_no_args 16 | cross = RakeCompilerDock.cross_rubies 17 | expected = cross.values.sort.reverse.join(":") 18 | 19 | assert_equal(expected, RakeCompilerDock.ruby_cc_version) 20 | end 21 | 22 | def test_ruby_cc_versions_strings 23 | cross = RakeCompilerDock.cross_rubies 24 | 25 | expected = cross["3.4"] 26 | assert_equal(expected, RakeCompilerDock.ruby_cc_version("3.4")) 27 | 28 | expected = [cross["3.4"], cross["3.2"]].join(":") 29 | assert_equal(expected, RakeCompilerDock.ruby_cc_version("3.4", "3.2")) 30 | 31 | expected = [cross["3.4"], cross["3.2"]].join(":") 32 | assert_equal(expected, RakeCompilerDock.ruby_cc_version("3.2", "3.4")) 33 | 34 | assert_raises do 35 | RakeCompilerDock.ruby_cc_version("9.8") 36 | end 37 | 38 | assert_raises do 39 | RakeCompilerDock.ruby_cc_version("foo") 40 | end 41 | end 42 | 43 | def test_ruby_cc_versions_requirements 44 | cross = RakeCompilerDock.cross_rubies 45 | 46 | expected = cross["3.4"] 47 | assert_equal(expected, RakeCompilerDock.ruby_cc_version("~> 3.4")) 48 | assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.4"))) 49 | 50 | expected = [cross["3.4"], cross["3.3"], cross["3.2"]].join(":") 51 | assert_equal(expected, RakeCompilerDock.ruby_cc_version("~> 3.2")) 52 | assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.2"))) 53 | 54 | expected = [cross["3.4"], cross["3.2"]].join(":") 55 | assert_equal(expected, RakeCompilerDock.ruby_cc_version("~> 3.2.0", "~> 3.4.0")) 56 | assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.2.0"), Gem::Requirement.new("~> 3.4.0"))) 57 | 58 | expected = [cross["3.4"], cross["3.3"], cross["3.2"]].join(":") 59 | assert_equal(expected, RakeCompilerDock.ruby_cc_version(">= 3.2")) 60 | assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new(">= 3.2"))) 61 | 62 | assert_raises do 63 | RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("> 9.8")) 64 | end 65 | end 66 | 67 | def test_set_ruby_cc_versions 68 | original_ruby_cc_versions = ENV["RUBY_CC_VERSION"] 69 | cross = RakeCompilerDock.cross_rubies 70 | 71 | RakeCompilerDock.set_ruby_cc_version(Gem::Requirement.new("~> 3.2.0"), Gem::Requirement.new("~> 3.4.0")) 72 | assert_equal([cross["3.4"], cross["3.2"]].join(":"), ENV["RUBY_CC_VERSION"]) 73 | 74 | RakeCompilerDock.set_ruby_cc_version("~> 3.2.0", "~> 3.4.0") 75 | assert_equal([cross["3.4"], cross["3.2"]].join(":"), ENV["RUBY_CC_VERSION"]) 76 | 77 | RakeCompilerDock.set_ruby_cc_version("~> 3.1") 78 | assert_equal([cross["3.4"], cross["3.3"], cross["3.2"], cross["3.1"]].join(":"), ENV["RUBY_CC_VERSION"]) 79 | ensure 80 | ENV["RUBY_CC_VERSION"] = original_ruby_cc_versions 81 | end 82 | end 83 | --------------------------------------------------------------------------------