ocran 1.3.15 → 1.3.17
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/CHANGELOG.txt +272 -0
- data/LICENSE.txt +23 -0
- data/README.md +531 -0
- data/exe/ocran +5 -0
- 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 +43 -9
- data/bin/ocran +0 -1317
- /data/{share/ocran/empty-msys-2.0.dll → lib/ocran/empty_source} +0 -0
data/bin/ocran
DELETED
@@ -1,1317 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# -*- ruby -*-
|
3
|
-
# encoding: UTF-8
|
4
|
-
|
5
|
-
module Ocran
|
6
|
-
# Path handling class. Ruby's Pathname class is not used because it
|
7
|
-
# is case sensitive and doesn't handle paths with mixed path
|
8
|
-
# separators.
|
9
|
-
class Pathname
|
10
|
-
def Pathname.pwd
|
11
|
-
Pathname.new(Dir.pwd.encode("UTF-8"))
|
12
|
-
end
|
13
|
-
|
14
|
-
def Pathname.pathequal(a, b)
|
15
|
-
a.downcase == b.downcase
|
16
|
-
end
|
17
|
-
|
18
|
-
def Pathname.glob(pattern, flags = 0)
|
19
|
-
if block_given?
|
20
|
-
Dir.glob(pattern, flags) { |s| yield Pathname.new(s) }
|
21
|
-
else
|
22
|
-
ary = Dir.glob(pattern, flags)
|
23
|
-
ary.map! { |s| Pathname.new(s) }
|
24
|
-
ary
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
attr_reader :path
|
29
|
-
if File::ALT_SEPARATOR.nil?
|
30
|
-
File::ALT_SEPARATOR = "\\"
|
31
|
-
end
|
32
|
-
SEPARATOR_PAT = /[#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}]/ # }
|
33
|
-
ABSOLUTE_PAT = /\A([A-Z]:)?#{SEPARATOR_PAT}/i
|
34
|
-
|
35
|
-
def initialize(path)
|
36
|
-
@path = path.encode("UTF-8")
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_native
|
40
|
-
@path.tr File::SEPARATOR, File::ALT_SEPARATOR
|
41
|
-
end
|
42
|
-
|
43
|
-
def to_posix
|
44
|
-
@path.tr File::ALT_SEPARATOR, File::SEPARATOR
|
45
|
-
end
|
46
|
-
|
47
|
-
# Compute the relative path from the 'src' path (directory) to 'tgt'
|
48
|
-
# (directory or file). Return the absolute path to 'tgt' if it can't
|
49
|
-
# be reached from 'src'.
|
50
|
-
def relative_path_from(other)
|
51
|
-
a = @path.split(SEPARATOR_PAT)
|
52
|
-
b = other.to_s.split(SEPARATOR_PAT)
|
53
|
-
while a.first && b.first && Pathname.pathequal(a.first, b.first)
|
54
|
-
a.shift
|
55
|
-
b.shift
|
56
|
-
end
|
57
|
-
return other if b.first && Pathname.new(b.first).absolute?
|
58
|
-
b.size.times { a.unshift ".." }
|
59
|
-
Pathname.new(File.join(*a))
|
60
|
-
end
|
61
|
-
|
62
|
-
# Determines if 'src' is contained in 'tgt' (i.e. it is a subpath of
|
63
|
-
# 'tgt'). Both must be absolute paths and not contain '..'
|
64
|
-
def subpath?(other)
|
65
|
-
other = Pathname.new(other)
|
66
|
-
src_normalized = to_posix.downcase
|
67
|
-
tgt_normalized = other.to_posix.downcase
|
68
|
-
src_normalized =~ /^#{Regexp.escape tgt_normalized}#{SEPARATOR_PAT}/i
|
69
|
-
end
|
70
|
-
|
71
|
-
# Join two pathnames together. Returns the right-hand side if it
|
72
|
-
# is an absolute path. Otherwise, returns the full path of the
|
73
|
-
# left + right.
|
74
|
-
def /(other)
|
75
|
-
other = Pathname.new(other)
|
76
|
-
if other.absolute?
|
77
|
-
other
|
78
|
-
else
|
79
|
-
Pathname.new(File.join(@path, other.to_s))
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def append_to_filename!(s)
|
84
|
-
@path = sub(/(\.[^.]*?|)$/) { s.to_s + $1 }
|
85
|
-
end
|
86
|
-
|
87
|
-
def sub(*a, &b)
|
88
|
-
Pathname.new(@path.sub(*a, &b))
|
89
|
-
end
|
90
|
-
|
91
|
-
def sub_ext(new_ext)
|
92
|
-
sub(/(\.[^.]*?)?$/, new_ext)
|
93
|
-
end
|
94
|
-
|
95
|
-
def extname
|
96
|
-
File.extname(@path)
|
97
|
-
end
|
98
|
-
|
99
|
-
def ext(new_ext = nil)
|
100
|
-
if new_ext
|
101
|
-
sub_ext(new_ext)
|
102
|
-
else
|
103
|
-
extname
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def ext?(expected_ext)
|
108
|
-
Pathname.pathequal(extname, expected_ext)
|
109
|
-
end
|
110
|
-
|
111
|
-
def entries
|
112
|
-
Dir.entries(@path, encoding: "UTF-8").map { |e| self / e }
|
113
|
-
end
|
114
|
-
|
115
|
-
# Recursively find all files which match a specified regular
|
116
|
-
# expression.
|
117
|
-
def find_all_files(re = //)
|
118
|
-
entries.map do |pn|
|
119
|
-
if pn.directory?
|
120
|
-
if pn.basename =~ /^\.\.?$/
|
121
|
-
[]
|
122
|
-
else
|
123
|
-
pn.find_all_files(re)
|
124
|
-
end
|
125
|
-
elsif pn.file?
|
126
|
-
if pn.basename =~ re
|
127
|
-
pn
|
128
|
-
else
|
129
|
-
[]
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end.flatten
|
133
|
-
end
|
134
|
-
|
135
|
-
def ==(other); to_posix.downcase == other.to_posix.downcase; end
|
136
|
-
alias === ==
|
137
|
-
alias eql? ==
|
138
|
-
def =~(o); @path =~ o; end
|
139
|
-
def <=>(other); @path.casecmp(other.to_s); end
|
140
|
-
def exist?; File.exist?(@path); end
|
141
|
-
def file?; File.file?(@path); end
|
142
|
-
def directory?; File.directory?(@path); end
|
143
|
-
def absolute?; @path =~ ABSOLUTE_PAT; end
|
144
|
-
def dirname; Pathname.new(File.dirname(@path)); end
|
145
|
-
def basename; Pathname.new(File.basename(@path)); end
|
146
|
-
|
147
|
-
def expand_path(dir = ".")
|
148
|
-
Pathname.new(File.expand_path(@path, dir))
|
149
|
-
end
|
150
|
-
|
151
|
-
def expand(dir = nil); dir ? expand_path(dir) : expand_path; end
|
152
|
-
def size; File.size(@path); end
|
153
|
-
def encode(e); to_posix.encode(e); end # called when creating an installer with innosetup
|
154
|
-
|
155
|
-
def binread(*args)
|
156
|
-
File.binread(@path, *args)
|
157
|
-
end
|
158
|
-
|
159
|
-
def parent
|
160
|
-
Pathname.new(File.basename(File.dirname(@path)))
|
161
|
-
end
|
162
|
-
|
163
|
-
alias to_path to_posix
|
164
|
-
|
165
|
-
alias to_s to_posix
|
166
|
-
alias to_str to_posix
|
167
|
-
end
|
168
|
-
|
169
|
-
# Type conversion for the Pathname class. Works with Pathname,
|
170
|
-
# String, NilClass and arrays of any of these.
|
171
|
-
def self.Pathname(obj)
|
172
|
-
case obj
|
173
|
-
when Pathname
|
174
|
-
obj
|
175
|
-
when Array
|
176
|
-
obj.map { |x| Pathname(x) }
|
177
|
-
when String
|
178
|
-
Pathname.new(obj)
|
179
|
-
when NilClass
|
180
|
-
nil
|
181
|
-
else
|
182
|
-
raise ArgumentError, obj
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Variables describing the host's build environment.
|
187
|
-
module Host
|
188
|
-
class << self
|
189
|
-
def exec_prefix
|
190
|
-
@exec_prefix ||= Ocran.Pathname(RbConfig::CONFIG["exec_prefix"])
|
191
|
-
end
|
192
|
-
|
193
|
-
def sitelibdir
|
194
|
-
@sitelibdir ||= Ocran.Pathname(RbConfig::CONFIG["sitelibdir"])
|
195
|
-
end
|
196
|
-
|
197
|
-
def bindir
|
198
|
-
@bindir ||= Ocran.Pathname(RbConfig::CONFIG["bindir"])
|
199
|
-
end
|
200
|
-
|
201
|
-
def libruby_so
|
202
|
-
@libruby_so ||= Ocran.Pathname(RbConfig::CONFIG["LIBRUBY_SO"])
|
203
|
-
end
|
204
|
-
|
205
|
-
def exeext
|
206
|
-
RbConfig::CONFIG["EXEEXT"] || ".exe"
|
207
|
-
end
|
208
|
-
|
209
|
-
def rubyw_exe
|
210
|
-
@rubyw_exe ||= (RbConfig::CONFIG["rubyw_install_name"] || "rubyw") + exeext
|
211
|
-
end
|
212
|
-
|
213
|
-
def ruby_exe
|
214
|
-
@ruby_exe ||= (RbConfig::CONFIG["ruby_install_name"] || "ruby") + exeext
|
215
|
-
end
|
216
|
-
|
217
|
-
def tempdir
|
218
|
-
@tempdir ||= Ocran.Pathname(ENV["TEMP"])
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Sorts and returns an array without duplicates. Works with complex
|
224
|
-
# objects (such as Pathname), in contrast to Array#uniq.
|
225
|
-
def self.sort_uniq(a)
|
226
|
-
a.sort.inject([]) { |r, e| r.last == e ? r : r << e }
|
227
|
-
end
|
228
|
-
|
229
|
-
VERSION = "1.3.15"
|
230
|
-
|
231
|
-
IGNORE_MODULE_NAMES = /\/(enumerator.so|rational.so|complex.so|fiber.so|thread.rb|ruby2_keywords.rb)$/
|
232
|
-
|
233
|
-
GEM_SCRIPT_RE = /\.rbw?$/
|
234
|
-
GEM_EXTRA_RE = %r{(
|
235
|
-
# Auxiliary files in the root of the gem
|
236
|
-
^(\.\/)?(History|Install|Manifest|README|CHANGES|Licen[sc]e|Contributors|ChangeLog|BSD|GPL).*$ |
|
237
|
-
# Installation files in the root of the gem
|
238
|
-
^(\.\/)?(Rakefile|setup.rb|extconf.rb)$ |
|
239
|
-
# Documentation/test directories in the root of the gem
|
240
|
-
^(\.\/)?(doc|ext|examples|test|tests|benchmarks|spec)\/ |
|
241
|
-
# Directories anywhere
|
242
|
-
(^|\/)(\.autotest|\.svn|\.cvs|\.git)(\/|$) |
|
243
|
-
# Unlikely extensions
|
244
|
-
\.(rdoc|c|cpp|c\+\+|cxx|h|hxx|hpp|obj|o|a)$
|
245
|
-
)}xi
|
246
|
-
|
247
|
-
GEM_NON_FILE_RE = /(#{GEM_EXTRA_RE}|#{GEM_SCRIPT_RE})/
|
248
|
-
|
249
|
-
# Alias for the temporary directory where files are extracted.
|
250
|
-
TEMPDIR_ROOT = Pathname.new("|")
|
251
|
-
# Directory for source files in temporary directory.
|
252
|
-
SRCDIR = Pathname.new("src")
|
253
|
-
# Directory for Ruby binaries in temporary directory.
|
254
|
-
BINDIR = Pathname.new("bin")
|
255
|
-
# Directory for GEMHOME files in temporary directory.
|
256
|
-
GEMHOMEDIR = Pathname.new("gemhome")
|
257
|
-
|
258
|
-
IGNORE_MODULES = []
|
259
|
-
|
260
|
-
@options = {
|
261
|
-
:lzma_mode => true,
|
262
|
-
:extra_dlls => [],
|
263
|
-
:files => [],
|
264
|
-
:run_script => true,
|
265
|
-
:add_all_core => false,
|
266
|
-
:output_override => nil,
|
267
|
-
:load_autoload => true,
|
268
|
-
:chdir_first => false,
|
269
|
-
:force_windows => false,
|
270
|
-
:force_console => false,
|
271
|
-
:icon_filename => nil,
|
272
|
-
:rubyopt => nil,
|
273
|
-
:gemfile => nil,
|
274
|
-
:inno_script => nil,
|
275
|
-
:quiet => false,
|
276
|
-
:verbose => false,
|
277
|
-
:autodll => true,
|
278
|
-
:show_warnings => true,
|
279
|
-
:debug => false,
|
280
|
-
:debug_extract => false,
|
281
|
-
:arg => [],
|
282
|
-
:enc => true,
|
283
|
-
:gem => [],
|
284
|
-
}
|
285
|
-
|
286
|
-
@options.each_key { |opt| eval("def self.#{opt}; @options[:#{opt}]; end") }
|
287
|
-
|
288
|
-
class << self
|
289
|
-
attr_reader :lzmapath
|
290
|
-
attr_reader :ediconpath
|
291
|
-
attr_reader :stubimage
|
292
|
-
attr_reader :stubwimage
|
293
|
-
end
|
294
|
-
|
295
|
-
def Ocran.msg(s)
|
296
|
-
puts "=== #{s}" unless Ocran.quiet
|
297
|
-
end
|
298
|
-
|
299
|
-
def Ocran.verbose_msg(s)
|
300
|
-
puts s if Ocran.verbose and not Ocran.quiet
|
301
|
-
end
|
302
|
-
|
303
|
-
def Ocran.warn(s)
|
304
|
-
msg "WARNING: #{s}" if Ocran.show_warnings
|
305
|
-
end
|
306
|
-
|
307
|
-
def Ocran.fatal_error(s)
|
308
|
-
puts "ERROR: #{s}"
|
309
|
-
exit 1
|
310
|
-
end
|
311
|
-
|
312
|
-
def Ocran.save_environment
|
313
|
-
@load_path_before = $LOAD_PATH.dup
|
314
|
-
@pwd_before = Dir.pwd
|
315
|
-
@env_before = ENV.to_hash
|
316
|
-
end
|
317
|
-
|
318
|
-
def Ocran.restore_environment
|
319
|
-
ENV.clear.update(@env_before)
|
320
|
-
Dir.chdir @pwd_before
|
321
|
-
end
|
322
|
-
|
323
|
-
def Ocran.find_stubs
|
324
|
-
ocranpath = Pathname(File.dirname(__FILE__))
|
325
|
-
@stubimage = (ocranpath / "../share/ocran/stub.exe").binread
|
326
|
-
@stubwimage = (ocranpath / "../share/ocran/stubw.exe").binread
|
327
|
-
@lzmapath = (ocranpath / "../share/ocran/lzma.exe").expand_path
|
328
|
-
@ediconpath = (ocranpath / "../share/ocran/edicon.exe").expand_path
|
329
|
-
end
|
330
|
-
|
331
|
-
def Ocran.parseargs(argv)
|
332
|
-
usage = <<EOF
|
333
|
-
ocran [options] script.rb
|
334
|
-
|
335
|
-
Ocran options:
|
336
|
-
|
337
|
-
--help Display this information.
|
338
|
-
--quiet Suppress output while building executable.
|
339
|
-
--verbose Show extra output while building executable.
|
340
|
-
--version Display version number and exit.
|
341
|
-
|
342
|
-
Packaging options:
|
343
|
-
|
344
|
-
--dll dllname Include additional DLLs from the Ruby bindir.
|
345
|
-
--add-all-core Add all core ruby libraries to the executable.
|
346
|
-
--gemfile <file> Add all gems and dependencies listed in a Bundler Gemfile.
|
347
|
-
--no-enc Exclude encoding support files
|
348
|
-
|
349
|
-
Gem content detection modes:
|
350
|
-
|
351
|
-
--gem-minimal[=gem1,..] Include only loaded scripts
|
352
|
-
--gem-guess=[gem1,...] Include loaded scripts & best guess (DEFAULT)
|
353
|
-
--gem-all[=gem1,..] Include all scripts & files
|
354
|
-
--gem-full[=gem1,..] Include EVERYTHING
|
355
|
-
--gem-spec[=gem1,..] Include files in gemspec (Does not work with Rubygems 1.7+)
|
356
|
-
|
357
|
-
minimal: loaded scripts
|
358
|
-
guess: loaded scripts and other files
|
359
|
-
all: loaded scripts, other scripts, other files (except extras)
|
360
|
-
full: Everything found in the gem directory
|
361
|
-
|
362
|
-
--[no-]gem-scripts[=..] Other script files than those loaded
|
363
|
-
--[no-]gem-files[=..] Other files (e.g. data files)
|
364
|
-
--[no-]gem-extras[=..] Extra files (README, etc.)
|
365
|
-
|
366
|
-
scripts: .rb/.rbw files
|
367
|
-
extras: C/C++ sources, object files, test, spec, README
|
368
|
-
files: all other files
|
369
|
-
|
370
|
-
Auto-detection options:
|
371
|
-
|
372
|
-
--no-dep-run Don't run script.rb to check for dependencies.
|
373
|
-
--no-autoload Don't load/include script.rb's autoloads.
|
374
|
-
--no-autodll Disable detection of runtime DLL dependencies.
|
375
|
-
|
376
|
-
Output options:
|
377
|
-
|
378
|
-
--output <file> Name the exe to generate. Defaults to ./<scriptname>.exe.
|
379
|
-
--no-lzma Disable LZMA compression of the executable.
|
380
|
-
--innosetup <file> Use given Inno Setup script (.iss) to create an installer.
|
381
|
-
|
382
|
-
Executable options:
|
383
|
-
|
384
|
-
--windows Force Windows application (rubyw.exe)
|
385
|
-
--console Force console application (ruby.exe)
|
386
|
-
--chdir-first When exe starts, change working directory to app dir.
|
387
|
-
--icon <ico> Replace icon with a custom one.
|
388
|
-
--rubyopt <str> Set the RUBYOPT environment variable when running the executable
|
389
|
-
--debug Executable will be verbose.
|
390
|
-
--debug-extract Executable will unpack to local dir and not delete after.
|
391
|
-
EOF
|
392
|
-
|
393
|
-
while arg = argv.shift
|
394
|
-
case arg
|
395
|
-
when /\A--(no-)?lzma\z/
|
396
|
-
@options[:lzma_mode] = !$1
|
397
|
-
when /\A--no-dep-run\z/
|
398
|
-
@options[:run_script] = false
|
399
|
-
when /\A--add-all-core\z/
|
400
|
-
@options[:add_all_core] = true
|
401
|
-
when /\A--output\z/
|
402
|
-
path = argv.shift
|
403
|
-
@options[:output_override] = Pathname.new(path) if path
|
404
|
-
when /\A--dll\z/
|
405
|
-
path = argv.shift
|
406
|
-
@options[:extra_dlls] << path if path
|
407
|
-
when /\A--quiet\z/
|
408
|
-
@options[:quiet] = true
|
409
|
-
when /\A--verbose\z/
|
410
|
-
@options[:verbose] = true
|
411
|
-
when /\A--windows\z/
|
412
|
-
@options[:force_windows] = true
|
413
|
-
when /\A--console\z/
|
414
|
-
@options[:force_console] = true
|
415
|
-
when /\A--no-autoload\z/
|
416
|
-
@options[:load_autoload] = false
|
417
|
-
when /\A--chdir-first\z/
|
418
|
-
@options[:chdir_first] = true
|
419
|
-
when /\A--icon\z/
|
420
|
-
path = argv.shift
|
421
|
-
Ocran.fatal_error "Icon file #{path} not found.\n" unless path && File.exist?(path)
|
422
|
-
@options[:icon_filename] = Pathname.new(path)
|
423
|
-
when /\A--rubyopt\z/
|
424
|
-
@options[:rubyopt] = argv.shift
|
425
|
-
when /\A--gemfile\z/
|
426
|
-
path = argv.shift
|
427
|
-
Ocran.fatal_error "Gemfile #{path} not found.\n" unless path && File.exist?(path)
|
428
|
-
@options[:gemfile] = Pathname.new(path)
|
429
|
-
when /\A--innosetup\z/
|
430
|
-
path = argv.shift
|
431
|
-
Ocran.fatal_error "Inno Script #{path} not found.\n" unless path && File.exist?(path)
|
432
|
-
@options[:inno_script] = Pathname.new(path)
|
433
|
-
when /\A--no-autodll\z/
|
434
|
-
@options[:autodll] = false
|
435
|
-
when /\A--version\z/
|
436
|
-
puts "Ocran #{VERSION}"
|
437
|
-
exit 0
|
438
|
-
when /\A--no-warnings\z/
|
439
|
-
@options[:show_warnings] = false
|
440
|
-
when /\A--debug\z/
|
441
|
-
@options[:debug] = true
|
442
|
-
when /\A--debug-extract\z/
|
443
|
-
@options[:debug_extract] = true
|
444
|
-
when /\A--\z/
|
445
|
-
@options[:arg] = ARGV.dup
|
446
|
-
ARGV.clear
|
447
|
-
when /\A--(no-)?enc\z/
|
448
|
-
@options[:enc] = !$1
|
449
|
-
when /\A--(no-)?gem-(\w+)(?:=(.*))?$/
|
450
|
-
negate, group, list = $1, $2, $3
|
451
|
-
@options[:gem] << [negate, group.to_sym, list&.split(",")] if group
|
452
|
-
when /\A--help\z/, /\A--./
|
453
|
-
puts usage
|
454
|
-
exit 0
|
455
|
-
else
|
456
|
-
path = arg.dup
|
457
|
-
|
458
|
-
if !File.exist?(path) || Dir.empty?(path)
|
459
|
-
Ocran.fatal_error "#{path} not found!"
|
460
|
-
end
|
461
|
-
|
462
|
-
@options[:files] << path
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
if Ocran.debug_extract && Ocran.inno_script
|
467
|
-
Ocran.fatal_error "The --debug-extract option conflicts with use of Inno Setup"
|
468
|
-
end
|
469
|
-
|
470
|
-
if Ocran.lzma_mode && Ocran.inno_script
|
471
|
-
Ocran.fatal_error "LZMA compression must be disabled (--no-lzma) when using Inno Setup"
|
472
|
-
end
|
473
|
-
|
474
|
-
if !Ocran.chdir_first && Ocran.inno_script
|
475
|
-
Ocran.fatal_error "Chdir-first mode must be enabled (--chdir-first) when using Inno Setup"
|
476
|
-
end
|
477
|
-
|
478
|
-
if files.empty?
|
479
|
-
puts usage
|
480
|
-
exit 1
|
481
|
-
end
|
482
|
-
|
483
|
-
@options[:files].map! { |path|
|
484
|
-
path = path.encode("UTF-8").tr("\\", "/")
|
485
|
-
if File.directory?(path)
|
486
|
-
# If a directory is passed, we want all files under that directory
|
487
|
-
path = "#{path}/**/*"
|
488
|
-
end
|
489
|
-
Pathname.glob(path).map!(&:expand_path)
|
490
|
-
}.flatten!
|
491
|
-
end
|
492
|
-
|
493
|
-
def Ocran.init(argv)
|
494
|
-
save_environment
|
495
|
-
parseargs(argv)
|
496
|
-
find_stubs
|
497
|
-
IGNORE_MODULES.push(*ObjectSpace.each_object(Module).to_a)
|
498
|
-
end
|
499
|
-
|
500
|
-
# Force loading autoloaded constants. Searches through all modules
|
501
|
-
# (and hence classes), and checks their constants for autoloaded
|
502
|
-
# ones, then attempts to load them.
|
503
|
-
def Ocran.attempt_load_autoload
|
504
|
-
modules_checked = {}
|
505
|
-
IGNORE_MODULES.each { |m| modules_checked[m] = true }
|
506
|
-
loop do
|
507
|
-
modules_to_check = []
|
508
|
-
ObjectSpace.each_object(Module) do |mod|
|
509
|
-
modules_to_check << mod unless modules_checked.include?(mod)
|
510
|
-
end
|
511
|
-
break if modules_to_check.empty?
|
512
|
-
modules_to_check.each do |mod|
|
513
|
-
modules_checked[mod] = true
|
514
|
-
mod.constants.each do |const|
|
515
|
-
if mod.autoload?(const)
|
516
|
-
Ocran.msg "Attempting to trigger autoload of #{mod}::#{const}"
|
517
|
-
begin
|
518
|
-
mod.const_get(const)
|
519
|
-
rescue NameError
|
520
|
-
Ocran.warn "#{mod}::#{const} was defined autoloadable, but caused NameError"
|
521
|
-
rescue LoadError
|
522
|
-
Ocran.warn "#{mod}::#{const} was not loadable"
|
523
|
-
end
|
524
|
-
end
|
525
|
-
end
|
526
|
-
end
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
# Guess the load path (from 'paths') that was used to load
|
531
|
-
# 'path'. This is primarily relevant on Ruby 1.8 which stores
|
532
|
-
# "unqualified" paths in $LOADED_FEATURES.
|
533
|
-
def Ocran.find_load_path(loadpaths, feature)
|
534
|
-
if feature.absolute?
|
535
|
-
# Choose those loadpaths which contain the feature
|
536
|
-
candidate_loadpaths = loadpaths.select { |loadpath| feature.subpath?(loadpath.expand_path) }
|
537
|
-
# Guess the require'd feature
|
538
|
-
feature_pairs = candidate_loadpaths.map { |loadpath| [loadpath, feature.relative_path_from(loadpath.expand_path)] }
|
539
|
-
# Select the shortest possible require-path (longest load-path)
|
540
|
-
if feature_pairs.empty?
|
541
|
-
nil
|
542
|
-
else
|
543
|
-
feature_pairs.sort_by { |loadpath, feature| feature.to_s.size }.first[0]
|
544
|
-
end
|
545
|
-
else
|
546
|
-
# Select the loadpaths that contain 'feature' and select the shortest
|
547
|
-
candidates = loadpaths.select { |loadpath| feature.expand_path(loadpath).exist? }
|
548
|
-
candidates.sort_by { |loadpath| loadpath.to_s.size }.last
|
549
|
-
end
|
550
|
-
end
|
551
|
-
|
552
|
-
# Find the root of all files specified on the command line and use
|
553
|
-
# it as the "src" of the output.
|
554
|
-
def Ocran.find_src_root(files)
|
555
|
-
src_files = files.map { |file| file.expand_path }
|
556
|
-
src_prefix = src_files.inject(src_files.first.dirname) do |srcroot, path|
|
557
|
-
if path.subpath?(Host.exec_prefix)
|
558
|
-
srcroot
|
559
|
-
else
|
560
|
-
loop do
|
561
|
-
relpath = path.relative_path_from(srcroot)
|
562
|
-
if relpath.absolute?
|
563
|
-
Ocran.fatal_error "No common directory contains all specified files"
|
564
|
-
end
|
565
|
-
if relpath.to_s =~ /^\.\.\//
|
566
|
-
srcroot = srcroot.dirname
|
567
|
-
else
|
568
|
-
break
|
569
|
-
end
|
570
|
-
end
|
571
|
-
srcroot
|
572
|
-
end
|
573
|
-
end
|
574
|
-
src_files = src_files.map do |file|
|
575
|
-
if file.subpath?(src_prefix)
|
576
|
-
file.relative_path_from(src_prefix)
|
577
|
-
else
|
578
|
-
file
|
579
|
-
end
|
580
|
-
end
|
581
|
-
return src_prefix, src_files
|
582
|
-
end
|
583
|
-
|
584
|
-
# Searches for features that are loaded from gems, then produces a
|
585
|
-
# list of files included in those gems' manifests. Also returns a
|
586
|
-
# list of original features that are caused gems to be
|
587
|
-
# included. Ruby 1.8 provides Gem.loaded_specs to detect gems, but
|
588
|
-
# this is empty with Ruby 1.9. So instead, we look for any loaded
|
589
|
-
# file from a gem path.
|
590
|
-
def Ocran.find_gem_files(features)
|
591
|
-
features_from_gems = []
|
592
|
-
gems = {}
|
593
|
-
|
594
|
-
# If a Bundler Gemfile was provided, add all gems it specifies
|
595
|
-
if Ocran.gemfile
|
596
|
-
Ocran.msg "Scanning Gemfile"
|
597
|
-
# Load Rubygems and Bundler so we can scan the Gemfile
|
598
|
-
["rubygems", "bundler"].each do |lib|
|
599
|
-
begin
|
600
|
-
require lib
|
601
|
-
rescue LoadError
|
602
|
-
Ocran.fatal_error "Couldn't scan Gemfile, unable to load #{lib}"
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
ENV["BUNDLE_GEMFILE"] = Ocran.gemfile.to_s
|
607
|
-
Bundler.load.specs.each do |spec|
|
608
|
-
Ocran.verbose_msg "From Gemfile, adding gem #{spec.full_name}"
|
609
|
-
gems[spec.name] ||= spec
|
610
|
-
end
|
611
|
-
|
612
|
-
# This code is implemented for compatibility with older Bundler versions (< v1.16.0.pre.1).
|
613
|
-
unless gems.any? { |name, spec| name == "bundler" }
|
614
|
-
# Bundler itself wasn't added for some reason, let's put it in directly
|
615
|
-
Ocran.verbose_msg "From Gemfile, forcing inclusion of bundler gem itself"
|
616
|
-
bundler_spec = Gem.loaded_specs["bundler"]
|
617
|
-
bundler_spec or Ocran.fatal_error "Unable to locate bundler gem"
|
618
|
-
gems["bundler"] ||= bundler_spec
|
619
|
-
end
|
620
|
-
end
|
621
|
-
|
622
|
-
if defined?(Gem)
|
623
|
-
# Include Gems that are loaded
|
624
|
-
Gem.loaded_specs.each { |gemname, spec| gems[gemname] ||= spec }
|
625
|
-
# Fall back to gem detection (loaded_specs are not population on
|
626
|
-
# all Ruby versions)
|
627
|
-
features.each do |feature|
|
628
|
-
# Detect load path unless absolute
|
629
|
-
if not feature.absolute?
|
630
|
-
feature = find_load_path(Pathname($:), feature)
|
631
|
-
next if feature.nil? # Could be enumerator.so
|
632
|
-
end
|
633
|
-
# Skip if found in known Gem dir
|
634
|
-
if gems.find { |gem, spec| feature.subpath?(spec.gem_dir) }
|
635
|
-
features_from_gems << feature
|
636
|
-
next
|
637
|
-
end
|
638
|
-
gempaths = Gem.path
|
639
|
-
gempaths.each do |gempath|
|
640
|
-
geminstallpath = Pathname(gempath) / "gems"
|
641
|
-
if feature.subpath?(geminstallpath)
|
642
|
-
gemlocalpath = feature.relative_path_from(geminstallpath)
|
643
|
-
fullgemname = gemlocalpath.to_s.split("/").first
|
644
|
-
gemspecpath = gempath / "specifications" / "#{fullgemname}.gemspec"
|
645
|
-
if spec = Gem::Specification.load(gemspecpath)
|
646
|
-
gems[spec.name] ||= spec
|
647
|
-
features_from_gems << feature
|
648
|
-
else
|
649
|
-
Ocran.warn "Failed to load gemspec for '#{fullgemname}'"
|
650
|
-
end
|
651
|
-
end
|
652
|
-
end
|
653
|
-
end
|
654
|
-
|
655
|
-
gem_files = []
|
656
|
-
|
657
|
-
gems.each do |gemname, spec|
|
658
|
-
if File.exist?(spec.spec_file) then
|
659
|
-
@gemspecs << Pathname(spec.spec_file)
|
660
|
-
else
|
661
|
-
spec_name = File.basename(spec.spec_file)
|
662
|
-
spec_path = File.dirname(spec.spec_file)
|
663
|
-
default_spec_file = spec_path + "/default/" + spec_name
|
664
|
-
if File.exist?(default_spec_file) then
|
665
|
-
@gemspecs << Pathname(default_spec_file)
|
666
|
-
Ocran.msg "Using default specification #{default_spec_file} for gem #{spec.full_name}"
|
667
|
-
end
|
668
|
-
end
|
669
|
-
|
670
|
-
# Determine which set of files to include for this particular gem
|
671
|
-
include = [:loaded, :files]
|
672
|
-
Ocran.gem.each do |negate, option, list|
|
673
|
-
if list.nil? or list.include?(spec.name)
|
674
|
-
case option
|
675
|
-
when :minimal
|
676
|
-
include = [:loaded]
|
677
|
-
when :guess
|
678
|
-
include = [:loaded, :files]
|
679
|
-
when :all
|
680
|
-
include = [:scripts, :files]
|
681
|
-
when :full
|
682
|
-
include = [:scripts, :files, :extras]
|
683
|
-
when :spec
|
684
|
-
include = [:spec]
|
685
|
-
when :scripts
|
686
|
-
if negate
|
687
|
-
include.delete(:scripts)
|
688
|
-
else
|
689
|
-
include.push(:scripts)
|
690
|
-
end
|
691
|
-
when :files
|
692
|
-
if negate
|
693
|
-
include.delete(:files)
|
694
|
-
else
|
695
|
-
include.push(:files)
|
696
|
-
end
|
697
|
-
when :extras
|
698
|
-
if negate
|
699
|
-
include.delete(:extras)
|
700
|
-
else
|
701
|
-
include.push(:extras)
|
702
|
-
end
|
703
|
-
end
|
704
|
-
end
|
705
|
-
end
|
706
|
-
|
707
|
-
Ocran.msg "Detected gem #{spec.full_name} (#{include.join(", ")})"
|
708
|
-
|
709
|
-
gem_root = Pathname(spec.gem_dir)
|
710
|
-
gem_extension = (gem_root / ".." / ".." / "extensions").expand_path
|
711
|
-
if gem_extension.exist?
|
712
|
-
build_complete = gem_extension.find_all_files(/gem.build_complete/).select { |p| p.parent.to_s == spec.full_name }
|
713
|
-
else
|
714
|
-
build_complete = nil
|
715
|
-
end
|
716
|
-
gem_root_files = nil
|
717
|
-
files = []
|
718
|
-
|
719
|
-
unless gem_root.directory?
|
720
|
-
Ocran.warn "Gem #{spec.full_name} root folder was not found, skipping"
|
721
|
-
next
|
722
|
-
end
|
723
|
-
|
724
|
-
# Find the selected files
|
725
|
-
include.each do |set|
|
726
|
-
case set
|
727
|
-
when :spec
|
728
|
-
files << Pathname(spec.files)
|
729
|
-
when :loaded
|
730
|
-
files << features_from_gems.select { |feature| feature.subpath?(gem_root) }
|
731
|
-
when :files
|
732
|
-
gem_root_files ||= gem_root.find_all_files
|
733
|
-
files << gem_root_files.select { |path| path.relative_path_from(gem_root) !~ GEM_NON_FILE_RE }
|
734
|
-
files << build_complete if build_complete
|
735
|
-
when :extras
|
736
|
-
gem_root_files ||= gem_root.find_all_files
|
737
|
-
files << gem_root_files.select { |path| path.relative_path_from(gem_root) =~ GEM_EXTRA_RE }
|
738
|
-
when :scripts
|
739
|
-
gem_root_files ||= gem_root.find_all_files
|
740
|
-
files << gem_root_files.select { |path| path.relative_path_from(gem_root) =~ GEM_SCRIPT_RE }
|
741
|
-
end
|
742
|
-
end
|
743
|
-
|
744
|
-
files.flatten!
|
745
|
-
actual_files = files.select { |file| file.file? }
|
746
|
-
|
747
|
-
(files - actual_files).each do |missing_file|
|
748
|
-
Ocran.warn "#{missing_file} was not found"
|
749
|
-
end
|
750
|
-
|
751
|
-
total_size = actual_files.sum(0, &:size)
|
752
|
-
Ocran.msg "\t#{actual_files.size} files, #{total_size} bytes"
|
753
|
-
|
754
|
-
gem_files += actual_files
|
755
|
-
end
|
756
|
-
gem_files = sort_uniq(gem_files)
|
757
|
-
else
|
758
|
-
gem_files = []
|
759
|
-
end
|
760
|
-
features_from_gems -= gem_files
|
761
|
-
return gem_files, features_from_gems
|
762
|
-
end
|
763
|
-
|
764
|
-
def Ocran.build_exe
|
765
|
-
all_load_paths = $LOAD_PATH.map { |loadpath| Pathname(loadpath).expand_path }
|
766
|
-
@added_load_paths = ($LOAD_PATH - @load_path_before).map { |loadpath| Pathname(loadpath).expand_path }
|
767
|
-
working_directory = Pathname.pwd
|
768
|
-
|
769
|
-
restore_environment
|
770
|
-
|
771
|
-
# If the script was run, then detect the features it used
|
772
|
-
if Ocran.run_script
|
773
|
-
# Attempt to autoload libraries before doing anything else.
|
774
|
-
attempt_load_autoload if Ocran.load_autoload
|
775
|
-
end
|
776
|
-
|
777
|
-
# Store the currently loaded files (before we require rbconfig for
|
778
|
-
# our own use).
|
779
|
-
features = $LOADED_FEATURES.map { |feature| Pathname(feature) }
|
780
|
-
|
781
|
-
# Since https://github.com/rubygems/rubygems/commit/cad4cf16cf8fcc637d9da643ef97cf0be2ed63cb
|
782
|
-
# 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
|
783
|
-
features.push(Pathname("rubygems/core_ext/kernel_require.rb"))
|
784
|
-
|
785
|
-
# Find gemspecs to include
|
786
|
-
if defined?(Gem)
|
787
|
-
@gemspecs = Gem.loaded_specs.map { |name, info| Pathname(info.loaded_from) }
|
788
|
-
else
|
789
|
-
@gemspecs = []
|
790
|
-
end
|
791
|
-
|
792
|
-
require "rbconfig"
|
793
|
-
instsitelibdir = Host.sitelibdir.relative_path_from(Host.exec_prefix)
|
794
|
-
|
795
|
-
load_path = []
|
796
|
-
src_load_path = []
|
797
|
-
|
798
|
-
# Find gems files and remove them from features
|
799
|
-
gem_files, features_from_gems = find_gem_files(features)
|
800
|
-
features -= features_from_gems
|
801
|
-
|
802
|
-
# Find the source root and adjust paths
|
803
|
-
src_prefix, src_files = find_src_root(Ocran.files)
|
804
|
-
|
805
|
-
# Include encoding support files
|
806
|
-
if Ocran.enc
|
807
|
-
all_load_paths.each do |path|
|
808
|
-
if path.subpath?(Host.exec_prefix)
|
809
|
-
encpath = path / "enc"
|
810
|
-
if encpath.exist?
|
811
|
-
encfiles = encpath.find_all_files(/\.so$/)
|
812
|
-
size = encfiles.sum(0, &:size)
|
813
|
-
Ocran.msg "Including #{encfiles.size} encoding support files (#{size} bytes, use --no-enc to exclude)"
|
814
|
-
features.push(*encfiles)
|
815
|
-
end
|
816
|
-
end
|
817
|
-
end
|
818
|
-
else
|
819
|
-
Ocran.msg "Not including encoding support files"
|
820
|
-
end
|
821
|
-
|
822
|
-
# Find features and decide where to put them in the temporary
|
823
|
-
# directory layout.
|
824
|
-
libs = []
|
825
|
-
features.each do |feature|
|
826
|
-
path = find_load_path(all_load_paths, feature)
|
827
|
-
if path.nil? || path.expand_path == Pathname.pwd
|
828
|
-
Ocran.files << feature
|
829
|
-
else
|
830
|
-
if feature.absolute?
|
831
|
-
feature = feature.relative_path_from(path.expand_path)
|
832
|
-
end
|
833
|
-
fullpath = feature.expand_path(path)
|
834
|
-
|
835
|
-
if fullpath.subpath?(Host.exec_prefix)
|
836
|
-
# Features found in the Ruby installation are put in the
|
837
|
-
# temporary Ruby installation.
|
838
|
-
libs << [fullpath, fullpath.relative_path_from(Host.exec_prefix)]
|
839
|
-
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| fullpath.subpath?(pth) }
|
840
|
-
# Features found in any other Gem path (e.g. ~/.gems) is put
|
841
|
-
# in a special 'gemhome' folder.
|
842
|
-
targetpath = GEMHOMEDIR / fullpath.relative_path_from(Pathname(gemhome))
|
843
|
-
libs << [fullpath, targetpath]
|
844
|
-
elsif fullpath.subpath?(src_prefix) || path == working_directory
|
845
|
-
# Any feature found inside the src_prefix automatically gets
|
846
|
-
# added as a source file (to go in 'src').
|
847
|
-
Ocran.files << fullpath
|
848
|
-
# Add the load path unless it was added by the script while
|
849
|
-
# running (or we assume that the script can also set it up
|
850
|
-
# correctly when running from the resulting executable).
|
851
|
-
src_load_path << path unless @added_load_paths.include?(path)
|
852
|
-
elsif @added_load_paths.include?(path)
|
853
|
-
# Any feature that exist in a load path added by the script
|
854
|
-
# itself is added as a file to go into the 'src' (src_prefix
|
855
|
-
# will be adjusted below to point to the common parent).
|
856
|
-
Ocran.files << fullpath
|
857
|
-
else
|
858
|
-
# All other feature that can not be resolved go in the the
|
859
|
-
# Ruby sitelibdir. This is automatically in the load path
|
860
|
-
# when Ruby starts.
|
861
|
-
libs << [fullpath, instsitelibdir / feature]
|
862
|
-
end
|
863
|
-
end
|
864
|
-
end
|
865
|
-
|
866
|
-
# Recompute the src_prefix. Files may have been added implicitly
|
867
|
-
# while scanning through features.
|
868
|
-
src_prefix, src_files = find_src_root(Ocran.files)
|
869
|
-
Ocran.files.replace(src_files)
|
870
|
-
|
871
|
-
# Add the load path that are required with the correct path after
|
872
|
-
# src_prefix was adjusted.
|
873
|
-
load_path += src_load_path.map { |loadpath| TEMPDIR_ROOT / SRCDIR / loadpath.relative_path_from(src_prefix) }
|
874
|
-
|
875
|
-
# Decide where to put gem files, either the system gem folder, or
|
876
|
-
# GEMHOME.
|
877
|
-
gem_files.each do |gemfile|
|
878
|
-
if gemfile.subpath?(Host.exec_prefix)
|
879
|
-
libs << [gemfile, gemfile.relative_path_from(Host.exec_prefix)]
|
880
|
-
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| gemfile.subpath?(pth) }
|
881
|
-
targetpath = GEMHOMEDIR / gemfile.relative_path_from(Pathname(gemhome))
|
882
|
-
libs << [gemfile, targetpath]
|
883
|
-
else
|
884
|
-
Ocran.fatal_error "Don't know where to put gemfile #{gemfile}"
|
885
|
-
end
|
886
|
-
end
|
887
|
-
|
888
|
-
# If requested, add all ruby standard libraries
|
889
|
-
if Ocran.add_all_core
|
890
|
-
Ocran.msg "Will include all ruby core libraries"
|
891
|
-
@load_path_before.each do |lp|
|
892
|
-
path = Pathname.new(lp)
|
893
|
-
next unless path.to_posix =~
|
894
|
-
/\/(ruby\/(?:site_ruby\/|vendor_ruby\/)?[0-9.]+)\/?$/i
|
895
|
-
subdir = $1
|
896
|
-
Pathname.glob("#{lp}/**/*").each do |fpath|
|
897
|
-
next if fpath.directory?
|
898
|
-
tgt = "lib/#{subdir}/#{fpath.relative_path_from(path).to_posix}"
|
899
|
-
libs << [fpath, tgt]
|
900
|
-
end
|
901
|
-
end
|
902
|
-
end
|
903
|
-
|
904
|
-
# Detect additional DLLs
|
905
|
-
dlls = Ocran.autodll ? LibraryDetector.detect_dlls : []
|
906
|
-
|
907
|
-
# Detect external manifests
|
908
|
-
# For RubyInstaller environments supporting Ruby 2.4 and above,
|
909
|
-
# a manifest file is required.
|
910
|
-
if (manifest = Host.exec_prefix / "bin/ruby_builtin_dlls/ruby_builtin_dlls.manifest").exist?
|
911
|
-
manifests = [manifest]
|
912
|
-
else
|
913
|
-
manifests = []
|
914
|
-
end
|
915
|
-
|
916
|
-
executable = nil
|
917
|
-
if Ocran.output_override
|
918
|
-
executable = Ocran.output_override
|
919
|
-
else
|
920
|
-
executable = Ocran.files.first.basename.sub_ext(".exe")
|
921
|
-
executable.append_to_filename!("-debug") if Ocran.debug
|
922
|
-
end
|
923
|
-
|
924
|
-
windowed = (Ocran.files.first.ext?(".rbw") || Ocran.force_windows) && !Ocran.force_console
|
925
|
-
|
926
|
-
Ocran.msg "Building #{executable}"
|
927
|
-
target_script = nil
|
928
|
-
OcranBuilder.new(executable, windowed) do |sb|
|
929
|
-
# Add explicitly mentioned files
|
930
|
-
Ocran.msg "Adding user-supplied source files"
|
931
|
-
Ocran.files.each do |file|
|
932
|
-
file = src_prefix / file
|
933
|
-
if file.subpath?(Host.exec_prefix)
|
934
|
-
target = file.relative_path_from(Host.exec_prefix)
|
935
|
-
elsif file.subpath?(src_prefix)
|
936
|
-
target = SRCDIR / file.relative_path_from(src_prefix)
|
937
|
-
else
|
938
|
-
target = SRCDIR / file.basename
|
939
|
-
end
|
940
|
-
|
941
|
-
target_script ||= target
|
942
|
-
|
943
|
-
if file.directory?
|
944
|
-
sb.ensuremkdir(target)
|
945
|
-
else
|
946
|
-
begin
|
947
|
-
sb.createfile(file, target)
|
948
|
-
rescue Errno::ENOENT
|
949
|
-
raise unless file =~ IGNORE_MODULE_NAMES
|
950
|
-
end
|
951
|
-
end
|
952
|
-
end
|
953
|
-
|
954
|
-
# Add the ruby executable and DLL
|
955
|
-
if windowed
|
956
|
-
rubyexe = Host.rubyw_exe
|
957
|
-
else
|
958
|
-
rubyexe = Host.ruby_exe
|
959
|
-
end
|
960
|
-
Ocran.msg "Adding ruby executable #{rubyexe}"
|
961
|
-
sb.createfile(Host.bindir / rubyexe, BINDIR / rubyexe)
|
962
|
-
if Host.libruby_so
|
963
|
-
sb.createfile(Host.bindir / Host.libruby_so, BINDIR / Host.libruby_so)
|
964
|
-
end
|
965
|
-
|
966
|
-
# Add detected DLLs
|
967
|
-
dlls.each do |dll|
|
968
|
-
Ocran.msg "Adding detected DLL #{dll}"
|
969
|
-
if dll.subpath?(Host.exec_prefix)
|
970
|
-
target = dll.relative_path_from(Host.exec_prefix)
|
971
|
-
else
|
972
|
-
target = BINDIR / File.basename(dll)
|
973
|
-
end
|
974
|
-
sb.createfile(dll, target)
|
975
|
-
end
|
976
|
-
|
977
|
-
# Add external manifest files
|
978
|
-
manifests.each do |manifest|
|
979
|
-
Ocran.msg "Adding external manifest #{manifest}"
|
980
|
-
target = manifest.relative_path_from(Host.exec_prefix)
|
981
|
-
sb.createfile(manifest, target)
|
982
|
-
end
|
983
|
-
|
984
|
-
# Add extra DLLs specified on the command line
|
985
|
-
Ocran.extra_dlls.each do |dll|
|
986
|
-
Ocran.msg "Adding supplied DLL #{dll}"
|
987
|
-
sb.createfile(Host.bindir / dll, BINDIR / dll)
|
988
|
-
end
|
989
|
-
|
990
|
-
# Add gemspec files
|
991
|
-
@gemspecs = sort_uniq(@gemspecs)
|
992
|
-
@gemspecs.each do |gemspec|
|
993
|
-
if gemspec.subpath?(Host.exec_prefix)
|
994
|
-
path = gemspec.relative_path_from(Host.exec_prefix)
|
995
|
-
sb.createfile(gemspec, path)
|
996
|
-
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| gemspec.subpath?(pth) }
|
997
|
-
path = GEMHOMEDIR / gemspec.relative_path_from(Pathname(gemhome))
|
998
|
-
sb.createfile(gemspec, path)
|
999
|
-
else
|
1000
|
-
Ocran.fatal_error "Gem spec #{gemspec} does not exist in the Ruby installation. Don't know where to put it."
|
1001
|
-
end
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
# Add loaded libraries (features, gems)
|
1005
|
-
Ocran.msg "Adding library files"
|
1006
|
-
libs.each do |path, target|
|
1007
|
-
sb.createfile(path, target)
|
1008
|
-
end
|
1009
|
-
|
1010
|
-
# 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)
|
1011
|
-
# Add an empty file to /msys64/usr/bin/msys-2.0.dll if the dll was not required otherwise
|
1012
|
-
unless sb.files.keys.any? { |entry| entry.to_s.include?("msys-2.0.dll") }
|
1013
|
-
sb.createfile(Pathname(File.dirname(__FILE__)) / "../share/ocran/empty-msys-2.0.dll".to_s, 'msys64/usr/bin/msys-2.0.dll')
|
1014
|
-
end
|
1015
|
-
|
1016
|
-
# Set environment variable
|
1017
|
-
sb.setenv("RUBYOPT", Ocran.rubyopt || ENV["RUBYOPT"] || "")
|
1018
|
-
sb.setenv("RUBYLIB", load_path.map { |path| path.to_native }.uniq.join(";"))
|
1019
|
-
|
1020
|
-
sb.setenv("GEM_PATH", (TEMPDIR_ROOT / GEMHOMEDIR).to_native)
|
1021
|
-
|
1022
|
-
# Add the opcode to launch the script
|
1023
|
-
extra_arg = Ocran.arg.map { |arg| ' "' + arg.gsub("\"", "\\\"") + '"' }.join
|
1024
|
-
installed_ruby_exe = TEMPDIR_ROOT / BINDIR / rubyexe
|
1025
|
-
launch_script = (TEMPDIR_ROOT / target_script).to_native
|
1026
|
-
sb.postcreateprocess(installed_ruby_exe,
|
1027
|
-
"#{rubyexe} \"#{launch_script}\"#{extra_arg}")
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
unless Ocran.inno_script
|
1031
|
-
Ocran.msg "Finished building #{executable} (#{File.size(executable)} bytes)"
|
1032
|
-
end
|
1033
|
-
end
|
1034
|
-
|
1035
|
-
module LibraryDetector
|
1036
|
-
# Windows API functions for handling files may return long paths,
|
1037
|
-
# with a maximum character limit of 32,767.
|
1038
|
-
# "\\?\" prefix(4 characters) + long path(32762 characters) + NULL = 32767 characters
|
1039
|
-
# https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
|
1040
|
-
MAX_PATH = 32767
|
1041
|
-
|
1042
|
-
# The byte size of the buffer given as an argument to the EnumProcessModules function.
|
1043
|
-
# This buffer is used to store the handles of the loaded modules.
|
1044
|
-
# If the buffer size is smaller than the number of loaded modules,
|
1045
|
-
# it will automatically increase the buffer size and call the EnumProcessModules function again.
|
1046
|
-
# Increasing the initial buffer size can reduce the number of iterations required.
|
1047
|
-
# https://learn.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process
|
1048
|
-
DEFAULT_HMODULE_BUFFER_SIZE = 1024
|
1049
|
-
|
1050
|
-
def LibraryDetector.init_fiddle
|
1051
|
-
require "fiddle/import"
|
1052
|
-
require "fiddle/types"
|
1053
|
-
|
1054
|
-
extend Fiddle::Importer
|
1055
|
-
dlload "kernel32.dll", "psapi.dll"
|
1056
|
-
|
1057
|
-
include Fiddle::Win32Types
|
1058
|
-
# https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
1059
|
-
typealias "HINSTANCE", "HANDLE" # for Ruby2.6 only
|
1060
|
-
typealias "HMODULE", "HINSTANCE"
|
1061
|
-
typealias "LPDWORD", "PDWORD"
|
1062
|
-
typealias "LPWSTR", "char*"
|
1063
|
-
|
1064
|
-
extern "BOOL EnumProcessModules(HANDLE, HMODULE*, DWORD, LPDWORD)"
|
1065
|
-
extern "DWORD GetModuleFileNameW(HMODULE, LPWSTR, DWORD)"
|
1066
|
-
extern "HANDLE GetCurrentProcess()"
|
1067
|
-
extern "DWORD GetLastError()"
|
1068
|
-
end
|
1069
|
-
|
1070
|
-
def LibraryDetector.loaded_dlls
|
1071
|
-
init_fiddle
|
1072
|
-
|
1073
|
-
dword = "L" # A DWORD is a 32-bit unsigned integer.
|
1074
|
-
bytes_needed = [0].pack(dword)
|
1075
|
-
bytes = DEFAULT_HMODULE_BUFFER_SIZE
|
1076
|
-
process_handle = GetCurrentProcess()
|
1077
|
-
handles = while true
|
1078
|
-
buffer = "\x00" * bytes
|
1079
|
-
if EnumProcessModules(process_handle, buffer, buffer.bytesize, bytes_needed) == 0
|
1080
|
-
Ocran.fatal_error "LibraryDetector: EnumProcessModules failed with error code %#010x" % GetLastError()
|
1081
|
-
end
|
1082
|
-
bytes = bytes_needed.unpack1(dword)
|
1083
|
-
if bytes <= buffer.bytesize
|
1084
|
-
break buffer.unpack("J#{bytes / Fiddle::SIZEOF_VOIDP}")
|
1085
|
-
end
|
1086
|
-
end
|
1087
|
-
str = "\x00".encode("UTF-16LE") * MAX_PATH
|
1088
|
-
handles.map do |handle|
|
1089
|
-
length = GetModuleFileNameW(handle, str, str.bytesize)
|
1090
|
-
if length == 0
|
1091
|
-
Ocran.fatal_error "LibraryDetector: GetModuleFileNameW failed with error code %#010x" % GetLastError()
|
1092
|
-
end
|
1093
|
-
Ocran.Pathname(str[0, length])
|
1094
|
-
end
|
1095
|
-
end
|
1096
|
-
|
1097
|
-
def LibraryDetector.detect_dlls
|
1098
|
-
loaded = loaded_dlls
|
1099
|
-
exec_prefix = Host.exec_prefix
|
1100
|
-
loaded.select { |path| path.subpath?(exec_prefix) && path.basename.ext?(".dll") && path.basename != Host.libruby_so }
|
1101
|
-
end
|
1102
|
-
end
|
1103
|
-
|
1104
|
-
# Utility class that produces the actual executable. Opcodes
|
1105
|
-
# (createfile, mkdir etc) are added by invoking methods on an
|
1106
|
-
# instance of OcranBuilder.
|
1107
|
-
class OcranBuilder
|
1108
|
-
Signature = [0x41, 0xb6, 0xba, 0x4e]
|
1109
|
-
OP_END = 0
|
1110
|
-
OP_CREATE_DIRECTORY = 1
|
1111
|
-
OP_CREATE_FILE = 2
|
1112
|
-
OP_CREATE_PROCESS = 3
|
1113
|
-
OP_DECOMPRESS_LZMA = 4
|
1114
|
-
OP_SETENV = 5
|
1115
|
-
OP_POST_CREATE_PROCESS = 6
|
1116
|
-
OP_ENABLE_DEBUG_MODE = 7
|
1117
|
-
OP_CREATE_INST_DIRECTORY = 8
|
1118
|
-
|
1119
|
-
attr_reader :files
|
1120
|
-
|
1121
|
-
def initialize(path, windowed)
|
1122
|
-
@paths = {}
|
1123
|
-
@files = {}
|
1124
|
-
File.open(path, "wb") do |ocranfile|
|
1125
|
-
image = nil
|
1126
|
-
if windowed
|
1127
|
-
image = Ocran.stubwimage
|
1128
|
-
else
|
1129
|
-
image = Ocran.stubimage
|
1130
|
-
end
|
1131
|
-
|
1132
|
-
unless image
|
1133
|
-
Ocran.fatal_error "Stub image not available"
|
1134
|
-
end
|
1135
|
-
ocranfile.write(image)
|
1136
|
-
end
|
1137
|
-
|
1138
|
-
if Ocran.icon_filename
|
1139
|
-
system Ocran.ediconpath.to_s, path.to_s, Ocran.icon_filename.to_s
|
1140
|
-
end
|
1141
|
-
|
1142
|
-
opcode_offset = File.size(path)
|
1143
|
-
|
1144
|
-
File.open(path, "ab") do |ocranfile|
|
1145
|
-
tmpinpath = "tmpin"
|
1146
|
-
|
1147
|
-
if Ocran.lzma_mode
|
1148
|
-
@of = File.open(tmpinpath, "wb")
|
1149
|
-
else
|
1150
|
-
@of = ocranfile
|
1151
|
-
end
|
1152
|
-
|
1153
|
-
if Ocran.debug
|
1154
|
-
Ocran.msg("Enabling debug mode in executable")
|
1155
|
-
ocranfile.write([OP_ENABLE_DEBUG_MODE].pack("V"))
|
1156
|
-
end
|
1157
|
-
|
1158
|
-
createinstdir Ocran.debug_extract, !Ocran.debug_extract, Ocran.chdir_first
|
1159
|
-
|
1160
|
-
yield(self)
|
1161
|
-
|
1162
|
-
@of.close if Ocran.lzma_mode
|
1163
|
-
|
1164
|
-
if Ocran.lzma_mode and not Ocran.inno_script
|
1165
|
-
tmpoutpath = "tmpout"
|
1166
|
-
begin
|
1167
|
-
data_size = File.size(tmpinpath)
|
1168
|
-
Ocran.msg "Compressing #{data_size} bytes"
|
1169
|
-
system(Ocran.lzmapath.to_s, "e", tmpinpath, tmpoutpath) or fail
|
1170
|
-
compressed_data_size = File.size?(tmpoutpath)
|
1171
|
-
ocranfile.write([OP_DECOMPRESS_LZMA, compressed_data_size].pack("VV"))
|
1172
|
-
IO.copy_stream(tmpoutpath, ocranfile)
|
1173
|
-
ensure
|
1174
|
-
File.unlink(@of.path) if File.exist?(@of.path)
|
1175
|
-
File.unlink(tmpoutpath) if File.exist?(tmpoutpath)
|
1176
|
-
end
|
1177
|
-
end
|
1178
|
-
|
1179
|
-
ocranfile.write([OP_END].pack("V"))
|
1180
|
-
ocranfile.write([opcode_offset].pack("V")) # Pointer to start of opcodes
|
1181
|
-
ocranfile.write(Signature.pack("C*"))
|
1182
|
-
end
|
1183
|
-
|
1184
|
-
if Ocran.inno_script
|
1185
|
-
begin
|
1186
|
-
iss = File.read(Ocran.inno_script) + "\n\n"
|
1187
|
-
|
1188
|
-
iss << "[Dirs]\n"
|
1189
|
-
@paths.each_key do |p|
|
1190
|
-
iss << "Name: \"{app}/#{p}\"\n"
|
1191
|
-
end
|
1192
|
-
iss << "\n"
|
1193
|
-
|
1194
|
-
iss << "[Files]\n"
|
1195
|
-
path_escaped = path.to_s.gsub('"', '""')
|
1196
|
-
iss << "Source: \"#{path_escaped}\"; DestDir: \"{app}\"\n"
|
1197
|
-
@files.each do |tgt, src|
|
1198
|
-
src_escaped = src.to_s.gsub('"', '""')
|
1199
|
-
target_dir_escaped = Pathname.new(tgt).dirname.to_s.gsub('"', '""')
|
1200
|
-
iss << "Source: \"#{src_escaped}\"; DestDir: \"{app}/#{target_dir_escaped}\"\n" unless src_escaped =~ IGNORE_MODULE_NAMES
|
1201
|
-
end
|
1202
|
-
iss << "\n"
|
1203
|
-
|
1204
|
-
Ocran.verbose_msg "### INNOSETUP SCRIPT ###\n\n#{iss}\n\n"
|
1205
|
-
|
1206
|
-
f = File.open("ocrantemp.iss", "w")
|
1207
|
-
f.write(iss)
|
1208
|
-
f.close()
|
1209
|
-
|
1210
|
-
iscc_cmd = ["iscc"]
|
1211
|
-
iscc_cmd << "/Q" unless Ocran.verbose
|
1212
|
-
iscc_cmd << "ocrantemp.iss"
|
1213
|
-
Ocran.msg "Running InnoSetup compiler ISCC"
|
1214
|
-
result = system(*iscc_cmd)
|
1215
|
-
if not result
|
1216
|
-
case $?.exitstatus
|
1217
|
-
when 0 then raise RuntimeError.new("ISCC reported success, but system reported error?")
|
1218
|
-
when 1 then raise RuntimeError.new("ISCC reports invalid command line parameters")
|
1219
|
-
when 2 then raise RuntimeError.new("ISCC reports that compilation failed")
|
1220
|
-
else raise RuntimeError.new("ISCC failed to run. Is the InnoSetup directory in your PATH?")
|
1221
|
-
end
|
1222
|
-
end
|
1223
|
-
rescue Exception => e
|
1224
|
-
Ocran.fatal_error("InnoSetup installer creation failed: #{e.message}")
|
1225
|
-
ensure
|
1226
|
-
File.unlink("ocrantemp.iss") if File.exist?("ocrantemp.iss")
|
1227
|
-
File.unlink(path) if File.exist?(path)
|
1228
|
-
end
|
1229
|
-
end
|
1230
|
-
end
|
1231
|
-
|
1232
|
-
def mkdir(path)
|
1233
|
-
return if @paths[path.to_s.downcase]
|
1234
|
-
@paths[path.to_posix.downcase] = true
|
1235
|
-
Ocran.verbose_msg "m #{showtempdir path}"
|
1236
|
-
unless Ocran.inno_script # The directory will be created by InnoSetup with a [Dirs] statement
|
1237
|
-
@of << [OP_CREATE_DIRECTORY, path.to_native].pack("VZ*")
|
1238
|
-
end
|
1239
|
-
end
|
1240
|
-
|
1241
|
-
def ensuremkdir(tgt)
|
1242
|
-
tgt = Ocran.Pathname(tgt)
|
1243
|
-
return if tgt.to_s == "."
|
1244
|
-
if not @paths[tgt.to_posix.downcase]
|
1245
|
-
ensuremkdir(tgt.dirname)
|
1246
|
-
mkdir(tgt)
|
1247
|
-
end
|
1248
|
-
end
|
1249
|
-
|
1250
|
-
def createinstdir(next_to_exe = false, delete_after = false, chdir_before = false)
|
1251
|
-
unless Ocran.inno_script # Creation of installation directory will be handled by InnoSetup
|
1252
|
-
@of << [OP_CREATE_INST_DIRECTORY, next_to_exe ? 1 : 0, delete_after ? 1 : 0, chdir_before ? 1 : 0].pack("VVVV")
|
1253
|
-
end
|
1254
|
-
end
|
1255
|
-
|
1256
|
-
def createfile(src, tgt)
|
1257
|
-
return if @files[tgt]
|
1258
|
-
@files[tgt] = src
|
1259
|
-
src, tgt = Ocran.Pathname(src), Ocran.Pathname(tgt)
|
1260
|
-
ensuremkdir(tgt.dirname)
|
1261
|
-
str = src.binread
|
1262
|
-
Ocran.verbose_msg "a #{showtempdir tgt}"
|
1263
|
-
unless Ocran.inno_script # InnoSetup will install the file with a [Files] statement
|
1264
|
-
@of << [OP_CREATE_FILE, tgt.to_native, str.size].pack("VZ*V")
|
1265
|
-
@of << str
|
1266
|
-
end
|
1267
|
-
end
|
1268
|
-
|
1269
|
-
def hasFile?(src)
|
1270
|
-
end
|
1271
|
-
|
1272
|
-
def createprocess(image, cmdline)
|
1273
|
-
Ocran.verbose_msg "l #{showtempdir image} #{showtempdir cmdline}"
|
1274
|
-
@of << [OP_CREATE_PROCESS, image.to_native, cmdline].pack("VZ*Z*")
|
1275
|
-
end
|
1276
|
-
|
1277
|
-
def postcreateprocess(image, cmdline)
|
1278
|
-
Ocran.verbose_msg "p #{showtempdir image} #{showtempdir cmdline}"
|
1279
|
-
@of << [OP_POST_CREATE_PROCESS, image.to_native, cmdline].pack("VZ*Z*")
|
1280
|
-
end
|
1281
|
-
|
1282
|
-
def setenv(name, value)
|
1283
|
-
Ocran.verbose_msg "e #{name} #{showtempdir value}"
|
1284
|
-
@of << [OP_SETENV, name, value].pack("VZ*Z*")
|
1285
|
-
end
|
1286
|
-
|
1287
|
-
def close
|
1288
|
-
@of.close
|
1289
|
-
end
|
1290
|
-
|
1291
|
-
def showtempdir(x)
|
1292
|
-
x.to_s.gsub(TEMPDIR_ROOT.to_s, "<tempdir>")
|
1293
|
-
end
|
1294
|
-
end # class OcranBuilder
|
1295
|
-
end # module Ocran
|
1296
|
-
|
1297
|
-
if File.basename(__FILE__) == File.basename($0)
|
1298
|
-
Ocran.init(ARGV)
|
1299
|
-
ARGV.replace(Ocran.arg)
|
1300
|
-
|
1301
|
-
if not Ocran.files.first.exist?
|
1302
|
-
Ocran.fatal_error "#{Ocran.files[0]} was not found!"
|
1303
|
-
end
|
1304
|
-
|
1305
|
-
at_exit do
|
1306
|
-
if $!.nil? or $!.kind_of?(SystemExit)
|
1307
|
-
Ocran.build_exe
|
1308
|
-
exit 0
|
1309
|
-
end
|
1310
|
-
end
|
1311
|
-
|
1312
|
-
if Ocran.run_script
|
1313
|
-
Ocran.msg "Loading script to check dependencies"
|
1314
|
-
$0 = Ocran.files.first.to_s
|
1315
|
-
load Ocran.files.first
|
1316
|
-
end
|
1317
|
-
end
|