Skip to content

Commit 44fe66e

Browse files
committed
Allow to ship the JSON gem with precompiled binaries
TL;DR I'd like to propose releasing the json gem with precompiled binaries built for different platforms and different ABI version (fat gem). I'm currently working on a tool to help the Ruby community ship gems with precompiled binaries with the intent to make `bundle install` much faster for everyone. The main bottleneck when installing gems in a project is the compilation of native extensions. [cibuildgem](https://github.com/Shopify/cibuildgem) modestly tries to follow the same approach as what the python community did with [cibuildwheel](https://cibuildwheel.pypa.io/en/stable/). It works with a native compilation using CI runners (GitHub it the only supported vendor for now) and tries to be as easy to setup as possible. The json already relies on Rake Compiler for development purposes, and because cibuildgem piggyback on top of Rake Compiler, there is no extra configuration required. The CI workflow in this commit was generated with the cibuildgem CLI which reads the gemspec and determine what ruby versions needs to be compiled and tested agains. The tool is very new and I did many tests internally to make sure that it create binaries that can be ported to other environment. For instance, I used it to precompile almost all gems that a new Rails application depends on and pushed them under a "namespaced" name on my [RubyGems](https://rubygems.org/profiles/edouardchin), I then confirmed that the rails application was bootable using all those gems (I'm on MacOS). I was hoping I could use the json gem and get the feedback of its maintainers in order to continue the development of cibuildgem and improve it. - On GitHub, the earliest glibc version we can use is 2.35 (using Ubuntu 22). This means that any users that are on a linux distro that comes with a glibc version lower than this will not be able to install the gem with precompiled binaries. They'll have to install the "normal" gem and specify it in their gemfile (`gem 'json', platform: 'ruby'`). - This workflow gets triggered manually through the GitHub action page when you are ready to cut a release. You can optionally decide to let the tool do the release on RubyGems (using a chechbox on the GitHub UI). If you'd prefer doing it from your machine, you can download the gems from the GitHub action artifacts.
1 parent 9c36681 commit 44fe66e

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

.github/workflows/cibuildgem.yaml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: "Package and release gems with precompiled binaries"
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
release:
6+
description: "If the whole build passes on all platforms, release the gems on RubyGems.org"
7+
required: false
8+
type: boolean
9+
default: false
10+
11+
jobs:
12+
compile:
13+
timeout-minutes: 20
14+
name: "Cross compile the gem on different ruby versions"
15+
strategy:
16+
matrix:
17+
os: ["macos-latest", "ubuntu-22.04"]
18+
runs-on: "${{ matrix.os }}"
19+
steps:
20+
- name: "Checkout code"
21+
uses: "actions/checkout@v5"
22+
- name: "Setup Ruby"
23+
uses: "ruby/setup-ruby@v1"
24+
with:
25+
ruby-version: "3.1.7"
26+
bundler-cache: true
27+
- name: "Run cibuildgem"
28+
uses: "shopify/cibuildgem/.github/actions/cibuildgem@main"
29+
with:
30+
step: "compile"
31+
test:
32+
timeout-minutes: 20
33+
name: "Run the test suite"
34+
needs: compile
35+
strategy:
36+
matrix:
37+
os: ["macos-latest", "ubuntu-22.04"]
38+
rubies: ["3.1", "3.2", "3.3", "3.4"]
39+
type: ["cross", "native"]
40+
runs-on: "${{ matrix.os }}"
41+
steps:
42+
- name: "Checkout code"
43+
uses: "actions/checkout@v5"
44+
- name: "Setup Ruby"
45+
uses: "ruby/setup-ruby@v1"
46+
with:
47+
ruby-version: "${{ matrix.rubies }}"
48+
bundler-cache: true
49+
- name: "Run cibuildgem"
50+
uses: "shopify/cibuildgem/.github/actions/cibuildgem@main"
51+
with:
52+
step: "test_${{ matrix.type }}"
53+
install:
54+
timeout-minutes: 5
55+
name: "Verify the gem can be installed"
56+
needs: test
57+
strategy:
58+
matrix:
59+
os: ["macos-latest", "ubuntu-22.04"]
60+
runs-on: "${{ matrix.os }}"
61+
steps:
62+
- name: "Setup Ruby"
63+
uses: "ruby/setup-ruby@v1"
64+
with:
65+
ruby-version: "3.4.7"
66+
- name: "Run cibuildgem"
67+
uses: "shopify/cibuildgem/.github/actions/cibuildgem@main"
68+
with:
69+
step: "install"
70+
release:
71+
permissions:
72+
id-token: write
73+
contents: read
74+
timeout-minutes: 5
75+
if: ${{ inputs.release }}
76+
name: "Release all gems with RubyGems"
77+
needs: install
78+
runs-on: "ubuntu-latest"
79+
steps:
80+
- name: "Setup Ruby"
81+
uses: "ruby/setup-ruby@v1"
82+
with:
83+
ruby-version: "3.4.7"
84+
- name: "Run cibuildgem"
85+
uses: "shopify/cibuildgem/.github/actions/cibuildgem@main"
86+
with:
87+
step: "release"

lib/json/ext.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,26 @@ def parse
2828
end
2929
end
3030

31-
require 'json/ext/parser'
31+
ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
32+
33+
begin
34+
require "json/ext/#{ruby_version}/parser"
35+
rescue LoadError
36+
require "json/ext/parser"
37+
end
38+
3239
Ext::Parser::Config = Ext::ParserConfig
3340
JSON.parser = Ext::Parser
3441

3542
if RUBY_ENGINE == 'truffleruby'
3643
require 'json/truffle_ruby/generator'
3744
JSON.generator = JSON::TruffleRuby::Generator
3845
else
39-
require 'json/ext/generator'
46+
begin
47+
require "json/ext/#{ruby_version}/generator"
48+
rescue LoadError
49+
require "json/ext/generator"
50+
end
4051
JSON.generator = Generator
4152
end
4253
end

0 commit comments

Comments
 (0)