autobuild 1.17.0 → 1.21.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +107 -0
  3. data/.travis.yml +3 -2
  4. data/Gemfile +2 -1
  5. data/Rakefile +1 -4
  6. data/autobuild.gemspec +18 -13
  7. data/bin/autobuild +4 -3
  8. data/lib/autobuild.rb +4 -5
  9. data/lib/autobuild/build_logfile.rb +6 -4
  10. data/lib/autobuild/config.rb +104 -41
  11. data/lib/autobuild/configurable.rb +32 -18
  12. data/lib/autobuild/environment.rb +126 -120
  13. data/lib/autobuild/exceptions.rb +48 -31
  14. data/lib/autobuild/import/archive.rb +134 -82
  15. data/lib/autobuild/import/cvs.rb +28 -24
  16. data/lib/autobuild/import/darcs.rb +13 -16
  17. data/lib/autobuild/import/git-lfs.rb +37 -30
  18. data/lib/autobuild/import/git.rb +246 -182
  19. data/lib/autobuild/import/hg.rb +23 -18
  20. data/lib/autobuild/import/svn.rb +48 -29
  21. data/lib/autobuild/importer.rb +534 -499
  22. data/lib/autobuild/mail_reporter.rb +77 -77
  23. data/lib/autobuild/package.rb +200 -122
  24. data/lib/autobuild/packages/autotools.rb +47 -42
  25. data/lib/autobuild/packages/cmake.rb +77 -65
  26. data/lib/autobuild/packages/dummy.rb +9 -8
  27. data/lib/autobuild/packages/genom.rb +1 -1
  28. data/lib/autobuild/packages/gnumake.rb +74 -31
  29. data/lib/autobuild/packages/import.rb +2 -6
  30. data/lib/autobuild/packages/orogen.rb +32 -31
  31. data/lib/autobuild/packages/pkgconfig.rb +2 -2
  32. data/lib/autobuild/packages/python.rb +12 -8
  33. data/lib/autobuild/packages/ruby.rb +22 -17
  34. data/lib/autobuild/parallel.rb +50 -46
  35. data/lib/autobuild/pkgconfig.rb +25 -13
  36. data/lib/autobuild/progress_display.rb +149 -64
  37. data/lib/autobuild/rake_task_extension.rb +12 -7
  38. data/lib/autobuild/reporting.rb +51 -26
  39. data/lib/autobuild/subcommand.rb +72 -65
  40. data/lib/autobuild/test.rb +9 -7
  41. data/lib/autobuild/test_utility.rb +12 -10
  42. data/lib/autobuild/timestamps.rb +28 -23
  43. data/lib/autobuild/tools.rb +17 -16
  44. data/lib/autobuild/utility.rb +67 -23
  45. data/lib/autobuild/version.rb +1 -1
  46. metadata +53 -37
@@ -8,100 +8,100 @@
8
8
 
9
9
  ## Report by mail
10
10
  if Autobuild::HAS_RMAIL
11
- module Autobuild
12
- class MailReporter < Reporter
13
- def default_mail
14
- Etc::endpwent
15
- uname = while (pwent = Etc::getpwent)
16
- break (pwent.name) if pwent.uid == Process.uid
17
- end
11
+ module Autobuild
12
+ class MailReporter < Reporter
13
+ def default_mail
14
+ Etc.endpwent
15
+ uname = while (pwent = Etc.getpwent)
16
+ break pwent.name if pwent.uid == Process.uid
17
+ end
18
18
 
19
- raise "FATAL: cannot find a user with uid=#{Process.uid}" unless uname
20
- "#{pwent.name}@#{Socket.gethostname}"
21
- end
22
-
23
- attr_reader :from_email, :to_email, :smtp_hostname, :smtp_port, :subject, :only_errors
24
- def initialize(config)
25
- @from_email = (config[:from] || default_mail)
26
- @to_email = (config[:to] || default_mail)
27
- @subject = (config[:subject] || "Build %result% on #{Socket.gethostname} at %time%")
28
- @only_errors = config[:only_errors]
29
- @smtp_hostname = (config[:smtp] || "localhost" )
30
- @smtp_port = Integer(config[:port] || Socket.getservbyname('smtp'))
31
- end
19
+ raise "FATAL: cannot find a user with uid=#{Process.uid}" unless uname
32
20
 
