autoproj 2.11.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -8
  3. data/.travis.yml +5 -3
  4. data/autoproj.gemspec +6 -6
  5. data/bin/alog +1 -0
  6. data/bin/autoproj +1 -1
  7. data/bin/autoproj_bootstrap +130 -67
  8. data/bin/autoproj_bootstrap.in +9 -7
  9. data/bin/autoproj_install +129 -63
  10. data/bin/autoproj_install.in +8 -3
  11. data/lib/autoproj/autobuild_extensions/dsl.rb +27 -12
  12. data/lib/autoproj/base.rb +18 -0
  13. data/lib/autoproj/cli/base.rb +1 -1
  14. data/lib/autoproj/cli/build.rb +8 -3
  15. data/lib/autoproj/cli/cache.rb +79 -7
  16. data/lib/autoproj/cli/inspection_tool.rb +5 -6
  17. data/lib/autoproj/cli/main.rb +33 -9
  18. data/lib/autoproj/cli/show.rb +12 -18
  19. data/lib/autoproj/cli/status.rb +15 -9
  20. data/lib/autoproj/cli/test.rb +1 -1
  21. data/lib/autoproj/cli/update.rb +72 -17
  22. data/lib/autoproj/cli/utility.rb +25 -28
  23. data/lib/autoproj/configuration.rb +15 -4
  24. data/lib/autoproj/default.osdeps +29 -3
  25. data/lib/autoproj/environment.rb +17 -13
  26. data/lib/autoproj/installation_manifest.rb +7 -5
  27. data/lib/autoproj/manifest.rb +14 -6
  28. data/lib/autoproj/ops/build.rb +23 -21
  29. data/lib/autoproj/ops/cache.rb +151 -33
  30. data/lib/autoproj/ops/cached_env.rb +2 -2
  31. data/lib/autoproj/ops/import.rb +23 -4
  32. data/lib/autoproj/ops/install.rb +121 -60
  33. data/lib/autoproj/ops/phase_reporting.rb +49 -0
  34. data/lib/autoproj/ops/snapshot.rb +2 -1
  35. data/lib/autoproj/ops/tools.rb +2 -2
  36. data/lib/autoproj/os_package_installer.rb +19 -11
  37. data/lib/autoproj/package_definition.rb +1 -1
  38. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +49 -28
  39. data/lib/autoproj/package_managers/bundler_manager.rb +102 -19
  40. data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
  41. data/lib/autoproj/package_managers/pip_manager.rb +34 -22
  42. data/lib/autoproj/package_managers/shell_script_manager.rb +44 -24
  43. data/lib/autoproj/package_manifest.rb +43 -31
  44. data/lib/autoproj/package_set.rb +2 -2
  45. data/lib/autoproj/python.rb +285 -0
  46. data/lib/autoproj/test.rb +26 -10
  47. data/lib/autoproj/variable_expansion.rb +3 -1
  48. data/lib/autoproj/vcs_definition.rb +25 -12
  49. data/lib/autoproj/version.rb +1 -1
  50. data/lib/autoproj/workspace.rb +60 -16
  51. data/lib/autoproj.rb +3 -0
  52. metadata +17 -28
@@ -50,12 +50,19 @@ module Autoproj
50
50
 
51
51
  # Load the manifest data contained in +file+
52
52
  def load(file)
53
- if !File.exist?(file)
54
- raise ConfigError.new(File.dirname(file)), "expected an autoproj configuration in #{File.dirname(file)}, but #{file} does not exist"
53
+ unless File.exist?(file)
54
+ raise ConfigError.new(File.dirname(file)),
55
+ "expected an autoproj configuration in #{File.dirname(file)}, "\
56
+ "but #{file} does not exist"
55
57
  end
56
58
 
57
59
  data = Autoproj.in_file(file, Autoproj::YAML_LOAD_ERROR) do
58
- YAML.load(File.read(file)) || Hash.new
60
+ YAML.safe_load(File.read(file)) || {}
61
+ end
62
+
63
+ if data["layout"]&.member?(nil)
64
+ Autoproj.warn "There is an empty entry in your layout in #{file}. All empty entries are ignored."
65
+ data["layout"] = data["layout"].compact
59
66
  end
60
67
 
61
68
  @file = file
@@ -81,7 +88,8 @@ module Autoproj
81
88
  @has_layout = !!data['layout']
82
89
 
83
90
  if data['constants']
84
- @constant_definitions = Autoproj.resolve_constant_definitions(data['constants'])
91
+ @constant_definitions =
92
+ Autoproj.resolve_constant_definitions(data['constants'])
85
93
  end
