autoproj 1.9.6 → 1.9.7.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ module Autoproj
2
+ # Sets an environment variable
3
+ #
4
+ # This sets (or resets) the environment variable +name+ to the given value.
5
+ # If multiple values are given, they are joined with ':'
6
+ #
7
+ # The values can contain configuration parameters using the
8
+ # $CONF_VARIABLE_NAME syntax.
9
+ def self.env_set(name, *value)
10
+ Autobuild.env_clear(name)
11
+ env_add(name, *value)
12
+ end
13
+
14
+ # Adds new values to a given environment variable
15
+ #
16
+ # Adds the given value(s) to the environment variable named +name+. The
17
+ # values are added using the ':' marker.
18
+ #
19
+ # The values can contain configuration parameters using the
20
+ # $CONF_VARIABLE_NAME syntax.
21
+ def self.env_add(name, *value)
22
+ value = value.map { |v| expand_environment(v) }
23
+ Autobuild.env_add(name, *value)
24
+ end
25
+
26
+ # Sets an environment variable which is a path search variable (such as
27
+ # PATH, RUBYLIB, PYTHONPATH)
28
+ #
29
+ # This sets (or resets) the environment variable +name+ to the given value.
30
+ # If multiple values are given, they are joined with ':'. Unlike env_set,
31
+ # duplicate values will be removed.
32
+ #
33
+ # The values can contain configuration parameters using the
34
+ # $CONF_VARIABLE_NAME syntax.
35
+ def self.env_set_path(name, *value)
36
+ Autobuild.env_clear(name)
37
+ env_add_path(name, *value)
38
+ end
39
+
40
+ # Adds new values to a given environment variable, which is a path search
41
+ # variable (such as PATH, RUBYLIB, PYTHONPATH)
42
+ #
43
+ # Adds the given value(s) to the environment variable named +name+. The
44
+ # values are added using the ':' marker. Unlike env_set, duplicate values
45
+ # will be removed.
46
+ #
47
+ # The values can contain configuration parameters using the
48
+ # $CONF_VARIABLE_NAME syntax.
49
+ #
50
+ # This is usually used in package configuration blocks to add paths
51
+ # dependent on the place of install, such as
52
+ #
53
+ # cmake_package 'test' do |pkg|
54
+ # Autoproj.env_add_path 'RUBYLIB', File.join(pkg.srcdir, 'bindings', 'ruby')
55
+ # end
56
+ def self.env_add_path(name, *value)
57
+ value = value.map { |v| expand_environment(v) }
58
+ Autobuild.env_add_path(name, *value)
59
+ end
60
+
61
+ # Requests that autoproj source the given shell script in its own env.sh
62
+ # script
63
+ def self.env_source_file(file)
64
+ Autobuild.env_source_file(file)
65
+ end
66
+
67
+ # Requests that autoproj source the given shell script in its own env.sh
68
+ # script
69
+ def self.env_source_after(file)
70
+ Autobuild.env_source_after(file)
71
+ end
72
+
73
+ # Requests that autoproj source the given shell script in its own env.sh
74
+ # script
75
+ def self.env_source_before(file)
76
+ Autobuild.env_source_before(file)
77
+ end
78
+ end
@@ -0,0 +1,36 @@
1
+ module Autoproj
2
+ # Manifest of installed packages imported from another autoproj installation
3
+ class InstallationManifest
4
+ Package = Struct.new :name, :srcdir, :prefix
5
+
6
+ attr_reader :path
7
+ attr_reader :packages
8
+ def initialize(path)
9
+ @path = path
10
+ end
11
+
12
+ def load(path)
13
+ @packages = CSV.read(path).map do |row|
14
+ Package.new(*row)
15
+ end
16
+ end
17
+
18
+ def each(&block)
19
+ packages.each(&block)
20
+ end
21
+
22
+ def [](name)
23
+ packages.find { |pkg| pkg.name == name }
24
+ end
25
+
26
+ def self.from_root(root_dir)
27
+ manifest = InstallationManifest.new(root_dir)
28
+ manifest_file = File.join(root_dir, ".autoproj-installation-manifest")
29
+ if !File.file?(manifest_file)
30
+ raise ConfigError.new, "no .autoproj-installation-manifest file exists in #{root_dir}. You should probably rerun autoproj envsh in that folder first"
31
+ end
32
+ manifest.load(manifest_file)
33
+ manifest
34
+ end
35
+ end
36
+ end
File without changes
@@ -16,6 +16,7 @@ module Autoproj
16
16
  def self.add_build_system_dependency(*names)
17
17
  @build_system_dependencies |= names.to_set
18
18
  end
19
+
19
20
  class << self
20
21
  # Returns the set of OS packages that are needed to build and/or import
21
22
  # the packages
@@ -24,970 +25,6 @@ module Autoproj
24
25
  attr_reader :build_system_dependencies
25
26
  end
26
27
 
