tebako 0.12.9 → 0.12.10

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.
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com).
4
+ # All rights reserved.
5
+ # This file is a part of tebako
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
20
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ # POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require_relative "patch_literals"
29
+ require_relative "patch_main"
30
+ require_relative "patch_libraries"
31
+ require_relative "patch_helpers"
32
+ require_relative "patch_buildsystem"
33
+
34
+ # Tebako - an executable packager
35
+ module Tebako
36
+ module Packager
37
+ # Ruby patching definitions (pass2)
38
+ class Pass2Patch < Patch
39
+ def initialize(ostype, deps_lib_dir, ruby_ver)
40
+ super()
41
+ @ostype = ostype
42
+ @scmb = ScenarioManagerBase.new(@ostype)
43
+ @deps_lib_dir = deps_lib_dir
44
+ @ruby_ver = ruby_ver
45
+ end
46
+
47
+ def patch_map
48
+ patch_map = patch_map_base
49
+ patch_map.store("thread_pthread.c", LINUX_MUSL_THREAD_PTHREAD_PATCH) if @scmb.musl?
50
+ if @scmb.msys?
51
+ patch_map.merge!(msys_patches)
52
+ elsif @ruby_ver.ruby3x?
53
+ patch_map.store("common.mk", COMMON_MK_PATCH)
54
+ end
55
+ extend_patch_map_r33(patch_map)
56
+ patch_map.store("prism_compile.c", PRISM_PATCHES) if @ruby_ver.ruby34?
57
+ patch_map
58
+ end
59
+
60
+ private
61
+
62
+ include Tebako::Packager::PatchBuildsystem
63
+ include Tebako::Packager::PatchLiterals
64
+ def extend_patch_map_r33(patch_map)
65
+ if @ruby_ver.ruby33? || @scmb.msys?
66
+ patch_map.store("config.status",
67
+ get_config_status_patch(@ostype, @deps_lib_dir, @ruby_ver))
68
+ end
69
+ patch_map
70
+ end
71
+
72
+ def dir_c_patch
73
+ pattern = ScenarioManagerBase.new.msys? ? "/* define system APIs */" : "#ifdef HAVE_GETATTRLIST"
74
+ patch = PatchHelpers.patch_c_file_pre(pattern)
75
+ patch.merge!(DIR_C_BASE_PATCH)
76
+ patch
77
+ end
78
+
79
+ def dln_c_patch
80
+ pattern = "#ifndef dln_loaderror"
81
+ # Not using substitutions of dlxxx functions on Windows
82
+ patch = {
83
+ pattern => "#{@scmb.msys? ? C_FILE_SUBST_LESS : C_FILE_SUBST}\n#{pattern}\n"
84
+ }
85
+
86
+ if @scmb.msys?
87
+ patch.merge!(@ruby_ver.ruby32? ? DLN_C_MSYS_PATCH : DLN_C_MSYS_PATCH_PRE32)
88
+ end
89
+
90
+ patch
91
+ end
92
+
93
+ def io_c_msys_patch
94
+ patch = @ruby_ver.ruby32? ? IO_C_MSYS_PATCH : IO_C_MSYS_PATCH_PRE_32
95
+ patch.merge(IO_C_MSYS_BASE_PATCH)
96
+ end
97
+
98
+ def io_c_patch
99
+ patch = PatchHelpers.patch_c_file_pre("/* define system APIs */")
100
+ patch.merge!(io_c_msys_patch) if @scmb.msys?
101
+ patch
102
+ end
103
+
104
+ def util_c_patch
105
+ if @ruby_ver.ruby31?
106
+ PatchHelpers.patch_c_file_post("#endif /* !HAVE_GNU_QSORT_R */")
107
+ else
108
+ PatchHelpers.patch_c_file_pre("#ifndef S_ISDIR")
109
+ end
110
+ end
111
+
112
+ def tool_mkconfig_rb_patch
113
+ subst = @scmb.msys? ? TOOL_MKCONFIG_RB_SUBST_MSYS : TOOL_MKCONFIG_RB_SUBST
114
+ {
115
+ " if fast[name]" => subst
116
+ }
117
+ end
118
+
119
+ def msys_patches
120
+ {
121
+ "cygwin/GNUmakefile.in" => get_gnumakefile_in_patch_p2(@ruby_ver),
122
+ "ruby.c" => RUBY_C_MSYS_PATCHES,
123
+ "win32/file.c" => WIN32_FILE_C_MSYS_PATCHES,
124
+ "win32/win32.c" => WIN32_WIN32_C_MSYS_PATCHES
125
+ }
126
+ end
127
+
128
+ def patch_map_base
129
+ {
130
+ "template/Makefile.in" => template_makefile_in_patch,
131
+ "tool/mkconfig.rb" => tool_mkconfig_rb_patch,
132
+ "dir.c" => dir_c_patch, "dln.c" => dln_c_patch,
133
+ "io.c" => io_c_patch, "main.c" => PatchMain.get_main_c_patch(@ruby_ver),
134
+ "file.c" => PatchHelpers.patch_c_file_pre("/* define system APIs */"),
135
+ "util.c" => util_c_patch
136
+ }
137
+ end
138
+
139
+ def mlibs_subst
140
+ yjit_libs = @ruby_ver.ruby32only? ? "$(YJIT_LIBS) " : ""
141
+ {
142
+ "MAINLIBS = #{yjit_libs}@MAINLIBS@" =>
143
+ "# -- Start of tebako patch -- \n" \
144
+ "MAINLIBS = #{yjit_libs}#{PatchLibraries.mlibs(@ostype, @deps_lib_dir, @ruby_ver, true)}\n" \
145
+ "# -- End of tebako patch -- \n"
146
+ }
147
+ end
148
+
149
+ def template_makefile_in_patch
150
+ template_makefile_in_patch_two(@ruby_ver).merge(mlibs_subst)
151
+ end
152
+ end
153
+ end
154
+ end
@@ -77,8 +77,8 @@ module Tebako
77
77
  "$(EXTOBJS) $(LIBRUBYARG_STATIC) $(OUTFLAG)$@\n" \
