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
@@ -55,6 +55,16 @@ def each(&block)
55
55
  each_source_package_name(&block)
56
56
  end
57
57
 
58
+ def each_package_name(&block)
59
+ return enum_for(__method__) if !block
60
+ each_source_package_name(&block)
61
+ each_osdep_package_name(&block)
62
+ end
63
+
64
+ def selected_source_package?(pkg)
65
+ source_packages.include?(pkg.name)
66
+ end
67
+
58
68
  def each_source_package_name(&block)
59
69
  source_packages.each(&block)
60
70
  end
@@ -106,6 +116,10 @@ def has_match_for?(sel)
106
116
  matches.has_key?(sel)
107
117
  end
108
118
 
119
+ def match_for(sel)
120
+ matches[sel]
121
+ end
122
+
109
123
  # Remove packages that are explicitely excluded and/or ignored
110
124
  #
111
125
  # Raise an error if an explicit selection expands only to an
@@ -130,9 +144,9 @@ def filter_excluded_and_ignored_packages(manifest)
130
144
  raise ExcludedSelection.new(sel), "#{base_msg}, but its dependency #{exclusions.map(&:first).join(", ")} is excluded from the build: #{reason}"
131
145
  end
132
146
  elsif weak_dependencies[sel]
133
- raise ExcludedSelection.new(sel), "#{base_msg}, but expands to #{exclusions.map(&:first).join(", ")}, and all these packages are excluded from the build:\n #{exclusions.map { |name, reason| "#{name}: #{reason}" }.join("\n ")}"
147
+ raise ExcludedSelection.new(sel), "#{base_msg}, but expands to #{exclusions.map(&:first).join(", ")}, and all these packages are excluded from the build:\n #{exclusions.map { |e_name, e_reason| "#{e_name}: #{e_reason}" }.join("\n ")}"
134
148
  else
135
- raise ExcludedSelection.new(sel), "#{base_msg}, but it requires #{exclusions.map(&:first).join(", ")}, and all these packages are excluded from the build:\n #{exclusions.map { |name, reason| "#{name}: #{reason}" }.join("\n ")}"
149
+ raise ExcludedSelection.new(sel), "#{base_msg}, but it requires #{exclusions.map(&:first).join(", ")}, and all these packages are excluded from the build:\n #{exclusions.map { |e_name, e_reason| "#{e_name}: #{e_reason}" }.join("\n ")}"
136
150
  end
137
151
  else
138
152
  self.exclusions[sel] |= excluded.to_set.dup
@@ -27,8 +27,29 @@ def add_source_file(name)
27
27
  end
28
28
  end
29
29
 
30
- # @return [Manifest] the manifest this package set is being used by
31
- attr_reader :manifest
30
+ # The underlying workspace
31
+ #
32
+ # @return [Workspace]
33
+ attr_reader :ws
34
+
35
+ # The manifest this package set is registered on
36
+ #
37
+ # @return [Manifest]
38
+ def manifest
39
+ ws.manifest
40
+ end
41
+
42
+ # The minimum autoproj version this package set requires
43
+ #
44
+ # It defaults to 0
45
+ #
46
+ # @return [String]
47
+ attr_accessor :required_autoproj_version
48
+
49
+ # The package set name
50
+ #
51
+ # @return [String]
52
+ attr_accessor :name
32
53
 
33
54
  # The VCSDefinition object that defines the version control holding
34
55
  # information for this source. Local package sets (i.e. the ones that are not
@@ -38,10 +59,13 @@ def add_source_file(name)
38
59
 
39
60
  # The set of OSPackageResolver object that represent the osdeps files
40
61
  # available in this package set
62
+ #
63
+ # @return [Array<(String,OSPackageResolver)>] the list of osdep files
64
+ # and the corresponding OSPackageResolver object
41
65
  attr_reader :all_osdeps
42
66
 
43
67
  # The OSPackageResolver which is a merged version of all OSdeps in
44
- # #all_osdeps
68
+ # {#all_osdeps}
45
69
  attr_reader :os_package_resolver
46
70
 
47
71
  # If this package set has been imported from another package set, this
@@ -53,9 +77,22 @@ def add_source_file(name)
53
77
  def explicit?; !!@explicit end
54
78
  attr_writer :explicit
55
79
 
56
- attr_reader :source_definition
80
+ # Definition of key => value mappings used to resolve e.g. $KEY values
81
+ # in the version control sections
57
82
  attr_reader :constants_definitions
58
83
 
84
+ # The version control information defined in this package set
85
+ attr_reader :version_control
86
+
87
+ # Cached results of {#importer_definition_for}
88
+ attr_reader :importer_definitions_cache
89
+
90
+ # The importer that should be used for packages that have no explicit
91
+ # entry
92
+ #
93
+ # @return [VCSDefinition]
94
+ attr_accessor :default_importer
95
+
59
96
  # The set of overrides defined in this package set
60
97
  attr_reader :overrides
61
98
 
@@ -83,18 +120,43 @@ def metapackage
83
120
  # List of the packages that are built if the package set is selected in
84
121
  # the layout
85
122
  def default_packages
