autoproj 1.6.2 → 1.7.0.b1

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.
@@ -86,7 +86,7 @@ module Autoproj
86
86
  def initialize(type, url, options)
87
87
  @type, @url, @options = type, url, options
88
88
  if type != "none" && type != "local" && !Autobuild.respond_to?(type)
89
- raise ConfigError, "version control #{type} is unknown to autoproj"
89
+ raise ConfigError.new, "version control #{type} is unknown to autoproj"
90
90
  end
91
91
  end
92
92
 
@@ -94,6 +94,17 @@ module Autoproj
94
94
  @type == 'local'
95
95
  end
96
96
 
97
+ def ==(other_vcs)
98
+ return false if !other_vcs.kind_of?(VCSDefinition)
99
+ if local?
100
+ other_vcs.local? && url == other.url
101
+ elsif !other_vcs.local?
102
+ this_importer = create_autobuild_importer
103
+ other_importer = other_vcs.create_autobuild_importer
104
+ this_importer.repository_id == other_importer.repository_id
105
+ end
106
+ end
107
+
97
108
  def self.to_absolute_url(url, root_dir = nil)
98
109
  # NOTE: we MUST use nil as default argument of root_dir as we don't
99
110
  # want to call Autoproj.root_dir unless completely necessary
@@ -119,7 +130,7 @@ module Autoproj
119
130
  else
120
131
  desc = "#{type}:#{url}"
121
132
  if !options.empty?
122
- desc = "#{desc} #{options.map { |key, value| "#{key}=#{value}" }.join(" ")}"
133
+ desc = "#{desc} #{options.to_a.sort_by { |key, _| key.to_s }.map { |key, value| "#{key}=#{value}" }.join(" ")}"
123
134
  end
124
135
  desc
125
136
  end
@@ -127,12 +138,21 @@ module Autoproj
127
138
  end
128
139
 
129
140
  def self.vcs_definition_to_hash(spec)
141
+ options = Hash.new
142
+ if spec.size == 1 && spec.keys.first =~ /auto_imports$/
143
+ # The user probably wrote
144
+ # - string
145
+ # auto_imports: false
146
+ options['auto_imports'] = spec.values.first
147
+ spec = spec.keys.first.split(" ").first
148
+ end
149
+
130
150
  if spec.respond_to?(:to_str)
131
151
  vcs, *url = spec.to_str.split ':'
132
152
  spec = if url.empty?
133
153
  source_dir = File.expand_path(File.join(Autoproj.config_dir, spec))
134
154
  if !File.directory?(source_dir)
135
- raise ConfigError, "'#{spec.inspect}' is neither a remote source specification, nor a local source definition"
155
+ raise ConfigError.new, "'#{spec.inspect}' is neither a remote source specification, nor a local source definition"
136
156
  end
137
157
 
138
158
  Hash[:type => 'local', :url => source_dir]
@@ -143,7 +163,7 @@ module Autoproj
143
163
 
144
164
  spec, vcs_options = Kernel.filter_options spec, :type => nil, :url => nil
145
165
 
146
- return spec.merge(vcs_options)
166
+ return spec.merge(vcs_options).merge(options)
147
167
  end
148
168
 
149
169
  # Autoproj configuration files accept VCS definitions in three forms:
@@ -155,7 +175,7 @@ module Autoproj
155
175
  def self.normalize_vcs_definition(spec)
156
176
  spec = vcs_definition_to_hash(spec)
157
177
  if !(spec[:type] && (spec[:type] == 'none' || spec[:url]))
158
- raise ConfigError, "the source specification #{spec.inspect} misses either the VCS type or an URL"
178
+ raise ConfigError.new, "the source specification #{spec.inspect} misses either the VCS type or an URL"
159
179
  end
160
180
 
161
181
  spec, vcs_options = Kernel.filter_options spec, :type => nil, :url => nil
@@ -191,7 +211,7 @@ module Autoproj
191
211
  else
192
212
  value = single_expansion(value, definitions)
193
213
  if contains_expansion?(value)
194
- raise ConfigError, "some expansions are not defined in #{value.inspect}"
214
+ raise ConfigError.new, "some expansions are not defined in #{value.inspect}"
195
215
  end
196
216
  value
197
217
  end
@@ -219,22 +239,43 @@ module Autoproj
219
239
  result
220
240
  end
221
241
 
222
- # A source is a version control repository which contains general source
242
+ # A package set is a version control repository which contains general
223
243
  # information with package version control information (source.yml file),
224
244
  # package definitions (.autobuild files), and finally definition of
225
245
  # dependencies that are provided by the operating system (.osdeps file).
226
- class Source
246
+ class PackageSet
247
+ attr_reader :manifest
227
248
  # The VCSDefinition object that defines the version control holding
228
249
  # information for this source. Local package sets (i.e. the ones that are not
229
250
  # under version control) use the 'local' version control name. For them,
230
251
  # local? returns true.
231
252
  attr_accessor :vcs
253
+
254
+ # If this package set has been imported from another package set, this
255
+ # is the other package set object
256
+ attr_accessor :imported_from
257
+
258
+ # If true, this package set has been loaded because another set imports
259
+ # it. If false, it is loaded explicitely by the user
260
+ def explicit?; !@imported_from end
261
+
232
262
  attr_reader :source_definition
233
263
  attr_reader :constants_definitions
234
264
 
265
+ # Sets the auto_imports flag. See #auto_imports?
266
+ attr_writer :auto_imports
267
+ # If true (the default), imports listed in this package set will be
268
+ # automatically loaded by autoproj
269
+ def auto_imports?; !!@auto_imports end
270
+
235
271
  # Create this source from a VCSDefinition object
236
- def initialize(vcs)
272
+ def initialize(manifest, vcs)
273
+ @manifest = manifest
237
274
  @vcs = vcs