33
- def error(error)
34
- if error.mail?
35
- send_mail("failed", error.to_s)
21
+ "#{pwent.name}@#{Socket.gethostname}"
36
22
  end
37
- end
38
23
 
39
- def success
40
- unless only_errors
41
- send_mail("success", Autobuild.post_success_message || "")
24
+ attr_reader :from_email, :to_email, :smtp_hostname, :smtp_port,
25
+ :subject, :only_errors
26
+ def initialize(config)
27
+ @from_email = (config[:from] || default_mail)
28
+ @to_email = (config[:to] || default_mail)
29
+ @subject =
30
+ config[:subject] ||
31
+ "Build %result% on #{Socket.gethostname} at %time%"
32
+ @only_errors = config[:only_errors]
33
+ @smtp_hostname = (config[:smtp] || "localhost")
34
+ @smtp_port = Integer(config[:port] || Socket.getservbyname('smtp'))
42
35
  end
43
- end
44
36
 
45
- def send_mail(result, body = "")
46
- mail = RMail::Message.new
47
- mail.header.date = Time.now
48
- mail.header.from = from_email
49
- mail.header.subject = subject.
50
- gsub('%result%', result).
51
- gsub('%time%', Time.now.to_s).
52
- gsub('%hostname%', Socket.gethostname)
53
-
54
- part = RMail::Message.new
55
- part.header.set('Content-Type', 'text/plain')
56
- part.body = body
57
- mail.add_part(part)
37
+ def error(error)
38
+ send_mail("failed", error.to_s) if error.mail?
39
+ end
58
40
 
59
- # Attach log files
60
- Reporting.each_log do |file|
61
- name = file[Autobuild.logdir.size..-1]
62
- mail.add_file(name, file)
41
+ def success
42
+ unless only_errors
43
+ send_mail("success", Autobuild.post_success_message || "")
44
+ end
63
45
  end
64
46
 
65
- # Send the mails
66
- if smtp_hostname =~ /\// && File.directory?(File.dirname(smtp_hostname))
67
- File.open(smtp_hostname, 'w') do |io|
68
- io.puts "From: #{from_email}"
69
- io.puts "To: #{to_email.join(" ")}"
70
- io.write RMail::Serialize.write('', mail)
47
+ def send_mail(result, body = "")
48
+ mail = RMail::Message.new
49
+ mail.header.date = Time.now
50
+ mail.header.from = from_email
51
+ mail.header.subject = subject.
52
+ gsub('%result%', result).
53
+ gsub('%time%', Time.now.to_s).
54
+ gsub('%hostname%', Socket.gethostname)
55
+
56
+ part = RMail::Message.new
57
+ part.header.set('Content-Type', 'text/plain')
58
+ part.body = body
59
+ mail.add_part(part)
60
+
61
+ # Attach log files
62
+ Reporting.each_log do |file|
63
+ name = file[Autobuild.logdir.size..-1]
64
+ mail.add_file(name, file)
71
65
  end
72
- puts "saved notification email in #{smtp_hostname}"
73
- else
74
- smtp = Net::SMTP.new(smtp_hostname, smtp_port)
75
- smtp.start {
76
- to_email.each do |email|
77
- mail.header.to = email
78
- smtp.send_mail RMail::Serialize.write('', mail), from_email, email
66
+
67
+ # Send the mails
68
+ if smtp_hostname =~ %r{/} && File.directory?(File.dirname(smtp_hostname))
69
+ File.open(smtp_hostname, 'w') do |io|
70
+ io.puts "From: #{from_email}"
71
+ io.puts "To: #{to_email.join(' ')}"
72
+ io.write RMail::Serialize.write('', mail)
73
+ end
74
+ puts "saved notification email in #{smtp_hostname}"
75
+ else
76
+ smtp = Net::SMTP.new(smtp_hostname, smtp_port)
77
+ smtp.start do
78
+ to_email.each do |email|
79
+ mail.header.to = email
80
+ smtp.send_mail(RMail::Serialize.write('', mail),
81
+ from_email, email)
82
+ end
79
83
  end
80
- }
81
84
 
