leptris 1.9.361.0-ppc64le-linux → 1.9.362.1-ppc64le-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8e862fd2373fdbb557f098ee97391453e3d99d93059743ac4bc88e24c26865c
4
- data.tar.gz: b33652a2a82d7b9cb0470be4b5c5e5d8e63e253cf5c6d55e0ca1f4b2f8d6592e
3
+ metadata.gz: 9780542c27af094df56b7dbf848569f0006fcb73c60b941430dd04324e5ec49e
4
+ data.tar.gz: 23fb2d1e896b432b6b19719bd5f4c7ba2684c527029abc41eb8b1795f3079bfc
5
5
  SHA512:
6
- metadata.gz: cd17ceb87f3da41ef960e8a8096f9cfa8751d26490f27d9a3a71b2775bb2fddfde8e3036b581239b4941abe42c85eac27844970405c245d69322d21664d92667
7
- data.tar.gz: 603ad2eb391d23de4eab6b4fa0b7ae19698822ab1ab688b3613469a4e96e1a1db4dc0d54365a631fd2efec844f1df34ff9bd2f8e7e2309b07e3e7cd61d57f341
6
+ metadata.gz: '034691190cf53120d6ac25a1508baf4884cabbcbfb610eaecd4d77e8df8030f603f144a70c9cc646dc0584280de9f3baeb96e6a81325ec0a70e6103fb9f25198'
7
+ data.tar.gz: 2fc3de4b4ace30d69581f8025737b2f3208a759b7d5218af3e311e4490cc0721aece5cc106e24c403a51860aeb264ea4f345054f42f6ec33f993e80a014fbf05
data/README.adoc CHANGED
@@ -17,6 +17,27 @@ The C DOM is the single source of truth — Ruby objects are thin FFI
17
17
  handles over the C pointers, so every Ruby method maps to one FFI call.
18
18
  No tree hydration, no parallel Ruby-side model.
19
19
 
20
+ == Versioning: {engine-semver}.{binding-patch}
21
+
22
+ Gem versions are **`{libleptris-semver}.{binding-patch}`** — the lockstep
23
+ scheme. The first three components name the EXACT libleptris engine
24
+ release the gem vendors; the fourth is the binding's own patch number
25
+ for that engine.
26
+
27
+ * engine `v1.9.361` + first binding release → `1.9.361.0`
28
+ * binding-only fix on the same engine → `1.9.361.1`
29
+ * engine `v1.9.362` ride → `1.9.362.0`
30
+
31
+ The triple is a contract: `~> 1.9.361` pins both the engine behavior
32
+ and the binding surface. **Never** publish a version whose first three
33
+ components are not an existing engine release, never let the binding
34
+ line run ahead of the engine line, and every engine release must get
35
+ its binding ride (`.0`, or `.1` when the ride itself carries binding
36
+ fixes). Releases go through the release workflow with an explicit
37
+ `next_version` — the workflow owns the bump; version numbers are
38
+ computed from the engine tag being ridden, never from
39
+ "previous gem + 1".
40
+
20
41
  == Installation
21
42
 
22
43
  Add to your Gemfile:
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
8
8
  # Pin for `rake compile` and the platform-gem builds. Keep in lockstep
9
9
  # with .github/workflows/build.yml (which calls `rake compile`) and the
10
10
  # CHANGELOG when libleptris releases.
11
- LIBLEPTRIS_VERSION = "1.9.359"
11
+ LIBLEPTRIS_VERSION = "1.9.362"
12
12
  # Vendored alongside libleptris for fn:normalize-unicode (TODO
13
13
  # .restructure/20): built per platform with a RELOCATABLE @rpath
14
14
  # install name, loaded by ffi.rb before libleptris so the
@@ -292,13 +292,14 @@ task :compile do
292
292
  Dir.chdir(ext_dir) do
293
293
  sh "ruby extconf.rb"
294
294
  if RUBY_PLATFORM =~ /darwin/
