autobuild 1.6.0.b2 → 1.6.0.b3

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt CHANGED
@@ -21,9 +21,11 @@ lib/autobuild/packages/autotools.rb
21
21
  lib/autobuild/packages/cmake.rb
22
22
  lib/autobuild/packages/dummy.rb
23
23
  lib/autobuild/packages/genom.rb
24
+ lib/autobuild/packages/gnumake.rb
24
25
  lib/autobuild/packages/import.rb
25
26
  lib/autobuild/packages/orogen.rb
26
27
  lib/autobuild/packages/pkgconfig.rb
28
+ lib/autobuild/parallel.rb
27
29
  lib/autobuild/pkgconfig.rb
28
30
  lib/autobuild/reporting.rb
29
31
  lib/autobuild/subcommand.rb
@@ -0,0 +1,48 @@
1
+ module Autobuild
2
+ def self.make_is_gnumake?(path = Autobuild.tool(:make))
3
+ @make_is_gnumake ||= Hash.new
4
+ if @make_is_gnumake.has_key?(path)
5
+ @make_is_gnumake[path]
6
+ else
7
+ result = `#{path} --version`
8
+ @make_is_gnumake[path] = $?.success? &&
9
+ (result.split("\n").first =~ /GNU Make/)
10
+ end
11
+ end
12
+
13
+ def self.make_has_j_option?(path = Autobuild.tool(:make))
14
+ make_is_gnumake?(path)
15
+ end
16
+
17
+ def self.make_has_gnumake_jobserver?(path = Autobuild.tool(:make))
18
+ make_is_gnumake?(path)
19
+ end
20
+
21
+ def self.make_subcommand(pkg, phase, *options, &block)
22
+ reserved = nil
23
+ cmd_path = Autobuild.tool(:make)
24
+ cmd = [cmd_path]
25
+ if make_has_j_option?(cmd_path) && pkg.parallel_build_level != 1
26
+ if manager = Autobuild.parallel_task_manager
27
+ job_server = manager.job_server
28
+ if !make_has_gnumake_jobserver?(cmd_path) || (pkg.parallel_build_level != Autobuild.parallel_build_level)
29
+ reserved = pkg.parallel_build_level
30
+ job_server.get(reserved - 1) # We already have one token taken by autobuild itself
31
+ cmd << "-j#{pkg.parallel_build_level}"
32
+ else
33
+ cmd << "--jobserver-fds=#{job_server.rio.fileno},#{job_server.wio.fileno}" << "-j"
34
+ end
35
+ else
36
+ cmd << "-j#{pkg.parallel_build_level}"
37
+ end
38
+ end
39
+
40
+ cmd.concat(options)
41
+ Subprocess.run(pkg, phase, *cmd, &block)
42
+
43
+ ensure
44
+ if reserved
45
+ job_server.put(reserved)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,216 @@
1
+ require 'thread'
2
+
3
+ module Autobuild
4
+ # This is a rewrite of the Rake task invocation code to use parallelism
5
+ #
6
+ # Since autobuild does not use task arguments, we don't support them for
7
+ # simplicity
8
+ class RakeTaskParallelism
9
+ attr_reader :available_workers
10
+ attr_reader :finished_workers
11
+ attr_reader :workers
12
+
13
+ attr_reader :job_server
14
+
15
+ attr_reader :roots
16
+ attr_reader :tasks
17
+ attr_reader :reverse_dependencies
18
+
19
+ class JobServer
20
+ attr_reader :rio
21
+ attr_reader :wio
22
+
23
+ def initialize(level)
24
+ @rio, @wio = IO.pipe
25
+ put(level)
26
+ end
27
+ def get(token_count = 1)
28
+ @rio.read(token_count)
29
+ end
30
+ def put(token_count = 1)
31
+ @wio.write(" " * token_count)
32
+ end
33
+ end
34
+
35
+ def initialize(level = Autobuild.parallel_build_level)
36
+ @job_server = JobServer.new(level)
37
+ @available_workers = Array.new
38
+ @finished_workers = Queue.new
39
+ @workers = Array.new
40
+
41
+ end
42
+
43
+ def process_finished_task(task, processed, queue)
44
+ processed << task
45
+ #puts "finished #{task} (#{task.object_id}), considering dependencies"
46
+ reverse_dependencies[task].each do |candidate|
47
+ if candidate.prerequisite_tasks.all? { |t| processed.include?(t) }
48
+ #puts " adding #{candidate}"
49
+ queue << candidate
50
+ else
51
+ #puts " rejecting #{candidate} (#{candidate.prerequisite_tasks.find_all { |t| !processed.include?(t) }.map(&:name).join(", ")})"
52
+ end
53
+ end
54
+ end
55
+
56
+ def wait_for_worker_to_end(processed, queue)
57
+ w = finished_workers.pop
58
+ finished_task, error = w.last_result
59
+ available_workers << w
60
+ if error
61
+ if available_workers.size != workers.size
62
+ Autobuild.message "got an error doing parallel processing, waiting for pending jobs to end"
63
+ end
64
+ finish_pending_work
65
+ raise error
66
+ end
67
+
68
+ process_finished_task(finished_task, processed, queue)
69
+ end
70
+
71
+ def discover_dependencies(t)
72
+ return if tasks.include?(t) # already discovered or being discovered
73
+ #puts "adding #{t}"
74
+ tasks << t
75
+
76
+ t.prerequisite_tasks.each do |dep_t|
77
+ reverse_dependencies[dep_t] << t
78
+ discover_dependencies(dep_t)
79
+ end
80
+ end
81
+
82
+ # Invokes the provided tasks. Unlike the rake code, this is a toplevel
83
+ # algorithm that does not use recursion
84
+ def invoke_parallel(required_tasks)
85
+ @tasks = Set.new
86
+ @reverse_dependencies = Hash.new { |h, k| h[k] = Set.new }
87
+ required_tasks.each do |t|
88
+ discover_dependencies(t)
89
+ end
90
+ @roots = tasks.find_all { |t| t.prerequisite_tasks.empty? }.to_set
91
+
92
+ #puts "roots:"
93
+ roots.each do |t|
94
+ #puts " #{t}"
95
+ end
96
+ #puts
97
+
98
+ # Build a reverse dependency graph (i.e. a mapping from a task to
99
+ # the tasks that depend on it)
100
+
101
+ # This is kind-of a topological sort. However, we don't do the full
102
+ # topological sort since we would then have to scan all tasks each
103
+ # time for tasks that have no currently running prerequisites
104
+
105
+ # The queue is the set of tasks for which all prerequisites have
106
+ # been successfully executed (or where not needed). I.e. it is the
107
+ # set of tasks that can be queued for execution.
108
+ queue = roots.to_a
109
+ processed = Set.new
110
+ while true
111
+ if queue.empty?
112
+ # If we have pending workers, wait for one to be finished
113
+ # until either they are all finished or the queue is not
114
+ # empty anymore
115
+ while queue.empty? && available_workers.size != workers.size
116
+ wait_for_worker_to_end(processed, queue)
117
+ end
118
+
119
+ if queue.empty? && available_workers.size == workers.size
120
+ break
121
+ end
122
+ end
123
+
124
+ pending_task = queue.pop
125
+ #puts "#{processed.size} tasks processed so far, #{tasks.size} total"
126
+ if pending_task.instance_variable_get(:@already_invoked) || !pending_task.needed?
127
+ process_finished_task(pending_task, processed, queue)
128
+ next
129
+ end
130
+
131
+ # Get a job server token
132
+ job_server.get
133
+
134
+ while !finished_workers.empty?
135
+ wait_for_worker_to_end(processed, queue)
136
+ end
137
+
138
+ # We do have a job server token, so we are allowed to allocate a
139
+ # new worker if none are available
140
+ if available_workers.empty?
141
+ w = Worker.new(job_server, finished_workers)
142
+ available_workers << w
143
+ workers << w
144
+ end
145
+
146
+ worker = available_workers.pop
147
+ #puts "queueing #{pending_task}"
148
+ worker.queue(pending_task)
149
+ end
150
+
151
+ if processed.size != tasks.size
152
+ with_cycle = tasks.to_set
153
+ (tasks.to_set - processed).each do |pending_task|
154
+ pending_task.prerequisite_tasks.each do |t|
155
+ if !processed.include?(t)
156
+ #puts "#{pending_task} => #{t}"
157
+ end
158
+ end
159
+ end
160
+ raise "cycle in task graph"
161
+ raise "cycle in task graph: #{with_cycle.map(&:name).sort.join(", ")}"
162
+ end
163
+ end
164
+
165
+ class Worker
166
+ attr_reader :job_server
167
+
168
+ def initialize(job_server, finished_workers)
169
+ @job_server = job_server
170
+ @finished_workers = finished_workers
171
+ @input = Queue.new
172
+ @thread = Thread.new do
173
+ loop do
174
+ task = @input.pop
175
+ do_task(task)
176
+ end
177
+ end
178
+ end
179
+
180
+ def do_task(task)
181
+ @last_error = nil
182
+ task_args = Rake::TaskArguments.new(task.arg_names, [])
183
+ task.instance_variable_set(:@already_invoked, true)
184
+ task.send(:execute, task_args)
185
+ @last_finished_task = task
186
+ rescue ::Exception => e
187
+ @last_finished_task = task
188
+ @last_error = e
189
+ ensure
190
+ job_server.put
191
+ @finished_workers.push(self)
192
+ end
193
+
194
+ def last_result
195
+ return @last_finished_task, @last_error
196
+ end
197
+
198
+ def queue(task)
199
+ @input.push(task)
200
+ end
201
+ end
202
+
203
+ def finish_pending_work
204
+ while available_workers.size != workers.size
205
+ w = finished_workers.pop
206
+ available_workers << w
207
+ end
208
+ end
209
+ end
210
+
211
+ class << self
212
+ attr_accessor :parallel_task_manager
213
+ end
214
+ end
215
+
216
+
@@ -16,15 +16,15 @@ require 'autobuild/exceptions'
16
16
 