82
- # Notify the sending
83
- puts "sent notification mail to #{to_email} with source #{from_email}"
85
+ # Notify the sending
86
+ puts "sent notification mail to #{to_email} with source #{from_email}"
87
+ end
84
88
  end
85
89
  end
86
90
  end
87
- end
88
91
 
89
- module RMail
90
- class Message
91
- ## Attachs a file to a message
92
- def add_file(name, path, content_type='text/plain')
93
- part = RMail::Message.new
94
- part.header.set('Content-Type', content_type)
95
- part.header.set('Content-Disposition', 'attachment', 'filename' => name)
96
- part.body = ''
97
- File.open(path) do |file|
98
- part.body << file.readlines.join("")
92
+ module RMail
93
+ class Message
94
+ ## Attachs a file to a message
95
+ def add_file(name, path, content_type = 'text/plain')
96
+ part = RMail::Message.new
97
+ part.header.set('Content-Type', content_type)
98
+ part.header.set('Content-Disposition', 'attachment', 'filename' => name)
99
+ part.body = ''
100
+ File.open(path) do |file|
101
+ part.body << file.readlines.join("")
102
+ end
103
+ add_part(part)
99
104
  end
100
- self.add_part(part)
101
105
  end
102
106
  end
103
107
  end
104
- end # if Autobuild::HAS_RMAIL
105
-
106
-
107
-
@@ -1,10 +1,10 @@
1
1
  module Autobuild
2
- TARGETS = %w{import prepare build}
2
+ TARGETS = %w[import prepare build].freeze
3
3
 
4
4
  class << self
5
5
  attr_accessor :ignore_errors
6
6
  end
7
-
7
+
8
8
  # Basic block for the autobuilder
9
9
  #
10
10
  # The build is done in three phases:
@@ -43,14 +43,18 @@ class Package
43
43
  # The set of utilities attached to this package
44
44
  # @return [{String=>Utility}]
45
45
  attr_reader :utilities
46
+
46
47
  # Whether {#apply_post_install} has been called
47
- def applied_post_install?; !!@applied_post_install end
48
-
48
+ def applied_post_install?
49
+ @applied_post_install
50
+ end
51
+
49
52
  # Sets importer object for this package. Defined for backwards compatibility.
50
53
  # Use the #importer attribute instead
51
54
  def import=(value)
52
55
  @importer = value
53
56
  end
57
+
54
58
  # Sets an importer object for this package
55
59
  attr_accessor :importer
56
60
 
@@ -74,11 +78,20 @@ def add_stat(phase, duration)
74
78
  end
75
79
 
76
80
  # Absolute path to the source directory. See #srcdir=
77
- def srcdir; File.expand_path(@srcdir || name, Autobuild.srcdir) end
81
+ def srcdir
82
+ File.expand_path(@srcdir || name, Autobuild.srcdir)
83
+ end
84
+
78
85
  # Absolute path to the import directory. See #importdir=
79
- def importdir; File.expand_path(@importdir || srcdir, Autobuild.srcdir) end
86
+ def importdir
87
+ File.expand_path(@importdir || srcdir, Autobuild.srcdir)
88
+ end
89
+
80
90
  # Absolute path to the installation directory. See #prefix=
81
- def prefix; File.expand_path(@prefix || '', Autobuild.prefix) end
91
+ def prefix
92
+ File.expand_path(@prefix || '', Autobuild.prefix)
93
+ end
94
+
82
95
  # Absolute path to the log directory for this package. See #logdir=
83
96
  def logdir
84
97
  if @logdir
@@ -115,17 +128,19 @@ def update?
115
128
  # Returns true if this package has already been updated. It will not be
116
129
  # true if the importer has been called while Autobuild.do_update was
117
130
  # false.
118
- def updated?; !!@updated end
131
+ def updated?
132
+ @updated
133
+ end
119
134
 
120
135
  def initialize(spec = Hash.new)
121
136
  @srcdir = @importdir = @logdir = @prefix = nil
122
137
  @updated = false
123
138
  @update = nil
124
139
  @failed = nil
