autoproj 1.4.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +15 -0
- data/Manifest.txt +5 -1
- data/README.txt +1 -41
- data/Rakefile +1 -1
- data/bin/autoproj +62 -747
- data/doc/guide/src/autoproj_bootstrap +54 -9
- data/doc/guide/src/customization.page +5 -4
- data/doc/guide/src/index.page +1 -41
- data/doc/guide/src/overview.png +0 -0
- data/doc/guide/src/overview.svg +537 -0
- data/doc/guide/src/package_sets/autobuild.page +37 -13
- data/doc/guide/src/package_sets/importers.page +1 -1
- data/doc/guide/src/package_sets/manifest-xml.page +2 -2
- data/doc/guide/src/package_sets/osdeps.page +14 -9
- data/doc/guide/src/quick_start.page +110 -0
- data/doc/guide/src/{structure.page → writing_manifest.page} +47 -57
- data/lib/autoproj.rb +1 -0
- data/lib/autoproj/autobuild.rb +74 -32
- data/lib/autoproj/cmdline.rb +912 -0
- data/lib/autoproj/default.osdeps +12 -2
- data/lib/autoproj/manifest.rb +141 -88
- data/lib/autoproj/osdeps.rb +42 -7
- data/lib/autoproj/version.rb +1 -1
- metadata +90 -53
@@ -0,0 +1,912 @@
|
|
1
|
+
module Autoproj
|
2
|
+
module CmdLine
|
3
|
+
def self.initialize
|
4
|
+
Autobuild::Reporting << Autoproj::Reporter.new
|
5
|
+
if mail_config[:to]
|
6
|
+
Autobuild::Reporting << Autobuild::MailReporter.new(mail_config)
|
7
|
+
end
|
8
|
+
|
9
|
+
Autoproj.load_config
|
10
|
+
|
11
|
+
# If we are under rubygems, check that the GEM_HOME is right ...
|
12
|
+
if $LOADED_FEATURES.any? { |l| l =~ /rubygems/ }
|
13
|
+
if ENV['GEM_HOME'] != Autoproj.gem_home
|
14
|
+
raise ConfigError, "RubyGems is already loaded with a different GEM_HOME, make sure you are loading the right env.sh script !"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set up some important autobuild parameters
|
19
|
+
Autoproj.env_inherit 'PATH', 'PKG_CONFIG_PATH', 'RUBYLIB', 'LD_LIBRARY_PATH'
|
20
|
+
Autoproj.env_set 'GEM_HOME', Autoproj.gem_home
|
21
|
+
Autoproj.env_add 'PATH', File.join(Autoproj.gem_home, 'bin')
|
22
|
+
Autoproj.env_set 'RUBYOPT', "-rubygems"
|
23
|
+
Autobuild.prefix = Autoproj.build_dir
|
24
|
+
Autobuild.srcdir = Autoproj.root_dir
|
25
|
+
Autobuild.logdir = File.join(Autobuild.prefix, 'log')
|
26
|
+
|
27
|
+
ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME']
|
28
|
+
if ruby != 'ruby'
|
29
|
+
bindir = File.join(Autoproj.build_dir, 'bin')
|
30
|
+
FileUtils.mkdir_p bindir
|
31
|
+
File.open(File.join(bindir, 'ruby'), 'w') do |io|
|
32
|
+
io.puts "#! /bin/sh"
|
33
|
+
io.puts "exec #{ruby} \"$@\""
|
34
|
+
end
|
35
|
+
FileUtils.chmod 0755, File.join(bindir, 'ruby')
|
36
|
+
|
37
|
+
Autoproj.env_add 'PATH', bindir
|
38
|
+
end
|
39
|
+
|
40
|
+
manifest_path = File.join(Autoproj.config_dir, 'manifest')
|
41
|
+
Autoproj.manifest = Manifest.load(manifest_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.update_myself
|
45
|
+
# First things first, see if we need to update ourselves
|
46
|
+
osdeps = Autoproj::OSDependencies.load_default
|
47
|
+
if osdeps.install(%w{autobuild autoproj})
|
48
|
+
# We updated autobuild or autoproj themselves ... Restart !
|
49
|
+
require 'rbconfig'
|
50
|
+
ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME']
|
51
|
+
exec(ruby, $0, *ARGV)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.load_configuration
|
56
|
+
manifest = Autoproj.manifest
|
57
|
+
|
58
|
+
# Load init.rb files. each_source must not load the source.yml file, as
|
59
|
+
# init.rb may define configuration options that are used there
|
60
|
+
manifest.each_source(false) do |source|
|
61
|
+
Autoproj.load_if_present(source, source.local_dir, "init.rb")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Load the required autobuild definitions
|
65
|
+
STDERR.puts color("autoproj: loading ...", :bold)
|
66
|
+
if !Autoproj.reconfigure?
|
67
|
+
STDERR.puts color("run 'autoproj --reconfigure' to change configuration values", :bold)
|
68
|
+
end
|
69
|
+
manifest.each_autobuild_file do |source, name|
|
70
|
+
Autoproj.import_autobuild_file source, name
|
71
|
+
end
|
72
|
+
|
73
|
+
# Load the package's override files. each_source must not load the
|
74
|
+
# source.yml file, as init.rb may define configuration options that are used
|
75
|
+
# there
|
76
|
+
manifest.each_source(false).to_a.reverse.each do |source|
|
77
|
+
Autoproj.load_if_present(source, source.local_dir, "overrides.rb")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Now, load the package's importer configurations (from the various
|
81
|
+
# source.yml files)
|
82
|
+
manifest.load_importers
|
83
|
+
|
84
|
+
# Configuration is finished, so all relevant configuration options should
|
85
|
+
# have been asked to the user. Save it.
|
86
|
+
Autoproj.save_config
|
87
|
+
|
88
|
+
# Loads OS package definitions once and for all
|
89
|
+
Autoproj.osdeps = manifest.known_os_packages
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.update_configuration
|
93
|
+
manifest = Autoproj.manifest
|
94
|
+
|
95
|
+
# Once thing left to do: handle the Autoproj.auto_update
|
96
|
+
# configuration parameter
|
97
|
+
#
|
98
|
+
# Namely, we must check if Autobuild.do_update has been explicitely
|
99
|
+
# set to true or false. If that is the case, don't do anything.
|
100
|
+
# Otherwise, set it to the value of auto_update (set in the
|
101
|
+
# manifest)
|
102
|
+
if Autobuild.do_update.nil?
|
103
|
+
Autobuild.do_update = manifest.auto_update?
|
104
|
+
end
|
105
|
+
if Autoproj::CmdLine.update_os_dependencies.nil?
|
106
|
+
Autoproj::CmdLine.update_os_dependencies = manifest.auto_update?
|
107
|
+
end
|
108
|
+
|
109
|
+
# Load the installation's manifest a first time, to check if we should
|
110
|
+
# update it ... We assume that the OS dependencies for this VCS is already
|
111
|
+
# installed (i.e. that the user did not remove it)
|
112
|
+
if manifest.vcs
|
113
|
+
manifest.update_yourself
|
114
|
+
manifest_path = File.join(Autoproj.config_dir, 'manifest')
|
115
|
+
Autoproj.manifest = manifest = Manifest.load(manifest_path)
|
116
|
+
end
|
117
|
+
|
118
|
+
source_os_dependencies = manifest.each_remote_source(false).
|
119
|
+
inject(Set.new) do |set, source|
|
120
|
+
set << source.vcs.type if !source.local?
|
121
|
+
end
|
122
|
+
|
123
|
+
# Update the remote sources if there are any
|
124
|
+
if manifest.has_remote_sources?
|
125
|
+
STDERR.puts color("autoproj: updating remote definitions of package sets", :bold)
|
126
|
+
# If we need to install some packages to import our remote sources, do it
|
127
|
+
if update_os_dependencies?
|
128
|
+
osdeps = OSDependencies.load_default
|
129
|
+
osdeps.install(source_os_dependencies)
|
130
|
+
end
|
131
|
+
|
132
|
+
manifest.update_remote_sources
|
133
|
+
STDERR.puts
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.initial_package_setup
|
138
|
+
manifest = Autoproj.manifest
|
139
|
+
|
140
|
+
# Now starts a different stage of the whole build. Until now, we were
|
141
|
+
# working on the whole package set. Starting from now, we need to build the
|
142
|
+
# package sets based on the layout file
|
143
|
+
#
|
144
|
+
# First, we allow to user to specify packages based on disk paths, so
|
145
|
+
# resolve those
|
146
|
+
seen = Set.new
|
147
|
+
manifest.each_package_set do |name, packages, enabled_packages|
|
148
|
+
packages -= seen
|
149
|
+
|
150
|
+
srcdir = File.join(Autoproj.root_dir, name)
|
151
|
+
prefix = File.join(Autoproj.build_dir, name)
|
152
|
+
logdir = File.join(prefix, "log")
|
153
|
+
packages.each do |pkg_name|
|
154
|
+
pkg = Autobuild::Package[pkg_name]
|
155
|
+
pkg.srcdir = File.join(srcdir, pkg_name)
|
156
|
+
pkg.prefix = prefix
|
157
|
+
pkg.doc_target_dir = File.join(Autoproj.build_dir, 'doc', name, pkg_name)
|
158
|
+
pkg.logdir = logdir
|
159
|
+
end
|
160
|
+
seen |= packages
|
161
|
+
end
|
162
|
+
|
163
|
+
# Now call the blocks that the user defined in the autobuild files. We do it
|
164
|
+
# now so that the various package directories are properly setup
|
165
|
+
manifest.packages.each_value do |pkg|
|
166
|
+
if pkg.user_block
|
167
|
+
pkg.user_block[pkg.autobuild]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def self.display_sources(manifest)
|
174
|
+
# We can't have the Manifest class load the source.yml file, as it
|
175
|
+
# cannot resolve all constants. So we need to do it ourselves to get
|
176
|
+
# the name ...
|
177
|
+
sources = manifest.each_source(false).to_a
|
178
|
+
|
179
|
+
if sources.empty?
|
180
|
+
STDERR.puts color("autoproj: no package sets defined in autoproj/manifest", :bold, :red)
|
181
|
+
else
|
182
|
+
STDERR.puts color("autoproj: available package sets", :bold)
|
183
|
+
manifest.each_source(false) do |source|
|
184
|
+
source_yml = source.raw_description_file
|
185
|
+
STDERR.puts " #{source_yml['name']}"
|
186
|
+
if source.local?
|
187
|
+
STDERR.puts " local source in #{source.local_dir}"
|
188
|
+
else
|
189
|
+
STDERR.puts " from: #{source.vcs}"
|
190
|
+
STDERR.puts " local: #{source.local_dir}"
|
191
|
+
end
|
192
|
+
|
193
|
+
lines = []
|
194
|
+
source.each_package.
|
195
|
+
map { |pkg| [pkg.name, manifest.package_manifests[pkg.name]] }.
|
196
|
+
sort_by { |name, _| name }.
|
197
|
+
each do |name, source_manifest|
|
198
|
+
vcs_def = manifest.importer_definition_for(name)
|
199
|
+
if source_manifest
|
200
|
+
lines << [name, source_manifest.short_documentation]
|
201
|
+
lines << ["", vcs_def.to_s]
|
202
|
+
else
|
203
|
+
lines << [name, vcs_def.to_s]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
w_col1, w_col2 = nil
|
208
|
+
lines.each do |col1, col2|
|
209
|
+
w_col1 = col1.size if !w_col1 || col1.size > w_col1
|
210
|
+
w_col2 = col2.size if !w_col2 || col2.size > w_col2
|
211
|
+
end
|
212
|
+
STDERR.puts " packages:"
|
213
|
+
format = " | %-#{w_col1}s | %-#{w_col2}s |"
|
214
|
+
lines.each do |col1, col2|
|
215
|
+
STDERR.puts(format % [col1, col2])
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.resolve_user_selection(selected_packages)
|
222
|
+
manifest = Autoproj.manifest
|
223
|
+
|
224
|
+
if selected_packages.empty?
|
225
|
+
return manifest.default_packages
|
226
|
+
end
|
227
|
+
if selected_packages.empty? # no packages, terminate
|
228
|
+
STDERR.puts
|
229
|
+
STDERR.puts color("autoproj: no packages defined", :red)
|
230
|
+
exit 0
|
231
|
+
end
|
232
|
+
selected_packages = selected_packages.to_set
|
233
|
+
|
234
|
+
selected_packages = manifest.expand_package_selection(selected_packages)
|
235
|
+
if selected_packages.empty?
|
236
|
+
STDERR.puts color("autoproj: wrong package selection on command line", :red)
|
237
|
+
exit 1
|
238
|
+
elsif Autoproj.verbose
|
239
|
+
STDERR.puts "will install #{selected_packages.to_a.join(", ")}"
|
240
|
+
end
|
241
|
+
selected_packages
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.import_packages(selected_packages)
|
245
|
+
selected_packages = selected_packages.dup.
|
246
|
+
map do |pkg_name|
|
247
|
+
pkg = Autobuild::Package[pkg_name]
|
248
|
+
if !pkg
|
249
|
+
raise ConfigError, "selected package #{pkg_name} does not exist"
|
250
|
+
end
|
251
|
+
pkg
|
252
|
+
end.to_set
|
253
|
+
|
254
|
+
# First, import all packages that are already there to make
|
255
|
+
# automatic dependency discovery possible
|
256
|
+
old_update_flag = Autobuild.do_update
|
257
|
+
begin
|
258
|
+
Autobuild.do_update = false
|
259
|
+
packages = Autobuild::Package.each.
|
260
|
+
find_all { |pkg_name, pkg| File.directory?(pkg.srcdir) }.
|
261
|
+
delete_if { |pkg_name, pkg| Autoproj.manifest.excluded?(pkg_name) || Autoproj.manifest.ignored?(pkg_name) }
|
262
|
+
|
263
|
+
packages.each do |_, pkg|
|
264
|
+
pkg.import
|
265
|
+
end
|
266
|
+
|
267
|
+
ensure
|
268
|
+
Autobuild.do_update = old_update_flag
|
269
|
+
end
|
270
|
+
|
271
|
+
all_enabled_packages = Set.new
|
272
|
+
|
273
|
+
package_queue = selected_packages.dup
|
274
|
+
while !package_queue.empty?
|
275
|
+
current_packages, package_queue = package_queue, Set.new
|
276
|
+
current_packages = current_packages.sort_by(&:name)
|
277
|
+
|
278
|
+
current_packages.
|
279
|
+
delete_if { |pkg| all_enabled_packages.include?(pkg.name) }
|
280
|
+
all_enabled_packages |= current_packages
|
281
|
+
|
282
|
+
# We import first so that all packages can export the
|
283
|
+
# additional targets they provide.
|
284
|
+
current_packages.each do |pkg|
|
285
|
+
# If the package has no importer, the source directory must
|
286
|
+
# be there
|
287
|
+
if !File.directory?(pkg.srcdir)
|
288
|
+
raise ConfigError, "#{pkg.name} has no VCS, but is not checked out in #{pkg.srcdir}"
|
289
|
+
end
|
290
|
+
|
291
|
+
Rake::Task["#{pkg.name}-import"].invoke
|
292
|
+
manifest.load_package_manifest(pkg.name)
|
293
|
+
end
|
294
|
+
|
295
|
+
current_packages.each do |pkg|
|
296
|
+
Rake::Task["#{pkg.name}-prepare"].invoke
|
297
|
+
|
298
|
+
# Verify that its dependencies are there, and add
|
299
|
+
# them to the selected_packages set so that they get
|
300
|
+
# imported as well
|
301
|
+
pkg.dependencies.each do |dep_name|
|
302
|
+
if Autoproj.manifest.excluded?(dep_name)
|
303
|
+
raise ConfigError, "#{pkg.name} depends on #{dep_name}, which is explicitely excluded in the manifest"
|
304
|
+
end
|
305
|
+
dep_pkg = Autobuild::Package[dep_name]
|
306
|
+
if !dep_pkg
|
307
|
+
raise ConfigError, "#{pkg.name} depends on #{dep_name}, but it does not seem to exist"
|
308
|
+
end
|
309
|
+
|
310
|
+
package_queue << dep_pkg
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
if Autoproj.verbose
|
316
|
+
STDERR.puts "autoproj: finished importing packages"
|
317
|
+
end
|
318
|
+
|
319
|
+
return all_enabled_packages.map(&:name)
|
320
|
+
end
|
321
|
+
|
322
|
+
def self.build_packages(selected_packages, all_enabled_packages)
|
323
|
+
if Autoproj::CmdLine.doc?
|
324
|
+
STDERR.puts color("autoproj: building and installing documentation", :bold)
|
325
|
+
else
|
326
|
+
STDERR.puts color("autoproj: building and installing packages", :bold)
|
327
|
+
end
|
328
|
+
|
329
|
+
if Autoproj::CmdLine.update_os_dependencies?
|
330
|
+
manifest.install_os_dependencies(all_enabled_packages)
|
331
|
+
end
|
332
|
+
|
333
|
+
if !selected_packages.empty? && !force_re_build_with_depends?
|
334
|
+
if Autobuild.do_rebuild
|
335
|
+
selected_packages.each do |pkg_name|
|
336
|
+
Autobuild::Package[pkg_name].prepare_for_rebuild
|
337
|
+
end
|
338
|
+
Autobuild.do_rebuild = false
|
339
|
+
elsif Autobuild.do_forced_build
|
340
|
+
selected_packages.each do |pkg_name|
|
341
|
+
Autobuild::Package[pkg_name].prepare_for_forced_build
|
342
|
+
end
|
343
|
+
Autobuild.do_forced_build = false
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
Autobuild.apply(all_enabled_packages, "autoproj-build")
|
348
|
+
end
|
349
|
+
|
350
|
+
def self.manifest; Autoproj.manifest end
|
351
|
+
def self.only_status?; !!@only_status end
|
352
|
+
def self.check?; !!@check end
|
353
|
+
def self.manifest_update?; !!@manifest_update end
|
354
|
+
def self.only_config?; !!@only_config end
|
355
|
+
def self.update_os_dependencies?; !!@update_os_dependencies end
|
356
|
+
class << self
|
357
|
+
attr_accessor :update_os_dependencies
|
358
|
+
end
|
359
|
+
def self.display_configuration?; !!@display_configuration end
|
360
|
+
def self.force_re_build_with_depends?; !!@force_re_build_with_depends end
|
361
|
+
def self.partial_build?; !!@partial_build end
|
362
|
+
def self.mail_config; @mail_config end
|
363
|
+
def self.update_packages?; @mode == "update" || @mode == "envsh" || build? end
|
364
|
+
def self.build?; @mode =~ /build/ end
|
365
|
+
def self.doc?; @mode == "doc" end
|
366
|
+
def self.parse_arguments(args)
|
367
|
+
@only_status = false
|
368
|
+
@check = false
|
369
|
+
@manifest_update = false
|
370
|
+
@display_configuration = false
|
371
|
+
@update_os_dependencies = true
|
372
|
+
update_os_dependencies = nil
|
373
|
+
@force_re_build_with_depends = false
|
374
|
+
force_re_build_with_depends = nil
|
375
|
+
@only_config = false
|
376
|
+
@partial_build = false
|
377
|
+
Autobuild.doc_errors = false
|
378
|
+
Autobuild.do_doc = false
|
379
|
+
Autobuild.only_doc = false
|
380
|
+
Autobuild.do_update = nil
|
381
|
+
do_update = nil
|
382
|
+
|
383
|
+
mail_config = Hash.new
|
384
|
+
|
385
|
+
# Parse the configuration options
|
386
|
+
parser = OptionParser.new do |opts|
|
387
|
+
opts.banner = <<-EOBANNER
|
388
|
+
autoproj mode [options]
|
389
|
+
where 'mode' is one of:
|
390
|
+
|
391
|
+
-- Build
|
392
|
+
build: import, build and install all packages that need it. A package or package
|
393
|
+
set name can be given, in which case only this package and its dependencies
|
394
|
+
will be taken into account. Example:
|
395
|
+
|
396
|
+
autoproj build drivers/hokuyo
|
397
|
+
|
398
|
+
fast-build: builds without updating and without considering OS dependencies
|
399
|
+
full-build: updates packages and OS dependencies, and then builds
|
400
|
+
force-build: triggers all build commands, i.e. don't be lazy like in "build".
|
401
|
+
If packages are selected on the command line, only those packages
|
402
|
+
will be affected unless the --with-depends option is used.
|
403
|
+
rebuild: clean and then rebuild. If packages are selected on the command line,
|
404
|
+
only those packages will be affected unless the --with-depends option
|
405
|
+
is used.
|
406
|
+
doc: generate and install documentation for packages that have some
|
407
|
+
|
408
|
+
-- Status & Update
|
409
|
+
envsh: update the env.sh script
|
410
|
+
status: displays the state of the packages w.r.t. their source VCS
|
411
|
+
list-sets: list all available package sets
|
412
|
+
update: only import/update packages, do not build them
|
413
|
+
update-sets: update the package sets definitions, but not the packages themselves
|
414
|
+
|
415
|
+
-- Autoproj Configuration
|
416
|
+
bootstrap: starts a new autoproj installation. Usage:
|
417
|
+
autoproj bootstrap [manifest_url|source_vcs source_url opt1=value1 opt2=value2 ...]
|
418
|
+
switch-config: change where the configuration should be taken from. Syntax:
|
419
|
+
autoproj switch-config source_vcs source_url opt1=value1 opt2=value2 ...
|
420
|
+
|
421
|
+
For example:
|
422
|
+
autoproj switch-config git git://github.com/doudou/rubim-all.git branch=all
|
423
|
+
|
424
|
+
-- Additional options:
|
425
|
+
EOBANNER
|
426
|
+
opts.on("--reconfigure", "re-ask all configuration options (build modes only)") do
|
427
|
+
Autoproj.reconfigure = true
|
428
|
+
end
|
429
|
+
opts.on("--version", "displays the version and then exits") do
|
430
|
+
STDERR.puts "autoproj v#{Autoproj::VERSION}"
|
431
|
+
exit(0)
|
432
|
+
end
|
433
|
+
opts.on("--[no-]update", "[do not] update already checked-out packages (build modes only)") do |value|
|
434
|
+
do_update = value
|
435
|
+
end
|
436
|
+
|
437
|
+
opts.on("--[no-]osdeps", "[do not] install prepackaged dependencies (build and update modes only)") do |value|
|
438
|
+
update_os_dependencies = value
|
439
|
+
end
|
440
|
+
opts.on("--with-depends", "apply rebuild and force-build to both packages selected on the command line and their dependencies") do
|
441
|
+
force_re_build_with_depends = true
|
442
|
+
end
|
443
|
+
|
444
|
+
opts.on("--verbose", "verbose output") do
|
445
|
+
Autoproj.verbose = true
|
446
|
+
Autobuild.verbose = true
|
447
|
+
Rake.application.options.trace = false
|
448
|
+
end
|
449
|
+
opts.on("--debug", "debugging output") do
|
450
|
+
Autoproj.verbose = true
|
451
|
+
Autobuild.verbose = true
|
452
|
+
Rake.application.options.trace = true
|
453
|
+
Autobuild.debug = true
|
454
|
+
end
|
455
|
+
opts.on('--nice NICE', Integer, 'nice the subprocesses to the given value') do |value|
|
456
|
+
Autobuild.nice = value
|
457
|
+
end
|
458
|
+
opts.on("-h", "--help", "Show this message") do
|
459
|
+
puts opts
|
460
|
+
exit
|
461
|
+
end
|
462
|
+
opts.on("--mail-from EMAIL", String, "From: field of the sent mails") do |from_email|
|
463
|
+
mail_config[:from] = from_email
|
464
|
+
end
|
465
|
+
opts.on("--mail-to EMAILS", String, "comma-separated list of emails to which the reports should be sent") do |emails|
|
466
|
+
mail_config[:to] ||= []
|
467
|
+
mail_config[:to] += emails.split(',')
|
468
|
+
end
|
469
|
+
opts.on("--mail-subject SUBJECT", String, "Subject: field of the sent mails") do |subject_email|
|
470
|
+
mail_config[:subject] = subject_email
|
471
|
+
end
|
472
|
+
opts.on("--mail-smtp HOSTNAME", String, " address of the mail server written as hostname[:port]") do |smtp|
|
473
|
+
raise "invalid SMTP specification #{smtp}" unless smtp =~ /^([^:]+)(?::(\d+))?$/
|
474
|
+
mail_config[:smtp] = $1
|
475
|
+
mail_config[:port] = Integer($2) if $2 && !$2.empty?
|
476
|
+
end
|
477
|
+
opts.on("--mail-only-errors", "send mail only on errors") do
|
478
|
+
mail_config[:only_errors] = true
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
parser.parse!(args)
|
483
|
+
@mail_config = mail_config
|
484
|
+
|
485
|
+
@mode = args.shift
|
486
|
+
unknown_mode = catch(:unknown) do
|
487
|
+
handle_mode(@mode, args)
|
488
|
+
end
|
489
|
+
if unknown_mode
|
490
|
+
STDERR.puts "unknown mode #{@mode}"
|
491
|
+
STDERR.puts "run autoproj --help for more documentation"
|
492
|
+
exit(1)
|
493
|
+
end
|
494
|
+
|
495
|
+
selection = args.dup
|
496
|
+
@partial_build = !selection.empty?
|
497
|
+
@update_os_dependencies = update_os_dependencies if !update_os_dependencies.nil?
|
498
|
+
@force_re_build_with_depends = force_re_build_with_depends if !force_re_build_with_depends.nil?
|
499
|
+
Autobuild.do_update = do_update if !do_update.nil?
|
500
|
+
selection
|
501
|
+
end
|
502
|
+
|
503
|
+
def self.handle_mode(mode, remaining_args)
|
504
|
+
case mode
|
505
|
+
when "bootstrap"
|
506
|
+
bootstrap(*remaining_args)
|
507
|
+
remaining_args.clear
|
508
|
+
|
509
|
+
@display_configuration = true
|
510
|
+
Autobuild.do_build = false
|
511
|
+
Autobuild.do_update = false
|
512
|
+
@update_os_dependencies = false
|
513
|
+
|
514
|
+
when "switch-config"
|
515
|
+
# We must switch to the root dir first, as it is required by the
|
516
|
+
# configuration switch code. This is acceptable as long as we
|
517
|
+
# quit just after the switch
|
518
|
+
Dir.chdir(Autoproj.root_dir)
|
519
|
+
switch_config(*remaining_args)
|
520
|
+
exit 0
|
521
|
+
|
522
|
+
when "build"
|
523
|
+
when "force-build"
|
524
|
+
Autobuild.do_forced_build = true
|
525
|
+
when "rebuild"
|
526
|
+
Autobuild.do_rebuild = true
|
527
|
+
when "fast-build"
|
528
|
+
Autobuild.do_update = false
|
529
|
+
@update_os_dependencies = false
|
530
|
+
when "full-build"
|
531
|
+
Autobuild.do_update = true
|
532
|
+
@update_os_dependencies = true
|
533
|
+
when "update"
|
534
|
+
Autobuild.do_update = true
|
535
|
+
@update_os_dependencies = true
|
536
|
+
Autobuild.do_build = false
|
537
|
+
when "check"
|
538
|
+
Autobuild.do_update = false
|
539
|
+
@update_os_dependencies = false
|
540
|
+
Autobuild.do_build = false
|
541
|
+
@check = true
|
542
|
+
when "manifest-update"
|
543
|
+
Autobuild.do_update = false
|
544
|
+
@update_os_dependencies = false
|
545
|
+
Autobuild.do_build = false
|
546
|
+
@manifest_update = true
|
547
|
+
when "osdeps"
|
548
|
+
Autobuild.do_update = false
|
549
|
+
@update_os_dependencies = true
|
550
|
+
Autobuild.do_build = false
|
551
|
+
when "status"
|
552
|
+
@only_status = true
|
553
|
+
Autobuild.do_update = false
|
554
|
+
@update_os_dependencies = false
|
555
|
+
when "envsh"
|
556
|
+
Autobuild.do_build = false
|
557
|
+
Autobuild.do_update = false
|
558
|
+
@update_os_dependencies = false
|
559
|
+
when "update-sets"
|
560
|
+
@only_config = true
|
561
|
+
Autobuild.do_update = true
|
562
|
+
@update_os_dependencies = false
|
563
|
+
Autobuild.do_build = false
|
564
|
+
when "list-sets"
|
565
|
+
@only_config = true
|
566
|
+
@display_configuration = true
|
567
|
+
Autobuild.do_update = false
|
568
|
+
@update_os_dependencies = false
|
569
|
+
when "doc"
|
570
|
+
Autobuild.do_update = false
|
571
|
+
@update_os_dependencies = false
|
572
|
+
Autobuild.do_doc = true
|
573
|
+
Autobuild.only_doc = true
|
574
|
+
else
|
575
|
+
throw :unknown, true
|
576
|
+
end
|
577
|
+
nil
|
578
|
+
end
|
579
|
+
|
580
|
+
def self.display_status(packages)
|
581
|
+
last_was_in_sync = false
|
582
|
+
|
583
|
+
packages.each do |pkg|
|
584
|
+
lines = []
|
585
|
+
|
586
|
+
if !pkg.importer.respond_to?(:status)
|
587
|
+
lines << color(" the #{pkg.importer.class.name.gsub(/.*::/, '')} importer does not support status display", :bold, :red)
|
588
|
+
elsif !File.directory?(pkg.srcdir)
|
589
|
+
lines << color(" is not imported yet", :magenta)
|
590
|
+
else
|
591
|
+
status = pkg.importer.status(pkg)
|
592
|
+
if status.uncommitted_code
|
593
|
+
lines << color(" contains uncommitted modifications", :red)
|
594
|
+
end
|
595
|
+
|
596
|
+
case status.status
|
597
|
+
when Autobuild::Importer::Status::UP_TO_DATE
|
598
|
+
if !status.uncommitted_code
|
599
|
+
if last_was_in_sync
|
600
|
+
STDERR.print ", #{pkg.autoproj_name}"
|
601
|
+
else
|
602
|
+
STDERR.print pkg.autoproj_name
|
603
|
+
end
|
604
|
+
last_was_in_sync = true
|
605
|
+
next
|
606
|
+
else
|
607
|
+
lines << color(" local and remote are in sync", :green)
|
608
|
+
end
|
609
|
+
when Autobuild::Importer::Status::ADVANCED
|
610
|
+
lines << color(" local contains #{status.local_commits.size} commit that remote does not have:", :magenta)
|
611
|
+
status.local_commits.each do |line|
|
612
|
+
lines << color(" #{line}", :magenta)
|
613
|
+
end
|
614
|
+
when Autobuild::Importer::Status::SIMPLE_UPDATE
|
615
|
+
lines << color(" remote contains #{status.remote_commits.size} commit that local does not have:", :magenta)
|
616
|
+
status.remote_commits.each do |line|
|
617
|
+
lines << color(" #{line}", :magenta)
|
618
|
+
end
|
619
|
+
when Autobuild::Importer::Status::NEEDS_MERGE
|
620
|
+
lines << color(" local and remote have diverged with respectively #{status.local_commits.size} and #{status.remote_commits.size} commits each", :magenta)
|
621
|
+
lines << " -- local commits --"
|
622
|
+
status.local_commits.each do |line|
|
623
|
+
lines << color(" #{line}", :magenta)
|
624
|
+
end
|
625
|
+
lines << " -- remote commits --"
|
626
|
+
status.remote_commits.each do |line|
|
627
|
+
lines << color(" #{line}", :magenta)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
if last_was_in_sync
|
633
|
+
STDERR.puts color(": local and remote are in sync", :green)
|
634
|
+
end
|
635
|
+
|
636
|
+
last_was_in_sync = false
|
637
|
+
if pkg.respond_to?(:text_name)
|
638
|
+
STDERR.print "#{pkg.text_name}:"
|
639
|
+
else
|
640
|
+
STDERR.print "#{pkg.autoproj_name}:"
|
641
|
+
end
|
642
|
+
|
643
|
+
if lines.size == 1
|
644
|
+
STDERR.puts lines.first
|
645
|
+
else
|
646
|
+
STDERR.puts
|
647
|
+
STDERR.puts lines.join("\n")
|
648
|
+
end
|
649
|
+
end
|
650
|
+
if last_was_in_sync
|
651
|
+
STDERR.puts color(": local and remote are in sync", :green)
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def self.status(packages)
|
656
|
+
console = Autoproj.console
|
657
|
+
|
658
|
+
sources = Autoproj.manifest.each_configuration_source.
|
659
|
+
map do |vcs, text_name, pkg_name, local_dir|
|
660
|
+
Autoproj::Manifest.create_autobuild_package(vcs, text_name, pkg_name, local_dir)
|
661
|
+
end
|
662
|
+
|
663
|
+
if !sources.empty?
|
664
|
+
STDERR.puts color("autoproj: displaying status of configuration", :bold)
|
665
|
+
display_status(sources)
|
666
|
+
STDERR.puts
|
667
|
+
end
|
668
|
+
|
669
|
+
|
670
|
+
STDERR.puts color("autoproj: displaying status of packages", :bold)
|
671
|
+
packages = packages.sort.map do |pkg_name|
|
672
|
+
Autobuild::Package[pkg_name]
|
673
|
+
end
|
674
|
+
display_status(packages)
|
675
|
+
end
|
676
|
+
|
677
|
+
def self.switch_config(*args)
|
678
|
+
Autoproj.load_config
|
679
|
+
if Autoproj.has_config_key?('manifest_source')
|
680
|
+
vcs = Autoproj.normalize_vcs_definition(Autoproj.user_config('manifest_source'))
|
681
|
+
end
|
682
|
+
|
683
|
+
if args.first =~ /^(\w+)=/
|
684
|
+
# First argument is an option string, we are simply setting the
|
685
|
+
# options without changing the type/url
|
686
|
+
type, url = vcs.type, vcs.url
|
687
|
+
else
|
688
|
+
type, url = args.shift, args.shift
|
689
|
+
end
|
690
|
+
options = args
|
691
|
+
|
692
|
+
url = VCSDefinition.to_absolute_url(url)
|
693
|
+
|
694
|
+
if vcs && (vcs.type == type && vcs.url == url)
|
695
|
+
# Don't need to do much: simply change the options and save the config
|
696
|
+
# file, the VCS handler will take care of the actual switching
|
697
|
+
else
|
698
|
+
# We will have to delete the current autoproj directory. Ask the user.
|
699
|
+
opt = Autoproj::BuildOption.new("delete current config", "boolean",
|
700
|
+
Hash[:default => "false",
|
701
|
+
:doc => "delete the current configuration ? (required to switch)"], nil)
|
702
|
+
|
703
|
+
return if !opt.ask(nil)
|
704
|
+
|
705
|
+
Dir.chdir(Autoproj.root_dir) do
|
706
|
+
do_switch_config(true, type, url, *options)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
# And now save the options: note that we keep the current option set even
|
711
|
+
# though we switched configuration. This is not a problem as undefined
|
712
|
+
# options will not be reused
|
713
|
+
#
|
714
|
+
# TODO: cleanup the options to only keep the relevant ones
|
715
|
+
vcs_def = Hash['type' => type, 'url' => url]
|
716
|
+
options.each do |opt|
|
717
|
+
opt_name, opt_val = opt.split '='
|
718
|
+
vcs_def[opt_name] = opt_val
|
719
|
+
end
|
720
|
+
# Validate the option hash, just in case
|
721
|
+
Autoproj.normalize_vcs_definition(vcs_def)
|
722
|
+
# Save the new options
|
723
|
+
Autoproj.change_option('manifest_source', vcs_def, true)
|
724
|
+
Autoproj.save_config
|
725
|
+
end
|
726
|
+
|
727
|
+
def self.do_switch_config(delete_current, type, url, *options)
|
728
|
+
vcs_def = Hash.new
|
729
|
+
vcs_def[:type] = type
|
730
|
+
vcs_def[:url] = VCSDefinition.to_absolute_url(url)
|
731
|
+
while !options.empty?
|
732
|
+
name, value = options.shift.split("=")
|
733
|
+
vcs_def[name] = value
|
734
|
+
end
|
735
|
+
|
736
|
+
vcs = Autoproj.normalize_vcs_definition(vcs_def)
|
737
|
+
|
738
|
+
# Install the OS dependencies required for this VCS
|
739
|
+
osdeps = Autoproj::OSDependencies.load_default
|
740
|
+
osdeps.install([vcs.type])
|
741
|
+
|
742
|
+
# Now check out the actual configuration
|
743
|
+
config_dir = File.join(Dir.pwd, "autoproj")
|
744
|
+
if delete_current
|
745
|
+
FileUtils.rm_rf config_dir
|
746
|
+
end
|
747
|
+
Autoproj::Manifest.update_source(vcs, "autoproj main configuration", 'autoproj_config', config_dir)
|
748
|
+
|
749
|
+
# Now write it in the config file
|
750
|
+
File.open(File.join(Autoproj.config_dir, "config.yml"), "a") do |io|
|
751
|
+
io.puts <<-EOTEXT
|
752
|
+
manifest_source:
|
753
|
+
type: #{vcs_def.delete(:type)}
|
754
|
+
url: #{vcs_def.delete(:url)}
|
755
|
+
#{vcs_def.map { |k, v| "#{k}: #{v}" }.join("\n ")}
|
756
|
+
EOTEXT
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
def self.bootstrap(*args)
|
761
|
+
if File.exists?(File.join("autoproj", "manifest"))
|
762
|
+
raise ConfigError, "this installation is already bootstrapped. Remove the autoproj directory if it is not the case"
|
763
|
+
end
|
764
|
+
Autobuild.logdir = File.join('build', 'log')
|
765
|
+
|
766
|
+
# Check if we are being called from another GEM_HOME. If it is the case,
|
767
|
+
# assume that we are bootstrapping from another installation directory and
|
768
|
+
# start by copying the .gems directory
|
769
|
+
if ENV['GEM_HOME'] && ENV['GEM_HOME'] =~ /\.gems\/?$/ && ENV['GEM_HOME'] != File.join(Dir.pwd, ".gems")
|
770
|
+
STDERR.puts "autoproj: reusing bootstrap from #{File.dirname(ENV['GEM_HOME'])}"
|
771
|
+
FileUtils.cp_r ENV['GEM_HOME'], ".gems"
|
772
|
+
ENV['GEM_HOME'] = File.join(Dir.pwd, ".gems")
|
773
|
+
|
774
|
+
require 'rbconfig'
|
775
|
+
ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME']
|
776
|
+
exec ruby, $0, *ARGV
|
777
|
+
end
|
778
|
+
|
779
|
+
# If we are not getting the installation setup from a VCS, copy the template
|
780
|
+
# files
|
781
|
+
if args.empty? || args.size == 1
|
782
|
+
sample_dir = File.expand_path(File.join("..", "..", "samples"), File.dirname(__FILE__))
|
783
|
+
FileUtils.cp_r File.join(sample_dir, "autoproj"), "autoproj"
|
784
|
+
end
|
785
|
+
|
786
|
+
if args.size == 1 # the user asks us to download a manifest
|
787
|
+
manifest_url = args.first
|
788
|
+
STDERR.puts color("autoproj: downloading manifest file #{manifest_url}", :bold)
|
789
|
+
manifest_data =
|
790
|
+
begin open(manifest_url) { |file| file.read }
|
791
|
+
rescue
|
792
|
+
raise ConfigError, "cannot read #{manifest_url}, did you mean 'autoproj bootstrap VCSTYPE #{manifest_url}' ?"
|
793
|
+
end
|
794
|
+
|
795
|
+
File.open(File.join(Autoproj.config_dir, "manifest"), "w") do |io|
|
796
|
+
io.write(manifest_data)
|
797
|
+
end
|
798
|
+
|
799
|
+
elsif args.size >= 2 # is a VCS definition for the manifest itself ...
|
800
|
+
type, url, *options = *args
|
801
|
+
url = VCSDefinition.to_absolute_url(url, Dir.pwd)
|
802
|
+
do_switch_config(false, type, url, *options)
|
803
|
+
end
|
804
|
+
|
805
|
+
# Finally, generate an env.sh script
|
806
|
+
File.open('env.sh', 'w') do |io|
|
807
|
+
io.write <<-EOSHELL
|
808
|
+
export RUBYOPT=-rubygems
|
809
|
+
export GEM_HOME=#{Dir.pwd}/.gems
|
810
|
+
export PATH=$GEM_HOME/bin:$PATH
|
811
|
+
EOSHELL
|
812
|
+
end
|
813
|
+
|
814
|
+
STDERR.puts <<EOTEXT
|
815
|
+
|
816
|
+
add the following line at the bottom of your .bashrc:
|
817
|
+
source #{Dir.pwd}/env.sh
|
818
|
+
|
819
|
+
WARNING: autoproj will not work until your restart all
|
820
|
+
your consoles, or run the following in them:
|
821
|
+
$ source #{Dir.pwd}/env.sh
|
822
|
+
|
823
|
+
EOTEXT
|
824
|
+
end
|
825
|
+
|
826
|
+
def self.missing_dependencies(pkg)
|
827
|
+
manifest = Autoproj.manifest.package_manifests[pkg.name]
|
828
|
+
all_deps = pkg.dependencies.map do |dep_name|
|
829
|
+
dep_pkg = Autobuild::Package[dep_name]
|
830
|
+
if dep_pkg then dep_pkg.name
|
831
|
+
else dep_name
|
832
|
+
end
|
833
|
+
end
|
834
|
+
|
835
|
+
if manifest
|
836
|
+
declared_deps = manifest.each_dependency.to_a
|
837
|
+
missing = all_deps - declared_deps
|
838
|
+
else
|
839
|
+
missing = all_deps
|
840
|
+
end
|
841
|
+
|
842
|
+
missing.to_set.to_a.sort
|
843
|
+
end
|
844
|
+
|
845
|
+
def self.check(packages)
|
846
|
+
packages.sort.each do |pkg_name|
|
847
|
+
result = []
|
848
|
+
|
849
|
+
pkg = Autobuild::Package[pkg_name]
|
850
|
+
manifest = Autoproj.manifest.package_manifests[pkg.name]
|
851
|
+
|
852
|
+
# Check if the manifest contains rosdep tags
|
853
|
+
# if manifest && !manifest.each_os_dependency.to_a.empty?
|
854
|
+
# result << "uses rosdep tags, convert them to normal <depend .../> tags"
|
855
|
+
# end
|
856
|
+
|
857
|
+
missing = missing_dependencies(pkg)
|
858
|
+
if !missing.empty?
|
859
|
+
result << "missing dependency tags for: #{missing.join(", ")}"
|
860
|
+
end
|
861
|
+
|
862
|
+
if !result.empty?
|
863
|
+
STDERR.puts pkg.name
|
864
|
+
STDERR.puts " #{result.join("\n ")}"
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
def self.manifest_update(packages)
|
870
|
+
packages.sort.each do |pkg_name|
|
871
|
+
pkg = Autobuild::Package[pkg_name]
|
872
|
+
manifest = Autoproj.manifest.package_manifests[pkg.name]
|
873
|
+
|
874
|
+
xml =
|
875
|
+
if !manifest
|
876
|
+
Nokogiri::XML::Document.parse("<package></package>") do |c|
|
877
|
+
c.noblanks
|
878
|
+
end
|
879
|
+
else
|
880
|
+
manifest.xml.dup
|
881
|
+
end
|
882
|
+
|
883
|
+
# Add missing dependencies
|
884
|
+
missing = missing_dependencies(pkg)
|
885
|
+
if !missing.empty?
|
886
|
+
package_node = xml.xpath('/package').to_a.first
|
887
|
+
missing.each do |pkg_name|
|
888
|
+
node = Nokogiri::XML::Node.new("depend", xml)
|
889
|
+
node['package'] = pkg_name
|
890
|
+
package_node.add_child(node)
|
891
|
+
end
|
892
|
+
modified = true
|
893
|
+
end
|
894
|
+
|
895
|
+
# Save the manifest back
|
896
|
+
if modified
|
897
|
+
path = File.join(pkg.srcdir, 'manifest.xml')
|
898
|
+
File.open(path, 'w') do |io|
|
899
|
+
io.write xml.to_xml
|
900
|
+
end
|
901
|
+
if !manifest
|
902
|
+
STDERR.puts "created #{path}"
|
903
|
+
else
|
904
|
+
STDERR.puts "modified #{path}"
|
905
|
+
end
|
906
|
+
end
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|