Class: Ocran::Runner
- Inherits:
-
Object
- Object
- Ocran::Runner
- Includes:
- CommandOutput
- Defined in:
- lib/ocran/runner.rb
Instance Method Summary collapse
-
#active_bundler_gemfile ⇒ Object
The Gemfile this process was started against, if any.
-
#apply_gemfile_to_bundler_env ⇒ Object
--gemfile names the Gemfile the application runs under, so it has to govern the dependency run as well, not just the later Gemfile scan.
-
#attempt_load_autoload(ignore_modules = []) ⇒ Object
Force loading autoloaded constants.
- #build ⇒ Object
- #fatal_error(statement) ⇒ Object
-
#initialize ⇒ Runner
constructor
A new instance of Runner.
- #run ⇒ Object
- #same_file?(a, b) ⇒ Boolean
-
#warn_about_foreign_bundle ⇒ Object
Without --gemfile the dependency run inherits whatever bundle the environment carries, and when that is some other project's bundle the script's own gems are simply absent.
Methods included from CommandOutput
#error, #say, #verbose, #warning
Constructor Details
#initialize ⇒ Runner
Returns a new instance of Runner.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/ocran/runner.rb', line 14 def initialize load File.("runtime_environment.rb", __dir__) @pre_env = RuntimeEnvironment.save load File.("option.rb", __dir__) @option = Option.new.tap do |opt| opt.parse(ARGV) rescue RuntimeError => e # Capture RuntimeError during parsing and display an appropriate # error message to the user. This error usually occurs from invalid # option arguments. fatal_error e. else # Update ARGV with the parsed command line arguments to pass to # the user's script. This ensures the script executes based on # the user-specified arguments. ARGV.replace(opt.argv) end Ocran.option = @option @ignore_modules = ObjectSpace.each_object(Module).to_a end |
Instance Method Details
#active_bundler_gemfile ⇒ Object
The Gemfile this process was started against, if any.
114 115 116 117 118 119 120 121 122 123 |
# File 'lib/ocran/runner.rb', line 114 def active_bundler_gemfile return ENV["BUNDLE_GEMFILE"] unless ENV["BUNDLE_GEMFILE"].to_s.empty? return nil unless defined?(Bundler) && Bundler.respond_to?(:default_gemfile) Bundler.default_gemfile.to_s rescue StandardError # Bundler raises when there is no Gemfile anywhere above the working # directory, which just means there is no ambient bundle to displace. nil end |
#apply_gemfile_to_bundler_env ⇒ Object
--gemfile names the Gemfile the application runs under, so it has to
govern the dependency run as well, not just the later Gemfile scan.
The script is loaded in this very process, and a BUNDLE_GEMFILE
inherited from the environment would otherwise win: invoke OCRAN from
inside another project's bundle exec and the script's
require "bundler/setup" sets up that project's bundle instead of the
application's. Gems only the application's Gemfile provides are then
missing, and local development gems declared with path: or gemspec
always are, since they exist nowhere else. The dependency run dies with
a LoadError before anything can be packed (github issue #34).
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/ocran/runner.rb', line 66 def apply_gemfile_to_bundler_env gemfile = @option.gemfile.to_s active = active_bundler_gemfile ENV["BUNDLE_GEMFILE"] = gemfile return if active.nil? || same_file?(active, gemfile) # Past this point the ambient bundle is a different one, so anything # else describing it has to go: BUNDLE_LOCKFILE names the lockfile of # the bundle being displaced, and Bundler would sooner believe it than # the Gemfile we just pointed it at. ENV.delete("BUNDLE_LOCKFILE") return unless defined?(Bundler) && Bundler.respond_to?(:reset!) # `bundle exec` puts -rbundler/setup in RUBYOPT as well, so the wrong # bundle may already be set up by the time this runs. The variable # alone is then too late: bundler/setup sits in $LOADED_FEATURES, and # the script's own `require "bundler/setup"` would be a no-op. Drop # Bundler's memoized state and let it be set up afresh, against the # Gemfile we were given. verbose "Rebinding Bundler from #{active} to #{gemfile}" Bundler.reset! $LOADED_FEATURES.delete_if { |feature| feature.match?(RuntimeEnvironment::BUNDLER_SETUP_FEATURE) } end |
#attempt_load_autoload(ignore_modules = []) ⇒ Object
Force loading autoloaded constants. Searches through all modules (and hence classes), and checks their constants for autoloaded ones, then attempts to load them.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/ocran/runner.rb', line 132 def attempt_load_autoload(ignore_modules = []) checked_modules = ignore_modules.inject({}) { |h, mod| h[mod] = true; h } while ObjectSpace.each_object(Module).count { |mod| next if checked_modules.include?(mod) mod.constants.each do |const| next unless mod.autoload?(const) say "Attempting to trigger autoload of #{mod}::#{const}" begin mod.const_get(const) rescue ScriptError, StandardError => e # Some autoload constants may throw exceptions beyond the expected # errors. This includes issues dependent on the system or execution # environment, so it is preferable to ignore exceptions other than # critical errors. warning "#{mod}::#{const} loading failed: #{e.}" end end checked_modules[mod] = true }.nonzero? # Loops until all constants have been checked. end end |
#build ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/ocran/runner.rb', line 155 def build # If the script was run and autoload is enabled, attempt to autoload libraries. if @option.force_autoload? attempt_load_autoload(@ignore_modules) end @post_env = RuntimeEnvironment.save # NOTE: From this point, $LOADED_FEATURES has been captured, so it is now # safe to call require_relative. ENV.replace(@pre_env.env) # It might be useful to reset the current directory to the point where the # command was launched, especially when implementing the builder object. Dir.chdir(@pre_env.pwd) require_relative "direction" direction = Direction.new(@post_env, @pre_env, @option) if @option.use_inno_setup? # Native on Windows; on POSIX allow it when an ISCC command is # available (e.g. via Wine wrapper scripts or in tests). if Gem.win_platform? || system("command -v ISCC > /dev/null 2>&1") direction.build_inno_setup_installer else raise "Inno Setup is only supported on Windows (no ISCC command found in PATH)" end elsif @option.macosx_bundle direction.build_macosx_bundle(@option.macosx_bundle) elsif @option.output_dir direction.build_output_dir(@option.output_dir) elsif @option.output_zip direction.build_zip(@option.output_zip) elsif @option.cosmo_zip? direction.build_cosmo_zip_exe else direction.build_stab_exe end rescue RuntimeError => e fatal_error e. end |
#fatal_error(statement) ⇒ Object
9 10 11 12 |
# File 'lib/ocran/runner.rb', line 9 def fatal_error(statement) error statement exit false end |
#run ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ocran/runner.rb', line 38 def run at_exit do if $!.nil? or $!.kind_of?(SystemExit) build exit end end exit unless @option.run_script? if @option.gemfile apply_gemfile_to_bundler_env else warn_about_foreign_bundle end say "Loading script to check dependencies" $PROGRAM_NAME = @option.script.to_s end |
#same_file?(a, b) ⇒ Boolean
125 126 127 |
# File 'lib/ocran/runner.rb', line 125 def same_file?(a, b) File.identical?(a, b) || File.(a) == File.(b) end |
#warn_about_foreign_bundle ⇒ Object
Without --gemfile the dependency run inherits whatever bundle the environment carries, and when that is some other project's bundle the script's own gems are simply absent. The failure that follows is a LoadError from deep inside the script, which says nothing about the bundle being the wrong one, so say it here while the reason is still visible.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/ocran/runner.rb', line 97 def warn_about_foreign_bundle # Only when a bundle really is set up. BUNDLE_GEMFILE alone is # inherited by any child process and governs nothing on its own. return unless @pre_env.bundler_setup_loaded? active = active_bundler_gemfile return if active.nil? || active.empty? app_gemfile = @option.application_gemfile return if app_gemfile && same_file?(active, app_gemfile) warning "Running under Bundler with #{active}, which is not the Gemfile " \ "#{@option.script} runs under" + (app_gemfile ? "; pass --gemfile #{app_gemfile} to package against that bundle" : "") end |