[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / lib / bundler / cli / exec.rb
blobf81cd5d2c40642ef253369ef280202e971186395
1 # frozen_string_literal: true
3 require_relative "../current_ruby"
5 module Bundler
6   class CLI::Exec
7     attr_reader :options, :args, :cmd
9     TRAPPED_SIGNALS = %w[INT].freeze
11     def initialize(options, args)
12       @options = options
13       @cmd = args.shift
14       @args = args
15       @args << { close_others: !options.keep_file_descriptors? } unless Bundler.current_ruby.jruby?
16     end
18     def run
19       validate_cmd!
20       SharedHelpers.set_bundle_environment
21       if bin_path = Bundler.which(cmd)
22         if !Bundler.settings[:disable_exec_load] && ruby_shebang?(bin_path)
23           return kernel_load(bin_path, *args)
24         end
25         kernel_exec(bin_path, *args)
26       else
27         # exec using the given command
28         kernel_exec(cmd, *args)
29       end
30     end
32     private
34     def validate_cmd!
35       return unless cmd.nil?
36       Bundler.ui.error "bundler: exec needs a command to run"
37       exit 128
38     end
40     def kernel_exec(*args)
41       Kernel.exec(*args)
42     rescue Errno::EACCES, Errno::ENOEXEC
43       Bundler.ui.error "bundler: not executable: #{cmd}"
44       exit 126
45     rescue Errno::ENOENT
46       Bundler.ui.error "bundler: command not found: #{cmd}"
47       Bundler.ui.warn "Install missing gem executables with `bundle install`"
48       exit 127
49     end
51     def kernel_load(file, *args)
52       args.pop if args.last.is_a?(Hash)
53       ARGV.replace(args)
54       $0 = file
55       Process.setproctitle(process_title(file, args)) if Process.respond_to?(:setproctitle)
56       require_relative "../setup"
57       TRAPPED_SIGNALS.each {|s| trap(s, "DEFAULT") }
58       Kernel.load(file)
59     rescue SystemExit, SignalException
60       raise
61     rescue Exception # rubocop:disable Lint/RescueException
62       Bundler.ui.error "bundler: failed to load command: #{cmd} (#{file})"
63       Bundler::FriendlyErrors.disable!
64       raise
65     end
67     def process_title(file, args)
68       "#{file} #{args.join(" ")}".strip
69     end
71     def ruby_shebang?(file)
72       possibilities = [
73         "#!/usr/bin/env ruby\n",
74         "#!/usr/bin/env jruby\n",
75         "#!/usr/bin/env truffleruby\n",
76         "#!#{Gem.ruby}\n",
77       ]
79       if File.zero?(file)
80         Bundler.ui.warn "#{file} is empty"
81         return false
82       end
84       first_line = File.open(file, "rb") {|f| f.read(possibilities.map(&:size).max) }
85       possibilities.any? {|shebang| first_line.start_with?(shebang) }
86     end
87   end
88 end