ocran 1.3.18 → 1.4.0

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.txt +306 -292
  3. data/LICENSE.txt +22 -22
  4. data/README.md +549 -533
  5. data/exe/ocran +5 -5
  6. data/ext/extconf.rb +15 -0
  7. data/lib/ocran/build_constants.rb +16 -16
  8. data/lib/ocran/build_facade.rb +17 -17
  9. data/lib/ocran/build_helper.rb +110 -105
  10. data/lib/ocran/command_output.rb +22 -22
  11. data/lib/ocran/dir_builder.rb +162 -0
  12. data/lib/ocran/direction.rb +623 -458
  13. data/lib/ocran/file_path_set.rb +69 -69
  14. data/lib/ocran/gem_spec_queryable.rb +172 -172
  15. data/lib/ocran/host_config_helper.rb +57 -44
  16. data/lib/ocran/inno_setup_script_builder.rb +111 -111
  17. data/lib/ocran/launcher_batch_builder.rb +85 -85
  18. data/lib/ocran/library_detector.rb +61 -61
  19. data/lib/ocran/library_detector_posix.rb +55 -0
  20. data/lib/ocran/option.rb +323 -273
  21. data/lib/ocran/refine_pathname.rb +104 -104
  22. data/lib/ocran/runner.rb +115 -105
  23. data/lib/ocran/runtime_environment.rb +46 -46
  24. data/lib/ocran/stub_builder.rb +298 -264
  25. data/lib/ocran/version.rb +5 -5
  26. data/lib/ocran/windows_command_escaping.rb +15 -15
  27. data/lib/ocran.rb +7 -7
  28. data/share/ocran/lzma.exe +0 -0
  29. data/src/Makefile +75 -0
  30. data/src/edicon.c +161 -0
  31. data/src/error.c +100 -0
  32. data/src/error.h +66 -0
  33. data/src/inst_dir.c +334 -0
  34. data/src/inst_dir.h +157 -0
  35. data/src/lzma/7zTypes.h +529 -0
  36. data/src/lzma/Compiler.h +43 -0
  37. data/src/lzma/LzmaDec.c +1363 -0
  38. data/src/lzma/LzmaDec.h +236 -0
  39. data/src/lzma/Precomp.h +10 -0
  40. data/src/script_info.c +246 -0
  41. data/src/script_info.h +7 -0
  42. data/src/stub.c +133 -0
  43. data/src/stub.manifest +29 -0
  44. data/src/stub.rc +3 -0
  45. data/src/system_utils.c +1002 -0
  46. data/src/system_utils.h +209 -0
  47. data/src/system_utils_posix.c +500 -0
  48. data/src/unpack.c +574 -0
  49. data/src/unpack.h +85 -0
  50. data/src/vit-ruby.ico +0 -0
  51. metadata +52 -16
  52. data/share/ocran/edicon.exe +0 -0
  53. data/share/ocran/stub.exe +0 -0
  54. data/share/ocran/stubw.exe +0 -0
