autobuild 1.17.0 → 1.21.0

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