ocran 1.3.12

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