78
78
  "# -- End of tebako patch --"
79
79
 
80
- def template_makefile_in_subst(ostype, ruby_ver)
81
- if PatchHelpers.msys?(ostype)
80
+ def template_makefile_in_subst(ruby_ver)
81
+ if ScenarioManagerBase.new.msys?
82
82
  TEMPLATE_MAKEFILE_IN_BASE_PATCH_MSYS
83
83
  elsif !ruby_ver.ruby31?
84
84
  TEMPLATE_MAKEFILE_IN_BASE_PATCH_PRE_3_1
@@ -87,16 +87,20 @@ module Tebako
87
87
  end
88
88
  end
89
89
 
90
- def template_makefile_in_patch_two(ostype, ruby_ver)
90
+ def template_makefile_in_patch_two(ruby_ver)
91
91
  if !ruby_ver.ruby31?
92
- { TEMPLATE_MAKEFILE_IN_BASE_PATTERN_PRE_3_1 => template_makefile_in_subst(ostype, ruby_ver) }
92
+ { TEMPLATE_MAKEFILE_IN_BASE_PATTERN_PRE_3_1 => template_makefile_in_subst(ruby_ver) }
93
93
  elsif !ruby_ver.ruby33?
94
- { TEMPLATE_MAKEFILE_IN_BASE_PATTERN_PRE_3_3 => template_makefile_in_subst(ostype, ruby_ver) }
94
+ { TEMPLATE_MAKEFILE_IN_BASE_PATTERN_PRE_3_3 => template_makefile_in_subst(ruby_ver) }
95
95
  else
96
- { TEMPLATE_MAKEFILE_IN_BASE_PATTERN => template_makefile_in_subst(ostype, ruby_ver) }
96
+ { TEMPLATE_MAKEFILE_IN_BASE_PATTERN => template_makefile_in_subst(ruby_ver) }
97
97
  end
98
98
  end
99
99
 
100
+ GNUMAKEFILE_IN_DLLTOOL_SUBST = <<~SUBST
101
+ $(Q) dlltool --output-lib=$(LIBRUBY) --output-def=tebako.def --export-all $(LIBRUBY_A) --output-exp=$(RUBY_EXP) # tebako patched
102
+ SUBST
103
+
100
104
  # This MSYS specific thing ensure compilation of winmain.c
101
105
  # Did try to understand why it did not work out of the box
