rcee_precompiled 0.4.0-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a2db5b17deb67ff771b0b171e94b3f031811d73753648649fcc185c7e5d7b4b1
4
+ data.tar.gz: d8dc00757d9853baecacf8dee928b45e859ced406fd76f57217392d82c48f31d
5
+ SHA512:
6
+ metadata.gz: 949862cf2477c6241730793cde083ad7505ece30714721d5b5f138f5d1e37ac6d8d52f40aa4587a88633181cef63978591bce0d04c983dfbcea9e8169a8c0642
7
+ data.tar.gz: 4c4ebbb8f4a6cee4980c2cd0343cf5b995e63af0e81375884af42e7d04fc25be3b3e97ec843e3dd59bb3076b51762cc48e3baa0f81aa6f4cd4b84ec43c5e004a
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+ ports
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in precompiled.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rake-compiler"
11
+ gem "rake-compiler-dock", "~> 1.2.1"
12
+
13
+ gem "minitest", "~> 5.0"
data/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # RCEE::Precompiled
2
+
3
+ This gem is part of the Ruby C Extensions Explained project at https://github.com/flavorjones/ruby-c-extensions-explained
4
+
5
+ ## Summary
6
+
7
+ Installation time is an important aspect of developer happiness, and the Packaged strategies can make your users very unhappy.
8
+
9
+ One way to speed up installation time is to precompile the extension, so that during `gem install` files are simply being unpacked, and the entire configure/compile/link process is avoided completely. This can be tricky to set up, and requires that you test across all the target platforms; but makes for much happier users.
10
+
11
+ Note, however, that it's necessary to _also_ ship the vanilla (platform=ruby) gem as a fallback for platforms you haven't pre-compiled for.
12
+
13
+ In the nine months since Nokogiri v1.11 started shipping precompiled native gems, we've seen over 45 million gem installations, and essentially zero support issues have been opened. Twitter complaints dropped to near zero. Page views of the Nokogiri installation tutorial dropped by half.
14
+
15
+ Precompiled Nokogiri gems have been an unmitigated success and have made both users and maintainers happy.
16
+
17
+
18
+ ## Details
19
+
20
+ An important tool we rely on for precompiling is [`rake-compiler/rake-compiler-dock`](https://github.com/rake-compiler/rake-compiler-dock), maintained by Lars Kanis. `rake-compiler-dock` is a Docker-based build environment that uses `rake-compiler` to _cross compile_ for all of these major platforms.
21
+
22
+ What this means is, I can run my normal gem build process in a docker container on my Linux machine, and it will build gems that I run on Windows and MacOS.
23
+
24
+ This is really powerful stuff, and once we assume that we can cross-compile reliably, the remaining problems boil down to modifying how we build the gemfile and making sure we test gems adequately on the target platforms.
25
+
26
+ First, we need to add some features to our `Rake::ExtensionTask` in `Rakefile`:
27
+
28
+ ``` ruby
29
+ cross_rubies = ["3.1.0", "3.0.0", "2.7.0", "2.6.0"]
30
+ cross_platforms = [
31
+ "x64-mingw32",
32
+ "x64-mingw-ucrt",
33
+ "x86-linux",
34
+ "x86_64-linux",
35
+ "aarch64-linux",
36
+ "x86_64-darwin",
37
+ "arm64-darwin",
38
+ ]
39
+ ENV["RUBY_CC_VERSION"] = cross_rubies.join(":")
40
+
41
+ Rake::ExtensionTask.new("precompiled", rcee_precompiled_spec) do |ext|
42
+ ext.lib_dir = "lib/rcee/precompiled"
43
+ ext.cross_compile = true
44
+ ext.cross_platform = cross_platforms
45
+ ext.cross_config_options << "--enable-cross-build" # so extconf.rb knows we're cross-compiling
46
+ ext.cross_compiling do |spec|
47
+ # remove things not needed for precompiled gems
48
+ spec.dependencies.reject! { |dep| dep.name == "mini_portile2" }
49
+ spec.files.reject! { |file| File.fnmatch?("*.tar.gz", file) }
50
+ end
51
+ end
52
+ ```
53
+
54
+ This does the following:
55
+
56
+ - set up some local variables to indicate what ruby versions and which platforms we will build for
57
+ - set an environment variable to let rake-compiler know which ruby versions we'll build
58
+ - tell the extension task to turn on cross-compiling features, including additional rake tasks
59
+ - signal to our `extconf.rb` when we're cross-compiling (in case its behavior needs to change)
60
+ - finally, in a block that is only run when cross-compiling, we modify the gemspec to remove things we don't need in native gems:
61
+ - the tarball
62
+ - the dependency on `mini_portile`
63
+
64
+ Next we need some new rake tasks:
65
+
66
+ ``` ruby
67
+ namespace "gem" do
68
+ cross_platforms.each do |platform|
69
+ desc "build native gem for #{platform}"
70
+ task platform do
71
+ RakeCompilerDock.sh(<<~EOF, platform: platform)
72
+ gem install bundler --no-document &&
73
+ bundle &&
74
+ bundle exec rake gem:#{platform}:buildit
75
+ EOF
76
+ end
77
+
78
+ namespace platform do
79
+ # this runs in the rake-compiler-dock docker container
80
+ task "buildit" do
81
+ # use Task#invoke because the pkg/*gem task is defined at runtime
82
+ Rake::Task["native:#{platform}"].invoke
83
+ Rake::Task["pkg/#{rcee_precompiled_spec.full_name}-#{Gem::Platform.new(platform)}.gem"].invoke
84
+ end
85
+ end
86
+ end
87
+ end
88
+ ```
89
+
90
+ The top task (or, really, _set_ of tasks) runs on the host system, and invokes the lower task within the appropriate docker container to cross-compile for that platform. So the bottom task is doing most of the work, and it's doing it inside a guest container. Please note that the worker is both building the extension *and* packaging the gem according to the gemspec that was just modified by our extensiontask cross-compiling block.
91
+
92
+ Changes to the `extconf.rb`:
93
+
94
+ ``` ruby
95
+ ENV["CC"] = RbConfig::CONFIG["CC"]
96
+ ```
97
+
98
+ This makes sure that the cross-compiler is the compiler used within the guest container (and not the native linux compiler).
99
+
100
+ ``` ruby
101
+ cross_build_p = enable_config("cross-build")
102
+ ```
103
+
104
+ The cross-compile rake task signals to `extconf.rb` that it's cross-compiling by using a commandline flag that we can inspect. We'll need this for `libyaml` to make sure that set the appropriate flags during precompilation (flags which shouldn't be set when compiling natively).
105
+
106
+ ``` ruby
107
+ MiniPortile.new("yaml", "0.2.5").tap do |recipe|
108
+ # ...
109
+ # configure the environment that MiniPortile will use for subshells
110
+ ENV.to_h.dup.tap do |env|
111
+ # -fPIC is necessary for linking into a shared library
112
+ env["CFLAGS"] = [env["CFLAGS"], "-fPIC"].join(" ")
113
+ env["SUBDIRS"] = "include src" # libyaml: skip tests
114
+
115
+ recipe.configure_options += env.map { |key, value| "#{key}=#{value.strip}" }
116
+ end
117
+ # ...
118
+ end
119
+ ```
120
+
121
+ The rest of the extconf changes are related to configuring libyaml at build time. We need to set the -fPIC option so we can mix static and shared libraries together. (This should probably always be set.)
122
+
123
+ The "SUBDIRS" environment variable is something that's very specific to libyaml, though: it tells libyaml's autoconf build system to skip running the tests. We have to do this because although we can *generate* binaries for other platforms, we can't actually *run* them.
124
+
125
+ We have one more small change we'll need to make to how the extension is required. Let's take a look at the directory structure in the packaged gem:
126
+
127
+ ``` text
128
+ lib
129
+ └── rcee
130
+ ├── precompiled
131
+ │   ├── 2.6
132
+ │   │   └── precompiled.so
133
+ │   ├── 2.7
134
+ │   │   └── precompiled.so
135
+ │   ├── 3.0
136
+ │   │   └── precompiled.so
137
+ │   ├── 3.1
138
+ │   │   └── precompiled.so
139
+ │   └── version.rb
140
+ └── precompiled.rb
141
+ ```
142
+
143
+ You can see that we have FOUR c extensions in this gem, one for each minor version of Ruby that we support. Remember that a C extension is specific to an architecture and a version of Ruby. For example, if we're running Ruby 3.0.1, then we need to load the extension in the 3.0 directory. Let's make sure we do that.
144
+
145
+ In `lib/rcee/precompiled.rb`, we'll replace the normal `require` with:
146
+
147
+ ``` ruby
148
+ begin
149
+ # load the precompiled extension file
150
+ ruby_version = /(\d+\.\d+)/.match(::RUBY_VERSION)
151
+ require_relative "precompiled/#{ruby_version}/precompiled"
152
+ rescue LoadError
153
+ # fall back to the extension compiled upon installation.
154
+ require "rcee/precompiled/precompiled"
155
+ end
156
+ ```
157
+
158
+ Go ahead and try it! `gem install rcee_precompiled`. If you're on windows, linux, or macos you should get a precompiled version that installs in under a second. Everyone else (hello FreeBSD people!) it'll take a few more seconds to build the vanilla gem's packaged tarball.
159
+
160
+
161
+ ## Testing
162
+
163
+ See [.github/workflows/precompiled.yml](../.github/workflows/precompiled.yml)
164
+
165
+ Key things to note:
166
+
167
+ - matrix across all supported Rubies and platforms (for compile-from-source installation testing)
168
+ - test native gems for a variety of platforms
169
+ - use rake-compiler-dock images to build the gems
170
+ - then install on native platforms and verify that it passes tests
171
+
172
+ Note that there's additional complexity because of how we test:
173
+
174
+ - see new script bin/test-gem-build which artificially bumps the VERSION string to double-check we're testing the packaged version of the gem (which the tests output)
175
+ - see new script bin/test-gem-install which installs the gem, deletes the local source code, and runs the tests against the installed gem
176
+ - the gemspec handles a missing version file (because we delete the local source code during testing)
177
+
178
+
179
+ ## What Can Go Wrong
180
+
181
+ This strategy isn't perfect. Remember what I said earlier, that a compiled C extension is specific to
182
+
183
+ - the minor version of ruby (e.g., 3.0)
184
+ - the machine architecture (e.g., x86_64)
185
+ - the system libraries
186
+
187
+ The precompiled strategy mostly takes care of the first two, but there are still edge cases for system libraries. The big gotcha is that linux libc is not the same as linux musl, and we've had to work around this a few times in Nokogiri.
188
+
189
+ I'm positive that there are more edge cases that will be found as users add more platforms and as more gems start precompiling. I'm willing to bet money that you can break this by setting some Ruby compile-time flags on your system. I'm honestly surprised it works as well as it has. (Worth noting: the `sassc` gem stopped shipping native gems for linux because of the musl incompatibilities.)
190
+
191
+ So the lesson here is: make sure you have an automated test pipeline that will build a gem and test it on the target platform! This takes time to set up, but it will save you time and effort in the long run.
data/Rakefile ADDED
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubygems/package_task"
5
+ require "rake/testtask"
6
+ require "rake/extensiontask"
7
+ require "rake_compiler_dock"
8
+
9
+ cross_rubies = ["3.1.0", "3.0.0", "2.7.0", "2.6.0"]
10
+ cross_platforms = [
11
+ "aarch64-linux",
12
+ "arm-linux",
13
+ "arm64-darwin",
14
+ "x64-mingw-ucrt",
15
+ "x64-mingw32",
16
+ "x86-linux",
17
+ "x86_64-darwin",
18
+ "x86_64-linux",
19
+ ]
20
+ ENV["RUBY_CC_VERSION"] = cross_rubies.join(":")
21
+
22
+ rcee_precompiled_spec = Bundler.load_gemspec("rcee_precompiled.gemspec")
23
+ Gem::PackageTask.new(rcee_precompiled_spec).define #packaged_tarball version of the gem for platform=ruby
24
+ task "package" => cross_platforms.map { |p| "gem:#{p}" } # "package" task for all the native platforms
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << "test"
28
+ t.libs << "lib"
29
+ t.test_files = FileList["test/**/*_test.rb"]
30
+ end
31
+
32
+ Rake::ExtensionTask.new("precompiled", rcee_precompiled_spec) do |ext|
33
+ ext.lib_dir = "lib/rcee/precompiled"
34
+ ext.cross_compile = true
35
+ ext.cross_platform = cross_platforms
36
+ ext.cross_config_options << "--enable-cross-build" # so extconf.rb knows we're cross-compiling
37
+ ext.cross_compiling do |spec|
38
+ # remove things not needed for precompiled gems
39
+ spec.dependencies.reject! { |dep| dep.name == "mini_portile2" }
40
+ spec.files.reject! { |file| File.fnmatch?("*.tar.gz", file) }
41
+ end
42
+ end
43
+
44
+ namespace "gem" do
45
+ cross_platforms.each do |platform|
46
+ desc "build native gem for #{platform}"
47
+ task platform do
48
+ RakeCompilerDock.sh(<<~EOF, platform: platform)
49
+ gem install bundler --no-document &&
50
+ bundle &&
51
+ bundle exec rake gem:#{platform}:buildit
52
+ EOF
53
+ end
54
+
55
+ namespace platform do
56
+ # this runs in the rake-compiler-dock docker container
57
+ task "buildit" do
58
+ # use Task#invoke because the pkg/*gem task is defined at runtime
59
+ Rake::Task["native:#{platform}"].invoke
60
+ Rake::Task["pkg/#{rcee_precompiled_spec.full_name}-#{Gem::Platform.new(platform)}.gem"].invoke
61
+ end
62
+ end
63
+ end
64
+
65
+ desc "build native gem for all platforms"
66
+ multitask "all" => [cross_platforms, "gem"].flatten
67
+ end
68
+
69
+ desc "Temporarily set VERSION to a unique timestamp"
70
+ task "set-version-to-timestamp" do
71
+ # this task is used by bin/test-gem-build
72
+ # to test building, packaging, and installing a precompiled gem
73
+ version_constant_re = /^\s*VERSION\s*=\s*["'](.*)["']$/
74
+
75
+ version_file_path = File.join(__dir__, "lib/rcee/precompiled/version.rb")
76
+ version_file_contents = File.read(version_file_path)
77
+
78
+ current_version_string = version_constant_re.match(version_file_contents)[1]
79
+ current_version = Gem::Version.new(current_version_string)
80
+
81
+ fake_version = Gem::Version.new(format("%s.test.%s", current_version.bump, Time.now.strftime("%Y.%m%d.%H%M")))
82
+
83
+ unless version_file_contents.gsub!(version_constant_re, " VERSION = \"#{fake_version}\"")
84
+ raise("Could not hack the VERSION constant")
85
+ end
86
+
87
+ File.open(version_file_path, "w") { |f| f.write(version_file_contents) }
88
+
89
+ puts "NOTE: wrote version as \"#{fake_version}\""
90
+ end
91
+
92
+ task default: [:clobber, :compile, :test]
93
+
94
+ CLEAN.add("{ext,lib}/**/*.{o,so}", "pkg")
95
+ CLOBBER.add("ports")
96
+
97
+ # when packaging the gem, if the tarball isn't cached, we need to fetch it. the easiest thing to do
98
+ # is to run the compile phase to invoke the extconf and have mini_portile download the file for us.
99
+ # this is wasteful and in the future I would prefer to separate mini_portile from the extconf to
100
+ # allow us to download without compiling.
101
+ Rake::Task["package"].prerequisites.prepend("compile")
@@ -0,0 +1,41 @@
1
+ require "mkmf"
2
+ require "mini_portile2"
3
+
4
+ package_root_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
5
+
6
+ RbConfig::CONFIG["CC"] = RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"] if ENV["CC"]
7
+ ENV["CC"] = RbConfig::CONFIG["CC"]
8
+
9
+ cross_build_p = enable_config("cross-build")
10
+
11
+ MiniPortile.new("yaml", "0.2.5").tap do |recipe|
12
+ recipe.files = [{
13
+ url: "https://github.com/yaml/libyaml/releases/download/0.2.5/yaml-0.2.5.tar.gz",
14
+ sha256: "c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4",
15
+ }]
16
+ recipe.target = File.join(package_root_dir, "ports")
17
+
18
+ # configure the environment that MiniPortile will use for subshells
19
+ if cross_build_p
20
+ ENV.to_h.tap do |env|
21
+ # -fPIC is necessary for linking into a shared library
22
+ env["CFLAGS"] = [env["CFLAGS"], "-fPIC"].join(" ")
23
+ env["SUBDIRS"] = "include src" # libyaml: skip tests
24
+
25
+ recipe.configure_options += env.map { |key, value| "#{key}=#{value.strip}" }
26
+ end
27
+ end
28
+
29
+ unless File.exist?(File.join(recipe.target, recipe.host, recipe.name, recipe.version))
30
+ recipe.cook
31
+ end
32
+
33
+ recipe.activate
34
+ pkg_config(File.join(recipe.path, "lib", "pkgconfig", "yaml-0.1.pc"))
35
+ end
36
+
37
+ unless have_library("yaml", "yaml_get_version", "yaml.h")
38
+ abort("\nERROR: *** could not find libyaml development environment ***\n\n")
39
+ end
40
+
41
+ create_makefile("rcee/precompiled/precompiled")
@@ -0,0 +1,25 @@
1
+ #include "precompiled.h"
2
+
3
+ VALUE rb_mRCEE;
4
+ VALUE rb_mPrecompiled;
5
+ VALUE rb_cPrecompiledExtension;
6
+
7
+ static VALUE
8
+ rb_precompiled_extension_class_do_something(VALUE self)
9
+ {
10
+ int major, minor, patch;
11
+
12
+ yaml_get_version(&major, &minor, &patch);
13
+
14
+ return rb_sprintf("libyaml version %d.%d.%d", major, minor, patch);
15
+ }
16
+
17
+ void
18
+ Init_precompiled(void)
19
+ {
20
+ rb_mRCEE = rb_define_module("RCEE");
21
+ rb_mPrecompiled = rb_define_module_under(rb_mRCEE, "Precompiled");
22
+ rb_cPrecompiledExtension = rb_define_class_under(rb_mPrecompiled, "Extension", rb_cObject);
23
+ rb_define_singleton_method(rb_cPrecompiledExtension, "do_something",
24
+ rb_precompiled_extension_class_do_something, 0);
25
+ }
@@ -0,0 +1,7 @@
1
+ #ifndef PRECOMPILED_H
2
+ #define PRECOMPILED_H 1
3
+
4
+ #include "ruby.h"
5
+ #include "yaml.h"
6
+
7
+ #endif /* PRECOMPILED_H */
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RCEE
4
+ module Precompiled
5
+ VERSION = "0.4.0"
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "precompiled/version"
4
+
5
+ begin
6
+ # load the precompiled extension file
7
+ ruby_version = /(\d+\.\d+)/.match(::RUBY_VERSION)
8
+ require_relative "precompiled/#{ruby_version}/precompiled"
9
+ rescue LoadError
10
+ # fall back to the extension compiled upon installation.
11
+ # use "require" instead of "require_relative" because non-native gems will place C extension files
12
+ # in Gem::BasicSpecification#extension_dir after compilation (during normal installation), which
13
+ # is in $LOAD_PATH but not necessarily relative to this file
14
+ # (see https://github.com/sparklemotion/nokogiri/issues/2300 for more)
15
+ require "rcee/precompiled/precompiled"
16
+ end
17
+
18
+ module RCEE
19
+ module Precompiled
20
+ class Error < StandardError; end
21
+ # Your code goes here...
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require_relative "lib/rcee/precompiled/version"
5
+ rescue LoadError
6
+ puts "WARNING: Could not load RCEE::Precompiled::VERSION"
7
+ end
8
+
9
+ Gem::Specification.new do |spec|
10
+ spec.name = "rcee_precompiled"
11
+ spec.version = defined?(RCEE::Precompiled::VERSION) ? RCEE::Precompiled::VERSION : "0.0.0"
12
+ spec.authors = ["Mike Dalessio"]
13
+ spec.email = ["mike.dalessio@gmail.com"]
14
+
15
+ spec.summary = "Example gem demonstrating a basic C extension."
16
+ spec.description = "Part of a project to explain how Ruby C extensions work."
17
+ spec.homepage = "https://github.com/flavorjones/ruby-c-extensions-explained"
18
+ spec.required_ruby_version = ">= 2.6.0"
19
+ spec.license = "MIT"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = [
24
+ ".gitignore",
25
+ "Gemfile",
26
+ "README.md",
27
+ "Rakefile",
28
+ "ext/precompiled/extconf.rb",
29
+ "ext/precompiled/precompiled.c",
30
+ "ext/precompiled/precompiled.h",
31
+ "lib/rcee/precompiled.rb",
32
+ "lib/rcee/precompiled/version.rb",
33
+ "ports/archives/yaml-0.2.5.tar.gz",
34
+ "rcee_precompiled.gemspec",
35
+ ]
36
+
37
+ spec.bindir = "exe"
38
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
39
+ spec.require_paths = ["lib"]
40
+ spec.extensions = ["ext/precompiled/extconf.rb"]
41
+
42
+ spec.add_dependency "mini_portile2"
43
+
44
+ # For more information and examples about making a new gem, checkout our
45
+ # guide at: https://bundler.io/guides/creating_gem.html
46
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rcee_precompiled
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: x86-linux
6
+ authors:
7
+ - Mike Dalessio
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-05-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Part of a project to explain how Ruby C extensions work.
14
+ email:
15
+ - mike.dalessio@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - Gemfile
22
+ - README.md
23
+ - Rakefile
24
+ - ext/precompiled/extconf.rb
25
+ - ext/precompiled/precompiled.c
26
+ - ext/precompiled/precompiled.h
27
+ - lib/rcee/precompiled.rb
28
+ - lib/rcee/precompiled/2.6/precompiled.so
29
+ - lib/rcee/precompiled/2.7/precompiled.so
30
+ - lib/rcee/precompiled/3.0/precompiled.so
31
+ - lib/rcee/precompiled/3.1/precompiled.so
32
+ - lib/rcee/precompiled/version.rb
33
+ - rcee_precompiled.gemspec
34
+ homepage: https://github.com/flavorjones/ruby-c-extensions-explained
35
+ licenses:
36
+ - MIT
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '2.6'
47
+ - - "<"
48
+ - !ruby/object:Gem::Version
49
+ version: 3.2.dev
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.3.4
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Example gem demonstrating a basic C extension.
60
+ test_files: []