autoproj 2.10.1 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -8
  3. data/.travis.yml +5 -3
  4. data/autoproj.gemspec +7 -6
  5. data/bin/alog +1 -0
  6. data/bin/autoproj +1 -1
  7. data/bin/autoproj_bootstrap +149 -86
  8. data/bin/autoproj_bootstrap.in +9 -7
  9. data/bin/autoproj_install +148 -82
  10. data/bin/autoproj_install.in +8 -3
  11. data/lib/autoproj.rb +3 -0
  12. data/lib/autoproj/aruba_minitest.rb +15 -0
  13. data/lib/autoproj/autobuild_extensions/dsl.rb +61 -27
  14. data/lib/autoproj/base.rb +35 -6
  15. data/lib/autoproj/cli/base.rb +1 -1
  16. data/lib/autoproj/cli/build.rb +9 -3
  17. data/lib/autoproj/cli/cache.rb +79 -7
  18. data/lib/autoproj/cli/doc.rb +4 -18
  19. data/lib/autoproj/cli/inspection_tool.rb +5 -6
  20. data/lib/autoproj/cli/main.rb +41 -18
  21. data/lib/autoproj/cli/main_doc.rb +86 -0
  22. data/lib/autoproj/cli/main_plugin.rb +3 -0
  23. data/lib/autoproj/cli/main_test.rb +15 -0
  24. data/lib/autoproj/cli/show.rb +12 -18
  25. data/lib/autoproj/cli/status.rb +15 -9
  26. data/lib/autoproj/cli/test.rb +13 -84
  27. data/lib/autoproj/cli/update.rb +77 -19
  28. data/lib/autoproj/cli/utility.rb +136 -0
  29. data/lib/autoproj/configuration.rb +28 -4
  30. data/lib/autoproj/default.osdeps +18 -0
  31. data/lib/autoproj/installation_manifest.rb +7 -5
  32. data/lib/autoproj/manifest.rb +15 -21
  33. data/lib/autoproj/ops/build.rb +23 -27
  34. data/lib/autoproj/ops/cache.rb +151 -33
  35. data/lib/autoproj/ops/cached_env.rb +2 -2
  36. data/lib/autoproj/ops/import.rb +146 -80
  37. data/lib/autoproj/ops/install.rb +140 -79
  38. data/lib/autoproj/ops/phase_reporting.rb +49 -0
  39. data/lib/autoproj/ops/snapshot.rb +2 -1
  40. data/lib/autoproj/ops/tools.rb +2 -2
  41. data/lib/autoproj/os_package_installer.rb +19 -11
  42. data/lib/autoproj/package_definition.rb +29 -10
  43. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +49 -28
  44. data/lib/autoproj/package_managers/bundler_manager.rb +257 -87
  45. data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
  46. data/lib/autoproj/package_managers/shell_script_manager.rb +44 -24
  47. data/lib/autoproj/package_manifest.rb +49 -34
  48. data/lib/autoproj/package_set.rb +48 -29
  49. data/lib/autoproj/repository_managers/apt.rb +0 -1
  50. data/lib/autoproj/test.rb +29 -10
  51. data/lib/autoproj/variable_expansion.rb +3 -1
  52. data/lib/autoproj/vcs_definition.rb +30 -15
  53. data/lib/autoproj/version.rb +1 -1
  54. data/lib/autoproj/workspace.rb +55 -13
  55. metadata +32 -28
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'autoproj/package_managers/manager'
2
4
  require 'autoproj/package_managers/unknown_os_manager'
3
5
  require 'autoproj/package_managers/shell_script_manager'
@@ -37,7 +39,9 @@ class OSPackageInstaller
37
39
  attr_reader :installed_resolved_packages
38
40
 
39
41
  attr_writer :silent
40
- def silent?; @silent end
42
+ def silent?
43
+ @silent
44
+ end
41
45
 
42
46
  class << self
43
47
  attr_accessor :force_osdeps
@@ -60,12 +64,12 @@ def initialize(ws, os_package_resolver, package_managers: PACKAGE_MANAGERS)
60
64
 
61
65
  # Returns the package manager object for the current OS
62
66
  def os_package_manager
63
- if !@os_package_manager
67
+ unless @os_package_manager
64
68
  name = os_package_resolver.os_package_manager
65
69
  @os_package_manager = package_managers[name] ||
66
- PackageManagers::UnknownOSManager.new(ws)
70
+ PackageManagers::UnknownOSManager.new(ws)
67
71
  end
68
- return @os_package_manager
72
+ @os_package_manager
69
73
  end
70
74
 
71
75
  # Returns the set of package managers
@@ -75,13 +79,17 @@ def each_manager(&block)
75
79
  package_managers.each_value(&block)
76
80
  end
77
81
 
82
+ def each_manager_with_name(&block)
83
+ package_managers.each(&block)
84
+ end
85
+
78
86
  HANDLE_ALL = 'all'
79
87
  HANDLE_RUBY = 'ruby'
80
88
  HANDLE_OS = 'os'
81
89
  HANDLE_NONE = 'none'
82
90
 
83
91
  def osdeps_mode_option_unsupported_os(config)
84
- long_doc =<<-EOT
92
+ long_doc = <<-EOT
85
93
  The software packages that autoproj will have to build may require other
86
94
  prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
87
95
  packages, packages from your operating system/distribution, ...). Autoproj is
@@ -153,9 +161,9 @@ def osdeps_mode_option_supported_os(config)
153
161
  message = [ "Which prepackaged software (a.k.a. 'osdeps') should autoproj install automatically (all, none or a comma-separated list of: os gem pip) ?", long_doc.strip ]