275
+
276
+ @provides = Set.new
277
+ @imports = Array.new
278
+ @auto_imports = true
238
279
  end
239
280
 
240
281
  # True if this source has already been checked out on the local autoproj
@@ -250,6 +291,47 @@ module Autoproj
250
291
  !File.exists?(File.join(raw_local_dir, "init.rb"))
251
292
  end
252
293
 
294
+ # Create a PackageSet instance from its description as found in YAML
295
+ # configuration files
296
+ def self.from_spec(manifest, spec, load_description)
297
+ spec = Autoproj.vcs_definition_to_hash(spec)
298
+ options, vcs_spec = Kernel.filter_options spec, :auto_imports => true
299
+
300
+ # Look up for short notation (i.e. not an explicit hash). It is
301
+ # either vcs_type:url or just url. In the latter case, we expect
302
+ # 'url' to be a path to a local directory
303
+ vcs_spec = Autoproj.expand(vcs_spec, manifest.constant_definitions)
304
+ vcs_def = Autoproj.normalize_vcs_definition(vcs_spec)
305
+
306
+ source = PackageSet.new(manifest, vcs_def)
307
+ source.auto_imports = options[:auto_imports]
308
+ if load_description
309
+ if source.present?
310
+ source.load_description_file
311
+ else
312
+ raise InternalError, "cannot load description file as it has not been checked out yet"
313
+ end
314
+ else
315
+ # Try to load just the name from the source.yml file
316
+ source.load_minimal
317
+ end
318
+
319
+ source
320
+ end
321
+
322
+ def repository_id
323
+ if local?
324
+ local_dir
325
+ else
326
+ importer = vcs.create_autobuild_importer
327
+ if importer.respond_to?(:repository_id)
328
+ importer.repository_id
329
+ else
330
+ vcs.to_s
331
+ end
332
+ end
333
+ end
334
+
253
335
  # Remote sources can be accessed through a hidden directory in
254
336
  # $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
255
337
  # autoproj/remotes/
@@ -259,9 +341,9 @@ module Autoproj
259
341
  # For local sources, is simply returns the path to the source directory.
260
342
  def raw_local_dir
261
343
  if local?
262
- return vcs.url
344
+ File.expand_path(vcs.url)
263
345
  else
264
- File.join(Autoproj.remotes_dir, automatic_name)
346
+ File.expand_path(File.join(Autoproj.remotes_dir, vcs.to_s.gsub(/[^\w]/, '_')))
265
347
  end
266
348
  end
267
349
 
@@ -291,19 +373,12 @@ module Autoproj
291
373
  end
292
374
  end
293
375
 
294
- # A name generated from the VCS url
295
- def automatic_name
296
- vcs.to_s.gsub(/[^\w]/, '_')
297
- end
298
-
299
376
  # Returns the source name
300
377
  def name
301
- if @source_definition then
302
- @source_definition['name'] || automatic_name
303
- elsif @name
378
+ if @name
304
379
  @name
305
380
  else
306
- automatic_name
381
+ vcs.to_s
307
382
  end
308
383
  end
309
384
 
@@ -318,34 +393,54 @@ module Autoproj
318
393
 
319
394
  source_file = File.join(raw_local_dir, "source.yml")
320
395
  if !File.exists?(source_file)
321
- raise ConfigError, "source #{vcs.type}:#{vcs.url} should have a source.yml file, but does not"
396
+ raise ConfigError.new, "source #{vcs.type}:#{vcs.url} should have a source.yml file, but does not"
322
397
  end
323
398
 
324
- begin
325
- source_definition = YAML.load(File.read(source_file))
326
- rescue ArgumentError => e
327
- raise ConfigError, "error in #{source_file}: #{e.message}"
399
+ source_definition = Autoproj.in_file(source_file, ArgumentError) do
400
+ YAML.load(File.read(source_file))
328
401
  end
329
402
 
330
403
  if !source_definition || !source_definition['name']
331
- raise ConfigError, "#{source_file} does not have a 'name' field"
404
+ raise ConfigError.new(source_file), "in #{source_file}: missing a 'name' field"
332
405
  end
333
406
 
334
407
  source_definition
335
408
  end
336
409
 
337
- # Load and validate the name from the YAML hash
338
- def load_name
410
+ # Load and validate the self-contained information from the YAML hash
411
+ def load_minimal
412
+ # If @source_definition is set, it means that load_description_file
413
+ # has been called and that therefore all information has already
414
+ # been parsed
339
415
  definition = @source_definition || raw_description_file
340
416
  @name = definition['name']
341
417
 
342
418
  if @name !~ /^[\w_\.-]+$/
343
- raise ConfigError, "invalid source name '#{@name}': source names can only contain alphanumeric characters, and .-_"
419
+ raise ConfigError.new(source_file),
420
+ "in #{source_file}: invalid source name '#{@name}': source names can only contain alphanumeric characters, and .-_"
344
421
  elsif @name == "local"
345
- raise ConfigError, "source #{self} is named 'local', but this is a reserved name"
422
+ raise ConfigError.new(source_file),
423
+ "in #{source_file}: the name 'local' is a reserved name"
424
+ end
425
+
426
+ @provides = (definition['provides'] || Set.new).to_set
427
+ @imports = (definition['imports'] || Array.new).map do |set_def|
428
+ pkg_set = Autoproj.in_file(source_file) do
429
+ PackageSet.from_spec(manifest, set_def, false)
430
+ end
431
+
432
+ pkg_set.imported_from = self
433
+ pkg_set
346
434
  end
347
435
 
348
436
  rescue InternalError
437
+ # This ignores raw_description_file error if the package set is not
438
+ # checked out yet
439
+ end
440
+
441
+ # Yields the imports this package set declares, as PackageSet instances
442
+ def each_imported_set(&block)
443
+ @imports.each(&block)
349
444
  end
