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,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
|
data/lib/autoproj/manifest.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
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
|
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.
|
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
|