ocran 1.3.12

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 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: []