350
445
 
351
446
  # Path to the source.yml file
@@ -353,21 +448,22 @@ module Autoproj
353
448
  File.join(local_dir, 'source.yml')
354
449
  end
355
450
 
356
- # Load the source.yml file that describes this source, and resolve the
357
- # $BLABLA values that are in there. Use #raw_description_file to avoid
358
- # resolving those values
451
+ # Load the source.yml file and resolves all information it contains.
452
+ #
453
+ # This for instance requires configuration options to be defined. Use
454
+ # PackageSet#load_minimal to load only self-contained information
359
455
  def load_description_file
456
+ if @source_definition
457
+ return
458
+ end
459
+
360
460
  @source_definition = raw_description_file
361
- load_name
461
+ load_minimal
362
462
 
363
-
364
463
  # Compute the definition of constants
365
- begin
464
+ Autoproj.in_file(source_file) do
366
465
  constants = source_definition['constants'] || Hash.new
367
466
  @constants_definitions = Autoproj.resolve_constant_definitions(constants)
368
-
369
- rescue ConfigError => e
370
- raise ConfigError, "#{File.join(local_dir, "source.yml")}: #{e.message}", e.backtrace
371
467
  end
372
468
  end
373
469
 
@@ -399,15 +495,19 @@ module Autoproj
399
495
  #
400
496
  # The returned value is a VCSDefinition object.
401
497
  def version_control_field(package_name, section_name, validate = true)
498
+ if !source_definition
499
+ raise InternalError, "source #{name} has not been loaded"
500
+ end
501
+
402
502
  urls = source_definition['urls'] || Hash.new
403
503
  urls['HOME'] = ENV['HOME']
404
504
 
405
505
  all_vcs = source_definition[section_name]
406
506
  if all_vcs
407
507
  if all_vcs.kind_of?(Hash)
408
- raise ConfigError, "wrong format for the #{section_name} section, you forgot the '-' in front of the package names"
508
+ raise ConfigError.new, "wrong format for the #{section_name} section, you forgot the '-' in front of the package names"
409
509
  elsif !all_vcs.kind_of?(Array)
410
- raise ConfigError, "wrong format for the #{section_name} section"
510
+ raise ConfigError.new, "wrong format for the #{section_name} section"
411
511
  end
412
512
  end
413
513
 
@@ -449,7 +549,7 @@ module Autoproj
449
549
  if spec == "none"
450
550
  spec = { :type => "none" }
451
551
  else
452
- raise ConfigError, "invalid VCS specification '#{name}: #{spec}'"
552
+ raise ConfigError.new, "invalid VCS specification '#{name}: #{spec}'"
453
553
  end
454
554
  end
455
555
  end
@@ -480,8 +580,6 @@ module Autoproj
480
580
  end
481
581
  vcs_spec
482
582
  end
483
- rescue ConfigError => e
484
- raise ConfigError, "#{e.message} in #{source_file}", e.backtrace
485
583
  end
486
584
 
487
585
  # Returns the VCS definition for +package_name+ as defined in this
@@ -508,18 +606,29 @@ module Autoproj
508
606
  end
509
607
  end
510
608
  end
609
+
610
+ # True if this package set provides the given package set name. I.e. if
611
+ # it has this name or the name is listed in the "replaces" field of
612
+ # source.yml
613
+ def provides?(name)
614
+ name == self.name ||
615
+ provides.include?(name)
616
+ end
511
617
  end
512
618
 
513
- # Specialization of the Source class for the overrides listed in autoproj/
514
- class LocalSource < Source
515
- def initialize
516
- super(Autoproj.normalize_vcs_definition(:type => 'local', :url => Autoproj.config_dir))
619
+ # Specialization of the PackageSet class for the overrides listed in autoproj/
620
+ class LocalPackageSet < PackageSet
621
+ def initialize(manifest)
622
+ super(manifest, Autoproj.normalize_vcs_definition(:type => 'local', :url => Autoproj.config_dir))
517
623
  end
518
624
 
519
625
  def name
520
626
  'local'
521
627
  end
522
- def load_name
628
+ def load_minimal
629
+ end
630
+ def repository_id
631
+ 'local'
523
632
  end
524
633
 
525
634
  def source_file
@@ -535,10 +644,8 @@ module Autoproj
535
644
  def raw_description_file
536
645
  path = source_file
537
646
  if File.file?(path)
538
- begin
539
- data = YAML.load(File.read(path)) || Hash.new
540
- rescue ArgumentError => e
541
- raise ConfigError, "error in #{source_file}: #{e.message}"
647
+ data = Autoproj.in_file(path, ArgumentError) do
648
+ YAML.load(File.read(path)) || Hash.new
542
649
  end
543
650
  data['name'] = 'local'
544
651
  data
@@ -548,12 +655,16 @@ module Autoproj
548
655
  end
549
656
  end
550
657
 
658
+ # DEPRECATED. For backward-compatibility only.
659
+ Source = PackageSet
660
+ # DEPRECATED. For backward-compatibility only.
661
+ LocalSource = LocalPackageSet
662
+
551
663
  PackageDefinition = Struct.new :autobuild, :user_block, :package_set, :file
552
664
 
553
665
  # The Manifest class represents the information included in the main
554
666
  # manifest file, and allows to manipulate it
555
667
  class Manifest
556
- FakePackage = Struct.new :text_name, :name, :srcdir, :importer, :updated
557
668
 
558
669
  # Data structure used to use autobuild importers without a package, to
559
670
  # import configuration data.
@@ -561,7 +672,21 @@ module Autoproj
561
672
  # It has to match the interface of Autobuild::Package that is relevant
562
673
  # for importers
563
674
  class FakePackage
