autoproj 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 Ops
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 @@ def default_gemfile_contents(autoproj_version = ">= 2.0.0")
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)))
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 @@ def parse_options(args = ARGV)
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 @@ def parse_options(args = ARGV)
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 @@ def parse_options(args = ARGV)
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 @@ def install_bundler(gem_program, silent: false)
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 @@ def install_bundler(gem_program, silent: false)
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 @@ def install_bundler(gem_program, silent: false)
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 @@ def install_autoproj(bundler)
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 @@ def install_autoproj(bundler)
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 @@ def self.shim_bundler(script_lines, ruby_executable, autoproj_gemfile_path, gems
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 @@ def self.shim_bundler(script_lines, ruby_executable, autoproj_gemfile_path, gems
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 @@ def self.shim_script(script_lines, ruby_executable, root_dir,
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 @@ def self.shim_script_old(ruby_executable, root_dir, autoproj_gemfile_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 @@ def gem_bindir
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 @@ def gem_bindir
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 @@ def install
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 @@ def install
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
@@ -0,0 +1,49 @@
1
+ module Autoproj
2
+ module Ops
3
+ # Common logic to generate build/import/utility reports
4
+ class PhaseReporting
5
+ def initialize(name, path, metadata_get)
6
+ @name = name
7
+ @path = path
8
+ @metadata_get = metadata_get
9
+ end
10
+
11
+ def create_report(autobuild_packages)
12
+ info = autobuild_packages.each_with_object({}) do |p, map|
13
+ map[p.name] = @metadata_get.call(p)
14
+ end
15
+
16
+ dump = JSON.dump(
17
+ "#{@name}_report" => {
18
+ 'timestamp' => Time.now,
19
+ 'packages' => info
20
+ }
21
+ )
22
+
23
+ FileUtils.mkdir_p File.dirname(@path)
24
+ File.open(@path, 'w') do |io|
25
+ io.write dump
26
+ end
27
+ end
28
+
29
+ def initialize_incremental_report
30
+ FileUtils.mkdir_p File.dirname(@path)
31
+ @incremental_report = ""
32
+ end
33
+
34
+ def report_incremental(autobuild_package)
35
+ new_metadata = @metadata_get.call(autobuild_package)
36
+ prefix = @incremental_report.empty? ? "\n" : ",\n"
37
+ @incremental_report.concat(
38
+ "#{prefix}\"#{autobuild_package.name}\": #{JSON.dump(new_metadata)}"
39
+ )
40
+ File.open(@path, 'w') do |io|
41
+ io.write "{ \"#{@name}_report\": "\
42
+ "{\"timestamp\": #{JSON.dump(Time.now)}, \"packages\": {"
43
+ io.write(@incremental_report)
44
+ io.write "}}}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -119,6 +119,7 @@ def error_or_warn(package, error)
119
119
 
120
120
  def snapshot_packages(packages, target_dir = nil, only_local: true, fingerprint: false)
121
121
  result = Array.new
122
+ fingerprint_memo = Hash.new
122
123
  packages.each do |package_name|
123
124
  package = manifest.find_package_definition(package_name)
124
125
  if !package
@@ -141,7 +142,7 @@ def snapshot_packages(packages, target_dir = nil, only_local: true, fingerprint:
141
142
  end
142
143
 
143
144
  if fingerprint
144
- vcs_info['fingerprint'] = package.autobuild.fingerprint
145
+ vcs_info['fingerprint'] = package.autobuild.fingerprint(memo: fingerprint_memo)
145
146
  end
146
147
 
147
148
  if vcs_info
@@ -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