autoproj 2.0.0.b5 → 2.0.0.b6

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,15 +11,14 @@ module Autoproj
11
11
  raise ConfigError, "you cannot run autoproj switch-config from autoproj's configuration directory or one of its subdirectories"
12
12
  end
13
13
 
14
+ ws.load_config
15
+
14
16
  # We must switch to the root dir first, as it is required by the
15
17
  # configuration switch code. This is acceptable as long as we
16
18
  # quit just after the switch
17
19
  switcher = Ops::MainConfigSwitcher.new(ws)
18
20
  if switcher.switch_config(*args)
19
- manifest = Manifest.load(File.join(ws.config_dir, 'manifest'))
20
- update = Ops::Configuration.new(ws, ws.loader)
21
- update.update_configuration
22
- ws.config.save
21
+ CLI::Main.start(['update', '--config'])
23
22
  end
24
23
  end
25
24
  end
@@ -21,10 +21,20 @@ module Autoproj
21
21
  packages = ['.']
22
22
  end
23
23
 
24
+ if options[:force_reset]
25
+ options[:reset] = :force
26
+ end
27
+
24
28
  if options[:autoproj].nil?
25
29
  options[:autoproj] = packages.empty?
26
30
  end
27
31
 
32
+ if mainline = options[:mainline]
33
+ if mainline == 'mainline' || mainline == 'true'
34
+ options[:mainline] = true
35
+ end
36
+ end
37
+
28
38
  return packages, options
29
39
  end
30
40
 
@@ -32,15 +42,12 @@ module Autoproj
32
42
  selected_packages, config_selected =
33
43
  normalize_command_line_package_selection(selected_packages)
34
44
 
35
- if options[:apply_post_install].nil?
36
- options[:apply_post_install] = true
37
- end
38
-
39
45
  if options[:config].nil?
40
46
  options[:config] = selected_packages.empty? || config_selected
41
47
  end
42
48
 
43
49
  ws.setup
50
+ parallel = options[:parallel] || ws.config.parallel_import_level
44
51
 
45
52
  # Do that AFTER we have properly setup ws.osdeps as to avoid
46
53
  # unnecessarily redetecting the operating system
@@ -56,11 +63,14 @@ module Autoproj
56
63
  end
57
64
 
58
65
  ws.load_package_sets(
66
+ mainline: options[:mainline],
59
67
  only_local: options[:local],
60
68
  checkout_only: !options[:config] || options[:checkout_only],
61
- ignore_errors: options[:keep_going])
62
- if selected_packages.empty? && config_selected
63
- return
69
+ reset: options[:reset],
70
+ ignore_errors: options[:keep_going],
71
+ retry_count: options[:retry_count])
72
+ if selected_packages.empty? && (config_selected || options[:config]) && !options[:all]
73
+ return [], [], true
64
74
  end
65
75
 
66
76
  ws.setup_all_package_directories
@@ -112,12 +122,11 @@ module Autoproj
112
122
  only_local: options[:local],
113
123
  reset: options[:reset],
114
124
  recursive: options[:deps],
115
- ignore_errors: options[:keep_going])
125
+ ignore_errors: options[:keep_going],
126
+ parallel: parallel,
127
+ retry_count: options[:retry_count])
116
128
 
117
129
  ws.finalize_setup
118
- if options[:apply_post_install]
119
- ws.apply_post_install
120
- end
121
130
  ws.export_installation_manifest
122
131
 
123
132
  if options[:osdeps] && !osdep_packages.empty?
@@ -14,11 +14,11 @@ module Autoproj
14
14
 
15
15
  def validate_options(packages, options = Hash.new)
16
16
  packages, options = super
17
- if options[:save].nil?
17
+ if !options[:save].nil?
18
18
  options[:save] = case options[:save]
19
19
  when '.'
20
20
  nil
21
- when true
21
+ when 'save'
22
22
  default_versions_file
23
23
  else
24
24
  options[:save].to_str
@@ -29,21 +29,23 @@ module Autoproj
29
29
 
30
30
  def run(user_selection, options)
31
31
  initialize_and_load
32
- packages, *, config_selected =
32
+ user_selection, config_selected =
33
+ normalize_command_line_package_selection(user_selection)
34
+ packages, * =
33
35
  finalize_setup(user_selection,
34
36
  ignore_non_imported_packages: true)