564
- def autoproj_name; name end
675
+ attr_reader :text_name
676
+ attr_reader :name
677
+ attr_reader :srcdir
678
+ attr_reader :importer
679
+
680
+ # Used by the autobuild importers
681
+ attr_accessor :updated
682
+
683
+ def initialize(text_name, srcdir, importer = nil)
684
+ @text_name = text_name
685
+ @name = text_name.gsub /[^\w]/, '_'
686
+ @srcdir = srcdir
687
+ @importer = importer
688
+ end
689
+
565
690
  def import
566
691
  importer.import(self)
567
692
  end
@@ -593,21 +718,34 @@ module Autoproj
593
718
  # Loads the manifest file located at +file+ and returns the Manifest
594
719
  # instance that represents it
595
720
  def self.load(file)
596
- begin
597
- data = YAML.load(File.read(file))
598
- rescue Errno::ENOENT
599
- raise ConfigError, "expected an autoproj configuration in #{File.expand_path(File.dirname(file))}, but #{file} does not exist"
600
- rescue ArgumentError => e
601
- raise ConfigError, "error in #{file}: #{e.message}"
602
- end
603
- Manifest.new(file, data)
721
+ manifest = Manifest.new
722
+ manifest.load(file)
723
+ manifest
604
724
  end
605
725
 
726
+ # Load the manifest data contained in +file+
727
+ def load(file)
728
+ if !File.exists?(file)
729
+ raise ConfigError.new(dirname), "expected an autoproj configuration in #{dirname}, but #{file} does not exist"
730
+ end
731
+
732
+ data = Autoproj.in_file(file, ArgumentError) do
733
+ YAML.load(File.read(file))
734
+ end
735
+
736
+ @file = file
737
+ @data = data
738
+
739
+ if data['constants']
740
+ @constant_definitions = Autoproj.resolve_constant_definitions(data['constants'])
741
+ end
742
+ end
743
+
606
744
  # The manifest data as a Hash
607
745
  attr_reader :data
608
746
 
609
747
  # The set of packages defined so far as a mapping from package name to
610
- # [Autobuild::Package, source, file] tuple
748
+ # [Autobuild::Package, package_set, file] tuple
611
749
  attr_reader :packages
612
750
 
613
751
  # A mapping from package names into PackageManifest objects
@@ -633,22 +771,19 @@ module Autoproj
633
771
 
634
772
  attr_reader :constant_definitions
635
773
 
636
- def initialize(file, data)
637
- @file = file
638
- @data = data
774
+ def initialize
775
+ @file = nil
776
+ @data = nil
639
777
  @packages = Hash.new
640
778
  @package_manifests = Hash.new
641
779
  @automatic_exclusions = Hash.new
642
780
  @constants_definitions = Hash.new
781
+ @disabled_imports = Set.new
643
782
 
783
+ @constant_definitions = Hash.new
644
784
  if Autoproj.has_config_key?('manifest_source')
645
785
  @vcs = Autoproj.normalize_vcs_definition(Autoproj.user_config('manifest_source'))
646
786
  end
647
- if data['constants']
648
- @constant_definitions = Autoproj.resolve_constant_definitions(data['constants'])
649
- else
650
- @constant_definitions = Hash.new
651
- end
652
787
  end
653
788
 
654
789
  # True if the given package should not be built, with the packages that
@@ -730,7 +865,7 @@ module Autoproj
730
865
  end
731
866
 
732
867
  if source_name && !done_something
733
- raise ConfigError, "in #{file}: source '#{source_name}' does not exist"
868
+ raise ConfigError.new(file), "in #{file}: source '#{source_name}' does not exist"
734
869
  end
735
870
  end
736
871
 
@@ -767,11 +902,11 @@ module Autoproj
767
902
  end
768
903
 
769
904
  # Like #each_source, but filters out local package sets
770
- def each_remote_source(load_description = true)
905
+ def each_remote_package_set(load_description = true)
771
906
  if !block_given?
772
- enum_for(:each_remote_source, load_description)
907
+ enum_for(:each_remote_package_set, load_description)
773
908
  else
774
- each_source(load_description) do |source|
909
+ each_package_set(load_description) do |source|
775
910
  if !source.local?
776
911
  yield(source)
777
912
  end
@@ -779,73 +914,95 @@ module Autoproj
779
914
  end
780
915
  end
781
916
 
782
- def source_from_spec(spec, load_description) # :nodoc:
783
- # Look up for short notation (i.e. not an explicit hash). It is
784
- # either vcs_type:url or just url. In the latter case, we expect
785
- # 'url' to be a path to a local directory
786
- vcs_def = begin
787
- spec = Autoproj.expand(spec, constant_definitions)
788
- Autoproj.normalize_vcs_definition(spec)
789
- rescue ConfigError => e
790
- raise ConfigError, "in #{file}: #{e.message}"
791
- end
792
-
793
- source = Source.new(vcs_def)
794
- if load_description
795
- if source.present?
796
- source.load_description_file
797
- else
798
- raise InternalError, "cannot load description file as it has not been checked out yet"
799
- end
800
- else
801
- # Try to load just the name from the source.yml file
802
- source.load_name
917
+ def each_remote_source(*args, &block)
918
+ each_remote_package_set(*args, &block)
919
+ end
920
+
921
+ # Helper method for #each_package_set
922
+ def enumerate_package_set(pkg_set, explicit_sets, all_sets) # :nodoc:
923
+ if @disabled_imports.include?(pkg_set.name)
924
+ pkg_set.auto_imports = false
803
925
  end
804
926
 
805
- source
806
- end
927
+ result = []
928
+ if pkg_set.auto_imports?
929
+ pkg_set.each_imported_set do |imported_set|
930
+ if explicit_sets.any? { |src| src.vcs == imported_set.vcs } ||
931
+ all_sets.any? { |src| src.vcs == imported_set.vcs }
932
+ next
933
+ end
807
934
 
