autoproj 2.0.0.rc37 → 2.0.0.rc38

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -2
  3. data/Rakefile +1 -1
  4. data/bin/autoproj_bootstrap +34 -2
  5. data/bin/autoproj_bootstrap.in +4 -2
  6. data/bin/autoproj_install +34 -2
  7. data/bin/autoproj_install.in +4 -2
  8. data/lib/autoproj.rb +9 -2
  9. data/lib/autoproj/autobuild.rb +13 -742
  10. data/lib/autoproj/autobuild_extensions/archive_importer.rb +44 -0
  11. data/lib/autoproj/autobuild_extensions/dsl.rb +439 -0
  12. data/lib/autoproj/autobuild_extensions/git.rb +116 -0
  13. data/lib/autoproj/autobuild_extensions/package.rb +159 -0
  14. data/lib/autoproj/autobuild_extensions/svn.rb +11 -0
  15. data/lib/autoproj/cli/base.rb +17 -18
  16. data/lib/autoproj/cli/clean.rb +1 -2
  17. data/lib/autoproj/cli/envsh.rb +1 -2
  18. data/lib/autoproj/cli/inspection_tool.rb +12 -21
  19. data/lib/autoproj/cli/locate.rb +130 -73
  20. data/lib/autoproj/cli/main.rb +31 -5
  21. data/lib/autoproj/cli/main_plugin.rb +79 -0
  22. data/lib/autoproj/cli/main_test.rb +19 -5
  23. data/lib/autoproj/cli/osdeps.rb +1 -2
  24. data/lib/autoproj/cli/patcher.rb +21 -0
  25. data/lib/autoproj/cli/query.rb +34 -41
  26. data/lib/autoproj/cli/show.rb +121 -52
  27. data/lib/autoproj/cli/status.rb +4 -5
  28. data/lib/autoproj/cli/tag.rb +1 -1
  29. data/lib/autoproj/cli/test.rb +7 -6
  30. data/lib/autoproj/cli/update.rb +8 -22
  31. data/lib/autoproj/cli/versions.rb +1 -2
  32. data/lib/autoproj/configuration.rb +1 -1
  33. data/lib/autoproj/environment.rb +2 -7
  34. data/lib/autoproj/exceptions.rb +10 -8
  35. data/lib/autoproj/find_workspace.rb +46 -12
  36. data/lib/autoproj/installation_manifest.rb +34 -25
  37. data/lib/autoproj/local_package_set.rb +86 -0
  38. data/lib/autoproj/manifest.rb +448 -503
  39. data/lib/autoproj/metapackage.rb +31 -5
  40. data/lib/autoproj/ops/configuration.rb +46 -45
  41. data/lib/autoproj/ops/import.rb +150 -60
  42. data/lib/autoproj/ops/install.rb +25 -1
  43. data/lib/autoproj/ops/loader.rb +4 -1
  44. data/lib/autoproj/ops/main_config_switcher.rb +4 -4
  45. data/lib/autoproj/ops/snapshot.rb +4 -3
  46. data/lib/autoproj/os_package_installer.rb +105 -46
  47. data/lib/autoproj/os_package_resolver.rb +63 -36
  48. data/lib/autoproj/package_definition.rb +1 -0
  49. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +30 -27
  50. data/lib/autoproj/package_managers/bundler_manager.rb +64 -18
  51. data/lib/autoproj/package_managers/gem_manager.rb +4 -2
  52. data/lib/autoproj/package_managers/manager.rb +26 -7
  53. data/lib/autoproj/package_managers/shell_script_manager.rb +4 -4
  54. data/lib/autoproj/package_managers/zypper_manager.rb +1 -1
  55. data/lib/autoproj/package_manifest.rb +154 -137
  56. data/lib/autoproj/package_selection.rb +16 -2
  57. data/lib/autoproj/package_set.rb +352 -309
  58. data/lib/autoproj/query.rb +13 -1
  59. data/lib/autoproj/system.rb +2 -2
  60. data/lib/autoproj/test.rb +164 -11
  61. data/lib/autoproj/variable_expansion.rb +15 -42
  62. data/lib/autoproj/vcs_definition.rb +93 -76
  63. data/lib/autoproj/version.rb +1 -1
  64. data/lib/autoproj/workspace.rb +116 -80
  65. metadata +10 -2