154
162
 
155
163
  config.declare 'osdeps_mode', 'string',
156
- default: 'all',
157
- doc: message,
158
- lowercase: true
164
+ default: 'all',
165
+ doc: message,
166
+ lowercase: true
159
167
  end
160
168
 
161
169
  def define_osdeps_mode_option
@@ -171,14 +179,14 @@ def osdeps_mode_string_to_value(string)
171
179
  modes = []
172
180
  user_modes.each do |str|
173
181
  case str
174
- when 'all' then modes.concat(['os', 'gem', 'pip'])
182
+ when 'all' then modes.concat(%w[os gem pip])
175
183
  when 'ruby' then modes << 'gem'
176
184
  when 'gem' then modes << 'gem'
177
185
  when 'pip' then modes << 'pip'
178
186
  when 'os' then modes << 'os'
179
187
  when 'none' then
180
188
  else
181
- if package_managers.has_key?(str)
189
+ if package_managers.key?(str)
182
190
  modes << str
183
191
  else
184
192
  raise ArgumentError, "#{str} is not a known package handler, known handlers are #{package_managers.keys.sort.join(", ")}"
@@ -442,5 +450,5 @@ def install_manager_packages(manager, package_list, install_only: false, run_pac
442
450
  end
443
451
  end
444
452
  end
445
- end
453
+ end
446
454
 
@@ -19,7 +19,9 @@ class PackageDefinition
19
19
  #
20
20
  # If the package is set up, its importer as well as all target
21
21
  # directories are properly set, and all {user_blocks} have been called.
22
- def setup?; !!@setup end
22
+ def setup?
23
+ @setup
24
+ end
23
25
 
24
26
  # Sets the {setup?} flag
25
27
  attr_writer :setup
@@ -29,10 +31,11 @@ def setup?; !!@setup end
29
31
  attr_accessor :vcs
30
32
 
31
33
  def initialize(autobuild, package_set, file)
32
- @autobuild, @package_set, @file =
33
- autobuild, package_set, file
34
+ @autobuild = autobuild
35
+ @package_set = package_set
36
+ @file = file
34
37
  @user_blocks = []
35
- @modes = ['import', 'build']
38
+ @modes = %w[import build]
36
39
  @setup = false
37
40
  @vcs = VCSDefinition.none
38
41
  end
@@ -44,9 +47,8 @@ def initialize(autobuild, package_set, file)
44
47
  #
45
48
  # @return [Array<String>]
46
49
  def modes
47
- @modes + autobuild.utilities.values.
48
- find_all { |u| u.enabled? }.
49
- map(&:name)
50
+ @modes + autobuild.utilities
51
+ .values.find_all(&:enabled?).map(&:name)
50
52
  end
51
53
 
52
54
  # The package name
@@ -65,9 +67,7 @@ def name
65
67
  # @see {user_blocks}
66
68
  def add_setup_block(block)
67
69
  user_blocks << block
68
- if setup?
69
- block.call(autobuild)
70
- end
70
+ block.call(autobuild) if setup?
71
71
  end
72
72
 
73
73
  # Whether this package is already checked out
@@ -79,5 +79,24 @@ def checked_out?
79
79
  def depends_on(pkg)
80
80
  autobuild.depends_on(pkg.autobuild)
81
81
  end
82
+
83
+ def apply_dependencies_from_manifest
84
+ manifest = autobuild.description
85
+ manifest.each_dependency(modes) do |name, is_optional|
86
+ begin
87
+ if is_optional
88
+ autobuild.optional_dependency name
89
+ else
90
+ autobuild.depends_on name
91
+ end
92
+ rescue ConfigError => e
93
+ raise PackageNotFound.new(manifest.path),
94
+ "manifest #{manifest.path} of #{self.name} from "\
95
+ "#{package_set.name} lists '#{name}' as dependency, "\
96
+ 'but it is neither a normal package nor an osdeps '\
97
+ "package. osdeps reports: #{e.message}", e.backtrace
98
+ end
99
+ end
100
+ end
82
101
  end
83
102
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'autoproj/package_managers/debian_version'
2
4
 
3
5
  module Autoproj
@@ -12,15 +14,17 @@ def initialize(ws, status_file = "/var/lib/dpkg/status")
12
14
  @installed_packages = nil
13
15
  @installed_versions = nil
14
16
  super(ws, true,
15
- %w{apt-get install},
16
- %w{DEBIAN_FRONTEND=noninteractive apt-get install -y})
17
+ %w[apt-get install],
18
+ %w[DEBIAN_FRONTEND=noninteractive apt-get install -y])
17
19
  end
18
20
 
19
21
  def configure_manager
20
22
  super
21
- ws.config.declare 'apt_dpkg_update', 'boolean',
23
+ ws.config.declare(
24
+ 'apt_dpkg_update', 'boolean',
22
25
  default: 'yes',
23
26
  doc: ['Would you like autoproj to keep apt packages up-to-date?']
27
+ )
24
28
  keep_uptodate?
25
29
  end
26
30
 
@@ -32,7 +36,9 @@ def keep_uptodate=(flag)
32
36
  ws.config.set('apt_dpkg_update', flag, true)
33
37
  end
34
38
 
35
- def self.parse_package_status(installed_packages, installed_versions, paragraph)
39
+ def self.parse_package_status(
40
+ installed_packages, installed_versions, paragraph, virtual: true
41
+ )
36
42
  if paragraph =~ /^Status: install ok installed$/
