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.
- checksums.yaml +4 -4
- data/CMakeLists.txt +3 -3
- data/README.adoc +26 -12
- data/exe/tebako-packager +10 -11
- data/lib/tebako/build_helpers.rb +1 -14
- data/lib/tebako/cli.rb +28 -7
- data/lib/tebako/cli_helpers.rb +2 -2
- data/lib/tebako/codegen.rb +21 -20
- data/lib/tebako/deploy_helper.rb +18 -30
- data/lib/tebako/error.rb +3 -0
- data/lib/tebako/options_manager.rb +5 -27
- data/lib/tebako/packager/pass1_patch.rb +11 -60
- data/lib/tebako/packager/pass2_patch.rb +154 -0
- data/lib/tebako/packager/patch_buildsystem.rb +10 -6
- data/lib/tebako/packager/patch_helpers.rb +0 -12
- data/lib/tebako/packager/rubygems_patch.rb +83 -0
- data/lib/tebako/packager.rb +20 -18
- data/lib/tebako/ruby_builder.rb +1 -1
- data/lib/tebako/ruby_version.rb +6 -6
- data/lib/tebako/scenario_manager.rb +159 -40
- data/lib/tebako/version.rb +1 -1
- data/tebako.gemspec +2 -0
- metadata +32 -3
- data/lib/tebako/packager/pass2.rb +0 -149
@@ -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(
|
81
|
-
if
|
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(
|
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(
|
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(
|
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(
|
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
|
data/lib/tebako/packager.rb
CHANGED
@@ -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/
|
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
|
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 =
|
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
|
-
|
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(
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
data/lib/tebako/ruby_builder.rb
CHANGED
data/lib/tebako/ruby_version.rb
CHANGED
@@ -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
|
-
|
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 #{
|
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
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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 :
|
54
|
+
attr_reader :fs_mount_point, :exe_suffix
|
44
55
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
53
|
-
|
65
|
+
def linux?
|
66
|
+
@linux
|
54
67
|
end
|
55
68
|
|
56
|
-
def
|
57
|
-
|
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
|
84
|
+
@macos
|
62
85
|
end
|
63
86
|
|
64
87
|
def msys?
|
65
|
-
@msys
|
88
|
+
@msys
|
66
89
|
end
|
67
90
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
@fs_entrance = Pathname.new(fs_entrance).cleanpath.to_s
|
91
|
+
def musl?
|
92
|
+
@musl
|
93
|
+
end
|
72
94
|
|
73
|
-
|
74
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@
|
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
|
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
|
data/lib/tebako/version.rb
CHANGED