125
- @dependencies = Array.new
126
- @provides = Array.new
140
+ @dependencies = Array.new
141
+ @provides = Array.new
142
+ @statistics = Hash.new
127
143
  @parallel_build_level = nil
128
- @statistics = Hash.new
129
144
  @failures = Array.new
130
145
  @post_install_blocks = Array.new
131
146
  @applied_post_install = false
@@ -139,19 +154,23 @@ def initialize(spec = Hash.new)
139
154
  if Hash === spec
140
155
  name, depends = spec.to_a.first
141
156
  else
142
- name, depends = spec, nil
157
+ name = spec
158
+ depends = nil
143
159
  end
144
160
 
145
161
  name = name.to_s
146
162
  @name = name
147
- raise ConfigException, "package #{name} is already defined" if Autobuild::Package[name]
163
+ if Autobuild::Package[name]
164
+ raise ConfigException, "package #{name} is already defined"
165
+ end
166
+
148
167
  @@packages[name] = self
149
168
 
150
169
  # Call the config block (if any)
151
170
  yield(self) if block_given?
152
171
 
153
- self.doc_utility.source_dir ||= 'doc'
154
- self.doc_utility.target_dir ||= name
172
+ doc_utility.source_dir ||= 'doc'
173
+ doc_utility.target_dir ||= name
155
174
 
156
175
  # Define the default tasks
157
176
  task "#{name}-import" do
@@ -174,12 +193,10 @@ def initialize(spec = Hash.new)
174
193
  Rake::Task["#{name}-import"].invoke
175
194
  Rake::Task["#{name}-prepare"].invoke
176
195
  Rake::Task["#{name}-build"].invoke
177
- if has_doc? && Autobuild.do_doc
178
- Rake::Task["#{name}-doc"].invoke
179
- end
196
+ Rake::Task["#{name}-doc"].invoke if has_doc? && Autobuild.do_doc
180
197
  end
181
198
  task :default => name
182
-
199
+
183
200
  # The dependencies will be declared in the import phase, so save
184
201
  # them there for now
185
202
  @spec_dependencies = depends
@@ -190,34 +207,29 @@ def checked_out?
190
207
  File.directory?(srcdir)
191
208
  end
192
209
 
193
- def prepare_invoked?
194
- task("#{name}-prepare").already_invoked?
195
- end
196
-
197
- def prepared?
198
- @prepared
199
- end
200
-
201
210
  def import_invoked?
202
- task("#{name}-import").already_invoked?
211
+ @import_invoked
203
212
  end
204
213
 
205
214
  def imported?
206
215
  @imported
207
216
  end
208
217
 
209
- def build_invoked?
210
- task("#{name}-build").already_invoked?
218
+ def install_invoked?
219
+ @install_invoked
211
220
  end
212
221
 
213
- def built?
214
- @built
222
+ def installed?
223
+ @installed
215
224
  end
216
225
 
217
226
  def to_s
218
227
  "#<#{self.class} name=#{name}>"
219
228
  end
220
- def inspect; to_s end
229
+
230
+ def inspect
231
+ to_s
232
+ end
221
233
 
222
234
  # @api private
223
235
  #
@@ -226,8 +238,8 @@ def inspect; to_s end
226
238
  #
227
239
  # @param [EnvOp] op
228
240
  # @return [void]
229
- def add_env_op(op)
230
- env << op
241
+ def add_env_op(envop)
242
+ env << envop
231
243
  end
232
244
 
233
245
  # Add value(s) to a list-based environment variable
@@ -300,11 +312,15 @@ class IncompatibleEnvironment < ConfigException; end
300
312
  def apply_env(env, set = Hash.new, ops = Array.new)
301
313
  self.env.each do |env_op|
302
314
  next if ops.last == env_op
315
+
303
316
  if env_op.type == :set
304
- if last = set[env_op.name]
317
+ if (last = set[env_op.name])
305
318
  last_pkg, last_values = *last
306
319
  if last_values != env_op.values