37
43
  if paragraph =~ /^Package: (.*)$/
38
44
  package_name = $1
@@ -41,38 +47,40 @@ def self.parse_package_status(installed_packages, installed_versions, paragraph)
41
47
  installed_versions[package_name] = DebianVersion.new($1)
42
48
  end
43
49
  end
44
- if paragraph =~ /^Provides: (.*)$/
50
+ if virtual && paragraph =~ /^Provides: (.*)$/
45
51
  installed_packages.merge($1.split(',').map(&:strip))
46
52
  end
47
53
  end
48
54
  end
49
55
 
50
- def self.parse_dpkg_status(status_file)
56
+ def self.parse_dpkg_status(status_file, virtual: true)
51
57
  installed_packages = Set.new
52
58
  installed_versions = {}
53
59
  dpkg_status = File.read(status_file)
54
60
  dpkg_status << "\n"
55
61
 
56
62
  dpkg_status = StringScanner.new(dpkg_status)
57
- if !dpkg_status.scan(/Package: /)
58
- raise ArgumentError, "expected #{status_file} to have Package: lines but found none"
63
+ unless dpkg_status.scan(/Package: /)
64
+ raise ArgumentError, "expected #{status_file} to have Package: "\
65
+ "lines but found none"
59
66
  end
60
67
 
61
- while paragraph_end = dpkg_status.scan_until(/Package: /)
68
+ while (paragraph_end = dpkg_status.scan_until(/Package: /))
62
69
  paragraph = "Package: #{paragraph_end[0..-10]}"
63
- parse_package_status(installed_packages, installed_versions, paragraph)
70
+ parse_package_status(installed_packages, installed_versions,
71
+ paragraph, virtual: virtual)
64
72
  end
65
- parse_package_status(installed_packages, installed_versions, "Package: #{dpkg_status.rest}")
73
+ parse_package_status(installed_packages, installed_versions,
74
+ "Package: #{dpkg_status.rest}", virtual: virtual)
66
75
  [installed_packages, installed_versions]
67
76
  end
68
77
 
69
78
  def self.parse_apt_cache_paragraph(paragraph)
70
79
  version = '0'
71
- if paragraph =~ /^Package: (.*)$/
72
- package_name = $1
73
- if paragraph =~ /^Version: (.*)$/
74
- version = $1
75
- end
80
+ if (paragraph_m = /^Package: (.*)$/.match(paragraph))
81
+ package_name = paragraph_m[1]
82
+ version_m = /^Version: (.*)$/.match(paragraph)
83
+ version = version_m[1] if version_m
76
84
  end
77
85
  [package_name, version]
78
86
  end
@@ -81,23 +89,24 @@ def self.parse_packages_versions(packages)
81
89
  packages_versions = {}
82
90
  apt_cache_show = `apt-cache show --no-all-versions #{packages.join(' ')}`
83
91
  apt_cache_show = StringScanner.new(apt_cache_show)
84
- if !apt_cache_show.scan(/Package: /)
85
- return packages_versions
86
- end
92
+ return packages_versions unless apt_cache_show.scan(/Package: /)
87
93
 
88
- while paragraph_end = apt_cache_show.scan_until(/Package: /)
94
+ while (paragraph_end = apt_cache_show.scan_until(/Package: /))
89
95
  paragraph = "Package: #{paragraph_end[0..-10]}"
90
96
  package_name, version = parse_apt_cache_paragraph(paragraph)
91
97
  packages_versions[package_name] = DebianVersion.new(version)
92
98
  end
93
- package_name, version = parse_apt_cache_paragraph("Package: #{apt_cache_show.rest}")
99
+ package_name, version = parse_apt_cache_paragraph(
100
+ "Package: #{apt_cache_show.rest}"
101
+ )
94
102
  packages_versions[package_name] = DebianVersion.new(version)
95
103
  packages_versions
96
104
  end
97
105
 
98
106
  def updated?(package, available_version)
99
- # Consider up-to-date if the package is provided by another package (purely virtual)
100
- # Ideally, we should check the version of the package that provides it
107
+ # Consider up-to-date if the package is provided by another
108
+ # package (purely virtual) Ideally, we should check the version
109
+ # of the package that provides it
101
110
  return true unless available_version && @installed_versions[package]
102
111
 
103
112
  (available_version <= @installed_versions[package])
@@ -105,8 +114,13 @@ def updated?(package, available_version)
105
114
 
106
115
  # On a dpkg-enabled system, checks if the provided package is installed
107
116
  # and returns true if it is the case
108
- def installed?(package_name, filter_uptodate_packages: false, install_only: false)
109
- @installed_packages, @installed_versions = self.class.parse_dpkg_status(status_file) unless @installed_packages && @installed_versions
117
+ def installed?(package_name, filter_uptodate_packages: false,
118
+ install_only: false)
119
+ unless @installed_packages && @installed_versions
120
+ @installed_packages, @installed_versions =
121
+ self.class.parse_dpkg_status(status_file)
122
+ end
123
+
110
124
  if package_name =~ /^(\w[a-z0-9+-.]+)/
111
125
  @installed_packages.include?($1)
112
126
  else