27
- # Expand build options in +value+.
28
- #
29
- # The method will expand in +value+ patterns of the form $NAME, replacing
30
- # them with the corresponding build option.
31
- def self.expand_environment(value)
32
- # Perform constant expansion on the defined environment variables,
33
- # including the option set
34
- options = Autoproj.option_set
35
- options.each_key do |k|
36
- options[k] = options[k].to_s
37
- end
38
-
39
- loop do
40
- new_value = Autoproj.single_expansion(value, options)
41
- if new_value == value
42
- break
43
- else
44
- value = new_value
45
- end
46
- end
47
- value
48
- end
49
-
50
- # Sets an environment variable
51
- #
52
- # This sets (or resets) the environment variable +name+ to the given value.
53
- # If multiple values are given, they are joined with ':'
54
- #
55
- # The values can contain configuration parameters using the
56
- # $CONF_VARIABLE_NAME syntax.
57
- def self.env_set(name, *value)
58
- Autobuild.env_clear(name)
59
- env_add(name, *value)
60
- end
61
-
62
- # Adds new values to a given environment variable
63
- #
64
- # Adds the given value(s) to the environment variable named +name+. The
65
- # values are added using the ':' marker.
66
- #
67
- # The values can contain configuration parameters using the
68
- # $CONF_VARIABLE_NAME syntax.
69
- def self.env_add(name, *value)
70
- value = value.map { |v| expand_environment(v) }
71
- Autobuild.env_add(name, *value)
72
- end
73
-
74
- # Sets an environment variable which is a path search variable (such as
75
- # PATH, RUBYLIB, PYTHONPATH)
76
- #
77
- # This sets (or resets) the environment variable +name+ to the given value.
78
- # If multiple values are given, they are joined with ':'. Unlike env_set,
79
- # duplicate values will be removed.
80
- #
81
- # The values can contain configuration parameters using the
82
- # $CONF_VARIABLE_NAME syntax.
83
- def self.env_set_path(name, *value)
84
- Autobuild.env_clear(name)
85
- env_add_path(name, *value)
86
- end
87
-
88
- # Adds new values to a given environment variable, which is a path search
89
- # variable (such as PATH, RUBYLIB, PYTHONPATH)
90
- #
91
- # Adds the given value(s) to the environment variable named +name+. The
92
- # values are added using the ':' marker. Unlike env_set, duplicate values
93
- # will be removed.
94
- #
95
- # The values can contain configuration parameters using the
96
- # $CONF_VARIABLE_NAME syntax.
97
- #
98
- # This is usually used in package configuration blocks to add paths
99
- # dependent on the place of install, such as
100
- #
101
- # cmake_package 'test' do |pkg|
102
- # Autoproj.env_add_path 'RUBYLIB', File.join(pkg.srcdir, 'bindings', 'ruby')
103
- # end
104
- def self.env_add_path(name, *value)
105
- value = value.map { |v| expand_environment(v) }
106
- Autobuild.env_add_path(name, *value)
107
- end
108
-
109
- # Requests that autoproj source the given shell script in its own env.sh
110
- # script
111
- def self.env_source_file(file)
112
- Autobuild.env_source_file(file)
113
- end
114
-
115
- # Requests that autoproj source the given shell script in its own env.sh
116
- # script
117
- def self.env_source_after(file)
118
- Autobuild.env_source_after(file)
119
- end
120
-
121
- # Requests that autoproj source the given shell script in its own env.sh
122
- # script
123
- def self.env_source_before(file)
124
- Autobuild.env_source_before(file)
125
- end
126
-
127
- # Representation of a VCS definition contained in a source.yml file or in
128
- # autoproj/manifest
129
- class VCSDefinition
130
- attr_reader :type
131
- attr_reader :url
132
- attr_reader :options
133
-
134
- # The original spec in hash form. Set if this VCSDefinition object has
135
- # been created using VCSDefinition.from_raw
136
- attr_reader :raw
137
-
138
- def initialize(type, url, options, raw = nil)
139
- if raw && !raw.respond_to?(:to_ary)
140
- raise ArgumentError, "wrong format for the raw field (#{raw.inspect})"
141
- end
142
-
143
- @type, @url, @options = type, url, options
144
- if type != "none" && type != "local" && !Autobuild.respond_to?(type)
145
- raise ConfigError.new, "version control #{type} is unknown to autoproj"
146
- end
147
- @raw = raw
148
- end
149
-
150
- def local?
151
- @type == 'local'
152
- end
153
-
154
- # Updates the VCS specification +old+ by the information contained in
155
- # +new+
156
- #
157
- # Both +old+ and +new+ are supposed to be in hash form. It is assumed
158
- # that +old+ has already been normalized by a call to
159
- # Autoproj.vcs_definition_to_hash. +new+ can be in "raw" form.
160
- def self.update_raw_vcs_spec(old, new)
161
- new = vcs_definition_to_hash(new)
162
- if new.has_key?(:type) && (old[:type] != new[:type])
163
- # The type changed. We replace the old definition by the new one
164
- # completely, and we make sure that the new definition is valid
165
- from_raw(new)
166
- new
167
- else
168
- old.merge(new)
169
- end
170
- end
171
-
172
- # Normalizes a VCS definition contained in a YAML file into a hash
173
- #
174
- # It handles custom source handler expansion, as well as the bad habit
175
- # of forgetting a ':' at the end of a line:
176
- #
177
- # - package_name
178
- # branch: value
179
- def self.vcs_definition_to_hash(spec)
180
- options = Hash.new
181
-
182
- plain = Array.new
183
- filtered_spec = Hash.new
184
- spec.each do |key, value|
185
- keys = key.to_s.split(/\s+/)
186
- plain.concat(keys[0..-2])
187
- filtered_spec[keys[-1].to_sym] = value
188
- end
189
- spec = filtered_spec
190
-
191
- if plain.size > 1
192
- raise ConfigError.new, "invalid syntax"
193
- elsif plain.size == 1
194
- short_url = plain.first
195
- vcs, *url = short_url.split(':')
196
-
197
- # Check if VCS is a known version control system or source handler
198
- # shortcut. If it is not, look for a local directory called
199
- # short_url
200
- if Autobuild.respond_to?(vcs)
201
- spec.merge!(:type => vcs, :url => url.join(':'))
202
- elsif Autoproj.has_source_handler?(vcs)
203
- spec = Autoproj.call_source_handler(vcs, url.join(':'), spec)
204
- else
205
- source_dir = File.expand_path(File.join(Autoproj.config_dir, short_url))
206
- if !File.directory?(source_dir)
207
- raise ConfigError.new, "'#{spec.inspect}' is neither a remote source specification, nor a local source definition"
208
- end
209
- spec.merge!(:type => 'local', :url => source_dir)
210
- end
211
- end
212
-
213
- spec, vcs_options = Kernel.filter_options spec, :type => nil, :url => nil
214
- spec.merge!(vcs_options)
215
- if !spec[:url]
216
- # Verify that none of the keys are source handlers. If it is the
217
- # case, convert
218
- filtered_spec = Hash.new
219
- spec.dup.each do |key, value|
220
- if Autoproj.has_source_handler?(key)
221
- spec.delete(key)
222
- spec = Autoproj.call_source_handler(key, value, spec)
223
- break
224
- end
225
- end
226
- end
227
-
228
- spec
229
- end
230
-
231
- # Autoproj configuration files accept VCS definitions in three forms:
232
- # * as a plain string, which is a relative/absolute path
233
- # * as a plain string, which is a vcs_type:url string
234
- # * as a hash
235
- #
236
- # This method returns the VCSDefinition object matching one of these
237
- # specs. It raises ConfigError if there is no type and/or url
238
- def self.from_raw(spec, raw_spec = [[nil, spec]])
239
- spec = vcs_definition_to_hash(spec)
240
- if !(spec[:type] && (spec[:type] == 'none' || spec[:url]))
241
- raise ConfigError.new, "the source specification #{spec.inspect} misses either the VCS type or an URL"
242
- end
243
-
244
- spec, vcs_options = Kernel.filter_options spec, :type => nil, :url => nil
245
- return VCSDefinition.new(spec[:type], spec[:url], vcs_options, raw_spec)
246
- end
247
-
248
- def ==(other_vcs)
249
- return false if !other_vcs.kind_of?(VCSDefinition)
250
- if local?
251
- other_vcs.local? && url == other.url
252
- elsif !other_vcs.local?
253
- this_importer = create_autobuild_importer
254
- other_importer = other_vcs.create_autobuild_importer
255
- this_importer.repository_id == other_importer.repository_id
256
- end
257
- end
258
-
259
- def self.to_absolute_url(url, root_dir = nil)
260
- # NOTE: we MUST use nil as default argument of root_dir as we don't
261
- # want to call Autoproj.root_dir unless completely necessary
262
- # (to_absolute_url might be called on installations that are being
263
- # bootstrapped, and as such don't have a root dir yet).
264
- url = Autoproj.single_expansion(url, 'HOME' => ENV['HOME'])
265
- if url && url !~ /^(\w+:\/)?\/|^[:\w]+\@|^(\w+\@)?[\w\.-]+:/
266
- url = File.expand_path(url, root_dir || Autoproj.root_dir)
267
- end
268
- url
269
- end
270
-
271
- # Returns a properly configured instance of a subclass of
272
- # Autobuild::Importer that match this VCS definition
273
- #
274
- # Returns nil if the VCS type is 'none'
275
- def create_autobuild_importer
276
- return if type == "none"
277
-
278
- url = VCSDefinition.to_absolute_url(self.url)
279
- Autobuild.send(type, url, options)
280
- end
281
-
282
- # Returns a pretty representation of this VCS definition
283
- def to_s
284
- if type == "none"
285
- "none"
286
- else
287
- desc = "#{type}:#{url}"
288
- if !options.empty?
289
- desc = "#{desc} #{options.to_a.sort_by { |key, _| key.to_s }.map { |key, value| "#{key}=#{value}" }.join(" ")}"
290
- end
291
- desc
292
- end
293
- end
294
- end
295
-
296
- @custom_source_handlers = Hash.new
297
-
298
- # Returns true if +vcs+ refers to a source handler name added by
299
- # #add_source_handler
300
- def self.has_source_handler?(vcs)
301
- @custom_source_handlers.has_key?(vcs.to_s)
302
- end
303
-
304
- # Returns the source handlers associated with +vcs+
305
- #
306
- # Source handlers are added by Autoproj.add_source_handler. The returned
307
- # value is an object that responds to #call(url, options) and return a VCS
308
- # definition as a hash
309
- def self.call_source_handler(vcs, url, options)
310
- handler = @custom_source_handlers[vcs.to_s]
311
- if !handler
312
- raise ArgumentError, "there is no source handler for #{vcs}"
313
- else
314
- return handler.call(url, options)
315
- end
316
- end
317
-
318
- # call-seq:
319
- # Autoproj.add_source_handler name do |url, options|
320
- # # build a hash that represent source configuration
321
- # # and return it
322
- # end
323
- #
324
- # Add a custom source handler named +name+
325
- #
326
- # Custom source handlers are shortcuts that can be used to represent VCS
327
- # information. For instance, the gitorious_server_configuration method
328
- # defines a source handler that allows to easily add new gitorious packages:
329
- #
330
- # gitorious_server_configuration 'GITORIOUS', 'gitorious.org'
331
- #
332
- # defines the "gitorious" source handler, which allows people to write
333
- #
334
- #
335
- # version_control:
336
- # - tools/orocos.rb
337
- # gitorious: rock-toolchain/orocos-rb
338
- # branch: test
339
- #
340
- #
341
- def self.add_source_handler(name, &handler)
342
- @custom_source_handlers[name.to_s] = lambda(&handler)
343
- end
344
-
345
- # Does a non-recursive expansion in +data+ of configuration variables
346
- # ($VAR_NAME) listed in +definitions+
347
- #
348
- # If the values listed in +definitions+ also contain configuration
349
- # variables, they do not get expanded
350
- def self.single_expansion(data, definitions)
351
- if !data.respond_to?(:to_str)
352
- return data
353
- end
354
- definitions = { 'HOME' => ENV['HOME'] }.merge(definitions)
355
-
356
- data = data.gsub /(.|^)\$(\w+)/ do |constant_name|
357
- prefix = constant_name[0, 1]
358
- if prefix == "\\"
359
- next(constant_name[1..-1])
360
- end
361
- if prefix == "$"
362
- prefix, constant_name = "", constant_name[1..-1]
363
- else
364
- constant_name = constant_name[2..-1]
365
- end
366
-
367
- if !(value = definitions[constant_name])
368
- if !(value = Autoproj.user_config(constant_name))
369
- if !block_given? || !(value = yield(constant_name))
370
- raise ArgumentError, "cannot find a definition for $#{constant_name}"
371
- end
372
- end
373
- end
374
- "#{prefix}#{value}"
375
- end
376
- data
377
- end
378
-
379
- # Expand constants within +value+
380
- #
381
- # The list of constants is given in +definitions+. It raises ConfigError if
382
- # some values are not found
383
- def self.expand(value, definitions = Hash.new)
384
- if value.respond_to?(:to_hash)
385
- value.dup.each do |name, definition|
386
- value[name] = expand(definition, definitions)
387
- end
388
- value
389
- elsif value.respond_to?(:to_ary)
390
- value.map { |val| expand(val, definitions) }
391
- else
392
- value = single_expansion(value, definitions)
393
- if contains_expansion?(value)
394
- raise ConfigError.new, "some expansions are not defined in #{value.inspect}"
395
- end
396
- value
397
- end
398
- end
399
-
400
- # True if the given string contains expansions
401
- def self.contains_expansion?(string); string =~ /\$/ end
402
-
403
- def self.resolve_one_constant(name, value, result, definitions)
404
- result[name] = single_expansion(value, result) do |missing_name|
405
- result[missing_name] = resolve_one_constant(missing_name, definitions.delete(missing_name), result, definitions)
406
- end
407
- end
408
-
409
- # Resolves all possible variable references from +constants+
410
- #
411
- # I.e. replaces variables by their values, so that no value in +constants+
412
- # refers to variables defined in +constants+
413
- def self.resolve_constant_definitions(constants)
414
- constants = constants.dup
415
- constants['HOME'] = ENV['HOME']
416
-
417
- result = Hash.new
418
- while !constants.empty?
419
- name = constants.keys.first
420
- value = constants.delete(name)
421
- resolve_one_constant(name, value, result, constants)
422
- end
423
- result
424
- end
425
-
426
- # A package set is a version control repository which contains general
427
- # information with package version control information (source.yml file),
428
- # package definitions (.autobuild files), and finally definition of
429
- # dependencies that are provided by the operating system (.osdeps file).
430
- class PackageSet
431
- attr_reader :manifest
432
- # The VCSDefinition object that defines the version control holding
433
- # information for this source. Local package sets (i.e. the ones that are not
434
- # under version control) use the 'local' version control name. For them,
435
- # local? returns true.
436
- attr_accessor :vcs
437
-
438
- # The set of OSDependencies object that represent the osdeps files
439
- # available in this package set
440
- attr_reader :all_osdeps
441
-
442
- # The OSDependencies which is a merged version of all OSdeps in
443
- # #all_osdeps
444
- attr_reader :osdeps
445
-
446
- # If this package set has been imported from another package set, this
447
- # is the other package set object
448
- attr_accessor :imported_from
449
-
450
- # If true, this package set has been loaded because another set imports
451
- # it. If false, it is loaded explicitely by the user
452
- def explicit?; !@imported_from end
453
-
454
- attr_reader :source_definition
455
- attr_reader :constants_definitions
456
-
457
- # Sets the auto_imports flag. See #auto_imports?
458
- attr_writer :auto_imports
459
- # If true (the default), imports listed in this package set will be
460
- # automatically loaded by autoproj
461
- def auto_imports?; !!@auto_imports end
462
-
463
- # Returns the Metapackage object that has the same name than this
464
- # package set
465
- def metapackage
466
- manifest.metapackage(name)
467
- end
468
-
469
- # List of the packages that are built if the package set is selected in
470
- # the layout
471
- def default_packages
472
- metapackage.packages
473
- end
474
-
475
- # Create this source from a VCSDefinition object
476
- def initialize(manifest, vcs)
477
- @manifest = manifest
478
- @vcs = vcs
479
- @osdeps = OSDependencies.new
480
- @all_osdeps = []
481
-
482
- @provides = Set.new
483
- @imports = Array.new
484
- @auto_imports = true
485
- end
486
-
487
- # Load a new osdeps file for this package set
488
- def load_osdeps(file)
489
- new_osdeps = OSDependencies.load(file)
490
- @all_osdeps << new_osdeps
491
- @osdeps.merge(@all_osdeps.last)
492
- new_osdeps
493
- end
494
-
495
- # Enumerate all osdeps package names from this package set
496
- def each_osdep(&block)
497
- @osdeps.definitions.each_key(&block)
498
- end
499
-
500
- # True if this source has already been checked out on the local autoproj
501
- # installation
502
- def present?; File.directory?(raw_local_dir) end
503
- # True if this source is local, i.e. is not under a version control
504
- def local?; vcs.local? end
505
- # True if this source defines nothing
506
- def empty?
507
- !source_definition['version_control'] && !source_definition['overrides']
508
- !each_package.find { true } &&
509
- !File.exists?(File.join(raw_local_dir, "overrides.rb")) &&
510
- !File.exists?(File.join(raw_local_dir, "init.rb"))
511
- end
512
-
513
- # Create a PackageSet instance from its description as found in YAML
514
- # configuration files
515
- def self.from_spec(manifest, raw_spec, load_description)
516
- if raw_spec.respond_to?(:to_str)
517
- local_path = File.join(Autoproj.config_dir, raw_spec)
518
- if File.directory?(local_path)
519
- raw_spec = { :type => 'local', :url => local_path }
520
- end
521
- end
522
- spec = VCSDefinition.vcs_definition_to_hash(raw_spec)
523
- options, vcs_spec = Kernel.filter_options spec, :auto_imports => true
524
-
525
- # Look up for short notation (i.e. not an explicit hash). It is
526
- # either vcs_type:url or just url. In the latter case, we expect
527
- # 'url' to be a path to a local directory
528
- vcs_spec = Autoproj.expand(vcs_spec, manifest.constant_definitions)
529
- vcs_def = VCSDefinition.from_raw(vcs_spec, [[nil, raw_spec]])
530
-
531
- source = PackageSet.new(manifest, vcs_def)
532
- source.auto_imports = options[:auto_imports]
533
- if load_description
534
- if source.present?
535
- source.load_description_file
536
- else
537
- raise InternalError, "cannot load description file as it has not been checked out yet"
538
- end
539
- else
540
- # Try to load just the name from the source.yml file
541
- source.load_minimal
542
- end
543
-
544
- source
545
- end
546
-
547
- # Returns a string that uniquely represents the version control
548
- # information for this package set.
549
- #
550
- # I.e. for two package sets set1 and set2, if set1.repository_id ==
551
- # set2.repository_id, it means that both package sets are checked out
552
- # from exactly the same source.
553
- def repository_id
554
- if local?
555
- local_dir
556
- else
557
- importer = vcs.create_autobuild_importer
558
- if importer.respond_to?(:repository_id)
559
- importer.repository_id
560
- else
561
- vcs.to_s
562
- end
563
- end
564
- end
565
-
566
- # Remote sources can be accessed through a hidden directory in
567
- # $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
568
- # autoproj/remotes/
569
- #
570
- # This returns the former. See #user_local_dir for the latter.
571
- #
572
- # For local sources, is simply returns the path to the source directory.
573
- def raw_local_dir
574
- if local?
575
- File.expand_path(vcs.url)
576
- else
577
- File.expand_path(File.join(Autoproj.remotes_dir, vcs.create_autobuild_importer.repository_id.gsub(/[^\w]/, '_')))
578
- end
579
- end
580
-
581
- # Remote sources can be accessed through a hidden directory in
582
- # $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
583
- # autoproj/remotes/
584
- #
585
- # This returns the latter. See #raw_local_dir for the former.
586
- #
587
- # For local sources, is simply returns the path to the source directory.
588
- def user_local_dir
589
- if local?
590
- return vcs.url
591
- else
592
- File.join(Autoproj.config_dir, 'remotes', name)
593
- end
594
- end
595
-
596
- # The directory in which data for this source will be checked out
597
- def local_dir
598
- ugly_dir = raw_local_dir
599
- pretty_dir = user_local_dir
600
- if File.symlink?(pretty_dir) && File.readlink(pretty_dir) == ugly_dir
601
- pretty_dir
602
- else
603
- ugly_dir
604
- end
605
- end
606
-
607
- def required_autoproj_version
608
- definition = @source_definition || raw_description_file
609
- definition['required_autoproj_version'] || '0'
610
- end
611
-
612
- # Returns the source name
613
- def name
614
- if @name
615
- @name
616
- else
617
- vcs.to_s
618
- end
619
- end
620
-
621
- # Loads the source.yml file, validates it and returns it as a hash
622
- #
623
- # Raises InternalError if the source has not been checked out yet (it
624
- # should have), and ConfigError if the source.yml file is not valid.
625
- def raw_description_file
626
- if !present?
627
- raise InternalError, "source #{vcs} has not been fetched yet, cannot load description for it"
628
- end
629
-
630
- source_file = File.join(raw_local_dir, "source.yml")
631
- if !File.exists?(source_file)
632
- raise ConfigError.new, "source #{vcs.type}:#{vcs.url} should have a source.yml file, but does not"
633
- end
634
-
635
- source_definition = Autoproj.in_file(source_file, Autoproj::YAML_LOAD_ERROR) do
636
- YAML.load(File.read(source_file))
637
- end
638
-
639
- if !source_definition || !source_definition['name']
640
- raise ConfigError.new(source_file), "in #{source_file}: missing a 'name' field"
641
- end
642
-
643
- source_definition
644
- end
645
-
646
- # Load and validate the self-contained information from the YAML hash
647
- def load_minimal
648
- # If @source_definition is set, it means that load_description_file
649
- # has been called and that therefore all information has already
650
- # been parsed
651
- definition = @source_definition || raw_description_file
652
- @name = definition['name']
653
-
654
- if @name !~ /^[\w_\.-]+$/
655
- raise ConfigError.new(source_file),
656
- "in #{source_file}: invalid source name '#{@name}': source names can only contain alphanumeric characters, and .-_"
657
- elsif @name == "local"
658
- raise ConfigError.new(source_file),
659
- "in #{source_file}: the name 'local' is a reserved name"
660
- end
661
-
662
- @provides = (definition['provides'] || Set.new).to_set
663
- @imports = (definition['imports'] || Array.new).map do |set_def|
664
- pkg_set = Autoproj.in_file(source_file) do
665
- PackageSet.from_spec(manifest, set_def, false)
666
- end
667
-
668
- pkg_set.imported_from = self
669
- pkg_set
670
- end
671
-
672
- rescue InternalError
673
- # This ignores raw_description_file error if the package set is not
674
- # checked out yet
675
- end
676
-
677
- # Yields the imports this package set declares, as PackageSet instances
678
- def each_imported_set(&block)
679
- @imports.each(&block)
680
- end
681
-
682
- # Path to the source.yml file
683
- def source_file
684
- File.join(local_dir, 'source.yml')
685
- end
686
-
687
- # Load the source.yml file and resolves all information it contains.
688
- #
689
- # This for instance requires configuration options to be defined. Use
690
- # PackageSet#load_minimal to load only self-contained information
691
- def load_description_file
692
- if @source_definition
693
- return
694
- end
695
-
696
- @source_definition = raw_description_file
697
- load_minimal
698
-
699
- # Compute the definition of constants
700
- Autoproj.in_file(source_file) do
701
- constants = source_definition['constants'] || Hash.new
702
- @constants_definitions = Autoproj.resolve_constant_definitions(constants)
703
- end
704
- end
705
-
706
- def single_expansion(data, additional_expansions = Hash.new)
707
- if !source_definition
708
- load_description_file
709
- end
710
- Autoproj.single_expansion(data, additional_expansions.merge(constants_definitions))
711
- end
712
-
713
- # Expands the given string as much as possible using the expansions
714
- # listed in the source.yml file, and returns it. Raises if not all
715
- # variables can be expanded.
716
- def expand(data, additional_expansions = Hash.new)
717
- if !source_definition
718
- load_description_file
719
- end
720
- Autoproj.expand(data, additional_expansions.merge(constants_definitions))
721
- end
722
-
723
- # Returns the default importer definition for this package set, as a
724
- # VCSDefinition instance
725
- def default_importer
726
- importer_definition_for('default')
727
- end
728
-
729
- # Returns an importer definition for the given package, if one is
730
- # available. Otherwise returns nil.
731
- #
732
- # The returned value is a VCSDefinition object.
733
- def version_control_field(package_name, section_name, validate = true)
734
- urls = source_definition['urls'] || Hash.new
735
- urls['HOME'] = ENV['HOME']
736
-
737
- all_vcs = source_definition[section_name]
738
- if all_vcs
739
- if all_vcs.kind_of?(Hash)
740
- raise ConfigError.new, "wrong format for the #{section_name} section, you forgot the '-' in front of the package names"
741
- elsif !all_vcs.kind_of?(Array)
742
- raise ConfigError.new, "wrong format for the #{section_name} section"
743
- end
744
- end
745
-
746
- raw = []
747
- vcs_spec = Hash.new
748
-
749
- if all_vcs
750
- all_vcs.each do |spec|
751
- spec = spec.dup
752
- if spec.values.size != 1
753
- # Maybe the user wrote the spec like
754
- # - package_name:
755
- # type: git
756
- # url: blah
757
- #
758
- # or as
759
- # - package_name
760
- # type: git
761
- # url: blah
762
- #
763
- # In that case, we should have the package name as
764
- # "name => nil". Check that.
765
- name, _ = spec.find { |n, v| v.nil? }
766
- if name
767
- spec.delete(name)
768
- else
769
- name, _ = spec.find { |n, v| n =~ / \w+$/ }
770
- name =~ / (\w+)$/
771
- spec[$1] = spec.delete(name)
772
- name = name.gsub(/ \w+$/, '')
773
- end
774
- else
775
- name, spec = spec.to_a.first
776
- if name =~ / (\w+)/
777
- spec = { $1 => spec }
778
- name = name.gsub(/ \w+$/, '')
779
- end
780
-
781
- if spec.respond_to?(:to_str)
782
- if spec == "none"
783
- spec = { :type => "none" }
784
- else
785
- raise ConfigError.new, "invalid VCS specification in the #{section_name} section '#{name}: #{spec}'"
786
- end
787
- end
788
- end
789
-
790
- name_match = name
791
- if name_match =~ /[^\w\/_-]/
792
- name_match = Regexp.new("^" + name_match)
793
- end
794
- if name_match === package_name
795
- raw << [self.name, spec]
796
- vcs_spec =
797
- begin
798
- VCSDefinition.update_raw_vcs_spec(vcs_spec, spec)
799
- rescue ConfigError => e
800
- raise ConfigError.new, "invalid VCS definition in the #{section_name} section for '#{name}': #{e.message}", e.backtrace
801
- end
802
- end
803
- end
804
- end
805
-
806
- if !vcs_spec.empty?
807
- expansions = Hash["PACKAGE" => package_name,
808
- "PACKAGE_BASENAME" => File.basename(package_name),
809
- "AUTOPROJ_ROOT" => Autoproj.root_dir,
810
- "AUTOPROJ_CONFIG" => Autoproj.config_dir,
811
- "AUTOPROJ_SOURCE_DIR" => local_dir]
812
-
813
- vcs_spec = expand(vcs_spec, expansions)
814
- vcs_spec.dup.each do |name, value|
815
- vcs_spec[name] = expand(value, expansions)
816
- end
817
-
818
- # If required, verify that the configuration is a valid VCS
819
- # configuration
820
- if validate
821
- begin
822
- VCSDefinition.from_raw(vcs_spec)
823
- rescue ConfigError => e
824
- raise ConfigError.new, "invalid resulting VCS definition for package #{package_name}: #{e.message}", e.backtrace
825
- end
826
- end
827
- return vcs_spec, raw
828
- else
829
- return nil, []
830
- end
831
- end
832
-
833
- # Returns the VCS definition for +package_name+ as defined in this
834
- # source, or nil if the source does not have any.
835
- #
836
- # The definition is an instance of VCSDefinition
837
- def importer_definition_for(package_name)
838
- Autoproj.in_file source_file do
839
- vcs_spec, raw = version_control_field(package_name, 'version_control')
840
- if vcs_spec
841
- VCSDefinition.from_raw(vcs_spec, raw)
842
- end
843
- end
844
- end
845
-
846
- # Enumerates the Autobuild::Package instances that are defined in this
847
- # source
848
- def each_package
849
- if !block_given?
850
- return enum_for(:each_package)
851
- end
852
-
853
- Autoproj.manifest.packages.each_value do |pkg|
854
- if pkg.package_set.name == name
855
- yield(pkg.autobuild)
856
- end
857
- end
858
- end
859
-
860
- # True if this package set provides the given package set name. I.e. if
861
- # it has this name or the name is listed in the "replaces" field of
862
- # source.yml
863
- def provides?(name)
864
- name == self.name ||
865
- provides.include?(name)
866
- end
867
- end
868
-
869
- # Specialization of the PackageSet class for the overrides listed in autoproj/
870
- class LocalPackageSet < PackageSet
871
- def initialize(manifest)
872
- super(manifest, VCSDefinition.from_raw(:type => 'local', :url => Autoproj.config_dir))
873
- end
874
-
875
- def name
876
- 'local'
877
- end
878
- def load_minimal
879
- end
880
- def repository_id
881
- 'local'
882
- end
883
-
884
- def source_file
885
- File.join(Autoproj.config_dir, "overrides.yml")
886
- end
887
-
888
- # Returns the default importer for this package set
889
- def default_importer
890
- importer_definition_for('default') ||
891
- VCSDefinition.from_raw(:type => 'none')
892
- end
893
-
894
- def raw_description_file
895
- path = source_file
896
- if File.file?(path)
897
- data = Autoproj.in_file(path, Autoproj::YAML_LOAD_ERROR) do
898
- YAML.load(File.read(path)) || Hash.new
899
- end
900
- data['name'] = 'local'
901
- data
902
- else
903
- { 'name' => 'local' }
904
- end
905
- end
906
- end
907
-
908
- # DEPRECATED. For backward-compatibility only.
909
- Source = PackageSet
910
- # DEPRECATED. For backward-compatibility only.
911
- LocalSource = LocalPackageSet
912
-
913
- # Class used to store information about a package definition
914
- class PackageDefinition
915
- attr_reader :autobuild
916
- attr_reader :user_blocks
917
- attr_reader :package_set
918
- attr_reader :file
919
- def setup?; !!@setup end
920
- attr_writer :setup
921
- attr_accessor :vcs
922
-
923
- def initialize(autobuild, package_set, file)
924
- @autobuild, @package_set, @file =
925
- autobuild, package_set, file
926
- @user_blocks = []
927
- end
928
-
929
- def name
930
- autobuild.name
931
- end
932
-
933
- def add_setup_block(block)
934
- user_blocks << block
935
- if setup?
936
- block.call(autobuild)
937
- end
938
- end
939
- end
940
-
941
- # A set of packages that can be referred to by name
942
- class Metapackage
943
- # The metapackage name
944
- attr_reader :name
945
- # The packages listed in this metapackage
946
- attr_reader :packages
947
- # The normal dependency handling behaviour is to generate an error if a
948
- # metapackage is selected for the build but some of its dependencies
949
- # cannot be built. This modifies the behaviour to simply ignore the
950
- # problematic packages.
951
- attr_writer :weak_dependencies
952
-
953
- # @return [Boolean] whether the dependencies from this metapackage are
954
- # weak or not
955
- # @see #weak_dependencies
956
- def weak_dependencies?
957
- !!@weak_dependencies
958
- end
959
-
960
- def initialize(name)
961
- @name = name
962
- @packages = []
963
- @weak_dependencies = false
964
- end
965
-
966
- # Adds a package to this metapackage
967
- #
968
- # @param [Autobuild::Package] pkg
969
- def add(pkg)
970
- @packages << pkg
971
- end
972
-
973
- # Lists the packages contained in this metapackage
974
- #
975
- # @yieldparam [Autobuild::Package] pkg
976
- def each_package(&block)
977
- @packages.each(&block)
978
- end
979
-
980
- # Tests if the given package is included in this metapackage
981
- #
982
- # @param [String,#name] pkg the package or package name
983
- def include?(pkg)
984
- if !pkg.respond_to?(:to_str)
985
- pkg = pkg.name
986
- end
987
- @packages.any? { |p| p.name == pkg }
988
- end
989
- end
990
-
991
28
  # The Manifest class represents the information included in the main