307
- raise IncompatibleEnvironment, "trying to reset #{env_op.name} to #{env_op.values} in #{self.name} but this conflicts with #{last_pkg.name} already setting it to #{last_values}"
320
+ raise IncompatibleEnvironment, "trying to reset "\
321
+ "#{env_op.name} to #{env_op.values} in #{name} "\
322
+ "but this conflicts with #{last_pkg.name} "\
323
+ "already setting it to #{last_values}"
308
324
  end
309
325
  else
310
326
  set[env_op.name] = [self, env_op.values]
@@ -356,9 +372,7 @@ def resolved_env(root = Autobuild.env)
356
372
  # target files so that all the build phases of this package gets
357
373
  # retriggered. However, it should not clean the build products.
358
374
  def prepare_for_forced_build
359
- if File.exist?(installstamp)
360
- FileUtils.rm_f installstamp
361
- end
375
+ FileUtils.rm_f installstamp if File.exist?(installstamp)
362
376
  end
363
377
 
364
378
  # Called when the user asked for a full rebuild. It should delete the
@@ -366,9 +380,7 @@ def prepare_for_forced_build
366
380
  def prepare_for_rebuild
367
381
  prepare_for_forced_build
368
382
 
369
- if File.exist?(installstamp)
370
- FileUtils.rm_f installstamp
371
- end
383
+ FileUtils.rm_f installstamp if File.exist?(installstamp)
372
384
  end
373
385
 
374
386
  # Returns true if one of the operations applied on this package failed
@@ -388,18 +400,18 @@ def failed?
388
400
  # will subsequently be a noop. I.e. if +build+ fails, +install+ will do
389
401
  # nothing.
390
402
  def isolate_errors(options = Hash.new)
391
- if !options.kind_of?(Hash)
392
- options = Hash[mark_as_failed: true]
393
- end
403
+ options = Hash[mark_as_failed: true] unless options.kind_of?(Hash)
394
404
  options = validate_options options,
395
405
  mark_as_failed: true,
396
406
  ignore_errors: Autobuild.ignore_errors
397
407
 
398
408
  # Don't do anything if we already have failed
399
409
  if failed?
400
- if !options[:ignore_errors]
401
- raise AlreadyFailedError, "attempting to do an operation on a failed package"
410
+ unless options[:ignore_errors]
411
+ raise AlreadyFailedError, "attempting to do an operation "\
412
+ "on a failed package"
402
413
  end
414
+
403
415
  return
404
416
  end
405
417
 
@@ -413,18 +425,12 @@ def isolate_errors(options = Hash.new)
413
425
  raise
414
426
  rescue ::Exception => e
415
427
  @failures << e
416
- if options[:mark_as_failed]
417
- @failed = true
418
- end
428
+ @failed = true if options[:mark_as_failed]
419
429
 
420
430
  if options[:ignore_errors]
421
431
  lines = e.to_s.split("\n")
422
- if lines.empty?
423
- lines = e.message.split("\n")
424
- end
425
- if lines.empty?
426
- lines = ["unknown error"]
427
- end
432
+ lines = e.message.split("\n") if lines.empty?
433
+ lines = ["unknown error"] if lines.empty?
428
434
  message(lines.shift, :red, :bold)
429
435
  lines.each do |line|
430
436
  message(line)
@@ -434,9 +440,7 @@ def isolate_errors(options = Hash.new)
434
440
  raise
435
441
  end
436
442
  ensure
437
- if toplevel
438
- Thread.current[:isolate_errors] = false
439
- end
443
+ Thread.current[:isolate_errors] = false if toplevel
440
444
  end
441
445
  end
442
446
 
@@ -445,16 +449,15 @@ def isolate_errors(options = Hash.new)
445
449
  #
446
450
  # (see Importer#import)
447
451
  def import(options = Hash.new)
448
- if !options.respond_to?(:to_hash)
449
- options = Hash[only_local: options]
450
- end
452
+ options = Hash[only_local: options] unless options.respond_to?(:to_hash)
451
453
 
454
+ @import_invoked = true
452
455
  if @importer
453
456
  result = @importer.import(self, options)
454
- @imported = true
455
457
  elsif update?
456
458
  message "%s: no importer defined, doing nothing"
457
459
  end
460
+ @imported = true
458
461
 
459
462
  # Add the dependencies declared in spec