17
17
  module Autobuild
18
18
  def self.message(*args)
19
- if !progress_messages.empty?
19
+ if @last_progress_msg
20
20
  puts
21
+ @last_progress_msg = nil
21
22
  end
22
23
  if args.empty?
23
24
  puts
24
25
  else
25
26
  puts "#{color(*args)}"
26
27
  end
27
- @last_msg = nil
28
28
  end
29
29
 
30
30
  class << self
@@ -85,11 +85,11 @@ module Autobuild
85
85
 
86
86
  def self.display_progress
87
87
  msg = "#{progress_messages.map(&:last).join(" | ")}"
88
- if @last_msg && @last_msg.length > msg.length
89
- print "\r" + " " * @last_msg.length
88
+ if @last_progress_msg && @last_progress_msg.length > msg.length
89
+ print "\r" + " " * @last_progress_msg.length
90
90
  end
91
91
  print "\r #{msg}"
92
- @last_msg = msg
92
+ @last_progress_msg = msg
93
93
  end
94
94
 
95
95
  # The exception type that is used to report multiple errors that occured
@@ -1,5 +1,5 @@
1
1
  module Autobuild
2
- VERSION = "1.6.0.b2" unless defined? Autobuild::VERSION
2
+ VERSION = "1.6.0.b3" unless defined? Autobuild::VERSION
3
3
  end
