autoproj 1.4.4 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,17 @@ ruby18:
12
12
  - dev-lang/ruby:1.8
13
13
 
14
14
  ruby19:
15
- debian,ubuntu:
15
+ debian:
16
+ squeeze,sid:
17
+ - ruby1.9.1
18
+ - ruby1.9.1-dev
19
+ - rubygems1.9.1
20
+ stable:
21
+ - ruby1.9.1
22
+ - ruby1.9.1-dev
23
+ - rubygems1.9.1
24
+
25
+ ubuntu:
16
26
  - ruby1.9.1
17
27
  - ruby1.9.1-dev
18
28
  - rubygems1.9.1
@@ -46,7 +56,7 @@ autoproj: gem
46
56
  # The following definitions are for the VCS and build systems
47
57
  git:
48
58
  debian,ubuntu: git-core
49
- gentoo: dev-util/git
59
+ gentoo: dev-vcs/git
50
60
  arch: git
51
61
  svn:
52
62
  debian,ubuntu: subversion
@@ -3,18 +3,6 @@ require 'utilrb/kernel/options'
3
3
  require 'nokogiri'
4
4
  require 'set'
5
5
 
6
- module Autobuild
7
- class Package
8
- def os_packages
9
- @os_packages || Array.new
10
- end
11
- def depends_on_os_package(name)
12
- @os_packages ||= Array.new
13
- @os_packages << name
14
- end
15
- end
16
- end
17
-
18
6
  module Autoproj
19
7
  @build_system_dependencies = Set.new
20
8
 
@@ -106,13 +94,22 @@ module Autoproj
106
94
  @type == 'local'
107
95
  end
108
96
 
97
+ def self.to_absolute_url(url, root_dir = nil)
98
+ # NOTE: we MUST use nil as default argument of root_dir as we don't
99
+ # want to call Autoproj.root_dir unless completely necessary
100
+ # (to_absolute_url might be called on installations that are being
101
+ # bootstrapped, and as such don't have a root dir yet).
102
+ url = Autoproj.single_expansion(url, 'HOME' => ENV['HOME'])
103
+ if url && url !~ /^(\w+:\/)?\/|^\w+\@|^(\w+\@)?[\w\.-]+:/
104
+ url = File.expand_path(url, root_dir || Autoproj.root_dir)
105
+ end
106
+ url
107
+ end
108
+
109
109
  def create_autobuild_importer
110
110
  return if type == "none"
111
111
 
112
- url = Autoproj.single_expansion(self.url, 'HOME' => ENV['HOME'])
113
- if url && url !~ /^(\w+:\/)?\/|^\w+\@|^(\w+\@)?[\w\.-]+:/
114
- url = File.expand_path(url, Autoproj.root_dir)
115
- end
112
+ url = VCSDefinition.to_absolute_url(self.url)
116
113
  Autobuild.send(type, url, options)
117
114
  end
118
115
 
@@ -196,26 +193,44 @@ module Autoproj
196
193
 
197
194
  # True if this source has already been checked out on the local autoproj
198
195
  # installation
199
- def present?; File.directory?(local_dir) end
196
+ def present?; File.directory?(raw_local_dir) end
200
197
  # True if this source is local, i.e. is not under a version control
201
198
  def local?; vcs.local? end
202
199
  # True if this source defines nothing
203
200
  def empty?
204
201
  !source_definition['version_control'] &&
205
202
  !each_package.find { true } &&
206
- !File.exists?(File.join(local_dir, "overrides.rb")) &&
207
- !File.exists?(File.join(local_dir, "init.rb"))
203
+ !File.exists?(File.join(raw_local_dir, "overrides.rb")) &&
204
+ !File.exists?(File.join(raw_local_dir, "init.rb"))
208
205
  end
209
206
 
210
- # The directory in which data for this source will be checked out
211
- def local_dir
207
+ def raw_local_dir
212
208
  if local?
213
- vcs.url
209
+ return vcs.url
214
210
  else
215
211
  File.join(Autoproj.remotes_dir, automatic_name)
216
212
  end
217
213
  end
218
214
 