86
94
  end
87
95
 
@@ -1234,7 +1242,7 @@ module Autoproj
1234
1242
  workspace.config
1235
1243
  end
1236
1244
 
1237
- def self.add_osdeps_overrides(*args, &block)
1238
- manifest.add_osdeps_overrides(*args, &block)
1245
+ def self.add_osdeps_overrides(*args, **kw, &block)
1246
+ manifest.add_osdeps_overrides(*args, **kw, &block)
1239
1247
  end
1240
1248
  end
@@ -87,34 +87,36 @@ module Autoproj
87
87
  # names of the packages that should be rebuilt
88
88
  # @return [void]
89
89
  def build_packages(all_enabled_packages, options = Hash.new)
90
+ if @report_path
91
+ reporting = Ops::PhaseReporting.new(
92
+ 'build', @report_path, method(:package_metadata)
93
+ )
94
+ end
95
+
90
96
  Autobuild.do_rebuild = false
91
97
  Autobuild.do_forced_build = false
98
+ reporting&.initialize_incremental_report
92
99
  begin
93
- Autobuild.apply(all_enabled_packages, "autoproj-build", ['build'], options)
100
+ Autobuild.apply(
101
+ all_enabled_packages,
102
+ "autoproj-build", ['build'], options
103
+ ) do |pkg, phase|
104
+ reporting&.report_incremental(pkg) if phase == 'build'
105
+ end
106
+
94
107
  ensure
95
- create_report(all_enabled_packages) if @report_path
108
+ packages = all_enabled_packages.map do |name|
109
+ @manifest.find_autobuild_package(name)
110
+ end
111
+ reporting&.create_report(packages)
96
112
  end
97
113
  end
98
114
 
99
- def create_report(package_list)
100
- FileUtils.mkdir_p File.dirname(@report_path)
101
-
102
- packages = package_list.each_with_object({}) do |pkg_name, h|
103
- pkg = manifest.find_autobuild_package(pkg_name)
104
-
105
- h[pkg.name] = {
106
- invoked: !!pkg.install_invoked?,
107
- success: !!pkg.installed?
108
- }
109
- end
110
-
111
- report = JSON.pretty_generate({
112
- build_report: {
113
- timestamp: Time.now,
114
- packages: packages
115
- }
116
- })
117
- IO.write(@report_path, report)
115
+ def package_metadata(autobuild_package)
116
+ {
117
+ invoked: !!autobuild_package.install_invoked?,
118
+ success: !!autobuild_package.installed?
119
+ }
118
120
  end
119
121
  end
120
122
  end
@@ -4,9 +4,10 @@ module Autoproj
4
4
  attr_reader :cache_dir
5
5
  attr_reader :manifest
6
6
 
7
- def initialize(cache_dir, manifest)
7
+ def initialize(cache_dir, ws)
8
8
  @cache_dir = cache_dir
9
- @manifest = manifest
9
+ @ws = ws
10
+ @manifest = ws.manifest
10
11
  end
11
12
 
12
13
  def with_retry(count)
@@ -27,29 +28,39 @@ module Autoproj
27
28
  File.join(cache_dir, 'git')
28
29
  end
29
30
 
30
- def cache_git(pkg, options = Hash.new)
31
+ def cache_git(pkg, checkout_only: false)
31
32
  pkg.importdir = File.join(git_cache_dir, pkg.name)
32
- if options[:checkout_only] && File.directory?(pkg.importdir)
33
- return
34
- end
33
+ return if checkout_only && File.directory?(pkg.importdir)
35
34
 
36
35
  pkg.importer.local_branch = nil
37
36
  pkg.importer.remote_branch = nil
38
37
  pkg.importer.remote_name = 'autobuild'
39
38
 
40
- if !File.directory?(pkg.importdir)
39
+ unless File.directory?(pkg.importdir)
41
40
  FileUtils.mkdir_p File.dirname(pkg.importdir)
42
- Autobuild::Subprocess.run("autoproj-cache", "import", Autobuild.tool(:git), "--git-dir", pkg.importdir, 'init', "--bare")
41
+ Autobuild::Subprocess.run(
42
+ "autoproj-cache", "import", Autobuild.tool(:git),
43
+ "--git-dir", pkg.importdir, 'init', "--bare"
44
+ )
43
45
  end
44
46
  pkg.importer.update_remotes_configuration(pkg)
45
47
 
46
48
  with_retry(10) do
