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/dir_builder.rb
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
require "pathname"
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require_relative "build_constants"
|
|
5
|
+
require_relative "windows_command_escaping"
|
|
5
6
|
|
|
6
7
|
module Ocran
|
|
7
8
|
# Builder that outputs all files to a plain directory instead of a self-extracting
|
|
8
9
|
# executable. A launch script (`.sh` on POSIX, `.bat` on Windows) is written at
|
|
9
10
|
# the root of the directory so the packaged app can be started directly.
|
|
10
11
|
class DirBuilder
|
|
11
|
-
include BuildConstants
|
|
12
|
+
include BuildConstants, WindowsCommandEscaping
|
|
12
13
|
|
|
13
14
|
WINDOWS = Gem.win_platform?
|
|
14
15
|
|
|
@@ -17,8 +18,20 @@ module Ocran
|
|
|
17
18
|
# by Direction to build the optional wrapper executable.
|
|
18
19
|
attr_reader :env, :exec_args
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
# +script_name+ names the launch script (without extension); it defaults
|
|
22
|
+
# to the name of the script that is executed. Direction passes the
|
|
23
|
+
# user's script, because what is executed can be a generated launcher
|
|
24
|
+
# (see Direction#generate_rubyopt_launcher).
|
|
25
|
+
#
|
|
26
|
+
# +chdir_before+ and +chdir_exe_dir+ mirror the stub flags of the same
|
|
27
|
+
# names (--chdir-first, --chdir-exe-dir): the launch script changes into
|
|
28
|
+
# the directory of the packed script, or into its own directory, which
|
|
29
|
+
# is where the output directory keeps the application.
|
|
30
|
+
def initialize(path, script_name: nil, chdir_before: false, chdir_exe_dir: false)
|
|
21
31
|
@path = Pathname(path)
|
|
32
|
+
@script_name = script_name
|
|
33
|
+
@chdir_before = chdir_before
|
|
34
|
+
@chdir_exe_dir = chdir_exe_dir
|
|
22
35
|
@path.mkpath
|
|
23
36
|
@env = {}
|
|
24
37
|
@exec_args = nil
|
|
@@ -55,17 +68,33 @@ module Ocran
|
|
|
55
68
|
@exec_args = [image.to_s, script.to_s, argv.map(&:to_s)]
|
|
56
69
|
end
|
|
57
70
|
|
|
58
|
-
#
|
|
71
|
+
# PowerShell command for create_zip on Windows. The paths come in through
|
|
72
|
+
# the environment rather than being spliced into the command, so no
|
|
73
|
+
# character in them (a quote in a user name, brackets) can change its
|
|
74
|
+
# meaning; -LiteralPath keeps wildcards in the source path literal.
|
|
75
|
+
POWERSHELL_ZIP_COMMAND =
|
|
76
|
+
"$ErrorActionPreference = 'Stop'; " \
|
|
77
|
+
"Get-ChildItem -LiteralPath $env:OCRAN_ZIP_SOURCE -Force | " \
|
|
78
|
+
"Compress-Archive -DestinationPath $env:OCRAN_ZIP_DESTINATION"
|
|
79
|
+
|
|
80
|
+
# Create a zip archive from a source directory, replacing any archive
|
|
81
|
+
# already at +zip_path+.
|
|
59
82
|
# Uses the `zip` command on POSIX and PowerShell on Windows.
|
|
60
83
|
def self.create_zip(zip_path, source_dir)
|
|
61
84
|
zip_path = File.expand_path(zip_path.to_s)
|
|
85
|
+
# Both tools add to an existing archive rather than replace it (zip
|
|
86
|
+
# keeps entries the new tree no longer has; Compress-Archive refuses
|
|
87
|
+
# without -Force), so start from nothing.
|
|
88
|
+
FileUtils.rm_f(zip_path)
|
|
62
89
|
if Gem.win_platform?
|
|
63
|
-
|
|
64
|
-
|
|
90
|
+
env = { "OCRAN_ZIP_SOURCE" => source_dir.to_s, "OCRAN_ZIP_DESTINATION" => zip_path }
|
|
91
|
+
system(env, "powershell", "-NoProfile", "-NonInteractive", "-Command", POWERSHELL_ZIP_COMMAND,
|
|
65
92
|
exception: true)
|
|
66
93
|
else
|
|
94
|
+
# -y stores symlinks (the libruby.so aliases) as links; without it
|
|
95
|
+
# zip follows them and stores the library once per alias.
|
|
67
96
|
Dir.chdir(source_dir) do
|
|
68
|
-
system("zip", "-r", zip_path, ".", exception: true)
|
|
97
|
+
system("zip", "-q", "-r", "-y", zip_path, ".", exception: true)
|
|
69
98
|
end
|
|
70
99
|
end
|
|
71
100
|
end
|
|
@@ -84,13 +113,41 @@ module Ocran
|
|
|
84
113
|
write_launch_script
|
|
85
114
|
end
|
|
86
115
|
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
def
|
|
90
|
-
|
|
116
|
+
# Anchors a relative packed path at the extraction root placeholder;
|
|
117
|
+
# absolute paths are kept as they are.
|
|
118
|
+
def root_path(path)
|
|
119
|
+
return path if path.start_with?("#{EXTRACT_ROOT}/") || File.absolute_path?(path)
|
|
120
|
+
"#{EXTRACT_ROOT}/#{path}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Quotes +value+ as one POSIX shell word. The extraction root placeholder
|
|
124
|
+
# becomes "$SCRIPT_DIR"; everything else is single-quoted, so the shell
|
|
125
|
+
# expands nothing in it.
|
|
126
|
+
def shell_word(value)
|
|
127
|
+
word = value.split("#{EXTRACT_ROOT}/", -1).map { |part|
|
|
128
|
+
part.empty? ? "" : "'#{part.gsub("'", %q('"'"'))}'"
|
|
129
|
+
}.join('"$SCRIPT_DIR"/')
|
|
130
|
+
word.empty? ? "''" : word
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The batch script's reference to its own directory, which ends in a
|
|
134
|
+
# backslash; see WindowsCommandEscaping#batch_value.
|
|
135
|
+
BATCH_SCRIPT_DIR = "%SCRIPT_DIR%"
|
|
136
|
+
|
|
137
|
+
# The directory the launch script changes into before it starts the
|
|
138
|
+
# application, as a packed path (see root_path), or nil to stay put.
|
|
139
|
+
def chdir_target
|
|
140
|
+
if @chdir_exe_dir
|
|
141
|
+
EXTRACT_ROOT.to_s
|
|
142
|
+
elsif @chdir_before && @exec_args
|
|
143
|
+
dir = File.dirname(@exec_args[1])
|
|
144
|
+
dir == "." ? EXTRACT_ROOT.to_s : root_path(dir)
|
|
145
|
+
end
|
|
91
146
|
end
|
|
92
147
|
|
|
93
148
|
def script_basename
|
|
149
|
+
return @script_name.to_s if @script_name
|
|
150
|
+
|
|
94
151
|
@exec_args ? Pathname(@exec_args[1]).basename.sub_ext("").to_s : "run"
|
|
95
152
|
end
|
|
96
153
|
|
|
@@ -107,24 +164,18 @@ module Ocran
|
|
|
107
164
|
]
|
|
108
165
|
|
|
109
166
|
@env.each do |name, value|
|
|
110
|
-
|
|
111
|
-
|
|
167
|
+
lines << "export #{name}=#{shell_word(value)}"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
if (dir = chdir_target)
|
|
171
|
+
target = dir == EXTRACT_ROOT.to_s ? '"$SCRIPT_DIR"' : shell_word(dir)
|
|
172
|
+
lines << "cd #{target} || exit 1"
|
|
112
173
|
end
|
|
113
174
|
|
|
114
175
|
if @exec_args
|
|
115
176
|
image, script, argv = @exec_args
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# Prepend SCRIPT_DIR to relative paths
|
|
120
|
-
image_r = "$SCRIPT_DIR/#{image_r}" unless image_r.start_with?("/", "$SCRIPT_DIR")
|
|
121
|
-
script_r = "$SCRIPT_DIR/#{script_r}" unless script_r.start_with?("/", "$SCRIPT_DIR")
|
|
122
|
-
|
|
123
|
-
args = argv.map { |a| "\"#{replace_root(a, "$SCRIPT_DIR")}\"" }.join(" ")
|
|
124
|
-
exec_line = "exec \"#{image_r}\" \"#{script_r}\""
|
|
125
|
-
exec_line += " #{args}" unless args.empty?
|
|
126
|
-
exec_line += ' "$@"'
|
|
127
|
-
lines << exec_line
|
|
177
|
+
words = [root_path(image), root_path(script), *argv].map { |a| shell_word(a) }
|
|
178
|
+
lines << "exec #{words.join(" ")} \"$@\""
|
|
128
179
|
end
|
|
129
180
|
|
|
130
181
|
File.write(script_path, lines.join("\n") + "\n")
|
|
@@ -134,29 +185,28 @@ module Ocran
|
|
|
134
185
|
def write_batch_script
|
|
135
186
|
script_path = @path / "#{script_basename}.bat"
|
|
136
187
|
|
|
188
|
+
# setlocal keeps the variables and the working directory set here from
|
|
189
|
+
# outliving the script in the cmd.exe session that ran it.
|
|
137
190
|
lines = [
|
|
138
191
|
"@echo off",
|
|
139
|
-
"
|
|
192
|
+
"setlocal",
|
|
193
|
+
'set "SCRIPT_DIR=%~dp0"',
|
|
140
194
|
]
|
|
141
195
|
|
|
142
196
|
@env.each do |name, value|
|
|
143
|
-
|
|
144
|
-
|
|
197
|
+
lines << "set \"#{name}=#{batch_value(value, BATCH_SCRIPT_DIR).tr("/", "\\")}\""
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
if (dir = chdir_target)
|
|
201
|
+
target = dir == EXTRACT_ROOT.to_s ? BATCH_SCRIPT_DIR : batch_value(dir, BATCH_SCRIPT_DIR).tr("/", "\\")
|
|
202
|
+
lines << "cd /d #{quote_and_escape(target)} || exit /b 1"
|
|
145
203
|
end
|
|
146
204
|
|
|
147
205
|
if @exec_args
|
|
148
206
|
image, script, argv = @exec_args
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
image_r = "%SCRIPT_DIR%#{image_r}" unless image_r.include?("%SCRIPT_DIR%") || File.absolute_path?(image_r)
|
|
153
|
-
script_r = "%SCRIPT_DIR%#{script_r}" unless script_r.include?("%SCRIPT_DIR%") || File.absolute_path?(script_r)
|
|
154
|
-
|
|
155
|
-
args = argv.map { |a| replace_root(a, "%SCRIPT_DIR%") }.join(" ")
|
|
156
|
-
exec_line = "\"#{image_r}\" \"#{script_r}\""
|
|
157
|
-
exec_line += " #{args}" unless args.empty?
|
|
158
|
-
exec_line += " %*"
|
|
159
|
-
lines << exec_line
|
|
207
|
+
words = [root_path(image), root_path(script)].map { |p| batch_value(p, BATCH_SCRIPT_DIR).tr("/", "\\") }
|
|
208
|
+
words += argv.map { |a| batch_value(a, BATCH_SCRIPT_DIR) }
|
|
209
|
+
lines << "#{words.map { |w| quote_and_escape(w) }.join(" ")} %*"
|
|
160
210
|
end
|
|
161
211
|
|
|
162
212
|
File.write(script_path, lines.join("\r\n") + "\r\n")
|