935
+ all_sets << imported_set
936
+ result.concat(enumerate_package_set(imported_set, explicit_sets, all_sets))
937
+ end
938
+ end
939
+ result << pkg_set
940
+ result
941
+ end
808
942
 
809
943
  # call-seq:
810
- # each_source { |source_description| ... }
944
+ # each_package_set { |pkg_set| ... }
811
945
  #
812
- # Lists all package sets defined in this manifest, by yielding a Source
813
- # object that describes it.
814
- def each_source(load_description = true, &block)
946
+ # Lists all package sets defined in this manifest, by yielding a
947
+ # PackageSet object that describes it.
948
+ def each_package_set(load_description = true, &block)
815
949
  if !block_given?
816
- return enum_for(:each_source, load_description)
950
+ return enum_for(:each_package_set, load_description)
817
951
  end
818
952
 
819
- if @sources
953
+ if @package_sets
820
954
  if load_description
821
- @sources.each do |src|
955
+ @package_sets.each do |src|
822
956
  if !src.source_definition
823
957
  src.load_description_file
824
958
  end
825
959
  end
826
960
  end
827
- return @sources.each(&block)
961
+ return @package_sets.each(&block)
828
962
  end
829
963
 
830
- all_sources = []
964
+ explicit_sets = (data['package_sets'] || []).map do |spec|
965
+ Autoproj.in_file(self.file) do
966
+ PackageSet.from_spec(self, spec, load_description)
967
+ end
968
+ end
831
969
 
832
- (data['package_sets'] || []).each do |spec|
833
- all_sources << source_from_spec(spec, load_description)
970
+ all_sets = Array.new
971
+ explicit_sets.each do |pkg_set|
972
+ all_sets.concat(enumerate_package_set(pkg_set, explicit_sets, all_sets + [pkg_set]))
834
973
  end
835
974
 
836
975
  # Now load the local source
837
- local = LocalSource.new
976
+ local = LocalPackageSet.new(self)
838
977
  if load_description
839
978
  local.load_description_file
840
979
  else
841
- local.load_name
980
+ local.load_minimal
842
981
  end
843
982
  if !load_description || !local.empty?
844
- all_sources << local
983
+ all_sets << local
984
+ end
985
+
986
+ if load_description
987
+ all_sets.each(&:load_description_file)
988
+ else
989
+ all_sets.each(&:load_minimal)
845
990
  end
991
+ all_sets.each(&block)
992
+ end
993
+
994
+ # DEPRECATED. For backward-compatibility only
995
+ #
996
+ # use #each_package_set instead
997
+ def each_source(*args, &block)
998
+ each_package_set(*args, &block)
999
+ end
846
1000
 
847
- all_sources.each(&block)
848
- @sources = all_sources
1001
+ # Save the currently known package sets. After this call,
1002
+ # #each_package_set will always return the same set regardless of
1003
+ # changes on the manifest's data structures
1004
+ def cache_package_sets
1005
+ @package_sets = each_package_set(false).to_a
849
1006
  end
850
1007
 
851
1008
  # Register a new package
@@ -860,6 +1017,10 @@ module Autoproj
860
1017
  @packages[package_name].file
861
1018
  end
862
1019
 
1020
+ def package(name)
1021
+ packages[name]
1022
+ end
1023
+
863
1024
  # Lists all defined packages and where they have been defined
864
1025
  def each_package
865
1026
  if !block_given?
@@ -877,11 +1038,11 @@ module Autoproj
877
1038
  end
878
1039
 
879
1040
  if vcs
880
- yield(vcs, "autoproj main configuration", "autoproj_config", Autoproj.config_dir)
1041
+ yield(vcs, "autoproj main configuration", Autoproj.config_dir)
881
1042
  end
882
1043
 
883
1044
  each_remote_source(false) do |source|
884
- yield(source.vcs, source.name || source.vcs.url, source.automatic_name, source.local_dir)
1045
+ yield(source.vcs, source.name || source.vcs.url, source.local_dir)
885
1046
  end
886
1047
  self
887
1048
  end
@@ -893,81 +1054,108 @@ module Autoproj
893
1054
  # the name used when displaying the import progress, +pkg_name+ the
894
1055
  # internal name used to represent the package and +into+ the directory
895
1056
  # in which the package should be checked out.
896
- def self.create_autobuild_package(vcs, text_name, pkg_name, into)
1057
+ def self.create_autobuild_package(vcs, text_name, into)
1058
+ if !into
1059
+ raise
1060
+ end
897
1061
  importer = vcs.create_autobuild_importer
898
1062
  return if !importer # updates have been disabled by using the 'none' type
899
1063
 
900
- fake_package = FakePackage.new(text_name, pkg_name, into)
901
- fake_package.importer = importer
902
- fake_package
1064
+ FakePackage.new(text_name, into, importer)
903
1065
 
904
1066
  rescue Autobuild::ConfigException => e
905
- raise ConfigError, "cannot import #{name}: #{e.message}", e.backtrace
1067
+ raise ConfigError.new, "cannot import #{name}: #{e.message}", e.backtrace
906
1068
  end
907
1069
 
908
1070
  # Imports or updates a source (remote or otherwise).
909
1071
  #
910
1072
  # See create_autobuild_package for informations about the arguments.
911
- def self.update_source(vcs, text_name, pkg_name, into)
912
- fake_package = create_autobuild_package(vcs, text_name, pkg_name, into)
1073
+ def self.update_package_set(vcs, text_name, into)
1074
+ if !into
1075
+ raise
1076
+ end
1077
+ fake_package = create_autobuild_package(vcs, text_name, into)
913
1078
  fake_package.import
