ocran 1.4.6 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +82 -0
- data/README.md +519 -15
- data/ext/extconf.rb +5 -1
- data/lib/ocran/aot_toolchain.rb +154 -0
- data/lib/ocran/build_helper.rb +7 -0
- data/lib/ocran/command_output.rb +8 -1
- data/lib/ocran/compatibility_scan.rb +414 -0
- data/lib/ocran/cosmo_toolchain.rb +29 -7
- data/lib/ocran/dir_builder.rb +88 -38
- data/lib/ocran/direction.rb +292 -44
- data/lib/ocran/ed_resource.rb +113 -0
- data/lib/ocran/ed_resource_builder.rb +199 -0
- data/lib/ocran/env_hook.rb +98 -0
- data/lib/ocran/file_path_set.rb +9 -5
- data/lib/ocran/gem_spec_queryable.rb +13 -5
- data/lib/ocran/host_config_helper.rb +11 -1
- data/lib/ocran/inno_setup_script_builder.rb +24 -14
- data/lib/ocran/launcher_batch_builder.rb +8 -11
- data/lib/ocran/npm_package.rb +106 -0
- data/lib/ocran/option.rb +465 -35
- data/lib/ocran/pe_imports.rb +96 -0
- data/lib/ocran/pe_resources.rb +226 -0
- data/lib/ocran/refine_pathname.rb +29 -32
- data/lib/ocran/roundhouse_builder.rb +316 -0
- data/lib/ocran/runner.rb +52 -11
- data/lib/ocran/runtime_sdk.rb +259 -0
- data/lib/ocran/spinel_builder.rb +183 -0
- data/lib/ocran/spinel_compatibility.rb +149 -0
- data/lib/ocran/static_runtime/boot.rb +443 -0
- data/lib/ocran/static_runtime/build_static_ext.rb +86 -0
- data/lib/ocran/static_runtime_builder.rb +814 -0
- data/lib/ocran/stub_builder.rb +70 -25
- data/lib/ocran/version.rb +1 -1
- data/lib/ocran/wasm_builder.rb +570 -0
- data/lib/ocran/wasm_compatibility.rb +198 -0
- data/lib/ocran/windows_command_escaping.rb +21 -0
- data/lib/ocran/zip_payload_builder.rb +18 -9
- data/lib/ocran/zip_writer.rb +48 -9
- data/src/Makefile +18 -2
- data/src/Makefile.mswin +65 -0
- data/src/inst_dir.c +34 -6
- data/src/path_utils.c +144 -0
- data/src/script_info.c +5 -0
- data/src/stub.c +77 -19
- data/src/stub.rc +1 -1
- data/src/system_utils.c +276 -155
- data/src/system_utils.h +46 -3
- data/src/system_utils_posix.c +243 -148
- data/src/unpack.c +9 -0
- metadata +25 -4
data/lib/ocran/option.rb
CHANGED
|
@@ -6,15 +6,21 @@ module Ocran
|
|
|
6
6
|
load File.expand_path("refine_pathname.rb", __dir__) unless defined? RefinePathname
|
|
7
7
|
using RefinePathname
|
|
8
8
|
|
|
9
|
+
# Valid values for --set-requested-execution-level (manifest UAC levels).
|
|
10
|
+
EXECUTION_LEVELS = %w[asInvoker highestAvailable requireAdministrator].freeze
|
|
11
|
+
|
|
9
12
|
def initialize
|
|
10
13
|
@options = {
|
|
11
14
|
:add_all_core? => false,
|
|
12
15
|
:add_all_encoding? => true,
|
|
16
|
+
:application_manifest => nil,
|
|
13
17
|
:argv => [],
|
|
14
18
|
:auto_detect_dlls? => true,
|
|
15
19
|
:bundle_identifier => nil,
|
|
16
20
|
:macosx_bundle => nil,
|
|
17
21
|
:macosx_bundle? => false,
|
|
22
|
+
:runtime_dlls => [],
|
|
23
|
+
:static_runtime => nil,
|
|
18
24
|
:chdir_before? => false,
|
|
19
25
|
:chdir_exe_dir? => false,
|
|
20
26
|
:cosmo_cc => nil,
|
|
@@ -23,26 +29,44 @@ module Ocran
|
|
|
23
29
|
:enable_compression? => true,
|
|
24
30
|
:enable_debug_extract? => false,
|
|
25
31
|
:enable_debug_mode? => false,
|
|
32
|
+
:execution_level => nil,
|
|
26
33
|
:extra_dlls => [],
|
|
34
|
+
:file_version => nil,
|
|
27
35
|
:force_console? => false,
|
|
28
36
|
:force_windows? => false,
|
|
29
37
|
:gem_options => [],
|
|
30
38
|
:gemfile => nil,
|
|
31
39
|
:icon_filename => nil,
|
|
40
|
+
:inputs => [],
|
|
32
41
|
:inno_setup_script => nil,
|
|
33
42
|
:load_autoload? => true,
|
|
34
43
|
:output_dir => nil,
|
|
35
44
|
:output_override => nil,
|
|
36
45
|
:output_zip => nil,
|
|
46
|
+
:product_version => nil,
|
|
37
47
|
:quiet? => false,
|
|
48
|
+
:resource_strings => {},
|
|
49
|
+
:roundhouse? => false,
|
|
50
|
+
:roundhouse_app => nil,
|
|
51
|
+
:roundhouse_options => [],
|
|
52
|
+
:roundhouse_output => nil,
|
|
38
53
|
:rubyopt => nil,
|
|
39
54
|
:run_script? => true,
|
|
40
55
|
:script => nil,
|
|
41
56
|
:source_files => [],
|
|
57
|
+
:spinel? => false,
|
|
58
|
+
:spinel_options => [],
|
|
42
59
|
:verbose? => false,
|
|
60
|
+
:version_strings => {},
|
|
43
61
|
:warning? => true,
|
|
62
|
+
:wasm_options => [],
|
|
63
|
+
:wasm_output => nil,
|
|
64
|
+
:wasm_ruby => nil,
|
|
65
|
+
:wasm_runtime => nil,
|
|
66
|
+
:wasm_zip? => false,
|
|
44
67
|
:wrapper_exe? => true,
|
|
45
68
|
}
|
|
69
|
+
@warnings = []
|
|
46
70
|
end
|
|
47
71
|
|
|
48
72
|
def usage
|
|
@@ -69,7 +93,7 @@ Gem content detection modes:
|
|
|
69
93
|
--gem-guess=[gem1,...] Include loaded scripts & best guess (DEFAULT)
|
|
70
94
|
--gem-all[=gem1,..] Include all scripts & files
|
|
71
95
|
--gem-full[=gem1,..] Include EVERYTHING
|
|
72
|
-
--gem-spec[=gem1,..] Include files in gemspec
|
|
96
|
+
--gem-spec[=gem1,..] Include the files listed in the gemspec
|
|
73
97
|
|
|
74
98
|
minimal: loaded scripts
|
|
75
99
|
guess: loaded scripts and other files
|
|
@@ -86,7 +110,8 @@ Gem content detection modes:
|
|
|
86
110
|
|
|
87
111
|
Auto-detection options:
|
|
88
112
|
|
|
89
|
-
--no-dep-run Don't run script.rb to check for dependencies
|
|
113
|
+
--no-dep-run Don't run script.rb to check for dependencies (usually
|
|
114
|
+
needs --add-all-core, and --gem-full with --gemfile).
|
|
90
115
|
--no-autoload Don't load/include script.rb's autoloads.
|
|
91
116
|
--no-autodll Disable detection of runtime DLL dependencies.
|
|
92
117
|
|
|
@@ -116,8 +141,38 @@ Executable options:
|
|
|
116
141
|
--debug Executable will be verbose.
|
|
117
142
|
--debug-extract Executable will unpack to local dir and not delete after.
|
|
118
143
|
|
|
144
|
+
PE resource options (Windows, rcedit-compatible):
|
|
145
|
+
|
|
146
|
+
--set-version-string <key> <value> Add a StringFileInfo entry (e.g. CompanyName).
|
|
147
|
+
Alias: --version-string
|
|
148
|
+
--set-file-version <x.y.z.w> Set the file version. Alias: --file-version
|
|
149
|
+
--set-product-version <x.y.z.w> Set the product version. Alias: --product-version
|
|
150
|
+
--set-resource-string <id> <value> Set an RT_STRING table entry by numeric id.
|
|
151
|
+
Alias: --resource-string
|
|
152
|
+
--set-requested-execution-level <level> Set the manifest UAC level (asInvoker,
|
|
153
|
+
highestAvailable, requireAdministrator).
|
|
154
|
+
Alias: --uac-level
|
|
155
|
+
--application-manifest <file> Embed the given file as the application manifest.
|
|
156
|
+
Alias: --manifest
|
|
157
|
+
|
|
119
158
|
Experimental options:
|
|
120
159
|
|
|
160
|
+
--static-runtime <runtime-dir> Build a single executable that runs the
|
|
161
|
+
application without extracting anything: a statically
|
|
162
|
+
linked Ruby (the static runtime in <runtime-dir>, built
|
|
163
|
+
with the scripts in runtime/) with the application as a
|
|
164
|
+
ZIP archive. The runtime decides the target: Windows or
|
|
165
|
+
Linux (archive appended, readable with unzip or 7-Zip)
|
|
166
|
+
or macOS (archive linked in as section __OCRAN,__zip;
|
|
167
|
+
packaging needs a Mac). The runtime's own standard
|
|
168
|
+
library and bundled gems are packed; C extensions must
|
|
169
|
+
be linked into the runtime. The build host's Ruby must
|
|
170
|
+
have the runtime's API version (e.g. 4.0.0).
|
|
171
|
+
--runtime-dll <lib,...> With --static-runtime: link these C libraries of the
|
|
172
|
+
runtime's SDK as DLLs shipped next to the executable
|
|
173
|
+
instead of into it (e.g. iconv,sybdb - the LGPL ones;
|
|
174
|
+
see runtime/LICENSES.md). Their DLL dependencies come
|
|
175
|
+
along.
|
|
121
176
|
--cosmo-ruby <ruby.com> Package the given cosmopolitan-built Ruby (an APE,
|
|
122
177
|
e.g. ruby.com) as the interpreter instead of the host
|
|
123
178
|
Ruby: the result is a single <scriptname>.com that runs
|
|
@@ -144,6 +199,50 @@ Experimental options:
|
|
|
144
199
|
packages the host Ruby behind an APE stub. Console-only;
|
|
145
200
|
output defaults to <scriptname>.com.
|
|
146
201
|
(Alias: --cosmo-toolchain)
|
|
202
|
+
|
|
203
|
+
Native compilation (experimental):
|
|
204
|
+
|
|
205
|
+
--spinel Compile script.rb ahead of time with Spinel
|
|
206
|
+
(https://github.com/matz/spinel) into a native executable
|
|
207
|
+
that needs no Ruby, instead of packaging the interpreter.
|
|
208
|
+
Spinel compiles a subset of Ruby: when the program is
|
|
209
|
+
outside it, OCRAN reports what in your code and in which
|
|
210
|
+
gems stands in the way. The script is not run at build
|
|
211
|
+
time. Requires the spinel command (SPINEL, PATH, or
|
|
212
|
+
~/.local/bin); OCRAN explains how to install it.
|
|
213
|
+
--spinel-opt <arg> Pass <arg> to the spinel compiler (repeatable), e.g.
|
|
214
|
+
--spinel-opt --int-overflow=promote
|
|
215
|
+
--roundhouse Compile a Rails application with Roundhouse
|
|
216
|
+
(https://github.com/rubys/roundhouse) and Spinel into a
|
|
217
|
+
native server binary. Give the application directory
|
|
218
|
+
instead of a script (default: the current directory).
|
|
219
|
+
The output is a directory (default <app>-spinel, or
|
|
220
|
+
--output/--output-dir) with the binary and the files it
|
|
221
|
+
serves. When the app is not covered yet, OCRAN shows
|
|
222
|
+
Roundhouse's analysis, including which gems it does not
|
|
223
|
+
support. Requires roundhouse, spinel and spin.
|
|
224
|
+
--roundhouse-opt <arg> Pass <arg> to roundhouse (repeatable), e.g.
|
|
225
|
+
--roundhouse-opt --survey
|
|
226
|
+
|
|
227
|
+
WebAssembly (experimental):
|
|
228
|
+
|
|
229
|
+
--wasm[=ruby|picoruby] Export the application as a web page that runs it
|
|
230
|
+
in the browser on WebAssembly: a folder (default
|
|
231
|
+
<scriptname>-wasm, or --output-dir) or a zip archive
|
|
232
|
+
(--output-zip, or an --output name ending in .zip) with
|
|
233
|
+
index.html, the runtime and the application.
|
|
234
|
+
ruby: ruby.wasm, CRuby with its standard library; needs
|
|
235
|
+
rbwasm (gem install ruby_wasm). Gems must be listed in
|
|
236
|
+
the application's Gemfile.
|
|
237
|
+
picoruby: PicoRuby, a 2 MB mruby-based runtime with its
|
|
238
|
+
own small library and no gems; needs nothing installed.
|
|
239
|
+
OCRAN reports what in the program and its gems may not
|
|
240
|
+
work there. The script is not run at build time.
|
|
241
|
+
--picoruby Same as --wasm=picoruby.
|
|
242
|
+
--wasm-ruby <x.y> The CRuby version for --wasm (4.0, 3.4, 3.3 or 3.2;
|
|
243
|
+
default: this Ruby's, when ruby.wasm has it).
|
|
244
|
+
--wasm-opt <arg> Pass <arg> to `rbwasm build` (repeatable), used when the
|
|
245
|
+
application has gems.
|
|
147
246
|
EOF
|
|
148
247
|
end
|
|
149
248
|
|
|
@@ -163,6 +262,32 @@ EOF
|
|
|
163
262
|
load File.expand_path("cosmo_toolchain.rb", __dir__) unless defined? CosmoToolchain
|
|
164
263
|
end
|
|
165
264
|
|
|
265
|
+
# The --gem-<group> options GemSpecQueryable.gem_inclusion_set knows,
|
|
266
|
+
# and the file sets among them that --no-gem-<set> can take away.
|
|
267
|
+
GEM_GROUPS = %i[minimal guess all full spec scripts files extras].freeze
|
|
268
|
+
GEM_FILE_SETS = %i[scripts files extras].freeze
|
|
269
|
+
|
|
270
|
+
# Warnings about the command line, for the caller to print once output
|
|
271
|
+
# is set up.
|
|
272
|
+
attr_reader :warnings
|
|
273
|
+
|
|
274
|
+
# The value of an option that takes a path or name: the next argument,
|
|
275
|
+
# which must be there and must not be empty.
|
|
276
|
+
def required_argument(argv, option)
|
|
277
|
+
value = argv.shift
|
|
278
|
+
raise "#{option} requires an argument" if value.nil? || value.empty?
|
|
279
|
+
|
|
280
|
+
value
|
|
281
|
+
end
|
|
282
|
+
private :required_argument
|
|
283
|
+
|
|
284
|
+
# Whether a --gem-* option makes every gem be packed with all of its
|
|
285
|
+
# scripts, which is what a build without the dependency run needs.
|
|
286
|
+
def whole_gems?
|
|
287
|
+
gem_options.any? { |negate, group, list| list.nil? && !negate && %i[all full spec scripts].include?(group) }
|
|
288
|
+
end
|
|
289
|
+
private :whole_gems?
|
|
290
|
+
|
|
166
291
|
def parse(argv)
|
|
167
292
|
while (arg = argv.shift)
|
|
168
293
|
case arg
|
|
@@ -173,23 +298,19 @@ EOF
|
|
|
173
298
|
when "--add-all-core"
|
|
174
299
|
@options[:add_all_core?] = true
|
|
175
300
|
when "--output"
|
|
176
|
-
|
|
177
|
-
@options[:output_override] = Pathname.new(path).expand_path if path
|
|
301
|
+
@options[:output_override] = Pathname.new(required_argument(argv, arg)).expand_path
|
|
178
302
|
when "--output-dir"
|
|
179
|
-
|
|
180
|
-
@options[:output_dir] = Pathname.new(path).expand_path if path
|
|
303
|
+
@options[:output_dir] = Pathname.new(required_argument(argv, arg)).expand_path
|
|
181
304
|
when "--output-zip"
|
|
182
|
-
|
|
183
|
-
@options[:output_zip] = Pathname.new(path).expand_path if path
|
|
305
|
+
@options[:output_zip] = Pathname.new(required_argument(argv, arg)).expand_path
|
|
184
306
|
when "--no-wrapper-exe"
|
|
185
307
|
@options[:wrapper_exe?] = false
|
|
186
308
|
when "--macosx-bundle"
|
|
187
309
|
@options[:macosx_bundle?] = true
|
|
188
310
|
when "--bundle-id"
|
|
189
|
-
@options[:bundle_identifier] = argv
|
|
311
|
+
@options[:bundle_identifier] = required_argument(argv, arg)
|
|
190
312
|
when "--dll"
|
|
191
|
-
|
|
192
|
-
@options[:extra_dlls] << path if path
|
|
313
|
+
@options[:extra_dlls] << required_argument(argv, arg)
|
|
193
314
|
when "--quiet"
|
|
194
315
|
@options[:quiet?] = true
|
|
195
316
|
when "--verbose"
|
|
@@ -205,26 +326,81 @@ EOF
|
|
|
205
326
|
when "--chdir-exe-dir"
|
|
206
327
|
@options[:chdir_exe_dir?] = true
|
|
207
328
|
when "--icon"
|
|
208
|
-
path = argv
|
|
209
|
-
raise "Icon file #{path} not found" unless
|
|
329
|
+
path = required_argument(argv, arg)
|
|
330
|
+
raise "Icon file #{path} not found" unless File.exist?(path)
|
|
210
331
|
@options[:icon_filename] = Pathname.new(path).expand_path
|
|
332
|
+
when "--set-version-string", "--version-string"
|
|
333
|
+
key = argv.shift
|
|
334
|
+
value = argv.shift
|
|
335
|
+
raise "#{arg} requires a key and a value" unless key && value
|
|
336
|
+
@options[:version_strings][key] = value
|
|
337
|
+
when "--set-file-version", "--file-version"
|
|
338
|
+
value = argv.shift
|
|
339
|
+
raise "#{arg} requires a version string" unless value
|
|
340
|
+
@options[:file_version] = value
|
|
341
|
+
when "--set-product-version", "--product-version"
|
|
342
|
+
value = argv.shift
|
|
343
|
+
raise "#{arg} requires a version string" unless value
|
|
344
|
+
@options[:product_version] = value
|
|
345
|
+
when "--set-resource-string", "--resource-string"
|
|
346
|
+
id = argv.shift
|
|
347
|
+
value = argv.shift
|
|
348
|
+
raise "#{arg} requires an id and a value" unless id && value
|
|
349
|
+
@options[:resource_strings][id.to_i] = value
|
|
350
|
+
when "--set-requested-execution-level", "--uac-level"
|
|
351
|
+
level = argv.shift
|
|
352
|
+
unless EXECUTION_LEVELS.include?(level)
|
|
353
|
+
raise "#{arg} must be one of: #{EXECUTION_LEVELS.join(", ")}"
|
|
354
|
+
end
|
|
355
|
+
@options[:execution_level] = level
|
|
356
|
+
when "--application-manifest", "--manifest"
|
|
357
|
+
path = argv.shift
|
|
358
|
+
raise "Manifest file #{path} not found" unless path && File.exist?(path)
|
|
359
|
+
@options[:application_manifest] = Pathname.new(path).expand_path
|
|
211
360
|
when "--rubyopt"
|
|
212
|
-
|
|
361
|
+
# An empty value is meaningful: run with no RUBYOPT at all.
|
|
362
|
+
@options[:rubyopt] = argv.shift or raise "#{arg} requires an argument"
|
|
213
363
|
when "--cosmo", "--cosmo-toolchain"
|
|
214
364
|
# Kept unresolved until validation: resolving here would report
|
|
215
365
|
# "cosmocc ... is not executable" on a Windows build host, ahead of
|
|
216
366
|
# the clearer "not supported when building on Windows" check.
|
|
217
|
-
@options[:cosmo_cc] = argv
|
|
367
|
+
@options[:cosmo_cc] = required_argument(argv, arg)
|
|
368
|
+
when "--spinel"
|
|
369
|
+
@options[:spinel?] = true
|
|
370
|
+
when "--spinel-opt"
|
|
371
|
+
@options[:spinel_options] << required_argument(argv, arg)
|
|
372
|
+
when "--roundhouse"
|
|
373
|
+
@options[:roundhouse?] = true
|
|
374
|
+
when /\A--wasm(?:=(.*))?\z/
|
|
375
|
+
runtime = $1.nil? || $1.empty? ? "ruby" : $1
|
|
376
|
+
@options[:wasm_runtime] =
|
|
377
|
+
case runtime
|
|
378
|
+
when "ruby", "ruby.wasm", "cruby" then :ruby
|
|
379
|
+
when "picoruby" then :picoruby
|
|
380
|
+
else raise "Unknown --wasm runtime #{runtime.inspect} (use --wasm=ruby or --wasm=picoruby)"
|
|
381
|
+
end
|
|
382
|
+
when "--picoruby"
|
|
383
|
+
@options[:wasm_runtime] = :picoruby
|
|
384
|
+
when "--wasm-ruby"
|
|
385
|
+
@options[:wasm_ruby] = required_argument(argv, arg)
|
|
386
|
+
when "--wasm-opt"
|
|
387
|
+
@options[:wasm_options] << required_argument(argv, arg)
|
|
388
|
+
when "--roundhouse-opt"
|
|
389
|
+
@options[:roundhouse_options] << required_argument(argv, arg)
|
|
390
|
+
when "--static-runtime"
|
|
391
|
+
@options[:static_runtime] = Pathname.new(required_argument(argv, arg)).expand_path
|
|
392
|
+
when "--runtime-dll"
|
|
393
|
+
@options[:runtime_dlls] |= required_argument(argv, arg).split(",").map(&:strip).reject(&:empty?)
|
|
218
394
|
when "--cosmo-ruby"
|
|
219
395
|
load_cosmo_toolchain
|
|
220
396
|
@options[:cosmo_ruby] = CosmoToolchain.resolve_ruby(argv.shift)
|
|
221
397
|
when "--gemfile"
|
|
222
|
-
path = argv
|
|
223
|
-
raise "Gemfile #{path} not found" unless
|
|
398
|
+
path = required_argument(argv, arg)
|
|
399
|
+
raise "Gemfile #{path} not found" unless File.exist?(path)
|
|
224
400
|
@options[:gemfile] = Pathname.new(path).expand_path
|
|
225
401
|
when "--innosetup"
|
|
226
|
-
path = argv
|
|
227
|
-
raise "Inno Script #{path} not found" unless
|
|
402
|
+
path = required_argument(argv, arg)
|
|
403
|
+
raise "Inno Script #{path} not found" unless File.exist?(path)
|
|
228
404
|
@options[:inno_setup_script] = Pathname.new(path).expand_path
|
|
229
405
|
when "--no-autodll"
|
|
230
406
|
@options[:auto_detect_dlls?] = false
|
|
@@ -246,25 +422,44 @@ EOF
|
|
|
246
422
|
@options[:add_all_encoding?] = !$1
|
|
247
423
|
when /\A--(no-)?gem-(\w+)(?:=(.*))?$/
|
|
248
424
|
negate, group, list = $1, $2, $3
|
|
249
|
-
|
|
250
|
-
|
|
425
|
+
group = group.to_sym
|
|
426
|
+
if negate ? !GEM_FILE_SETS.include?(group) : !GEM_GROUPS.include?(group)
|
|
427
|
+
raise "Invalid gem content detection option #{arg}: use " \
|
|
428
|
+
"#{GEM_GROUPS.map { |g| "--gem-#{g}" }.join(", ")} or " \
|
|
429
|
+
"#{GEM_FILE_SETS.map { |g| "--no-gem-#{g}" }.join(", ")}"
|
|
430
|
+
end
|
|
431
|
+
@options[:gem_options] << [negate, group, list&.split(",")]
|
|
432
|
+
when "--help", "-h"
|
|
251
433
|
puts usage
|
|
252
434
|
raise SystemExit
|
|
435
|
+
when /\A--./
|
|
436
|
+
raise "Unknown option #{arg} (see ocran --help)"
|
|
253
437
|
else
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
expanded = [arg]
|
|
258
|
-
end
|
|
438
|
+
@options[:inputs] << arg
|
|
439
|
+
end
|
|
440
|
+
end
|
|
259
441
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
442
|
+
if roundhouse?
|
|
443
|
+
# A Rails application directory rather than a script: none of the
|
|
444
|
+
# packaging below applies, and the application is not loaded.
|
|
445
|
+
parse_roundhouse
|
|
446
|
+
return
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
@options[:inputs].each do |arg|
|
|
450
|
+
expanded = Dir.glob(arg)
|
|
451
|
+
if expanded.empty?
|
|
452
|
+
raise "#{arg} not found!" unless File.exist?(arg)
|
|
453
|
+
expanded = [arg]
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
expanded.each do |f|
|
|
457
|
+
if File.directory?(f)
|
|
458
|
+
raise "#{f} is empty!" if Dir.empty?(f)
|
|
459
|
+
# If a directory is passed, we want all files under that directory
|
|
460
|
+
@options[:source_files] += Pathname.new(f).find.reject(&:directory?).map(&:expand_path)
|
|
461
|
+
else
|
|
462
|
+
@options[:source_files] << Pathname.new(f).expand_path
|
|
268
463
|
end
|
|
269
464
|
end
|
|
270
465
|
end
|
|
@@ -273,6 +468,17 @@ EOF
|
|
|
273
468
|
|
|
274
469
|
@options[:script] = source_files.first
|
|
275
470
|
|
|
471
|
+
if wasm_runtime
|
|
472
|
+
parse_wasm
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
if spinel?
|
|
476
|
+
reject_with_native_compilation("--spinel", output_dir: "--output-dir")
|
|
477
|
+
# Spinel compiles the source; it never needs the program to run, and
|
|
478
|
+
# running it would only pull OCRAN's dependency detection in.
|
|
479
|
+
@options[:run_script?] = false
|
|
480
|
+
end
|
|
481
|
+
|
|
276
482
|
@options[:force_autoload?] = run_script? && load_autoload?
|
|
277
483
|
|
|
278
484
|
@options[:output_executable] =
|
|
@@ -281,7 +487,8 @@ EOF
|
|
|
281
487
|
# without .exe - `--output myapp`, as a build script shared with
|
|
282
488
|
# Linux and macOS writes it - gets one there. .exe and .com names
|
|
283
489
|
# are used as given.
|
|
284
|
-
|
|
490
|
+
windows_target = static_runtime ? static_runtime_os == "windows" : Gem.win_platform?
|
|
491
|
+
if windows_target && !output_override.extname?(".exe") && !output_override.extname?(".com")
|
|
285
492
|
Pathname("#{output_override}.exe")
|
|
286
493
|
else
|
|
287
494
|
output_override
|
|
@@ -294,6 +501,8 @@ EOF
|
|
|
294
501
|
# APE binaries built with cosmocc conventionally use .com.
|
|
295
502
|
ext = if cosmo?
|
|
296
503
|
".com"
|
|
504
|
+
elsif static_runtime
|
|
505
|
+
static_runtime_os == "windows" ? ".exe" : ""
|
|
297
506
|
elsif Gem.win_platform?
|
|
298
507
|
".exe"
|
|
299
508
|
else
|
|
@@ -311,6 +520,28 @@ EOF
|
|
|
311
520
|
raise "--chdir-first and --chdir-exe-dir cannot be used together"
|
|
312
521
|
end
|
|
313
522
|
|
|
523
|
+
if force_windows? && force_console?
|
|
524
|
+
raise "--windows and --console cannot be used together"
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
if inno_setup_script && (output_dir || output_zip)
|
|
528
|
+
raise "--innosetup cannot be combined with --output-dir or --output-zip"
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
unless run_script?
|
|
532
|
+
# Without the dependency run nothing the script loads is detected,
|
|
533
|
+
# so the libraries it needs must be named some other way.
|
|
534
|
+
unless add_all_core?
|
|
535
|
+
@warnings << "--no-dep-run without --add-all-core: the script is not run, so none of the " \
|
|
536
|
+
"standard library it requires is detected or packed"
|
|
537
|
+
end
|
|
538
|
+
if gemfile && !whole_gems?
|
|
539
|
+
@warnings << "--no-dep-run with --gemfile but without --gem-all or --gem-full: the script " \
|
|
540
|
+
"is not run, so no gem file it loads is detected, and the gems are packed " \
|
|
541
|
+
"without their scripts"
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
314
545
|
@options[:use_inno_setup?] = !!inno_setup_script
|
|
315
546
|
|
|
316
547
|
@options[:verbose?] &&= !quiet?
|
|
@@ -373,12 +604,112 @@ EOF
|
|
|
373
604
|
if macosx_bundle && (output_dir || output_zip || inno_setup_script)
|
|
374
605
|
raise "--macosx-bundle cannot be combined with --output-dir, --output-zip, or --innosetup"
|
|
375
606
|
end
|
|
607
|
+
|
|
608
|
+
if static_runtime
|
|
609
|
+
reject_with_static_runtime
|
|
610
|
+
raise "#{static_runtime} is not a directory" unless static_runtime.directory?
|
|
611
|
+
end
|
|
612
|
+
raise "--runtime-dll needs --static-runtime" if runtime_dlls.any? && !static_runtime
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
# --static-runtime produces one executable from a prebuilt runtime, so neither
|
|
616
|
+
# the other output formats nor what only the C stub implements apply.
|
|
617
|
+
def reject_with_static_runtime
|
|
618
|
+
conflicts = {
|
|
619
|
+
"--output-dir" => output_dir,
|
|
620
|
+
"--output-zip" => output_zip,
|
|
621
|
+
"--innosetup" => inno_setup_script,
|
|
622
|
+
"--macosx-bundle" => @options[:macosx_bundle?],
|
|
623
|
+
"--cosmo/--cosmo-ruby" => cosmo?,
|
|
624
|
+
"--spinel/--roundhouse" => spinel? || roundhouse?,
|
|
625
|
+
"--wasm" => wasm_runtime,
|
|
626
|
+
"--debug-extract" => enable_debug_extract?,
|
|
627
|
+
}
|
|
628
|
+
conflicts.each do |name, given|
|
|
629
|
+
raise "#{name} cannot be used with --static-runtime" if given
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
private :reject_with_static_runtime
|
|
633
|
+
|
|
634
|
+
# Validates --wasm and works out where the site goes: a zip archive for
|
|
635
|
+
# --output-zip or an --output name ending in .zip, a folder otherwise.
|
|
636
|
+
def parse_wasm
|
|
637
|
+
mode = wasm_runtime == :picoruby ? "--wasm=picoruby" : "--wasm"
|
|
638
|
+
reject_with_native_compilation(mode, output_zip: false)
|
|
639
|
+
raise "#{mode} cannot be used with --spinel or --roundhouse" if spinel? || roundhouse?
|
|
640
|
+
raise "--output-dir and --output-zip cannot be used together" if output_dir && output_zip
|
|
641
|
+
|
|
642
|
+
suffix = wasm_runtime == :picoruby ? "-picoruby" : "-wasm"
|
|
643
|
+
if output_zip || output_override&.extname?(".zip")
|
|
644
|
+
@options[:wasm_zip?] = true
|
|
645
|
+
@options[:wasm_output] = output_zip || output_override
|
|
646
|
+
else
|
|
647
|
+
@options[:wasm_output] = output_dir || output_override ||
|
|
648
|
+
Pathname("#{script.basename(".*")}#{suffix}").expand_path
|
|
649
|
+
end
|
|
650
|
+
# Nothing runs at build time: the browser is where the program runs.
|
|
651
|
+
@options[:run_script?] = false
|
|
652
|
+
end
|
|
653
|
+
private :parse_wasm
|
|
654
|
+
|
|
655
|
+
# Validates --roundhouse and works out the application and the output
|
|
656
|
+
# directory.
|
|
657
|
+
def parse_roundhouse
|
|
658
|
+
raise "--static-runtime cannot be used with --roundhouse" if static_runtime
|
|
659
|
+
raise "--roundhouse takes one Rails application directory" if @options[:inputs].size > 1
|
|
660
|
+
reject_with_native_compilation("--roundhouse")
|
|
661
|
+
raise "--spinel and --roundhouse cannot be used together (--roundhouse compiles with Spinel already)" if spinel?
|
|
662
|
+
|
|
663
|
+
input = Pathname(@options[:inputs].first || Dir.pwd).expand_path
|
|
664
|
+
raise "#{input} not found!" unless input.exist?
|
|
665
|
+
|
|
666
|
+
app = rails_root(input.directory? ? input : input.dirname)
|
|
667
|
+
unless app
|
|
668
|
+
raise "#{input} is not a Rails application (no config/application.rb in it or above it); " \
|
|
669
|
+
"--roundhouse compiles a Rails app - use --spinel for a plain script"
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
output = output_dir || output_override || Pathname("#{app.basename}-spinel").expand_path
|
|
673
|
+
raise "The output directory #{output} would overwrite the application itself" if output == app
|
|
674
|
+
|
|
675
|
+
@options[:roundhouse_app] = app
|
|
676
|
+
@options[:roundhouse_output] = output
|
|
677
|
+
@options[:run_script?] = false
|
|
678
|
+
@options[:force_autoload?] = false
|
|
679
|
+
@options[:verbose?] &&= !quiet?
|
|
680
|
+
end
|
|
681
|
+
private :parse_roundhouse
|
|
682
|
+
|
|
683
|
+
# The Rails application root at or above the given directory.
|
|
684
|
+
def rails_root(dir)
|
|
685
|
+
dir.ascend.find { |d| (d + "config" + "application.rb").file? }
|
|
686
|
+
end
|
|
687
|
+
private :rails_root
|
|
688
|
+
|
|
689
|
+
# Native compilation produces a program, not a package of one, so the
|
|
690
|
+
# options that shape a package do not apply to it.
|
|
691
|
+
def reject_with_native_compilation(mode, output_dir: nil, output_zip: true)
|
|
692
|
+
conflicts = {
|
|
693
|
+
"--innosetup" => inno_setup_script,
|
|
694
|
+
"--macosx-bundle" => @options[:macosx_bundle?],
|
|
695
|
+
"--cosmo/--cosmo-ruby" => cosmo?,
|
|
696
|
+
"--windows" => force_windows?,
|
|
697
|
+
"--icon" => icon_filename,
|
|
698
|
+
}
|
|
699
|
+
conflicts[output_dir] = self.output_dir if output_dir
|
|
700
|
+
conflicts["--output-zip"] = self.output_zip if output_zip
|
|
701
|
+
conflicts.each do |name, given|
|
|
702
|
+
raise "#{name} cannot be used with #{mode}" if given
|
|
703
|
+
end
|
|
376
704
|
end
|
|
705
|
+
private :reject_with_native_compilation
|
|
377
706
|
|
|
378
707
|
def add_all_core? = @options[__method__]
|
|
379
708
|
|
|
380
709
|
def add_all_encoding? = @options[__method__]
|
|
381
710
|
|
|
711
|
+
def application_manifest = @options[__method__]
|
|
712
|
+
|
|
382
713
|
def argv = @options[__method__]
|
|
383
714
|
|
|
384
715
|
def bundle_identifier = @options[__method__]
|
|
@@ -394,6 +725,23 @@ EOF
|
|
|
394
725
|
|
|
395
726
|
def cosmo_ruby = @options[__method__]
|
|
396
727
|
|
|
728
|
+
# Directory of the static runtime given with --static-runtime, or nil.
|
|
729
|
+
def static_runtime = @options[__method__]
|
|
730
|
+
|
|
731
|
+
def static_runtime? = !!static_runtime
|
|
732
|
+
|
|
733
|
+
# The operating system the --static-runtime runtime is for, from its
|
|
734
|
+
# runtime.json ("windows" when that cannot be read yet).
|
|
735
|
+
def static_runtime_os
|
|
736
|
+
require "json"
|
|
737
|
+
JSON.parse(File.read(File.join(static_runtime, "runtime.json"))).fetch("os", "windows")
|
|
738
|
+
rescue SystemCallError, JSON::ParserError
|
|
739
|
+
"windows"
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
# C libraries --runtime-dll ships as DLLs next to the executable.
|
|
743
|
+
def runtime_dlls = @options[__method__]
|
|
744
|
+
|
|
397
745
|
# True when the application is packaged by injecting it into the ZIP
|
|
398
746
|
# store of the cosmopolitan Ruby itself: no cosmocc, no launcher stub,
|
|
399
747
|
# and no extraction to a temporary directory at run time.
|
|
@@ -418,8 +766,12 @@ EOF
|
|
|
418
766
|
|
|
419
767
|
def enable_debug_mode? = @options[__method__]
|
|
420
768
|
|
|
769
|
+
def execution_level = @options[__method__]
|
|
770
|
+
|
|
421
771
|
def extra_dlls = @options[__method__]
|
|
422
772
|
|
|
773
|
+
def file_version = @options[__method__]
|
|
774
|
+
|
|
423
775
|
def force_autoload? = @options[__method__]
|
|
424
776
|
|
|
425
777
|
def force_console? = @options[__method__]
|
|
@@ -444,12 +796,42 @@ EOF
|
|
|
444
796
|
|
|
445
797
|
def output_zip = @options[__method__]
|
|
446
798
|
|
|
799
|
+
def product_version = @options[__method__]
|
|
800
|
+
|
|
447
801
|
def quiet? = @options[__method__]
|
|
448
802
|
|
|
803
|
+
def resource_strings = @options[__method__]
|
|
804
|
+
|
|
449
805
|
def wrapper_exe? = @options[__method__]
|
|
450
806
|
|
|
451
807
|
def rubyopt = @options[__method__]
|
|
452
808
|
|
|
809
|
+
def roundhouse? = @options[__method__]
|
|
810
|
+
|
|
811
|
+
# The root directory of the Rails application --roundhouse compiles.
|
|
812
|
+
def roundhouse_app = @options[__method__]
|
|
813
|
+
|
|
814
|
+
def roundhouse_options = @options[__method__]
|
|
815
|
+
|
|
816
|
+
# The directory --roundhouse writes the binary and its files to.
|
|
817
|
+
def roundhouse_output = @options[__method__]
|
|
818
|
+
|
|
819
|
+
def spinel? = @options[__method__]
|
|
820
|
+
|
|
821
|
+
def spinel_options = @options[__method__]
|
|
822
|
+
|
|
823
|
+
# :ruby (ruby.wasm) or :picoruby with --wasm, else nil.
|
|
824
|
+
def wasm_runtime = @options[__method__]
|
|
825
|
+
|
|
826
|
+
def wasm_options = @options[__method__]
|
|
827
|
+
|
|
828
|
+
def wasm_ruby = @options[__method__]
|
|
829
|
+
|
|
830
|
+
# The folder or zip archive --wasm writes.
|
|
831
|
+
def wasm_output = @options[__method__]
|
|
832
|
+
|
|
833
|
+
def wasm_zip? = @options[__method__]
|
|
834
|
+
|
|
453
835
|
def run_script? = @options[__method__]
|
|
454
836
|
|
|
455
837
|
def script = @options[__method__]
|
|
@@ -490,11 +872,59 @@ EOF
|
|
|
490
872
|
|
|
491
873
|
def verbose? = @options[__method__]
|
|
492
874
|
|
|
875
|
+
def version_strings = @options[__method__]
|
|
876
|
+
|
|
493
877
|
def warning? = @options[__method__]
|
|
494
878
|
|
|
495
879
|
def windowed?
|
|
880
|
+
if static_runtime?
|
|
881
|
+
# The runtime decides, not the build host. For a runtime that is
|
|
882
|
+
# not for Windows only an explicit --windows counts, so that the
|
|
883
|
+
# builder can reject it; a .rbw script is just a script there.
|
|
884
|
+
return static_runtime_os == "windows" ? @options[:windowed?] : force_windows?
|
|
885
|
+
end
|
|
496
886
|
return false unless Gem.win_platform?
|
|
497
887
|
@options[:windowed?]
|
|
498
888
|
end
|
|
889
|
+
|
|
890
|
+
# Where the compiled launcher stubs live (StubBuilder::STUB_PATH and
|
|
891
|
+
# STUBW_PATH). A released gem ships them; a git checkout, such as
|
|
892
|
+
# Bundler's `gem "ocran", git: ...`, has only their sources in src/.
|
|
893
|
+
STUB_DIR = File.expand_path("../../share/ocran", __dir__)
|
|
894
|
+
|
|
895
|
+
# The prebuilt stub the build will copy, or nil when it copies none:
|
|
896
|
+
# native compilation, --wasm, --static-runtime and --cosmo-ruby's ZIP
|
|
897
|
+
# mode use no stub, --cosmo compiles its own, and installer, directory
|
|
898
|
+
# and zip output only need one for the wrapper executable.
|
|
899
|
+
def prebuilt_stub
|
|
900
|
+
return nil if spinel? || roundhouse? || wasm_runtime || static_runtime? || cosmo_zip? || cosmo_cc
|
|
901
|
+
return nil if (inno_setup_script || output_dir || output_zip) && !wrapper_exe?
|
|
902
|
+
|
|
903
|
+
name = if !Gem.win_platform? then "stub"
|
|
904
|
+
elsif windowed? && !macosx_bundle then "stubw.exe"
|
|
905
|
+
else "stub.exe"
|
|
906
|
+
end
|
|
907
|
+
File.join(STUB_DIR, name)
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
# Fails when the build needs a stub that was never compiled. Checked
|
|
911
|
+
# before the dependency run, which can take minutes, since the build
|
|
912
|
+
# would otherwise end in a bare ENOENT for the stub after it.
|
|
913
|
+
def check_prebuilt_stub(path = prebuilt_stub)
|
|
914
|
+
return if path.nil? || File.exist?(path)
|
|
915
|
+
|
|
916
|
+
root = File.dirname(STUB_DIR, 2)
|
|
917
|
+
build = if RUBY_PLATFORM.include?("mswin")
|
|
918
|
+
"`nmake -f Makefile.mswin install` in #{File.join(root, "src")} from a Visual Studio developer prompt"
|
|
919
|
+
elsif Gem.win_platform?
|
|
920
|
+
"`ridk exec make -C src install` in #{root}"
|
|
921
|
+
else
|
|
922
|
+
"`make -C src install` in #{root}"
|
|
923
|
+
end
|
|
924
|
+
message = "The launcher stub #{path} has not been built (OCRAN from a git checkout " \
|
|
925
|
+
"ships only its sources): run #{build}"
|
|
926
|
+
message += ", or pass --no-wrapper-exe to skip the wrapper executable" if inno_setup_script || output_dir || output_zip
|
|
927
|
+
raise message
|
|
928
|
+
end
|
|
499
929
|
end
|
|
500
930
|
end
|