86
- metapackage.packages
123
+ metapackage.each_package
87
124
  end
88
125
 
126
+ # Remote sources can be accessed through a hidden directory in
127
+ # {Workspace#remotes_dir}, or through a symbolic link in
128
+ # autoproj/remotes/
129
+ #
130
+ # This returns the former. See #user_local_dir for the latter.
131
+ #
132
+ # For local sources, is simply returns the path to the source directory.
133
+ attr_reader :raw_local_dir
134
+
89
135
  # Create this source from a VCSDefinition object
90
- def initialize(manifest, vcs)
91
- @manifest = manifest
136
+ def initialize(
137
+ ws, vcs,
138
+ name: self.class.name_of(ws, vcs),
139
+ raw_local_dir: self.class.raw_local_dir_of(ws, vcs))
140
+
141
+ @ws = ws
92
142
  @vcs = vcs
93
- @os_package_resolver = OSPackageResolver.new
143
+ if !vcs
144
+ raise ArgumentError, "cannot create a package set with a nil vcs, create a null VCS using VCSDefinition.none"
145
+ end
146
+ @name = name
147
+ @os_package_resolver = OSPackageResolver.new(
148
+ operating_system: ws.os_package_resolver.operating_system,
149
+ package_managers: ws.os_package_resolver.package_managers,
150
+ os_package_manager: ws.os_package_resolver.os_package_manager)
151
+ @importer_definitions_cache = Hash.new
94
152
  @all_osdeps = []
153
+ @constants_definitions = Hash.new
154
+ @required_autoproj_version = '0'
155
+ @version_control = Array.new
95
156
  @overrides = Array.new
157
+ @raw_local_dir = raw_local_dir
158
+ @default_importer = VCSDefinition.from_raw(type: 'none')
96
159
 
97
- @provides = Set.new
98
160
  @imports = Set.new
99
161
  @imports_vcs = Array.new
100
162
  @imported_from = Array.new
@@ -103,8 +165,8 @@ def initialize(manifest, vcs)
103
165
  end
104
166
 
105
167
  # Load a new osdeps file for this package set
106
- def load_osdeps(file)
107
- new_osdeps = OSPackageResolver.load(file)
168
+ def load_osdeps(file, **options)
169
+ new_osdeps = OSPackageResolver.load(file, **options)
108
170
  all_osdeps << new_osdeps
109
171
  os_package_resolver.merge(all_osdeps.last)
110
172
  new_osdeps
@@ -112,7 +174,7 @@ def load_osdeps(file)
112
174
 
113
175
  # Enumerate all osdeps package names from this package set
114
176
  def each_osdep(&block)
115
- os_package_resolver.definitions.each_key(&block)
177
+ os_package_resolver.all_package_names.each(&block)
116
178
  end
117
179
 
118
180
  # True if this source has already been checked out on the local autoproj
@@ -125,7 +187,7 @@ def main?; false end
125
187
  def local?; vcs.local? end
126
188
  # True if this source defines nothing
127
189
  def empty?
128
- !source_definition['version_control'] && overrides.empty?
190
+ version_control.empty? && overrides.empty?
129
191
  !each_package.find { true } &&
130
192
  !File.exist?(File.join(raw_local_dir, "overrides.rb")) &&
131
193
  !File.exist?(File.join(raw_local_dir, "init.rb"))
@@ -136,6 +198,8 @@ def autobuild
136
198
  create_autobuild_package
137
199
  end
138
200
 
201
+ # Create a stub autobuild package to handle the import of this package
202
+ # set
139
203
  def create_autobuild_package
140
204
  Ops::Tools.create_autobuild_package(vcs, name, raw_local_dir)
141
205
  end
@@ -158,12 +222,12 @@ def snapshot(target_dir, options = Hash.new)
158
222
  # checked out, and the vcs (as a string) otherwise
159
223
  #
160
224
  # @return [String]
161
- def self.name_of(manifest, vcs)
162
- pkg_set = PackageSet.new(manifest, vcs)
163
- if pkg_set.present?
164
- name = pkg_set.raw_description_file['name']
225
+ def self.name_of(ws, vcs, raw_local_dir: raw_local_dir_of(ws, vcs))
226
+ if File.directory?(raw_local_dir)
227
+ raw_description_file(raw_local_dir, package_set_name: "#{vcs.type}:#{vcs.url}")['name']
228
+ else
229
+ vcs.to_s
165
230
  end
166
- name || vcs.to_s
167
231
  end
168
232
 
169
233
  # Returns the local directory in which the given package set should be
@@ -172,11 +236,13 @@ def self.name_of(manifest, vcs)
172
236
  # @param [VCSDefinition] vcs the version control information for the
173
237
  # package set
174
238
  # @return [String]
175
- def self.raw_local_dir_of(vcs)
176
- if vcs.local?
239
+ def self.raw_local_dir_of(ws, vcs)
240
+ if vcs.needs_import?
241
+ repository_id = vcs.create_autobuild_importer.repository_id
242
+ path = File.join(ws.remotes_dir, repository_id.gsub(/[^\w]/, '_'))
243
+ File.expand_path(path)
244
+ elsif !vcs.none?
177
245
  File.expand_path(vcs.url)