992
29
  # manifest file, and allows to manipulate it
993
30
  class Manifest
@@ -1011,7 +48,7 @@ module Autoproj
1011
48
  end
1012
49
 
1013
50
  def import
1014
- importer.import(self)
51
+ importer.import(self,Manifest.only_local_updates)
1015
52
  end
1016
53
 
1017
54
  def add_stat(*args)
@@ -1093,6 +130,11 @@ module Autoproj
1093
130
 
1094
131
  attr_reader :metapackages
1095
132
 
133
+ class << self
134
+ #Set to true if this manifest should only do local updates through the Autobuild::Importer
135
+ attr_accessor :only_local_updates
136
+ end
137
+
1096
138
  def initialize
1097
139
  @file = nil
1098
140
  @data = Hash.new
@@ -1107,6 +149,7 @@ module Autoproj
1107
149
  @ignored_os_dependencies = Set.new
1108
150
  @reused_installations = Array.new
1109
151
  @ignored_packages = Set.new
152
+ Manifest::only_local_updates = false
1110
153
 
1111
154
  @constant_definitions = Hash.new
1112
155
  if Autoproj.has_config_key?('manifest_source')
@@ -1387,11 +430,15 @@ module Autoproj
1387
430
  @metapackages["#{pkg.package_set.name}.all"].add(pkg.autobuild)