460
463
  depends_on(*@spec_dependencies) if @spec_dependencies
@@ -470,7 +473,10 @@ def prepare
470
473
  stamps = dependencies.map { |p| Package[p].installstamp }
471
474
 
472
475
  file installstamp => stamps do
473
- isolate_errors { install }
476
+ isolate_errors do
477
+ @install_invoked = true
478
+ install
479
+ end
474
480
  end
475
481
  task "#{name}-build" => installstamp
476
482
 
@@ -478,7 +484,8 @@ def prepare
478
484
  end
479
485
 
480
486
  def process_formatting_string(msg, *prefix_style)
481
- prefix, suffix = [], []
487
+ prefix = []
488
+ suffix = []
482
489
  msg.split(" ").each do |token|
483
490
  if token =~ /%s/
484
491
  suffix << token.gsub(/%s/, name)
@@ -492,7 +499,8 @@ def process_formatting_string(msg, *prefix_style)
492
499
  elsif prefix_style.empty?
493
500
  return (prefix + suffix).join(" ")
494
501
  else
495
- return [Autobuild.color(prefix.join(" "), *prefix_style), *suffix].join(" ")
502
+ colorized_prefix = Autobuild.color(prefix.join(" "), *prefix_style)
503
+ return [colorized_prefix, *suffix].join(" ")
496
504
  end
497
505
  end
498
506
 
@@ -511,17 +519,13 @@ def error(error_string)
511
519
  # Display a progress message. %s in the string is replaced by the
512
520
  # package name
513
521
  def message(*args)
514
- if !args.empty?
515
- args[0] = " #{process_formatting_string(args[0])}"
516
- end
522
+ args[0] = " #{process_formatting_string(args[0])}" unless args.empty?
517
523
  Autobuild.message(*args)
518
524
  end
519
525
 
520
526
  def progress_start(*args, done_message: nil, **raw_options, &block)
521
527
  args[0] = process_formatting_string(args[0], :bold)
522
- if done_message
523
- done_message = process_formatting_string(done_message)
524
- end
528
+ done_message = process_formatting_string(done_message) if done_message
525
529
  Autobuild.progress_start(self, *args,
526
530
  done_message: done_message, **raw_options, &block)
527
531
  end
@@ -555,22 +559,31 @@ def install
555
559
 
556
560
  Autobuild.touch_stamp(installstamp)
557
561
 
558
- @built = true
562
+ @installed = true
559
563
  end
560
564
 
561
565
  def run(*args, &block)
562
- if args.last.kind_of?(Hash)
563
- options = args.pop
564
- else
565
- options = Hash.new
566
- end
566
+ options =
567
+ if args.last.kind_of?(Hash)
568
+ args.pop
569
+ else
570
+ Hash.new
571
+ end
567
572
  options[:env] = options.delete(:resolved_env) ||
568
- (options[:env] || Hash.new).merge(resolved_env)
573
+ (options[:env] || Hash.new).merge(resolved_env)
569
574
  Autobuild::Subprocess.run(self, *args, options, &block)
570
575
  end
571
576
 
572
577
  module TaskExtension
573
578
  attr_accessor :package
579
+
580
+ def disabled?
581
+ if @disabled.nil? && package
582
+ package.disabled?
583
+ else
584
+ super
585
+ end
586
+ end
574
587
  end
575
588
 
576
589
  def source_tree(*args, &block)
@@ -596,17 +609,49 @@ def task(*args, &block)
596
609
  task
597
610
  end
598
611
 
