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.
- data/Manifest.txt +15 -0
- data/Rakefile +2 -1
- data/bin/autoproj +1 -2
- data/bin/autoproj-cache +56 -0
- data/bin/autoproj-doc +28 -0
- data/bin/autoproj-query +35 -32
- data/bin/autoproj-snapshot +38 -0
- data/bin/autoproj-test +36 -0
- data/bin/autoproj_bootstrap +104 -10
- data/lib/autoproj.rb +9 -0
- data/lib/autoproj/autobuild.rb +40 -99
- data/lib/autoproj/cmdline.rb +64 -37
- data/lib/autoproj/default.osdeps +30 -1
- data/lib/autoproj/environment.rb +78 -0
- data/lib/autoproj/installation_manifest.rb +36 -0
- data/lib/autoproj/loader.rb +0 -0
- data/lib/autoproj/manifest.rb +35 -1298
- data/lib/autoproj/metapackage.rb +51 -0
- data/lib/autoproj/options.rb +14 -0
- data/lib/autoproj/osdeps.rb +60 -8
- data/lib/autoproj/package_definition.rb +58 -0
- data/lib/autoproj/package_manifest.rb +155 -0
- data/lib/autoproj/package_selection.rb +153 -0
- data/lib/autoproj/package_set.rb +497 -0
- data/lib/autoproj/system.rb +1 -1
- data/lib/autoproj/variable_expansion.rb +107 -0
- data/lib/autoproj/vcs_definition.rb +223 -0
- data/lib/autoproj/version.rb +1 -1
- metadata +28 -10
@@ -0,0 +1,497 @@
|
|
1
|
+
module Autoproj
|
2
|
+
# A package set is a version control repository which contains general
|
3
|
+
# information with package version control information (source.yml file),
|
4
|
+
# package definitions (.autobuild files), and finally definition of
|
5
|
+
# dependencies that are provided by the operating system (.osdeps file).
|
6
|
+
class PackageSet
|
7
|
+
attr_reader :manifest
|
8
|
+
# The VCSDefinition object that defines the version control holding
|
9
|
+
# information for this source. Local package sets (i.e. the ones that are not
|
10
|
+
# under version control) use the 'local' version control name. For them,
|
11
|
+
# local? returns true.
|
12
|
+
attr_accessor :vcs
|
13
|
+
|
14
|
+
# The set of OSDependencies object that represent the osdeps files
|
15
|
+
# available in this package set
|
16
|
+
attr_reader :all_osdeps
|
17
|
+
|
18
|
+
# The OSDependencies which is a merged version of all OSdeps in
|
19
|
+
# #all_osdeps
|
20
|
+
attr_reader :osdeps
|
21
|
+
|
22
|
+
# If this package set has been imported from another package set, this
|
23
|
+
# is the other package set object
|
24
|
+
attr_accessor :imported_from
|
25
|
+
|
26
|
+
# If true, this package set has been loaded because another set imports
|
27
|
+
# it. If false, it is loaded explicitely by the user
|
28
|
+
def explicit?; !@imported_from end
|
29
|
+
|
30
|
+
attr_reader :source_definition
|
31
|
+
attr_reader :constants_definitions
|
32
|
+
|
33
|
+
# Sets the auto_imports flag. See #auto_imports?
|
34
|
+
attr_writer :auto_imports
|
35
|
+
# If true (the default), imports listed in this package set will be
|
36
|
+
# automatically loaded by autoproj
|
37
|
+
def auto_imports?; !!@auto_imports end
|
38
|
+
|
39
|
+
# Returns the Metapackage object that has the same name than this
|
40
|
+
# package set
|
41
|
+
def metapackage
|
42
|
+
manifest.metapackage(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# List of the packages that are built if the package set is selected in
|
46
|
+
# the layout
|
47
|
+
def default_packages
|
48
|
+
metapackage.packages
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create this source from a VCSDefinition object
|
52
|
+
def initialize(manifest, vcs)
|
53
|
+
@manifest = manifest
|
54
|
+
@vcs = vcs
|
55
|
+
@osdeps = OSDependencies.new
|
56
|
+
@all_osdeps = []
|
57
|
+
|
58
|
+
@provides = Set.new
|
59
|
+
@imports = Array.new
|
60
|
+
@auto_imports = true
|
61
|
+
end
|
62
|
+
|
63
|
+
# Load a new osdeps file for this package set
|
64
|
+
def load_osdeps(file)
|
65
|
+
new_osdeps = OSDependencies.load(file)
|
66
|
+
@all_osdeps << new_osdeps
|
67
|
+
@osdeps.merge(@all_osdeps.last)
|
68
|
+
new_osdeps
|
69
|
+
end
|
70
|
+
|
71
|
+
# Enumerate all osdeps package names from this package set
|
72
|
+
def each_osdep(&block)
|
73
|
+
@osdeps.definitions.each_key(&block)
|
74
|
+
end
|
75
|
+
|
76
|
+
# True if this source has already been checked out on the local autoproj
|
77
|
+
# installation
|
78
|
+
def present?; File.directory?(raw_local_dir) end
|
79
|
+
# True if this source is local, i.e. is not under a version control
|
80
|
+
def local?; vcs.local? end
|
81
|
+
# True if this source defines nothing
|
82
|
+
def empty?
|
83
|
+
!source_definition['version_control'] && !source_definition['overrides']
|
84
|
+
!each_package.find { true } &&
|
85
|
+
!File.exists?(File.join(raw_local_dir, "overrides.rb")) &&
|
86
|
+
!File.exists?(File.join(raw_local_dir, "init.rb"))
|
87
|
+
end
|
88
|
+
|
89
|
+
def snapshot(target_dir)
|
90
|
+
if local?
|
91
|
+
Hash.new
|
92
|
+
else
|
93
|
+
package = Manifest.create_autobuild_package(vcs, name, raw_local_dir)
|
94
|
+
package.importer.snapshot(package, target_dir)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Create a PackageSet instance from its description as found in YAML
|
99
|
+
# configuration files
|
100
|
+
def self.from_spec(manifest, raw_spec, load_description)
|
101
|
+
if raw_spec.respond_to?(:to_str)
|
102
|
+
local_path = File.join(Autoproj.config_dir, raw_spec)
|
103
|
+
if File.directory?(local_path)
|
104
|
+
raw_spec = { :type => 'local', :url => local_path }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
spec = VCSDefinition.vcs_definition_to_hash(raw_spec)
|
108
|
+
options, vcs_spec = Kernel.filter_options spec, :auto_imports => true
|
109
|
+
|
110
|
+
# Look up for short notation (i.e. not an explicit hash). It is
|
111
|
+
# either vcs_type:url or just url. In the latter case, we expect
|
112
|
+
# 'url' to be a path to a local directory
|
113
|
+
vcs_spec = Autoproj.expand(vcs_spec, manifest.constant_definitions)
|
114
|
+
vcs_def = VCSDefinition.from_raw(vcs_spec, [[nil, raw_spec]])
|
115
|
+
|
116
|
+
source = PackageSet.new(manifest, vcs_def)
|
117
|
+
source.auto_imports = options[:auto_imports]
|
118
|
+
if load_description
|
119
|
+
if source.present?
|
120
|
+
source.load_description_file
|
121
|
+
else
|
122
|
+
raise InternalError, "cannot load description file as it has not been checked out yet"
|
123
|
+
end
|
124
|
+
else
|
125
|
+
# Try to load just the name from the source.yml file
|
126
|
+
source.load_minimal
|
127
|
+
end
|
128
|
+
|
129
|
+
source
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns a string that uniquely represents the version control
|
133
|
+
# information for this package set.
|
134
|
+
#
|
135
|
+
# I.e. for two package sets set1 and set2, if set1.repository_id ==
|
136
|
+
# set2.repository_id, it means that both package sets are checked out
|
137
|
+
# from exactly the same source.
|
138
|
+
def repository_id
|
139
|
+
if local?
|
140
|
+
local_dir
|
141
|
+
else
|
142
|
+
importer = vcs.create_autobuild_importer
|
143
|
+
if importer.respond_to?(:repository_id)
|
144
|
+
importer.repository_id
|
145
|
+
else
|
146
|
+
vcs.to_s
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Remote sources can be accessed through a hidden directory in
|
152
|
+
# $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
|
153
|
+
# autoproj/remotes/
|
154
|
+
#
|
155
|
+
# This returns the former. See #user_local_dir for the latter.
|
156
|
+
#
|
157
|
+
# For local sources, is simply returns the path to the source directory.
|
158
|
+
def raw_local_dir
|
159
|
+
if local?
|
160
|
+
File.expand_path(vcs.url)
|
161
|
+
else
|
162
|
+
File.expand_path(File.join(Autoproj.remotes_dir, vcs.create_autobuild_importer.repository_id.gsub(/[^\w]/, '_')))
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Remote sources can be accessed through a hidden directory in
|
167
|
+
# $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
|
168
|
+
# autoproj/remotes/
|
169
|
+
#
|
170
|
+
# This returns the latter. See #raw_local_dir for the former.
|
171
|
+
#
|
172
|
+
# For local sources, is simply returns the path to the source directory.
|
173
|
+
def user_local_dir
|
174
|
+
if local?
|
175
|
+
return vcs.url
|
176
|
+
else
|
177
|
+
File.join(Autoproj.config_dir, 'remotes', name)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# The directory in which data for this source will be checked out
|
182
|
+
def local_dir
|
183
|
+
ugly_dir = raw_local_dir
|
184
|
+
pretty_dir = user_local_dir
|
185
|
+
if File.symlink?(pretty_dir) && File.readlink(pretty_dir) == ugly_dir
|
186
|
+
pretty_dir
|
187
|
+
else
|
188
|
+
ugly_dir
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def required_autoproj_version
|
193
|
+
definition = @source_definition || raw_description_file
|
194
|
+
definition['required_autoproj_version'] || '0'
|
195
|
+
end
|
196
|
+
|
197
|
+
# Returns the source name
|
198
|
+
def name
|
199
|
+
if @name
|
200
|
+
@name
|
201
|
+
else
|
202
|
+
vcs.to_s
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Loads the source.yml file, validates it and returns it as a hash
|
207
|
+
#
|
208
|
+
# Raises InternalError if the source has not been checked out yet (it
|
209
|
+
# should have), and ConfigError if the source.yml file is not valid.
|
210
|
+
def raw_description_file
|
211
|
+
if !present?
|
212
|
+
raise InternalError, "source #{vcs} has not been fetched yet, cannot load description for it"
|
213
|
+
end
|
214
|
+
|
215
|
+
source_file = File.join(raw_local_dir, "source.yml")
|
216
|
+
if !File.exists?(source_file)
|
217
|
+
raise ConfigError.new, "source #{vcs.type}:#{vcs.url} should have a source.yml file, but does not"
|
218
|
+
end
|
219
|
+
|
220
|
+
source_definition = Autoproj.in_file(source_file, Autoproj::YAML_LOAD_ERROR) do
|
221
|
+
YAML.load(File.read(source_file))
|
222
|
+
end
|
223
|
+
|
224
|
+
if !source_definition || !source_definition['name']
|
225
|
+
raise ConfigError.new(source_file), "in #{source_file}: missing a 'name' field"
|
226
|
+
end
|
227
|
+
|
228
|
+
source_definition
|
229
|
+
end
|
230
|
+
|
231
|
+
# Load and validate the self-contained information from the YAML hash
|
232
|
+
def load_minimal
|
233
|
+
# If @source_definition is set, it means that load_description_file
|
234
|
+
# has been called and that therefore all information has already
|
235
|
+
# been parsed
|
236
|
+
definition = @source_definition || raw_description_file
|
237
|
+
@name = definition['name']
|
238
|
+
|
239
|
+
if @name !~ /^[\w_\.-]+$/
|
240
|
+
raise ConfigError.new(source_file),
|
241
|
+
"in #{source_file}: invalid source name '#{@name}': source names can only contain alphanumeric characters, and .-_"
|
242
|
+
elsif @name == "local"
|
243
|
+
raise ConfigError.new(source_file),
|
244
|
+
"in #{source_file}: the name 'local' is a reserved name"
|
245
|
+
end
|
246
|
+
|
247
|
+
@provides = (definition['provides'] || Set.new).to_set
|
248
|
+
@imports = (definition['imports'] || Array.new).map do |set_def|
|
249
|
+
pkg_set = Autoproj.in_file(source_file) do
|
250
|
+
PackageSet.from_spec(manifest, set_def, false)
|
251
|
+
end
|
252
|
+
|
253
|
+
pkg_set.imported_from = self
|
254
|
+
pkg_set
|
255
|
+
end
|
256
|
+
|
257
|
+
rescue InternalError
|
258
|
+
# This ignores raw_description_file error if the package set is not
|
259
|
+
# checked out yet
|
260
|
+
end
|
261
|
+
|
262
|
+
# Yields the imports this package set declares, as PackageSet instances
|
263
|
+
def each_imported_set(&block)
|
264
|
+
@imports.each(&block)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Path to the source.yml file
|
268
|
+
def source_file
|
269
|
+
File.join(local_dir, 'source.yml')
|
270
|
+
end
|
271
|
+
|
272
|
+
# Load the source.yml file and resolves all information it contains.
|
273
|
+
#
|
274
|
+
# This for instance requires configuration options to be defined. Use
|
275
|
+
# PackageSet#load_minimal to load only self-contained information
|
276
|
+
def load_description_file
|
277
|
+
if @source_definition
|
278
|
+
return
|
279
|
+
end
|
280
|
+
|
281
|
+
@source_definition = raw_description_file
|
282
|
+
load_minimal
|
283
|
+
|
284
|
+
# Compute the definition of constants
|
285
|
+
Autoproj.in_file(source_file) do
|
286
|
+
constants = source_definition['constants'] || Hash.new
|
287
|
+
@constants_definitions = Autoproj.resolve_constant_definitions(constants)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def single_expansion(data, additional_expansions = Hash.new)
|
292
|
+
if !source_definition
|
293
|
+
load_description_file
|
294
|
+
end
|
295
|
+
Autoproj.single_expansion(data, additional_expansions.merge(constants_definitions))
|
296
|
+
end
|
297
|
+
|
298
|
+
# Expands the given string as much as possible using the expansions
|
299
|
+
# listed in the source.yml file, and returns it. Raises if not all
|
300
|
+
# variables can be expanded.
|
301
|
+
def expand(data, additional_expansions = Hash.new)
|
302
|
+
if !source_definition
|
303
|
+
load_description_file
|
304
|
+
end
|
305
|
+
Autoproj.expand(data, additional_expansions.merge(constants_definitions))
|
306
|
+
end
|
307
|
+
|
308
|
+
# Returns the default importer definition for this package set, as a
|
309
|
+
# VCSDefinition instance
|
310
|
+
def default_importer
|
311
|
+
importer_definition_for('default')
|
312
|
+
end
|
313
|
+
|
314
|
+
# Returns an importer definition for the given package, if one is
|
315
|
+
# available. Otherwise returns nil.
|
316
|
+
#
|
317
|
+
# The returned value is a VCSDefinition object.
|
318
|
+
def version_control_field(package_name, section_name, validate = true)
|
319
|
+
urls = source_definition['urls'] || Hash.new
|
320
|
+
urls['HOME'] = ENV['HOME']
|
321
|
+
|
322
|
+
all_vcs = source_definition[section_name]
|
323
|
+
if all_vcs
|
324
|
+
if all_vcs.kind_of?(Hash)
|
325
|
+
raise ConfigError.new, "wrong format for the #{section_name} section, you forgot the '-' in front of the package names"
|
326
|
+
elsif !all_vcs.kind_of?(Array)
|
327
|
+
raise ConfigError.new, "wrong format for the #{section_name} section"
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
raw = []
|
332
|
+
vcs_spec = Hash.new
|
333
|
+
|
334
|
+
if all_vcs
|
335
|
+
all_vcs.each do |spec|
|
336
|
+
spec = spec.dup
|
337
|
+
if spec.values.size != 1
|
338
|
+
# Maybe the user wrote the spec like
|
339
|
+
# - package_name:
|
340
|
+
# type: git
|
341
|
+
# url: blah
|
342
|
+
#
|
343
|
+
# or as
|
344
|
+
# - package_name
|
345
|
+
# type: git
|
346
|
+
# url: blah
|
347
|
+
#
|
348
|
+
# In that case, we should have the package name as
|
349
|
+
# "name => nil". Check that.
|
350
|
+
name, _ = spec.find { |n, v| v.nil? }
|
351
|
+
if name
|
352
|
+
spec.delete(name)
|
353
|
+
else
|
354
|
+
name, _ = spec.find { |n, v| n =~ / \w+$/ }
|
355
|
+
name =~ / (\w+)$/
|
356
|
+
spec[$1] = spec.delete(name)
|
357
|
+
name = name.gsub(/ \w+$/, '')
|
358
|
+
end
|
359
|
+
else
|
360
|
+
name, spec = spec.to_a.first
|
361
|
+
if name =~ / (\w+)/
|
362
|
+
spec = { $1 => spec }
|
363
|
+
name = name.gsub(/ \w+$/, '')
|
364
|
+
end
|
365
|
+
|
366
|
+
if spec.respond_to?(:to_str)
|
367
|
+
if spec == "none"
|
368
|
+
spec = { :type => "none" }
|
369
|
+
else
|
370
|
+
raise ConfigError.new, "invalid VCS specification in the #{section_name} section '#{name}: #{spec}'"
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
name_match = name
|
376
|
+
if name_match =~ /[^\w\/_-]/
|
377
|
+
name_match = Regexp.new("^" + name_match)
|
378
|
+
end
|
379
|
+
if name_match === package_name
|
380
|
+
raw << [self.name, spec]
|
381
|
+
vcs_spec =
|
382
|
+
begin
|
383
|
+
VCSDefinition.update_raw_vcs_spec(vcs_spec, spec)
|
384
|
+
rescue ConfigError => e
|
385
|
+
raise ConfigError.new, "invalid VCS definition in the #{section_name} section for '#{name}': #{e.message}", e.backtrace
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
if !vcs_spec.empty?
|
392
|
+
expansions = Hash["PACKAGE" => package_name,
|
393
|
+
"PACKAGE_BASENAME" => File.basename(package_name),
|
394
|
+
"AUTOPROJ_ROOT" => Autoproj.root_dir,
|
395
|
+
"AUTOPROJ_CONFIG" => Autoproj.config_dir,
|
396
|
+
"AUTOPROJ_SOURCE_DIR" => local_dir]
|
397
|
+
|
398
|
+
vcs_spec = expand(vcs_spec, expansions)
|
399
|
+
vcs_spec.dup.each do |name, value|
|
400
|
+
vcs_spec[name] = expand(value, expansions)
|
401
|
+
end
|
402
|
+
|
403
|
+
# If required, verify that the configuration is a valid VCS
|
404
|
+
# configuration
|
405
|
+
if validate
|
406
|
+
begin
|
407
|
+
VCSDefinition.from_raw(vcs_spec)
|
408
|
+
rescue ConfigError => e
|
409
|
+
raise ConfigError.new, "invalid resulting VCS definition for package #{package_name}: #{e.message}", e.backtrace
|
410
|
+
end
|
411
|
+
end
|
412
|
+
return vcs_spec, raw
|
413
|
+
else
|
414
|
+
return nil, []
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# Returns the VCS definition for +package_name+ as defined in this
|
419
|
+
# source, or nil if the source does not have any.
|
420
|
+
#
|
421
|
+
# The definition is an instance of VCSDefinition
|
422
|
+
def importer_definition_for(package_name)
|
423
|
+
Autoproj.in_file source_file do
|
424
|
+
vcs_spec, raw = version_control_field(package_name, 'version_control')
|
425
|
+
if vcs_spec
|
426
|
+
VCSDefinition.from_raw(vcs_spec, raw)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Enumerates the Autobuild::Package instances that are defined in this
|
432
|
+
# source
|
433
|
+
def each_package
|
434
|
+
if !block_given?
|
435
|
+
return enum_for(:each_package)
|
436
|
+
end
|
437
|
+
|
438
|
+
Autoproj.manifest.packages.each_value do |pkg|
|
439
|
+
if pkg.package_set.name == name
|
440
|
+
yield(pkg.autobuild)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# True if this package set provides the given package set name. I.e. if
|
446
|
+
# it has this name or the name is listed in the "replaces" field of
|
447
|
+
# source.yml
|
448
|
+
def provides?(name)
|
449
|
+
name == self.name ||
|
450
|
+
provides.include?(name)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
# Specialization of the PackageSet class for the overrides listed in autoproj/
|
455
|
+
class LocalPackageSet < PackageSet
|
456
|
+
def initialize(manifest)
|
457
|
+
super(manifest, VCSDefinition.from_raw(:type => 'local', :url => Autoproj.config_dir))
|
458
|
+
end
|
459
|
+
|
460
|
+
def name
|
461
|
+
'local'
|
462
|
+
end
|
463
|
+
def load_minimal
|
464
|
+
end
|
465
|
+
def repository_id
|
466
|
+
'local'
|
467
|
+
end
|
468
|
+
|
469
|
+
def source_file
|
470
|
+
File.join(Autoproj.config_dir, "overrides.yml")
|
471
|
+
end
|
472
|
+
|
473
|
+
# Returns the default importer for this package set
|
474
|
+
def default_importer
|
475
|
+
importer_definition_for('default') ||
|
476
|
+
VCSDefinition.from_raw(:type => 'none')
|
477
|
+
end
|
478
|
+
|
479
|
+
def raw_description_file
|
480
|
+
path = source_file
|
481
|
+
if File.file?(path)
|
482
|
+
data = Autoproj.in_file(path, Autoproj::YAML_LOAD_ERROR) do
|
483
|
+
YAML.load(File.read(path)) || Hash.new
|
484
|
+
end
|
485
|
+
data['name'] = 'local'
|
486
|
+
data
|
487
|
+
else
|
488
|
+
{ 'name' => 'local' }
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
# DEPRECATED. For backward-compatibility only.
|
494
|
+
Source = PackageSet
|
495
|
+
# DEPRECATED. For backward-compatibility only.
|
496
|
+
LocalSource = LocalPackageSet
|
497
|
+
end
|