47
- Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'remote', 'update', 'autobuild')
49
+ Autobuild::Subprocess.run(
50
+ 'autoproj-cache', :import, Autobuild.tool('git'),
51
+ '--git-dir', pkg.importdir, 'remote', 'update', 'autobuild'
52
+ )
48
53
  end
49
54
  with_retry(10) do
50
- Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'fetch', 'autobuild', '--tags')
55
+ Autobuild::Subprocess.run(
56
+ 'autoproj-cache', :import, Autobuild.tool('git'),
57
+ '--git-dir', pkg.importdir, 'fetch', 'autobuild', '--tags'
58
+ )
51
59
  end
52
- Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'gc', '--prune=all')
60
+ Autobuild::Subprocess.run(
61
+ 'autoproj-cache', :import, Autobuild.tool('git'),
62
+ '--git-dir', pkg.importdir, 'gc', '--prune=all'
63
+ )
53
64
  end
54
65
 
55
66
  def archive_cache_dir
@@ -63,7 +74,8 @@ module Autoproj
63
74
  end
64
75
  end
65
76
 
66
- def create_or_update(*package_names, all: true, keep_going: false, checkout_only: false)
77
+ def create_or_update(*package_names, all: true, keep_going: false,
78
+ checkout_only: false)
67
79
  FileUtils.mkdir_p cache_dir
68
80
 
69
81
  if package_names.empty?
@@ -75,7 +87,7 @@ module Autoproj
75
87
  end
76
88
  else
77
89
  packages = package_names.map do |name|
78
- if pkg = manifest.find_autobuild_package(name)
90
+ if (pkg = manifest.find_autobuild_package(name))
79
91
  pkg
80
92
  else
81
93
  raise PackageNotFound, "no package named #{name}"
@@ -88,42 +100,148 @@ module Autoproj
88
100
  total = packages.size
89
101
  Autoproj.message "Handling #{total} packages"
90
102
  packages.each_with_index do |pkg, i|
91
- next if pkg.srcdir != pkg.importdir # No need to process this one, it is uses another package's import
103
+ # No need to process this one, it is uses another package's
104
+ # import
105
+ next if pkg.srcdir != pkg.importdir
106
+
92
107
  begin
93
108
  case pkg.importer
94
109
  when Autobuild::Git
95
- Autoproj.message " [#{i}/#{total}] caching #{pkg.name} (git)"
110
+ Autoproj.message(
111
+ " [#{i}/#{total}] caching #{pkg.name} (git)"
112
+ )
96
113
  cache_git(pkg, checkout_only: checkout_only)
97
114
  when Autobuild::ArchiveImporter
98
- Autoproj.message " [#{i}/#{total}] caching #{pkg.name} (archive)"
115
+ Autoproj.message(
116
+ " [#{i}/#{total}] caching #{pkg.name} (archive)"
117
+ )
99
118
  cache_archive(pkg)
100
119
  else
101
- Autoproj.message " [#{i}/#{total}] not caching #{pkg.name} (cannot cache #{pkg.importer.class})"
120
+ Autoproj.message(
121
+ " [#{i}/#{total}] not caching #{pkg.name} "\
122
+ "(cannot cache #{pkg.importer.class})"
123
+ )
102
124
  end
103
125
  rescue Interrupt
104
126
  raise
105
127
  rescue ::Exception => e