1388
431
  end
1389
432
 
1390
- def definition_source(package_name)
433
+ def definition_package_set(package_name)
1391
434
  if pkg_def = @packages[package_name]
1392
435
  pkg_def.package_set
1393
436
  end
1394
437
  end
438
+
439
+ def definition_source(package_name)
440
+ definition_package_set(package_name)
441
+ end
1395
442
  def definition_file(package_name)
1396
443
  if pkg_def = @packages[package_name]
1397
444
  pkg_def.file
@@ -1689,9 +736,6 @@ module Autoproj
1689
736
  # The returned array can be empty if +name+ is an ignored package
1690
737
  def resolve_package_name(name, options = Hash.new)
1691
738
  if pkg_set = find_metapackage(name)
1692
- if !pkg_set
1693
- raise ConfigError.new, "#{name} is neither a package nor a package set name. Packages in autoproj must be declared in an autobuild file."
1694
- end
1695
739
  pkg_names = pkg_set.each_package.map(&:name)
1696
740
  else
1697
741
  pkg_names = [name]
@@ -2152,16 +1196,6 @@ module Autoproj
2152
1196
  @osdeps_overrides.delete(osdeps_name.to_s)
2153
1197
  end
2154
1198
 
2155
- # Exception raised by
2156
- # PackageSelection#filter_excluded_and_ignored_packages when a given
2157
- # selection is completely excluded
2158
- class ExcludedSelection < ConfigError
2159
- attr_reader :selection
2160
- def initialize(selection)
2161
- @selection = selection
2162
- end
2163
- end
2164
-
2165
1199
  # Exception raised when an unknown package is encountered
