autoproj 1.4.4 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+