106
- if keep_going
107
- pkg.error " failed to cache #{pkg.name}, but going on as requested"
108
- lines = e.to_s.split("\n")
109
- if lines.empty?
110
- lines = e.message.split("\n")
111
- end
112
- if lines.empty?
113
- lines = ["unknown error"]
114
- end
115
- pkg.message(lines.shift, :red, :bold)
116
- lines.each do |line|
117
- pkg.message(line)
128
+ raise unless keep_going
129
+
130
+ pkg.error " failed to cache #{pkg.name}, "\
131
+ 'but going on as requested'
132
+ lines = e.to_s.split('\n')
133
+ lines = e.message.split('\n') if lines.empty?
134
+ lines = ['unknown error'] if lines.empty?
135
+ pkg.message(lines.shift, :red, :bold)
136
+ lines.each do |line|
137
+ pkg.message(line)
138
+ end
139
+ nil
140
+ end
141
+ end
142
+ end
143
+
144
+ def gems_cache_dir
145
+ File.join(cache_dir, 'package_managers', 'gem')
146
+ end
147
+
148
+ def create_or_update_gems(keep_going: true, compile_force: false, compile: [])
149
+ # Note: this might directly copy into the cache directoy, and
150
+ # we support it later
151
+ cache_dir = File.join(@ws.prefix_dir, 'gems', 'vendor', 'cache')
152
+ PackageManagers::BundlerManager.run_bundler(
153
+ @ws, 'cache'
154
+ )
155
+
156
+ FileUtils.mkdir_p(gems_cache_dir) unless File.exist?(gems_cache_dir)
157
+
158
+ needs_copy =
159
+ if File.exist?(cache_dir)
160
+ real_cache_dir = File.realpath(cache_dir)
161
+ real_target_dir = File.realpath(gems_cache_dir)
162
+ (real_cache_dir != real_target_dir)
163
+ end
164
+
165
+ synchronize_gems_cache_dirs(real_cache_dir, real_target_dir) if needs_copy
166
+
167
+ platform_suffix = "-#{Gem::Platform.local}.gem"
168
+ failed = []
169
+ compile.each do |gem_name, artifacts: []|
170
+ Dir.glob(File.join(cache_dir, "#{gem_name}*.gem")) do |gem|
171
+ next if gem.end_with?(platform_suffix)
172
+
173
+ gem_basename = File.basename(gem, ".gem")
174
+ expected_platform_gem = File.join(
175
+ real_target_dir, "#{gem_basename}#{platform_suffix}"
176
+ )
177
+ next if !compile_force && File.file?(expected_platform_gem)
178
+
179
+ begin
180
+ compile_gem(
181
+ gem, artifacts: artifacts, output: real_target_dir
182
+ )
183
+ rescue CompilationFailed
184
+ unless keep_going
185
+ raise CompilationFailed, "#{gem} failed to compile"
118
186
  end
119
- nil
120
- else
121
- raise
187
+
188
+ failed << gem
122
189
  end
123
190
  end
124
191
  end
192
+
193
+ unless failed.empty?
194
+ raise CompilationFailed, "#{failed.sort.join(', ')} failed to compile"
195
+ end
196
+ end
197
+
198
+ class CompilationFailed < RuntimeError; end
199
+
200
+ def synchronize_gems_cache_dirs(source, target)
201
+ Dir.glob(File.join(source, "*.gem")) do |source_file|
202
+ basename = File.basename(source_file)
203
+ target_file = File.join(target, basename)
204
+ next if File.file?(target_file)
205
+
206
+ Autoproj.message "gems: caching #{basename}"
207
+ FileUtils.cp source_file, target_file
208
+ end
209
+ end
210
+
211
+ def guess_gem_program
212
+ return Autobuild.programs['gem'] if Autobuild.programs['gem']
213
+
214
+ ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME']
215
+ ruby_bindir = RbConfig::CONFIG['bindir']
216
+
217
+ candidates = ['gem']
218
+ candidates << "gem#{$1}" if ruby_bin =~ /^ruby(.+)$/
219
+
220
+ candidates.each do |gem_name|
221
+ if File.file?(gem_full_path = File.join(ruby_bindir, gem_name))
222
+ Autobuild.programs['gem'] = gem_full_path
223
+ return Autobuild.programs['gem']
224
+ end
225
+ end
226
+
227
+ raise ArgumentError,
228
+ 'cannot find a gem program (tried '\
229
+ "#{candidates.sort.join(', ')} in #{ruby_bindir})"
230
+ end
231
+
232
+ private def compile_gem(gem_path, output:, artifacts: [])
233
+ artifacts = artifacts.flat_map do |include, n|
234
+ if include
235
+ ["--include", n]
236
+ else
237
+ ["--exclude", n]
238
+ end
239
+ end
240
+ unless system(Autobuild.tool('ruby'), '-S', guess_gem_program,
241
+ 'compile', '--output', output, *artifacts, gem_path)
242
+ raise CompilationFailed, "#{gem_path} failed to compile"
243
+ end
125
244
  end
126
245
  end
127
246
  end
128
247
  end
129
-
@@ -9,7 +9,7 @@ module Autoproj
9
9
  def self.load_cached_env(root_dir)
10
10
  path = cached_env_path(root_dir)
11
11
  if File.file?(path)
12
- env = YAML.load(File.read(path))
12
+ env = YAML.safe_load(File.read(path))
13
13
  Autobuild::Environment::ExportedEnvironment.new(
14
14
  env['set'], env['unset'], env['update'])
15
15
  end
@@ -20,7 +20,7 @@ module Autoproj
20
20
  path = cached_env_path(root_dir)
21
21
  existing =
22
22
  begin
23
- YAML.load(File.read(path))
23
+ YAML.safe_load(File.read(path))
24
24
  rescue Exception
