aibika 1.3.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +11 -0
- data/History.txt +214 -0
- data/LICENSE.md +30 -0
- data/Manifest.txt +11 -0
- data/README.adoc +564 -0
- data/Rakefile +30 -0
- data/aibika.gemspec +50 -0
- data/bin/aibika +25 -0
- data/lib/aibika/aibika_builder.rb +191 -0
- data/lib/aibika/cli.rb +181 -0
- data/lib/aibika/host.rb +40 -0
- data/lib/aibika/library_detector.rb +88 -0
- data/lib/aibika/pathname.rb +158 -0
- data/lib/aibika/version.rb +5 -0
- data/lib/aibika.rb +675 -0
- data/samples/activerecord_sample.rb +6 -0
- data/samples/bundler_git/Gemfile +3 -0
- data/samples/bundler_git/bundler_git.rb +5 -0
- data/samples/mech.rb +8 -0
- data/samples/mime-types_sample.rb +4 -0
- data/samples/pg_sample.rb +4 -0
- data/samples/prawn_sample.rb +9 -0
- data/samples/readchar.rb +4 -0
- data/samples/sysproctable.rb +12 -0
- data/samples/tk.rb +13 -0
- data/samples/tkextlib.rb +4 -0
- data/samples/watir_sample.rb +31 -0
- data/samples/win32_api_sample.rb +5 -0
- data/samples/win32ole.rb +4 -0
- data/samples/wxruby_sample.rbw +29 -0
- data/share/aibika/lzma.exe +0 -0
- data/src/Makefile +37 -0
- data/src/edicon.c +146 -0
- data/src/lzma/LzmaDec.c +1007 -0
- data/src/lzma/LzmaDec.h +223 -0
- data/src/lzma/Types.h +208 -0
- data/src/seb.exe +0 -0
- data/src/stub.c +703 -0
- data/src/stub.rc +1 -0
- data/src/vit-ruby.ico +0 -0
- metadata +109 -0
data/lib/aibika.rb
ADDED
@@ -0,0 +1,675 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'aibika/aibika_builder'
|
4
|
+
require_relative 'aibika/cli'
|
5
|
+
require_relative 'aibika/host'
|
6
|
+
require_relative 'aibika/library_detector'
|
7
|
+
require_relative 'aibika/pathname'
|
8
|
+
require_relative 'aibika/version'
|
9
|
+
|
10
|
+
module Aibika
|
11
|
+
# Fence against packaging of self (aibika gem) unless implicitly requestd
|
12
|
+
# String, NilClass and arrays of any of these.
|
13
|
+
def self.fence_self?(name)
|
14
|
+
!Aibika.allow_self && name == 'aibika'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.fence_self_dir?(dir)
|
18
|
+
!Aibika.allow_self && dir.start_with?(__dir__)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Type conversion for the Pathname class. Works with Pathname,
|
22
|
+
# String, NilClass and arrays of any of these.
|
23
|
+
def self.Pathname(obj)
|
24
|
+
case obj
|
25
|
+
when Pathname
|
26
|
+
obj
|
27
|
+
when Array
|
28
|
+
obj.map { |x| Pathname(x) }
|
29
|
+
when String
|
30
|
+
Pathname.new(obj)
|
31
|
+
when NilClass
|
32
|
+
nil
|
33
|
+
else
|
34
|
+
raise ArgumentError, obj
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sorts and returns an array without duplicates. Works with complex
|
39
|
+
# objects (such as Pathname), in contrast to Array#uniq.
|
40
|
+
def self.sort_uniq(arr)
|
41
|
+
arr.sort.inject([]) { |r, e| r.last == e ? r : r << e }
|
42
|
+
end
|
43
|
+
|
44
|
+
IGNORE_MODULE_NAMES = %r{/(enumerator.so|rational.so|complex.so|fiber.so|thread.rb|ruby2_keywords.rb)$}.freeze
|
45
|
+
|
46
|
+
GEM_SCRIPT_RE = /\.rbw?$/.freeze
|
47
|
+
GEM_EXTRA_RE = %r{(
|
48
|
+
# Auxiliary files in the root of the gem
|
49
|
+
^(\./)?(History|Install|Manifest|README|CHANGES|Licen[sc]e|Contributors|ChangeLog|BSD|GPL).*$ |
|
50
|
+
# Installation files in the root of the gem
|
51
|
+
^(\./)?(Rakefile|setup.rb|extconf.rb)$ |
|
52
|
+
# Documentation/test directories in the root of the gem
|
53
|
+
^(\./)?(doc|ext|examples|test|tests|benchmarks|spec)/ |
|
54
|
+
# Directories anywhere
|
55
|
+
(^|/)(\.autotest|\.svn|\.cvs|\.git)(/|$) |
|
56
|
+
# Unlikely extensions
|
57
|
+
\.(rdoc|c|cpp|c\+\+|cxx|h|hxx|hpp|obj|o|a)$/
|
58
|
+
)}xi.freeze
|
59
|
+
|
60
|
+
GEM_NON_FILE_RE = /(#{GEM_EXTRA_RE}|#{GEM_SCRIPT_RE})/.freeze
|
61
|
+
|
62
|
+
# Alias for the temporary directory where files are extracted.
|
63
|
+
TEMPDIR_ROOT = Pathname.new('|')
|
64
|
+
# Directory for source files in temporary directory.
|
65
|
+
SRCDIR = Pathname.new('src')
|
66
|
+
# Directory for Ruby binaries in temporary directory.
|
67
|
+
BINDIR = Pathname.new('bin')
|
68
|
+
# Directory for GEMHOME files in temporary directory.
|
69
|
+
GEMHOMEDIR = Pathname.new('gemhome')
|
70
|
+
|
71
|
+
@ignore_modules = []
|
72
|
+
|
73
|
+
@options = {
|
74
|
+
lzma_mode: true,
|
75
|
+
extra_dlls: [],
|
76
|
+
files: [],
|
77
|
+
run_script: true,
|
78
|
+
add_all_core: false,
|
79
|
+
output_override: nil,
|
80
|
+
load_autoload: true,
|
81
|
+
chdir_first: false,
|
82
|
+
force_windows: false,
|
83
|
+
force_console: false,
|
84
|
+
icon_filename: nil,
|
85
|
+
gemfile: nil,
|
86
|
+
inno_script: nil,
|
87
|
+
quiet: false,
|
88
|
+
verbose: false,
|
89
|
+
autodll: true,
|
90
|
+
show_warnings: true,
|
91
|
+
debug: false,
|
92
|
+
debug_extract: false,
|
93
|
+
arg: [],
|
94
|
+
enc: true,
|
95
|
+
allow_self: false,
|
96
|
+
gem: []
|
97
|
+
}
|
98
|
+
|
99
|
+
@options.each_key { |opt| eval("def self.#{opt}; @options[:#{opt}]; end") }
|
100
|
+
|
101
|
+
class << self
|
102
|
+
attr_reader :lzmapath, :ediconpath, :stubimage, :stubwimage
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns a binary blob store embedded in the current Ruby script.
|
106
|
+
def self.get_next_embedded_image
|
107
|
+
DATA.read(DATA.readline.to_i).unpack1('m')
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.save_environment
|
111
|
+
@load_path_before = $LOAD_PATH.dup
|
112
|
+
@pwd_before = Dir.pwd
|
113
|
+
@env_before = {}
|
114
|
+
ENV.each { |key, value| @env_before[key] = value }
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.restore_environment
|
118
|
+
@env_before.each { |key, value| ENV[key] = value }
|
119
|
+
ENV.each_key { |key| ENV.delete(key) unless @env_before.key?(key) }
|
120
|
+
Dir.chdir @pwd_before
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.find_stubs
|
124
|
+
if defined?(DATA)
|
125
|
+
@stubimage = get_next_embedded_image
|
126
|
+
@stubwimage = get_next_embedded_image
|
127
|
+
lzmaimage = get_next_embedded_image
|
128
|
+
@lzmapath = Host.tempdir / 'lzma.exe'
|
129
|
+
File.open(@lzmapath, 'wb') { |file| file << lzmaimage }
|
130
|
+
ediconimage = get_next_embedded_image
|
131
|
+
@ediconpath = Host.tempdir / 'edicon.exe'
|
132
|
+
File.open(@ediconpath, 'wb') { |file| file << ediconimage }
|
133
|
+
else
|
134
|
+
aibikapath = Pathname(File.dirname(__FILE__))
|
135
|
+
@stubimage = File.open(aibikapath / '../share/aibika/stub.exe', 'rb', &:read)
|
136
|
+
@stubwimage = File.open(aibikapath / '../share/aibika/stubw.exe', 'rb', &:read)
|
137
|
+
@lzmapath = (aibikapath / '../share/aibika/lzma.exe').expand
|
138
|
+
@ediconpath = (aibikapath / '../share/aibika/edicon.exe').expand
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.init(argv)
|
143
|
+
save_environment
|
144
|
+
parseargs(argv)
|
145
|
+
find_stubs
|
146
|
+
@ignore_modules.push(*ObjectSpace.each_object(Module).to_a)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Force loading autoloaded constants. Searches through all modules
|
150
|
+
# (and hence classes), and checks their constants for autoloaded
|
151
|
+
# ones, then attempts to load them.
|
152
|
+
def self.attempt_load_autoload
|
153
|
+
modules_checked = {}
|
154
|
+
@ignore_modules.each { |m| modules_checked[m] = true }
|
155
|
+
loop do
|
156
|
+
modules_to_check = []
|
157
|
+
ObjectSpace.each_object(Module) do |mod|
|
158
|
+
modules_to_check << mod unless modules_checked.include?(mod)
|
159
|
+
end
|
160
|
+
break if modules_to_check.empty?
|
161
|
+
|
162
|
+
modules_to_check.each do |mod|
|
163
|
+
modules_checked[mod] = true
|
164
|
+
mod.constants.each do |const|
|
165
|
+
# Module::Config causes warning on Ruby 1.9.3 - prevent autoloading
|
166
|
+
next if mod.is_a?(Module) && const == :Config
|
167
|
+
|
168
|
+
next unless mod.autoload?(const)
|
169
|
+
|
170
|
+
Aibika.msg "Attempting to trigger autoload of #{mod}::#{const}"
|
171
|
+
begin
|
172
|
+
mod.const_get(const)
|
173
|
+
rescue NameError
|
174
|
+
Aibika.warn "#{mod}::#{const} was defined autoloadable, but caused NameError"
|
175
|
+
rescue LoadError
|
176
|
+
Aibika.warn "#{mod}::#{const} was not loadable"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Guess the load path (from 'paths') that was used to load
|
184
|
+
# 'path'. This is primarily relevant on Ruby 1.8 which stores
|
185
|
+
# "unqualified" paths in $LOADED_FEATURES.
|
186
|
+
def self.find_load_path(loadpaths, feature)
|
187
|
+
if feature.absolute?
|
188
|
+
# Choose those loadpaths which contain the feature
|
189
|
+
candidate_loadpaths = loadpaths.select { |loadpath| feature.subpath?(loadpath.expand) }
|
190
|
+
# Guess the require'd feature
|
191
|
+
feature_pairs = candidate_loadpaths.map { |loadpath| [loadpath, feature.relative_path_from(loadpath.expand)] }
|
192
|
+
# Select the shortest possible require-path (longest load-path)
|
193
|
+
if feature_pairs.empty?
|
194
|
+
nil
|
195
|
+
else
|
196
|
+
feature_pairs.min_by { |_loadpath, feature| feature.path.size }[0]
|
197
|
+
end
|
198
|
+
else
|
199
|
+
# Select the loadpaths that contain 'feature' and select the shortest
|
200
|
+
candidates = loadpaths.select { |loadpath| feature.expand(loadpath).exist? }
|
201
|
+
candidates.max_by { |loadpath| loadpath.path.size }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Find the root of all files specified on the command line and use
|
206
|
+
# it as the "src" of the output.
|
207
|
+
def self.find_src_root(files)
|
208
|
+
src_files = files.map(&:expand)
|
209
|
+
src_prefix = src_files.inject(src_files.first.dirname) do |srcroot, path|
|
210
|
+
unless path.subpath?(Host.exec_prefix)
|
211
|
+
loop do
|
212
|
+
relpath = path.relative_path_from(srcroot)
|
213
|
+
Aibika.fatal_error 'No common directory contains all specified files' if relpath.absolute?
|
214
|
+
break unless relpath.to_s =~ %r{^\.\./}
|
215
|
+
|
216
|
+
srcroot = srcroot.dirname
|
217
|
+
if srcroot == srcroot.dirname
|
218
|
+
Aibika.fatal_error "Endless loop detected in find_src_root method. Reducing srcroot --> #{srcroot.dirname}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
srcroot
|
223
|
+
end
|
224
|
+
src_files = src_files.map do |file|
|
225
|
+
if file.subpath?(src_prefix)
|
226
|
+
file.relative_path_from(src_prefix)
|
227
|
+
else
|
228
|
+
file
|
229
|
+
end
|
230
|
+
end
|
231
|
+
[src_prefix, src_files]
|
232
|
+
end
|
233
|
+
|
234
|
+
# Searches for features that are loaded from gems, then produces a
|
235
|
+
# list of files included in those gems' manifests. Also returns a
|
236
|
+
# list of original features that are caused gems to be
|
237
|
+
# included. Ruby 1.8 provides Gem.loaded_specs to detect gems, but
|
238
|
+
# this is empty with Ruby 1.9. So instead, we look for any loaded
|
239
|
+
# file from a gem path.
|
240
|
+
def self.find_gem_files(features)
|
241
|
+
features_from_gems = []
|
242
|
+
gems = {}
|
243
|
+
|
244
|
+
# If a Bundler Gemfile was provided, add all gems it specifies
|
245
|
+
if Aibika.gemfile
|
246
|
+
Aibika.msg 'Scanning Gemfile'
|
247
|
+
# Load Rubygems and Bundler so we can scan the Gemfile
|
248
|
+
%w[rubygems bundler].each do |lib|
|
249
|
+
require lib
|
250
|
+
rescue LoadError
|
251
|
+
Aibika.fatal_error "Couldn't scan Gemfile, unable to load #{lib}"
|
252
|
+
end
|
253
|
+
|
254
|
+
ENV['BUNDLE_GEMFILE'] = Aibika.gemfile
|
255
|
+
Bundler.load.specs.each do |spec|
|
256
|
+
Aibika.verbose_msg "From Gemfile, adding gem #{spec.full_name}"
|
257
|
+
gems[spec.name] ||= spec unless fence_self?(spec.name)
|
258
|
+
end
|
259
|
+
|
260
|
+
unless gems.any? { |name, _spec| name == 'bundler' }
|
261
|
+
# Bundler itself wasn't added for some reason, let's put it in directly
|
262
|
+
Aibika.verbose_msg 'From Gemfile, forcing inclusion of bundler gem itself'
|
263
|
+
bundler_spec = Gem.loaded_specs['bundler']
|
264
|
+
bundler_spec or Aibika.fatal_error 'Unable to locate bundler gem'
|
265
|
+
gems['bundler'] ||= spec
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
if defined?(Gem)
|
270
|
+
# Include Gems that are loaded
|
271
|
+
Gem.loaded_specs.each { |gemname, spec| gems[gemname] ||= spec unless fence_self?(gemname) }
|
272
|
+
# Fall back to gem detection (loaded_specs are not population on
|
273
|
+
# all Ruby versions)
|
274
|
+
features.each do |feature|
|
275
|
+
# Detect load path unless absolute
|
276
|
+
unless feature.absolute?
|
277
|
+
feature = find_load_path(Pathname($LOAD_PATH), feature)
|
278
|
+
next if feature.nil? # Could be enumerator.so
|
279
|
+
end
|
280
|
+
# Skip if found in known Gem dir
|
281
|
+
if gems.find { |_gem, spec| feature.subpath?(spec.gem_dir) }
|
282
|
+
features_from_gems << feature
|
283
|
+
next
|
284
|
+
end
|
285
|
+
gempaths = Pathname(Gem.path)
|
286
|
+
gempaths.each do |gempath|
|
287
|
+
geminstallpath = Pathname(gempath) / 'gems'
|
288
|
+
next unless feature.subpath?(geminstallpath)
|
289
|
+
|
290
|
+
gemlocalpath = feature.relative_path_from(geminstallpath)
|
291
|
+
fullgemname = gemlocalpath.path.split('/').first
|
292
|
+
gemspecpath = gempath / 'specifications' / "#{fullgemname}.gemspec"
|
293
|
+
if (spec = Gem::Specification.load(gemspecpath))
|
294
|
+
gems[spec.name] ||= spec
|
295
|
+
features_from_gems << feature
|
296
|
+
else
|
297
|
+
Aibika.warn "Failed to load gemspec for '#{fullgemname}'"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
gem_files = []
|
303
|
+
|
304
|
+
gems.each do |gemname, spec|
|
305
|
+
next if fence_self?(gemname)
|
306
|
+
|
307
|
+
if File.exist?(spec.spec_file)
|
308
|
+
@gemspecs << Pathname(spec.spec_file)
|
309
|
+
else
|
310
|
+
spec_name = File.basename(spec.spec_file)
|
311
|
+
spec_path = File.dirname(spec.spec_file)
|
312
|
+
default_spec_file = "#{spec_path}/default/#{spec_name}"
|
313
|
+
if File.exist?(default_spec_file)
|
314
|
+
@gemspecs << Pathname(default_spec_file)
|
315
|
+
Aibika.msg "Using default specification #{default_spec_file} for gem #{spec.full_name}"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# Determine which set of files to include for this particular gem
|
320
|
+
include = %i[loaded files]
|
321
|
+
Aibika.gem.each do |negate, option, list|
|
322
|
+
next unless list.nil? || list.include?(spec.name)
|
323
|
+
|
324
|
+
case option
|
325
|
+
when :minimal
|
326
|
+
include = [:loaded]
|
327
|
+
when :guess
|
328
|
+
include = %i[loaded files]
|
329
|
+
when :all
|
330
|
+
include = %i[scripts files]
|
331
|
+
when :full
|
332
|
+
include = %i[scripts files extras]
|
333
|
+
when :spec
|
334
|
+
include = [:spec]
|
335
|
+
when :scripts
|
336
|
+
if negate
|
337
|
+
include.delete(:scripts)
|
338
|
+
else
|
339
|
+
include.push(:scripts)
|
340
|
+
end
|
341
|
+
when :files
|
342
|
+
if negate
|
343
|
+
include.delete(:files)
|
344
|
+
else
|
345
|
+
include.push(:files)
|
346
|
+
end
|
347
|
+
when :extras
|
348
|
+
if negate
|
349
|
+
include.delete(:extras)
|
350
|
+
else
|
351
|
+
include.push(:extras)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
Aibika.msg "Detected gem #{spec.full_name} (#{include.join(', ')})"
|
357
|
+
|
358
|
+
gem_root = Pathname(spec.gem_dir)
|
359
|
+
gem_extension = (gem_root / '..' / '..' / 'extensions').expand
|
360
|
+
build_complete = if gem_extension.exist?
|
361
|
+
gem_extension.find_all_files(/gem.build_complete/).select do |p|
|
362
|
+
p.dirname.basename.to_s == spec.full_name
|
363
|
+
end
|
364
|
+
end
|
365
|
+
gem_root_files = nil
|
366
|
+
files = []
|
367
|
+
|
368
|
+
unless gem_root.directory?
|
369
|
+
Aibika.warn "Gem #{spec.full_name} root folder was not found, skipping"
|
370
|
+
next
|
371
|
+
end
|
372
|
+
|
373
|
+
# Find the selected files
|
374
|
+
include.each do |set|
|
375
|
+
case set
|
376
|
+
when :spec
|
377
|
+
files << Pathname(spec.files)
|
378
|
+
when :loaded
|
379
|
+
files << features_from_gems.select { |feature| feature.subpath?(gem_root) }
|
380
|
+
when :files
|
381
|
+
gem_root_files ||= gem_root.find_all_files(//)
|
382
|
+
files << gem_root_files.reject { |path| path.relative_path_from(gem_root) =~ GEM_NON_FILE_RE }
|
383
|
+
files << build_complete if build_complete
|
384
|
+
when :extras
|
385
|
+
gem_root_files ||= gem_root.find_all_files(//)
|
386
|
+
files << gem_root_files.select { |path| path.relative_path_from(gem_root) =~ GEM_EXTRA_RE }
|
387
|
+
when :scripts
|
388
|
+
gem_root_files ||= gem_root.find_all_files(//)
|
389
|
+
files << gem_root_files.select { |path| path.relative_path_from(gem_root) =~ GEM_SCRIPT_RE }
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
files.flatten!
|
394
|
+
actual_files = files.select(&:file?)
|
395
|
+
|
396
|
+
(files - actual_files).each do |missing_file|
|
397
|
+
Aibika.warn "#{missing_file} was not found"
|
398
|
+
end
|
399
|
+
|
400
|
+
total_size = actual_files.inject(0) { |size, path| size + path.size }
|
401
|
+
Aibika.msg "\t#{actual_files.size} files, #{total_size} bytes"
|
402
|
+
|
403
|
+
gem_files += actual_files
|
404
|
+
end
|
405
|
+
gem_files = sort_uniq(gem_files)
|
406
|
+
else
|
407
|
+
gem_files = []
|
408
|
+
end
|
409
|
+
features_from_gems -= gem_files
|
410
|
+
[gem_files, features_from_gems]
|
411
|
+
end
|
412
|
+
|
413
|
+
def self.build_exe
|
414
|
+
all_load_paths = $LOAD_PATH.map { |loadpath| Pathname(loadpath).expand }
|
415
|
+
@added_load_paths = ($LOAD_PATH - @load_path_before).map { |loadpath| Pathname(loadpath).expand }
|
416
|
+
working_directory = Pathname.pwd.expand
|
417
|
+
|
418
|
+
restore_environment
|
419
|
+
|
420
|
+
# If the script was run, then detect the features it used
|
421
|
+
if Aibika.run_script && Aibika.load_autoload
|
422
|
+
# Attempt to autoload libraries before doing anything else.
|
423
|
+
attempt_load_autoload
|
424
|
+
end
|
425
|
+
|
426
|
+
# Reject own aibika itself, store the currently loaded files (before we require rbconfig for
|
427
|
+
# our own use).
|
428
|
+
features = $LOADED_FEATURES.reject { |feature| fence_self_dir?(feature) }
|
429
|
+
features.map! { |feature| Pathname(feature) }
|
430
|
+
|
431
|
+
# Since https://github.com/rubygems/rubygems/commit/cad4cf16cf8fcc637d9da643ef97cf0be2ed63cb
|
432
|
+
# rubygems/core_ext/kernel_require.rb is evaled and thus missing in $LOADED_FEATURES,
|
433
|
+
# so we can't find it and need to add it manually
|
434
|
+
features.push(Pathname('rubygems/core_ext/kernel_require.rb'))
|
435
|
+
|
436
|
+
# Find gemspecs to include
|
437
|
+
if defined?(Gem)
|
438
|
+
loaded_specs = Gem.loaded_specs.reject { |name, _info| fence_self?(name) }
|
439
|
+
@gemspecs = loaded_specs.map { |_name, info| Pathname(info.loaded_from) }
|
440
|
+
else
|
441
|
+
@gemspecs = []
|
442
|
+
end
|
443
|
+
|
444
|
+
require 'rbconfig'
|
445
|
+
instsitelibdir = Host.sitelibdir.relative_path_from(Host.exec_prefix)
|
446
|
+
|
447
|
+
load_path = []
|
448
|
+
src_load_path = []
|
449
|
+
|
450
|
+
# Find gems files and remove them from features
|
451
|
+
gem_files, features_from_gems = find_gem_files(features)
|
452
|
+
features -= features_from_gems
|
453
|
+
|
454
|
+
# Find the source root and adjust paths
|
455
|
+
src_prefix, = find_src_root(Aibika.files)
|
456
|
+
# Include encoding support files
|
457
|
+
if Aibika.enc
|
458
|
+
all_load_paths.each do |path|
|
459
|
+
next unless path.subpath?(Host.exec_prefix)
|
460
|
+
|
461
|
+
encpath = path / 'enc'
|
462
|
+
next unless encpath.exist?
|
463
|
+
|
464
|
+
encfiles = encpath.find_all_files(/\.so$/)
|
465
|
+
size = encfiles.inject(0) { |sum, pn| sum + pn.size }
|
466
|
+
Aibika.msg "Including #{encfiles.size} encoding support files (#{size} bytes, use --no-enc to exclude)"
|
467
|
+
features.push(*encfiles)
|
468
|
+
end
|
469
|
+
else
|
470
|
+
Aibika.msg 'Not including encoding support files'
|
471
|
+
end
|
472
|
+
|
473
|
+
# Find features and decide where to put them in the temporary
|
474
|
+
# directory layout.
|
475
|
+
libs = []
|
476
|
+
features.each do |feature|
|
477
|
+
path = find_load_path(all_load_paths, feature)
|
478
|
+
if path.nil? || path.expand == Pathname.pwd
|
479
|
+
Aibika.files << feature
|
480
|
+
else
|
481
|
+
feature = feature.relative_path_from(path.expand) if feature.absolute?
|
482
|
+
fullpath = feature.expand(path)
|
483
|
+
|
484
|
+
if fullpath.subpath?(Host.exec_prefix)
|
485
|
+
# Features found in the Ruby installation are put in the
|
486
|
+
# temporary Ruby installation.
|
487
|
+
libs << [fullpath, fullpath.relative_path_from(Host.exec_prefix)]
|
488
|
+
elsif defined?(Gem) && ((gemhome = Gem.path.find { |pth| fullpath.subpath?(pth) }))
|
489
|
+
# Features found in any other Gem path (e.g. ~/.gems) is put
|
490
|
+
# in a special 'gemhome' folder.
|
491
|
+
targetpath = GEMHOMEDIR / fullpath.relative_path_from(Pathname(gemhome))
|
492
|
+
libs << [fullpath, targetpath]
|
493
|
+
elsif fullpath.subpath?(src_prefix) || path == working_directory
|
494
|
+
# Any feature found inside the src_prefix automatically gets
|
495
|
+
# added as a source file (to go in 'src').
|
496
|
+
Aibika.files << fullpath
|
497
|
+
# Add the load path unless it was added by the script while
|
498
|
+
# running (or we assume that the script can also set it up
|
499
|
+
# correctly when running from the resulting executable).
|
500
|
+
src_load_path << path unless @added_load_paths.include?(path)
|
501
|
+
elsif @added_load_paths.include?(path)
|
502
|
+
# Any feature that exist in a load path added by the script
|
503
|
+
# itself is added as a file to go into the 'src' (src_prefix
|
504
|
+
# will be adjusted below to point to the common parent).
|
505
|
+
Aibika.files << fullpath
|
506
|
+
else
|
507
|
+
# All other feature that can not be resolved go in the the
|
508
|
+
# Ruby sitelibdir. This is automatically in the load path
|
509
|
+
# when Ruby starts.
|
510
|
+
libs << [fullpath, instsitelibdir / feature]
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
# Recompute the src_prefix. Files may have been added implicitly
|
516
|
+
# while scanning through features.
|
517
|
+
src_prefix, src_files = find_src_root(Aibika.files)
|
518
|
+
Aibika.files.replace(src_files)
|
519
|
+
|
520
|
+
# Add the load path that are required with the correct path after
|
521
|
+
# src_prefix was adjusted.
|
522
|
+
load_path += src_load_path.map { |loadpath| TEMPDIR_ROOT / SRCDIR / loadpath.relative_path_from(src_prefix) }
|
523
|
+
|
524
|
+
# Decide where to put gem files, either the system gem folder, or
|
525
|
+
# GEMHOME.
|
526
|
+
gem_files.each do |gemfile|
|
527
|
+
if gemfile.subpath?(Host.exec_prefix)
|
528
|
+
libs << [gemfile, gemfile.relative_path_from(Host.exec_prefix)]
|
529
|
+
elsif defined?(Gem) && ((gemhome = Gem.path.find { |pth| gemfile.subpath?(pth) }))
|
530
|
+
targetpath = GEMHOMEDIR / gemfile.relative_path_from(Pathname(gemhome))
|
531
|
+
libs << [gemfile, targetpath]
|
532
|
+
else
|
533
|
+
Aibika.msg "Processing #{gemfile}"
|
534
|
+
Aibika.msg "Host.exec_prefix #{Host.exec_prefix}"
|
535
|
+
Aibika.msg "Gem: #{Gem}" if defined?(Gem)
|
536
|
+
Aibika.fatal_error "Don't know where to put gem file #{gemfile}"
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
# If requested, add all ruby standard libraries
|
541
|
+
if Aibika.add_all_core
|
542
|
+
Aibika.msg 'Will include all ruby core libraries'
|
543
|
+
@load_path_before.each do |lp|
|
544
|
+
path = Pathname.new(lp)
|
545
|
+
next unless path.to_posix =~
|
546
|
+
%r{/(ruby/(?:site_ruby/|vendor_ruby/)?[0-9.]+)/?$}i
|
547
|
+
|
548
|
+
subdir = ::Regexp.last_match(1)
|
549
|
+
Dir["#{lp}/**/*"].each do |f|
|
550
|
+
fpath = Pathname.new(f)
|
551
|
+
next if fpath.directory?
|
552
|
+
|
553
|
+
tgt = "lib/#{subdir}/#{fpath.relative_path_from(path).to_posix}"
|
554
|
+
libs << [f, tgt]
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
# Detect additional DLLs
|
560
|
+
dlls = Aibika.autodll ? LibraryDetector.detect_dlls : []
|
561
|
+
|
562
|
+
# Detect external manifests
|
563
|
+
manifests = Host.exec_prefix.find_all_files(/\.manifest$/)
|
564
|
+
|
565
|
+
executable = nil
|
566
|
+
if Aibika.output_override
|
567
|
+
executable = Aibika.output_override
|
568
|
+
else
|
569
|
+
executable = Aibika.files.first.basename.ext('.exe')
|
570
|
+
executable.append_to_filename!('-debug') if Aibika.debug
|
571
|
+
end
|
572
|
+
|
573
|
+
windowed = (Aibika.files.first.ext?('.rbw') || Aibika.force_windows) && !Aibika.force_console
|
574
|
+
|
575
|
+
Aibika.msg "Building #{executable}"
|
576
|
+
target_script = nil
|
577
|
+
AibikaBuilder.new(executable, windowed) do |sb|
|
578
|
+
# Add explicitly mentioned files
|
579
|
+
Aibika.msg 'Adding user-supplied source files'
|
580
|
+
Aibika.files.each do |file|
|
581
|
+
file = src_prefix / file
|
582
|
+
target = if file.subpath?(Host.exec_prefix)
|
583
|
+
file.relative_path_from(Host.exec_prefix)
|
584
|
+
elsif file.subpath?(src_prefix)
|
585
|
+
SRCDIR / file.relative_path_from(src_prefix)
|
586
|
+
else
|
587
|
+
SRCDIR / file.basename
|
588
|
+
end
|
589
|
+
|
590
|
+
target_script ||= target
|
591
|
+
|
592
|
+
if file.directory?
|
593
|
+
sb.ensuremkdir(target)
|
594
|
+
else
|
595
|
+
begin
|
596
|
+
sb.createfile(file, target)
|
597
|
+
rescue Errno::ENOENT
|
598
|
+
raise unless file =~ IGNORE_MODULE_NAMES
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
# Add the ruby executable and DLL
|
604
|
+
rubyexe = if windowed
|
605
|
+
Host.rubyw_exe
|
606
|
+
else
|
607
|
+
Host.ruby_exe
|
608
|
+
end
|
609
|
+
Aibika.msg "Adding ruby executable #{rubyexe}"
|
610
|
+
sb.createfile(Host.bindir / rubyexe, BINDIR / rubyexe)
|
611
|
+
sb.createfile(Host.bindir / Host.libruby_so, BINDIR / Host.libruby_so) if Host.libruby_so
|
612
|
+
|
613
|
+
# Add detected DLLs
|
614
|
+
dlls.each do |dll|
|
615
|
+
Aibika.msg "Adding detected DLL #{dll}"
|
616
|
+
target = if dll.subpath?(Host.exec_prefix)
|
617
|
+
dll.relative_path_from(Host.exec_prefix)
|
618
|
+
else
|
619
|
+
BINDIR / File.basename(dll)
|
620
|
+
end
|
621
|
+
sb.createfile(dll, target)
|
622
|
+
end
|
623
|
+
|
624
|
+
# Add external manifest files
|
625
|
+
manifests.each do |manifest|
|
626
|
+
Aibika.msg "Adding external manifest #{manifest}"
|
627
|
+
target = manifest.relative_path_from(Host.exec_prefix)
|
628
|
+
sb.createfile(manifest, target)
|
629
|
+
end
|
630
|
+
|
631
|
+
# Add extra DLLs specified on the command line
|
632
|
+
Aibika.extra_dlls.each do |dll|
|
633
|
+
Aibika.msg "Adding supplied DLL #{dll}"
|
634
|
+
sb.createfile(Host.bindir / dll, BINDIR / dll)
|
635
|
+
end
|
636
|
+
|
637
|
+
# Add gemspec files
|
638
|
+
@gemspecs = sort_uniq(@gemspecs)
|
639
|
+
@gemspecs.each do |gemspec|
|
640
|
+
if gemspec.subpath?(Host.exec_prefix)
|
641
|
+
path = gemspec.relative_path_from(Host.exec_prefix)
|
642
|
+
sb.createfile(gemspec, path)
|
643
|
+
elsif defined?(Gem) && ((gemhome = Pathname(Gem.path.find { |pth| gemspec.subpath?(pth) })))
|
644
|
+
path = GEMHOMEDIR / gemspec.relative_path_from(gemhome)
|
645
|
+
sb.createfile(gemspec, path)
|
646
|
+
else
|
647
|
+
Aibika.fatal_error "Gem spec #{gemspec} does not exist in the Ruby installation. Don't know where to put it."
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
# Add loaded libraries (features, gems)
|
652
|
+
Aibika.msg 'Adding library files'
|
653
|
+
libs.each do |path, target|
|
654
|
+
sb.createfile(path, target)
|
655
|
+
end
|
656
|
+
|
657
|
+
# Set environment variable
|
658
|
+
sb.setenv('RUBYOPT', ENV['RUBYOPT'] || '')
|
659
|
+
sb.setenv('RUBYLIB', load_path.map(&:to_native).uniq.join(';'))
|
660
|
+
|
661
|
+
sb.setenv('GEM_PATH', (TEMPDIR_ROOT / GEMHOMEDIR).to_native)
|
662
|
+
|
663
|
+
# Add the opcode to launch the script
|
664
|
+
extra_arg = Aibika.arg.map { |arg| " \"#{arg.gsub('"', '\"')}\"" }.join
|
665
|
+
installed_ruby_exe = TEMPDIR_ROOT / BINDIR / rubyexe
|
666
|
+
launch_script = (TEMPDIR_ROOT / target_script).to_native
|
667
|
+
sb.postcreateprocess(installed_ruby_exe,
|
668
|
+
"#{rubyexe} \"#{launch_script}\"#{extra_arg}")
|
669
|
+
end
|
670
|
+
|
671
|
+
return if Aibika.inno_script
|
672
|
+
|
673
|
+
Aibika.msg "Finished building #{executable} (#{File.size(executable)} bytes)"
|
674
|
+
end
|
675
|
+
end
|