914
1079
 
915
1080
  rescue Autobuild::ConfigException => e
916
- raise ConfigError, "cannot import #{name}: #{e.message}", e.backtrace
1081
+ raise ConfigError.new, "cannot import #{name}: #{e.message}", e.backtrace
917
1082
  end
918
1083
 
919
1084
  # Updates the main autoproj configuration
920
1085
  def update_yourself
921
- Manifest.update_source(vcs, "autoproj main configuration", "autoproj_conf", Autoproj.config_dir)
1086
+ Manifest.update_package_set(vcs, "autoproj main configuration", Autoproj.config_dir)
922
1087
  end
923
1088
 
924
- # Updates all the remote sources in ROOT_DIR/.remotes, as well as the
925
- # symbolic links in ROOT_DIR/autoproj/remotes
926
- def update_remote_sources
927
- # Iterate on the remote sources, without loading the source.yml
928
- # file (we're not ready for that yet)
929
- sources = []
930
- each_remote_source(false) do |source|
931
- name = if source.present? then source.name
932
- else source.vcs.url
933
- end
934
- Manifest.update_source(source.vcs, name, source.automatic_name, source.raw_local_dir)
935
- sources << source
936
- end
1089
+ def update_remote_set(pkg_set, remotes_symlinks_dir = nil)
1090
+ Manifest.update_package_set(
1091
+ pkg_set.vcs,
1092
+ pkg_set.name,
1093
+ pkg_set.raw_local_dir)
937
1094
 
938
- # Check for directories in ROOT_DIR/.remotes that do not map to a
939
- # source repository, and remove them
940
- Dir.glob(File.join(Autoproj.remotes_dir, '*')).each do |dir|
941
- dir = File.expand_path(dir)
942
- if File.directory?(dir) && !sources.any? { |s| s.raw_local_dir == dir }
943
- FileUtils.rm_rf dir
944
- end
945
- end
946
-
947
- remotes_symlinks_dir = File.join(Autoproj.config_dir, 'remotes')
948
- FileUtils.mkdir_p remotes_symlinks_dir
949
- known_remotes = []
950
-
951
- # Create symbolic links from .remotes/weird_url to
952
- # autoproj/remotes/name. Explicitely load the source name first
953
- each_remote_source(false) do |source|
954
- source.load_name
955
- symlink_dest = File.join(remotes_symlinks_dir, source.name)
1095
+ if remotes_symlinks_dir
1096
+ pkg_set.load_minimal
1097
+ symlink_dest = File.join(remotes_symlinks_dir, pkg_set.name)
956
1098
 
957
1099
  # Check if the current symlink is valid, and recreate it if it
958
1100
  # is not
959
1101
  if File.symlink?(symlink_dest)
960
1102
  dest = File.readlink(symlink_dest)
961
- if dest != source.raw_local_dir
1103
+ if dest != pkg_set.raw_local_dir
962
1104
  FileUtils.rm_f symlink_dest
963
- FileUtils.ln_sf source.raw_local_dir, symlink_dest
1105
+ FileUtils.ln_sf pkg_set.raw_local_dir, symlink_dest
964
1106
  end
965
1107
  else
966
1108
  FileUtils.rm_f symlink_dest
967
- FileUtils.ln_sf source.raw_local_dir, symlink_dest
1109
+ FileUtils.ln_sf pkg_set.raw_local_dir, symlink_dest
1110
+ end
1111
+
1112
+ symlink_dest
1113
+ end
1114
+ end
1115
+
1116
+ # Updates all the remote sources in ROOT_DIR/.remotes, as well as the
1117
+ # symbolic links in ROOT_DIR/autoproj/remotes
1118
+ def update_remote_package_sets
1119
+ remotes_symlinks_dir = File.join(Autoproj.config_dir, 'remotes')
1120
+ FileUtils.mkdir_p remotes_symlinks_dir
1121
+
1122
+ # Iterate on the remote sources, without loading the source.yml
1123
+ # file (we're not ready for that yet)
1124
+ #
1125
+ # Do it iteratively to properly take imports into account, but we
1126
+ # first unconditionally update all the existing sets to properly
1127
+ # handle imports that have been removed
1128
+ updated_sets = Hash.new
1129
+ known_remotes = []
1130
+ each_remote_package_set(false) do |pkg_set|
1131
+ next if !pkg_set.explicit?
1132
+ if pkg_set.present?
1133
+ known_remotes << update_remote_set(pkg_set, remotes_symlinks_dir)
1134
+ updated_sets[pkg_set.repository_id] = pkg_set
1135
+ end
1136
+ end
1137
+
1138
+ old_updated_sets = nil
1139
+ while old_updated_sets != updated_sets
1140
+ old_updated_sets = updated_sets.dup
1141
+ each_remote_package_set(false) do |pkg_set|
1142
+ next if updated_sets.has_key?(pkg_set.repository_id)
1143
+
1144
+ if !pkg_set.explicit?
1145
+ Autoproj.progress " #{pkg_set.imported_from.name}: auto-importing #{pkg_set.name}"
1146
+ end
1147
+ known_remotes << update_remote_set(pkg_set, remotes_symlinks_dir)
1148
+ updated_sets[pkg_set.repository_id] = pkg_set
968
1149
  end
1150
+ end
969
1151
 
970
- known_remotes << symlink_dest
1152
+ # Check for directories in ROOT_DIR/.remotes that do not map to a
1153
+ # source repository, and remove them
1154
+ Dir.glob(File.join(Autoproj.remotes_dir, '*')).each do |dir|
1155
+ dir = File.expand_path(dir)
1156
+ if File.directory?(dir) && !updated_sets.values.find { |pkg| pkg.raw_local_dir == dir }
1157
+ FileUtils.rm_rf dir
1158
+ end
971
1159
  end