215
+ def user_local_dir
216
+ if local?
217
+ return vcs.url
218
+ else
219
+ File.join(Autoproj.config_dir, 'remotes', name)
220
+ end
221
+ end
222
+
223
+ # The directory in which data for this source will be checked out
224
+ def local_dir
225
+ ugly_dir = raw_local_dir
226
+ pretty_dir = user_local_dir
227
+ if File.symlink?(pretty_dir) && File.readlink(pretty_dir) == ugly_dir
228
+ pretty_dir
229
+ else
230
+ ugly_dir
231
+ end
232
+ end
233
+
219
234
  # A name generated from the VCS url
220
235
  def automatic_name
221
236
  vcs.to_s.gsub(/[^\w]/, '_')
@@ -237,7 +252,7 @@ module Autoproj
237
252
  raise InternalError, "source #{vcs} has not been fetched yet, cannot load description for it"
238
253
  end
239
254
 
240
- source_file = File.join(local_dir, "source.yml")
255
+ source_file = File.join(raw_local_dir, "source.yml")
241
256
  if !File.exists?(source_file)
242
257
  raise ConfigError, "source #{vcs.type}:#{vcs.url} should have a source.yml file, but does not"
243
258
  end
@@ -255,21 +270,30 @@ module Autoproj
255
270
  source_definition
256
271
  end
257
272
 
273
+ # Load and validate the name from the YAML hash
258
274
  def load_name
259
- definition = raw_description_file
275
+ definition = @source_definition || raw_description_file
260
276
  @name = definition['name']
261
- if @name == "local"
277
+
278
+ if @name !~ /^[\w_\.-]+$/
279
+ raise ConfigError, "invalid source name '#{@name}': source names can only contain alphanumeric characters, and .-_"
280
+ elsif @name == "local"
262
281
  raise ConfigError, "source #{self} is named 'local', but this is a reserved name"
263
282
  end
264
283
 
265
284
  rescue InternalError
266
285
  end
267
286
 
287
+ def source_file
288
+ File.join(local_dir, 'source.yml')
289
+ end
290
+
268
291
  # Load the source.yml file that describes this source, and resolve the
269
292
  # $BLABLA values that are in there. Use #raw_description_file to avoid
270
293
  # resolving those values
271
294
  def load_description_file
272
295
  @source_definition = raw_description_file
296
+ load_name
273
297
 
274
298
  # Compute the definition of constants
275
299
  begin
@@ -342,8 +366,12 @@ module Autoproj
342
366
  urls['HOME'] = ENV['HOME']
343
367
 
344
368
  all_vcs = source_definition['version_control']
345
- if all_vcs && !all_vcs.kind_of?(Array)
346
- raise ConfigError, "wrong format for the version_control field"
369
+ if all_vcs
370
+ if all_vcs.kind_of?(Hash)
371
+ raise ConfigError, "wrong format for the version_control field, you forgot the '-' in front of the package names"
372
+ elsif !all_vcs.kind_of?(Array)
373
+ raise ConfigError, "wrong format for the version_control field"
374
+ end
347
375
  end
348
376
 
349
377
  vcs_spec = Hash.new
@@ -412,7 +440,7 @@ module Autoproj
412
440
  Autoproj.normalize_vcs_definition(vcs_spec)
413
441
  end
414
442
  rescue ConfigError => e
415
- raise ConfigError, "#{e.message} in the source.yml file of #{name} (#{File.join(local_dir, "source.yml")})", e.backtrace
443
+ raise ConfigError, "#{e.message} in #{source_file}", e.backtrace
416
444
  end
417
445
 
418
446
  def each_package
@@ -439,8 +467,12 @@ module Autoproj
439
467
  def load_name
440
468
  end
441
469
 
470
+ def source_file
471
+ File.join(Autoproj.config_dir, "overrides.yml")
472
+ end
473
+
442
474
  def raw_description_file
443
- path = File.join(Autoproj.config_dir, "overrides.yml")
475
+ path = source_file
444
476
  if File.file?(path)
445
477
  begin
446
478
  data = YAML.load(File.read(path)) || Hash.new
@@ -458,7 +490,7 @@ module Autoproj
458
490
  PackageDefinition = Struct.new :autobuild, :user_block, :package_set, :file
459
491
 
460
492
  class Manifest
461
- FakePackage = Struct.new :text_name, :name, :srcdir, :importer
493
+ FakePackage = Struct.new :text_name, :name, :srcdir, :importer, :updated
462
494
  class FakePackage
