ocran 1.3.15 → 1.3.17
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 +272 -0
- data/LICENSE.txt +23 -0
- data/README.md +531 -0
- data/exe/ocran +5 -0
- data/lib/ocran/build_constants.rb +16 -0
- data/lib/ocran/build_facade.rb +17 -0
- data/lib/ocran/build_helper.rb +105 -0
- data/lib/ocran/command_output.rb +22 -0
- data/lib/ocran/direction.rb +386 -0
- data/lib/ocran/file_path_set.rb +69 -0
- data/lib/ocran/gem_spec_queryable.rb +172 -0
- data/lib/ocran/host_config_helper.rb +37 -0
- data/lib/ocran/inno_setup_script_builder.rb +111 -0
- data/lib/ocran/launcher_batch_builder.rb +85 -0
- data/lib/ocran/library_detector.rb +61 -0
- data/lib/ocran/option.rb +273 -0
- data/lib/ocran/refine_pathname.rb +104 -0
- data/lib/ocran/runner.rb +105 -0
- data/lib/ocran/runtime_environment.rb +46 -0
- data/lib/ocran/stub_builder.rb +224 -0
- data/lib/ocran/version.rb +1 -1
- data/lib/ocran/windows_command_escaping.rb +15 -0
- data/lib/ocran.rb +3 -2
- data/share/ocran/edicon.exe +0 -0
- data/share/ocran/stub.exe +0 -0
- data/share/ocran/stubw.exe +0 -0
- metadata +43 -9
- data/bin/ocran +0 -1317
- /data/{share/ocran/empty-msys-2.0.dll → lib/ocran/empty_source} +0 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "tempfile"
|
3
|
+
require_relative "file_path_set"
|
4
|
+
require_relative "windows_command_escaping"
|
5
|
+
|
6
|
+
module Ocran
|
7
|
+
class InnoSetupScriptBuilder
|
8
|
+
ISCC_CMD = "ISCC"
|
9
|
+
ISCC_SUCCESS = 0
|
10
|
+
ISCC_INVALID_PARAMS = 1
|
11
|
+
ISCC_COMPILATION_FAILED = 2
|
12
|
+
|
13
|
+
extend WindowsCommandEscaping
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def compile(iss_filename, quiet: false)
|
17
|
+
unless system("where #{quote_and_escape(ISCC_CMD)} >NUL 2>&1")
|
18
|
+
raise "ISCC command not found. Is the InnoSetup directory in your PATH?"
|
19
|
+
end
|
20
|
+
|
21
|
+
cmd_line = [ISCC_CMD]
|
22
|
+
cmd_line << "/Q" if quiet
|
23
|
+
cmd_line << iss_filename
|
24
|
+
system(*cmd_line)
|
25
|
+
|
26
|
+
case $?&.exitstatus
|
27
|
+
when ISCC_SUCCESS
|
28
|
+
# ISCC reported success
|
29
|
+
when ISCC_INVALID_PARAMS
|
30
|
+
raise "ISCC reports invalid command line parameters"
|
31
|
+
when ISCC_COMPILATION_FAILED
|
32
|
+
raise "ISCC reports that compilation failed"
|
33
|
+
else
|
34
|
+
raise "ISCC failed to run"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
include WindowsCommandEscaping
|
40
|
+
|
41
|
+
def initialize(inno_setup_script)
|
42
|
+
# ISSC generates the installer files relative to the directory of the
|
43
|
+
# ISS file. Therefore, it is necessary to create Tempfiles in the
|
44
|
+
# working directory.
|
45
|
+
@build_file = Tempfile.new("", Dir.pwd)
|
46
|
+
if inno_setup_script
|
47
|
+
IO.copy_stream(inno_setup_script, @build_file)
|
48
|
+
end
|
49
|
+
@dirs = FilePathSet.new
|
50
|
+
@files = FilePathSet.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def build
|
54
|
+
@build_file.tap do |f|
|
55
|
+
if @dirs.any?
|
56
|
+
f.puts
|
57
|
+
f.puts "[Dirs]"
|
58
|
+
@dirs.each { |_source, target| f.puts build_dir_item(target) }
|
59
|
+
end
|
60
|
+
|
61
|
+
if @files.any?
|
62
|
+
f.puts
|
63
|
+
f.puts "[Files]"
|
64
|
+
@files.each { |source, target| f.puts build_file_item(source, target) }
|
65
|
+
end
|
66
|
+
end.close
|
67
|
+
path
|
68
|
+
end
|
69
|
+
|
70
|
+
def path
|
71
|
+
@build_file.to_path
|
72
|
+
end
|
73
|
+
|
74
|
+
def compile(verbose: false)
|
75
|
+
InnoSetupScriptBuilder.compile(path, quiet: !verbose)
|
76
|
+
end
|
77
|
+
|
78
|
+
def mkdir(target)
|
79
|
+
@dirs.add?("/", target)
|
80
|
+
end
|
81
|
+
|
82
|
+
def cp(source, target)
|
83
|
+
unless File.exist?(source)
|
84
|
+
raise "The file does not exist (#{source})"
|
85
|
+
end
|
86
|
+
|
87
|
+
@files.add?(source, target)
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_dir_item(target)
|
91
|
+
name = File.join("{app}", target)
|
92
|
+
"Name: #{quote_and_escape(name)};"
|
93
|
+
end
|
94
|
+
private :build_dir_item
|
95
|
+
|
96
|
+
def build_file_item(source, target)
|
97
|
+
dest_dir = File.join("{app}", File.dirname(target))
|
98
|
+
s = [
|
99
|
+
"Source: #{quote_and_escape(source)};",
|
100
|
+
"DestDir: #{quote_and_escape(dest_dir)};"
|
101
|
+
]
|
102
|
+
src_name = File.basename(source)
|
103
|
+
dest_name = File.basename(target)
|
104
|
+
if src_name != dest_name
|
105
|
+
s << "DestName: #{quote_and_escape(dest_name)};"
|
106
|
+
end
|
107
|
+
s.join(" ")
|
108
|
+
end
|
109
|
+
private :build_file_item
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "tempfile"
|
3
|
+
require_relative "windows_command_escaping"
|
4
|
+
require_relative "build_constants"
|
5
|
+
|
6
|
+
module Ocran
|
7
|
+
class LauncherBatchBuilder
|
8
|
+
include BuildConstants, WindowsCommandEscaping
|
9
|
+
|
10
|
+
# BATCH_FILE_DIR is a parameter expansion used in Windows batch files,
|
11
|
+
# representing the full path to the directory where the batch file resides.
|
12
|
+
# It allows for the use of pseudo-relative paths by referencing the
|
13
|
+
# batch file's own location without changing the working directory.
|
14
|
+
BATCH_FILE_DIR = "%~dp0"
|
15
|
+
|
16
|
+
# BATCH_FILE_PATH is a parameter expansion used in Windows batch files,
|
17
|
+
# representing the full path to the batch file itself, including the file name.
|
18
|
+
BATCH_FILE_PATH = "%~f0"
|
19
|
+
|
20
|
+
def initialize(chdir_before: nil, title: nil)
|
21
|
+
@build_file = Tempfile.new
|
22
|
+
@title = title
|
23
|
+
@chdir_before = chdir_before
|
24
|
+
@environments = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def build
|
28
|
+
@build_file.tap do |f|
|
29
|
+
f.puts "@echo off"
|
30
|
+
@environments.each { |name, val| f.puts build_set_command(name, val) }
|
31
|
+
f.puts build_set_command("OCRAN_EXECUTABLE", BATCH_FILE_PATH)
|
32
|
+
f.puts build_start_command(@title, @executable, @script, *@args, chdir_before: @chdir_before)
|
33
|
+
f
|
34
|
+
end.close
|
35
|
+
path
|
36
|
+
end
|
37
|
+
|
38
|
+
def path
|
39
|
+
@build_file.to_path
|
40
|
+
end
|
41
|
+
|
42
|
+
def export(name, value)
|
43
|
+
@environments[name] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
def exec(executable, script, *args)
|
47
|
+
@executable, @script, @args = executable, script, args
|
48
|
+
end
|
49
|
+
|
50
|
+
def replace_inst_dir_placeholder(s)
|
51
|
+
s.to_s.gsub(/#{Regexp.escape(EXTRACT_ROOT.to_s)}[\/\\]/, BATCH_FILE_DIR)
|
52
|
+
end
|
53
|
+
private :replace_inst_dir_placeholder
|
54
|
+
|
55
|
+
def build_set_command(name, value)
|
56
|
+
"set \"#{name}=#{replace_inst_dir_placeholder(value)}\""
|
57
|
+
end
|
58
|
+
private :build_set_command
|
59
|
+
|
60
|
+
def build_start_command(title, executable, script, *args, chdir_before: nil)
|
61
|
+
cmd = ["start"]
|
62
|
+
|
63
|
+
# Title for Command Prompt window title bar
|
64
|
+
cmd << quote_and_escape(title)
|
65
|
+
|
66
|
+
# Use /d to set the startup directory for the process,
|
67
|
+
# which will be BATCH_FILE_DIR/SRCDIR. This path is where
|
68
|
+
# the script is located, establishing the working directory
|
69
|
+
# at process start.
|
70
|
+
if chdir_before
|
71
|
+
cmd << "/d #{quote_and_escape("#{BATCH_FILE_DIR}#{SRCDIR}")}"
|
72
|
+
end
|
73
|
+
|
74
|
+
cmd << quote_and_escape("#{BATCH_FILE_DIR}#{executable}")
|
75
|
+
cmd << quote_and_escape("#{BATCH_FILE_DIR}#{script}")
|
76
|
+
cmd += args.map { |arg| quote_and_escape(replace_inst_dir_placeholder(arg)) }
|
77
|
+
|
78
|
+
# Forward batch file arguments to the command with `%*`
|
79
|
+
cmd << "%*"
|
80
|
+
|
81
|
+
cmd.join(" ")
|
82
|
+
end
|
83
|
+
private :build_start_command
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "fiddle/import"
|
2
|
+
require "fiddle/types"
|
3
|
+
|
4
|
+
module Ocran
|
5
|
+
module LibraryDetector
|
6
|
+
# Windows API functions for handling files may return long paths,
|
7
|
+
# with a maximum character limit of 32,767.
|
8
|
+
# "\\?\" prefix(4 characters) + long path(32762 characters) + NULL = 32767 characters
|
9
|
+
# https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
|
10
|
+
MAX_PATH = 32767
|
11
|
+
|
12
|
+
# The byte size of the buffer given as an argument to the EnumProcessModules function.
|
13
|
+
# This buffer is used to store the handles of the loaded modules.
|
14
|
+
# If the buffer size is smaller than the number of loaded modules,
|
15
|
+
# it will automatically increase the buffer size and call the EnumProcessModules function again.
|
16
|
+
# Increasing the initial buffer size can reduce the number of iterations required.
|
17
|
+
# https://learn.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process
|
18
|
+
DEFAULT_HMODULE_BUFFER_SIZE = 1024
|
19
|
+
|
20
|
+
extend Fiddle::Importer
|
21
|
+
dlload "kernel32.dll", "psapi.dll"
|
22
|
+
|
23
|
+
include Fiddle::Win32Types
|
24
|
+
# https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
25
|
+
typealias "HMODULE", "HINSTANCE"
|
26
|
+
typealias "LPDWORD", "PDWORD"
|
27
|
+
typealias "LPWSTR", "char*"
|
28
|
+
|
29
|
+
extern "BOOL EnumProcessModules(HANDLE, HMODULE*, DWORD, LPDWORD)"
|
30
|
+
extern "DWORD GetModuleFileNameW(HMODULE, LPWSTR, DWORD)"
|
31
|
+
extern "HANDLE GetCurrentProcess()"
|
32
|
+
extern "DWORD GetLastError()"
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def loaded_dlls
|
36
|
+
dword = "L" # A DWORD is a 32-bit unsigned integer.
|
37
|
+
bytes_needed = [0].pack(dword)
|
38
|
+
bytes = DEFAULT_HMODULE_BUFFER_SIZE
|
39
|
+
process_handle = GetCurrentProcess()
|
40
|
+
handles = while true
|
41
|
+
buffer = "\x00" * bytes
|
42
|
+
if EnumProcessModules(process_handle, buffer, buffer.bytesize, bytes_needed) == 0
|
43
|
+
raise "EnumProcessModules failed with error code #{GetLastError()}"
|
44
|
+
end
|
45
|
+
bytes = bytes_needed.unpack1(dword)
|
46
|
+
if bytes <= buffer.bytesize
|
47
|
+
break buffer.unpack("J#{bytes / Fiddle::SIZEOF_VOIDP}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
str = "\x00".encode("UTF-16LE") * MAX_PATH
|
51
|
+
handles.map do |handle|
|
52
|
+
length = GetModuleFileNameW(handle, str, str.bytesize)
|
53
|
+
if length == 0
|
54
|
+
raise "GetModuleFileNameW failed with error code #{GetLastError()}"
|
55
|
+
end
|
56
|
+
str[0, length].encode("UTF-8")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/ocran/option.rb
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module Ocran
|
5
|
+
class Option
|
6
|
+
load File.expand_path("refine_pathname.rb", __dir__) unless defined? RefinePathname
|
7
|
+
using RefinePathname
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@options = {
|
11
|
+
:add_all_core? => false,
|
12
|
+
:add_all_encoding? => true,
|
13
|
+
:argv => [],
|
14
|
+
:auto_detect_dlls? => true,
|
15
|
+
:chdir_before? => false,
|
16
|
+
:enable_compression? => true,
|
17
|
+
:enable_debug_extract? => false,
|
18
|
+
:enable_debug_mode? => false,
|
19
|
+
:extra_dlls => [],
|
20
|
+
:force_console? => false,
|
21
|
+
:force_windows? => false,
|
22
|
+
:gem_options => [],
|
23
|
+
:gemfile => nil,
|
24
|
+
:icon_filename => nil,
|
25
|
+
:inno_setup_script => nil,
|
26
|
+
:load_autoload? => true,
|
27
|
+
:output_override => nil,
|
28
|
+
:quiet? => false,
|
29
|
+
:rubyopt => nil,
|
30
|
+
:run_script? => true,
|
31
|
+
:script => nil,
|
32
|
+
:source_files => [],
|
33
|
+
:verbose? => false,
|
34
|
+
:warning? => true,
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def usage
|
39
|
+
<<EOF
|
40
|
+
ocran [options] script.rb
|
41
|
+
|
42
|
+
Ocran options:
|
43
|
+
|
44
|
+
--help Display this information.
|
45
|
+
--quiet Suppress output while building executable.
|
46
|
+
--verbose Show extra output while building executable.
|
47
|
+
--version Display version number and exit.
|
48
|
+
|
49
|
+
Packaging options:
|
50
|
+
|
51
|
+
--dll dllname Include additional DLLs from the Ruby bindir.
|
52
|
+
--add-all-core Add all core ruby libraries to the executable.
|
53
|
+
--gemfile <file> Add all gems and dependencies listed in a Bundler Gemfile.
|
54
|
+
--no-enc Exclude encoding support files
|
55
|
+
|
56
|
+
Gem content detection modes:
|
57
|
+
|
58
|
+
--gem-minimal[=gem1,..] Include only loaded scripts
|
59
|
+
--gem-guess=[gem1,...] Include loaded scripts & best guess (DEFAULT)
|
60
|
+
--gem-all[=gem1,..] Include all scripts & files
|
61
|
+
--gem-full[=gem1,..] Include EVERYTHING
|
62
|
+
--gem-spec[=gem1,..] Include files in gemspec (Does not work with Rubygems 1.7+)
|
63
|
+
|
64
|
+
minimal: loaded scripts
|
65
|
+
guess: loaded scripts and other files
|
66
|
+
all: loaded scripts, other scripts, other files (except extras)
|
67
|
+
full: Everything found in the gem directory
|
68
|
+
|
69
|
+
--[no-]gem-scripts[=..] Other script files than those loaded
|
70
|
+
--[no-]gem-files[=..] Other files (e.g. data files)
|
71
|
+
--[no-]gem-extras[=..] Extra files (README, etc.)
|
72
|
+
|
73
|
+
scripts: .rb/.rbw files
|
74
|
+
extras: C/C++ sources, object files, test, spec, README
|
75
|
+
files: all other files
|
76
|
+
|
77
|
+
Auto-detection options:
|
78
|
+
|
79
|
+
--no-dep-run Don't run script.rb to check for dependencies.
|
80
|
+
--no-autoload Don't load/include script.rb's autoloads.
|
81
|
+
--no-autodll Disable detection of runtime DLL dependencies.
|
82
|
+
|
83
|
+
Output options:
|
84
|
+
|
85
|
+
--output <file> Name the exe to generate. Defaults to ./<scriptname>.exe.
|
86
|
+
--no-lzma Disable LZMA compression of the executable.
|
87
|
+
--innosetup <file> Use given Inno Setup script (.iss) to create an installer.
|
88
|
+
|
89
|
+
Executable options:
|
90
|
+
|
91
|
+
--windows Force Windows application (rubyw.exe)
|
92
|
+
--console Force console application (ruby.exe)
|
93
|
+
--chdir-first When exe starts, change working directory to app dir.
|
94
|
+
--icon <ico> Replace icon with a custom one.
|
95
|
+
--rubyopt <str> Set the RUBYOPT environment variable when running the executable
|
96
|
+
--debug Executable will be verbose.
|
97
|
+
--debug-extract Executable will unpack to local dir and not delete after.
|
98
|
+
EOF
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse(argv)
|
102
|
+
while (arg = argv.shift)
|
103
|
+
case arg
|
104
|
+
when /\A--(no-)?lzma\z/
|
105
|
+
@options[:enable_compression?] = !$1
|
106
|
+
when "--no-dep-run"
|
107
|
+
@options[:run_script?] = false
|
108
|
+
when "--add-all-core"
|
109
|
+
@options[:add_all_core?] = true
|
110
|
+
when "--output"
|
111
|
+
path = argv.shift
|
112
|
+
@options[:output_override] = Pathname.new(path).expand_path if path
|
113
|
+
when "--dll"
|
114
|
+
path = argv.shift
|
115
|
+
@options[:extra_dlls] << path if path
|
116
|
+
when "--quiet"
|
117
|
+
@options[:quiet?] = true
|
118
|
+
when "--verbose"
|
119
|
+
@options[:verbose?] = true
|
120
|
+
when "--windows"
|
121
|
+
@options[:force_windows?] = true
|
122
|
+
when "--console"
|
123
|
+
@options[:force_console?] = true
|
124
|
+
when "--no-autoload"
|
125
|
+
@options[:load_autoload?] = false
|
126
|
+
when "--chdir-first"
|
127
|
+
@options[:chdir_before?] = true
|
128
|
+
when "--icon"
|
129
|
+
path = argv.shift
|
130
|
+
raise "Icon file #{path} not found" unless path && File.exist?(path)
|
131
|
+
@options[:icon_filename] = Pathname.new(path).expand_path
|
132
|
+
when "--rubyopt"
|
133
|
+
@options[:rubyopt] = argv.shift
|
134
|
+
when "--gemfile"
|
135
|
+
path = argv.shift
|
136
|
+
raise "Gemfile #{path} not found" unless path && File.exist?(path)
|
137
|
+
@options[:gemfile] = Pathname.new(path).expand_path
|
138
|
+
when "--innosetup"
|
139
|
+
path = argv.shift
|
140
|
+
raise "Inno Script #{path} not found" unless path && File.exist?(path)
|
141
|
+
@options[:inno_setup_script] = Pathname.new(path).expand_path
|
142
|
+
when "--no-autodll"
|
143
|
+
@options[:auto_detect_dlls?] = false
|
144
|
+
when "--version"
|
145
|
+
require_relative "version"
|
146
|
+
puts "Ocran #{VERSION}"
|
147
|
+
raise SystemExit
|
148
|
+
when "--no-warnings"
|
149
|
+
@options[:warning?] = false
|
150
|
+
when "--debug"
|
151
|
+
@options[:enable_debug_mode?] = true
|
152
|
+
when "--debug-extract"
|
153
|
+
@options[:enable_debug_extract?] = true
|
154
|
+
when "--"
|
155
|
+
@options[:argv] = argv.dup
|
156
|
+
argv.clear
|
157
|
+
break
|
158
|
+
when /\A--(no-)?enc\z/
|
159
|
+
@options[:add_all_encoding?] = !$1
|
160
|
+
when /\A--(no-)?gem-(\w+)(?:=(.*))?$/
|
161
|
+
negate, group, list = $1, $2, $3
|
162
|
+
@options[:gem_options] << [negate, group.to_sym, list&.split(",")] if group
|
163
|
+
when "--help", /\A--./
|
164
|
+
puts usage
|
165
|
+
raise SystemExit
|
166
|
+
else
|
167
|
+
raise "#{arg} not found!" unless File.exist?(arg)
|
168
|
+
|
169
|
+
if File.directory?(arg)
|
170
|
+
raise "#{arg} is empty!" if Dir.empty?(arg)
|
171
|
+
# If a directory is passed, we want all files under that directory
|
172
|
+
@options[:source_files] += Pathname.new(arg).find.reject(&:directory?).map(&:expand_path)
|
173
|
+
else
|
174
|
+
@options[:source_files] << Pathname.new(arg).expand_path
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
raise "No script file specified" if source_files.empty?
|
180
|
+
|
181
|
+
@options[:script] = source_files.first
|
182
|
+
|
183
|
+
@options[:force_autoload?] = run_script? && load_autoload?
|
184
|
+
|
185
|
+
@options[:output_executable] =
|
186
|
+
if output_override
|
187
|
+
output_override
|
188
|
+
else
|
189
|
+
executable = script
|
190
|
+
# If debug mode is enabled, append "-debug" to the filename
|
191
|
+
executable = executable.append_to_filename("-debug") if enable_debug_mode?
|
192
|
+
# Build output files are created in the current directory
|
193
|
+
executable.basename.sub_ext(".exe").expand_path
|
194
|
+
end
|
195
|
+
|
196
|
+
@options[:use_inno_setup?] = !!inno_setup_script
|
197
|
+
|
198
|
+
@options[:verbose?] &&= !quiet?
|
199
|
+
|
200
|
+
@options[:windowed?] = (script.extname?(".rbw") || force_windows?) && !force_console?
|
201
|
+
|
202
|
+
if inno_setup_script
|
203
|
+
if enable_debug_extract?
|
204
|
+
raise "The --debug-extract option conflicts with use of Inno Setup"
|
205
|
+
end
|
206
|
+
|
207
|
+
if enable_compression?
|
208
|
+
raise "LZMA compression must be disabled (--no-lzma) when using Inno Setup"
|
209
|
+
end
|
210
|
+
|
211
|
+
unless chdir_before?
|
212
|
+
raise "Chdir-first mode must be enabled (--chdir-first) when using Inno Setup"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def add_all_core? = @options[__method__]
|
218
|
+
|
219
|
+
def add_all_encoding? = @options[__method__]
|
220
|
+
|
221
|
+
def argv = @options[__method__]
|
222
|
+
|
223
|
+
def auto_detect_dlls? = @options[__method__]
|
224
|
+
|
225
|
+
def chdir_before? = @options[__method__]
|
226
|
+
|
227
|
+
def enable_compression? = @options[__method__]
|
228
|
+
|
229
|
+
def enable_debug_extract? = @options[__method__]
|
230
|
+
|
231
|
+
def enable_debug_mode? = @options[__method__]
|
232
|
+
|
233
|
+
def extra_dlls = @options[__method__]
|
234
|
+
|
235
|
+
def force_autoload? = @options[__method__]
|
236
|
+
|
237
|
+
def force_console? = @options[__method__]
|
238
|
+
|
239
|
+
def force_windows? = @options[__method__]
|
240
|
+
|
241
|
+
def gem_options = @options[__method__]
|
242
|
+
|
243
|
+
def gemfile = @options[__method__]
|
244
|
+
|
245
|
+
def icon_filename = @options[__method__]
|
246
|
+
|
247
|
+
def inno_setup_script = @options[__method__]
|
248
|
+
|
249
|
+
def load_autoload? = @options[__method__]
|
250
|
+
|
251
|
+
def output_executable = @options[__method__]
|
252
|
+
|
253
|
+
def output_override = @options[__method__]
|
254
|
+
|
255
|
+
def quiet? = @options[__method__]
|
256
|
+
|
257
|
+
def rubyopt = @options[__method__]
|
258
|
+
|
259
|
+
def run_script? = @options[__method__]
|
260
|
+
|
261
|
+
def script = @options[__method__]
|
262
|
+
|
263
|
+
def source_files = @options[__method__]
|
264
|
+
|
265
|
+
def use_inno_setup? = @options[__method__]
|
266
|
+
|
267
|
+
def verbose? = @options[__method__]
|
268
|
+
|
269
|
+
def warning? = @options[__method__]
|
270
|
+
|
271
|
+
def windowed? = @options[__method__]
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module Ocran
|
5
|
+
# The Pathname class in Ruby is modified to handle mixed path separators and
|
6
|
+
# to be case-insensitive.
|
7
|
+
module RefinePathname
|
8
|
+
refine Pathname do
|
9
|
+
def normalize_file_separator(s)
|
10
|
+
if File::ALT_SEPARATOR
|
11
|
+
s.tr(File::ALT_SEPARATOR, File::SEPARATOR)
|
12
|
+
else
|
13
|
+
s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
private :normalize_file_separator
|
17
|
+
|
18
|
+
# Compares two paths for equality based on the case sensitivity of the
|
19
|
+
# Ruby execution environment's file system.
|
20
|
+
# If the file system is case-insensitive, it performs a case-insensitive
|
21
|
+
# comparison. Otherwise, it performs a case-sensitive comparison.
|
22
|
+
def pathequal(a, b)
|
23
|
+
if File::FNM_SYSCASE.nonzero?
|
24
|
+
a.casecmp(b) == 0
|
25
|
+
else
|
26
|
+
a == b
|
27
|
+
end
|
28
|
+
end
|
29
|
+
private :pathequal
|
30
|
+
|
31
|
+
def to_posix
|
32
|
+
normalize_file_separator(to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Checks if two Pathname objects are equal, considering the file system's
|
36
|
+
# case sensitivity and path separators. Returns false if the other object is not
|
37
|
+
# an Pathname.
|
38
|
+
# This method enables the use of the `uniq` method on arrays of Pathname objects.
|
39
|
+
def eql?(other)
|
40
|
+
return false unless other.is_a?(Pathname)
|
41
|
+
|
42
|
+
a = normalize_file_separator(to_s)
|
43
|
+
b = normalize_file_separator(other.to_s)
|
44
|
+
pathequal(a, b)
|
45
|
+
end
|
46
|
+
|
47
|
+
alias == eql?
|
48
|
+
alias === eql?
|
49
|
+
|
50
|
+
# Calculates a normalized hash value for a pathname to ensure consistent
|
51
|
+
# hashing across different environments, particularly in Windows.
|
52
|
+
# This method first normalizes the path by:
|
53
|
+
# 1. Converting the file separator from the platform-specific separator
|
54
|
+
# to the common POSIX separator ('/') if necessary.
|
55
|
+
# 2. Converting the path to lowercase if the filesystem is case-insensitive.
|
56
|
+
# The normalized path string is then hashed, providing a stable hash value
|
57
|
+
# that is consistent with the behavior of eql? method, thus maintaining
|
58
|
+
# the integrity of hash-based data structures like Hash or Set.
|
59
|
+
#
|
60
|
+
# @return [Integer] A hash integer based on the normalized path.
|
61
|
+
def hash
|
62
|
+
path = if File::FNM_SYSCASE.nonzero?
|
63
|
+
to_s.downcase
|
64
|
+
else
|
65
|
+
to_s
|
66
|
+
end
|
67
|
+
normalize_file_separator(path).hash
|
68
|
+
end
|
69
|
+
|
70
|
+
# Checks if the current path is a sub path of the specified base_directory.
|
71
|
+
# Both paths must be either absolute paths or relative paths; otherwise, this
|
72
|
+
# method returns false.
|
73
|
+
def subpath?(base_directory)
|
74
|
+
s = relative_path_from(base_directory).each_filename.first
|
75
|
+
s != '.' && s != ".."
|
76
|
+
rescue ArgumentError
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
# Appends the given suffix to the filename, preserving the file extension.
|
81
|
+
# If the filename has an extension, the suffix is inserted before the extension.
|
82
|
+
# If the filename does not have an extension, the suffix is appended to the end.
|
83
|
+
# This method handles both directory and file paths correctly.
|
84
|
+
#
|
85
|
+
# Examples:
|
86
|
+
# pathname = Pathname("path.to/foo.tar.gz")
|
87
|
+
# pathname.append_to_filename("_bar") # => #<Pathname:path.to/foo_bar.tar.gz>
|
88
|
+
#
|
89
|
+
# pathname = Pathname("path.to/foo")
|
90
|
+
# pathname.append_to_filename("_bar") # => #<Pathname:path.to/foo_bar>
|
91
|
+
#
|
92
|
+
def append_to_filename(suffix)
|
93
|
+
dirname + basename.sub(/(\.?[^.]+)?(\..*)?\z/, "\\1#{suffix}\\2")
|
94
|
+
end
|
95
|
+
|
96
|
+
# Checks if the file's extension matches the expected extension.
|
97
|
+
# The comparison is case-insensitive.
|
98
|
+
# Example usage: ocran_pathname.extname?(".exe")
|
99
|
+
def extname?(expected_ext)
|
100
|
+
extname.casecmp(expected_ext) == 0
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|