@@ -54,6 +54,14 @@ class Query
54
54
  attr_predicate :use_dir_prefix?
55
55
  attr_predicate :partial?
56
56
 
57
+ class All
58
+ def match(pkg); true end
59
+ end
60
+
61
+ def self.all
62
+ All.new
63
+ end
64
+
57
65
  def initialize(fields, value, partial)
58
66
  @fields = fields
59
67
  @value = value
@@ -158,7 +166,11 @@ def self.parse_query(query)
158
166
  Query.parse(str)
159
167
  end
160
168
  end
161
- And.new(query)
169
+ if query.size == 1
170
+ query.first
171
+ else
172
+ And.new(query)
173
+ end
162
174
  end
163
175
 
164
176
  # Match object that combines multiple matches using a logical OR
@@ -112,8 +112,8 @@ def self.run_as_user(*args)
112
112
  end
113
113
  end
114
114
  # Run the provided command as root, using sudo to gain root access
115
- def self.run_as_root(*args)
116
- if !system(Autobuild.tool_in_path('sudo'), *args)
115
+ def self.run_as_root(*args, env: self.workspace.env)
116
+ if !system(Autobuild.tool_in_path('sudo', env: env), *args)
117
117
  raise "failed to run #{args.join(" ")} as root"
118
118
  end
119
119
  end
data/lib/autoproj/test.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  begin
5
5
  require 'simplecov'
6
6
  SimpleCov.start do
7
+ command_name 'master'
7
8
  add_filter "/test/"
8
9
  end
9
10
  rescue LoadError
@@ -18,6 +19,7 @@
18
19
  require 'minitest/autorun'
19
20
  require 'autoproj'
20
21
  require 'flexmock/minitest'
22
+ FlexMock.partials_are_based = true
21
23
  require 'minitest/spec'
22
24
 
23
25
  if ENV['TEST_ENABLE_PRY'] != '0'
@@ -40,15 +42,27 @@ module Autoproj
40
42
  # end
41
43
  #
42
44
  module SelfTest
45
+ # Define package managers for the next workspace created by {#ws_create}
46
+ #
47
+ # Use {#ws_define_package_manager}
48
+ #
49
+ # Two package managers called 'os' and 'os_indep' are always created,
50
+ # 'os' is used as the os package manager.
51
+ #
52
+ # @return [Hash<String,PackageManagers::Manager>]
53
+ attr_reader :ws_package_managers
54
+ # The workspace created by the last call to #ws_create
43
55
  attr_reader :ws
44
56
 
45
57
  def setup
58
+ FileUtils.rm_rf fixture_gem_home
46
59
  @gem_server_pid = nil
47
60
  @tmpdir = Array.new
48
- @ws = Workspace.new('/test/dir')
49
- ws.load_config
50
- Autoproj.workspace = ws
51
- FileUtils.rm_rf fixture_gem_home
61
+ @ws = nil
62
+ @ws_package_managers = Hash.new
63
+ Autobuild.logdir = make_tmpdir
64
+ ws_define_package_manager 'os'
65
+ ws_define_package_manager 'os_indep'
52
66
 
53
67
  super
54
68
  end
@@ -59,6 +73,7 @@ def teardown
59
73
  FileUtils.remove_entry_secure dir
60
74
  end
61
75
  Autobuild::Package.clear
76
+ Autoproj.silent = false
62
77
 
63
78
  if @gem_server_pid
64
79
  stop_gem_server
@@ -68,12 +83,11 @@ def teardown
68
83
  end
69
84
 
70
85
  def create_bootstrap
71
- dir = make_tmpdir
72
- require 'autoproj/ops/main_config_switcher'
73
- FileUtils.cp_r Ops::MainConfigSwitcher::MAIN_CONFIGURATION_TEMPLATE, File.join(dir, 'autoproj')
74
- FileUtils.mkdir_p File.join(dir, '.autoproj')
75
- FileUtils.touch File.join(dir, '.autoproj', 'config.yml')
76
- Workspace.new(dir)
86
+ ws_create
87
+ end
88
+
89
+ def skip_long_tests?
90
+ ENV['AUTOPROJ_SKIP_LONG_TESTS'] == '1'
77
91
  end