295
- # Link the bundle ourselves: setup-ruby's custom rubies
296
- # make mkmf link libruby by absolute runner path no matter
297
- # which RbConfig entries are cleared, and any libruby
298
- # LC_LOAD_DYLIB is unresolvable on user machines. Compile
299
- # via the Makefile, link with pure -undefined
300
- # dynamic_lookup (Ruby symbols resolve from the loading
301
- # interpreter) and a conservative deployment target.
295
+ # Link the bundle ourselves: mkmf snapshots LIBRUBYARG from
296
+ # RbConfig at require time, so setup-ruby's shared-libruby
297
+ # rubies record their absolute hostedtoolcache path in the
298
+ # bundle (shipped broken through 1.9.36x, #1739) and no
299
+ # post-require clearing can stop it. Compile via the
300
+ # Makefile, link with pure -undefined dynamic_lookup (Ruby
301
+ # symbols resolve from the loading interpreter) and a
302
+ # conservative deployment target.
302
303
  sh "make native.o"
303
304
  sh "cc -dynamic -bundle -undefined dynamic_lookup " \
304
305
  "-mmacosx-version-min=#{ENV['MACOSX_DEPLOYMENT_TARGET'] || '11.0'} " \
@@ -312,10 +313,11 @@ task :compile do
312
313
  # their symbols via -rdynamic, like any extension .so).
313
314
  sh "make native.o"
314
315
  sh "#{RbConfig::CONFIG['CC']} -shared -fPIC -o native.so native.o"
315
- # Guard the contract in the build log.
316
- sh "if strings native.so | grep -q libruby; then " \
317
- "echo 'ERROR: native.so links libruby (#207)'; exit 1; fi"
318
316
  end
317
+ # Guard the linkage contract at the build chokepoint (#1739):
318
+ # no libruby reference, allowlisted load commands on darwin.
319
+ sh "#{RbConfig.ruby} ../../check_bundle_linkage.rb " \
320
+ "#{RUBY_PLATFORM =~ /darwin/ ? 'native.bundle' : 'native.so'}"
319
321
  end
320
322
  bundle = Dir.glob("#{ext_dir}/native.{bundle,so,dll}").first
321
323
  if bundle.nil?
@@ -337,6 +339,18 @@ task :compile do
337
339
  cp(bundle, "#{dest}.new")
338
340
  mv("#{dest}.new", dest)
339
341
  sign_darwin_binaries(dest)
342
+ # Per-minor twin (ruby#476): the loader prefers
343
+ # native_<minor>.{bundle,so}; dev builds run same-minor, so
344
+ # the copy is byte-identical and keeps `rake compile` +
345
+ # `rake spec` working without a full release matrix.
346
+ unless Gem.win_platform?
347
+ minor = RUBY_VERSION[/\A\d+\.\d+/].tr('.', '_')
348
+ ext = RUBY_PLATFORM.include?('darwin') ? '.bundle' : '.so'
349
+ twin = "lib/leptris/xml/native_#{minor}#{ext}"
350
+ cp(dest, "#{twin}.new")
351
+ mv("#{twin}.new", twin)
352
+ sign_darwin_binaries(twin)
353
+ end
340
354
  puts "Vendored native layer (#{File.basename(bundle)}) into lib/leptris/xml/"
341
355
  end
342
356
  end
@@ -412,11 +426,19 @@ namespace :audit do
412
426
  # leptris_version() equals the gem's engine pin (a half-bumped
413
427
  # lockstep state can no longer package). Negative proof lives
414
428
  # in spec/audit_codesign_spec.rb.
415
- desc "Fail on unsigned/tampered macOS artifacts or engine-version drift"
429
+ desc "Fail on unsigned/tampered macOS artifacts, engine-version drift, or libruby-linked native layers"
416
430
  task :artifacts do
431
+ # Linkage contract over EVERY shipped native layer, per-minor
432
+ # twins included (#1739) — every platform: the 1.9.36x trains
433
+ # packaged runner-absolute libruby loads in native_3_2/3_4
434
+ # artifacts that audit:symbols/audit:artifacts never opened.
435
+ bundles = Dir.glob("lib/leptris/xml/native*.{bundle,so}")
436
+ bundles.each do |b|
437
+ sh "#{RbConfig.ruby} ext/check_bundle_linkage.rb #{b}"
438
+ end
417
439
  unless RUBY_PLATFORM =~ /darwin/