@@ -116,11 +130,18 @@ def installed?(package_name, filter_uptodate_packages: false, install_only: fals
116
130
  end
117
131
 
118
132
  def install(packages, filter_uptodate_packages: false, install_only: false)
119
- packages_versions = self.class.parse_packages_versions(packages)
120
133
  if filter_uptodate_packages || install_only
121
- packages = packages.find_all do |package_name|
122
- !installed?(package_name) || (keep_uptodate? && !updated?(package_name, packages_versions[package_name]))
134
+ already_installed, missing = packages.partition do |package_name|
135
+ installed?(package_name)
136
+ end
137
+
138
+ if keep_uptodate? && !install_only
139
+ packages_versions = self.class.parse_packages_versions(already_installed)
140
+ need_update = already_installed.find_all do |package_name|
141
+ !updated?(package_name, packages_versions[package_name])
142
+ end
123
143
  end
144
+ packages = missing + (need_update || [])
124
145
  end
125
146
 
126
147
  if super(packages)
@@ -24,6 +24,14 @@ def self.with_prerelease(*value)
24
24
  end
25
25
  end
26
26
 
27
+ # Directory with cached .gem packages
28
+ #
29
+ # The directory must exist, but may be empty.
30
+ # It is initialized with {BundlerManager.cache_dir}
31
+ #
32
+ # @return [String]
33
+ attr_accessor :cache_dir
34
+
27
35
  # (see Manager#call_while_empty?)
28
36
  def call_while_empty?
29
37
  !workspace_configuration_gemfiles.empty?
@@ -45,14 +53,31 @@ def initialize_environment
45
53
  env.add_path 'PATH', File.join(ws.dot_autoproj_dir, 'bin')
46
54
  env.set 'GEM_HOME', config.gems_gem_home
47
55
  env.clear 'GEM_PATH'
56
+ if (bundler_version = config.bundler_version)
57
+ env.set 'BUNDLER_VERSION', bundler_version
58
+ else
59
+ env.clear 'BUNDLER_VERSION'
60
+ end
48
61
 
49
62
  gemfile_path = File.join(ws.prefix_dir, 'gems', 'Gemfile')
50
- if File.file?(gemfile_path)
51
- env.set('BUNDLE_GEMFILE', gemfile_path)
63
+ env.set('BUNDLE_GEMFILE', gemfile_path) if File.file?(gemfile_path)
64
+
65
+ if cache_dir && File.exist?(cache_dir)
66
+ vendor_dir = File.join(File.dirname(gemfile_path), 'vendor')
67
+ FileUtils.mkdir_p vendor_dir
68
+ bundler_cache_dir = File.join(vendor_dir, 'cache')
69
+ if File.writable?(cache_dir)
70
+ create_cache_symlink(cache_dir, bundler_cache_dir)
71
+ else
72
+ Autoproj.warn "BundlerManager: #{cache_dir} is read-only "\
73
+ "copying the cache instead of symlinking it"
74
+ create_cache_copy(cache_dir, bundler_cache_dir)
75
+ end
52
76
  end
53
77
 
54
- Autobuild.programs['bundler'] = File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
55
- Autobuild.programs['bundle'] = File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
78
+ Autobuild.programs['bundler'] =
79
+ Autobuild.programs['bundle'] =
80
+ File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
56
81
 
57
82
  env.init_from_env 'RUBYLIB'
58
83
  env.inherit 'RUBYLIB'
@@ -61,17 +86,20 @@ def initialize_environment
61
86
  original_rubylib =
62
87
  (env['RUBYLIB'] || "").split(File::PATH_SEPARATOR).find_all do |p|
63
88
  !p.start_with?(Bundler.rubygems.gem_dir) &&
64
- !Bundler.rubygems.gem_path.any? { |gem_p| p.start_with?(p) }
89
+ Bundler.rubygems.gem_path
90
+ .none? { |gem_p| p.start_with?(gem_p) }
65
91
  end
92
+
66
93
  # And discover the system's rubylib
67
- if system_rubylib = discover_rubylib
94
+ if (system_rubylib = discover_rubylib)
68
95
  # Do not explicitely add the system rubylib to the
69
96
  # environment, the interpreter will do it for us.
70
97
  #
71
98
  # This allows to use a binstub generated for one of ruby
72
99
  # interpreter version on our workspace
73
100
  env.system_env['RUBYLIB'] = []
74
- env.original_env['RUBYLIB'] = (original_rubylib - system_rubylib).join(File::PATH_SEPARATOR)
101
+ env.original_env['RUBYLIB'] = (original_rubylib - system_rubylib)
102
+ .join(File::PATH_SEPARATOR)
75
103
  end
76
104
 
77
105
  ws.config.each_reused_autoproj_installation do |p|
@@ -82,17 +110,135 @@ def initialize_environment
82
110
  prefix_gems = File.join(ws.prefix_dir, "gems")
83
111
  FileUtils.mkdir_p prefix_gems
84
112
  gemfile = File.join(prefix_gems, 'Gemfile')
85
- if !File.exist?(gemfile)
113
+ unless File.exist?(gemfile)
86
114
  Ops.atomic_write(gemfile) do |io|
87
- io.puts "eval_gemfile \"#{File.join(ws.dot_autoproj_dir, 'Gemfile')}\""
115
+ dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, 'Gemfile')
116
+ io.puts "eval_gemfile \"#{dot_autoproj_gemfile}\""
88
117
  end
89
118
  end
90
119
 
91
- if bundle_rubylib = discover_bundle_rubylib(silent_errors: true)
120
+ if (bundle_rubylib = discover_bundle_rubylib(silent_errors: true))
92
121
  update_env_rubylib(bundle_rubylib, system_rubylib)
93
122
  end
94
123
  end
95
124
 