178
- else
179
- File.expand_path(File.join(Autoproj.workspace.remotes_dir, vcs.create_autobuild_importer.repository_id.gsub(/[^\w]/, '_')))
180
246
  end
181
247
  end
182
248
 
@@ -185,21 +251,12 @@ def self.raw_local_dir_of(vcs)
185
251
  # This parses the information stored in the package_sets section of
186
252
  # autoproj/manifest, or the imports section of the source.yml files and
187
253
  # returns the corresponding VCSDefinition object
188
- def self.resolve_definition(manifest, raw_spec)
189
- if raw_spec.respond_to?(:to_str)
190
- local_path = File.join(Autoproj.workspace.config_dir, raw_spec)
191
- if File.directory?(local_path)
192
- raw_spec = { :type => 'local', :url => local_path }
193
- end
194
- end
195
- spec = VCSDefinition.vcs_definition_to_hash(raw_spec)
196
- options, vcs_spec = Kernel.filter_options spec, :auto_imports => true
254
+ def self.resolve_definition(ws, raw_spec, from: nil, raw: Array.new)
255
+ spec = VCSDefinition.normalize_vcs_hash(raw_spec, base_dir: ws.config_dir)
256
+ options, vcs_spec = Kernel.filter_options spec, auto_imports: true
197
257
 
198
- # Look up for short notation (i.e. not an explicit hash). It is
199
- # either vcs_type:url or just url. In the latter case, we expect
200
- # 'url' to be a path to a local directory
201
- vcs_spec = Autoproj.expand(vcs_spec, manifest.constant_definitions)
202
- return VCSDefinition.from_raw(vcs_spec, raw: [[nil, raw_spec]]), options
258
+ vcs_spec = Autoproj.expand(vcs_spec, ws.manifest.constant_definitions)
259
+ return VCSDefinition.from_raw(vcs_spec, from: from, raw: raw), options
203
260
  end
204
261
 
205
262
  # Returns a string that uniquely represents the version control
@@ -210,7 +267,7 @@ def self.resolve_definition(manifest, raw_spec)
210
267
  # from exactly the same source.
211
268
  def repository_id
212
269
  if local?
213
- local_dir
270
+ raw_local_dir
214
271
  else
215
272
  importer = vcs.create_autobuild_importer
216
273
  if importer.respond_to?(:repository_id)
@@ -221,17 +278,6 @@ def repository_id
221
278
  end
222
279
  end
223
280
 
224
- # Remote sources can be accessed through a hidden directory in
225
- # {Workspace#remotes_dir}, or through a symbolic link in
226
- # autoproj/remotes/
227
- #
228
- # This returns the former. See #user_local_dir for the latter.
229
- #
230
- # For local sources, is simply returns the path to the source directory.
231
- def raw_local_dir
232
- self.class.raw_local_dir_of(vcs)
233
- end
234
-
235
281
  # Remote sources can be accessed through a hidden directory in
236
282
  # {Workspace#remotes_dir}, or through a symbolic link in
237
283
  # autoproj/remotes/
@@ -243,7 +289,7 @@ def user_local_dir
243
289
  if local?
244
290
  return vcs.url
245
291
  else
246
- File.join(Autoproj.workspace.config_dir, 'remotes', name)
292
+ File.join(ws.config_dir, 'remotes', name)
247
293
  end
248
294
  end
249
295
 
@@ -251,35 +297,26 @@ def user_local_dir
251
297
  def local_dir
252
298
  ugly_dir = raw_local_dir
253
299
  pretty_dir = user_local_dir
254
- if File.symlink?(pretty_dir) && File.readlink(pretty_dir) == ugly_dir
300
+ if ugly_dir == pretty_dir
301
+ pretty_dir
302
+ elsif File.symlink?(pretty_dir) && File.readlink(pretty_dir) == ugly_dir
255
303
  pretty_dir
256
304
  else
257
305
  ugly_dir
258
306
  end
259
307
  end
260
308
 
261
- def required_autoproj_version
262
- definition = @source_definition || raw_description_file
263
- definition['required_autoproj_version'] || '0'
264
- end
265
-
266
- # Returns the source name
267
- def name
268
- @name || self.class.name_of(manifest, vcs)
269
- end
270
-
271
- # Loads the source.yml file, validates it and returns it as a hash
309
+ # @api private
272
310
  #
273
- # Raises InternalError if the source has not been checked out yet (it
274
- # should have), and ConfigError if the source.yml file is not valid.
275
- def raw_description_file
276
- if !present?
277
- raise InternalError, "source #{vcs} has not been fetched yet, cannot load description for it"
278
- end
279
-
311
+ # Read the description information for a package set in a given
312
+ # directory
313
+ #
314
+ # @param [String] raw_local_dir the package set's directory
315
+ # @return [Hash] the raw description information
316
+ def self.raw_description_file(raw_local_dir, package_set_name: nil)
280
317
  master_source_file = File.join(raw_local_dir, PackageSet.master_source_file)