463
495
  def autoproj_name; name end
464
496
  def import
@@ -503,6 +535,10 @@ module Autoproj
503
535
 
504
536
  attr_reader :file
505
537
 
538
+ def auto_update?
539
+ !!data['auto_update']
540
+ end
541
+
506
542
  def initialize(file, data)
507
543
  @file = file
508
544
  @data = data
@@ -633,36 +669,26 @@ module Autoproj
633
669
  return @sources.each(&block)
634
670
  end
635
671
 
636
- # Load the local source first ...
672
+ all_sources = []
673
+
674
+ (data['package_sets'] || []).each do |spec|
675
+ all_sources << source_from_spec(spec, load_description)
676
+ end
677
+
678
+ # Now load the local source
637
679
  local = LocalSource.new
638
680
  if load_description
639
681
  local.load_description_file
640
682
  else
641
683
  local.load_name
642
684
  end
643
- if load_description
644
- if !local.empty?
645
- @sources = [local]
646
- else
647
- @sources = []
648
- end
649
- else
650
- yield(local)
651
- end
652
-
653
- return if !data['package_sets']
654
-
655
- data['package_sets'].each do |spec|
656
- source = source_from_spec(spec, load_description)
657
- if load_description
658
- @sources << source
659
- else
660
- yield(source)
661
- end
685
+ if !load_description || !local.empty?
686
+ all_sources << local
662
687
  end
663
688
 
689
+ all_sources.each(&block)
664
690
  if load_description
665
- @sources.each(&block)
691
+ @sources = all_sources
666
692
  end
667
693
  end
668
694
 
@@ -704,6 +730,13 @@ module Autoproj
704
730
  self
705
731
  end
706
732
 
733
+ # Creates an autobuild package whose job is to allow the import of a
734
+ # specific repository into a given directory.
735
+ #
736
+ # +vcs+ is the VCSDefinition file describing the repository, +text_name+
737
+ # the name used when displaying the import progress, +pkg_name+ the
738
+ # internal name used to represent the package and +into+ the directory
739
+ # in which the package should be checked out.
707
740
  def self.create_autobuild_package(vcs, text_name, pkg_name, into)
708
741
  importer = vcs.create_autobuild_importer
709
742
  return if !importer # updates have been disabled by using the 'none' type
@@ -716,6 +749,9 @@ module Autoproj
716
749
  raise ConfigError, "cannot import #{name}: #{e.message}", e.backtrace
717
750
  end
718
751
 
752
+ # Imports or updates a source (remote or otherwise).
753
+ #
754
+ # See create_autobuild_package for informations about the arguments.
719
755
  def self.update_source(vcs, text_name, pkg_name, into)
720
756
  fake_package = create_autobuild_package(vcs, text_name, pkg_name, into)
721
757
  fake_package.import
@@ -724,15 +760,38 @@ module Autoproj
724
760
  raise ConfigError, "cannot import #{name}: #{e.message}", e.backtrace
725
761
  end
726
762
 
763
+ # Updates the main autoproj configuration
727
764
  def update_yourself
728
765
  Manifest.update_source(vcs, "autoproj main configuration", "autoproj_conf", Autoproj.config_dir)
729
766
  end
730
767
 
768
+ # Updates all the remote sources in ROOT_DIR/.remotes, as well as the
769
+ # symbolic links in ROOT_DIR/autoproj/remotes
731
770
  def update_remote_sources
732
771
  # Iterate on the remote sources, without loading the source.yml
733
772
  # file (we're not ready for that yet)
773
+ sources = []
734
774
  each_remote_source(false) do |source|
735
- Manifest.update_source(source.vcs, source.name || source.vcs.url, source.automatic_name, source.local_dir)
775
+ Manifest.update_source(source.vcs, source.name || source.vcs.url, source.automatic_name, source.raw_local_dir)
776
+ sources << source
777
+ end
778
+
779
+ # Check for directories in ROOT_DIR/.remotes that do not map to a
780
+ # source repository, and remove them
781
+ Dir.glob(File.join(Autoproj.remotes_dir, '*')).each do |dir|
782
+ dir = File.expand_path(dir)
783
+ if File.directory?(dir) && !sources.any? { |s| s.raw_local_dir == dir }
784
+ FileUtils.rm_rf dir
785
+ end
786
+ end
787
+
788
+ remotes_symlinks_dir = File.join(Autoproj.config_dir, 'remotes')
789
+ FileUtils.rm_rf remotes_symlinks_dir
790
+ FileUtils.mkdir remotes_symlinks_dir
791
+ # Create symbolic links from .remotes/weird_url to
792
+ # autoproj/remotes/name
793
+ each_remote_source(false) do |source|
794
+ FileUtils.ln_sf source.raw_local_dir, File.join(remotes_symlinks_dir, source.name)
736
795
  end