125
+ def create_cache_symlink(cache_dir, bundler_cache_dir)
126
+ valid = !File.exist?(bundler_cache_dir) ||
127
+ File.symlink?(bundler_cache_dir)
128
+
129
+ unless valid
130
+ Autoproj.warn "cannot use #{cache_dir} as gem cache as "\
131
+ "#{bundler_cache_dir} already exists"
132
+ return
133
+ end
134
+
135
+ FileUtils.rm_f bundler_cache_dir
136
+ FileUtils.ln_s cache_dir, bundler_cache_dir
137
+ end
138
+
139
+ def create_cache_copy(cache_dir, bundler_cache_dir)
140
+ valid = !File.exist?(bundler_cache_dir) ||
141
+ File.directory?(bundler_cache_dir) ||
142
+ File.symlink?(bundler_cache_dir)
143
+
144
+ unless valid
145
+ Autoproj.warn "cannot use #{cache_dir} as gem cache as "\
146
+ "#{bundler_cache_dir} already exists"
147
+ return
148
+ end
149
+
150
+ # Gracefully upgrade from the symlinks
151
+ FileUtils.rm_f bundler_cache_dir if File.symlink?(bundler_cache_dir)
152
+ FileUtils.mkdir_p bundler_cache_dir
153
+
154
+ Dir.glob(File.join(cache_dir, '*.gem')) do |path_src|
155
+ path_dest = File.join(bundler_cache_dir, File.basename(path_src))
156
+ next if File.exist?(path_dest)
157
+
158
+ FileUtils.cp path_src, path_dest
159
+ end
160
+ end
161
+
162
+ # Enumerate the per-gem build configurations
163
+ def self.per_gem_build_config(ws)
164
+ ws.config.get('bundler.build', {})
165
+ end
166
+
167
+ # Add new build configuration arguments for a given gem
168
+ #
169
+ # This is meant to be used from the Autoproj configuration files,
170
+ # e.g. overrides.rb or package configuration
171
+ def self.add_build_configuration_for(gem_name, build_config, ws: Autoproj.workspace)
172
+ c = ws.config.get('bundler.build', {})
173
+ c[gem_name] = [c[gem_name], build_config].compact.join(" ")
174
+ ws.config.set('bundler.build', c)
175
+ end
176
+
177
+ # Set the build configuration for the given gem
178
+ #
179
+ # This is meant to be used from the Autoproj configuration files,
180
+ # e.g. overrides.rb or package configuration
181
+ def self.configure_build_for(gem_name, build_config, ws: Autoproj.workspace)
182
+ c = ws.config.get('bundler.build', {})
183
+ c[gem_name] = build_config
184
+ ws.config.set('bundler.build', c)
185
+ end
186
+
187
+ # Removes build configuration flags for the given gem
188
+ #
189
+ # This is meant to be used from the Autoproj configuration files,
190
+ # e.g. overrides.rb or package configuration
191
+ def self.remove_build_configuration_for(gem_name, ws: Autoproj.workspace)
192
+ c = ws.config.get('bundler.build', {})
193
+ c.delete(gem_name)
194
+ ws.config.set('bundler.build', c)
195
+ end
196
+
197
+ # @api private
198
+ #
199
+ # Apply configured per-gem build configuration options
200
+ #
201
+ # @param [Workspace] ws the workspace whose bundler configuration
202
+ # should be updated
203
+ # @return [void]
204
+ def self.apply_build_config(ws)
205
+ root_dir = File.join(ws.prefix_dir, 'gems')
206
+ current_config_path = File.join(root_dir, ".bundle", "config")
207
+ current_config =
208
+ if File.file?(current_config_path)
209
+ File.readlines(current_config_path)
210
+ else
211
+ []
212
+ end
213
+
214
+ build_config = {}
215
+ per_gem_build_config(ws).each do |name, conf|
216
+ build_config[name.upcase] = conf
217
+ end
218
+
219
+ new_config = current_config.map do |line|
220
+ next(line) unless (m = line.match(/BUNDLE_BUILD__(.*): "(.*)"$/))
221
+ next unless (desired_config = build_config.delete(m[1]))
222
+
223
+ if m[2] != desired_config
224
+ "BUNDLE_BUILD__#{m[1]}: \"#{desired_config}\""
225
+ else
226
+ line
227
+ end
228
+ end.compact
229
+
230
+ build_config.each do |name, config|
231
+ new_config << "BUNDLE_BUILD__#{name}: \"#{config}\""
232
+ end
233
+
234
+ if new_config != current_config
235
+ FileUtils.mkdir_p File.dirname(current_config_path)
236
+ File.open(current_config_path, 'w') do |io|
237
+ io.write new_config.join
238
+ end
239
+ end
240
+ end
241
+
96
242
  # @api private
97
243
  #
98
244
  # Update RUBYLIB to add the gems that are part of the bundler
@@ -103,7 +249,8 @@ def initialize_environment
103
249
  # @param [Array<String>] system_rubylib the rubylib entries that are
104
250
  # set by the underlying ruby interpreter itself
105
251
  def update_env_rubylib(bundle_rubylib, system_rubylib = discover_rubylib)
106
- current = (ws.env.resolved_env['RUBYLIB'] || '').split(File::PATH_SEPARATOR) + system_rubylib
252
+ current = (ws.env.resolved_env['RUBYLIB'] || '')
253
+ .split(File::PATH_SEPARATOR) + system_rubylib
107
254
  (bundle_rubylib - current).each do |p|
108
255
  ws.env.add_path('RUBYLIB', p)
109
256
  end
