autobuild 1.6.0.b2 → 1.6.0.b3
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.
- data/Manifest.txt +2 -0
- data/lib/autobuild/packages/gnumake.rb +48 -0
- data/lib/autobuild/parallel.rb +216 -0
- data/lib/autobuild/reporting.rb +5 -5
- data/lib/autobuild/version.rb +1 -1
- metadata +11 -9
- data/.gemtest +0 -0
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
|
+
|
data/lib/autobuild/reporting.rb
CHANGED
@@ -16,15 +16,15 @@ require 'autobuild/exceptions'
|
|
16
16
|
|
17
17
|
module Autobuild
|
18
18
|
def self.message(*args)
|
19
|
-
if
|
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 @
|
89
|
-
print "\r" + " " * @
|
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
|
-
@
|
92
|
+
@last_progress_msg = msg
|
93
93
|
end
|
94
94
|
|
95
95
|
# The exception type that is used to report multiple errors that occured
|
data/lib/autobuild/version.rb
CHANGED
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:
|
4
|
+
hash: 49
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 6
|
9
9
|
- 0
|
10
10
|
- b
|
11
|
-
-
|
12
|
-
version: 1.6.0.
|
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-
|
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:
|
62
|
+
hash: 47
|
63
63
|
segments:
|
64
64
|
- 2
|
65
|
-
-
|
66
|
-
|
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
|