autoproj 2.9.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
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