4
4
 
5
5
 
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autobuild
3
3
  version: !ruby/object:Gem::Version
4
- hash: 51
4
+ hash: 49
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 1
8
8
  - 6
9
9
  - 0
10
10
  - b
11
- - 2
12
- version: 1.6.0.b2
11
+ - 3
12
+ version: 1.6.0.b3
13
13
  platform: ruby
14
14
  authors:
15
15
  - Sylvain Joyeux
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2012-03-08 00:00:00 Z
20
+ date: 2012-03-09 00:00:00 Z
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
23
  name: rake
@@ -57,13 +57,14 @@ dependencies:
57
57
  requirement: &id003 !ruby/object:Gem::Requirement
58
58
  none: false
59
59
  requirements:
60
- - - ~>
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
- hash: 27
62
+ hash: 47
63
63
  segments:
64
64
  - 2
65
- - 12
66
- version: "2.12"
65
+ - 8
66
+ - 0
67
+ version: 2.8.0
67
68
  type: :development
68
69
  version_requirements: *id003
69
70
  description: |-
@@ -118,9 +119,11 @@ files:
118
119
  - lib/autobuild/packages/cmake.rb
119
120
  - lib/autobuild/packages/dummy.rb
120
121
  - lib/autobuild/packages/genom.rb
122
+ - lib/autobuild/packages/gnumake.rb
121
123
  - lib/autobuild/packages/import.rb
122
124
  - lib/autobuild/packages/orogen.rb
123
125
  - lib/autobuild/packages/pkgconfig.rb
126
+ - lib/autobuild/parallel.rb
124
127
  - lib/autobuild/pkgconfig.rb
125
128
  - lib/autobuild/reporting.rb
126
129
  - lib/autobuild/subcommand.rb
@@ -135,7 +138,6 @@ files:
135
138
  - test/test_import_tar.rb
136
139
  - test/test_subcommand.rb
137
140
  - test/tools.rb
138
- - .gemtest
139
141
  homepage:
140
142
  licenses: []
141
143
 
data/.gemtest DELETED
File without changes