25
25
  end
26
26
 
@@ -199,6 +199,7 @@ module Autoproj
199
199
  install_vcs_packages: Hash.new,
200
200
  non_imported_packages: :checkout,
201
201
  auto_exclude: auto_exclude?,
202
+ filter: ->(package) { true },
202
203
  **import_options)
203
204
 
204
205
  unless %i[checkout ignore return].include?(non_imported_packages)
@@ -235,6 +236,11 @@ module Autoproj
235
236
  missing_vcs = Array.new
236
237
  installed_vcs_packages = Set['none', 'local']
237
238
  while failures.empty? || keep_going
239
+ # Allow 'filter' to parallelize as well
240
+ if filter.respond_to?(:lookahead)
241
+ package_queue.each { |pkg| filter.lookahead(pkg) }
242
+ end
243
+
238
244
  # Queue work for all packages in the queue
239
245
  package_queue.each do |pkg|
240
246
  # Remove packages that have already been processed
@@ -255,6 +261,11 @@ module Autoproj
255
261
  end
256
262
  all_processed_packages << pkg
257
263
 
264
+ unless filter.call(pkg)
265
+ completion_queue << [pkg, Time.now]
266
+ next
267
+ end
268
+
258
269
  importer = pkg.autobuild.importer
259
270
  if !pre_package_import(selection, manifest, pkg.autobuild,
260
271
  reverse_dependencies)
@@ -272,8 +283,8 @@ module Autoproj
272
283
  next
273
284
  end
274
285
 
275
- pending_packages << pkg
276
286
  begin
287
+ pending_packages << pkg
277
288
  queue_import_work(
278
289
  executor, completion_queue, pkg,
279
290
  retry_count: retry_count,
@@ -288,8 +299,12 @@ module Autoproj
288
299
  if completion_queue.empty? && pending_packages.empty?
289
300
  unless missing_vcs.empty?
290
301
  installed_vcs_packages.merge(
291
- install_vcs_packages_for(*missing_vcs,
292
- **install_vcs_packages))
302
+ install_vcs_packages_for(
303
+ *missing_vcs,
304
+ install_only: import_options[:checkout_only],
305
+ **install_vcs_packages
306
+ )
307
+ )
293
308
  package_queue.concat(missing_vcs)
294
309
  missing_vcs.clear
295
310
  next
@@ -366,7 +381,9 @@ module Autoproj
366
381
  end
367
382
  end
368
383
 
369
- def finalize_package_load(processed_packages, auto_exclude: auto_exclude?)
384
+ def finalize_package_load(processed_packages,
385
+ ignore_optional_dependencies: false,
386
+ auto_exclude: auto_exclude?)
370
387
  manifest = ws.manifest
371
388
 
372
389
  all = Set.new
@@ -426,6 +443,7 @@ module Autoproj
426
443
  keep_going: false,
427
444
  install_vcs_packages: Hash.new,
428
445
  auto_exclude: auto_exclude?,
446
+ filter: ->(pkg) { true },
429
447
  **import_options)
430
448
 
431
449
  manifest = ws.manifest
@@ -437,6 +455,7 @@ module Autoproj
437
455
  recursive: recursive,
438
456
  install_vcs_packages: install_vcs_packages,
439
457
  auto_exclude: auto_exclude,
458
+ filter: filter,
440
459
  **import_options)
441
460
 
442
461
  raise failures.first if !keep_going && !failures.empty?
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
  require 'optparse'
3
5
  require 'fileutils'
4
6
  require 'yaml'
7
+ require 'English'
5
8
 
6
9
  module Autoproj
7
10
  module Ops
@@ -13,6 +16,36 @@ module Autoproj
13
16
  class Install
14
17
  class UnexpectedBinstub < RuntimeError; end
15
18
 
19
+ RUBYLIB_REINIT = <<~RUBY
20
+ if defined?(Bundler)
21
+ if Bundler.respond_to?(:with_unbundled_env)
22
+ Bundler.with_unbundled_env do
23
+ exec(Hash['RUBYLIB' => nil], $0, *ARGV)
24
+ end
25
+ else
26
+ Bundler.with_clean_env do
27
+ exec(Hash['RUBYLIB' => nil], $0, *ARGV)
28
+ end
29
+ end
30
+ elsif ENV['RUBYLIB']
31
+ exec(Hash['RUBYLIB' => nil], $0, *ARGV)
32
+ end
33
+ RUBY
34
+
35
+ WITHOUT_BUNDLER = <<~RUBY
36
+ if defined?(Bundler)
37
+ if Bundler.respond_to?(:with_unbundled_env)
38
+ Bundler.with_unbundled_env do
39
+ exec($0, *ARGV)
40
+ end
41
+ else
42
+ Bundler.with_clean_env do
43
+ exec($0, *ARGV)
44
+ end
45
+ end
46
+ end
47
+ RUBY
48
+
16
49
  # The created workspace's root directory