281
318
  if !File.exist?(master_source_file)
282
- raise ConfigError.new, "source #{vcs.type}:#{vcs.url} should have a source.yml file, but does not"
319
+ raise ConfigError.new, "package set #{package_set_name} present in #{raw_local_dir} should have a source.yml file, but does not"
283
320
  end
284
321
 
285
322
  source_definition = Hash.new
@@ -297,13 +334,24 @@ def raw_description_file
297
334
  end
298
335
  end
299
336
  end
300
-
301
337
  if !source_definition['name']
302
- raise ConfigError.new(master_source_file), "in #{master_source_file}: missing a 'name' field"
338
+ raise ConfigError.new(master_source_file), "#{master_source_file} does not have a 'name' field"
303
339
  end
304
340
  source_definition
305
341
  end
306
342
 
343
+ # Loads the source.yml file, validates it and returns it as a hash
344
+ #
345
+ # Raises InternalError if the source has not been checked out yet (it
346
+ # should have), and ConfigError if the source.yml file is not valid.
347
+ def raw_description_file
348
+ if !present?
349
+ raise InternalError, "source #{vcs} has not been fetched yet, cannot load description for it"
350
+ end
351
+
352
+ self.class.raw_description_file(raw_local_dir, package_set_name: name)
353
+ end
354
+
307
355
  # Yields the package sets imported by this package set
308
356
  #
309
357
  # This information is available only after the whole configuration has
@@ -314,23 +362,53 @@ def each_imported_set(&block)
314
362
  @imports.each(&block)
315
363
  end
316
364
 
365
+ # Add a new constant to be used to resolve e.g. version control entries
366
+ def add_constant_definition(key, value)
367
+ constants_definitions[key] = value
368
+ end
369
+
370
+ # Add a new VCS import to the list of imports
371
+ #
372
+ # @param [VCSDefinition] vcs the VCS specification for the import
373
+ # @return [void]
374
+ def add_raw_imported_set(vcs, auto_imports: true)
375
+ imports_vcs << [vcs, Hash[auto_imports: auto_imports]]
376
+ end
377
+
317
378
  # Yields the imports raw information
318
379
  #
319
380
  # @yieldparam [VCSDefinition] vcs the import VCS information
320
381
  # @yieldparam [Hash] options import options
321
382
  def each_raw_imported_set(&block)
322
- @imports_vcs.each(&block)
383
+ imports_vcs.each(&block)
384
+ end
385
+
386
+ # Add a new entry in the list of version control resolutions
387
+ def add_version_control_entry(matcher, vcs_definition)
388
+ invalidate_importer_definitions_cache
389
+ version_control << [matcher, vcs_definition]
390
+ end
391
+
392
+ # Add a new entry in the list of version control resolutions
393
+ def add_overrides_entry(matcher, vcs_definition, file: '#add_overrides_entry')
394
+ if (last_entry = overrides.last) && last_entry[0] == file
395
+ last_entry[1] << [matcher, vcs_definition]
396
+ else
397
+ overrides << [file, [[matcher, vcs_definition]]]
398
+ end
323
399
  end
324
400
 
325
401
  # Path to the source.yml file
326
402
  def source_file
327
- File.join(local_dir, 'source.yml')
403
+ if local_dir
404
+ File.join(local_dir, 'source.yml')
405
+ end
328
406
  end
329
407
 
330
408
  # Load the source.yml file and resolves all information it contains.
331
409
  def load_description_file
332
- @source_definition = raw_description_file
333
- name = @source_definition['name']
410
+ source_definition = raw_description_file
411
+ name = source_definition['name']
334
412
  if name !~ /^[\w\.-]+$/
335
413
  raise ConfigError.new(source_file),
336
414
  "in #{source_file}: invalid source name '#{@name}': source names can only contain alphanumeric characters, and .-_"
@@ -339,198 +417,251 @@ def load_description_file
339
417
  "in #{source_file}: the name 'local' is a reserved name"
340
418
  end
341
419
 
342
- parse_source_definition
343
- @overrides = load_overrides
420
+ parse_source_definition(source_definition)
344
421
  end
345
422
 
346
- def load_overrides
423
+ def load_overrides(source_definition)
347
424
  if data = source_definition['overrides']
348
425
  [[source_file, data]]
349
- else
350
- []
351
426
  end
352
427
  end
353
428
 
354
- def parse_source_definition
355
- @name = source_definition['name']
356
- @provides = (source_definition['provides'] || Set.new).to_set
357
- @imports_vcs = Array(source_definition['imports'] || Array.new).map do |set_def|
358
- if !set_def.kind_of?(Hash) && !set_def.respond_to?(:to_str)
359
- raise ConfigError.new(source_file),
360
- "in #{source_file}: wrong format for 'imports' section. Expected an array of maps or strings (e.g. - github: my/url)."
429
+ def parse_source_definition(source_definition)
430
+ @name = source_definition['name'] || self.name
431
+ @required_autoproj_version = source_definition.fetch('required_autoproj_version', self.required_autoproj_version)
432
+ if new_imports = source_definition['imports']
433
+ @imports_vcs = Array(new_imports).map do |set_def|
434
+ if !set_def.kind_of?(Hash) && !set_def.respond_to?(:to_str)
435
+ raise ConfigError.new(source_file),
436
+ "in #{source_file}: wrong format for 'imports' section. Expected an array of maps or strings (e.g. - github: my/url)."
437
+ end
438
+
439
+ Autoproj.in_file(source_file) do
440
+ PackageSet.resolve_definition(ws, set_def, from: self, raw: [VCSDefinition::RawEntry.new(self, source_file, set_def)])
441
+ end
361
442
  end