2166
1200
  class UnknownPackage < ConfigError
2167
1201
  attr_reader :name
@@ -2170,132 +1204,7 @@ module Autoproj
2170
1204
  end
2171
1205
  end
2172
1206
 
2173
- # Class holding information about which packages have been selected, and
2174
- # why. It is used to decide whether some non-availability of packages
2175
- # are errors or simply warnings (i.e. if the user really wants a given
2176
- # package, or merely might be adding it by accident)
2177
- class PackageSelection
2178
- # The set of matches, i.e. a mapping from a user-provided string to
2179
- # the set of packages it selected
2180
- attr_reader :matches
2181
- # The set of selected packages, as a hash of the package name to the
2182
- # set of user-provided strings that caused that package to be
2183
- # selected
2184
- attr_reader :selection
2185
- # A flag that tells #filter_excluded_and_ignored_packages whether
2186
- # the a given package selection is weak or not.
2187
- #
2188
- # If true, a selection that have some excluded packages will not
2189
- # generate an error. Otherwise (the default), an error is generated
2190
- attr_reader :weak_dependencies
2191
- # After a call to #filter_excluded_and_ignored_packages, this
2192
- # contains the set of package exclusions that have been ignored
2193
- # because the corresponding metapackage has a weak dependency policy
2194
- attr_reader :exclusions
2195
- # After a call to #filter_excluded_and_ignored_packages, this
2196
- # contains the set of package ignores that have been ignored because
2197
- # the corresponding metapackage has a weak dependency policy
2198
- attr_reader :ignores
2199
-
2200
- def initialize
2201
- @selection = Hash.new { |h, k| h[k] = Set.new }
2202
- @matches = Hash.new { |h, k| h[k] = Set.new }
2203
- @weak_dependencies = Hash.new
2204
- @ignores = Hash.new { |h, k| h[k] = Set.new }
2205
- @exclusions = Hash.new { |h, k| h[k] = Set.new }
2206
- end
2207
-
2208
- # The set of packages that have been selected
2209
- def packages
2210
- selection.keys
2211
- end
2212
-
2213
- def include?(pkg_name)
2214
- selection.has_key?(pkg_name)
2215
- end
2216
-
2217
- def empty?
2218
- selection.empty?
2219
- end
2220
-
2221
- def each(&block)
2222
- selection.each_key(&block)
2223
- end
2224
-
2225
- def select(sel, packages, weak = false)
2226
- packages = Array(packages)
2227
- matches[sel] |= packages.to_set.dup
2228
- packages.each do |pkg_name|
2229
- selection[pkg_name] << sel
2230
- end
2231
- weak_dependencies[sel] = weak
2232
- end
2233
-
2234
- def initialize_copy(old)
2235
- old.selection.each do |pkg_name, set|
2236
- @selection[pkg_name] = set.dup
2237
- end
2238
- old.matches.each do |sel, set|
2239
- @matches[sel] = set.dup
2240
- end
2241
- end
2242
-
2243
- def has_match_for?(sel)
2244
- matches.has_key?(sel)
2245
- end
2246
-
2247
- # Remove packages that are explicitely excluded and/or ignored
2248
- #
2249
- # Raise an error if an explicit selection expands only to an
2250
- # excluded package, and display a warning for ignored packages
2251
- def filter_excluded_and_ignored_packages(manifest)
2252
- matches.each do |sel, expansion|
2253
- excluded, other = expansion.partition { |pkg_name| manifest.excluded?(pkg_name) }
2254
- ignored, ok = other.partition { |pkg_name| manifest.ignored?(pkg_name) }
2255
-
2256
- if !excluded.empty? && (!weak_dependencies[sel] || (ok.empty? && ignored.empty?))
2257
- exclusions = excluded.map do |pkg_name|
2258
- [pkg_name, manifest.exclusion_reason(pkg_name)]
2259
- end
2260
- base_msg = "#{sel} is selected in the manifest or on the command line"
2261
- if exclusions.size == 1
2262
- reason = exclusions[0][1]
2263
- if sel == exclusions[0][0]
2264
- raise ExcludedSelection.new(sel), "#{base_msg}, but it is excluded from the build: #{reason}"
2265
- elsif weak_dependencies[sel]
2266
- raise ExcludedSelection.new(sel), "#{base_msg}, but it expands to #{exclusions.map(&:first).join(", ")}, which is excluded from the build: #{reason}"
2267
- else
2268
- raise ExcludedSelection.new(sel), "#{base_msg}, but its dependency #{exclusions.map(&:first).join(", ")} is excluded from the build: #{reason}"
2269
- end
2270
- elsif weak_dependencies[sel]
2271
- 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 ")}"
2272
- else
2273
- 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 ")}"
2274
- end
2275
- else
2276
- self.exclusions[sel] |= excluded.to_set.dup
2277
- self.ignores[sel] |= ignored.to_set.dup
2278
- end
2279
-
2280
- excluded = excluded.to_set
2281
- ignored = ignored.to_set
2282
- expansion.delete_if do |pkg_name|
2283
- ignored.include?(pkg_name) || excluded.include?(pkg_name)
2284
- end
2285
- end
2286
-
2287
- selection.keys.sort.each do |pkg_name|
2288
- if manifest.excluded?(pkg_name)
2289
- selection.delete(pkg_name)
2290
- elsif manifest.ignored?(pkg_name)
2291
- selection.delete(pkg_name)
2292
- end
2293
- end
2294
- matches.delete_if do |key, sel|
2295
- sel.empty?
2296
- end
2297
- end
2298
- end
1207
+ PackageSelection = Autoproj::PackageSelection
2299
1208
 