35
37
 
36
- if (config_selected || user_selection.empty?) && (options[:package_sets] != false)
37
- options[:package_sets] = true
38
- end
39
-
40
- ops = Ops::Snapshot.new(ws.manifest, keep_going: options[:keep_going])
38
+
39
+ ops = Ops::Snapshot.new(ws.manifest, ignore_errors: options[:keep_going])
41
40
 
42
41
  versions = Array.new
43
- if options[:package_sets]
42
+ if config_selected || (options[:config] != false) || user_selection.empty?
44
43
  versions += ops.snapshot_package_sets
45
44
  end
46
- versions += ops.snapshot_packages(packages)
45
+ if (!config_selected && !options[:config]) || !user_selection.empty?
46
+ versions += ops.snapshot_packages(packages)
47
+ end
48
+
47
49
  if output_file = options[:save]
48
50
  ops.save_versions(versions, output_file, replace: options[:replace])
49
51
  else
@@ -189,6 +189,14 @@ module Autoproj
189
189
  set('import_log_enabled', !!value)
190
190
  end
191
191
 
192
+ def parallel_import_level
193
+ get('parallel_import_level', 10)
194
+ end
195
+
196
+ def parallel_import_level=(level)
197
+ set('parallel_import_level', level)
198
+ end
199
+
192
200
  def ruby_executable
193
201
  if path = get('ruby_executable', nil)
194
202
  path
@@ -1,105 +1,3 @@
1
- module Autoproj
2
- # Adds the relevant options to handle a gitorious server
3
- # What this does is ask the user how he would like to access the gitorious
4
- # server. Then, it sets
5
- #
6
- # #{name}_ROOT to be the base URL for pulling
7
- # #{name}_PUSH_ROOT to be the corresponding ssh-based URL for pushing
8
- #
9
- # For instance, with
10
- #
11
- # handle_gitorious_server "GITORIOUS", "gitorious.org"
12
- #
13
- # the URLs for the rtt repository in the orocos-toolchain gitorious project would
14
- # be defined in the source.yml files as
15
- #
16
- # ${GITORIOUS_ROOT}/orocos-toolchain/rtt.git
17
- # ${GITORIOUS_PUSH_ROOT}/orocos-toolchain/rtt.git
18
- #
19
- # Since it seems that the http method for gitorious servers is more stable, a
20
- # fallback importer is set up that falls back to using http for pulling as soon
21
- # as import failed
22
- def self.gitorious_server_configuration(name, base_url, options = Hash.new)
23
- options = Kernel.validate_options options,
24
- :git_url => "git://#{base_url}",
25
- :http_url => "https://git.#{base_url}",
26
- :ssh_url => "git@#{base_url}:",
27
- :fallback_to_http => true,
28
- :default => 'http,ssh',
29
- :disabled_methods => [],
30
- config: Autoproj.config
31
-
32
- config = options.delete(:config)
33
- disabled_methods = Array(options[:disabled_methods])
34
-
35
- access_methods = Hash[
36
- 'git' => 'git,ssh',
37
- 'ssh' => 'ssh,ssh',
38
- 'http' => 'http,http']
39
-
40
- gitorious_long_doc = [
41
- "How should I interact with #{base_url} (#{(access_methods.keys - disabled_methods).sort.join(", ")})",
42
- "If you give two values, comma-separated, the first one will be",
43
- "used for pulling and the second one for pushing"]
44
-
45
- validator = lambda do |value|
46
- if value =~ /,/
47
- value.split(',').each do |method|
48
- if !access_methods.has_key?(method)
49
- raise Autoproj::InputError, "#{method} is not a known access method"
50
- elsif disabled_methods.include?(method)
51
- raise Autoproj::InputError, "#{method} is disabled on #{base_url}"
52
- end
53
- end
54
- elsif !access_methods.has_key?(value)
55
- raise Autoproj::InputError, "#{value} is not a known access method"
56
- elsif disabled_methods.include?(value)
57
- raise Autoproj::InputError, "#{value} is disabled on #{base_url}"
58
- end
59
-
60
- value
61
- end
62
-
63
- config.declare name, 'string',
64
- :default => options[:default],
65
- :doc => gitorious_long_doc, &validator
66
-
67
- access_mode = config.get(name)
68
- begin
69
- validator[access_mode]
70
- rescue Autoproj::InputError => e
71
- Autoproj.warn e.message
72
- config.reset(name)
73
- access_mode = config.get(name)
74
- end
75
- access_mode = access_methods[access_mode] || access_mode
76
- pull, push = access_mode.split(',')
77
- [[pull, "_ROOT"], [push, "_PUSH_ROOT"]].each do |method, var_suffix|
78
- url = if method == "git" then options[:git_url]
79
- elsif method == "http" then options[:http_url]
80
- elsif method == "ssh" then options[:ssh_url]
81
- end
82
- config.set("#{name}#{var_suffix}", url)
83
- end
84
-
85
- Autoproj.add_source_handler name.downcase do |url, vcs_options|
86
- if url !~ /\.git$/
87
- url += ".git"
88
- end
89
- if url !~ /^\//
90
- url = "/#{url}"
91
- end
92
- pull_base_url = config.get("#{name}_ROOT")
93
- push_base_url = config.get("#{name}_PUSH_ROOT")
94
- Hash[type: 'git',
95
- url: "#{pull_base_url}#{url}",
96
- push_to: "#{push_base_url}#{url}",
97
- retry_count: 10,
98
- repository_id: "#{name.downcase}:#{url}"].merge(vcs_options)
99
- end
100
- end
101
- end
102
-
103
- Autoproj.gitorious_server_configuration('GITORIOUS', 'gitorious.org', :default => 'http,ssh', :disabled_methods => 'git')
104
- Autoproj.gitorious_server_configuration('GITHUB', 'github.com', :http_url => 'https://github.com', :default => 'http,ssh')
105
-
1
+ Autoproj.warn "gitorious_server_configuration is deprecated, use require 'git_server_configuration' and Autoproj.git_server_configuration instead"
2
+ Autoproj.warn "note that the method call interface has not changed, you just have to change the name(s)"
3
+ require 'autoproj/git_server_configuration'
@@ -66,7 +66,7 @@ module Autoproj
66
66
  end