443
+ end
362
444
 
445
+ # Compute the definition of constants
446
+ if new_constants = source_definition['constants']
363
447
  Autoproj.in_file(source_file) do
364
- PackageSet.resolve_definition(manifest, set_def)
448
+ variables = inject_constants_and_config_for_expansion(Hash.new)
449
+ @constants_definitions = Autoproj.resolve_constant_definitions(new_constants, variables)
365
450
  end
366
451
  end
367
452
 
368
- # Compute the definition of constants
369
- Autoproj.in_file(source_file) do
370
- constants = source_definition['constants'] || Hash.new
371
- @constants_definitions = Autoproj.resolve_constant_definitions(constants)
453
+ if new_version_control = source_definition['version_control']
454
+ invalidate_importer_definitions_cache
455
+ @version_control = normalize_vcs_list('version_control', source_file, new_version_control)
456
+
457
+ Autoproj.in_file(source_file) do
458
+ default_vcs_spec, raw = version_control_field('default', version_control, file: source_file)
459
+ if default_vcs_spec
460
+ @default_importer = VCSDefinition.from_raw(default_vcs_spec, raw: raw, from: self)
461
+ end
462
+ end
372
463
  end
464
+ if new_overrides = load_overrides(source_definition)
465
+ @overrides = new_overrides.map do |file, entries|
466
+ [file, normalize_vcs_list('overrides', file, entries)]
467
+ end
468
+ end
469
+ end
470
+
471
+ # @api private
472
+ #
473
+ # Injects the values of {#constants_definitions} and
474
+ # {#manifest}.constant_definitions, as well as the available
475
+ # configuration variables, into a hash suitable to be used for variable
476
+ # expansion using {Autoproj.expand} and {Autoproj.single_expansion}
477
+ def inject_constants_and_config_for_expansion(additional_expansions)
478
+ defs = Hash[
479
+ "AUTOPROJ_ROOT" => ws.root_dir,
480
+ "AUTOPROJ_CONFIG" => ws.config_dir,
481
+ "AUTOPROJ_SOURCE_DIR" => local_dir].
482
+ merge(manifest.constant_definitions).
483
+ merge(constants_definitions).
484
+ merge(additional_expansions)
485
+
486
+ config = ws.config
487
+ Hash.new do |h, k|
488
+ if config.has_value_for?(k) || config.declared?(k)
489
+ config.get(k)
490
+ end
491
+ end.merge(defs)
373
492
  end
374
493
 
375
494
  def single_expansion(data, additional_expansions = Hash.new)
376
- if !source_definition
377
- raise NotLoaded.new(self), "you must load the package set information with #load_description_file before you can call #single_expansion"
378
- end
379
- Autoproj.single_expansion(data, additional_expansions.merge(constants_definitions))
495
+ defs = inject_constants_and_config_for_expansion(additional_expansions)
496
+ Autoproj.single_expansion(data, defs)
380
497
  end
381
498
 
382
499
  # Expands the given string as much as possible using the expansions
383
500
  # listed in the source.yml file, and returns it. Raises if not all
384
501
  # variables can be expanded.
385
502
  def expand(data, additional_expansions = Hash.new)
386
- if !source_definition
387
- load_description_file
388
- end
389
- Autoproj.expand(data, additional_expansions.merge(constants_definitions))
390
- end
391
-
392
- # Returns the default importer definition for this package set, as a
393
- # VCSDefinition instance
394
- def default_importer
395
- importer_definition_for('default')
503
+ defs = inject_constants_and_config_for_expansion(additional_expansions)
504
+ Autoproj.expand(data, defs)
396
505
  end
397
506
 
398
- # Returns an importer definition for the given package, if one is
399
- # available. Otherwise returns nil.
507
+ # @api private
400
508
  #
401
- # The returned value is a VCSDefinition object.
402
- def version_control_field(package_name, section_name, validate = true)
403
- vcs_field( source_definition, package_name, section_name, validate )
509
+ # Converts a number to an ordinal string representation (i.e. 1st, 25th)
510
+ def number_to_nth(number)
511
+ Hash[1 => '1st', 2 => '2nd', 3 => '3rd'].fetch(number, "#{number}th")
404
512
  end
405
513
 