418
- puts "audit:artifacts: skipped off-darwin (codesign gates " \
419
- "the macOS packaging legs)"
440
+ puts "audit:artifacts: #{bundles.size} native layers linkage-clean; " \
441
+ "codesign gates skip off-darwin"
420
442
  next
421
443
  end
422
444
  abort "audit:artifacts: codesign unavailable" \
@@ -439,8 +461,8 @@ namespace :audit do
439
461
  abort "audit:artifacts: engine-version drift — vendored " \
440
462
  "#{vendored.inspect} != pin #{pinned.inspect}"
441
463
  end
442
- puts "audit:artifacts: #{libs.size} macOS artifacts verify " \
443
- "strictly and engine-synced (#{pinned})"
464
+ puts "audit:artifacts: #{libs.size} macOS artifacts + #{bundles.size} native " \
465
+ "layers verify strictly, linkage-clean and engine-synced (#{pinned})"
444
466
  end
445
467
 
446
468
  # Packaging doctrine (#271): every BUILT gem carries the engine
@@ -563,14 +585,18 @@ platforms.each do |platform|
563
585
  spec.files += Dir.glob("vendor-src/**/*")
564
586
  spec.files += %w[ext/leptris/native/extconf.rb
565
587
  ext/leptris/native/native.c]
566
- if platform.include?("mingw")
567
- # #207/#227: per-Ruby-minor DLLs (PE must bind its Ruby).
568
- # DOT-FREE names — MRI derives the init symbol from the
569
- # basename cut at the first dot (native_3_3.so ->
570
- # Init_native_3_3; the dotted form made Ruby look for
571
- # "Init_native-3", which cannot exist).
572
- spec.files += Dir.glob("lib/leptris/xml/native_*.so")
573
- else
588
+ # #207/#227 + ruby#476: per-Ruby-minor bundles on EVERY
589
+ # platform (the ext's compiled-in RString layout changed in
590
+ # 3.2 — one CI-built bundle segfaults 3.1). DOT-FREE names —
591
+ # MRI derives the init symbol from the basename cut at the
592
+ # first dot (native_3_3.so -> Init_native_3_3; the dotted form
593
+ # made Ruby look for "Init_native-3", which cannot exist).
594
+ spec.files += Dir.glob("lib/leptris/xml/native_*.so")
595
+ spec.files += Dir.glob("lib/leptris/xml/native_*.bundle")
596
+ if !platform.include?("mingw")
597
+ # The unversioned bundle rides for toolchains that only run
598
+ # rake compile's single build (native_layer prefers the
599
+ # per-minor variant and never loads a wrong-ABI file).
574
600
  spec.files += Dir.glob("lib/leptris/xml/native.{bundle,so,dll}")
575
601
  end