17
50
  attr_reader :root_dir
18
51
  # Content of the Gemfile generated to install autoproj itself
@@ -220,6 +253,10 @@ module Autoproj
220
253
  "gem \"utilrb\", \">= 3.0.1\""].join("\n")
221
254
  end
222
255
 
256
+ def add_seed_config(path)
257
+ @config.merge!(YAML.safe_load(File.read(path), [Symbol]))
258
+ end
259
+
223
260
  # Parse the provided command line options and returns the non-options
224
261
  def parse_options(args = ARGV)
225
262
  options = OptionParser.new do |opt|
@@ -243,6 +280,10 @@ module Autoproj
243
280
  opt.on '--public-gems', "install gems in the default gem location" do
244
281
  self.install_gems_in_gem_user_dir
245
282
  end
283
+ opt.on '--bundler-version=VERSION_CONSTRAINT', String, 'use the provided '\
284
+ 'string as a version constraint for bundler' do |version|
285
+ @config['bundler_version'] = version
286
+ end
246
287
  opt.on '--version=VERSION_CONSTRAINT', String, 'use the provided '\
247
288
  'string as a version constraint for autoproj' do |version|
248
289
  if @gemfile
@@ -257,9 +298,14 @@ module Autoproj
257
298
  end
258
299
  @gemfile = File.read(path)
259
300
  end
301
+ opt.on '--no-seed-config',
302
+ 'when reinstalling an existing autoproj workspace, do not '\
303
+ 'use the config in .autoproj/ as seed' do
304
+ @config.clear
305
+ end
260
306
  opt.on '--seed-config=PATH', String, 'path to a seed file that '\
261
307
  'should be used to initialize the configuration' do |path|
262
- @config.merge!(YAML.load(File.read(path)))
308
+ add_seed_config(path)
263
309
  end
264
310
  opt.on '--prefer-os-independent-packages', 'prefer OS-independent '\
265
311
  'packages (such as a RubyGem) over their OS-packaged equivalent '\
@@ -289,22 +335,50 @@ module Autoproj
289
335
  @autoproj_options + args
290
336
  end
291
337
 
292
- def find_bundler(gem_program)
293
- setup_paths =
294
- IO.popen([env_for_child, Gem.ruby, gem_program, 'which','-a', 'bundler/setup']) do |io|
295
- io.read
296
- end
297
- return unless $?.success?
338
+ def bundler_version
339
+ @config['bundler_version']
340
+ end
341
+
342
+ def find_bundler(gem_program, version: nil)
298
343
  bundler_path = File.join(gems_gem_home, 'bin', 'bundle')
299
- setup_paths.each_line do |setup_path|
300
- if File.exist?(bundler_path) && setup_path.start_with?(gems_gem_home)
301
- return bundler_path
344
+ return unless File.exist?(bundler_path)
345
+
346
+ setup_paths =
347
+ if version
348
+ find_versioned_bundler_setup(gem_program, version)
349
+ else
350
+ find_unversioned_bundler_setup(gem_program)
302
351
  end
352
+
353
+ setup_paths.each do |setup_path|
354
+ return bundler_path if setup_path.start_with?(gems_gem_home)
303
355
  end
304
- return
356
+ nil
357
+ end
358
+
359
+ def find_versioned_bundler_setup(gem_program, version)
360
+ contents = IO.popen(
361
+ [env_for_child, Gem.ruby, gem_program,
362
+ 'contents', '-v', version, 'bundler'],
363
+ &:readlines
364
+ )
365
+ return [] unless $CHILD_STATUS.success?
366
+
367
+ contents.grep(%r{bundler/setup.rb$})
368
+ end
369
+
370
+ def find_unversioned_bundler_setup(gem_program)
371
+ setup_paths = IO.popen(
372
+ [env_for_child, Gem.ruby, gem_program,
373
+ 'which', '-a', 'bundler/setup'],
374
+ &:readlines
375
+ )
376
+ return [] unless $CHILD_STATUS.success?
377
+
378
+ setup_paths
305
379
  end
306
380
 