78
92
 
79
93
  def make_tmpdir
@@ -140,8 +154,13 @@ def invoke_test_script(name, *arguments,
140
154
  end
141
155
  result = nil
142
156
  stdout, stderr = capture_subprocess_io do
157
+ default_env = Hash[
158
+ 'TEST_COMMAND_NAME' => self.to_s.gsub(/[^\w]/, '_'),
159
+ 'PACKAGE_BASE_DIR' => package_base_dir,
160
+ 'RUBY' => Gem.ruby
161
+ ]
143
162
  result = Bundler.clean_system(
144
- Hash['PACKAGE_BASE_DIR' => package_base_dir, 'RUBY' => Gem.ruby].merge(env),
163
+ default_env.merge(env),
145
164
  script, *arguments, in: :close, **Hash[chdir: dir].merge(system_options))
146
165
  end
147
166
 
@@ -200,6 +219,16 @@ def stop_gem_server
200
219
  @gem_server_pid = nil
201
220
  end
202
221
 
222
+ def capture_deprecation_message(&block)
223
+ level = Autoproj.warn_deprecated_level
224
+ Autoproj.warn_deprecated_level = -1
225
+ capture_subprocess_io do
226
+ yield
227
+ end
228
+ ensure
229
+ Autoproj.warn_deprecated_level = level
230
+ end
231
+
203
232
  def find_bundled_gem_path(bundler, gem_name, gemfile)
204
233
  out_r, out_w = IO.pipe
205
234
  result = Bundler.clean_system(
@@ -217,6 +246,130 @@ def workspace_env(varname)
217
246
  stdout.chomp
218
247
  end
219
248
 
249
+ attr_reader :ws_os_package_resolver
250
+
251
+ def ws_define_package_manager(name, strict: false, call_while_empty: false)
252
+ manager = Class.new(PackageManagers::Manager)
253
+ manager.class_eval do
254
+ define_method(:strict?) { strict }
255
+ define_method(:call_while_empty?) { call_while_empty }
256
+ end
257
+ manager = flexmock(manager),
258
+ ws_package_managers[name] = manager
259
+ end
260
+
261
+ def ws_create_os_package_resolver
262
+ @ws_os_package_resolver = OSPackageResolver.new(
263
+ operating_system: [['test_os_family'], ['test_os_version']],
264
+ package_managers: ws_package_managers.keys,
265
+ os_package_manager: 'os')
266
+ end
267
+
268
+ def ws_create
269
+ dir = make_tmpdir
270
+ require 'autoproj/ops/main_config_switcher'
271
+ FileUtils.cp_r Ops::MainConfigSwitcher::MAIN_CONFIGURATION_TEMPLATE, File.join(dir, 'autoproj')
272
+ FileUtils.mkdir_p File.join(dir, '.autoproj')
273
+
274
+ ws_create_os_package_resolver
275
+ @ws = Workspace.new(
276
+ dir, os_package_resolver: ws_os_package_resolver,
277
+ package_managers: ws_package_managers)
278
+ ws.config.set 'osdeps_mode', 'all'
279
+ ws.config.set 'gems_install_path', File.join(dir, 'gems')
280
+ ws.config.save
281
+ ws.prefix_dir = make_tmpdir
282
+ ws
283
+ end
284
+
285
+ def ws_clear_layout
286
+ ws.manifest.clear_layout
287
+ end
288
+
289
+ def ws_define_package_set(name, vcs = VCSDefinition.from_raw(type: 'none'), **options)
290
+ package_set = PackageSet.new(ws, vcs, name: name, **options)
291
+ ws.manifest.register_package_set(package_set)
292
+ package_set
293
+ end
294
+
295
+ def ws_add_package_set_to_layout(name, vcs = VCSDefinition.from_raw(type: 'none'), **options)
296
+ package_set = ws_define_package_set(name, vcs, **options)
297
+ ws.manifest.add_package_set_to_layout(package_set)
298
+ package_set
299
+ end
300
+
301
+ def ws_define_osdep_entries(entries)
302
+ ws_os_package_resolver.add_entries(entries)
303
+ end
304
+
305
+ def ws_add_osdep_entries_to_layout(entries)
306
+ ws_os_package_resolver.add_entries(entries)
307
+ entries.each_key do |pkg_name|
308
+ ws.manifest.add_package_to_layout(pkg_name)
309
+ end
310
+ end
311
+
312
+ def ws_define_package(package_type, package_name, package_set: ws.manifest.main_package_set, create: true)
313
+ package = Autobuild.send(package_type, package_name)
314
+ package.srcdir = File.join(ws.root_dir, package_name.to_s)
315
+ if create
316
+ FileUtils.mkdir_p package.srcdir
317
+ end
318
+ autoproj_package = ws.register_package(package, nil, package_set)
319
+ yield(package) if block_given?
320
+ autoproj_package
321
+ end
322
+
323
+ def ws_define_package_vcs(package, vcs_spec)
324
+ package.package_set.add_version_control_entry(package.name, vcs_spec)
325
+ end
326
+
327
+ def ws_define_package_overrides(package, package_set, vcs_spec)
328
+ package_set.add_overrides_entry(package.name, vcs_spec)
329
+ end
330
+
331
+ def ws_add_package_to_layout(package_type, package_name, package_set: ws.manifest.main_package_set, &block)
332
+ pkg = ws_define_package(package_type, package_name, package_set: package_set, &block)
333
+ ws.manifest.add_package_to_layout(pkg)
334
+ pkg
335
+ end
336
+
337
+ def ws_set_version_control_entry(package, entry)
338
+ package.package_set.add_version_control_entry(package.name, entry)
339
+ end
340
+
341
+ def ws_set_overrides_entry(package, package_set, entry)
342
+ package_set.add_overrides_entry(package.name, entry)
343
+ end
344
+
345
+ def ws_setup_package_dirs(package, create_srcdir: true)
346
+ package.autobuild.srcdir = srcdir = File.join(ws.root_dir, package.name)
347
+ if create_srcdir
348
+ FileUtils.mkdir_p srcdir
349
+ elsif File.directory?(srcdir)
350
+ FileUtils.rm_rf srcdir
351
+ end
352
+ package.autobuild.builddir = builddir = File.join(ws.root_dir, 'build', package.name)
353
+ package.autobuild.prefix = prefix = File.join(ws.root_dir, 'prefix', package.name)
354
+ return srcdir, builddir, prefix
355
+ end
356
+
357
+ def ws_create_git_package_set(name, source_data = Hash.new)
358
+ dir = make_tmpdir
359
+ if !system('git', 'init', chdir: dir, out: :close)
360
+ raise "failed to run git init"
361
+ end
362
+ File.open(File.join(dir, 'source.yml'), 'w') do |io|
363
+ YAML.dump(Hash['name' => name].merge(source_data), io)
364
+ end
365
+ if !system('git', 'add', 'source.yml', chdir: dir, out: :close)
366
+ raise "failed to add the source.yml"
367
+ end
368
+ if !system('git', 'commit', '-m', 'add source.yml', chdir: dir, out: :close)
369
+ raise "failed to commit the source.yml"
370
+ end
371
+ dir
372
+ end
220
373
  end
221
374
  end
222
375
 
@@ -26,40 +26,15 @@ def self.flatten_recursive_hash(hash, prefix = "")
26
26
  result
27
27
  end
28
28
 
29
- # Expand build options in +value+.
30
- #
31
- # The method will expand in +value+ patterns of the form $NAME, replacing
32
- # them with the corresponding build option.
33
- def self.expand_environment(value)
34
- return value if !contains_expansion?(value)
35
-
36
- # Perform constant expansion on the defined environment variables,
37
- # including the option set
38
- options = flatten_recursive_hash(config.validated_values)
39
-
40
- loop do
41
- new_value = single_expansion(value, options)
42
- if new_value == value
43
- break
44
- else
45
- value = new_value
46
- end
47
- end
48
- value
49
- end
50
-
51
29
  # Does a non-recursive expansion in +data+ of configuration variables
52
30
  # ($VAR_NAME) listed in +definitions+
53
31
  #
54
32
  # If the values listed in +definitions+ also contain configuration
55
33
  # variables, they do not get expanded
56
- def self.single_expansion(data, definitions, options = Hash.new)
57
- options = Kernel.validate_options options, config: Autoproj.config
58
-
34
+ def self.single_expansion(data, definitions)
59
35
  if !data.respond_to?(:to_str)
60
36
  return data
61
37
  end
62
- definitions = { 'HOME' => ENV['HOME'] }.merge(definitions)
63
38
 
64
39
  data = data.gsub(/(.|^)\$(\w+)/) do |constant_name|
65
40
  prefix = constant_name[0, 1]
@@ -72,11 +47,10 @@ def self.single_expansion(data, definitions, options = Hash.new)
72
47
  constant_name = constant_name[2..-1]
73
48
  end
74
49
 
75
- if !(value = definitions[constant_name])
76
- if !(value = options[:config].get(constant_name))
77
- if !block_given? || !(value = yield(constant_name))
78
- raise ArgumentError, "cannot find a definition for $#{constant_name}"
79
- end
50
+ value = definitions[constant_name]
51
+ if value.nil?
52
+ if !block_given? || !(value = yield(constant_name))
53
+ raise ArgumentError, "cannot find a definition for $#{constant_name}"
80
54
  end
81
55
  end
82
56
  "#{prefix}#{value}"
@@ -109,8 +83,9 @@ def self.expand(value, definitions = Hash.new)
109
83
  def self.contains_expansion?(string); string =~ /\$/ end
110
84
 
111
85
  def self.resolve_one_constant(name, value, result, definitions)
112
- result[name] = single_expansion(value, result) do |missing_name|
113
- result[missing_name] = resolve_one_constant(missing_name, definitions.delete(missing_name), result, definitions)
86
+ result[name] ||= single_expansion(value, result) do |missing_name|
87
+ result[missing_name] =
88
+ resolve_one_constant(missing_name, definitions[missing_name], result, definitions)
114
89
  end
115
90
  end
116
91
 
@@ -118,17 +93,15 @@ def self.resolve_one_constant(name, value, result, definitions)
118
93
  #
119
94
  # I.e. replaces variables by their values, so that no value in +constants+
120
95
  # refers to variables defined in +constants+
121
- def self.resolve_constant_definitions(constants)
122
- constants = constants.dup
123
- constants['HOME'] = ENV['HOME']
96
+ def self.resolve_constant_definitions(constants, definitions = Hash.new)
97
+ definitions = definitions.merge(constants)
124
98
 
125
- result = Hash.new
126
- while !constants.empty?
127
- name = constants.keys.first
128
- value = constants.delete(name)
129
- resolve_one_constant(name, value, result, constants)
99
+ all_resolutions = Hash.new
100
+ resolution_cache = Hash.new
101
+ constants.each do |key, value|
102
+ all_resolutions[key] = resolve_one_constant(key, value, resolution_cache, definitions)
130
103
  end
131
- result
104
+ all_resolutions
132
105
  end
133
106
  end
134
107
 
@@ -22,36 +22,39 @@ class VCSDefinition
22
22
  # i.e. the list of VCSDefinition objects that led to this one by means
23
23
  # of calls to {#update}
24
24
  #
25
- # @return [Array<VCSDefinition>]
25
+ # @return [Array<HistoryEntry>]
26
26
  attr_reader :history
27
27
 
28
28
  # The original spec in hash form
29
29
  #
30
- # Set if this VCSDefinition object has been created using
31
- # VCSDefinition.from_raw
32
- #
33
- # @return [nil,Hash]
30
+ # @return [Array<RawEntry>]
34
31
  attr_reader :raw
35
32
 
36
- def initialize(type, url, vcs_options, options = Hash.new)
37
- if raw && !raw.respond_to?(:to_ary)
33
+ HistoryEntry = Struct.new :package_set, :vcs
34
+ RawEntry = Struct.new :package_set, :file, :vcs
35
+
36
+ def initialize(type, url, vcs_options, from: nil, raw: Array.new, history: Array.new)
37
+ if !raw.respond_to?(:to_ary)
38
38
  raise ArgumentError, "wrong format for the raw field (#{raw.inspect})"
39
39
  end
40
- if !options.kind_of?(Hash)
41
- options = Hash[raw: options]
42
- end
43
- if vcs_options[:raw]
44
- raise
45
- end
46
- options = validate_options options, from: nil, raw: nil, history: nil
47
40
 
48
41
  @type, @url, @options = type, url, vcs_options
49
42
  if type != "none" && type != "local" && !Autobuild.respond_to?(type)
50
43
  raise ConfigError.new, "version control #{type} is unknown to autoproj"
51
44
  end
52
45
 
53
- @history = (options[:history] || Array.new) + [[options[:from], self]]
54
- @raw = options[:raw] || [[options[:from], to_hash]]
46
+ @history = history + [HistoryEntry.new(from, self)]
47
+ @raw = raw
48
+ end
49
+
50
+ # Create a null VCS object
51
+ def self.none
52
+ from_raw(type: 'none')
53
+ end
54
+
55
+ # Whether there is actually a version control definition
56
+ def none?
57
+ @type == 'none'
55
58
  end
56
59
 
57
60
  # Whether this points to a local directory
@@ -70,21 +73,16 @@ def to_hash
70
73
  # the updated definition
71
74
  #
72
75
  # @return [VCSDefinition]
73
- def update(spec, options = Hash.new)
74
- if !options.kind_of?(Hash)
75
- options = Hash[raw: options]
76
- end
77
- from, options = filter_options options, from: nil, raw: nil
78
- from = from[:from]
79
- new = self.class.vcs_definition_to_hash(spec)
80
- new_raw = options[:raw] || [[from, spec]]
76
+ def update(spec, from: nil, raw: Array.new)
77
+ new = self.class.normalize_vcs_hash(spec)
78
+ new_raw = Array.new
81
79
  new_history = Array.new
82
80
 
83
81
  # If the type changed, we replace the old definition by the new one
84
82
  # completely. Otherwise, we append it to the current one
85
83
  if !new.has_key?(:type) || (type == new[:type])
86
84
  new = to_hash.merge(new)
87
- new_raw = self.raw + new_raw
85
+ new_raw = self.raw + raw
88
86
  new_history = self.history
89
87
  end
90
88
  self.class.from_raw(new, from: from, history: new_history, raw: new_raw)
@@ -95,9 +93,9 @@ def update(spec, options = Hash.new)
95
93
  #
96
94
  # Both +old+ and +new+ are supposed to be in hash form. It is assumed
97
95
  # that +old+ has already been normalized by a call to
98
- # {.vcs_definition_to_hash}. +new+ can be in "raw" form.
96
+ # {.normalize_vcs_hash}. +new+ can be in "raw" form.
99
97
  def self.update_raw_vcs_spec(old, new)
100
- new = vcs_definition_to_hash(new)
98
+ new = normalize_vcs_hash(new)
101
99
  if new.has_key?(:type) && (old[:type] != new[:type])
102
100
  # The type changed. We replace the old definition by the new one
103
101
  # completely, and we make sure that the new definition is valid
@@ -115,7 +113,7 @@ def self.update_raw_vcs_spec(old, new)
115
113
  #
116
114
  # - package_name
117
115
  # branch: value
118
- def self.vcs_definition_to_hash(spec)
116
+ def self.normalize_vcs_hash(spec, base_dir: nil)
119
117
  plain = Array.new
120
118
  filtered_spec = Hash.new
121
119
 
@@ -141,24 +139,26 @@ def self.vcs_definition_to_hash(spec)
141
139
  # shortcut. If it is not, look for a local directory called
142
140
  # short_url
143
141
  if Autobuild.respond_to?(vcs)
144
- spec.merge!(:type => vcs, :url => url.join(':'))
142
+ spec.merge!(type: vcs, url: url.join(':'))
145
143
  elsif Autoproj.has_source_handler?(vcs)
146
144
  spec = Autoproj.call_source_handler(vcs, url.join(':'), spec)
147
145
  else
148
146
  source_dir =
149
147
  if Pathname.new(short_url).absolute?
150
148
  File.expand_path(short_url)
149
+ elsif base_dir
150
+ File.expand_path(short_url, base_dir)
151
151
  else
152
- File.expand_path(File.join(Autoproj.config_dir, short_url))
152
+ raise ArgumentError, "VCS path '#{short_url}' is relative and no base_dir was given"
153
153
  end
154
154
  if !File.directory?(source_dir)
155
- raise ConfigError.new, "'#{spec.inspect}' is neither a remote source specification, nor an existing local directory"
155
+ raise ArgumentError, "'#{short_url}' is neither a remote source specification, nor an existing local directory"
156
156
  end
157
- spec.merge!(:type => 'local', :url => source_dir)
157
+ spec.merge!(type: 'local', url: source_dir)
158
158
  end
159
159
  end
160
160
 
161
- spec, vcs_options = Kernel.filter_options spec, :type => nil, :url => nil
161
+ spec, vcs_options = Kernel.filter_options spec, type: nil, url: nil
162
162
  spec.merge!(vcs_options)
163
163
  if !spec[:url]
164
164
  # Verify that none of the keys are source handlers. If it is the
@@ -176,6 +176,13 @@ def self.vcs_definition_to_hash(spec)
176
176
  spec
177
177
  end
178
178
 
179
+ # @api private
180
+ #
181
+ # Converts a raw spec (a hash, really) into a nicely formatted string
182
+ def self.raw_spec_to_s(spec)
183
+ "{ #{spec.sort_by(&:first).map { |k, v| "#{k}: #{v}" }.join(", ")} }"
184
+ end
185
+
179
186
  # Converts a 'raw' VCS representation to a normalized hash.
180
187
  #
181
188
  # The raw definition can have three forms:
@@ -186,29 +193,30 @@ def self.vcs_definition_to_hash(spec)
186
193
  # @return [VCSDefinition]
187
194
  # @raise ArgumentError if the raw specification does not match an
188
195
  # expected format
189
- def self.from_raw(spec, options = Hash.new)
190
- if !options.kind_of?(Hash)
191
- options = Hash[raw: options]
192
- end
193
- options = validate_options options, from: nil, raw: nil, history: nil
194
- from = options[:from]
195
- history = options[:history] || Array.new
196
- raw = options[:raw] || [[from, spec]]
197
-
198
- spec = vcs_definition_to_hash(spec)
199
- if !(spec[:type] && (spec[:type] == 'none' || spec[:url]))
200
- raise ConfigError.new, "the source specification #{spec.inspect} misses either the VCS type or an URL"
196
+ def self.from_raw(spec, from: nil, raw: Array.new, history: Array.new)
197
+ normalized_spec = normalize_vcs_hash(spec)
198
+ if !(type = normalized_spec.delete(:type))
199
+ raise ArgumentError, "the source specification #{raw_spec_to_s(spec)} normalizes into #{raw_spec_to_s(normalized_spec)}, which does not have a VCS type"
200
+ elsif !(url = normalized_spec.delete(:url))
201
+ if type != 'none'
202
+ raise ArgumentError, "the source specification #{raw_spec_to_s(spec)} normalizes into #{raw_spec_to_s(normalized_spec)}, which does not have a URL. Only VCS of type 'none' do not require one"
203
+ end
201
204
  end
202
205
 
203
- spec, vcs_options = Kernel.filter_options spec, :type => nil, :url => nil
204
- return VCSDefinition.new(spec[:type], spec[:url], vcs_options, from: from, history: history, raw: raw)
206
+ VCSDefinition.new(type, url, normalized_spec, from: from, history: history, raw: raw)
205
207
  end
206
208
 
207
209
  def ==(other_vcs)
208
210
  return false if !other_vcs.kind_of?(VCSDefinition)
209
211
  if local?
210
- other_vcs.local? && url == other.url
211
- elsif !other_vcs.local?
212
+ other_vcs.local? && url == other_vcs.url
213
+ elsif other_vcs.local?
214
+ false
215
+ elsif none?
216
+ other_vcs.none?
217
+ elsif other_vcs.none?
218
+ false
219
+ else
212
220
  this_importer = create_autobuild_importer
213
221
  other_importer = other_vcs.create_autobuild_importer
214
222
  this_importer.source_id == other_importer.source_id
@@ -223,14 +231,11 @@ def eql?(other_vcs)
223
231
  to_hash == other_vcs.to_hash
224
232
  end
225
233
 
226
- def self.to_absolute_url(url, root_dir = nil)
227
- # NOTE: we MUST use nil as default argument of root_dir as we don't
228
- # want to call Autoproj.root_dir unless completely necessary
229
- # (to_absolute_url might be called on installations that are being
230
- # bootstrapped, and as such don't have a root dir yet).
231
- url = Autoproj.single_expansion(url, 'HOME' => ENV['HOME'])
232
- if url && url !~ /^(\w+:\/)?\/|^[:\w]+\@|^(\w+\@)?[\w\.-]+:/
233
- url = File.expand_path(url, root_dir || Autoproj.root_dir)
234
+ ABSOLUTE_PATH_OR_URI = /^([\w+]+:\/)?\/|^[:\w]+\@|^(\w+\@)?[\w\.-]+:/
235
+
236
+ def self.to_absolute_url(url, root_dir)
237
+ if url && url !~ ABSOLUTE_PATH_OR_URI
238
+ url = File.expand_path(url, root_dir)
234
239
  end
235
240
  url
236
241
  end
@@ -240,6 +245,15 @@ def needs_import?
240
245
  type != 'none' && type != 'local'
241
246
  end
242
247
 
248
+ def overrides_key
249
+ return if none?
250
+ if local?
251
+ "local:#{url}"
252
+ else
253
+ create_autobuild_importer.repository_id
254
+ end
255
+ end
256
+
243
257
  # Returns a properly configured instance of a subclass of
244
258
  # Autobuild::Importer that match this VCS definition
245
259
  #
@@ -247,13 +261,13 @@ def needs_import?
247
261
  def create_autobuild_importer
248
262
  return if !needs_import?
249
263
 
250
- url = VCSDefinition.to_absolute_url(self.url)
251
264
  importer = Autobuild.send(type, url, options)
252
265
  if importer.respond_to?(:declare_alternate_repository)
253
- history.each do |from, vcs|
254
- next if !from || from.main?
255
- url = VCSDefinition.to_absolute_url(vcs.url)
256
- importer.declare_alternate_repository(from.name, url, vcs.options)
266
+ history.each do |entry|
267
+ package_set = entry.package_set
268
+ vcs = entry.vcs
269
+ next if !package_set || package_set.main?
270
+ importer.declare_alternate_repository(package_set.name, vcs.url, vcs.options)
257
271
  end
258
272
  end
259
273
  importer
@@ -295,30 +309,33 @@ def self.call_source_handler(vcs, url, options)
295
309
  end
296
310
  end
297
311
 
298
- # call-seq:
299
- # Autoproj.add_source_handler name do |url, options|
300
- # # build a hash that represent source configuration
301
- # # and return it
302
- # end
303
- #
304
312
  # Add a custom source handler named +name+
305
313
  #
306
314
  # Custom source handlers are shortcuts that can be used to represent VCS
307
- # information. For instance, the gitorious_server_configuration method
308
- # defines a source handler that allows to easily add new gitorious packages:
309
- #
310
- # gitorious_server_configuration 'GITORIOUS', 'gitorious.org'
315
+ # information. For instance, the {Autoproj.git_server_configuration} helper defines a
316
+ # source handler that allows to easily add new github packages:
311
317
  #
312
- # defines the "gitorious" source handler, which allows people to write
318
+ # Autoproj.git_server_configuration('GITHUB', 'github.com',
319
+ # http_url: 'https://github.com',
320
+ # default: 'http,ssh')
313
321
  #
322
+ # Defines a 'github' custom handler that expands into the full VCS
323
+ # configuration to access github
314
324
  #
315
325
  # version_control:
316
- # - tools/orocos.rb
317
- # gitorious: rock-toolchain/orocos-rb
326
+ # - tools/orocos.rb:
327
+ # github: rock-core/base-types
318
328
  # branch: test
319
- #
320
329
  #
330
+ # @yieldparam [String] url the url given to the handler
331
+ # @yieldparam [Hash] the rest of the VCS hash
332
+ # @return [Hash] a VCS hash with the information expanded
321
333
  def self.add_source_handler(name, &handler)
322
334
  @custom_source_handlers[name.to_s] = lambda(&handler)
323
335
  end
336
+
337
+ # Deregister a source handler defined with {.add_source_handler}
338
+ def self.remove_source_handler(name)
339
+ @custom_source_handlers.delete(name)
340
+ end
324
341
  end