737
796
  end
738
797
 
@@ -772,7 +831,8 @@ module Autoproj
772
831
  # by S1
773
832
  def load_importers
774
833
  packages.each_value do |pkg|
775
- vcs = importer_definition_for(pkg.autobuild.name, pkg.package_set)
834
+ vcs = importer_definition_for(pkg.autobuild.name, pkg.package_set) ||
835
+ importer_definition_for("default", pkg.package_set)
776
836
 
777
837
  if vcs
778
838
  Autoproj.add_build_system_dependency vcs.type
@@ -876,21 +936,6 @@ module Autoproj
876
936
  yield
877
937
  end
878
938
 
879
- def handle_enabled_packages(selected_packages)
880
- handled_packages = Set.new
881
- each_package_set(selected_packages) do |name, packages, enabled_packages|
882
- packages -= handled_packages
883
- enabled_packages -= handled_packages
884
-
885
- in_sublayout(name, packages) do
886
- if !packages.empty?
887
- yield(name, packages, enabled_packages)
888
- end
889
- end
890
- handled_packages |= packages
891
- end
892
- end
893
-
894
939
  def default_packages
895
940
  names = if layout = data['layout']
896
941
  layout_packages(layout, true)
@@ -925,10 +970,7 @@ module Autoproj
925
970
  manifest = PackageManifest.load(package, manifest_path)
926
971
  package_manifests[package.name] = manifest
927
972
 
928
- manifest.each_package_dependency do |name|
929
- if Autoproj.verbose
930
- STDERR.puts " #{package.name} depends on #{name}"
931
- end
973
+ manifest.each_dependency do |name|
932
974
  begin
933
975
  package.depends_on name
934
976
  rescue Autobuild::ConfigException => e
@@ -959,18 +1001,14 @@ module Autoproj
959
1001
  required_os_packages = Set.new
960
1002
  package_os_deps = Hash.new { |h, k| h[k] = Array.new }
961
1003
  packages.each do |pkg_name|
962
- if manifest = package_manifests[pkg_name]
963
- manifest.each_os_dependency.each do |osdep_name|
964
- package_os_deps[osdep_name] << pkg_name
965
- required_os_packages << osdep_name
966
- end
1004
+ pkg = Autobuild::Package[pkg_name]
1005
+ if !pkg
1006
+ raise InternalError, "internal error: #{pkg_name} is not a package"
967
1007
  end
968
1008
 
969
- if pkg = Autobuild::Package[pkg_name]
970
- pkg.os_packages.each do |osdep_name|
971
- package_os_deps[osdep_name] << pkg_name
972
- required_os_packages << osdep_name
973
- end
1009
+ pkg.os_packages.each do |osdep_name|
1010
+ package_os_deps[osdep_name] << pkg_name
1011
+ required_os_packages << osdep_name
974
1012
  end
975
1013
  end
976
1014
 
@@ -1020,11 +1058,12 @@ module Autoproj
1020
1058
  if layout_name[0..-1] =~ Regexp.new("#{sel}\/?$")
1021
1059
  expanded_packages.concat(packages.to_a)
1022
1060
  else
1023
- packages = packages.find_all do |pkg_name|
1024
- sel =~ Regexp.new(Regexp.quote(layout_name + pkg_name))
1061
+ match = Regexp.new("^#{Regexp.quote(sel)}")
1062
+ Autobuild::Package.each(true) do |name, pkg|
1063
+ if pkg.srcdir =~ match
1064
+ expanded_packages << name
1065
+ end
1025
1066
  end
1026
- expanded_packages.concat(packages)
1027
- !packages.empty?
1028
1067
  end
1029
1068
  end
1030
1069
  end
@@ -1032,14 +1071,19 @@ module Autoproj
1032
1071
  end