307
- def install_bundler(gem_program, silent: false)
381
+ def install_bundler(gem_program, version: nil, silent: false)
308
382
  local = ['--local'] if local?
309
383
 
310
384
  redirection = Hash.new
@@ -312,6 +386,9 @@ module Autoproj
312
386
  redirection = Hash[out: :close]
313
387
  end
314
388
 
389
+ version_args = []
390
+ version_args << '-v' << version if version
391
+
315
392
  # Shut up the bundler warning about 'bin' not being in PATH
316
393
  env = self.env
317
394
  env['PATH'] += [File.join(gems_gem_home, 'bin')]
@@ -322,14 +399,14 @@ module Autoproj
322
399
  '--clear-sources', '--source', gem_source,
323
400
  '--no-user-install', '--install-dir', gems_gem_home,
324
401
  *local, "--bindir=#{File.join(gems_gem_home, 'bin')}",
325
- 'bundler', **redirection)
402
+ 'bundler', *version_args, **redirection)
326
403
 
327
404
  if !result
328
405
  STDERR.puts "FATAL: failed to install bundler in #{gems_gem_home}"
329
406
  nil
330
407
  end
331
408
 
332
- if (bundler_path = find_bundler(gem_program))
409
+ if (bundler_path = find_bundler(gem_program, version: version))
333
410
  bundler_path
334
411
  else
335
412
  STDERR.puts "gem install bundler returned successfully, but still "\
@@ -338,7 +415,7 @@ module Autoproj
338
415
  end
339
416
  end
340
417
 
341
- def install_autoproj(bundler)
418
+ def install_autoproj(bundler, bundler_version: self.bundler_version)
342
419
  # Force bundler to update. If the user does not want this, let
343
420
  # him specify a Gemfile with tighter version constraints
344
421
  lockfile = File.join(dot_autoproj, 'Gemfile.lock')
@@ -353,14 +430,19 @@ module Autoproj
353
430
  opts << "--path=#{gems_install_path}"
354
431
  shims_path = File.join(dot_autoproj, 'bin')
355
432
 
356
- result = system(clean_env,
357
- Gem.ruby, bundler, 'install',
358
- "--gemfile=#{autoproj_gemfile_path}",
359
- "--shebang=#{Gem.ruby}",
360
- "--binstubs=#{shims_path}",
361
- *opts, chdir: dot_autoproj)
433
+ version_arg = []
434
+ version_arg << "_#{bundler_version}_" if bundler_version
362
435
 
363
- if !result
436
+ result = system(
437
+ clean_env,
438
+ Gem.ruby, bundler, *version_arg, 'install',
439
+ "--gemfile=#{autoproj_gemfile_path}",
440
+ "--shebang=#{Gem.ruby}",
441
+ "--binstubs=#{shims_path}",
442
+ *opts, chdir: dot_autoproj
443
+ )
444
+
445
+ unless result
364
446
  STDERR.puts "FATAL: failed to install autoproj in #{dot_autoproj}"
365
447
  exit 1
366
448
  end
@@ -369,7 +451,7 @@ module Autoproj
369
451
  root_dir, autoproj_gemfile_path, gems_gem_home)
370
452
  end
371
453
 
372
- EXCLUDED_FROM_SHIMS = %w{rake thor}
454
+ EXCLUDED_FROM_SHIMS = %w[rake thor].freeze
373
455
 
374
456
  def self.rewrite_shims(shim_path, ruby_executable,
375
457
  root_dir, autoproj_gemfile_path, gems_gem_home)
@@ -425,11 +507,7 @@ module Autoproj
425
507
  #
426
508
 
427
509
  # Autoproj generated preamble
428
- if defined?(Bundler)
429
- Bundler.with_clean_env do
430
- exec($0, *ARGV)
431
- end
432
- end
510
+ #{WITHOUT_BUNDLER}
433
511
  ENV['BUNDLE_GEMFILE'] ||= '#{autoproj_gemfile_path}'
434
512
  ENV['GEM_HOME'] = '#{gems_gem_home}'
435
513
  ENV.delete('GEM_PATH')
@@ -441,12 +519,7 @@ Gem.paths = Hash['GEM_HOME' => '#{gems_gem_home}', 'GEM_PATH' => '']
441
519
  def self.shim_bundler_old(ruby_executable, autoproj_gemfile_path, gems_gem_home)
442
520
  "#! #{ruby_executable}
443
521
 
444
- if defined?(Bundler)
445
- Bundler.with_clean_env do
446
- exec($0, *ARGV)
447
- end
448
- end
449
-
522
+ #{WITHOUT_BUNDLER}
450
523
  ENV['BUNDLE_GEMFILE'] ||= '#{autoproj_gemfile_path}'
