autoproj 2.9.0 → 2.10.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +113 -0
  3. data/.travis.yml +0 -2
  4. data/Gemfile +1 -0
  5. data/README.md +59 -14
  6. data/bin/autoproj_bootstrap +21 -12
  7. data/bin/autoproj_bootstrap.in +2 -2
  8. data/bin/autoproj_install +21 -12
  9. data/bin/autoproj_install.in +2 -2
  10. data/lib/autoproj/aruba_minitest.rb +4 -4
  11. data/lib/autoproj/autobuild_extensions/dsl.rb +91 -70
  12. data/lib/autoproj/autobuild_extensions/package.rb +20 -1
  13. data/lib/autoproj/build_option.rb +24 -7
  14. data/lib/autoproj/cli/base.rb +12 -1
  15. data/lib/autoproj/cli/bootstrap.rb +9 -3
  16. data/lib/autoproj/cli/build.rb +17 -13
  17. data/lib/autoproj/cli/envsh.rb +1 -1
  18. data/lib/autoproj/cli/exec.rb +5 -7
  19. data/lib/autoproj/cli/main.rb +44 -9
  20. data/lib/autoproj/cli/main_test.rb +2 -0
  21. data/lib/autoproj/cli/test.rb +29 -6
  22. data/lib/autoproj/cli/version.rb +52 -0
  23. data/lib/autoproj/cli/versions.rb +4 -1
  24. data/lib/autoproj/cli/watch.rb +2 -1
  25. data/lib/autoproj/configuration.rb +49 -11
  26. data/lib/autoproj/default.osdeps +9 -0
  27. data/lib/autoproj/manifest.rb +6 -6
  28. data/lib/autoproj/ops/build.rb +5 -15
  29. data/lib/autoproj/ops/import.rb +22 -3
  30. data/lib/autoproj/ops/install.rb +19 -10
  31. data/lib/autoproj/ops/main_config_switcher.rb +12 -6
  32. data/lib/autoproj/ops/snapshot.rb +5 -1
  33. data/lib/autoproj/os_package_resolver.rb +245 -209
  34. data/lib/autoproj/package_selection.rb +18 -0
  35. data/lib/autoproj/reporter.rb +45 -31
  36. data/lib/autoproj/test.rb +107 -56
  37. data/lib/autoproj/version.rb +1 -1
  38. data/lib/autoproj/workspace.rb +90 -72
  39. data/shell/completion/amake_bash +1 -0
  40. data/shell/completion/amake_zsh +1 -0
  41. data/shell/completion/autoproj_bash +2 -0
  42. data/shell/completion/autoproj_zsh +2 -0
  43. metadata +5 -3
@@ -32,16 +32,22 @@ def validate_options(args, options)
32
32
  end
33
33
  return args, options
34
34
  end
35
-
36
- def run(buildconf_info, options)
35
+
36
+ def run(buildconf_info, interactive: nil, **options)
37
37
  ws = Workspace.new(root_dir)
38
+ ws.config.interactive = interactive unless interactive.nil?
38
39
  ws.setup
39
40
 
40
41
  seed_config = options.delete(:seed_config)
41
42
 
42
43
  switcher = Ops::MainConfigSwitcher.new(ws)
44
+ check_root_dir_empty =
45
+ ws.config.interactive? && switcher.check_root_dir_empty?
46
+
43
47
  begin
44
- switcher.bootstrap(buildconf_info, options)
48
+ switcher.bootstrap(buildconf_info,
49
+ check_root_dir_empty: check_root_dir_empty,
50
+ **options)
45
51
  if seed_config
46
52
  FileUtils.cp seed_config, File.join(ws.config_dir, 'config.yml')
47
53
  end
@@ -9,22 +9,21 @@ def validate_options(selected_packages, options)
9
9
  super(selected_packages, options.merge(
10
10
  checkout_only: true, aup: options[:amake]))
11
11
 