599
- def doc_dir=(value); doc_utility.source_dir = value end
600
- def doc_dir; doc_utility.source_dir end
601
- def doc_target_dir=(value); doc_utility.target_dir = value end
602
- def doc_target_dir; doc_utility.target_dir end
603
- def doc_task(&block); doc_utility.task(&block) end
604
- def generates_doc?; doc_utility.enabled? end
605
- def enable_doc; doc_utility.enabled = true end
606
- def disable_doc; doc_utility.enabled = false end
607
- def install_doc; doc_utility.install end
608
- def doc_disabled; doc_utility.disabled end
609
- def has_doc?; doc_utility.has_task? end
612
+ def doc_dir=(value)
613
+ doc_utility.source_dir = value
614
+ end
615
+
616
+ def doc_dir
617
+ doc_utility.source_dir
618
+ end
619
+
620
+ def doc_target_dir=(value)
621
+ doc_utility.target_dir = value
622
+ end
623
+
624
+ def doc_target_dir
625
+ doc_utility.target_dir
626
+ end
627
+
628
+ def doc_task(&block)
629
+ doc_utility.task(&block)
630
+ end
631
+
632
+ def generates_doc?
633
+ doc_utility.enabled?
634
+ end
635
+
636
+ def enable_doc
637
+ doc_utility.enabled = true
638
+ end
639
+
640
+ def disable_doc
641
+ doc_utility.enabled = false
642
+ end
643
+
644
+ def install_doc
645
+ doc_utility.install
646
+ end
647
+
648
+ def doc_disabled
649
+ doc_utility.disabled
650
+ end
651
+
652
+ def has_doc?
653
+ doc_utility.has_task?
654
+ end
610
655
 
611
656
  def post_install(*args, &block)
612
657
  if args.empty?
@@ -618,11 +663,43 @@ def post_install(*args, &block)
618
663
  end
619
664
  end
620
665
 
666
+ def self_fingerprint
667
+ importer.fingerprint(self)
668
+ end
669
+
670
+ # Returns a unique hash representing a state of the package and
671
+ # its dependencies, if any dependency can't calculate its own
672
+ # fingerprint the result will be nil
673
+ # @return [String]
674
+ def fingerprint(recursive: true, memo: {})
675
+ return memo[name] if memo.key?(name)
676
+
677
+ self_fingerprint = self.self_fingerprint
678
+ return unless self_fingerprint
679
+ if dependencies.empty?
680
+ return (memo[name] = self_fingerprint)
681
+ elsif !recursive
682
+ return self_fingerprint
683
+ end
684
+
685
+ dependency_fingerprints = dependencies.sort.map do |pkg_name|
686
+ pkg = Autobuild::Package[pkg_name]
687
+ unless (fingerprint = memo[pkg.name])
688
+ fingerprint = pkg.fingerprint(recursive: true, memo: memo)
689
+ return unless fingerprint
690
+ end
691
+ fingerprint
692
+ end
693
+
694
+ memo[name] = Digest::SHA1.hexdigest(
695
+ self_fingerprint + dependency_fingerprints.join(""))
696
+ end
697
+
621
698
  # Returns the name of all the packages +self+ depends on
622
699
  def all_dependencies(result = Set.new)
623
700
  dependencies.each do |pkg_name|
624
701
  pkg = Autobuild::Package[pkg_name]
625
- if !result.include?(pkg.name)
702
+ unless result.include?(pkg.name)
626
703
  result << pkg.name
627
704
  pkg.all_dependencies(result)
628
705
  end
@@ -642,18 +719,21 @@ def depends_on?(package_name)
642
719
  def depends_on(*packages)
643
720
  packages.each do |p|
644
721
  p = p.name if p.respond_to?(:name)
645
- raise ArgumentError, "#{p.inspect} should be a string" if !p.respond_to? :to_str
722
+ unless p.respond_to?(:to_str)
723
+ raise ArgumentError, "#{p.inspect} should be a string"
724
+ end
725
+
646
726
  p = p.to_str
647
727
  next if p == name
648
- unless pkg = Package[p]
649
- raise ConfigException.new(self), "package #{p}, listed as a dependency of #{self.name}, is not defined"
728
+
729
+ unless (pkg = Package[p])
730
+ raise ConfigException.new(self), "package #{p}, "\
731
+ "listed as a dependency of #{name}, is not defined"
650
732
  end
651
733
 
652
734
  next if @dependencies.include?(pkg.name)
653
735
 
654
- if Autobuild.verbose
655
- Autobuild.message "#{name} depends on #{pkg.name}"
656
- end
736
+ Autobuild.message "#{name} depends on #{pkg.name}" if Autobuild.verbose
657
737
 
658
738
  task "#{name}-import" => "#{pkg.name}-import"
659
739
  task "#{name}-prepare" => "#{pkg.name}-prepare"