576
602
  task = Gem::PackageTask.new(spec)
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Builds the native read layer bundle for the CURRENT Ruby and
4
+ # installs it under its minor-versioned name
5
+ # (lib/leptris/xml/native_<major>_<minor>.bundle on darwin, .so on
6
+ # Linux).
7
+ #
8
+ # ruby#476: the ext's compiled-in RString layout changed in Ruby
9
+ # 3.2 — a bundle built by a newer minor reads garbage pointers and
10
+ # lengths under 3.1 (the parse segfaulted inside the engine's
11
+ # correctly-bounded NEON text counter). One bundle per supported
12
+ # minor therefore ships in the darwin/Linux platform gems — the
13
+ # same contract the Windows gems have had since #207/#227 — and
14
+ # native_layer picks by RUBY_VERSION at require, falling back to
15
+ # the FFI surface when the variant for the running minor is
16
+ # absent. A WRONG-ABI load is never attempted.
17
+ #
18
+ # The name is DOT-FREE: MRI derives a C extension's init symbol
19
+ # from the require feature's basename cut at the first dot, so
20
+ # native_3_1.bundle resolves Init_native_3_1 (exported by
21
+ # native.c).
22
+
23
+ require "rbconfig"
24
+ require "fileutils"
25
+
26
+ root = File.expand_path("..", __dir__)
27
+ ext_dir = File.join(root, "ext", "leptris", "native")
28
+ minor = RUBY_VERSION[/\A\d+\.\d+/]
29
+ minor_us = minor.tr(".", "_")
30
+ ext = RUBY_PLATFORM.include?("darwin") ? ".bundle" : ".so"
31
+
32
+ Dir.chdir(ext_dir) do
33
+ # Force a clean compile on every invocation (stale objects from
34
+ # a failed earlier attempt ship silent wrong-ABI builds).
35
+ FileUtils.rm_f(Dir.glob("#{ext_dir}/native.{o,so,bundle}"))
36
+ system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
37
+ # Link WITHOUT libruby (#1739): mkmf's Makefile rule records the
38
+ # building ruby's libruby — for setup-ruby builds by absolute
39
+ # hostedtoolcache path — in every LC_LOAD_DYLIB / DT_NEEDED, which
40
+ # dlopen-fails on any other machine. Compile via the Makefile,
41
+ # link exactly like rake compile's native branch: pure
42
+ # dynamic_lookup, rb_* resolve from the loading interpreter.
43
+ system("make native.o") or abort "make native.o failed under #{RUBY_VERSION}"
44
+ cc = RbConfig::CONFIG["CC"]
45
+ link =
46
+ if ext == ".bundle"
47
+ "#{cc} -dynamic -bundle -undefined dynamic_lookup " \
48
+ "-mmacosx-version-min=#{ENV['MACOSX_DEPLOYMENT_TARGET'] || '11.0'} " \
49
+ "-o native.bundle native.o"
50
+ else
51
+ "#{cc} -shared -fPIC -o native.so native.o"
52
+ end
53
+ system(link) or abort "native link failed under #{RUBY_VERSION}: #{link}"
54
+ built = Dir.glob("native.{so,bundle}").first
55
+ abort "native bundle not produced under #{RUBY_VERSION}" unless built
56
+
57
+ # Linkage gate: no libruby reference, and (darwin) only
58
+ # allowlisted load commands — enforced HERE so a violated bundle
59
+ # never reaches packaging (the 1.9.36x trains shipped exactly
60
+ # this shape, #1739).
61
+ system(RbConfig.ruby, File.join(root, "ext", "check_bundle_linkage.rb"),
62
+ File.expand_path(built)) or
63
+ abort "linkage gate failed under #{RUBY_VERSION}"
64
+
65
+ # Functional gate: dlopen the built bundle and resolve the
66
+ # MINOR-SUFFIXED init symbol — only a real lookup proves
67
+ # require "leptris/xml/native_<minor>" will resolve.
68
+ begin
69
+ require "fiddle"
70
+ Fiddle.dlopen(File.expand_path(built))["Init_native_#{minor_us}"]
71
+ rescue LoadError => e
72
+ warn "fiddle unavailable under #{RUBY_VERSION} (#{e.message}) — skipping"
73
+ rescue Fiddle::DLError => e
74
+ abort "#{built} does not export Init_native_#{minor_us} — refusing to " \
75
+ "install a bundle that would fail at require (#{e.message})"
76
+ end
77
+
78
+ dest = File.join(root, "lib", "leptris", "xml", "native_#{minor_us}#{ext}")
79
+ FileUtils.cp(built, dest)
80
+ puts "Installed native layer for Ruby #{minor} -> #{dest}"
81
+ end
@@ -29,14 +29,17 @@ Dir.chdir(ext_dir) do
29
29
  # because make reused a stale native.obj from a failed earlier
30
30
  # attempt (the log shows "creating Makefile -> linking" with no
31
31
  # compile). Force a clean compile on every invocation.
32
- FileUtils.rm_f(Dir.glob("#{ext_dir}/native.{o,obj,so,dll}"))
33
- FileUtils.rm_f("#{ext_dir}/leptris/xml/native.so")
32
+ # The globs cover both spellings of the mkmf target: the
33
+ # unversioned name and the per-minor name extconf now creates
34
+ # the Makefile for (#476 source-install parity).
35
+ FileUtils.rm_f(Dir.glob("#{ext_dir}/native*.{o,obj,so,dll}"))
36
+ FileUtils.rm_f(Dir.glob("#{ext_dir}/leptris/xml/native*.so"))
34
37
  system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
35
38
  # mkmf's link binds the CURRENT Ruby's runtime DLL — exactly
36
39
  # what the versioned naming is for.
