ocran 1.3.15 → 1.3.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/ocran +2 -1314
- data/lib/ocran/build_constants.rb +16 -0
- data/lib/ocran/build_facade.rb +17 -0
- data/lib/ocran/build_helper.rb +105 -0
- data/lib/ocran/command_output.rb +22 -0
- data/lib/ocran/direction.rb +386 -0
- data/lib/ocran/file_path_set.rb +69 -0
- data/lib/ocran/gem_spec_queryable.rb +172 -0
- data/lib/ocran/host_config_helper.rb +37 -0
- data/lib/ocran/inno_setup_script_builder.rb +111 -0
- data/lib/ocran/launcher_batch_builder.rb +85 -0
- data/lib/ocran/library_detector.rb +61 -0
- data/lib/ocran/option.rb +273 -0
- data/lib/ocran/refine_pathname.rb +104 -0
- data/lib/ocran/runner.rb +105 -0
- data/lib/ocran/runtime_environment.rb +46 -0
- data/lib/ocran/stub_builder.rb +224 -0
- data/lib/ocran/version.rb +1 -1
- data/lib/ocran/windows_command_escaping.rb +15 -0
- data/lib/ocran.rb +3 -2
- data/share/ocran/edicon.exe +0 -0
- data/share/ocran/stub.exe +0 -0
- data/share/ocran/stubw.exe +0 -0
- metadata +23 -6
- /data/{share/ocran/empty-msys-2.0.dll → lib/ocran/empty_source} +0 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ocran
|
4
|
+
module BuildConstants
|
5
|
+
# Alias for the temporary directory where files are extracted.
|
6
|
+
EXTRACT_ROOT = Pathname.new("|")
|
7
|
+
# Directory for source files in temporary directory.
|
8
|
+
SRCDIR = Pathname.new("src")
|
9
|
+
# Directory for Ruby binaries in temporary directory.
|
10
|
+
BINDIR = Pathname.new("bin")
|
11
|
+
# Directory for gem files in temporary directory.
|
12
|
+
GEMDIR = Pathname.new("gems")
|
13
|
+
# Directory for Ruby library in temporary directory.
|
14
|
+
LIBDIR = Pathname.new("lib")
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ocran
|
4
|
+
class BuildFacade
|
5
|
+
def initialize(filer, launcher)
|
6
|
+
@filer, @launcher = filer, launcher
|
7
|
+
end
|
8
|
+
|
9
|
+
def mkdir(...) = @filer.__send__(__method__, ...)
|
10
|
+
|
11
|
+
def cp(...) = @filer.__send__(__method__, ...)
|
12
|
+
|
13
|
+
def export(...) = @launcher.__send__(__method__, ...)
|
14
|
+
|
15
|
+
def exec(...) = @launcher.__send__(__method__, ...)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
require_relative "refine_pathname"
|
4
|
+
require_relative "command_output"
|
5
|
+
require_relative "build_constants"
|
6
|
+
|
7
|
+
module Ocran
|
8
|
+
module BuildHelper
|
9
|
+
using RefinePathname
|
10
|
+
|
11
|
+
include BuildConstants, CommandOutput
|
12
|
+
|
13
|
+
EMPTY_SOURCE = File.expand_path("empty_source", __dir__).freeze
|
14
|
+
|
15
|
+
def mkdir(target)
|
16
|
+
verbose "mkdir #{target}"
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def cp(source, target)
|
21
|
+
verbose "cp #{source} #{target}"
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def exec(image, script, *argv)
|
26
|
+
args = argv.map { |s| replace_placeholder(s) }.join(" ")
|
27
|
+
verbose "exec #{image} #{script} #{args}"
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def export(name, value)
|
32
|
+
verbose "export #{name}=#{replace_placeholder(value)}"
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_placeholder(s)
|
37
|
+
s.to_s.gsub(EXTRACT_ROOT.to_s, "<tempdir>")
|
38
|
+
end
|
39
|
+
private :replace_placeholder
|
40
|
+
|
41
|
+
def copy_to_bin(source, target)
|
42
|
+
cp(source, BINDIR / target)
|
43
|
+
end
|
44
|
+
|
45
|
+
def copy_to_gem(source, target)
|
46
|
+
cp(source, GEMDIR / target)
|
47
|
+
end
|
48
|
+
|
49
|
+
def copy_to_lib(source, target)
|
50
|
+
cp(source, LIBDIR / target)
|
51
|
+
end
|
52
|
+
|
53
|
+
def duplicate_to_exec_prefix(source)
|
54
|
+
cp(source, Pathname(source).relative_path_from(HostConfigHelper.exec_prefix))
|
55
|
+
end
|
56
|
+
|
57
|
+
def duplicate_to_gem_home(source, gem_path)
|
58
|
+
copy_to_gem(source, Pathname(source).relative_path_from(gem_path))
|
59
|
+
end
|
60
|
+
|
61
|
+
def resolve_source_path(source, root_prefix)
|
62
|
+
source = Pathname(source)
|
63
|
+
|
64
|
+
if source.subpath?(HostConfigHelper.exec_prefix)
|
65
|
+
source.relative_path_from(HostConfigHelper.exec_prefix)
|
66
|
+
elsif source.subpath?(root_prefix)
|
67
|
+
SRCDIR / source.relative_path_from(root_prefix)
|
68
|
+
else
|
69
|
+
SRCDIR / source.basename
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets an environment variable with a joined path value.
|
74
|
+
# This method processes an array of path strings or Pathname objects, accepts
|
75
|
+
# absolute paths as is, and appends a placeholder to relative paths to convert
|
76
|
+
# them into absolute paths. The converted paths are then joined into a single
|
77
|
+
# string using the system's path separator.
|
78
|
+
#
|
79
|
+
# @param name [String] the name of the environment variable to set.
|
80
|
+
# @param paths [Array<String, Pathname>] an array of path arguments which can
|
81
|
+
# be either absolute or relative.
|
82
|
+
#
|
83
|
+
# Example:
|
84
|
+
# set_env_path("RUBYLIB", "lib", "ext", "vendor/lib")
|
85
|
+
# # This sets RUBYLIB to a string such as "C:/ProjectRoot/lib;C:/ProjectRoot/ext;C:/ProjectRoot/vendor/lib"
|
86
|
+
# # assuming each path is correctly converted to an absolute path through a placeholder.
|
87
|
+
#
|
88
|
+
def set_env_path(name, *paths)
|
89
|
+
value = paths.map { |path|
|
90
|
+
if File.absolute_path?(path)
|
91
|
+
path
|
92
|
+
else
|
93
|
+
File.join(EXTRACT_ROOT, path)
|
94
|
+
end
|
95
|
+
}.join(File::PATH_SEPARATOR)
|
96
|
+
|
97
|
+
export(name, value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def touch(target)
|
101
|
+
verbose "touch #{target}"
|
102
|
+
cp(EMPTY_SOURCE, target)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
load File.expand_path("../ocran.rb", __dir__)
|
3
|
+
|
4
|
+
module Ocran
|
5
|
+
module CommandOutput
|
6
|
+
def say(s)
|
7
|
+
puts "=== #{s}" unless Ocran.option&.quiet?
|
8
|
+
end
|
9
|
+
|
10
|
+
def verbose(s)
|
11
|
+
puts s if Ocran.option&.verbose?
|
12
|
+
end
|
13
|
+
|
14
|
+
def warning(s)
|
15
|
+
STDERR.puts "WARNING: #{s}" if Ocran.option&.warning?
|
16
|
+
end
|
17
|
+
|
18
|
+
def error(s)
|
19
|
+
STDERR.puts "ERROR: #{s}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "rbconfig"
|
3
|
+
require "pathname"
|
4
|
+
require_relative "refine_pathname"
|
5
|
+
require_relative "host_config_helper"
|
6
|
+
require_relative "command_output"
|
7
|
+
require_relative "build_constants"
|
8
|
+
|
9
|
+
module Ocran
|
10
|
+
class Direction
|
11
|
+
using RefinePathname
|
12
|
+
|
13
|
+
# Match the load path against standard library, site_ruby, and vendor_ruby paths
|
14
|
+
# This regular expression matches:
|
15
|
+
# - /ruby/3.0.0/
|
16
|
+
# - /ruby/site_ruby/3.0.0/
|
17
|
+
# - /ruby/vendor_ruby/3.0.0/
|
18
|
+
RUBY_LIBRARY_PATH_REGEX = %r{/(ruby/(?:site_ruby/|vendor_ruby/)?\d+\.\d+\.\d+)/?$}i
|
19
|
+
|
20
|
+
include BuildConstants, CommandOutput, HostConfigHelper
|
21
|
+
|
22
|
+
attr_reader :ruby_executable, :rubyopt
|
23
|
+
|
24
|
+
def initialize(post_env, pre_env, option)
|
25
|
+
@post_env, @pre_env, @option = post_env, pre_env, option
|
26
|
+
@ruby_executable = @option.windowed? ? rubyw_exe : ruby_exe
|
27
|
+
|
28
|
+
# Initializes @rubyopt with the user-intended RUBYOPT environment variable.
|
29
|
+
# This ensures that RUBYOPT matches the user's initial settings before any
|
30
|
+
# modifications that may occur during script execution.
|
31
|
+
@rubyopt = @option.rubyopt || pre_env.env["RUBYOPT"] || ""
|
32
|
+
|
33
|
+
# FIXME: Remove the absolute path to bundler/setup from RUBYOPT
|
34
|
+
# This is a temporary measure to ensure compatibility with self-extracting executables
|
35
|
+
# built in a bundle exec environment, particularly for Ruby 3.2 and later where
|
36
|
+
# absolute paths are included in RUBYOPT.
|
37
|
+
# In the future, we plan to implement a more appropriate solution.
|
38
|
+
@rubyopt = @rubyopt.gsub(%r(-r#{Regexp.escape(RbConfig::TOPDIR)}(/.*/bundler/setup)), "")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Resolves the common root directory prefix from an array of absolute paths.
|
42
|
+
# This method iterates over each file path, checking if they have a subpath
|
43
|
+
# that matches a given execution prefix.
|
44
|
+
def resolve_root_prefix(files)
|
45
|
+
files.inject(files.first.dirname) do |current_root, file|
|
46
|
+
next current_root if file.subpath?(exec_prefix)
|
47
|
+
|
48
|
+
current_root.ascend.find do |candidate_root|
|
49
|
+
path_from_root = file.relative_path_from(candidate_root)
|
50
|
+
rescue ArgumentError
|
51
|
+
raise "No common directory contains all specified files"
|
52
|
+
else
|
53
|
+
path_from_root.each_filename.first != ".."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# For RubyInstaller environments supporting Ruby 2.4 and above,
|
59
|
+
# this method checks for the existence of a required manifest file
|
60
|
+
def ruby_builtin_manifest
|
61
|
+
manifest_path = exec_prefix / "bin/ruby_builtin_dlls/ruby_builtin_dlls.manifest"
|
62
|
+
manifest_path.exist? ? manifest_path : nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def detect_dlls
|
66
|
+
require_relative "library_detector"
|
67
|
+
LibraryDetector.loaded_dlls.map { |path| Pathname.new(path).cleanpath }
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_gemspecs(features)
|
71
|
+
require_relative "gem_spec_queryable"
|
72
|
+
|
73
|
+
specs = []
|
74
|
+
# If a Bundler Gemfile was provided, add all gems it specifies
|
75
|
+
if @option.gemfile
|
76
|
+
say "Scanning Gemfile"
|
77
|
+
specs += GemSpecQueryable.scanning_gemfile(@option.gemfile).each do |spec|
|
78
|
+
verbose "From Gemfile, adding gem #{spec.full_name}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
if defined?(Gem)
|
82
|
+
specs += Gem.loaded_specs.values
|
83
|
+
# Now, we also detect gems that are not included in Gem.loaded_specs.
|
84
|
+
# Therefore, we look for any loaded file from a gem path.
|
85
|
+
specs += GemSpecQueryable.detect_gems_from(features, verbose: @option.verbose?)
|
86
|
+
end
|
87
|
+
# Prioritize the spec detected from Gemfile.
|
88
|
+
specs.uniq!(&:name)
|
89
|
+
specs
|
90
|
+
end
|
91
|
+
|
92
|
+
def normalized_features
|
93
|
+
features = @post_env.loaded_features.map { |feature| Pathname(feature) }
|
94
|
+
|
95
|
+
# Since https://github.com/rubygems/rubygems/commit/cad4cf16cf8fcc637d9da643ef97cf0be2ed63cb
|
96
|
+
# rubygems/core_ext/kernel_require.rb is evaled and thus missing in $LOADED_FEATURES, so we can't find it and need to add it manually
|
97
|
+
features.push(Pathname("rubygems/core_ext/kernel_require.rb"))
|
98
|
+
|
99
|
+
# Convert all relative paths to absolute paths before building.
|
100
|
+
# NOTE: In the future, different strategies may be needed before and after script execution.
|
101
|
+
features.filter_map do |feature|
|
102
|
+
if feature.absolute?
|
103
|
+
feature
|
104
|
+
elsif (load_path = @post_env.find_load_path(feature))
|
105
|
+
feature.expand_path(@post_env.expand_path(load_path))
|
106
|
+
else
|
107
|
+
# This message occurs when paths for core library files (e.g., enumerator.so,
|
108
|
+
# rational.so, complex.so, fiber.so, thread.rb, ruby2_keywords.rb) are not
|
109
|
+
# found. These are integral to Ruby's standard libraries or extensions and
|
110
|
+
# may not be located via normal load path searches, especially in RubyInstaller
|
111
|
+
# environments.
|
112
|
+
verbose "Load path not found for #{feature}, skip this feature"
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def construct(builder)
|
119
|
+
# Store the currently loaded files
|
120
|
+
features = normalized_features
|
121
|
+
|
122
|
+
say "Building #{@option.output_executable}"
|
123
|
+
require_relative "build_helper"
|
124
|
+
builder.extend(BuildHelper)
|
125
|
+
|
126
|
+
# Add the ruby executable and DLL
|
127
|
+
say "Adding ruby executable #{ruby_executable}"
|
128
|
+
builder.copy_to_bin(bindir / ruby_executable, ruby_executable)
|
129
|
+
if libruby_so
|
130
|
+
builder.copy_to_bin(bindir / libruby_so, libruby_so)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Add detected DLLs
|
134
|
+
if @option.auto_detect_dlls?
|
135
|
+
detect_dlls.each do |dll|
|
136
|
+
next unless dll.subpath?(exec_prefix) && dll.extname?(".dll") && dll.basename != libruby_so
|
137
|
+
|
138
|
+
say "Adding detected DLL #{dll}"
|
139
|
+
if dll.subpath?(exec_prefix)
|
140
|
+
builder.duplicate_to_exec_prefix(dll)
|
141
|
+
else
|
142
|
+
builder.copy_to_bin(dll, dll.basename)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Add external manifest files
|
148
|
+
if (manifest = ruby_builtin_manifest)
|
149
|
+
say "Adding external manifest #{manifest}"
|
150
|
+
builder.duplicate_to_exec_prefix(manifest)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Add extra DLLs specified on the command line
|
154
|
+
@option.extra_dlls.each do |dll|
|
155
|
+
say "Adding supplied DLL #{dll}"
|
156
|
+
builder.copy_to_bin(bindir / dll, dll)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Searches for features that are loaded from gems, then produces a
|
160
|
+
# list of files included in those gems' manifests. Also returns a
|
161
|
+
# list of original features that caused those gems to be included.
|
162
|
+
gem_files = find_gemspecs(features).flat_map do |spec|
|
163
|
+
spec_file = Pathname(spec.loaded_from)
|
164
|
+
# FIXME: From Ruby 3.2 onwards, launching Ruby with bundle exec causes
|
165
|
+
# Bundler's loaded_from to point to the root directory of the
|
166
|
+
# bundler gem, not returning the path to gemspec files. Here, we
|
167
|
+
# are only collecting gemspec files.
|
168
|
+
unless spec_file.file?
|
169
|
+
verbose "Gem #{spec.full_name} root folder was not found, skipping"
|
170
|
+
next []
|
171
|
+
end
|
172
|
+
|
173
|
+
# Add gemspec files
|
174
|
+
if spec_file.subpath?(exec_prefix)
|
175
|
+
builder.duplicate_to_exec_prefix(spec_file)
|
176
|
+
elsif (gem_path = GemSpecQueryable.find_gem_path(spec_file))
|
177
|
+
builder.duplicate_to_gem_home(spec_file, gem_path)
|
178
|
+
else
|
179
|
+
raise "Gem spec #{spec_file} does not exist in the Ruby installation. Don't know where to put it."
|
180
|
+
end
|
181
|
+
|
182
|
+
# Determine which set of files to include for this particular gem
|
183
|
+
include = GemSpecQueryable.gem_inclusion_set(spec.name, @option.gem_options)
|
184
|
+
say "Detected gem #{spec.full_name} (#{include.join(", ")})"
|
185
|
+
|
186
|
+
spec.extend(GemSpecQueryable)
|
187
|
+
|
188
|
+
actual_files = spec.find_gem_files(include, features)
|
189
|
+
say "\t#{actual_files.size} files, #{actual_files.sum(0, &:size)} bytes"
|
190
|
+
|
191
|
+
# Decide where to put gem files, either the system gem folder, or
|
192
|
+
# GEMDIR.
|
193
|
+
actual_files.each do |gemfile|
|
194
|
+
if gemfile.subpath?(exec_prefix)
|
195
|
+
builder.duplicate_to_exec_prefix(gemfile)
|
196
|
+
elsif (gem_path = GemSpecQueryable.find_gem_path(gemfile))
|
197
|
+
builder.duplicate_to_gem_home(gemfile, gem_path)
|
198
|
+
else
|
199
|
+
raise "Don't know where to put gemfile #{gemfile}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
actual_files
|
204
|
+
end
|
205
|
+
gem_files.uniq!
|
206
|
+
|
207
|
+
features -= gem_files
|
208
|
+
|
209
|
+
# If requested, add all ruby standard libraries
|
210
|
+
if @option.add_all_core?
|
211
|
+
say "Will include all ruby core libraries"
|
212
|
+
@pre_env.load_path.each do |load_path|
|
213
|
+
path = Pathname.new(load_path)
|
214
|
+
# Match the load path against standard library, site_ruby, and vendor_ruby paths
|
215
|
+
path.to_posix.match(RUBY_LIBRARY_PATH_REGEX) do |m|
|
216
|
+
subdir = m[1]
|
217
|
+
path.find.each do |src|
|
218
|
+
next if src.directory?
|
219
|
+
builder.copy_to_lib(src, Pathname(subdir) / src.relative_path_from(path))
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Include encoding support files
|
226
|
+
if @option.add_all_encoding?
|
227
|
+
@post_env.load_path.each do |load_path|
|
228
|
+
load_path = Pathname(@post_env.expand_path(load_path))
|
229
|
+
next unless load_path.subpath?(exec_prefix)
|
230
|
+
|
231
|
+
enc_dir = load_path / "enc"
|
232
|
+
next unless enc_dir.directory?
|
233
|
+
|
234
|
+
enc_files = enc_dir.find.select { |path| path.file? && path.extname?(".so") }
|
235
|
+
say "Including #{enc_files.size} encoding support files (#{enc_files.sum(0, &:size)} bytes, use --no-enc to exclude)"
|
236
|
+
enc_files.each do |path|
|
237
|
+
builder.duplicate_to_exec_prefix(path)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
else
|
241
|
+
say "Not including encoding support files"
|
242
|
+
end
|
243
|
+
|
244
|
+
# Workaround: RubyInstaller cannot find the msys folder if ../msys64/usr/bin/msys-2.0.dll is not present (since RubyInstaller-2.4.1 rubyinstaller 2 issue 23)
|
245
|
+
# Add an empty file to /msys64/usr/bin/msys-2.0.dll if the dll was not required otherwise
|
246
|
+
builder.touch('msys64/usr/bin/msys-2.0.dll')
|
247
|
+
|
248
|
+
# Find the source root and adjust paths
|
249
|
+
source_files = @option.source_files.dup
|
250
|
+
src_prefix = resolve_root_prefix(source_files)
|
251
|
+
|
252
|
+
# Find features and decide where to put them in the temporary
|
253
|
+
# directory layout.
|
254
|
+
src_load_path = []
|
255
|
+
# Add loaded libraries (features, gems)
|
256
|
+
say "Adding library files"
|
257
|
+
added_load_paths = (@post_env.load_path - @pre_env.load_path).map { |load_path| Pathname(@post_env.expand_path(load_path)) }
|
258
|
+
pre_working_directory = Pathname(@pre_env.pwd)
|
259
|
+
working_directory = Pathname(@post_env.pwd)
|
260
|
+
features.each do |feature|
|
261
|
+
load_path = @post_env.find_load_path(feature)
|
262
|
+
if load_path.nil?
|
263
|
+
source_files << feature
|
264
|
+
next
|
265
|
+
end
|
266
|
+
abs_load_path = Pathname(@post_env.expand_path(load_path))
|
267
|
+
if abs_load_path == pre_working_directory
|
268
|
+
source_files << feature
|
269
|
+
elsif feature.subpath?(exec_prefix)
|
270
|
+
# Features found in the Ruby installation are put in the
|
271
|
+
# temporary Ruby installation.
|
272
|
+
builder.duplicate_to_exec_prefix(feature)
|
273
|
+
elsif (gem_path = GemSpecQueryable.find_gem_path(feature))
|
274
|
+
# Features found in any other Gem path (e.g. ~/.gems) is put
|
275
|
+
# in a special 'gems' folder.
|
276
|
+
builder.duplicate_to_gem_home(feature, gem_path)
|
277
|
+
elsif feature.subpath?(src_prefix) || abs_load_path == working_directory
|
278
|
+
# Any feature found inside the src_prefix automatically gets
|
279
|
+
# added as a source file (to go in 'src').
|
280
|
+
source_files << feature
|
281
|
+
# Add the load path unless it was added by the script while
|
282
|
+
# running (or we assume that the script can also set it up
|
283
|
+
# correctly when running from the resulting executable).
|
284
|
+
src_load_path << abs_load_path unless added_load_paths.include?(abs_load_path)
|
285
|
+
elsif added_load_paths.include?(abs_load_path)
|
286
|
+
# Any feature that exist in a load path added by the script
|
287
|
+
# itself is added as a file to go into the 'src' (src_prefix
|
288
|
+
# will be adjusted below to point to the common parent).
|
289
|
+
source_files << feature
|
290
|
+
else
|
291
|
+
# All other feature that can not be resolved go in the the
|
292
|
+
# Ruby sitelibdir. This is automatically in the load path
|
293
|
+
# when Ruby starts.
|
294
|
+
inst_sitelibdir = sitelibdir.relative_path_from(exec_prefix)
|
295
|
+
builder.cp(feature, inst_sitelibdir / feature.relative_path_from(abs_load_path))
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Recompute the src_prefix. Files may have been added implicitly
|
300
|
+
# while scanning through features.
|
301
|
+
inst_src_prefix = resolve_root_prefix(source_files)
|
302
|
+
|
303
|
+
# Add explicitly mentioned files
|
304
|
+
say "Adding user-supplied source files"
|
305
|
+
source_files.each do |source|
|
306
|
+
target = builder.resolve_source_path(source, inst_src_prefix)
|
307
|
+
|
308
|
+
if source.directory?
|
309
|
+
builder.mkdir(target)
|
310
|
+
else
|
311
|
+
builder.cp(source, target)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# Set environment variable
|
316
|
+
builder.export("RUBYOPT", rubyopt)
|
317
|
+
# Add the load path that are required with the correct path after
|
318
|
+
# src_prefix was adjusted.
|
319
|
+
load_path = src_load_path.map { |path| SRCDIR / path.relative_path_from(inst_src_prefix) }.uniq
|
320
|
+
builder.set_env_path("RUBYLIB", *load_path)
|
321
|
+
builder.set_env_path("GEM_PATH", GEMDIR)
|
322
|
+
|
323
|
+
# Add the opcode to launch the script
|
324
|
+
installed_ruby_exe = BINDIR / ruby_executable
|
325
|
+
target_script = builder.resolve_source_path(@option.script, inst_src_prefix)
|
326
|
+
builder.exec(installed_ruby_exe, target_script, *@option.argv)
|
327
|
+
end
|
328
|
+
|
329
|
+
def to_proc
|
330
|
+
method(:construct).to_proc
|
331
|
+
end
|
332
|
+
|
333
|
+
def build_inno_setup_installer
|
334
|
+
require_relative "inno_setup_script_builder"
|
335
|
+
iss_builder = InnoSetupScriptBuilder.new(@option.inno_setup_script)
|
336
|
+
|
337
|
+
require_relative "launcher_batch_builder"
|
338
|
+
launcher_builder = LauncherBatchBuilder.new(
|
339
|
+
chdir_before: @option.chdir_before?,
|
340
|
+
title: @option.output_executable.basename.sub_ext("")
|
341
|
+
)
|
342
|
+
|
343
|
+
require_relative "build_facade"
|
344
|
+
builder = BuildFacade.new(iss_builder, launcher_builder)
|
345
|
+
|
346
|
+
if @option.icon_filename
|
347
|
+
builder.cp(@option.icon_filename, File.basename(@option.icon_filename))
|
348
|
+
end
|
349
|
+
|
350
|
+
construct(builder)
|
351
|
+
|
352
|
+
say "Build launcher batch file"
|
353
|
+
launcher_path = launcher_builder.build
|
354
|
+
verbose File.read(launcher_path)
|
355
|
+
builder.cp(launcher_path, "launcher.bat")
|
356
|
+
|
357
|
+
say "Build inno setup script file"
|
358
|
+
iss_path = iss_builder.build
|
359
|
+
verbose File.read(iss_path)
|
360
|
+
|
361
|
+
say "Running Inno Setup Command-Line compiler (ISCC)"
|
362
|
+
iss_builder.compile(verbose: @option.verbose?)
|
363
|
+
|
364
|
+
say "Finished building installer file"
|
365
|
+
end
|
366
|
+
|
367
|
+
def build_stab_exe
|
368
|
+
require_relative "stub_builder"
|
369
|
+
|
370
|
+
if @option.enable_debug_mode?
|
371
|
+
say "Enabling debug mode in executable"
|
372
|
+
end
|
373
|
+
|
374
|
+
StubBuilder.new(@option.output_executable,
|
375
|
+
chdir_before: @option.chdir_before?,
|
376
|
+
debug_extract: @option.enable_debug_extract?,
|
377
|
+
debug_mode: @option.enable_debug_mode?,
|
378
|
+
enable_compression: @option.enable_compression?,
|
379
|
+
gui_mode: @option.windowed?,
|
380
|
+
icon_path: @option.icon_filename,
|
381
|
+
&to_proc) => builder
|
382
|
+
say "Finished building #{@option.output_executable} (#{@option.output_executable.size} bytes)"
|
383
|
+
say "After decompression, the data will expand to #{builder.data_size} bytes."
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
require_relative "refine_pathname"
|
4
|
+
|
5
|
+
module Ocran
|
6
|
+
class FilePathSet
|
7
|
+
using RefinePathname
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@set = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(source, target)
|
15
|
+
add?(source, target)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# Adds a source and target path pair to the set and validates the paths before adding.
|
20
|
+
# This method performs various checks to ensure the source path is an absolute path
|
21
|
+
# and the target path is a relative path that does not include '.' or '..'.
|
22
|
+
# If a conflict is detected (i.e., different source for the same target),
|
23
|
+
# it raises an exception.
|
24
|
+
#
|
25
|
+
# @param [String, Pathname] source - The source file path; must be an absolute path.
|
26
|
+
# @param [String, Pathname] target - The target file path; must be a relative path.
|
27
|
+
# @return [self, nil] Returns self if the path pair is added successfully,
|
28
|
+
# returns nil if the same source and target pair is already present.
|
29
|
+
# @raise [ArgumentError] If the source is not an absolute path, if the target is not a relative path,
|
30
|
+
# or if the target includes '.' or '..'.
|
31
|
+
# @raise [RuntimeError] If a conflicting source is found for the same target.
|
32
|
+
def add?(source, target)
|
33
|
+
source = Pathname.new(source) unless source.is_a?(Pathname)
|
34
|
+
source = source.cleanpath
|
35
|
+
unless source.absolute?
|
36
|
+
raise ArgumentError, "Source path must be absolute, given: #{source}"
|
37
|
+
end
|
38
|
+
|
39
|
+
target = Pathname.new(target) unless target.is_a?(Pathname)
|
40
|
+
target = target.cleanpath
|
41
|
+
unless target.relative?
|
42
|
+
raise ArgumentError, "Target path must be relative, given: #{target}"
|
43
|
+
end
|
44
|
+
if %w(. ..).include?(target.each_filename.first)
|
45
|
+
raise ArgumentError, "Relative paths such as '.' or '..' are not allowed, given: #{target}"
|
46
|
+
end
|
47
|
+
|
48
|
+
if (path = @set[target])
|
49
|
+
if path.eql?(source)
|
50
|
+
return nil
|
51
|
+
else
|
52
|
+
raise "Conflicting sources for the same target. Target: #{target}, Existing Source: #{path}, Given Source: #{source}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@set[target] = source
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def each
|
61
|
+
return to_enum(__method__) unless block_given?
|
62
|
+
@set.each { |target, source| yield(source, target) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_a
|
66
|
+
each.to_a
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|