1033
1072
  end
1034
1073
 
1035
- # The singleton manifest object on which the current run works
1036
1074
  class << self
1075
+ # The singleton manifest object on which the current run works
1037
1076
  attr_accessor :manifest
1077
+
1078
+ # The operating system package definitions
1079
+ attr_accessor :osdeps
1038
1080
  end
1039
1081
 
1040
1082
  class PackageManifest
1041
1083
  def self.load(package, file)
1042
- doc = Nokogiri::XML(File.read(file))
1084
+ doc = Nokogiri::XML(File.read(file)) do |c|
1085
+ c.noblanks
1086
+ end
1043
1087
  PackageManifest.new(package, doc)
1044
1088
  end
1045
1089
 
@@ -1053,6 +1097,15 @@ module Autoproj
1053
1097
  @xml = doc
1054
1098
  end
1055
1099
 
1100
+ def each_dependency(&block)
1101
+ if block_given?
1102
+ each_os_dependency(&block)
1103
+ each_package_dependency(&block)
1104
+ else
1105
+ enum_for(:each_dependency, &block)
1106
+ end
1107
+ end
1108
+
1056
1109
  def each_os_dependency
1057
1110
  if block_given?
1058
1111
  xml.xpath('//rosdep').each do |node|
@@ -2,12 +2,12 @@ require 'tempfile'
2
2
  module Autoproj
3
3
  class OSDependencies
4
4
  def self.load(file)
5
- data =
6
- begin
7
- YAML.load(File.read(file))
8
- rescue ArgumentError => e
9
- raise ConfigError, "error in #{file}: #{e.message}"
10
- end
5
+ begin
6
+ data = YAML.load(File.read(file))
7
+ verify_definitions(data)
8
+ rescue ArgumentError => e
9
+ raise ConfigError, "error in #{file}: #{e.message}"
10
+ end
11
11
 
12
12
  OSDependencies.new(data)
13
13
  end
@@ -47,6 +47,23 @@ module Autoproj
47
47
  @definitions = definitions.merge(info.definitions)
48
48
  end
49
49
 
50
+ def self.verify_definitions(hash = nil)
51
+ hash ||= definitions
52
+ hash.each do |key, value|
53
+ if !key.kind_of?(String)
54
+ raise ArgumentError, "invalid osdeps definition: found an #{key.class}. Don't forget to put quotes around numbers"
55
+ end
56
+ next if !value
57
+ if value.kind_of?(Array) || value.kind_of?(Hash)
58
+ verify_definitions(value)
59
+ else
60
+ if !value.kind_of?(String)
61
+ raise ArgumentError, "invalid osdeps definition: found an #{value.class}. Don't forget to put quotes around numbers"
62
+ end
63
+ end
64
+ end
65
+ end
66
+
50
67
  def operating_system
51
68
  if @operating_system
52
69
  return @operating_system
@@ -144,7 +161,9 @@ module Autoproj
144
161
  version_entry = data.find do |version_list, data|
145
162
  version_list.to_s.split(',').
146
163
  map(&:downcase).
147
- any? { |v| os_version.include?(v) }
164
+ any? do |v|
165
+ os_version.any? { |osv| Regexp.new(v) =~ osv }
166
+ end
148
167
  end
149
168
 
150
169
  if !version_entry
@@ -168,8 +187,24 @@ module Autoproj
168
187
  "\n" + shell_snippets
169
188
  end
170
189
 
190
+ # Returns true if there is an operating-system package with that name,
191
+ # and false otherwise
192
+ def has?(name)
193
+ partition_packages([name].to_set)
194
+ true
195
+ rescue ConfigError
196
+ false
197
+ end
198
+
171
199
  # call-seq:
172
200
  # partition_packages(package_names) => os_packages, gem_packages
201
+ #
202
+ # Resolves the package names listed in +package_set+, and returns a set
203
+ # of packages that have to be installed using the platform's native
204
+ # package manager, and the set of packages that have to be installed
205
+ # using Ruby's package manager, RubyGems.
206
+ #
207
+ # Raises ConfigError if no package can be found
173
208
  def partition_packages(package_set, package_osdeps = Hash.new)
174
209
  package_set = package_set.
175
210
  map { |name| OSDependencies.aliases[name] || name }.