@@ -1,104 +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
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
data/lib/ocran/runner.rb CHANGED
@@ -1,105 +1,115 @@
1
- # frozen_string_literal: true
2
- load File.expand_path("../ocran.rb", __dir__)
3
-
4
- module Ocran
5
- class Runner
6
- load File.expand_path("command_output.rb", __dir__)
7
- include CommandOutput
8
-
9
- def fatal_error(statement)
10
- error statement
11
- exit false
12
- end
13
-
14
- def initialize
15
- load File.expand_path("runtime_environment.rb", __dir__)
16
- @pre_env = RuntimeEnvironment.save
17
-
18
- load File.expand_path("option.rb", __dir__)
19
- @option = Option.new.tap do |opt|
20
- opt.parse(ARGV)
21
- rescue RuntimeError => e
22
- # Capture RuntimeError during parsing and display an appropriate
23
- # error message to the user. This error usually occurs from invalid
24
- # option arguments.
25
- fatal_error e.message
26
- else
27
- # Update ARGV with the parsed command line arguments to pass to
28
- # the user's script. This ensures the script executes based on
29
- # the user-specified arguments.
30
- ARGV.replace(opt.argv)
31
- end
32
-
33
- Ocran.option = @option
34
-
35
- @ignore_modules = ObjectSpace.each_object(Module).to_a
36
- end
37
-
38
- def run
39
- at_exit do
40
- if $!.nil? or $!.kind_of?(SystemExit)
41
- build
42
- exit
43
- end
44
- end
45
-
46
- exit unless @option.run_script?
47
- say "Loading script to check dependencies"
48
- $PROGRAM_NAME = @option.script.to_s
49
- end
50
-
51
- # Force loading autoloaded constants. Searches through all modules
52
- # (and hence classes), and checks their constants for autoloaded
53
- # ones, then attempts to load them.
54
- def attempt_load_autoload(ignore_modules = [])
55
- checked_modules = ignore_modules.inject({}) { |h, mod| h[mod] = true; h }
56
- while ObjectSpace.each_object(Module).count { |mod|
57
- next if checked_modules.include?(mod)
58
- mod.constants.each do |const|
59
- next unless mod.autoload?(const)
60
- say "Attempting to trigger autoload of #{mod}::#{const}"
61
- begin
62
- mod.const_get(const)
63
- rescue ScriptError, StandardError => e
64
- # Some autoload constants may throw exceptions beyond the expected
65
- # errors. This includes issues dependent on the system or execution
66
- # environment, so it is preferable to ignore exceptions other than
67
- # critical errors.
68
- warning "#{mod}::#{const} loading failed: #{e.message}"
69
- end
70
- end
71
- checked_modules[mod] = true
72
- }.nonzero?
73
- # Loops until all constants have been checked.
74
- end
75
- end
76
-
77
- def build
78
- # If the script was run and autoload is enabled, attempt to autoload libraries.
79
- if @option.force_autoload?
80
- attempt_load_autoload(@ignore_modules)
81
- end
82
-
83
- @post_env = RuntimeEnvironment.save
84
- # NOTE: From this point, $LOADED_FEATURES has been captured, so it is now
85
- # safe to call require_relative.
86
-
87
- ENV.replace(@pre_env.env)
88
-
89
- # It might be useful to reset the current directory to the point where the
90
- # command was launched, especially when implementing the builder object.
91
- Dir.chdir(@pre_env.pwd)
92
-
93
- require_relative "direction"
94
- direction = Direction.new(@post_env, @pre_env, @option)
95
-
96
- if @option.use_inno_setup?
97
- direction.build_inno_setup_installer
98
- else
99
- direction.build_stab_exe
100
- end
101
- rescue RuntimeError => e
102
- fatal_error e.message
103
- end
104
- end
105
- end
1
+ # frozen_string_literal: true
2
+ load File.expand_path("../ocran.rb", __dir__)
3
+
4
+ module Ocran
5
+ class Runner
6
+ load File.expand_path("command_output.rb", __dir__)
7
+ include CommandOutput
8
+
9
+ def fatal_error(statement)
10
+ error statement
11
+ exit false
12
+ end
13
+
14
+ def initialize
15
+ load File.expand_path("runtime_environment.rb", __dir__)
16
+ @pre_env = RuntimeEnvironment.save
17
+
18
+ load File.expand_path("option.rb", __dir__)
19
+ @option = Option.new.tap do |opt|
20
+ opt.parse(ARGV)
21
+ rescue RuntimeError => e
22
+ # Capture RuntimeError during parsing and display an appropriate
23
+ # error message to the user. This error usually occurs from invalid
24
+ # option arguments.
25
+ fatal_error e.message
26
+ else
27
+ # Update ARGV with the parsed command line arguments to pass to
28
+ # the user's script. This ensures the script executes based on
29
+ # the user-specified arguments.
30
+ ARGV.replace(opt.argv)
31
+ end
32
+
33
+ Ocran.option = @option
34
+
35
+ @ignore_modules = ObjectSpace.each_object(Module).to_a
36
+ end
37
+
38
+ def run
39
+ at_exit do
40
+ if $!.nil? or $!.kind_of?(SystemExit)
41
+ build
42
+ exit
43
+ end
44
+ end
45
+
46
+ exit unless @option.run_script?
47
+ say "Loading script to check dependencies"
48
+ $PROGRAM_NAME = @option.script.to_s
49
+ end
50
+
51
+ # Force loading autoloaded constants. Searches through all modules
52
+ # (and hence classes), and checks their constants for autoloaded
53
+ # ones, then attempts to load them.
54
+ def attempt_load_autoload(ignore_modules = [])
55
+ checked_modules = ignore_modules.inject({}) { |h, mod| h[mod] = true; h }
56
+ while ObjectSpace.each_object(Module).count { |mod|
57
+ next if checked_modules.include?(mod)
58
+ mod.constants.each do |const|
59
+ next unless mod.autoload?(const)
60
+ say "Attempting to trigger autoload of #{mod}::#{const}"
61
+ begin
62
+ mod.const_get(const)
63
+ rescue ScriptError, StandardError => e
64
+ # Some autoload constants may throw exceptions beyond the expected
65
+ # errors. This includes issues dependent on the system or execution
66
+ # environment, so it is preferable to ignore exceptions other than
67
+ # critical errors.
68
+ warning "#{mod}::#{const} loading failed: #{e.message}"
69
+ end
70
+ end
71
+ checked_modules[mod] = true
72
+ }.nonzero?
73
+ # Loops until all constants have been checked.
74
+ end
75
+ end
76
+
77
+ def build
78
+ # If the script was run and autoload is enabled, attempt to autoload libraries.
79
+ if @option.force_autoload?
80
+ attempt_load_autoload(@ignore_modules)
81
+ end
82
+
83
+ @post_env = RuntimeEnvironment.save
84
+ # NOTE: From this point, $LOADED_FEATURES has been captured, so it is now
85
+ # safe to call require_relative.
86
+
87
+ ENV.replace(@pre_env.env)
88
+
89
+ # It might be useful to reset the current directory to the point where the
90
+ # command was launched, especially when implementing the builder object.
91
+ Dir.chdir(@pre_env.pwd)
92
+
93
+ require_relative "direction"
94
+ direction = Direction.new(@post_env, @pre_env, @option)
95
+
96
+ if @option.use_inno_setup?
97
+ if Gem.win_platform?
98
+ direction.build_inno_setup_installer
99
+ else
100
+ raise "Inno Setup is only supported on Windows"
101
+ end
102
+ elsif @option.macosx_bundle
103
+ direction.build_macosx_bundle(@option.macosx_bundle)
104
+ elsif @option.output_dir
105
+ direction.build_output_dir(@option.output_dir)
106
+ elsif @option.output_zip
107
+ direction.build_zip(@option.output_zip)
108
+ else
109
+ direction.build_stab_exe
110
+ end
111
+ rescue RuntimeError => e
112
+ fatal_error e.message
113
+ end
114
+ end
115
+ end
@@ -1,46 +1,46 @@
1
- # frozen_string_literal: true
2
- require "pathname"
3
-
4
- module Ocran
5
- load File.expand_path("refine_pathname.rb", __dir__) unless defined? RefinePathname
6
- using RefinePathname
7
-
8
- class RuntimeEnvironment
9
- class << self
10
- alias save new
11
- end
12
-
13
- attr_reader :env, :load_path, :loaded_features, :pwd
14
-
15
- def initialize
16
- @env = ENV.to_hash.freeze
17
- @load_path = $LOAD_PATH.dup.freeze
18
- @loaded_features = $LOADED_FEATURES.dup.freeze
19
- @pwd = Dir.pwd.freeze
20
- end
21
-
22
- # Expands the given path using the working directory stored in this
23
- # instance as the base. This method resolves relative paths to
24
- # absolute paths, ensuring they are fully qualified based on the
25
- # working directory stored within this instance.
26
- def expand_path(path)
27
- File.expand_path(path, @pwd)
28
- end
29
-
30
- def find_load_path(path)
31
- path = Pathname.new(path) unless path.is_a?(Pathname)
32
-
33
- if path.absolute?
34
- # For an absolute path feature, find the load path that contains the feature
35
- # and determine the longest matching path (most specific path).
36
- @load_path.select { |load_path| path.subpath?(expand_path(load_path)) }
37
- .max_by { |load_path| expand_path(load_path).length }
38
- else
39
- # For a relative path feature, find the load path where the expanded feature exists
40
- # and select the longest load path (most specific path).
41
- @load_path.select { |load_path| path.expand_path(load_path).exist? }
42
- .max_by { |load_path| load_path.length }
43
- end
44
- end
45
- end
46
- end
1
+ # frozen_string_literal: true
2
+ require "pathname"
3
+
4
+ module Ocran
5
+ load File.expand_path("refine_pathname.rb", __dir__) unless defined? RefinePathname
6
+ using RefinePathname
7
+
8
+ class RuntimeEnvironment
9
+ class << self
10
+ alias save new
11
+ end
12
+
13
+ attr_reader :env, :load_path, :loaded_features, :pwd
14
+
15
+ def initialize
16
+ @env = ENV.to_hash.freeze
17
+ @load_path = $LOAD_PATH.dup.freeze
18
+ @loaded_features = $LOADED_FEATURES.dup.freeze
19
+ @pwd = Dir.pwd.freeze
20
+ end
21
+
22
+ # Expands the given path using the working directory stored in this
23
+ # instance as the base. This method resolves relative paths to
24
+ # absolute paths, ensuring they are fully qualified based on the
25
+ # working directory stored within this instance.
26
+ def expand_path(path)
27
+ File.expand_path(path, @pwd)
28
+ end
29
+
30
+ def find_load_path(path)
31
+ path = Pathname.new(path) unless path.is_a?(Pathname)
32
+
33
+ if path.absolute?
34
+ # For an absolute path feature, find the load path that contains the feature
35
+ # and determine the longest matching path (most specific path).
36
+ @load_path.select { |load_path| path.subpath?(expand_path(load_path)) }
37
+ .max_by { |load_path| expand_path(load_path).length }
38
+ else
39
+ # For a relative path feature, find the load path where the expanded feature exists
40
+ # and select the longest load path (most specific path).
41
+ @load_path.select { |load_path| path.expand_path(load_path).exist? }
42
+ .max_by { |load_path| load_path.length }
43
+ end
44
+ end
45
+ end
46
+ end