2300
1209
  # Package selection can be done in three ways:
2301
1210
  # * as a subdirectory in the layout
@@ -2426,211 +1335,39 @@ module Autoproj
2426
1335
  ignore_package pkg.name
2427
1336
  end
2428
1337
  end
1338
+
1339
+ # Load OS dependency information contained in our registered package
1340
+ # sets into the provided osdep object
1341
+ #
1342
+ # @param [OSDependencies] osdeps the osdep handling object
1343
+ # @return [void]
1344
+ def load_osdeps_from_package_sets(osdeps)
1345
+ each_osdeps_file do |source, file|
1346
+ osdeps.merge(source.load_osdeps(file))
1347
+ end
1348
+ end
2429
1349
  end
2430
1350
 
2431
1351
  class << self
2432
- # The singleton manifest object on which the current run works
1352
+ # The singleton manifest object that represents the current build
1353
+ # configuration
1354
+ #
1355
+ # @return [Manifest]
2433
1356
  attr_accessor :manifest
2434
1357
 
2435
- # The operating system package definitions
1358
+ # The known osdeps definitions
1359
+ #
1360
+ # @return [OSDependencies]
1361
+ # @see load_osdeps_from_package_sets
2436
1362
  attr_accessor :osdeps
2437
1363
  end
2438
1364
 
