lib/autoproj/package_managers/bundler_manager.rb in autoproj-2.14.0 vs lib/autoproj/package_managers/bundler_manager.rb in autoproj-2.15.0
- old
+ new
@@ -1,6 +1,6 @@
-require 'bundler'
+require "bundler"
module Autoproj
module PackageManagers
# Package manager interface for the RubyGems system
class BundlerManager < Manager
class << self
@@ -47,46 +47,46 @@
def initialize_environment
env = ws.env
config = ws.config
- 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'
+ 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
+ env.set "BUNDLER_VERSION", bundler_version
else
- env.clear 'BUNDLER_VERSION'
+ 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)
+ gemfile_path = File.join(ws.prefix_dir, "gems", "Gemfile")
+ env.set("BUNDLE_GEMFILE", gemfile_path) if File.file?(gemfile_path)
if cache_dir && File.exist?(cache_dir)
- vendor_dir = File.join(File.dirname(gemfile_path), 'vendor')
+ vendor_dir = File.join(File.dirname(gemfile_path), "vendor")
FileUtils.mkdir_p vendor_dir
- bundler_cache_dir = File.join(vendor_dir, 'cache')
+ 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')
+ Autobuild.programs["bundler"] =
+ Autobuild.programs["bundle"] =
+ File.join(ws.dot_autoproj_dir, "bin", "bundle")
- env.init_from_env 'RUBYLIB'
- env.inherit 'RUBYLIB'
+ 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 =
- (env['RUBYLIB'] || "").split(File::PATH_SEPARATOR).find_all do |p|
+ (env["RUBYLIB"] || "").split(File::PATH_SEPARATOR).find_all do |p|
!p.start_with?(Bundler.rubygems.gem_dir) &&
Bundler.rubygems.gem_path
.none? { |gem_p| p.start_with?(gem_p) }
end
@@ -95,26 +95,26 @@
# Do not explicitely add the system rubylib to the
# environment, the interpreter will do it for us.
#
# This allows to use a binstub generated for one of ruby
# interpreter version on our workspace
- env.system_env['RUBYLIB'] = []
- env.original_env['RUBYLIB'] = (original_rubylib - system_rubylib)
+ env.system_env["RUBYLIB"] = []
+ env.original_env["RUBYLIB"] = (original_rubylib - system_rubylib)
.join(File::PATH_SEPARATOR)
end
ws.config.each_reused_autoproj_installation do |p|
reused_w = ws.new(p)
- env.add_path 'PATH', File.join(reused_w.prefix_dir, 'gems', 'bin')
+ env.add_path "PATH", File.join(reused_w.prefix_dir, "gems", "bin")
end
prefix_gems = File.join(ws.prefix_dir, "gems")
FileUtils.mkdir_p prefix_gems
- gemfile = File.join(prefix_gems, 'Gemfile')
+ gemfile = File.join(prefix_gems, "Gemfile")
unless File.exist?(gemfile)
Ops.atomic_write(gemfile) do |io|
- dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, 'Gemfile')
+ dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, "Gemfile")
io.puts "eval_gemfile \"#{dot_autoproj_gemfile}\""
end
end
if (bundle_rubylib = discover_bundle_rubylib(silent_errors: true))
@@ -150,62 +150,62 @@
# 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|
+ 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', {})
+ ws.config.get("bundler.build", {})
end
# Add new build configuration arguments for a given gem
#
# This is meant to be used from the Autoproj configuration files,
# e.g. overrides.rb or package configuration
def self.add_build_configuration_for(gem_name, build_config, ws: Autoproj.workspace)
- c = ws.config.get('bundler.build', {})
+ c = ws.config.get("bundler.build", {})
c[gem_name] = [c[gem_name], build_config].compact.join(" ")
- ws.config.set('bundler.build', c)
+ ws.config.set("bundler.build", c)
end
# Set the build configuration for the given gem
#
# This is meant to be used from the Autoproj configuration files,
# e.g. overrides.rb or package configuration
def self.configure_build_for(gem_name, build_config, ws: Autoproj.workspace)
- c = ws.config.get('bundler.build', {})
+ c = ws.config.get("bundler.build", {})
c[gem_name] = build_config
- ws.config.set('bundler.build', c)
+ ws.config.set("bundler.build", c)
end
# Removes build configuration flags for the given gem
#
# This is meant to be used from the Autoproj configuration files,
# e.g. overrides.rb or package configuration
def self.remove_build_configuration_for(gem_name, ws: Autoproj.workspace)
- c = ws.config.get('bundler.build', {})
+ c = ws.config.get("bundler.build", {})
c.delete(gem_name)
- ws.config.set('bundler.build', c)
+ ws.config.set("bundler.build", c)
end
# @api private
#
# Apply configured per-gem build configuration options
#
# @param [Workspace] ws the workspace whose bundler configuration
# should be updated
# @return [void]
def self.apply_build_config(ws)
- root_dir = File.join(ws.prefix_dir, 'gems')
+ root_dir = File.join(ws.prefix_dir, "gems")
current_config_path = File.join(root_dir, ".bundle", "config")
current_config =
if File.file?(current_config_path)
File.readlines(current_config_path)
else
@@ -219,24 +219,24 @@
new_config = current_config.map do |line|
next(line) unless (m = line.match(/BUNDLE_BUILD__(.*): "(.*)"$/))
next unless (desired_config = build_config.delete(m[1]))
- if m[2] != desired_config
- "BUNDLE_BUILD__#{m[1]}: \"#{desired_config}\""
- else
+ if m[2] == desired_config
line
+ else
+ "BUNDLE_BUILD__#{m[1]}: \"#{desired_config}\""
end
end.compact
build_config.each do |name, config|
new_config << "BUNDLE_BUILD__#{name}: \"#{config}\""
end
if new_config != current_config
FileUtils.mkdir_p File.dirname(current_config_path)
- File.open(current_config_path, 'w') do |io|
+ File.open(current_config_path, "w") do |io|
io.write new_config.join
end
end
end
@@ -248,35 +248,17 @@
# @param [Array<String>] bundle_rubylib the rubylib entries reported
# by bundler
# @param [Array<String>] system_rubylib the rubylib entries that are
# set by the underlying ruby interpreter itself
def update_env_rubylib(bundle_rubylib, system_rubylib = discover_rubylib)
- current = (ws.env.resolved_env['RUBYLIB'] || '')
+ current = (ws.env.resolved_env["RUBYLIB"] || "")
.split(File::PATH_SEPARATOR) + system_rubylib
(bundle_rubylib - current).each do |p|
- ws.env.add_path('RUBYLIB', p)
+ ws.env.add_path("RUBYLIB", p)
end
end
- # @api private
- #
- # Parse an osdep entry into a gem name and gem version
- #
- # The 'gem' entries in the osdep files can contain a version
- # specification. This method parses the two parts and return them
- #
- # @param [String] entry the osdep entry
- # @return [(String,String),(String,nil)] the gem name, and an
- # optional version specification
- def parse_package_entry(entry)
- if entry =~ /^([^><=~]*)([><=~]+.*)$/
- [$1.strip, $2.strip]
- else
- [entry]
- end
- end
-
class NotCleanState < RuntimeError; end
# @api private
#
# Create backup files matching a certain file mapping
@@ -319,69 +301,70 @@
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 << "--path" << gem_path
options << "--shebang" << Gem.ruby
options << "--binstubs" << binstubs if binstubs
apply_build_config(ws)
connections = Set.new
- run_bundler(ws, 'install', *options,
+ 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 (.*)/
- host = $1.gsub(/\.+$/, '')
+ host = $1.gsub(/\.+$/, "")
unless connections.include?(host)
Autobuild.message " bundler: connected to #{host}"
connections << host
end
end
end
end
def self.bundle_gem_path(ws, gem_name,
- bundler_version: ws.config.bundler_version,
- gem_home: nil, gemfile: nil)
+ bundler_version: ws.config.bundler_version,
+ gem_home: nil, gemfile: nil)
path = String.new
run_bundler(
- ws, 'show', gem_name,
+ ws, "show", gem_name,
bundler_version: bundler_version, gem_home: gem_home,
- gemfile: gemfile) { |line| path << line }
+ gemfile: gemfile
+ ) { |line| path << line }
path.chomp
end
def self.default_bundler(ws)
- File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
+ File.join(ws.dot_autoproj_dir, "bin", "bundle")
end
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_version: ws.config.bundler_version,
+ gem_home: ws.config.gems_gem_home,
+ gemfile: default_gemfile_path(ws))
+ bundle = Autobuild.programs["bundle"] || default_bundler(ws)
Autoproj.bundler_with_unbundled_env do
bundler_version_env =
if bundler_version
- { 'BUNDLER_VERSION' => 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,
+ "GEM_HOME" => gem_home,
+ "GEM_PATH" => nil,
+ "BUNDLE_GEMFILE" => gemfile,
+ "RUBYOPT" => nil,
+ "RUBYLIB" => rubylib_for_bundler,
].merge(bundler_version_env)
- ws.run('autoproj', 'osdeps',
+ ws.run("autoproj", "osdeps",
bundle, *commandline,
working_directory: File.dirname(gemfile),
env: target_env) { |line| yield(line) if block_given? }
end
end
@@ -399,34 +382,34 @@
path.each do |gemfile|
bundler_def =
begin Bundler::Dsl.evaluate(gemfile, nil, [])
rescue Exception => e
cleaned_message = e
- .message
- .gsub(/There was an error parsing([^:]+)/,
- "Error in gem definitions")
- .gsub(/# from.*/, '')
+ .message
+ .gsub(/There was an error parsing([^:]+)/,
+ "Error in gem definitions")
+ .gsub(/# from.*/, "")
raise ConfigError, cleaned_message
end
gems_remotes |= bundler_def.send(:sources).rubygems_remotes.to_set
bundler_def.dependencies.each do |d|
d.groups.each do |group_name|
- if !d.platforms.empty?
+ if d.platforms.empty?
+ dependencies[group_name][""][d.name] = d
+ else
d.platforms.each do |platform_name|
dependencies[group_name][platform_name][d.name] = d
end
- else
- dependencies[group_name][''][d.name] = d
end
end
end
end
contents = []
gems_remotes.each do |g|
g = g.to_s
- g = g[0..-2] if g.end_with?('/')
+ g = g[0..-2] if g.end_with?("/")
contents << "source '#{g}'"
end
valid_keys = %w[group groups git path glob name branch ref tag
require submodules platform platforms type
source install_if]
@@ -443,23 +426,23 @@
options = d.source.options.dup
options.delete_if { |k, _| !valid_keys.include?(k) }
options = options.map { |k, v| "#{k}: \"#{v}\"" }
end
contents << [" #{platform_indent}gem \"#{d.name}\",
- \"#{d.requirement}\"", *options].join(', ')
+ \"#{d.requirement}\"", *options].join(", ")
end
- contents << ' end' unless platform_name.empty?
+ contents << " end" unless platform_name.empty?
end
- contents << 'end'
+ contents << "end"
end
contents.join("\n")
end
def workspace_configuration_gemfiles
gemfiles = []
ws.manifest.each_package_set do |source|
- pkg_set_gemfile = File.join(source.local_dir, 'Gemfile')
+ pkg_set_gemfile = File.join(source.local_dir, "Gemfile")
if source.local_dir && File.file?(pkg_set_gemfile)
gemfiles << pkg_set_gemfile
end
end
# In addition, look into overrides.d
@@ -468,13 +451,64 @@
end
gemfiles
end
def self.default_gemfile_path(ws)
- File.join(ws.prefix_dir, 'gems', 'Gemfile')
+ File.join(ws.prefix_dir, "gems", "Gemfile")
end
+ GemEntry = Struct.new :name, :version, :options do
+ def self.parse(object)
+ if object.respond_to?(:to_str)
+ parse_from_string(object)
+ elsif object.respond_to?(:to_hash)
+ parse_from_hash(object)
+ else
+ raise ArgumentError,
+ "expected #{object} to either be a string or a map"
+ end
+ end
+
+ # Parse an osdep entry string into a gem name and gem version
+ #
+ # The 'gem' entries in the osdep files can contain a version
+ # specification. This method parses the two parts and return them
+ #
+ # @param [String] entry the osdep entry
+ # @return [(String,String),(String,nil)] the gem name, and an
+ # optional version specification
+ def self.parse_from_string(entry)
+ if entry =~ /^([^><=~]*)([><=~]+.*)$/
+ GemEntry.new($1.strip, $2.strip, {})
+ else
+ GemEntry.new(entry, nil, {})
+ end
+ end
+
+ # Parse an option hash into a GemEntry
+ def self.parse_from_hash(hash)
+ hash = hash.dup
+ unless (name = hash.delete("name"))
+ raise ArgumentError,
+ "expected gem entry #{hash} to have at least a 'name' key"
+ end
+
+ version = hash.delete("version")
+ GemEntry.new(name, version, hash)
+ end
+
+ def to_gemfile_line
+ options_s = options.map { |k, v| "#{k}: \"#{v}\"" }.join(", ")
+ entries = [
+ "\"#{name}\"",
+ ("\"#{version}\"" if version),
+ (options_s unless options_s.empty?)
+ ].compact
+ "gem #{entries.join(', ')}"
+ end
+ end
+
def install(gems, filter_uptodate_packages: false, install_only: false)
gemfile_path = self.class.default_gemfile_path(ws)
root_dir = File.dirname(gemfile_path)
gemfile_lock_path = "#{gemfile_path}.lock"
backups = Hash[
@@ -485,25 +519,25 @@
# Back up the existing gemfile, we'll restore it if something is
# wrong to avoid leaving bundler in an inconsistent state
backup_files(backups)
unless File.file?("#{gemfile_path}.orig")
Ops.atomic_write("#{gemfile_path}.orig") do |io|
- dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, 'Gemfile')
+ dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, "Gemfile")
io.puts "eval_gemfile \"#{dot_autoproj_gemfile}\""
end
end
gemfiles = workspace_configuration_gemfiles
- gemfiles << File.join(ws.dot_autoproj_dir, 'Gemfile')
+ gemfiles << File.join(ws.dot_autoproj_dir, "Gemfile")
# Save the osdeps entries in a temporary gemfile and finally
# merge the whole lot of it
- gemfile_contents = Tempfile.open 'autoproj-gemfile' do |io|
- gems.sort.each do |name|
- name, version = parse_package_entry(name)
- io.puts "gem \"#{name}\", \"#{version || '>= 0'}\""
- end
+ gemfile_contents = Tempfile.open "autoproj-gemfile" do |io|
+ gems.map { |entry| GemEntry.parse(entry) }
+ .sort_by(&:name)
+ .each { |entry| io.puts entry.to_gemfile_line }
+
io.flush
gemfiles.unshift io.path
# The autoproj gemfile needs to be last, we really don't
# want to mess it up
merge_gemfiles(*gemfiles)
@@ -518,11 +552,11 @@
io.puts gemfile_contents
end
end
options = Array.new
- binstubs_path = File.join(root_dir, 'bin')
+ binstubs_path = File.join(root_dir, "bin")
if updated || !install_only || !File.file?("#{gemfile_path}.lock")
self.class.run_bundler_install(ws, gemfile_path, *options,
binstubs: binstubs_path)
end
@@ -538,24 +572,25 @@
FileUtils.cp gemfile_path, "#{gemfile_path}.FAILED"
backup_restore(backups)
raise
ensure
if binstubs_path
- FileUtils.rm_f File.join(binstubs_path, 'bundle')
- FileUtils.rm_f File.join(binstubs_path, 'bundler')
+ FileUtils.rm_f File.join(binstubs_path, "bundle")
+ FileUtils.rm_f File.join(binstubs_path, "bundler")
end
backup_clean(backups)
end
def discover_rubylib
- require 'bundler'
- Tempfile.open 'autoproj-rubylib' do |io|
+ require "bundler"
+ Tempfile.open "autoproj-rubylib" do |io|
result = Autoproj.bundler_unbundled_system(
- Hash['RUBYLIB' => nil],
- Autobuild.tool('ruby'), '-e', 'puts $LOAD_PATH',
+ Hash["RUBYLIB" => nil],
+ Autobuild.tool("ruby"), "-e", "puts $LOAD_PATH",
out: io,
- err: '/dev/null')
+ err: "/dev/null"
+ )
if result
io.rewind
io.readlines.map(&:chomp).find_all { |l| !l.empty? }
end
end
@@ -565,22 +600,23 @@
rx = Regexp.new("/gems/#{Regexp.quote("bundler-#{Bundler::VERSION}")}/")
$LOAD_PATH.grep(rx).join(File::PATH_SEPARATOR)
end
def discover_bundle_rubylib(silent_errors: false)
- require 'bundler'
- gemfile = File.join(ws.prefix_dir, 'gems', 'Gemfile')
+ require "bundler"
+ 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|
+ Tempfile.open "autoproj-rubylib" do |io|
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',
- out: io, **silent_redirect)
+ 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",
+ out: io, **silent_redirect
+ )
if result
io.rewind
io.readlines.map(&:chomp).find_all { |l| !l.empty? }
end