406
- def vcs_field( source_definition, package_name, section_name, validate )
407
- urls = source_definition['urls'] || Hash.new
408
- urls['HOME'] = ENV['HOME']
409
-
410
- all_vcs = source_definition[section_name]
411
- if all_vcs
412
- if all_vcs.kind_of?(Hash)
413
- raise ConfigError.new, "wrong format for the #{section_name} section, you forgot the '-' in front of the package names"
414
- elsif !all_vcs.kind_of?(Array)
415
- raise ConfigError.new, "wrong format for the #{section_name} section"
416
- end
514
+ # @api private
515
+ #
516
+ # Validate the format of a VCS list field (formatted in array-of-hashes)
517
+ def normalize_vcs_list(section_name, file, list)
518
+ if list.kind_of?(Hash)
519
+ raise InvalidYAMLFormatting, "wrong format for the #{section_name} section of #{file}, you forgot the '-' in front of the package names"
520
+ elsif !list.kind_of?(Array)
521
+ raise InvalidYAMLFormatting, "wrong format for the #{section_name} section of #{file}"
417
522
  end
418
523
 
419
- raw = []
420
- vcs_spec = Hash.new
524
+ list.each_with_index.map do |spec, spec_idx|
525
+ spec_nth = number_to_nth(spec_idx + 1)
526
+ if !spec.kind_of?(Hash)
527
+ raise InvalidYAMLFormatting, "wrong format for the #{spec_nth} entry (#{spec.inspect}) of the #{section_name} section of #{file}, expected a package name, followed by a colon, and one importer option per following line"
528
+ end
421
529
 
422
- if all_vcs
423
- all_vcs.each do |spec|
424
- spec = spec.dup
425
- if !spec.kind_of?(Hash)
426
- raise ConfigError.new, "wrong format for the #{spec} entry, expected #{spec} followed by a colon and one importer option per following line"
530
+ spec = spec.dup
531
+ if spec.values.size != 1
532
+ # Maybe the user wrote the spec like
533
+ # - package_name:
534
+ # type: git
535
+ # url: blah
536
+ #
537
+ # In that case, we should have the package name as
538
+ # "name => nil". Check that.
539
+ name, _ = spec.find { |n, v| v.nil? }
540
+ if name
541
+ spec.delete(name)
542
+ else
543
+ raise InvalidYAMLFormatting, "cannot make sense of the #{spec_nth} entry in the #{section_name} section of #{file}: #{spec}"
427
544
  end
428
-
429
- if spec.values.size != 1
430
- # Maybe the user wrote the spec like
431
- # - package_name:
432
- # type: git
433
- # url: blah
434
- #
435
- # or as
436
- # - package_name
437
- # type: git
438
- # url: blah
439
- #
440
- # In that case, we should have the package name as
441
- # "name => nil". Check that.
442
- name, _ = spec.find { |n, v| v.nil? }
443
- if name
444
- spec.delete(name)
545
+ else
546
+ name, spec = spec.to_a.first
547
+ if spec.respond_to?(:to_str)
548
+ if spec == "none"
549
+ spec = Hash['type' => "none"]
445
550
  else
446
- name, _ = spec.find { |n, v| n =~ / \w+$/ }
447
- name =~ / (\w+)$/
448
- spec[$1] = spec.delete(name)
449
- name = name.gsub(/ \w+$/, '')
450
- end
451
- else
452
- name, spec = spec.to_a.first
453
- if name =~ / (\w+)/
454
- spec = { $1 => spec }
455
- name = name.gsub(/ \w+$/, '')
456
- end
457
-
458
- if spec.respond_to?(:to_str)
459
- if spec == "none"
460
- spec = { :type => "none" }
461
- else
462
- raise ConfigError.new, "invalid VCS specification in the #{section_name} section '#{name}: #{spec}'"
463
- end
551
+ raise ConfigError.new, "invalid VCS specification in the #{section_name} section of #{file}: '#{name}: #{spec}'. One can only use this shorthand to declare the absence of a VCS with the 'none' keyword"
464
552
  end
553
+ elsif !spec.kind_of?(Hash)
554
+ raise InvalidYAMLFormatting, "expected '#{name}:' followed by version control options, but got nothing, in the #{spec_nth} entry of the #{section_name} section of #{file}"
465
555
  end
556
+ end
466
557
 
467
- name_match = name
468
- if name_match =~ /[^\w\/-]/
469
- name_match = Regexp.new("^" + name_match)
470
- end
471
- if name_match === package_name
472
- raw << [self, spec]
473
- vcs_spec =
474
- begin
475
- VCSDefinition.update_raw_vcs_spec(vcs_spec, spec)
476
- rescue ConfigError => e
477
- raise ConfigError.new, "invalid VCS definition in the #{section_name} section for '#{name}': #{e.message}", e.backtrace
478
- end
479
- end
558
+ name_match = name
559
+ if name_match =~ /[^\w\/-]/
560
+ name_match = Regexp.new("^" + name_match)
480
561
  end
562
+
563
+ [name_match, spec]
481
564
  end
565
+ end
482
566
 