@@ -128,7 +275,7 @@ def parse_package_entry(entry)
128
275
  end
129
276
 
130
277
  class NotCleanState < RuntimeError; end
131
-
278
+
132
279
  # @api private
133
280
  #
134
281
  # Create backup files matching a certain file mapping
@@ -138,9 +285,7 @@ class NotCleanState < RuntimeError; end
138
285
  # file might not exist.
139
286
  def backup_files(mapping)
140
287
  mapping.each do |file, backup_file|
141
- if File.file?(file)
142
- FileUtils.cp file, backup_file
143
- end
288
+ FileUtils.cp file, backup_file if File.file?(file)
144
289
  end
145
290
  end
146
291
 
@@ -151,9 +296,7 @@ def backup_files(mapping)
151
296
  # @param (see #backup_file)
152
297
  def backup_restore(mapping)
153
298
  mapping.each do |file, backup_file|
154
- if File.file?(backup_file)
155
- FileUtils.cp backup_file, file
156
- end
299
+ FileUtils.cp backup_file, file if File.file?(backup_file)
157
300
  end
158
301
  end
159
302
 
@@ -163,34 +306,36 @@ def backup_restore(mapping)
163
306
  #
164
307
  # @param (see #backup_file)
165
308
  def backup_clean(mapping)
166
- mapping.each do |file, backup_file|
167
- if File.file?(backup_file)
168
- FileUtils.rm backup_file
169
- end
309
+ mapping.each do |_file, backup_file|
310
+ FileUtils.rm backup_file if File.file?(backup_file)
170
311
  end
171
312
  end
172
313
 
173
- def self.run_bundler_install(ws, gemfile, *options, update: true, binstubs: nil,
174
- gem_home: ws.config.gems_gem_home,
175
- gem_path: ws.config.gems_install_path)
176
- if update && File.file?("#{gemfile}.lock")
177
- FileUtils.rm "#{gemfile}.lock"
178
- end
314
+ def self.run_bundler_install(
315
+ ws, gemfile, *options,
316
+ update: true, binstubs: nil,
317
+ bundler_version: ws.config.bundler_version,
318
+ gem_home: ws.config.gems_gem_home,
319
+ gem_path: ws.config.gems_install_path
320
+ )
321
+ FileUtils.rm "#{gemfile}.lock" if update && File.file?("#{gemfile}.lock")
179
322
 
180
323
  options << '--path' << gem_path
181
324
  options << "--shebang" << Gem.ruby
182
- if binstubs
183
- options << "--binstubs" << binstubs
184
- end
325
+ options << "--binstubs" << binstubs if binstubs
326
+
327
+ apply_build_config(ws)
185
328
 
186
329
  connections = Set.new
187
- run_bundler(ws, 'install', *options, gem_home: gem_home, gemfile: gemfile) do |line|
330
+ run_bundler(ws, 'install', *options,
331
+ bundler_version: bundler_version,
332
+ gem_home: gem_home, gemfile: gemfile) do |line|
188
333
  case line
189
334
  when /Installing (.*)/
190
335
  Autobuild.message " bundler: installing #{$1}"
191
336
  when /Fetching.*from (.*)/
192
337
  host = $1.gsub(/\.+$/, '')
193
- if !connections.include?(host)
338
+ unless connections.include?(host)
194
339
  Autobuild.message " bundler: connected to #{host}"
195
340
  connections << host
196
341
  end
@@ -198,32 +343,49 @@ def self.run_bundler_install(ws, gemfile, *options, update: true, binstubs: nil,
198
343
  end
199
344
  end
200
345
 
201
- def self.bundle_gem_path(ws, gem_name, gem_home: nil, gemfile: nil)
346
+ def self.bundle_gem_path(ws, gem_name,
347
+ bundler_version: ws.config.bundler_version,
348
+ gem_home: nil, gemfile: nil)
202
349
  path = String.new
203
- PackageManagers::BundlerManager.run_bundler(ws, 'show', gem_name, gem_home: gem_home, gemfile: gemfile) do |line|
204
- path << line
205
- end
350
+ run_bundler(
351
+ ws, 'show', gem_name,
352
+ bundler_version: bundler_version, gem_home: gem_home,
353
+ gemfile: gemfile) { |line| path << line }
206
354
  path.chomp
207
355
  end
208
356
 
209
- def self.run_bundler(ws, *commandline, gem_home: nil, gemfile: nil)
210
- Bundler.with_clean_env do
357
+ def self.default_bundler(ws)
358
+ File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
359
+ end
360
+
361
+ def self.run_bundler(ws, *commandline,
362
+ bundler_version: ws.config.bundler_version,
363
+ gem_home: ws.config.gems_gem_home,
364
+ gemfile: default_gemfile_path(ws))
365
+ bundle = Autobuild.programs['bundle'] || default_bundler(ws)
366
+
367
+ Autoproj.bundler_with_unbundled_env do
368
+ bundler_version_env =
369
+ if bundler_version
370
+ { 'BUNDLER_VERSION' => bundler_version }
371
+ else
372
+ {}
373
+ end
211
374
  target_env = Hash[
212
375
  'GEM_HOME' => gem_home,
213
376
  'GEM_PATH' => nil,
214
377
  'BUNDLE_GEMFILE' => gemfile,
215
378
  'RUBYOPT' => nil,
216
- 'RUBYLIB' => rubylib_for_bundler
217
- ]
218
- ws.run 'autoproj', 'osdeps',
219
- Autobuild.tool('bundle'), *commandline,
220
- working_directory: File.dirname(gemfile), env: target_env do |line|
221
- yield(line) if block_given?
222
- end
379
+ 'RUBYLIB' => rubylib_for_bundler,
380
+ ].merge(bundler_version_env)
381
+ ws.run('autoproj', 'osdeps',
382
+ bundle, *commandline,
383
+ working_directory: File.dirname(gemfile),
384
+ env: target_env) { |line| yield(line) if block_given? }
223
385
  end
