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.
@@ -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
+