autoproj 2.0.0.rc37 → 2.0.0.rc38

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