1365
+ # Load the osdeps files contained in {manifest} into {osdeps}
2439
1366
  def self.load_osdeps_from_package_sets
2440
- manifest.each_osdeps_file do |source, file|
2441
- osdeps.merge(source.load_osdeps(file))
2442
- end
1367
+ manifest.load_osdeps_from_package_sets(osdeps)
2443
1368
  osdeps
2444
1369
  end
2445
1370
 
2446
- # Manifest of installed packages imported from another autoproj installation
2447
- class InstallationManifest
2448
- Package = Struct.new :name, :srcdir, :prefix
2449
-
2450
- attr_reader :path
2451
- attr_reader :packages
2452
- def initialize(path)
2453
- @path = path
2454
- end
2455
-
2456
- def load(path)
2457
- @packages = CSV.read(path).map do |row|
2458
- Package.new(*row)
2459
- end
2460
- end
2461
-
2462
- def each(&block)
2463
- packages.each(&block)
2464
- end
2465
-
2466
- def [](name)
2467
- packages.find { |pkg| pkg.name == name }
2468
- end
2469
-
2470
- def self.from_root(root_dir)
2471
- manifest = InstallationManifest.new(root_dir)
2472
- manifest_file = File.join(root_dir, ".autoproj-installation-manifest")
2473
- if !File.file?(manifest_file)
2474
- raise ConfigError.new, "no .autoproj-installation-manifest file exists in #{root_dir}. You should probably rerun autoproj envsh in that folder first"
2475
- end
2476
- manifest.load(manifest_file)
2477
- manifest
2478
- end
2479
- end
2480
-
2481
- # Access to the information contained in a package's manifest.xml file
2482
- #
2483
- # Use PackageManifest.load to create
2484
- class PackageManifest
2485
- # Load a manifest.xml file and returns the corresponding
2486
- # PackageManifest object
2487
- def self.load(package, file)
2488
- doc =
2489
- begin REXML::Document.new(File.read(file))
2490
- rescue REXML::ParseException => e
2491
- raise Autobuild::PackageException.new(package.name, 'prepare'), "invalid #{file}: #{e.message}"
2492
- end
2493
-
2494
- PackageManifest.new(package, doc)
2495
- end
2496
-
2497
- # The Autobuild::Package instance this manifest applies on
2498
- attr_reader :package
2499
- # The raw XML data as a Nokogiri document
2500
- attr_reader :xml
2501
-
2502
- # The list of tags defined for this package
2503
- #
2504
- # Tags are defined as multiple <tags></tags> blocks, each of which can
2505
- # contain multiple comma-separated tags
2506
- def tags
2507
- result = []
2508
- xml.elements.each('package/tags') do |node|
2509
- result.concat((node.text || "").strip.split(','))
2510
- end
2511
- result
2512
- end
2513
-
2514
- def documentation
2515
- xml.elements.each('package/description') do |node|
2516
- doc = (node.text || "").strip
2517
- if !doc.empty?
2518
- return doc
2519
- end
2520
- end
2521
- return short_documentation
2522
- end
2523
-
2524
- def short_documentation
2525
- xml.elements.each('package/description') do |node|
2526
- doc = node.attributes['brief']
2527
- if doc
2528
- doc = doc.to_s.strip
2529
- end
2530
- if doc && !doc.empty?
2531
- return doc.to_s
2532
- end
2533
- end
2534
- "no documentation available for #{package.name} in its manifest.xml file"
2535
- end
2536
-
2537
- def initialize(package, doc = REXML::Document.new)
2538
- @package = package
2539
- @xml = doc
2540
- end
2541
-
2542
- def each_dependency(&block)
2543
- if block_given?
2544
- each_os_dependency(&block)
2545
- each_package_dependency(&block)
2546
- else
2547
- enum_for(:each_dependency, &block)
2548
- end
2549
- end
2550
-
2551
- def each_os_dependency
2552
- if block_given?
2553
- xml.elements.each('package/rosdep') do |node|
2554
- yield(node.attributes['name'], false)
2555
- end
2556
- package.os_packages.each do |name|
2557
- yield(name, false)
2558
- end
2559
- else
2560
- enum_for :each_os_dependency
2561
- end
2562
- end
2563
-
2564
- def each_package_dependency
2565
- if block_given?
2566
- depend_nodes = xml.elements.to_a('package/depend') +
2567
- xml.elements.to_a('package/depend_optional')
2568
-
2569
- depend_nodes.each do |node|
2570
- dependency = node.attributes['package']
2571
- optional = (node.attributes['optional'].to_s == '1' || node.name == "depend_optional")
2572
-
2573
- if dependency
2574
- yield(dependency, optional)
2575
- else
2576
- raise ConfigError.new, "manifest of #{package.name} has a <depend> tag without a 'package' attribute"
2577
- end
2578
- end
2579
- else
2580
- enum_for :each_package_dependency
2581
- end
2582
- end
2583
-
2584
- # Enumerates the name and email of each author. If no email is present,
2585
- # yields (name, nil)
2586
- def each_author
2587
- if !block_given?
2588
- return enum_for(:each_author)
2589
- end
2590
-
2591
- xml.elements.each('package/author') do |author|
2592
- (author.text || "").strip.split(',').each do |str|
2593
- name, email = str.split('/').map(&:strip)
2594
- email = nil if email && email.empty?
2595
- yield(name, email)
2596
- end
2597
- end
2598
- end
2599
-
2600
- # If +name+ points to a text element in the XML document, returns the
2601
- # content of that element. If no element matches +name+, or if the
2602
- # content is empty, returns nil
2603
- def text_node(name)
2604
- xml.elements.each(name) do |str|
2605
- str = (str.text || "").strip
2606
- if !str.empty?
2607
- return str
2608
- end
2609
- end
2610
- nil
2611
- end
2612
-
2613
- # The package associated URL, usually meant to direct to a website
2614
- #
2615
- # Returns nil if there is none
2616
- def url
2617
- return text_node('package/url')
2618
- end
2619
-
2620
- # The package license name
2621
- #
2622
- # Returns nil if there is none
2623
- def license
2624
- return text_node('package/license')
2625
- end
2626
-
2627
- # The package version number
2628
- #
2629
- # Returns 0 if none is declared
2630
- def version
2631
- return text_node("version")
2632
- end
2633
- end
2634
1371
  def self.add_osdeps_overrides(*args, &block)
2635
1372
  manifest.add_osdeps_overrides(*args, &block)
2636
1373
  end