lib/autoproj/package_managers/bundler_manager.rb in autoproj-2.11.0 vs lib/autoproj/package_managers/bundler_manager.rb in autoproj-2.12.0

- old
+ new

@@ -22,10 +22,18 @@ @with_prerelease = saved_flag end end end + # Directory with cached .gem packages + # + # The directory must exist, but may be empty. + # It is initialized with {BundlerManager.cache_dir} + # + # @return [String] + attr_accessor :cache_dir + # (see Manager#call_while_empty?) def call_while_empty? !workspace_configuration_gemfiles.empty? end @@ -43,19 +51,36 @@ env.add_path 'PATH', File.join(ws.prefix_dir, 'gems', 'bin') env.add_path 'PATH', File.join(ws.dot_autoproj_dir, 'bin') env.set 'GEM_HOME', config.gems_gem_home env.clear 'GEM_PATH' + if (bundler_version = config.bundler_version) + env.set 'BUNDLER_VERSION', bundler_version + else + env.clear 'BUNDLER_VERSION' + end gemfile_path = File.join(ws.prefix_dir, 'gems', 'Gemfile') env.set('BUNDLE_GEMFILE', gemfile_path) if File.file?(gemfile_path) - Autobuild.programs['bundler'] = File.join(ws.dot_autoproj_dir, - 'bin', 'bundle') - Autobuild.programs['bundle'] = File.join(ws.dot_autoproj_dir, - 'bin', 'bundle') + if cache_dir && File.exist?(cache_dir) + vendor_dir = File.join(File.dirname(gemfile_path), 'vendor') + FileUtils.mkdir_p vendor_dir + bundler_cache_dir = File.join(vendor_dir, 'cache') + if File.writable?(cache_dir) + create_cache_symlink(cache_dir, bundler_cache_dir) + else + Autoproj.warn "BundlerManager: #{cache_dir} is read-only "\ + "copying the cache instead of symlinking it" + create_cache_copy(cache_dir, bundler_cache_dir) + end + end + Autobuild.programs['bundler'] = + Autobuild.programs['bundle'] = + File.join(ws.dot_autoproj_dir, 'bin', 'bundle') + env.init_from_env 'RUBYLIB' env.inherit 'RUBYLIB' # Sanitize the rubylib we get from the environment by removing # anything that comes from Gem or Bundler original_rubylib = @@ -94,10 +119,47 @@ if (bundle_rubylib = discover_bundle_rubylib(silent_errors: true)) update_env_rubylib(bundle_rubylib, system_rubylib) end end + def create_cache_symlink(cache_dir, bundler_cache_dir) + valid = !File.exist?(bundler_cache_dir) || + File.symlink?(bundler_cache_dir) + + unless valid + Autoproj.warn "cannot use #{cache_dir} as gem cache as "\ + "#{bundler_cache_dir} already exists" + return + end + + FileUtils.rm_f bundler_cache_dir + FileUtils.ln_s cache_dir, bundler_cache_dir + end + + def create_cache_copy(cache_dir, bundler_cache_dir) + valid = !File.exist?(bundler_cache_dir) || + File.directory?(bundler_cache_dir) || + File.symlink?(bundler_cache_dir) + + unless valid + Autoproj.warn "cannot use #{cache_dir} as gem cache as "\ + "#{bundler_cache_dir} already exists" + return + end + + # Gracefully upgrade from the symlinks + FileUtils.rm_f bundler_cache_dir if File.symlink?(bundler_cache_dir) + FileUtils.mkdir_p bundler_cache_dir + + Dir.glob(File.join(cache_dir, '*.gem')) do |path_src| + path_dest = File.join(bundler_cache_dir, File.basename(path_src)) + next if File.exist?(path_dest) + + FileUtils.cp path_src, path_dest + end + end + # Enumerate the per-gem build configurations def self.per_gem_build_config(ws) ws.config.get('bundler.build', {}) end @@ -246,24 +308,28 @@ mapping.each do |_file, backup_file| FileUtils.rm backup_file if File.file?(backup_file) end end - def self.run_bundler_install(ws, gemfile, *options, - update: true, binstubs: nil, - gem_home: ws.config.gems_gem_home, - gem_path: ws.config.gems_install_path) + def self.run_bundler_install( + ws, gemfile, *options, + update: true, binstubs: nil, + bundler_version: ws.config.bundler_version, + gem_home: ws.config.gems_gem_home, + gem_path: ws.config.gems_install_path + ) FileUtils.rm "#{gemfile}.lock" if update && File.file?("#{gemfile}.lock") options << '--path' << gem_path options << "--shebang" << Gem.ruby options << "--binstubs" << binstubs if binstubs apply_build_config(ws) connections = Set.new run_bundler(ws, 'install', *options, + bundler_version: bundler_version, gem_home: gem_home, gemfile: gemfile) do |line| case line when /Installing (.*)/ Autobuild.message " bundler: installing #{$1}" when /Fetching.*from (.*)/ @@ -274,34 +340,45 @@ end end end end - def self.bundle_gem_path(ws, gem_name, gem_home: nil, gemfile: nil) + def self.bundle_gem_path(ws, gem_name, + bundler_version: ws.config.bundler_version, + gem_home: nil, gemfile: nil) path = String.new - PackageManagers::BundlerManager.run_bundler( + run_bundler( ws, 'show', gem_name, - gem_home: gem_home, + bundler_version: bundler_version, gem_home: gem_home, gemfile: gemfile) { |line| path << line } path.chomp end def self.default_bundler(ws) File.join(ws.dot_autoproj_dir, 'bin', 'bundle') end - def self.run_bundler(ws, *commandline, gem_home: nil, gemfile: nil) + def self.run_bundler(ws, *commandline, + bundler_version: ws.config.bundler_version, + gem_home: ws.config.gems_gem_home, + gemfile: default_gemfile_path(ws)) bundle = Autobuild.programs['bundle'] || default_bundler(ws) - Bundler.with_clean_env do + Autoproj.bundler_with_unbundled_env do + bundler_version_env = + if bundler_version + { 'BUNDLER_VERSION' => bundler_version } + else + {} + end target_env = Hash[ 'GEM_HOME' => gem_home, 'GEM_PATH' => nil, 'BUNDLE_GEMFILE' => gemfile, 'RUBYOPT' => nil, - 'RUBYLIB' => rubylib_for_bundler - ] + 'RUBYLIB' => rubylib_for_bundler, + ].merge(bundler_version_env) ws.run('autoproj', 'osdeps', bundle, *commandline, working_directory: File.dirname(gemfile), env: target_env) { |line| yield(line) if block_given? } end @@ -388,13 +465,17 @@ gemfiles << overrides_gemfile end gemfiles end + def self.default_gemfile_path(ws) + File.join(ws.prefix_dir, 'gems', 'Gemfile') + end + def install(gems, filter_uptodate_packages: false, install_only: false) - root_dir = File.join(ws.prefix_dir, 'gems') - gemfile_path = File.join(root_dir, 'Gemfile') + gemfile_path = self.class.default_gemfile_path(ws) + root_dir = File.dirname(gemfile_path) gemfile_lock_path = "#{gemfile_path}.lock" backups = Hash[ gemfile_path => "#{gemfile_path}.orig", gemfile_lock_path => "#{gemfile_lock_path}.orig" ] @@ -464,11 +545,11 @@ end def discover_rubylib require 'bundler' Tempfile.open 'autoproj-rubylib' do |io| - result = Bundler.clean_system( + result = Autoproj.bundler_unbundled_system( Hash['RUBYLIB' => nil], Autobuild.tool('ruby'), '-e', 'puts $LOAD_PATH', out: io, err: '/dev/null') if result @@ -488,10 +569,10 @@ gemfile = File.join(ws.prefix_dir, 'gems', 'Gemfile') silent_redirect = Hash.new silent_redirect[:err] = :close if silent_errors env = ws.env.resolved_env Tempfile.open 'autoproj-rubylib' do |io| - result = Bundler.clean_system( + result = Autoproj.bundler_unbundled_system( Hash['GEM_HOME' => env['GEM_HOME'], 'GEM_PATH' => env['GEM_PATH'], 'BUNDLE_GEMFILE' => gemfile, 'RUBYOPT' => nil, 'RUBYLIB' => self.class.rubylib_for_bundler], Autobuild.tool('ruby'), '-rbundler/setup', '-e', 'puts $LOAD_PATH',