67
67
 
68
68
  def each(&block)
69
- packages.each(&block)
69
+ packages.each_value(&block)
70
70
  end
71
71
 
72
72
  def [](name)
@@ -56,7 +56,9 @@ module Autoproj
56
56
  options = Kernel.validate_options options,
57
57
  only_local: false,
58
58
  checkout_only: !Autobuild.do_update,
59
- ignore_errors: false
59
+ ignore_errors: false,
60
+ reset: false,
61
+ retry_count: nil
60
62
 
61
63
  fake_package = Tools.create_autobuild_package(vcs, name, into)
62
64
  if update_from
@@ -79,6 +81,9 @@ module Autoproj
79
81
  fake_package.update = false
80
82
  end
81
83
  end
84
+ if retry_count = options.delete(:retry_count)
85
+ fake_package.importer.retry_count = retry_count
86
+ end
82
87
  fake_package.import(options)
83
88
 
84
89
  rescue Autobuild::ConfigException => e
@@ -96,7 +101,9 @@ module Autoproj
96
101
  options = validate_options options,
97
102
  only_local: false,
98
103
  checkout_only: !Autobuild.do_update,
99
- ignore_errors: false
104
+ ignore_errors: false,
105
+ reset: false,
106
+ retry_count: nil
100
107
 