483
- if !vcs_spec.empty?
484
- expansions = Hash["PACKAGE" => package_name,
485
- "PACKAGE_BASENAME" => File.basename(package_name),
486
- "AUTOPROJ_ROOT" => Autoproj.workspace.root_dir,
487
- "AUTOPROJ_CONFIG" => Autoproj.workspace.config_dir,
488
- "AUTOPROJ_SOURCE_DIR" => local_dir]
567
+ # Returns a VCS definition for the given package, if one is
568
+ # available. Otherwise returns nil.
569
+ #
570
+ # @return [[(Hash,nil),Array]] the resolved VCS definition, as well
571
+ # as the "history" of it, that is the list of entries that matched the
572
+ # package in the form (PackageSet,Hash), where PackageSet is self.
573
+ # The Hash part is nil if there are no matching entries. Hash keys are
574
+ # normalized to symbols
575
+ def version_control_field(package_name, entry_list, validate: true, file: source_file)
576
+ raw = []
577
+ vcs_spec = Hash.new
489
578
 
490
- vcs_spec = expand(vcs_spec, expansions)
491
- vcs_spec.dup.each do |name, value|
492
- vcs_spec[name] = expand(value, expansions)
579
+ entry_list.each do |name_match, spec|
580
+ if name_match === package_name
581
+ raw << VCSDefinition::RawEntry.new(self, file, spec)
582
+ vcs_spec =
583
+ begin
584
+ VCSDefinition.update_raw_vcs_spec(vcs_spec, spec)
585
+ rescue ConfigError => e
586
+ raise ConfigError.new, "invalid VCS definition in the #{section_name} section for '#{name}': #{e.message}", e.backtrace
587
+ end
493
588
  end
589
+ end
494
590
 
495
- # If required, verify that the configuration is a valid VCS
496
- # configuration
497
- if validate
498
- begin
499
- VCSDefinition.from_raw(vcs_spec)
500
- rescue ConfigError => e
501
- raise ConfigError.new, "invalid resulting VCS definition for package #{package_name}: #{e.message}", e.backtrace
502
- end
503
- end
504
- return vcs_spec, raw
505
- else
591
+ if vcs_spec.empty?
506
592
  return nil, []
507
593
  end
594
+
595
+ expansions = Hash["PACKAGE" => package_name,
596
+ "PACKAGE_BASENAME" => File.basename(package_name)]
597
+
598
+ vcs_spec = expand(vcs_spec, expansions)
599
+ vcs_spec.dup.each do |name, value|
600
+ vcs_spec[name] = expand(value, expansions)
601
+ end
602
+
603
+ # Resolve relative paths w.r.t. the workspace root dir
604
+ if url = (vcs_spec.delete('url') || vcs_spec.delete(:url))
605
+ vcs_spec[:url] = VCSDefinition.to_absolute_url(url, ws.root_dir)
606
+ end
607
+
608
+ # If required, verify that the configuration is a valid VCS
609
+ # configuration
610
+ if validate
611
+ begin
612
+ VCSDefinition.from_raw(vcs_spec)
613
+ rescue ConfigError => e
614
+ raise ConfigError.new, "invalid resulting VCS definition for package #{package_name}: #{e.message}", e.backtrace
615
+ end
616
+ end
617
+ return vcs_spec, raw
618
+ end
619
+
620
+ # @api private
621
+ #
622
+ # Invalidate {#importer_definitions_cache}
623
+ def invalidate_importer_definitions_cache
624
+ @importer_definitions_cache.clear
508
625
  end
509
626
 
510
627
  # Returns the VCS definition for +package_name+ as defined in this
511
628
  # source, or nil if the source does not have any.
512
629
  #
513
- # The definition is an instance of VCSDefinition
514
- def importer_definition_for(package_name)
515
- Autoproj.in_file source_file do
516
- vcs_spec, raw = version_control_field(package_name, 'version_control')
630
+ # @param [PackageDefinition] package
631
+ # @return [VCSDefinition] the importer definition, or nil if none
632
+ # could be found
633
+ def importer_definition_for(package, default: default_importer, require_existing: true)
634
+ package_name = manifest.validate_package_name_argument(package, require_existing: require_existing)
635
+ importer_definitions_cache[package_name] ||= Autoproj.in_file source_file do
636
+ vcs_spec, raw = version_control_field(package_name, version_control, file: source_file)
517
637
  if vcs_spec
518
638
  VCSDefinition.from_raw(vcs_spec, raw: raw, from: self)
639
+ else
640
+ default
519
641
  end
520
642
  end
521
643
  end
522
644
 
523
645
  # Update a VCS object using the overrides defined in this package set
524
646
  #
525
- # @param [String] package_name the package name
647
+ # @param [PackageDefinition] package the package
526
648
  # @param [VCSDefinition] the vcs to be updated
527
649
  # @return [VCSDefinition] the new, updated vcs object
528
- def overrides_for(package_name, vcs)
529
- overrides.each do |file, override|
650
+ def overrides_for(package, vcs, require_existing: true)
651
+ package_name = manifest.validate_package_name_argument(package, require_existing: require_existing)
652
+ resolve_overrides(package_name, vcs)
653
+ end
654
+
655
+ # @api private
656
+ #
657
+ # Apply overrides on a VCS object from its (string) key
658
+ #
659
+ # This is a helper for {#overrides_for}
660
+ def resolve_overrides(key, vcs)
661
+ overrides.each do |file, file_overrides|
530
662
  new_spec, new_raw_entry =