451
524
  ENV['GEM_HOME'] = '#{gems_gem_home}'
452
525
  ENV.delete('GEM_PATH')
@@ -471,14 +544,7 @@ load Gem.bin_path('bundler', 'bundler')"
471
544
  #
472
545
 
473
546
  # Autoproj generated preamble, v1
474
- if defined?(Bundler)
475
- Bundler.with_clean_env do
476
- exec(Hash['RUBYLIB' => nil], $0, *ARGV)
477
- end
478
- elsif ENV['RUBYLIB']
479
- exec(Hash['RUBYLIB' => nil], $0, *ARGV)
480
- end
481
-
547
+ #{RUBYLIB_REINIT}
482
548
  ENV['BUNDLE_GEMFILE'] = '#{autoproj_gemfile_path}'
483
549
  ENV['AUTOPROJ_CURRENT_ROOT'] = '#{root_dir}'
484
550
  Gem.paths = Hash['GEM_HOME' => '#{gems_gem_home}', 'GEM_PATH' => '']
@@ -490,14 +556,7 @@ Gem.paths = Hash['GEM_HOME' => '#{gems_gem_home}', 'GEM_PATH' => '']
490
556
  gems_gem_home, load_line)
491
557
  "#! #{ruby_executable}
492
558
 
493
- if defined?(Bundler)
494
- Bundler.with_clean_env do
495
- exec(Hash['RUBYLIB' => nil], $0, *ARGV)
496
- end
497
- elsif ENV['RUBYLIB']
498
- exec(Hash['RUBYLIB' => nil], $0, *ARGV)
499
- end
500
-
559
+ #{RUBYLIB_REINIT}
501
560
  ENV['BUNDLE_GEMFILE'] = '#{autoproj_gemfile_path}'
502
561
  ENV['AUTOPROJ_CURRENT_ROOT'] = '#{root_dir}'
503
562
  require 'rubygems'
@@ -591,8 +650,11 @@ require 'bundler/setup'
591
650
  #
592
651
  # So, we're calling 'gem' as a subcommand to discovery the
593
652
  # actual bindir
594
- bindir = IO.popen(env_for_child,
595
- [Gem.ruby, '-e', 'puts "#{Gem.user_dir}/bin"']).read
653
+ bindir = IO.popen(
654
+ env_for_child,
655
+ [Gem.ruby, '-e', 'puts "#{Gem.user_dir}/bin"'], # rubocop:disable Lint/InterpolationCheck
656
+ &:read
657
+ )
596
658
  if bindir
597
659
  @gem_bindir = bindir.chomp
598
660
  else
@@ -600,11 +662,11 @@ require 'bundler/setup'
600
662
  end
601
663
  end
602
664
 
603
- def install
665
+ def install(bundler_version: self.bundler_version)
604
666
  if ENV['BUNDLER_GEMFILE']
605
667
  raise "cannot run autoproj_install or autoproj_bootstrap while "\
606
- "under a 'bundler exec' subcommand or having loaded an env.sh. "\
607
- "Open a new console and try again"
668
+ "under a 'bundler exec' subcommand or having loaded an "\
669
+ "env.sh. Open a new console and try again"
608
670
  end
609
671
 
610
672
  gem_program = self.class.guess_gem_program
@@ -612,13 +674,12 @@ require 'bundler/setup'
612
674
  env['GEM_HOME'] = [gems_gem_home]
613
675
  env['GEM_PATH'] = [gems_gem_home]
614
676
 
615
- if bundler = find_bundler(gem_program)
677
+ if (bundler = find_bundler(gem_program, version: bundler_version))
616
678
  puts "Detected bundler at #{bundler}"
617
679
  else
618
680
  puts "Installing bundler in #{gems_gem_home}"
619
- if !(bundler = install_bundler(gem_program))
620
- exit 1
621
- end
681
+ bundler = install_bundler(gem_program, version: bundler_version)
682
+ exit(1) unless bundler
622
683
  end
623
684
  self.class.rewrite_shims(
624
685
  File.join(dot_autoproj, 'bin'),
@@ -630,7 +691,7 @@ require 'bundler/setup'
630
691
  save_gemfile
631
692
 
632
693
  puts "Installing autoproj in #{gems_gem_home}"
633
- install_autoproj(bundler)
694
+ install_autoproj(bundler, bundler_version: bundler_version)
634
695
  end
635
696
 
636
697
  def load_config