101
108
  update_configuration_repository(
102
109
  ws.manifest.vcs,
@@ -118,7 +125,9 @@ module Autoproj
118
125
  options = validate_options options,
119
126
  only_local: false,
120
127
  checkout_only: !Autobuild.do_update,
121
- ignore_errors: false
128
+ ignore_errors: false,
129
+ reset: false,
130
+ retry_count: nil
122
131
 
123
132
  name = PackageSet.name_of(ws.manifest, vcs)
124
133
  raw_local_dir = PackageSet.raw_local_dir_of(vcs)
@@ -208,7 +217,9 @@ module Autoproj
208
217
  options = validate_options options,
209
218
  only_local: false,
210
219
  checkout_only: !Autobuild.do_update,
211
- ignore_errors: false
220
+ ignore_errors: false,
221
+ reset: false,
222
+ retry_count: nil
212
223
 
213
224
  package_sets = [root_pkg_set]
214
225
  by_repository_id = Hash.new
@@ -339,7 +350,9 @@ module Autoproj
339
350
  options = validate_options options,
340
351
  only_local: false,
341
352
  checkout_only: true,
342
- ignore_errors: false
353
+ ignore_errors: false,
354
+ reset: false,
355
+ retry_count: nil
343
356
  update_configuration(options)
344
357
  end
345
358
 
@@ -350,7 +363,9 @@ module Autoproj
350
363
  options = validate_options options,
351
364
  only_local: false,
352
365
  checkout_only: !Autobuild.do_update,
353
- ignore_errors: false
366
+ ignore_errors: false,
367
+ reset: false,
368
+ retry_count: nil
354
369
 
355
370
  # Load the installation's manifest a first time, to check if we should
356
371
  # update it ... We assume that the OS dependencies for this VCS is already
@@ -63,12 +63,65 @@ module Autoproj
63
63
  new_packages
64
64
  end
65
65
 
66
+ def pre_package_import(selection, manifest, pkg, reverse_dependencies)
67
+ # Try to auto-exclude the package early. If the autobuild file
68
+ # contained some information that allows us to exclude it now,
69
+ # then let's just do it
70
+ import_next_step(pkg, reverse_dependencies)
71
+ if manifest.excluded?(pkg.name)
72
+ selection.filter_excluded_and_ignored_packages(manifest)
73
+ false
74
+ elsif manifest.ignored?(pkg.name)
75
+ false
76
+ elsif !pkg.importer && !File.directory?(pkg.srcdir)
77
+ raise ConfigError.new, "#{pkg.name} has no VCS, but is not checked out in #{pkg.srcdir}"
78
+ elsif pkg.importer
79
+ true
80
+ end
81
+ end
82
+
83
+ def post_package_import(selection, manifest, pkg, reverse_dependencies)
84
+ Rake::Task["#{pkg.name}-import"].instance_variable_set(:@already_invoked, true)
85
+ manifest.load_package_manifest(pkg.name)
86
+
87
+ # The package setup mechanisms might have added an exclusion
88
+ # on this package. Handle this.
89
+ if manifest.excluded?(pkg.name)
90
+ mark_exclusion_along_revdeps(pkg.name, reverse_dependencies)
91
+ # Run a filter now, to have errors as early as possible
92
+ selection.filter_excluded_and_ignored_packages(manifest)
93
+ # Delete this package from the current_packages set
94
+ false
95
+ elsif manifest.ignored?(pkg.name)
96
+ false
97
+ else
98
+ Autoproj.each_post_import_block(pkg) do |block|
99
+ block.call(pkg)
100
+ end
101
+ import_next_step(pkg, reverse_dependencies)
102
+ end
103
+ end
104
+
105
+ class ImportFailed < RuntimeError; end
106
+
66
107
  # Import all packages from the given selection, and their
67
108
  # dependencies
68
109
  def import_selected_packages(selection, updated_packages, options = Hash.new)
110
+ all_processed_packages = Set.new
111
+
112
+ parallel_options, options = Kernel.filter_options options,
113
+ parallel: ws.config.parallel_import_level
114
+
115
+ # This is used in the ensure block, initialize as early as
116
+ # possible
117
+ executor = Concurrent::FixedThreadPool.new(parallel_options[:parallel], max_length: 0)
118
+
69
119
  options, import_options = Kernel.filter_options options,
70
- recursive: true
120
+ recursive: true,
121
+ retry_count: nil
71
122
 
123
+ ignore_errors = options[:ignore_errors]
124
+ retry_count = options[:retry_count]
72
125
  manifest = ws.manifest
73
126
 
74
127
  selected_packages = selection.each_source_package_name.map do |pkg_name|
@@ -83,72 +136,107 @@ module Autoproj
83
136
  # package exclusion (and that does not affect optional dependencies)
84
137
  reverse_dependencies = Hash.new { |h, k| h[k] = Set.new }
85
138
 
139
+ completion_queue = Queue.new
140
+ pending_packages = Set.new
86
141
  # The set of all packages that are currently selected by +selection+
87
142
  all_processed_packages = Set.new
143
+ interactive_imports = Array.new
88
144
  package_queue = selected_packages.to_a.sort_by(&:name)
89
- while !package_queue.empty?
90
- pkg = package_queue.shift
91
- # Remove packages that have already been processed
92
- next if all_processed_packages.include?(pkg.name)
93
- all_processed_packages << pkg.name
94
-
95
- # Try to auto-exclude the package early. If the autobuild file
96
- # contained some information that allows us to exclude it now,
97
- # then let's just do it
98
- import_next_step(pkg, reverse_dependencies)
99
- if manifest.excluded?(pkg.name)
100
- selection.filter_excluded_and_ignored_packages(manifest)
101
- next
102
- elsif manifest.ignored?(pkg.name)
103
- next
104
- end
145
+ failures = Hash.new
146
+ while failures.empty? || ignore_errors
147
+ # Queue work for all packages in the queue
148
+ package_queue.each do |pkg|
149
+ # Remove packages that have already been processed
150
+ next if all_processed_packages.include?(pkg)
151
+ all_processed_packages << pkg
105
152
 
106
- # If the package has no importer, the source directory must
107
- # be there
108
- if !pkg.importer && !File.directory?(pkg.srcdir)
109
- raise ConfigError.new, "#{pkg.name} has no VCS, but is not checked out in #{pkg.srcdir}"
110
- end
153
+ if !pre_package_import(selection, manifest, pkg, reverse_dependencies)
154
+ next
155
+ elsif pkg.importer.interactive?
156
+ interactive_imports << pkg
157
+ next
158
+ end
111
159
 
112
- ## COMPLETELY BYPASS RAKE HERE
113
- # The reason is that the ordering of import/prepare between
114
- # packages is not important BUT the ordering of import vs.
115
- # prepare in one package IS important: prepare is the method
116
- # that takes into account dependencies.
117
- pkg.import(import_options)
118
- if pkg.updated?
119
- updated_packages << pkg.name
120
- end
121
- Rake::Task["#{pkg.name}-import"].instance_variable_set(:@already_invoked, true)
122
- manifest.load_package_manifest(pkg.name)
123
-
124
- # The package setup mechanisms might have added an exclusion
125
- # on this package. Handle this.
126
- if manifest.excluded?(pkg.name)
127
- mark_exclusion_along_revdeps(pkg.name, reverse_dependencies)
128
- # Run a filter now, to have errors as early as possible
129
- selection.filter_excluded_and_ignored_packages(manifest)
130
- # Delete this package from the current_packages set
131
- next
132
- elsif manifest.ignored?(pkg.name)
133
- next
160
+ pending_packages << pkg
161
+ import_future = Concurrent::Future.new(executor: executor, args: [pkg]) do |import_pkg|
162
+ ## COMPLETELY BYPASS RAKE HERE
163
+ # The reason is that the ordering of import/prepare between
164
+ # packages is not important BUT the ordering of import vs.
165
+ # prepare in one package IS important: prepare is the method
166
+ # that takes into account dependencies.
167
+ if retry_count
168
+ import_pkg.importer.retry_count = retry_count
169
+ end
170
+ import_pkg.import(import_options.merge(allow_interactive: false))
171
+ end
172
+ import_future.add_observer do |time, result, reason|
173
+ completion_queue << [pkg, time, result, reason]
174
+ end
175
+ import_future.execute
134
176
  end
177
+ package_queue.clear
135
178
 
136
- Autoproj.each_post_import_block(pkg) do |block|
137
- block.call(pkg)
179
+ if completion_queue.empty? && pending_packages.empty?
180
+ # We've nothing to process anymore ... process
181
+ # interactive imports if there are some. Otherwise,
182
+ # we're done
183
+ if interactive_imports.empty?
184
+ return all_processed_packages
185
+ else
186
+ interactive_imports.each do |pkg|
187
+ begin
188
+ result = pkg.import(import_options.merge(allow_interactive: true))
189
+ rescue Exception => reason
190
+ end
191
+ completion_queue << [pkg, Time.now, result, reason]
192
+ end
193
+ interactive_imports.clear
194
+ end
138
195
  end
139
196
 
140
- new_packages = import_next_step(pkg, reverse_dependencies)
141
-
142
- # Excluded dependencies might have caused the package to be
143
- # excluded as well ... do not add any dependency to the
144
- # processing queue if it is the case
145
- if manifest.excluded?(pkg.name)
146
- selection.filter_excluded_and_ignored_packages(manifest)
147
- elsif options[:recursive]
148
- package_queue.concat(new_packages.sort_by(&:name))
197
+ # And wait one to finish
198
+ pkg, time, result, reason = completion_queue.pop
199
+ pending_packages.delete(pkg)
200
+ if reason
201
+ if reason.kind_of?(Autobuild::InteractionRequired)
202
+ interactive_imports << pkg
203
+ else
204
+ # One importer failed... terminate
205
+ Autoproj.error "import of #{pkg.name} failed"
206
+ if !reason.kind_of?(Interrupt)
207
+ Autoproj.error "#{reason}"
208
+ end
209
+ failures[pkg] = reason
210
+ end
211
+ else
212
+ if new_packages = post_package_import(selection, manifest, pkg, reverse_dependencies)
213
+ # Excluded dependencies might have caused the package to be
214
+ # excluded as well ... do not add any dependency to the
215
+ # processing queue if it is the case
216
+ if manifest.excluded?(pkg.name)
217
+ selection.filter_excluded_and_ignored_packages(manifest)
218
+ elsif options[:recursive]
219
+ package_queue = new_packages.sort_by(&:name)
220
+ end
221
+ end
149
222
  end
150
223
  end
224
+
225
+ if !failures.empty?
226
+ raise ImportFailed, "import of #{failures.size} packages failed: #{failures.keys.map(&:name).sort.join(", ")}"
227
+ end
228
+
151
229
  all_processed_packages
230
+
231
+ ensure
232
+ if failures && !failures.empty? && !ignore_errors
233
+ Autoproj.error "waiting for pending import jobs to finish"
234
+ end
235
+ if executor
236
+ executor.shutdown
237
+ executor.wait_for_termination
238
+ end
239
+ updated_packages.concat(all_processed_packages.find_all(&:updated?).map(&:name))
152
240
  end
153
241
 
154
242
  def finalize_package_load(processed_packages)
@@ -164,7 +252,7 @@ module Autoproj
164
252
  next if manifest.ignored?(pkg_name) || manifest.excluded?(pkg_name)
165
253
 
166
254
  pkg = manifest.find_autobuild_package(pkg_name)
167
- if !processed_packages.include?(pkg.name)
255
+ if !processed_packages.include?(pkg)
168
256
  manifest.load_package_manifest(pkg.name)
169
257
  Autoproj.each_post_import_block(pkg) do |block|
170
258
  block.call(pkg)
@@ -187,6 +275,9 @@ module Autoproj
187
275
  end
188
276
 
189
277
  def import_packages(selection, options = Hash.new)
278
+ # Used in the ensure block, initialize as soon as possible
279
+ updated_packages = Array.new
280
+
190
281
  options, import_options = Kernel.filter_options options,
191
282
  warn_about_ignored_packages: true,
192
283
  warn_about_excluded_packages: true,
@@ -194,13 +285,17 @@ module Autoproj
194
285
 
195
286
  manifest = ws.manifest
196
287
 
197
- updated_packages = Array.new
198
288
  all_processed_packages = import_selected_packages(
199
289
  selection, updated_packages, import_options.merge(recursive: options[:recursive]))
200
290
  finalize_package_load(all_processed_packages)
201
291
 
202
292
  all_enabled_osdeps = selection.each_osdep_package_name.to_set
203
- all_enabled_sources = all_processed_packages
293
+ all_enabled_sources = all_processed_packages.map(&:name)
294
+ if options[:recursive]
295
+ all_processed_packages.each do |pkg|
296
+ all_enabled_osdeps.merge(pkg.os_packages)
297
+ end
298
+ end
204
299
 
205
300
  if options[:warn_about_excluded_packages]
206
301
  selection.exclusions.each do |sel, pkg_names|
@@ -225,7 +320,7 @@ module Autoproj
225
320
  if $!
226
321
  " (#{$!.message.split("\n").first})"
227
322
  end
228
- ops = Ops::Snapshot.new(ws.manifest, keep_going: true)
323
+ ops = Ops::Snapshot.new(ws.manifest, ignore_errors: true)
229
324
  ops.update_package_import_state(
230
325
  "#{$0} #{ARGV.join(" ")}#{failure_message}",
231
326
  updated_packages)