12
- if options[:no_deps_shortcut]
13
- options[:deps] = false
14
- end
12
+ options[:deps] = false if options[:no_deps_shortcut]
15
13
  if options[:deps].nil?
16
14
  options[:deps] =
17
15
  !(options[:rebuild] || options[:force])
18
16
  end
19
- return selected_packages, options
17
+ [selected_packages, options]
20
18
  end
21
19
 
22
- def run(selected_packages, options)
20
+ def run(selected_packages, **options)
23
21
  build_options, options = filter_options options,
24
22
  force: false,
25
23
  rebuild: false,
26
24
  parallel: nil,
27
- confirm: true
25
+ confirm: true,
26
+ not: Array.new
28
27
 
29
28
  command_line_selection, source_packages, _osdep_packages =
30
29
  super(selected_packages,
@@ -36,15 +35,17 @@ def run(selected_packages, options)
36
35
 
37
36
  return if source_packages.empty?
38
37
 
38
+ active_packages = source_packages - build_options[:not]
39
+
39
40
  # Disable all packages that are not selected
40
41
  ws.manifest.each_autobuild_package do |pkg|
41
- next if source_packages.include?(pkg.name)
42
+ next if active_packages.include?(pkg.name)
42
43
  pkg.disable
43
44
  end
44
45
 
45
46
  Autobuild.ignore_errors = options[:keep_going]
46
47
 
47
- ops = Ops::Build.new(ws.manifest, report_dir: ws.log_dir)
48
+ ops = Ops::Build.new(ws.manifest, report_path: ws.build_report_path)
48
49
  if build_options[:rebuild] || build_options[:force]
49
50
  packages_to_rebuild =
50
51
  if options[:deps] || command_line_selection.empty?
@@ -59,10 +60,12 @@ def run(selected_packages, options)
59
60
  else 'force-build'
60
61
  end
61
62
  if build_options[:confirm] != false
62
- opt = BuildOption.new("", "boolean", {:doc => "this is going to trigger a #{mode_name} of all packages. Is that really what you want ?"}, nil)
63
- if !opt.ask(false)
64
- raise Interrupt
65
- end
63
+ opt = BuildOption.new("", "boolean",
64
+ {
65
+ doc: "this is going to trigger a #{mode_name} "\
66
+ "of all packages. Is that really what you want ?"
67
+ }, nil)
68
+ raise Interrupt unless opt.ask(false)
66
69
  end
67
70
 
68
71
  if build_options[:rebuild]
@@ -82,7 +85,8 @@ def run(selected_packages, options)
82
85
  ops.build_packages(source_packages, parallel: parallel)
83
86
  Main.run_post_command_hook(:build, ws, source_packages: source_packages)
84
87
  ensure
85
- export_env_sh
88
+ # Update env.sh, but only if we managed to load the configuration
89
+ export_env_sh if source_packages
86
90
  end
87
91
  end
88
92
  end
@@ -4,7 +4,7 @@ module CLI
4
4
  class Envsh < InspectionTool
5
5
  def validate_options(_unused, options = Hash.new)
6
6
  _, options = super(_unused, options)
7
- options
7
+ [options]
8
8
  end
9
9
 
10
10
  def run(**options)
@@ -8,7 +8,7 @@ module CLI
8
8
  class Exec
9
9
  def initialize
10
10
  @root_dir = Autoproj.find_workspace_dir
11
- if !@root_dir
11
+ unless @root_dir
12
12
  require 'autoproj/workspace'
13
13
  # Will do all sorts of error reporting,
14
14
  # or may be able to resolve
@@ -18,18 +18,16 @@ def initialize
18
18
 
19
19
  def load_cached_env
20
20
  env = Ops.load_cached_env(@root_dir)
21
- return if !env
21
+ return unless env
22
22
 
23
23
  Autobuild::Environment.
24
24
  environment_from_export(env, ENV)
25
25
  end
26
26
 
27
27
  def run(cmd, *args, use_cached_env: Ops.watch_running?(@root_dir))
28
- if use_cached_env
29
- env = load_cached_env
30
- end
28
+ env = load_cached_env if use_cached_env
31
29
 
32
- if !env
30
+ unless env
33
31
  require 'autoproj'
34
32
  require 'autoproj/cli/inspection_tool'
35
33
  ws = Workspace.from_dir(@root_dir)
@@ -40,7 +38,7 @@ def run(cmd, *args, use_cached_env: Ops.watch_running?(@root_dir))
40
38
  end
41
39
 
42
40
  path = env['PATH'].split(File::PATH_SEPARATOR)
43
- program =
41
+ program =
44
42
  begin Ops.which(cmd, path_entries: path)
45
43
  rescue ::Exception => e
46
44
  require 'autoproj'
@@ -30,10 +30,24 @@ class Main < Thor
30
30
  desc: 'enables or disables colored display (enabled by default if the terminal supports it)'
31
31
  class_option :progress, type: :boolean, default: TTY::Color.color?,
32
32
  desc: 'enables or disables progress display (enabled by default if the terminal supports it)'
33
+ class_option 'interactive', type: :boolean,
34
+ default: (ENV['AUTOPROJ_NONINTERACTIVE'] != '1'),
35
+ desc: 'tell autoproj to run (non)interactively'
33
36
 
34
37
  stop_on_unknown_option! :exec
35
38
  check_unknown_options! except: :exec
36
39
 
40
+ class << self
41
+ # @api private
42
+ #
43
+ # Override the CLI logic to determine what should be done
44
+ # on package failure (between raising or exiting)
45
+ #
46
+ # This is used mainly in tests, to make sure that the CLI won't
47
+ # be calling exit(). Set to nil to restore the default behavior
48
+ attr_accessor :default_report_on_package_failures
49
+ end
50
+
37
51
  # @api private
38
52
  #
39
53
  # Run hooks defined for a given hook name
@@ -74,13 +88,28 @@ def self.register_post_command_hook(hook_name, &block)
74
88
 
75
89
  no_commands do
76
90
  def default_report_on_package_failures
77
- if options[:debug]
91
+ if (override = Main.default_report_on_package_failures)
92
+ override
93
+ elsif options[:debug]
78
94
  :raise
79
95
  else
80
96
  :exit
81
97
  end
82
98
  end
83
99
 
100
+ # Generate a command line for Ops::Install, which has an
101
+ # internal option parser based on OptionParse (to be
102
+ # self-sufficient)
103
+ def thor_options_to_optparse
104
+ flags = []
105
+ %i[color progress debug interactive].each do |option|
106
+ if options[option] then flags << "--#{option}"
107
+ else flags << "--no-#{option}"
108
+ end
109
+ end
110
+ flags
111
+ end
112
+
84
113
  def run_autoproj_cli(filename, classname, report_options, *args, tool_failure_mode: :exit_silent, **extra_options)
85
114
  require "autoproj/cli/#{filename}"
86
115
  if Autobuild::Subprocess.transparent_mode = options[:tool]
@@ -118,9 +147,9 @@ def bootstrap(*args)
118
147
  if !File.directory?(File.join(Dir.pwd, '.autoproj'))
119
148
  require 'autoproj/ops/install'
120
149
  ops = Autoproj::Ops::Install.new(Dir.pwd)
121
- ops.parse_options(args)
150
+ bootstrap_options = ops.parse_options(thor_options_to_optparse + args)
122
151
  ops.run
123
- exec Gem.ruby, $0, 'bootstrap', *args
152
+ exec Gem.ruby, $0, 'bootstrap', *bootstrap_options
124
153
  end
125
154
  run_autoproj_cli(:bootstrap, :Bootstrap, Hash[], *args)
126
155
  end
@@ -243,6 +272,8 @@ def update(*packages)
243
272
  desc: "act as a build tool, transparently passing the subcommand's outputs to STDOUT"
244
273
  option :confirm, type: :boolean, default: nil,
245
274
  desc: '--force and --rebuild will ask confirmation if applied to the whole workspace. Use --no-confirm to disable this confirmation'
275
+ option :not, type: :array, default: nil,
276
+ desc: 'do not build the packages listed'
246
277
  def build(*packages)
247
278
  report_options = Hash[silent: false, on_package_failures: default_report_on_package_failures]
248
279
  if options[:auto_exclude]
@@ -351,6 +382,13 @@ def osdeps(*packages)
351
382
  run_autoproj_cli(:osdeps, :OSDeps, Hash[silent: options[:system_info]], *packages)
352
383
  end
353
384
 
385
+ desc 'version', 'display the current version of autobuild and optionally with dependencies'
386
+ option :deps, type: :boolean, default: false, banner:'',
387
+ desc: "controls whether to list dependant gems as well"
388
+ def version(*args)
389
+ run_autoproj_cli(:version, :Version, Hash[], *args)
390
+ end
391
+
354
392
  desc 'versions [PACKAGES]', 'generate a version file for the given packages, or all packages if none are given'
355
393
  option :config, type: :boolean, default: nil, banner: '',
356
394
  desc: "controls whether the package sets should be versioned as well",
@@ -373,6 +411,8 @@ def osdeps(*packages)
373
411
  desc: 'whether we should access the remote server to verify that the snapshotted state is present'
374
412
  option :save, type: :string,
375
413
  desc: 'save to the given file instead of displaying it on the standard output'
414
+ option :fingerprint, type: :boolean, default: false,
415
+ desc: 'calculate unique fingerprint for each package'
376
416
  def versions(*packages)
377
417
  run_autoproj_cli(:versions, :Versions, Hash[], *packages, deps: true)
378
418
  end
@@ -500,12 +540,7 @@ def query(query_string = nil)
500
540
  def install_stage2(root_dir, *vars)
501
541
  require 'autoproj/ops/install'
502
542
  ops = Autoproj::Ops::Install.new(root_dir)
503
- if options[:color] then ops.autoproj_options << "--color"
504
- else ops.autoproj_options << "--no-color"
505
- end
506
- if options[:progress] then ops.autoproj_options << "--progress"
507
- else ops.autoproj_options << "--no-progress"
508
- end
543
+ ops.parse_options(thor_options_to_optparse)
509
544
  ops.stage2(*vars)
510
545
  end
511
546
 
@@ -64,6 +64,8 @@ def list(*packages)
64
64
  desc: 'do not stop on build or checkout errors'
65
65
  option :deps, type: :boolean, default: false,
66
66
  desc: 'controls whether to execute the tests of the dependencies of the packages given on the command line (the default is not)'
67
+ option :parallel, aliases: :p, type: :numeric,
68
+ desc: 'maximum number of parallel jobs'
67
69
  option :fail, type: :boolean, default: true,
68
70
  desc: 'return with a nonzero exit code if the test does not pass'
69
71
  option :coverage, type: :boolean, default: false,
@@ -46,7 +46,11 @@ def list(user_selection, options = {})
46
46
  lines = []
47
47
  resolved_selection.each do |pkg_name|
48
48
  pkg = ws.manifest.find_package_definition(pkg_name).autobuild
49
- lines << [pkg.name, pkg.test_utility.enabled?, pkg.test_utility.available?]
49
+ lines << [
50
+ pkg.name,
51
+ pkg.test_utility.enabled?,
52
+ pkg.test_utility.available?
53
+ ]
50
54
  end
51
55
  lines = lines.sort_by { |name, _| name }
52
56
  w = lines.map { |name, _| name.length }.max
@@ -57,14 +61,33 @@ def list(user_selection, options = {})
57
61
  end
58
62
  end
59
63
 
60
- def run(user_selection, deps: true)
64
+ def run(user_selection, options = {})
65
+ options[:parallel] ||= ws.config.parallel_build_level
61
66
  initialize_and_load
62
- packages, =
63
- finalize_setup(user_selection, recursive: user_selection.empty? || deps)
67
+
68
+ packages, _, resolved_selection = finalize_setup(
69
+ user_selection,
70
+ recursive: user_selection.empty? || options[:deps]
71
+ )
72
+
73
+ validate_user_selection(user_selection, resolved_selection)
74
+ if packages.empty?
75
+ raise CLIInvalidArguments, "autoproj: the provided package "\
76
+ "is not selected for build"
77
+ end
78
+
64
79
  packages.each do |pkg|
65
- ws.manifest.find_autobuild_package(pkg).disable_phases('import', 'prepare', 'install')
80
+ ws.manifest.find_autobuild_package(pkg).disable_phases(
81
+ 'import', 'prepare', 'install'
82
+ )
66
83
  end
67
- Autobuild.apply(packages, 'autoproj-test', ['test'])
84
+
85
+ Autobuild.apply(
86
+ packages,
87
+ 'autoproj-test',
88
+ ['test'],
89
+ parallel: options[:parallel]
90
+ )
68
91
  end
69
92
  end
70
93
  end
@@ -0,0 +1,52 @@
1
+ require 'autoproj/cli/base'
2
+ require 'rubygems'
3
+
4
+ module Autoproj
5
+ module CLI
6
+ class Version < Base
7
+ # List the version of autoproj and optionally include
8
+ # the installed dependencies with information about the requirement
9
+ # and the actual used version
10
+ def run(args, options = Hash.new)
11
+ puts "autoproj version: #{Autoproj::VERSION}"
12
+ return unless options[:deps]
13
+
14
+ dependency = Gem::Deprecate.skip_during do
15
+ Gem::Dependency.new "autoproj", Autoproj::VERSION
16
+ end
17
+ autoproj_spec = dependency.matching_specs
18
+ return if autoproj_spec.empty?
19
+
20
+ installed_deps = collect_dependencies(dependency)
21
+ puts " specified dependencies:"
22
+ autoproj_spec.first.dependencies.each do |dep|
23
+ puts " #{dep}: #{installed_deps[dep.name] || 'n/a'}"
24
+ installed_deps.delete(dep.name)
25
+ end
26
+ puts " implicit dependencies:"
27
+ installed_deps.keys.sort.each do |name|
28
+ puts " #{name}: #{installed_deps[name]}" unless name == "autoproj"
29
+ end
30
+ end
31
+
32
+ # Collect the dependencies of a given dependency
33
+ # @param [Gem::Dependency] dependency a gem depencency
34
+ # @param [Array<Gem::Dependency] list of already known dependencies
35
+ #
36
+ # @return [Array<Gem::Dependency>] all known dependencies
37
+ def collect_dependencies(dependency, known_dependencies: {})
38
+ dep_spec = dependency.matching_specs
39
+ return known_dependencies if dep_spec.empty?
40
+
41
+ dep_spec = dep_spec.first
42
+ known_dependencies[dep_spec.name] = dep_spec.version
43
+ dep_spec.dependencies.each do |dep|
44
+ if !known_dependencies.has_key?(dep.name)
45
+ collect_dependencies(dep, known_dependencies: known_dependencies)
46
+ end
47
+ end
48
+ known_dependencies
49
+ end
50
+ end
51
+ end
52
+ end
@@ -51,7 +51,10 @@ def run(user_selection, options)
51
51
  versions += ops.snapshot_package_sets(nil, only_local: options[:only_local])
52
52
  end
53
53
  if snapshot_packages
54
- versions += ops.snapshot_packages(packages, nil, only_local: options[:only_local])
54
+ versions += ops.snapshot_packages(packages,
55
+ nil,
56
+ only_local: options[:only_local],
57
+ fingerprint: options[:fingerprint])
55
58
  end
56
59
 
57
60
  if output_file = options[:save]
@@ -51,7 +51,7 @@ def load_info_from_installation_manifest
51
51
  end
52
52
 
53
53
  def callback
54
- notifier.stop
54
+ Thread.new { notifier.stop }
55
55
  end
56
56
 
57
57
  def create_file_watcher(file)
@@ -107,6 +107,7 @@ def cleanup_notifier
107
107
 
108
108
  def assert_watchers_available
109
109
  return if RbConfig::CONFIG['target_os'] =~ /linux/
110
+
110
111
  puts 'error: Workspace watching not available on this platform'
111
112
  exit 1
112
113
  end
@@ -22,6 +22,9 @@ class Configuration
22
22
  attr_reader :displayed_options
23
23
  # The path to the underlying configuration file
24
24
  attr_reader :path
25
+ # Whether the configuration should be performed interactively
26
+ # or not
27
+ attr_accessor :interactive
25
28
 
26
29
  def initialize(path = nil)
27
30
  @config = Hash.new
@@ -43,16 +46,24 @@ def reset_modified
43
46
  @modified = false
44
47
  end
45
48
 
46
- # Deletes the current value for an option
49
+ # Deletes the current configuration for all options or the one specified
47
50
  #
48
- # The user will be asked for a new value next time the option is needed
51
+ # The user will be asked for a new value next time one of the reset
52
+ # options is needed
49
53
  #
50
- # @param [String] the option name
54
+ # @param [String] name the option name (or by default nil to reset all
55
+ # options)
51
56
  # @return the deleted value
52
- def reset(name)
53
- @modified ||= config.has_key?(name)
54
- config.delete(name)
55
- overrides.delete(name)
57
+ def reset(name = nil)
58
+ if name
59
+ @modified ||= config.has_key?(name)
60
+ config.delete(name)
61
+ overrides.delete(name)
62
+ else
63
+ @config.clear
64
+ @overrides.clear
65
+ @modified = true
66
+ end
56
67
  end
57
68
 
58
69
  # Sets a configuration option
@@ -177,10 +188,18 @@ def configure(option_name)
177
188
  if current_value = config[option_name]
178
189
  current_value = current_value.first
179
190
  end
180
- value = opt.ask(current_value)
191
+ is_default = false
192
+ if interactive?
193
+ value = opt.ask(current_value, nil)
194
+ else
195
+ value, is_default = opt.ensure_value(current_value)
196
+ Autoproj.info " using: #{value} (noninteractive mode)"
197
+ end
181
198
  @modified = true
182
- config[option_name] = [value, true]
183
- displayed_options[option_name] = value
199
+ if !is_default
200
+ config[option_name] = [value, true]
201
+ displayed_options[option_name] = value
202
+ end
184
203
  value
185
204
  else
186
205
  raise ConfigError.new, "undeclared option '#{option_name}'"
@@ -440,6 +459,21 @@ def randomize_layout=(value)
440
459
  set('randomize_layout', value, true)
441
460
  end
442
461
 
462
+ # Returns true if the configuration should be performed interactively
463
+ #
464
+ # @return [Boolean]
465
+ # @see interactive=
466
+ def interactive?
467
+ if !interactive.nil?
468
+ return interactive
469
+ elsif ENV['AUTOPROJ_NONINTERACTIVE'] == '1'
470
+ return false
471
+ elsif has_value_for?("interactive")
472
+ return get('interactive')
473
+ end
474
+ true
475
+ end
476
+
443
477
  DEFAULT_UTILITY_SETUP = Hash[
444
478
  'doc' => true,
445
479
  'test' => false]
@@ -545,7 +579,11 @@ def prefer_indep_over_os_packages?
545
579
  def to_hash
546
580
  result = Hash.new
547
581
  @config.each do |key, (value, _)|
548
- result[key] = value
582
+ if declared_options.include?(key)
583
+ result[key] = declared_options[key].ensure_value(value)
584
+ else
585
+ result[key] = value
586
+ end
549
587
  end
550
588
  overrides.each do |key, value|
551
589
  result[key] = value