@@ -666,16 +746,17 @@ def depends_on(*packages)
666
746
  # listed in +packages+ are aliases for this package.
667
747
  def provides(*packages)
668
748
  packages.each do |p|
669
- raise ArgumentError, "#{p.inspect} should be a string" if !p.respond_to? :to_str
749
+ unless p.respond_to?(:to_str)
750
+ raise ArgumentError, "#{p.inspect} should be a string"
751
+ end
752
+
670
753
  p = p.to_str
671
754
  next if p == name
672
755
  next if @provides.include?(name)
673
756
 
674
- @@provides[p] = self
757
+ @@provides[p] = self
675
758
 
676
- if Autobuild.verbose
677
- Autobuild.message "#{name} provides #{p}"
678
- end
759
+ Autobuild.message "#{name} provides #{p}" if Autobuild.verbose
679
760
 
680
761
  task p => name
681
762
  task "#{p}-import" => "#{name}-import"
@@ -688,13 +769,11 @@ def provides(*packages)
688
769
  # Iterates on all available packages
689
770
  # if with_provides is true, includes the list
690
771
  # of package aliases
691
- def self.each(with_provides = false, &p)
692
- if !p
693
- return enum_for(:each, with_provides)
694
- end
772
+ def self.each(with_provides = false, &block)
773
+ return enum_for(:each, with_provides) unless block
695
774
 
696
- @@packages.each(&p)
697
- @@provides.each(&p) if with_provides
775
+ @@packages.each(&block)
776
+ @@provides.each(&block) if with_provides
698
777
  end
699
778
 
700
779
  # Gets a package from its name
@@ -719,7 +798,7 @@ def parallel_build_level=(value)
719
798
  end
720
799
 
721
800
  # Returns the level of parallelism authorized during the build for this
722
- # particular package. If not set, defaults to the system-wide option
801
+ # particular package. If not set, defaults to the system-wide option
723
802
  # (Autobuild.parallel_build_level and Autobuild.parallel_build_level=).
724
803
  #
725
804
  # The default value is the number of CPUs on this system.
@@ -740,7 +819,6 @@ def working_directory
740
819
  def in_dir(directory)
741
820
  @in_dir_stack << directory
742
821
  yield
743
-
744
822
  ensure
745
823
  @in_dir_stack.pop
746
824
  end
@@ -754,35 +832,36 @@ def disable_phases(*phases)
754
832
  phases.each do |phase|
755
833
  task "#{name}-#{phase}"
756
834
  t = Rake::Task["#{name}-#{phase}"]
757
- t.disable!
835
+ t.disable
758
836
  end
759
837
  end
760
838
 
761
839
  # Make sure that this package will be ignored in the build
762
840
  def disable(phases = Autobuild.all_phases)
763
841
  @disabled = true
764
- disable_phases(*phases)
765
- task(installstamp)
766
- t = Rake::Task[installstamp]
767
- t.disable!
768
842
  end
769
843
 
770
844
  def utility(utility_name)
771
- utilities[utility_name] ||= Autobuild.create_utility(utility_name, self)
845
+ utilities[utility_name.to_s] ||= Autobuild.create_utility(utility_name, self)
772
846
  end
773
847
 
848
+ def respond_to_missing?(name, _include_all)
849
+ utilities.key?(name.to_s)
850
+ end
774
851
 
775
- def method_missing(m, *args, &block)
776
- case m.to_s
852
+ def method_missing(name, *args, &block)
853
+ case name.to_s
777
854
  when /(\w+)_utility$/
778
855
  utility_name = $1
779
- if !args.empty?
856
+
857
+ unless args.empty?
780
858
  raise ArgumentError, "expected 0 arguments and got #{args.size}"
781
859
  end
860
+
782
861
  begin
783
862
  return utility(utility_name)
784
863
  rescue ArgumentError => e
785
- raise NoMethodError.new(m), e.message, e.backtrace
864
+ raise NoMethodError.new(name), e.message, e.backtrace
786
865
  end
787
866
  end
788
867
  super
@@ -797,4 +876,3 @@ def self.package_set(spec)
797
876
  end
798
877
  end
799
878
  end
800
-