531
663
  Autoproj.in_file file do
532
- vcs_field(Hash['overrides' => override],
533
- package_name, 'overrides', false)
664
+ version_control_field(key, file_overrides, validate: false, file: file)
534
665
  end
535
666
 
536
667
  if new_spec
@@ -538,7 +669,7 @@ def overrides_for(package_name, vcs)
538
669
  begin
539
670
  vcs = vcs.update(new_spec, raw: new_raw_entry, from: self)
540
671
  rescue ConfigError => e
541
- raise ConfigError.new, "invalid resulting VCS specification in the overrides section for package #{package_name}: #{e.message}"
672
+ raise ConfigError.new, "invalid resulting VCS specification in the overrides section for #{key}: #{e.message}"
542
673
  end
543
674
  end
544
675
  end
@@ -553,117 +684,29 @@ def each_package
553
684
  return enum_for(:each_package)
554
685
  end
555
686
 
556
- manifest.packages.each_value do |pkg|
557
- if pkg.package_set.name == name
687
+ manifest.each_package_definition do |pkg|
688
+ if pkg.package_set == self
558
689
  yield(pkg.autobuild)
559
690
  end
560
691
  end
561
692
  end
562
693
 
563
- # True if this package set provides the given package set name. I.e. if
564
- # it has this name or the name is listed in the "replaces" field of
565
- # source.yml
566
- def provides?(name)
567
- name == self.name ||
568
- provides.include?(name)
569
- end
570
- end
571
-
572
- # Specialization of the PackageSet class for the overrides listed in autoproj/
573
- class LocalPackageSet < PackageSet
574
- def initialize(manifest, local_dir = nil)
575
- super(manifest, manifest.vcs)
576
- @local_dir = local_dir
577
- end
578
-
579
- def name
580
- 'main configuration'
581
- end
582
-
583
- def vcs
584
- manifest.vcs
585
- end
586
-
587
- def main?
588
- true
589
- end
590
-
591
- def local?
592
- true
593
- end
594
-
595
- def local_dir
596
- @local_dir || (File.dirname(manifest.file) if manifest.file)
597
- end
598
-
599
- def raw_local_dir; local_dir end
600
-
601
- def manifest_path
602
- manifest.file
603
- end
604
-
605
- def overrides_file_path
606
- if d = local_dir
607
- File.join(d, "overrides.yml")
608
- end
609
- end
610
-
611
- def source_file
612
- manifest_path
613
- end
614
-
615
- # Returns the default importer for this package set
616
- def default_importer
617
- importer_definition_for('default') ||
618
- VCSDefinition.from_raw(:type => 'none')
619
- end
620
-
621
- def load_description_file
622
- @source_definition = raw_description_file
623
- parse_source_definition
624
- @overrides = load_overrides
625
- end
626
-
627
- def load_overrides
628
- files = Dir.glob(File.join( Autoproj.workspace.overrides_dir, "*.yml" ) ).sort
629
- overrides = files.map do |file|
630
- source_data = Autoproj.in_file(file, Autoproj::YAML_LOAD_ERROR) do
631
- YAML.load(File.read(file)) || Array.new
632
- end
633
- source_data =
634
- if source_data.respond_to?(:to_ary)
635
- source_data
636
- else source_data['overrides'] || Array.new
637
- end
638
- [file, source_data]
694
+ # List the autobuild files that are part of this package set
695
+ def each_autobuild_file
696
+ return enum_for(__method__) if !block_given?
697
+ Dir.glob(File.join(local_dir, "*.autobuild")).sort.each do |file|
698
+ yield(file)
639
699
  end
640
- overrides + super
641
700
  end
642
701
 
643
- def raw_description_file
644
- description = Hash[
645
- 'imports' => Array.new,
646
- 'version_control' => Array.new,
647
- 'overrides' => Array.new]
648
- if File.file?(overrides_file_path)
649
- overrides_data = Autoproj.in_file(overrides_file_path, Autoproj::YAML_LOAD_ERROR) do
650
- YAML.load(File.read(overrides_file_path)) || Hash.new
651
- end
652
- description = description.merge(overrides_data)
653
- end
654
-
655
- manifest_data = Autoproj.in_file(manifest_path, Autoproj::YAML_LOAD_ERROR) do
656
- YAML.load(File.read(manifest_path)) || Hash.new
702
+ # Yields each osdeps definition files that are present in this package
703
+ # set
704
+ def each_osdeps_file
705
+ return enum_for(__method__) if !block_given?
706
+ Dir.glob(File.join(local_dir, "*.osdeps")).each do |file|
707
+ yield(file)
657
708
  end
658
- description['imports'] = description['imports'].
659
- concat(manifest_data['package_sets'] || Array.new)
660
- description['name'] = name
661
- description
662
709
  end
663
710
  end
664
-
665
- # DEPRECATED. For backward-compatibility only.
666
- Source = PackageSet
667
- # DEPRECATED. For backward-compatibility only.
668
- LocalSource = LocalPackageSet
669
711
  end
712
+