972
1160
 
973
1161
  # Now remove obsolete symlinks
@@ -978,6 +1166,11 @@ module Autoproj
978
1166
  end
979
1167
  end
980
1168
 
1169
+ # DEPRECATED. For backward-compatibility only
1170
+ def update_remote_sources(*args, &block)
1171
+ update_remote_package_sets(*args, &block)
1172
+ end
1173
+
981
1174
  def importer_definition_for(package_name, package_source = nil)
982
1175
  if !package_source
983
1176
  package_source = packages.values.
@@ -985,13 +1178,16 @@ module Autoproj
985
1178
  package_set
986
1179
  end
987
1180
 
988
- sources = each_source.to_a
1181
+ sources = each_source.to_a.dup
989
1182
 
990
1183
  # Remove sources listed before the package source
991
1184
  while !sources.empty? && sources[0].name != package_source.name
992
1185
  sources.shift
993
1186
  end
994
1187
  package_source = sources.shift
1188
+ if !package_source
1189
+ raise InternalError, "cannot find the package set that defines #{package_name}"
1190
+ end
995
1191
 
996
1192
  # Get the version control information from the package source. There
997
1193
  # must be one
@@ -1032,7 +1228,7 @@ module Autoproj
1032
1228
  Autoproj.add_build_system_dependency vcs.type
1033
1229
  pkg.autobuild.importer = vcs.create_autobuild_importer
1034
1230
  else
1035
- raise ConfigError, "source #{pkg.package_set.name} defines #{pkg.autobuild.name}, but does not provide a version control definition for it"
1231
+ raise ConfigError.new, "source #{pkg.package_set.name} defines #{pkg.autobuild.name}, but does not provide a version control definition for it"
1036
1232
  end
1037
1233
  end
1038
1234
  end
@@ -1044,12 +1240,12 @@ module Autoproj
1044
1240
  if Autobuild::Package[name]
1045
1241
  [name]
1046
1242
  else
1047
- source = each_source.find { |source| source.name == name }
1048
- if !source
1049
- raise ConfigError, "in #{file}: #{name} is neither a package nor a source"
1243
+ pkg_set = each_package_set(false).find { |set| set.name == name }
1244
+ if !pkg_set
1245
+ raise ConfigError.new, "#{name} is neither a package nor a source"
1050
1246
  end
1051
1247
  packages.values.
1052
- find_all { |pkg| pkg.package_set.name == source.name }.
1248
+ find_all { |pkg| pkg.package_set.name == pkg_set.name }.
1053
1249
  map { |pkg| pkg.autobuild.name }.
1054
1250
  find_all { |pkg_name| !Autoproj.osdeps || !Autoproj.osdeps.has?(pkg_name) }
1055
1251
  end
@@ -1059,11 +1255,15 @@ module Autoproj
1059
1255
  #
1060
1256
  # If recursive is false, yields only the packages at this level.
1061
1257
  # Otherwise, return all packages.
1062
- def layout_packages(layout_def, recursive)
1258
+ def layout_packages(layout_def, recursive, validate = true)
1063
1259
  result = []
1064
1260
  layout_def.each do |value|
1065
1261
  if !value.kind_of?(Hash) # sublayout
1066
- result.concat(resolve_package_set(value))
1262
+ begin
1263
+ result.concat(resolve_package_set(value))
1264
+ rescue ConfigError
1265
+ raise if validate
1266
+ end
1067
1267
  end
1068
1268
  end
1069
1269
 
@@ -1088,7 +1288,7 @@ module Autoproj
1088
1288
 
1089
1289
  # Looks into the layout setup in the manifest, and yields each layout
1090
1290
  # and sublayout in order
1091
- def each_package_set(selection = nil, layout_name = '/', layout_def = data['layout'], &block)
1291
+ def each_layout_level(selection = nil, layout_name = '/', layout_def = data['layout'], &block)
1092
1292
  if !layout_def
1093
1293
  yield(layout_name, default_packages, default_packages)
1094
1294
  return nil
@@ -1112,14 +1312,14 @@ module Autoproj
1112
1312
 
1113
1313
  # Now, enumerate the sublayouts
1114
1314
  each_sublayout(layout_def) do |subname, sublayout|
1115
- each_package_set(selection, "#{layout_name}#{subname}/", sublayout, &block)
1315
+ each_layout_level(selection, "#{layout_name}#{subname}/", sublayout, &block)
1116
1316
  end
1117
1317
  end
1118
1318
 
1119
1319
  # Returns the set of package names that are explicitely listed in the
1120
1320
  # layout, minus the excluded and ignored ones
1121
- def all_layout_packages
1122
- default_packages
1321
+ def all_layout_packages(validate = true)
1322
+ default_packages(validate)
1123
1323
  end
1124
1324
 
1125
1325
  # Returns all defined package names, minus the excluded and ignored ones
@@ -1134,11 +1334,30 @@ module Autoproj
1134
1334
  find_all { |pkg_name| !Autoproj.osdeps || !Autoproj.osdeps.has?(pkg_name) }
1135
1335
  end
1136
1336
 
1337
+ # Returns true if +name+ is a valid package and is included in the build
1338
+ #
1339
+ # If +validate+ is true, the layout will have to be well-defined. It can
1340
+ # therefore be used only after all sources have been successfully
1341
+ # loaded.
1342
+ #
1343
+ # If it is false, non-defined packages/package sets in the layout will
1344
+ # simply be ignored
1345
+ def package_enabled?(name, validate = true)
1346
+ if !Autobuild::Package[name]
1347
+ if validate
1348
+ raise ArgumentError, "package #{name} does not exist"
1349
+ end
1350
+ return false
1351
+ end
1352
+
1353
+ !excluded?(name)
1354
+ end
1355
+
1137
1356
  # Returns the set of packages that should be built if the user does not
