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
@@ -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
+