37
40
  success = system("make")
38
41
  abort "make failed under #{RUBY_VERSION}" unless success
39
- so = Dir.glob("native.{so,dll}").first
42
+ so = Dir.glob("native*.so").first
40
43
  abort "native bundle not produced under #{RUBY_VERSION}" unless so
41
44
  # Functional export gate: dlopen the built DLL and resolve the
42
45
  # MINOR-SUFFIXED init symbol. A PE DLL can carry the symbol in its
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Linkage contract for shipped native layers (#1739 / ruby#481): a
4
+ # Ruby extension resolves rb_* symbols from the LOADING interpreter,
5
+ # so the artifact must never link libruby — mkmf records the
6
+ # building ruby's absolute path (setup-ruby's hostedtoolcache
7
+ # location), which dlopen-fails on every other machine and silently
8
+ # degrades the whole install to FFI. On darwin the check is
9
+ # stricter: every load command must stay inside the loader
10
+ # allowlist. Windows PE DLLs are exempt — binding the build
11
+ # runtime is the documented contract there (#227).
12
+ #
13
+ # Runnable as a script over any number of artifacts (exit 1 on the
14
+ # first violated contract batch) or required for the pure checks in
15
+ # spec/check_bundle_linkage_spec.rb.
16
+ module CheckBundleLinkage
17
+ ALLOWED_PREFIXES = %w[@rpath/ @loader_path/ @executable_path/ /usr/lib/ /System/].freeze
18
+
19
+ # The LC_LOAD_DYLIB paths otool -L lists that fall outside the
20
+ # allowlist. +otool_output+ is the raw stdout of `otool -L <file>`;
21
+ # entries are the tab-indented "<install name> (compatibility
22
+ # version X, current version Y)" lines.
23
+ def self.load_command_violations(otool_output)
24
+ otool_output.lines.grep(/^\t.*\(compatibility version/)
25
+ .filter_map { |l| l.split.first }
26
+ .reject { |path| ALLOWED_PREFIXES.any? { |p| path.start_with?(p) } }
27
+ end
28
+
29
+ # The libruby sonames a strings(1) dump reveals — the ELF-side
30
+ # tripwire for DT_NEEDED entries and RUNPATH spellings alike.
31
+ def self.libruby_references(strings_output)
32
+ strings_output.scan(/libruby[\w.]*/).uniq
33
+ end
34
+ end
35
+
36
+ if $PROGRAM_NAME == __FILE__
37
+ require "shellwords"
38
+
39
+ status = 0
40
+ ARGV.each do |path|
41
+ next if path.end_with?(".dll")
42
+
43
+ bad = CheckBundleLinkage.libruby_references(`strings #{Shellwords.escape(path)} 2>/dev/null`)
44
+ unless bad.empty?
45
+ warn "ERROR: #{path} references #{bad.join(', ')} — Ruby symbols must " \
46
+ "resolve from the loading interpreter, never a linked libruby " \
47
+ "(#1739); link with -undefined dynamic_lookup / plain -shared"
48
+ status = 1
49
+ end
50
+ next unless RUBY_PLATFORM.include?("darwin")
51
+
52
+ viol = CheckBundleLinkage.load_command_violations(`otool -L #{Shellwords.escape(path)} 2>/dev/null`)
53
+ next if viol.empty?
54
+
55
+ warn "ERROR: #{path} carries load commands outside the loader " \
56
+ "allowlist (#{CheckBundleLinkage::ALLOWED_PREFIXES.join(' ')}): " \
57
+ "#{viol.join(', ')} (#1739)"
58
+ status = 1
59
+ end
60
+ exit status
61
+ end
@@ -28,6 +28,19 @@ unless %w[ruby truffleruby].include?(RUBY_ENGINE)
28
28
  "installing without the compiled native layer"
29
29
  exit 0
30
30
  end
31
+ # Never link libruby: a Ruby extension resolves rb_* symbols from
32
+ # the LOADING interpreter, and mkmf records the building ruby's
33
+ # libruby (for setup-ruby, an absolute hostedtoolcache path) into
34
+ # the bundle otherwise — dlopen-failing on every other machine
35
+ # (#1739). mkmf snapshots $LIBRUBYARG* from RbConfig at require
36
+ # time, so this MUST precede `require "mkmf"`; a later clearing is
37
+ # a silent no-op (exactly how the 1.9.36x trains shipped broken).
38
+ if RUBY_PLATFORM =~ /darwin/
39
+ %w[LIBRUBYARG LIBRUBYARG_SHARED LIBRUBYARG_STATIC].each do |key|
40
+ RbConfig::CONFIG[key] = ""
41
+ RbConfig::MAKEFILE_CONFIG[key] = ""
42
+ end
43
+ end
31
44
  require "mkmf"
32
45
 
33
46
  # A prebuilt libleptris in lib/ (dev checkout after rake compile,
@@ -106,18 +119,25 @@ if RUBY_PLATFORM =~ /darwin/
106
119
  [$LDFLAGS, $DLDFLAGS].each do |var|
107
120
  var << lookup unless var.include?("dynamic_lookup")
108
121
  end
109
- # setup-ruby builds carry the RUNNER's absolute libruby path in
110
- # LIBRUBYARG_SHARED — recorded as an LC_LOAD_DYLIB in the
111
- # bundle, unresolvable on user machines. A Ruby extension
112
- # resolves Ruby symbols from the loading interpreter; never
113
- # link libruby.
114
- # BOTH maps: mkmf's Makefile interpolates from MAKEFILE_CONFIG,
115
- # not CONFIG — clearing only CONFIG is a silent no-op (caught via
116
- # the build-log otool on the runner).
117
- RbConfig::CONFIG["LIBRUBYARG_SHARED"] = ""
118
- RbConfig::CONFIG["LIBRUBYARG_STATIC"] = ""
119
- RbConfig::MAKEFILE_CONFIG["LIBRUBYARG_SHARED"] = ""
120
- RbConfig::MAKEFILE_CONFIG["LIBRUBYARG_STATIC"] = ""
122
+ # ruby#476: newer ld64 defaults BUNDLES to two-level namespace —
123
+ # with -undefined dynamic_lookup a two-level bind records the
124
+ # symbol as expected from the MAIN EXECUTABLE ("main-executable"),
125
+ # and interpreters that don't re-export every ruby symbol from
126
+ # their binary abort at first call ("dyld missing symbol called",
127
+ # hit at Init_native's first import). -flat_namespace is the
128
+ # classic C-extension pattern: dynamic_lookup symbols search all
129
+ # loaded images at call time and resolve from libruby.
130
+ flat = " -Wl,-flat_namespace"
131
+ [$LDFLAGS, $DLDFLAGS].each do |var|
132
+ var << flat unless var.include?("flat_namespace")
133
+ end
134
+ # Belt for the require-time snapshot: RbConfig was cleared BEFORE
135
+ # `require "mkmf"` (top of file), and the module globals mkmf
136
+ # captured then are zeroed here too — a Ruby extension resolves
137
+ # Ruby symbols from the loading interpreter; never link libruby.
138
+ $LIBRUBYARG = ""
139
+ $LIBRUBYARG_SHARED = ""
140
+ $LIBRUBYARG_STATIC = ""
121
141
  end
122
142
  if Gem.win_platform?
123
143
  # Windows names the artifact per Ruby minor, DOT-FREE (#227
@@ -137,4 +157,12 @@ if Gem.win_platform?
137
157
  win_def = " #{def_path.tr('/', '\\\\')}"
138
158
  $DLDFLAGS << win_def unless $DLDFLAGS.include?("native_exports.def")
139
159
  end
140
- create_makefile("leptris/xml/native")
160
+ # Name the artifact per Ruby minor EVERYWHERE (#476): the loader
161
+ # requires native_<minor> and deliberately has NO unversioned
162
+ # fallback (loading a bundle built by another minor is the 3.1
163
+ # wrong-ABI segfault class), so a source install must produce the
164
+ # per-minor name itself. mkmf derives Init_native_<minor> from the
165
+ # same basename — exported by native.c and pinned in the Windows
166
+ # .def above.
167
+ minor_us = RUBY_VERSION[/\A\d+\.\d+/].tr(".", "_")
168
+ create_makefile("leptris/xml/native_#{minor_us}")