1138
1357
  # specify any on the command line
1139
- def default_packages
1358
+ def default_packages(validate = true)
1140
1359
  names = if layout = data['layout']
1141
- layout_packages(layout, true)
1360
+ layout_packages(layout, true, validate)
1142
1361
  else
1143
1362
  # No layout, all packages are selected
1144
1363
  all_packages
@@ -1150,12 +1369,20 @@ module Autoproj
1150
1369
 
1151
1370
  # Returns the package directory for the given package name
1152
1371
  def whereis(package_name)
1153
- each_package_set do |layout_name, packages, _|
1154
- if packages.include?(package_name)
1155
- return layout_name
1372
+ Autoproj.in_file(self.file) do
1373
+ each_layout_level do |layout_name, packages, _|
1374
+ if packages.include?(package_name)
1375
+ return layout_name
1376
+ end
1156
1377
  end
1378
+ raise ArgumentError, "cannot find #{package_name} in the layout section"
1379
+ end
1380
+ end
1381
+
1382
+ def resolve_optional_dependencies
1383
+ packages.each_value do |pkg|
1384
+ pkg.autobuild.resolve_optional_dependencies
1157
1385
  end
1158
- raise ArgumentError, "cannot find #{package_name} in the current layout"
1159
1386
  end
1160
1387
 
1161
1388
  # Loads the package's manifest.xml file for the current package
@@ -1180,13 +1407,19 @@ module Autoproj
1180
1407
  manifest = PackageManifest.load(package, manifest_path)
1181
1408
  package_manifests[package.name] = manifest
1182
1409
 
1183
- manifest.each_dependency do |name|
1410
+ manifest.each_dependency do |name, is_optional|
1184
1411
  begin
1185
- package.depends_on name
1412
+ if is_optional
1413
+ package.optional_dependency name
1414
+ else
1415
+ package.depends_on name
1416
+ end
1186
1417
  rescue Autobuild::ConfigException => e
1187
- raise ConfigError, "manifest #{manifest_path} of #{package.name} from #{source.name} lists '#{name}' as dependency, which is listed in the layout of #{file} but has no autobuild definition", e.backtrace
1418
+ raise ConfigError.new(manifest_path),
1419
+ "manifest #{manifest_path} of #{package.name} from #{source.name} lists '#{name}' as dependency, which is listed in the layout of #{file} but has no autobuild definition", e.backtrace
1188
1420
  rescue ConfigError => e
1189
- raise ConfigError, "manifest #{manifest_path} of #{package.name} from #{source.name} lists '#{name}' as dependency, but it is neither a normal package nor an osdeps package. osdeps reports: #{e.message}", e.backtrace
1421
+ raise ConfigError.new(manifest_path),
1422
+ "manifest #{manifest_path} of #{package.name} from #{source.name} lists '#{name}' as dependency, but it is neither a normal package nor an osdeps package. osdeps reports: #{e.message}", e.backtrace
1190
1423
  end
1191
1424
  end
1192
1425
  end
@@ -1198,6 +1431,11 @@ module Autoproj
1198
1431
  selected_packages.each(&:load_package_manifest)
1199
1432
  end
1200
1433
 
1434
+ # Disable all automatic imports from the given package set name
1435
+ def disable_imports_from(pkg_set_name)
1436
+ @disabled_imports << pkg_set_name
1437
+ end
1438
+
1201
1439
  # call-seq:
1202
1440
  # list_os_dependencies(packages) => required_packages, ospkg_to_pkg
1203
1441
  #
@@ -1264,7 +1502,7 @@ module Autoproj
1264
1502
  end
1265
1503
 
1266
1504
  # Now, search for layout names
1267
- each_package_set(nil) do |layout_name, packages, _|
1505
+ each_layout_level(nil) do |layout_name, packages, _|
1268
1506
  selected_packages.each do |sel|
1269
1507
  if layout_name[0..-1] =~ Regexp.new("#{sel}\/?$")
1270
1508
  expanded_packages |= packages.to_set
@@ -1325,6 +1563,15 @@ module Autoproj
1325
1563
  # The raw XML data as a Nokogiri document
1326
1564
  attr_reader :xml
1327
1565
 
1566
+ def short_documentation
1567
+ xml.xpath('//description').each do |node|
1568
+ if doc = node['brief']
1569
+ return doc.to_s
1570
+ end
1571
+ end
1572
+ nil
1573
+ end
1574
+
1328
1575
  def initialize(package, doc)
1329
1576
  @package = package
1330
1577
  @xml = doc
@@ -1342,10 +1589,10 @@ module Autoproj
1342
1589
  def each_os_dependency
1343
1590
  if block_given?
1344
1591
  xml.xpath('//rosdep').each do |node|
1345
- yield(node['name'])
1592
+ yield(node['name'], false)
1346
1593
  end
1347
1594
  package.os_packages.each do |name|
1348
- yield(name)
1595
+ yield(name, false)
1349
1596
  end
1350
1597
  else
1351
1598
  enum_for :each_os_dependency
@@ -1356,10 +1603,16 @@ module Autoproj
1356
1603
  if block_given?
1357
1604
  xml.xpath('//depend').each do |node|
1358
1605
  dependency = node['package']
1606
+ optional =
1607
+ if node['optional'].to_s == '1'
1608
+ true
1609
+ else false
1610
+ end
1611
+
1359
1612
  if dependency
1360
- yield(dependency)
1613
+ yield(dependency, optional)
1361
1614
  else
1362
- raise ConfigError, "manifest of #{package.name} has a <depend> tag without a 'package' attribute"
1615
+ raise ConfigError.new, "manifest of #{package.name} has a <depend> tag without a 'package' attribute"
1363
1616
  end
1364
1617
  end
1365
1618
  else