bin/autoproj_install in autoproj-2.7.1 vs bin/autoproj_install in autoproj-2.8.0

- old
+ new

@@ -55,17 +55,19 @@ env['PATH'] = self.class.sanitize_env(ENV['PATH'] || "") env['BUNDLE_GEMFILE'] = [] load_config if config['ruby_executable'] != Gem.ruby - raise "this autoproj installation was already bootstrapped using #{config['ruby_executable']}, but you are currently running under #{Gem.ruby}. Changing the ruby interpreter in a given workspace is not supported, you need to do a clean bootstrap" + raise "this autoproj installation was already bootstrapped using "\ + "#{config['ruby_executable']}, but you are currently running "\ + "under #{Gem.ruby}. Changing the ruby interpreter in a given "\ + "workspace is not supported, you need to do a clean bootstrap" end @ruby_executable = config['ruby_executable'] @local = false - default_gem_path = File.join(Dir.home, '.autoproj', 'gems') - @gems_install_path = default_gem_path + install_gems_in_gem_user_dir end def env_for_child env.inject(Hash.new) do |h, (k, v)| h[k] = if v && !v.empty? then v.join(File::PATH_SEPARATOR) @@ -140,52 +142,79 @@ # The path into which the workspace's gems should be installed # # They are installed in a versioned subdirectory of this path, e.g. # {#gem_path_suffix}. - # + # # @return [String] attr_reader :gems_install_path # The GEM_HOME under which the workspace's gems should be installed - # + # # @return [String] - def gems_gem_home; File.join(gems_install_path, gem_path_suffix) end + def gems_gem_home + File.join(gems_install_path, gem_path_suffix) + end # Sets where the workspace's gems should be installed # # @param [String] path the absolute path that should be given to # bundler. The gems themselves will be installed in the # {#gem_path_suffix} subdirectory under this def gems_install_path=(path) @gems_install_path = path end + + private def xdg_var(varname, default) + if (env = ENV[varname]) && !env.empty? + env + else + default + end + end + # Install autoproj in Gem's default user dir def install_gems_in_gem_user_dir - @gems_install_path = File.join(Gem.user_home, '.gem') + xdg_default_gem_path = xdg_var('XDG_DATA_HOME', + File.join(Dir.home, '.local', 'share', 'autoproj', 'gems')) + default_gem_path = File.join( + Dir.home, '.autoproj', 'gems') + @gems_install_path = + if File.directory?(xdg_default_gem_path) + xdg_default_gem_path + elsif File.directory?(default_gem_path) + default_gem_path + else + xdg_default_gem_path + end end # Whether autoproj should prefer OS-independent packages over their # OS-packaged equivalents (e.g. the thor gem vs. the ruby-thor # Debian package) - def prefer_indep_over_os_packages?; @prefer_indep_over_os_packages end + def prefer_indep_over_os_packages? + @prefer_indep_over_os_packages + end # (see #prefer_index_over_os_packages?) - def prefer_indep_over_os_packages=(flag); @prefer_indep_over_os_packages = !!flag end + def prefer_indep_over_os_packages=(flag) + @prefer_indep_over_os_packages = !!flag + end def self.guess_gem_program ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME'] ruby_bindir = RbConfig::CONFIG['bindir'] candidates = ['gem'] if ruby_bin =~ /^ruby(.+)$/ - candidates.unshift "gem#{$1}" + candidates.unshift "gem#{$1}" end candidates.each do |gem_name| if File.file?(gem_full_path = File.join(ruby_bindir, gem_name)) return gem_full_path end end - raise ArgumentError, "cannot find a gem program (tried #{candidates.sort.join(", ")} in #{ruby_bindir})" + raise ArgumentError, "cannot find a gem program "\ + "(tried #{candidates.sort.join(", ")} in #{ruby_bindir})" end # The content of the default {#gemfile} # # @param [String] autoproj_version a constraint on the autoproj version @@ -205,43 +234,52 @@ @local = true end opt.on '--skip-stage2', 'do not run the stage2 install' do @skip_stage2 = true end - opt.on '--gem-source=URL', String, "use this source for RubyGems instead of rubygems.org" do |url| + opt.on '--gem-source=URL', String, "use this source for RubyGems "\ + "instead of rubygems.org" do |url| @gem_source = url end - opt.on '--gems-path=PATH', "install gems under this path instead of ~/.autoproj/gems" do |path| + opt.on '--gems-path=PATH', "install gems under this path instead "\ + "of ~/.autoproj/gems" do |path| self.gems_install_path = path end opt.on '--public-gems', "install gems in the default gem location" do self.install_gems_in_gem_user_dir end - opt.on '--version=VERSION_CONSTRAINT', String, 'use the provided string as a version constraint for autoproj' do |version| + opt.on '--version=VERSION_CONSTRAINT', String, 'use the provided "\ + "string as a version constraint for autoproj' do |version| if @gemfile raise "cannot give both --version and --gemfile" end @gemfile = default_gemfile_contents(version) end - opt.on '--gemfile=PATH', String, 'use the given Gemfile to install autoproj instead of the default' do |path| + opt.on '--gemfile=PATH', String, 'use the given Gemfile to install "\ + "autoproj instead of the default' do |path| if @gemfile raise "cannot give both --version and --gemfile" end @gemfile = File.read(path) end - opt.on '--seed-config=PATH', String, 'path to a seed file that should be used to initialize the configuration' do |path| + opt.on '--seed-config=PATH', String, 'path to a seed file that "\ + "should be used to initialize the configuration' do |path| @config.merge!(YAML.load(File.read(path))) end - opt.on '--prefer-os-independent-packages', 'prefer OS-independent packages (such as a RubyGem) over their OS-packaged equivalent (e.g. the thor gem vs. the ruby-thor debian package)' do + opt.on '--prefer-os-independent-packages', 'prefer OS-independent "\ + "packages (such as a RubyGem) over their OS-packaged equivalent "\ + "(e.g. the thor gem vs. the ruby-thor debian package)' do @prefer_indep_over_os_packages = true end - opt.on '--[no-]color', 'do not use colored output (enabled by default if the terminal supports it)' do |color| + opt.on '--[no-]color', 'do not use colored output (enabled by "\ + "default if the terminal supports it)' do |color| if color then autoproj_options << "--color" else autoproj_options << '--no-color' end end - opt.on '--[no-]progress', 'do not use progress output (enabled by default if the terminal supports it)' do |color| + opt.on '--[no-]progress', 'do not use progress output (enabled by "\ + "default if the terminal supports it)' do |color| if color then autoproj_options << "--progress" else autoproj_options << '--no-progress' end end end @@ -270,31 +308,34 @@ redirection = Hash[out: :close] end result = system( env_for_child.merge('GEM_HOME' => gems_gem_home), - Gem.ruby, gem_program, 'install', '--env-shebang', '--no-document', '--no-format-executable', '--clear-sources', '--source', gem_source, - *local, - "--bindir=#{File.join(gems_gem_home, 'bin')}", 'bundler', **redirection) + Gem.ruby, gem_program, 'install', + '--env-shebang', '--no-document', '--no-format-executable', + '--clear-sources', '--source', gem_source, + *local, "--bindir=#{File.join(gems_gem_home, 'bin')}", + 'bundler', **redirection) if !result STDERR.puts "FATAL: failed to install bundler in #{gems_gem_home}" nil end bundler_path = File.join(gems_gem_home, 'bin', 'bundler') if File.exist?(bundler_path) bundler_path else - STDERR.puts "gem install bundler returned successfully, but still cannot find bundler in #{bundler_path}" + STDERR.puts "gem install bundler returned successfully, but still "\ + "cannot find bundler in #{bundler_path}" nil end end def install_autoproj(bundler) - # Force bundler to update. If the user does not want this, let him specify a - # Gemfile with tighter version constraints + # Force bundler to update. If the user does not want this, let + # him specify a Gemfile with tighter version constraints lockfile = File.join(dot_autoproj, 'Gemfile.lock') if File.exist?(lockfile) FileUtils.rm lockfile end @@ -302,10 +343,11 @@ opts = Array.new opts << '--local' if local? opts << "--path=#{gems_install_path}" shims_path = File.join(dot_autoproj, 'bin') + result = system(clean_env, Gem.ruby, bundler, 'install', "--gemfile=#{autoproj_gemfile_path}", "--shebang=#{Gem.ruby}", "--binstubs=#{shims_path}", @@ -314,49 +356,77 @@ if !result STDERR.puts "FATAL: failed to install autoproj in #{dot_autoproj}" exit 1 end ensure - self.class.rewrite_shims(shims_path, ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home) + self.class.rewrite_shims(shims_path, ruby_executable, + root_dir, autoproj_gemfile_path, gems_gem_home) end EXCLUDED_FROM_SHIMS = %w{rake thor} - def self.rewrite_shims(shim_path, ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home) + def self.rewrite_shims(shim_path, ruby_executable, + root_dir, autoproj_gemfile_path, gems_gem_home) FileUtils.mkdir_p shim_path File.open(File.join(shim_path, 'ruby'), 'w') do |io| io.puts "#! /bin/sh" io.puts "exec #{ruby_executable} \"$@\"" end FileUtils.chmod 0755, File.join(shim_path, 'ruby') - FileUtils.touch File.join(shim_path, 'bundler') - FileUtils.touch File.join(shim_path, 'bundle') Dir.glob(File.join(shim_path, '*')) do |bin_script| - next if !File.file?(bin_script) + next unless File.file?(bin_script) + bin_name = File.basename(bin_script) if EXCLUDED_FROM_SHIMS.include?(bin_name) FileUtils.rm_f bin_script next end next if bin_name == 'ruby' bin_shim = File.join(shim_path, bin_name) bin_script_lines = File.readlines(bin_script) + next if has_autoproj_preamble?(bin_script_lines) + File.open(bin_shim, 'w') do |io| if bin_name == 'bundler' || bin_name == 'bundle' - io.puts shim_bundler(ruby_executable, autoproj_gemfile_path, gems_gem_home) + io.puts shim_bundler(bin_script_lines, ruby_executable, + autoproj_gemfile_path, gems_gem_home) else - load_line = bin_script_lines.grep(/load Gem.bin_path/).first - io.puts shim_script(ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home, load_line) + io.puts shim_script(bin_script_lines, ruby_executable, root_dir, + autoproj_gemfile_path, gems_gem_home) end end FileUtils.chmod 0755, bin_shim end end - def self.shim_bundler(ruby_executable, autoproj_gemfile_path, gems_gem_home) + def self.new_style_bundler_binstub?(script_lines) + script_lines.any? { |l| l =~ /This file was generated by Bundler/ } + end + + def self.has_autoproj_preamble?(script_lines) + script_lines.any? { |l| l =~ /Autoproj generated preamble/ } + end + + def self.shim_bundler(script_lines, ruby_executable, autoproj_gemfile_path, gems_gem_home) + return shim_bundler_old(ruby_executable, autoproj_gemfile_path, gems_gem_home) \ + unless new_style_bundler_binstub?(script_lines) + + script_lines.insert(1, <<-RESTART_BUNDLER) +# Autoproj generated preamble +if defined?(Bundler) + Bundler.with_clean_env do + exec($0, *ARGV) + end +end +ENV['BUNDLE_GEMFILE'] = '#{autoproj_gemfile_path}' + RESTART_BUNDLER + script_lines.join + end + + def self.shim_bundler_old(ruby_executable, autoproj_gemfile_path, gems_gem_home) "#! #{ruby_executable} if defined?(Bundler) Bundler.with_clean_env do exec($0, *ARGV) @@ -368,12 +438,39 @@ ENV.delete('GEM_PATH') Gem.paths = Hash['GEM_HOME' => '#{gems_gem_home}', 'GEM_PATH' => ''] load Gem.bin_path('bundler', 'bundler')" end - - def self.shim_script(ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home, load_line) + + def self.shim_script(script_lines, ruby_executable, root_dir, + autoproj_gemfile_path, gems_gem_home) + new_style = !script_lines.empty? && script_lines.any? do |l| + l =~ /This file was generated by Bundler/ + end + load_line = script_lines.grep(/load Gem.bin_path/).first + return shim_script_old(ruby_executable, root_dir, + autoproj_gemfile_path, gems_gem_home, load_line) \ + unless new_style + + script_lines.insert(1, <<-AUTOPROJ_PREAMBLE) +# Autoproj generated preamble, v1 +if defined?(Bundler) + Bundler.with_clean_env do + exec(Hash['RUBYLIB' => nil], $0, *ARGV) + end +elsif ENV['RUBYLIB'] + exec(Hash['RUBYLIB' => nil], $0, *ARGV) +end + +ENV['BUNDLE_GEMFILE'] = '#{autoproj_gemfile_path}' +ENV['AUTOPROJ_CURRENT_ROOT'] = '#{root_dir}' + AUTOPROJ_PREAMBLE + return script_lines.join + end + + def self.shim_script_old(ruby_executable, root_dir, autoproj_gemfile_path, + gems_gem_home, load_line) "#! #{ruby_executable} if defined?(Bundler) Bundler.with_clean_env do exec(Hash['RUBYLIB' => nil], $0, *ARGV) @@ -431,13 +528,14 @@ "", "config_path = File.join(__dir__, 'config.yml')", "if File.file?(config_path)", " require 'yaml'", " config = YAML.load(File.read(config_path)) || Hash.new", - " (config['plugins'] || Hash.new).each do |plugin_name, (version, options)|", - " gem plugin_name, version, **options", - " end", + " (config['plugins'] || Hash.new).", + " each do |plugin_name, (version, options)|", + " gem plugin_name, version, **options", + " end", "end" ].join("\n") FileUtils.mkdir_p File.dirname(autoproj_gemfile_path) File.open(autoproj_gemfile_path, 'w') do |io| @@ -447,11 +545,12 @@ ENV_BUNDLE_GEMFILE_RX = /^(\s*ENV\[['"]BUNDLE_GEMFILE['"]\]\s*)(?:\|\|)?=/ def find_in_clean_path(command, *additional_paths) - clean_path = env_for_child['PATH'].split(File::PATH_SEPARATOR) + additional_paths + clean_path = env_for_child['PATH'].split(File::PATH_SEPARATOR) + + additional_paths clean_path.each do |p| full_path = File.join(p, command) if File.file?(full_path) return full_path end @@ -471,21 +570,24 @@ # because of limitations of autoproj 1.x. This leads to # Gem.bindir being *not* valid for subprocesses # # So, we're calling 'gem' as a subcommand to discovery the # actual bindir - bindir = IO.popen(env_for_child, [Gem.ruby, '-e', 'puts "#{Gem.user_dir}/bin"']).read + bindir = IO.popen(env_for_child, + [Gem.ruby, '-e', 'puts "#{Gem.user_dir}/bin"']).read if bindir @gem_bindir = bindir.chomp else raise "FATAL: cannot run #{Gem.ruby} -e 'puts Gem.bindir'" end end def install if ENV['BUNDLER_GEMFILE'] - raise "cannot run autoproj_install or autoproj_bootstrap while under a 'bundler exec' subcommand or having loaded an env.sh. Open a new console and try again" + raise "cannot run autoproj_install or autoproj_bootstrap while "\ + "under a 'bundler exec' subcommand or having loaded an env.sh. "\ + "Open a new console and try again" end gem_program = self.class.guess_gem_program puts "Detected 'gem' to be #{gem_program}" env['GEM_HOME'] = [gems_gem_home] @@ -511,11 +613,11 @@ install_autoproj(bundler) end def load_config v1_config_path = File.join(root_dir, 'autoproj', 'config.yml') - + config = Hash.new if File.file?(v1_config_path) config.merge!(YAML.load(File.read(v1_config_path)) || Hash.new) end if File.file?(autoproj_config_path) @@ -525,11 +627,14 @@ ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME'] ruby_bindir = RbConfig::CONFIG['bindir'] ruby_executable = File.join(ruby_bindir, ruby) if current = config['ruby_executable'] # When upgrading or reinstalling if current != ruby_executable - raise "this workspace has already been initialized using #{current}, you cannot run autoproj install with #{ruby_executable}. If you know what you're doing, delete the ruby_executable line in config.yml and try again" + raise "this workspace has already been initialized using "\ + "#{current}, you cannot run autoproj install with "\ + "#{ruby_executable}. If you know what you're doing, "\ + "delete the ruby_executable line in config.yml and try again" end else config['ruby_executable'] = ruby_executable end @@ -584,24 +689,25 @@ require 'autobuild' puts "saving temporary env.sh and .autoproj/env.sh" save_env_sh(*vars) puts "running 'autoproj envsh' to generate a proper env.sh" if !system(Gem.ruby, autoproj_path, 'envsh', *autoproj_options) - STDERR.puts "failed to run autoproj envsh on the newly installed autoproj (#{autoproj_path})" + STDERR.puts "failed to run autoproj envsh on the newly installed "\ + "autoproj (#{autoproj_path})" exit 1 end # This is really needed on an existing install to install the # gems that were present in the v1 layout puts "running 'autoproj osdeps' to re-install missing gems" if !system(Gem.ruby, autoproj_path, 'osdeps') - STDERR.puts "failed to run autoproj osdeps on the newly installed autoproj (#{autoproj_path})" + STDERR.puts "failed to run autoproj osdeps on the newly installed "\ + "autoproj (#{autoproj_path})" exit 1 end end end end end - ENV.delete('BUNDLE_GEMFILE') ENV.delete('RUBYLIB') ops = Autoproj::Ops::Install.new(Dir.pwd)