102
106
  GNUMAKEFILE_IN_WINMAIN_SUBST = <<~SUBST
@@ -62,18 +62,6 @@ module Tebako
62
62
  out
63
63
  end
64
64
 
65
- def exe_suffix(ostype)
66
- msys?(ostype) ? ".exe" : ""
67
- end
68
-
69
- def msys?(ostype)
70
- ostype =~ /msys|cygwin|mingw/
71
- end
72
-
73
- def macos?(ostype)
74
- ostype =~ /darwin/
75
- end
76
-
77
65
  def patch_c_file_pre(pattern)
78
66
  {
79
67
  pattern => "#{PatchLiterals::C_FILE_SUBST}\n#{pattern}"
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024-2025 [Ribose Inc](https://www.ribose.com).
4
+ # All rights reserved.
5
+ # This file is a part of tebako
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
20
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ # POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require_relative "patch"
29
+ # Tebako - an executable packager
30
+ module Tebako
31
+ module Packager
32
+ # Shared accross Pass1Patch and RybugemsUpdatePatch
33
+ class RubygemsPatch < Patch
34
+ RUBYGEMS_OPENSSL_RB_SUBST = <<~SUBST
35
+ # Start of tebako patch
36
+ require "openssl"
37
+ # End of tebako patch
38
+ autoload :OpenSSL, "openssl"
39
+ SUBST
40
+
41
+ RUBYGEMS_OPENSSL_RB_PATCH = {
42
+ 'autoload :OpenSSL, "openssl"' => RUBYGEMS_OPENSSL_RB_SUBST
43
+ }.freeze
44
+
45
+ def initialize(mount_point)
46
+ super()
47
+ @mount_point = mount_point
48
+ end
49
+
50
+ protected
51
+
52
+ def rubygems_path_support_patch(mount_point)
53
+ patch = <<~SUBST
54
+ # -- Start of tebako patch --
55
+ @home = Gem.default_dir unless @home.index("#{mount_point}") == 0 unless env["TEBAKO_PASS_THROUGH"]
56
+ # -- End of tebako patch --
57
+ @path = split_gem_path env["GEM_PATH"], @home
58
+ # -- Start of tebako patch --
59
+ @path.keep_if do |xpath|
60
+ xpath.index("#{mount_point}") == 0
61
+ end unless env["TEBAKO_PASS_THROUGH"]
62
+ # -- End of tebako patch --
63
+ SUBST
64
+
65
+ {
66
+ ' @path = split_gem_path env["GEM_PATH"], @home' => patch
67
+ }
68
+ end
69
+ end
70
+
71
+ # Rubygems patch after update
72
+ class RubygemsUpdatePatch < RubygemsPatch
73
+ def patch_map
74
+ pm = {
75
+ "rubygems/openssl.rb" => RUBYGEMS_OPENSSL_RB_PATCH,
76
+ "rubygems/path_support.rb" => rubygems_path_support_patch(@mount_point)
77
+ }
78
+ pm.merge!(super)
79
+ pm.freeze
80
+ end
81
+ end
82
+ end
83
+ end
@@ -35,7 +35,7 @@ require_relative "ruby_builder"
35
35
  require_relative "stripper"
36
36
  require_relative "packager/pass1_patch"
37
37
  require_relative "packager/pass1a_patch"
38
- require_relative "packager/pass2"
38
+ require_relative "packager/pass2_patch"
39
39
  require_relative "packager/patch_helpers"
40
40
 
41
41
  # Tebako - an executable packager
@@ -89,15 +89,20 @@ module Tebako
89
89
  Tebako::Stripper.strip(deploy_helper, target_dir)
90
90
  end
91
91
 
92
- def finalize(os_type, src_dir, app_name, ruby_ver, patchelf)
92
+ def do_patch(patch_map, root)
93
+ patch_map.each { |fname, mapping| PatchHelpers.patch_file("#{root}/#{fname}", mapping) }
94
+ end
95
+
96
+ def finalize(src_dir, app_name, ruby_ver, patchelf)
93
97
  puts "-- Running finalize script"
94
98
 
95
99
  RubyBuilder.new(ruby_ver, src_dir).target_build
96
- exe_suffix = Packager::PatchHelpers.exe_suffix(os_type)
100
+ exe_suffix = ScenarioManagerBase.new.exe_suffix
97
101
  src_name = File.join(src_dir, "ruby#{exe_suffix}")
98
102
  patchelf(src_name, patchelf)
99
103
  package_name = "#{app_name}#{exe_suffix}"
100
- strip_or_copy(os_type, src_name, package_name)
104
+ # strip_or_copy(os_type, src_name, package_name)
105
+ Tebako::Stripper.strip_file(src_name, package_name)
101
106
  puts "Created tebako package at \"#{package_name}\""
102
107
  end
103
108
 
@@ -145,7 +150,8 @@ module Tebako
145
150
  def pass2(ostype, ruby_source_dir, deps_lib_dir, ruby_ver)
146
151
  puts "-- Running pass2 script"
147
152
 
148
- do_patch(Pass2.get_patch_map(ostype, deps_lib_dir, ruby_ver), ruby_source_dir)
153
+ patch = Pass2Patch.new(ostype, deps_lib_dir, ruby_ver).patch_map
154
+ do_patch(patch, ruby_source_dir)
149
155
  end
150
156
 
151
157
  # Stash
@@ -183,10 +189,6 @@ module Tebako
183
189
  File.join(src_dir, "lib", "libx64-ucrt-ruby#{ruby_ver.lib_version}.a")
184
190
  end
185
191
 
186
- def do_patch(patch_map, root)
187
- patch_map.each { |fname, mapping| PatchHelpers.patch_file("#{root}/#{fname}", mapping) }
188
- end
189
-
190
192
  def patchelf(src_name, patchelf)
191
193
  return if patchelf.nil?
192
194
 
@@ -194,15 +196,15 @@ module Tebako
194
196
  BuildHelpers.run_with_capture(params)
195
197
  end
196
198
 
197
- def strip_or_copy(os_type, src_name, package_name)
198
- # [TODO] On MSys strip sometimes creates a broken executable
199
- # https://github.com/tamatebako/tebako/issues/172
200
- if Packager::PatchHelpers.msys?(os_type)
201
- FileUtils.cp(src_name, package_name)
202
- else
203
- Tebako::Stripper.strip_file(src_name, package_name)
204
- end
205
- end
199
+ # def strip_or_copy(_os_type, src_name, package_name)
200
+ # [TODO] On MSys strip sometimes creates a broken executable
201
+ # https://github.com/tamatebako/tebako/issues/172
202
+ # if Packager::PatchHelpers.msys?(os_type)
203
+ # FileUtils.cp(src_name, package_name)
204
+ # else
205
+ # Tebako::Stripper.strip_file(src_name, package_name)
206
+ # end
207
+ # end
206
208
  end
207
209
  end
208
210
  end
@@ -37,7 +37,7 @@ module Tebako
37
37
  def initialize(ruby_ver, src_dir)
38
38
  @ruby_ver = ruby_ver
39
39
  @src_dir = src_dir
40
- @ncores = BuildHelpers.ncores
40
+ @ncores = ScenarioManagerBase.new.ncores
41
41
  end
42
42
 
43
43
  # Final build of tebako package
@@ -120,8 +120,7 @@ module Tebako
120
120
  end
121
121
 
122
122
  def version_check_msys
123
- if Gem::Version.new(@ruby_version) < Gem::Version.new(MIN_RUBY_VERSION_WINDOWS) &&
124
- RUBY_PLATFORM =~ /msys|mingw|cygwin/
123
+ if Gem::Version.new(@ruby_version) < Gem::Version.new(MIN_RUBY_VERSION_WINDOWS) && ScenarioManagerBase.new.msys?
125
124
  raise Tebako::Error.new("Ruby version #{@ruby_version} is not supported on Windows", 111)
126
125
  end
127
126
  end
@@ -132,14 +131,14 @@ module Tebako
132
131
  def initialize(ruby_version, gemfile_path)
133
132
  # Assuming that it does not attempt to load any gems or resolve dependencies
134
133
  # this can be done with any bundler version
135
- gemfile = Bundler::Definition.build(gemfile_path, nil, nil)
136
- ruby_v = gemfile.ruby_version&.versions
134
+ ruby_v = Bundler::Definition.build(gemfile_path, nil, nil).ruby_version&.versions
137
135
  if ruby_v.nil?
138
136
  super(ruby_version)
139
137
  else
140
138
  process_gemfile_ruby_version(ruby_version, ruby_v)
141
- run_checks
142
139
  end
140
+ rescue Tebako::Error
141
+ raise
143
142
  rescue StandardError => e
144
143
  Tebako.packaging_error(115, e.message)
145
144
  end
@@ -153,12 +152,13 @@ module Tebako
153
152
  else
154
153
  process_gemfile_ruby_version_d(ruby_version, requirement)
155
154
  end
155
+ run_checks
156
156
  end
157
157
 
158
158
  def process_gemfile_ruby_version_d(ruby_version, requirement)
159
159
  current_version = Gem::Version.new(ruby_version)
160
160
  unless requirement.satisfied_by?(current_version)
161
- raise Tebako::Error.new("Ruby version #{ruby_version} does not satisfy requirement #{ruby_v}", 116)
161
+ raise Tebako::Error.new("Ruby version #{ruby_version} does not satisfy requirement '#{requirement}'", 116)
162
162
  end
163
163
 
164
164
  @ruby_version = ruby_version
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 [Ribose Inc](https://www.ribose.com).
3
+ # Copyright (c) 2024-2025 [Ribose Inc](https://www.ribose.com).
4
4
  # All rights reserved.
5
5
  # This file is a part of tebako
6
6
  #
@@ -32,64 +32,99 @@ require_relative "error"
32
32
 
33
33
  # Tebako - an executable packager
34
34
  module Tebako
35
- # Manages packaging scenario based on input files (gemfile, gemspec, etc)
36
- class ScenarioManager
37
- def initialize(fs_root, fs_entrance)
38
- @with_gemfile = false
39
- initialize_root(fs_root)
40
- initialize_entry_point(fs_entrance || "stub.rb")
35
+ # Magic version numbers used to ensure compatibility for Ruby 2.7.x, 3.0.x
36
+ # These are the minimal versions required to provide linux-gnu / linux-musl differentiation by bundler
37
+ # Ruby 3.1+ default rubygems versions work correctly out of the box
38
+ BUNDLER_VERSION = "2.4.22"
39
+ RUBYGEMS_VERSION = "3.4.22"
40
+
41
+ # A couple of static Scenario definitions
42
+ class ScenarioManagerBase
43
+ def initialize(ostype = RUBY_PLATFORM)
44
+ @ostype = ostype
45
+ @linux = @ostype =~ /linux/ ? true : false
46
+ @musl = @ostype =~ /linux-musl/ ? true : false
47
+ @macos = @ostype =~ /darwin/ ? true : false
48
+ @msys = @ostype =~ /msys|mingw|cygwin/ ? true : false
49
+
50
+ @fs_mount_point = @msys ? "A:/__tebako_memfs__" : "/__tebako_memfs__"
51
+ @exe_suffix = @msys ? ".exe" : ""
41
52
  end
42
53
 
43
- attr_reader :fs_entry_point, :fs_mount_point, :fs_entrance, :gemfile_path, :with_gemfile
54
+ attr_reader :fs_mount_point, :exe_suffix
44
55
 
45
- def configure_scenario
46
- @fs_mount_point = if msys?
47
- "A:/__tebako_memfs__"
48
- else
49
- "/__tebako_memfs__"
50
- end
56
+ def b_env
57
+ u_flags = if @macos
58
+ "-DTARGET_OS_SIMULATOR=0 -DTARGET_OS_IPHONE=0 #{ENV.fetch("CXXFLAGS", nil)}"
59
+ else
60
+ ENV.fetch("CXXFLAGS", nil)
61
+ end
62
+ @b_env ||= { "CXXFLAGS" => u_flags }
63
+ end
51
64
 
52
- lookup_files
53
- configure_scenario_inner
65
+ def linux?
66
+ @linux
54
67
  end
55
68
 
56
- def exe_suffix
57
- @exe_suffix ||= msys? ? ".exe" : ""
69
+ def m_files
70
+ # [TODO]
71
+ # Ninja generates incorrect script for tebako press target -- gets lost in a chain custom targets
72
+ # Using makefiles has negative performance impact so it needs to be fixed
73
+
74
+ @m_files ||= if @linux || @macos
75
+ "Unix Makefiles"
76
+ elsif @msys
77
+ "MinGW Makefiles"
78
+ else
79
+ raise Tebako::Error.new("#{RUBY_PLATFORM} is not supported.", 112)
80
+ end
58
81
  end
59
82
 
60
83
  def macos?
61
- @macos ||= RUBY_PLATFORM =~ /darwin/ ? true : false
84
+ @macos
62
85
  end
63
86
 
64
87
  def msys?
65
- @msys ||= RUBY_PLATFORM =~ /msys|mingw|cygwin/ ? true : false
88
+ @msys
66
89
  end
67
90
 
68
- private
69
-
70
- def initialize_entry_point(fs_entrance)
71
- @fs_entrance = Pathname.new(fs_entrance).cleanpath.to_s
91
+ def musl?
92
+ @musl
93
+ end
72
94
 
73
- if Pathname.new(@fs_entrance).absolute?
74
- Tebako.packaging_error 114 unless @fs_entrance.start_with?(@fs_root)
95
+ def ncores
96
+ if @ncores.nil?
97
+ if @macos
98
+ out, st = Open3.capture2e("sysctl", "-n", "hw.ncpu")
99
+ else
100
+ out, st = Open3.capture2e("nproc", "--all")
101
+ end
75
102
 
76
- fetmp = @fs_entrance
77
- @fs_entrance = Pathname.new(@fs_entrance).relative_path_from(Pathname.new(@fs_root)).to_s
78
- puts "-- Absolute path to entry point '#{fetmp}' will be reduced to '#{@fs_entrance}' relative to '#{@fs_root}'"
103
+ @ncores = !st.signaled? && st.exitstatus.zero? ? out.strip.to_i : 4
79
104
  end
80
- # Can check after deploy, because entry point can be generated during bundle install or gem install
81
- # Tebako.packaging_error 106 unless File.file?(File.join(@fs_root, @fs_entrance))
82
- @fs_entry_point = "/bin/#{@fs_entrance}"
105
+ @ncores
83
106
  end
107
+ end
84
108
 
85
- def initialize_root(fs_root)
86
- Tebako.packaging_error 107 unless Dir.exist?(fs_root)
87
- p_root = Pathname.new(fs_root).cleanpath
88
- Tebako.packaging_error 113 unless p_root.absolute?
89
- @fs_root = p_root.realpath.to_s
109
+ # Manages packaging scenario based on input files (gemfile, gemspec, etc)
110
+ class ScenarioManager < ScenarioManagerBase
111
+ def initialize(fs_root, fs_entrance)
112
+ super()
113
+ @with_gemfile = @with_lockfile = @needs_bundler = false
114
+ @bundler_version = BUNDLER_VERSION
115
+ initialize_root(fs_root)
116
+ initialize_entry_point(fs_entrance || "stub.rb")
117
+ end
118
+
119
+ attr_reader :fs_entry_point, :fs_entrance, :gemfile_path, :needs_bundler, :with_gemfile
120
+
121
+ def bundler_reference
122
+ @needs_bundler ? "_#{@bundler_version}_" : nil
90
123
  end
91
124
 
92
- def configure_scenario_inner
125
+ def configure_scenario
126
+ lookup_files
127
+
93
128
  case @gs_length
94
129
  when 0
95
130
  configure_scenario_no_gemspec
@@ -100,6 +135,8 @@ module Tebako
100
135
  end
101
136
  end
102
137
 
138
+ private
139
+
103
140
  def configure_scenario_no_gemspec
104
141
  @fs_entry_point = "/local/#{@fs_entrance}" if @with_gemfile || @g_length.zero?
105
142
 
@@ -112,11 +149,93 @@ module Tebako
112
149
  end
113
150
  end
114
151
 
152
+ def initialize_entry_point(fs_entrance)
153
+ @fs_entrance = Pathname.new(fs_entrance).cleanpath.to_s
154
+
155
+ if Pathname.new(@fs_entrance).absolute?
156
+ Tebako.packaging_error 114 unless @fs_entrance.start_with?(@fs_root)
157
+
158
+ fetmp = @fs_entrance
159
+ @fs_entrance = Pathname.new(@fs_entrance).relative_path_from(Pathname.new(@fs_root)).to_s
160
+ puts "-- Absolute path to entry point '#{fetmp}' will be reduced to '#{@fs_entrance}' relative to '#{@fs_root}'"
161
+ end
162
+ # Can check after deploy, because entry point can be generated during bundle install or gem install
163
+ # Tebako.packaging_error 106 unless File.file?(File.join(@fs_root, @fs_entrance))
164
+ @fs_entry_point = "/bin/#{@fs_entrance}"
165
+ end
166
+
167
+ def initialize_root(fs_root)
168
+ Tebako.packaging_error 107 unless Dir.exist?(fs_root)
169
+ p_root = Pathname.new(fs_root).cleanpath
170
+ Tebako.packaging_error 113 unless p_root.absolute?
171
+ @fs_root = p_root.realpath.to_s
172
+ end
173
+
115
174
  def lookup_files
116
- @gemfile_path = File.join(@fs_root, "Gemfile")
117
175
  @gs_length = Dir.glob(File.join(@fs_root, "*.gemspec")).length
118
- @with_gemfile = File.exist?(@gemfile_path)
119
176
  @g_length = Dir.glob(File.join(@fs_root, "*.gem")).length
177
+ @with_gemfile = File.exist?(@gemfile_path = File.join(@fs_root, "Gemfile"))
178
+ @with_lockfile = File.exist?(@lockfile_path = File.join(@fs_root, "Gemfile.lock"))
179
+ end
180
+ end
181
+
182
+ # Configure scenraio and do bundler resolution
183
+ class ScenarioManagerWithBundler < ScenarioManager
184
+ protected
185
+
186
+ def lookup_files
187
+ super
188
+ if @with_lockfile
189
+ update_bundler_version_from_lockfile(@lockfile_path)
190
+ elsif @with_gemfile
191
+ update_bundler_version_from_gemfile(@gemfile_path)
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ def store_compatible_bundler_version(requirement)
198
+ fetcher = Gem::SpecFetcher.fetcher
199
+ tuples = fetcher.detect(:released) do |name_tuple|
200
+ name_tuple.name == "bundler" && requirement.satisfied_by?(name_tuple.version)
201
+ end
202
+
203
+ Tebako.packaging_error 119 if tuples.empty?
204
+
205
+ # Get latest compatible version
206
+ @bundler_version = tuples.map { |tuple, _| tuple.version }.max.to_s
207
+ end
208
+
209
+ def update_bundler_version_from_gemfile(gemfile_path)
210
+ # Build definition without lockfile
211
+ definition = Bundler::Definition.build(gemfile_path, nil, nil)
212
+
213
+ # Get bundler dependency from Gemfile
214
+ bundler_dep = definition.dependencies.find { |d| d.name == "bundler" }
215
+
216
+ return unless bundler_dep
217
+
218
+ @needs_bundler = true
219
+ min_requirement = Gem::Requirement.create(">= #{Tebako::BUNDLER_VERSION}")
220
+ requirement = Gem::Requirement.create(bundler_dep.requirement, min_requirement)
221
+
222
+ store_compatible_bundler_version(requirement)
223
+ end
224
+
225
+ def update_bundler_version_from_lockfile(lockfile_path)
226
+ puts " ... using lockfile at #{@lockfile_path}"
227
+ Tebako.packaging_error 117 unless File.exist?(lockfile_path)
228
+
229
+ lockfile_content = File.read(lockfile_path)
230
+ Tebako.packaging_error 117 unless lockfile_content =~ /BUNDLED WITH\n\s+(#{Gem::Version::VERSION_PATTERN})\n/
231
+
232
+ @bundler_version = ::Regexp.last_match(1)
233
+ @needs_bundler = true
234
+
235
+ bundler_requirement = Gem::Requirement.new(">= #{BUNDLER_VERSION}")
236
+ return if bundler_requirement.satisfied_by?(Gem::Version.new(@bundler_version))
237
+
238
+ Tebako.packaging_error 118, " : #{@bundler_version} requested, #{BUNDLER_VERSION} minimum required"
120
239
  end
121
240
  end
122
241
  end
@@ -26,5 +26,5 @@
26
26
  # POSSIBILITY OF SUCH DAMAGE.
27
27
 
28
28
  module Tebako
29
- VERSION = "0.12.9"
29
+ VERSION = "0.12.10"
30
30
  end