224
386
  end
225
387
 
226
- # Parse the contents of a gemfile into a set of
388
+ # Parse the contents of a gemfile into a set of
227
389
  def merge_gemfiles(*path, unlock: [])
228
390
  gems_remotes = Set.new
229
391
  dependencies = Hash.new do |h, k|
@@ -237,9 +399,11 @@ def merge_gemfiles(*path, unlock: [])
237
399
  bundler_def =
238
400
  begin Bundler::Dsl.evaluate(gemfile, nil, [])
239
401
  rescue Exception => e
240
- cleaned_message = e.message.
241
- gsub(/There was an error parsing([^:]+)/, "Error in gem definitions").
242
- gsub(/# from.*/, '')
402
+ cleaned_message = e
403
+ .message
404
+ .gsub(/There was an error parsing([^:]+)/,
405
+ "Error in gem definitions")
406
+ .gsub(/# from.*/, '')
243
407
  raise ConfigError, cleaned_message
244
408
  end
245
409
  gems_remotes |= bundler_def.send(:sources).rubygems_remotes.to_set
@@ -259,17 +423,17 @@ def merge_gemfiles(*path, unlock: [])
259
423
  contents = []
260
424
  gems_remotes.each do |g|
261
425
  g = g.to_s
262
- if g.end_with?('/')
263
- g = g[0..-2]
264
- end
265
- contents << "source '#{g.to_s}'"
426
+ g = g[0..-2] if g.end_with?('/')
427
+ contents << "source '#{g}'"
266
428
  end
267
- valid_keys = %w{group groups git path glob name branch ref tag require submodules platform platforms type source install_if}
429
+ valid_keys = %w[group groups git path glob name branch ref tag
430
+ require submodules platform platforms type
431
+ source install_if]
268
432
  dependencies.each do |group_name, by_platform|
269
433
  contents << "group :#{group_name} do"
270
434
  by_platform.each do |platform_name, deps|
271
435
  deps = deps.values.sort_by(&:name)
272
- if !platform_name.empty?
436
+ unless platform_name.empty?
273
437
  contents << " platform :#{platform_name} do"
274
438
  platform_indent = " "
275
439
  end
@@ -279,13 +443,12 @@ def merge_gemfiles(*path, unlock: [])
279
443
  options.delete_if { |k, _| !valid_keys.include?(k) }
280
444
  options = options.map { |k, v| "#{k}: \"#{v}\"" }
281
445
  end
282
- contents << [" #{platform_indent}gem \"#{d.name}\", \"#{d.requirement}\"", *options].join(", ")
283
- end
284
- if !platform_name.empty?
285
- contents << " end"
446
+ contents << [" #{platform_indent}gem \"#{d.name}\",
447
+ \"#{d.requirement}\"", *options].join(', ')
286
448
  end
449
+ contents << ' end' unless platform_name.empty?
287
450
  end
288
- contents << "end"
451
+ contents << 'end'
289
452
  end
290
453
  contents.join("\n")
291
454
  end
@@ -293,20 +456,25 @@ def merge_gemfiles(*path, unlock: [])
293
456
  def workspace_configuration_gemfiles
294
457
  gemfiles = []
295
458
  ws.manifest.each_package_set do |source|
296
- if source.local_dir && File.file?(pkg_set_gemfile = File.join(source.local_dir, 'Gemfile'))
459
+ pkg_set_gemfile = File.join(source.local_dir, 'Gemfile')
460
+ if source.local_dir && File.file?(pkg_set_gemfile)
297
461
  gemfiles << pkg_set_gemfile
298
462
  end
299
463
  end
300
464
  # In addition, look into overrides.d
301
- Dir.glob(File.join(ws.overrides_dir, "*.gemfile")) do |overrides_gemfile_path|
302
- gemfiles << overrides_gemfile_path
465
+ Dir.glob(File.join(ws.overrides_dir, "*.gemfile")) do |overrides_gemfile|
466
+ gemfiles << overrides_gemfile
303
467
  end
304
468
  gemfiles
305
469
  end
306
470
 
471
+ def self.default_gemfile_path(ws)
472
+ File.join(ws.prefix_dir, 'gems', 'Gemfile')
473
+ end
474
+
307
475
  def install(gems, filter_uptodate_packages: false, install_only: false)
308
- root_dir = File.join(ws.prefix_dir, 'gems')
309
- gemfile_path = File.join(root_dir, 'Gemfile')
476
+ gemfile_path = self.class.default_gemfile_path(ws)
477
+ root_dir = File.dirname(gemfile_path)
310
478
  gemfile_lock_path = "#{gemfile_path}.lock"
311
479
  backups = Hash[
312
480
  gemfile_path => "#{gemfile_path}.orig",
@@ -316,9 +484,10 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
316
484
  # Back up the existing gemfile, we'll restore it if something is
317
485
  # wrong to avoid leaving bundler in an inconsistent state
318
486
  backup_files(backups)
319
- if !File.file?("#{gemfile_path}.orig")
487
+ unless File.file?("#{gemfile_path}.orig")
320
488
  Ops.atomic_write("#{gemfile_path}.orig") do |io|
321
- io.puts "eval_gemfile \"#{File.join(ws.dot_autoproj_dir, 'Gemfile')}\""
489
+ dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, 'Gemfile')
490
+ io.puts "eval_gemfile \"#{dot_autoproj_gemfile}\""
322
491
  end
323
492
  end
324
493
 
@@ -330,7 +499,7 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
330
499
  gemfile_contents = Tempfile.open 'autoproj-gemfile' do |io|
331
500
  gems.sort.each do |name|
332
501
  name, version = parse_package_entry(name)
333
- io.puts "gem \"#{name}\", \"#{version || ">= 0"}\""
502
+ io.puts "gem \"#{name}\", \"#{version || '>= 0'}\""
334
503
  end
335
504
  io.flush
336
505
  gemfiles.unshift io.path
@@ -340,7 +509,9 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
340
509
  end
341
510
 
342
511
  FileUtils.mkdir_p root_dir
343
- if updated = (!File.exist?(gemfile_path) || File.read(gemfile_path) != gemfile_contents)
512
+ updated = (!File.exist?(gemfile_path) ||
513
+ File.read(gemfile_path) != gemfile_contents)
514
+ if updated
344
515
  Ops.atomic_write(gemfile_path) do |io|
345
516
  io.puts "ruby \"#{RUBY_VERSION}\" if respond_to?(:ruby)"
346
517
  io.puts gemfile_contents
@@ -350,24 +521,25 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
350
521
  options = Array.new
351
522
  binstubs_path = File.join(root_dir, 'bin')
352
523
  if updated || !install_only || !File.file?("#{gemfile_path}.lock")
353
- self.class.run_bundler_install ws, gemfile_path, *options,
354
- binstubs: binstubs_path
524
+ self.class.run_bundler_install(ws, gemfile_path, *options,
525
+ binstubs: binstubs_path)
355
526
  end
356
527
 
357
- if bundle_rubylib = discover_bundle_rubylib
528
+ if (bundle_rubylib = discover_bundle_rubylib)
358
529
  update_env_rubylib(bundle_rubylib)
359
530
  else
360
- raise NotCleanState, "bundler executed successfully, but the result was not in a clean state"
531
+ raise NotCleanState, "bundler executed successfully, "\
532
+ "but the result was not in a clean state"
361
533
  end
362
-
363
534
  rescue Exception
364
- Autoproj.warn "saved the new Gemfile in #{gemfile_path}.FAILED and restored the last Gemfile version"
535
+ Autoproj.warn "saved the new Gemfile in #{gemfile_path}.FAILED "\
536
+ "and restored the last Gemfile version"
365
537
  FileUtils.cp gemfile_path, "#{gemfile_path}.FAILED"
366
538
  backup_restore(backups)
367
539
  raise
368
540
  ensure
369
541
  if binstubs_path
370
- FileUtils.rm_f File.join(binstubs_path, 'bundle')
542
+ FileUtils.rm_f File.join(binstubs_path, 'bundle')
371
543
  FileUtils.rm_f File.join(binstubs_path, 'bundler')
372
544
  end
373
545
  backup_clean(backups)
@@ -376,14 +548,14 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
376
548
  def discover_rubylib
377
549
  require 'bundler'
378
550
  Tempfile.open 'autoproj-rubylib' do |io|
379
- result = Bundler.clean_system(
551
+ result = Autoproj.bundler_unbundled_system(
380
552
  Hash['RUBYLIB' => nil],
381
553
  Autobuild.tool('ruby'), '-e', 'puts $LOAD_PATH',
382
554
  out: io,
383
555
  err: '/dev/null')
384
556
  if result
385
557
  io.rewind
386
- io.readlines.map { |l| l.chomp }.find_all { |l| !l.empty? }
558
+ io.readlines.map(&:chomp).find_all { |l| !l.empty? }
387
559
  end
388
560
  end
389
561
  end
@@ -397,25 +569,23 @@ def discover_bundle_rubylib(silent_errors: false)
397
569
  require 'bundler'
398
570
  gemfile = File.join(ws.prefix_dir, 'gems', 'Gemfile')
399
571
  silent_redirect = Hash.new
400
- if silent_errors
401
- silent_redirect[:err] = :close
402
- end
572
+ silent_redirect[:err] = :close if silent_errors
403
573
  env = ws.env.resolved_env
404
574
  Tempfile.open 'autoproj-rubylib' do |io|
405
- result = Bundler.clean_system(
575
+ result = Autoproj.bundler_unbundled_system(
406
576
  Hash['GEM_HOME' => env['GEM_HOME'], 'GEM_PATH' => env['GEM_PATH'],
407
577
  'BUNDLE_GEMFILE' => gemfile, 'RUBYOPT' => nil,
408
578
  'RUBYLIB' => self.class.rubylib_for_bundler],
409
- Autobuild.tool('ruby'), '-rbundler/setup', '-e', 'puts $LOAD_PATH',
579
+ Autobuild.tool('ruby'), '-rbundler/setup',
580
+ '-e', 'puts $LOAD_PATH',
410
581
  out: io, **silent_redirect)
411
-
582
+
412
583
  if result
413
584
  io.rewind
414
- io.readlines.map { |l| l.chomp }.find_all { |l| !l.empty? }
585
+ io.readlines.map(&:chomp).find_all { |l| !l.empty? }
415
586
  end
416
587
  end
417
588
  end
418
589
  end
419
590
  end
420
591
  end
421
-