rake 0.9.2.2 → 0.9.3
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/CHANGES +4 -0
- data/README.rdoc +10 -8
- data/Rakefile +6 -1
- data/TODO +1 -0
- data/bin/rake +4 -0
- data/doc/command_line_usage.rdoc +61 -12
- data/doc/release_notes/rake-0.9.2.2.rdoc +55 -0
- data/doc/release_notes/rake-0.9.3.rdoc +102 -0
- data/install.rb +1 -1
- data/lib/rake/application.rb +244 -140
- data/lib/rake/backtrace.rb +18 -0
- data/lib/rake/clean.rb +1 -1
- data/lib/rake/cloneable.rb +7 -16
- data/lib/rake/contrib/ftptools.rb +2 -1
- data/lib/rake/contrib/sys.rb +6 -6
- data/lib/rake/dsl_definition.rb +13 -7
- data/lib/rake/ext/module.rb +1 -1
- data/lib/rake/ext/string.rb +2 -1
- data/lib/rake/ext/time.rb +2 -1
- data/lib/rake/file_list.rb +2 -2
- data/lib/rake/file_utils_ext.rb +4 -3
- data/lib/rake/multi_task.rb +2 -5
- data/lib/rake/phony.rb +13 -0
- data/lib/rake/private_reader.rb +20 -0
- data/lib/rake/promise.rb +99 -0
- data/lib/rake/rake_module.rb +15 -0
- data/lib/rake/rdoctask.rb +1 -1
- data/lib/rake/runtest.rb +1 -1
- data/lib/rake/task.rb +29 -7
- data/lib/rake/task_arguments.rb +1 -1
- data/lib/rake/task_manager.rb +1 -1
- data/lib/rake/testtask.rb +5 -1
- data/lib/rake/thread_history_display.rb +48 -0
- data/lib/rake/thread_pool.rb +155 -0
- data/lib/rake/version.rb +6 -4
- data/lib/rake.rb +1 -0
- data/test/helper.rb +30 -0
- data/test/test_private_reader.rb +42 -0
- data/test/test_rake_application.rb +12 -1
- data/test/test_rake_application_options.rb +114 -4
- data/test/test_rake_backtrace.rb +81 -0
- data/test/test_rake_directory_task.rb +16 -5
- data/test/test_rake_file_task.rb +21 -1
- data/test/test_rake_functional.rb +22 -0
- data/test/test_rake_multi_task.rb +8 -0
- data/test/test_rake_reduce_compat.rb +65 -0
- data/test/test_rake_task.rb +50 -1
- data/test/test_rake_thread_pool.rb +123 -0
- data/test/test_thread_history_display.rb +91 -0
- metadata +53 -37
data/lib/rake/ext/module.rb
CHANGED
data/lib/rake/ext/string.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'rake/ext/core'
|
|
|
4
4
|
# Rake extension methods for String.
|
|
5
5
|
#
|
|
6
6
|
class String
|
|
7
|
+
|
|
7
8
|
rake_extension("ext") do
|
|
8
9
|
# Replace the file extension with +newext+. If there is no extension on
|
|
9
10
|
# the string, append the new extension to the end. If the new extension
|
|
@@ -163,5 +164,5 @@ class String
|
|
|
163
164
|
result
|
|
164
165
|
end
|
|
165
166
|
end
|
|
166
|
-
end # class String
|
|
167
167
|
|
|
168
|
+
end
|
data/lib/rake/ext/time.rb
CHANGED
data/lib/rake/file_list.rb
CHANGED
|
@@ -286,7 +286,7 @@ module Rake
|
|
|
286
286
|
matched = 0
|
|
287
287
|
each do |fn|
|
|
288
288
|
begin
|
|
289
|
-
open(fn, "
|
|
289
|
+
open(fn, "r", *options) do |inf|
|
|
290
290
|
count = 0
|
|
291
291
|
inf.each do |line|
|
|
292
292
|
count += 1
|
|
@@ -340,7 +340,7 @@ module Rake
|
|
|
340
340
|
|
|
341
341
|
# Add matching glob patterns.
|
|
342
342
|
def add_matching(pattern)
|
|
343
|
-
|
|
343
|
+
Rake.glob(pattern).each do |fn|
|
|
344
344
|
self << fn unless exclude?(fn)
|
|
345
345
|
end
|
|
346
346
|
end
|
data/lib/rake/file_utils_ext.rb
CHANGED
|
@@ -21,12 +21,13 @@ module Rake
|
|
|
21
21
|
$fileutils_verbose = true
|
|
22
22
|
$fileutils_nowrite = false
|
|
23
23
|
|
|
24
|
-
FileUtils
|
|
24
|
+
FileUtils.commands.each do |name|
|
|
25
|
+
opts = FileUtils.options_of name
|
|
25
26
|
default_options = []
|
|
26
|
-
if opts.include?(
|
|
27
|
+
if opts.include?("verbose")
|
|
27
28
|
default_options << ':verbose => FileUtilsExt.verbose_flag'
|
|
28
29
|
end
|
|
29
|
-
if opts.include?(
|
|
30
|
+
if opts.include?("noop")
|
|
30
31
|
default_options << ':noop => FileUtilsExt.nowrite_flag'
|
|
31
32
|
end
|
|
32
33
|
|
data/lib/rake/multi_task.rb
CHANGED
|
@@ -5,11 +5,8 @@ module Rake
|
|
|
5
5
|
#
|
|
6
6
|
class MultiTask < Task
|
|
7
7
|
private
|
|
8
|
-
def invoke_prerequisites(
|
|
9
|
-
|
|
10
|
-
Thread.new(p) { |r| application[r, @scope].invoke_with_call_chain(args, invocation_chain) }
|
|
11
|
-
}
|
|
12
|
-
threads.each { |t| t.join }
|
|
8
|
+
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
|
|
9
|
+
invoke_prerequisites_concurrently(task_args, invocation_chain)
|
|
13
10
|
end
|
|
14
11
|
end
|
|
15
12
|
|
data/lib/rake/phony.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Defines a :phony task that you can use as a dependency. This allows
|
|
2
|
+
# file-based tasks to use non-file-based tasks as prerequisites
|
|
3
|
+
# without forcing them to rebuild.
|
|
4
|
+
#
|
|
5
|
+
# See FileTask#out_of_date? and Task#timestamp for more info.
|
|
6
|
+
|
|
7
|
+
require 'rake'
|
|
8
|
+
|
|
9
|
+
task :phony
|
|
10
|
+
|
|
11
|
+
def (Rake::Task[:phony]).timestamp
|
|
12
|
+
Time.at 0
|
|
13
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Rake
|
|
2
|
+
|
|
3
|
+
# Include PrivateReader to use +private_reader+.
|
|
4
|
+
module PrivateReader # :nodoc: all
|
|
5
|
+
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
|
|
12
|
+
# Declare a list of private accessors
|
|
13
|
+
def private_reader(*names)
|
|
14
|
+
attr_reader(*names)
|
|
15
|
+
private(*names)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/rake/promise.rb
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Rake
|
|
2
|
+
|
|
3
|
+
# A Promise object represents a promise to do work (a chore) in the
|
|
4
|
+
# future. The promise is created with a block and a list of
|
|
5
|
+
# arguments for the block. Calling value will return the value of
|
|
6
|
+
# the promised chore.
|
|
7
|
+
#
|
|
8
|
+
# Used by ThreadPool.
|
|
9
|
+
#
|
|
10
|
+
class Promise # :nodoc: all
|
|
11
|
+
NOT_SET = Object.new.freeze # :nodoc:
|
|
12
|
+
|
|
13
|
+
attr_accessor :recorder
|
|
14
|
+
|
|
15
|
+
# Create a promise to do the chore specified by the block.
|
|
16
|
+
def initialize(args, &block)
|
|
17
|
+
@mutex = Mutex.new
|
|
18
|
+
@result = NOT_SET
|
|
19
|
+
@error = NOT_SET
|
|
20
|
+
@args = args.collect { |a| begin; a.dup; rescue; a; end }
|
|
21
|
+
@block = block
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Return the value of this promise.
|
|
25
|
+
#
|
|
26
|
+
# If the promised chore is not yet complete, then do the work
|
|
27
|
+
# synchronously. We will wait.
|
|
28
|
+
def value
|
|
29
|
+
unless complete?
|
|
30
|
+
stat :sleeping_on, :item_id => object_id
|
|
31
|
+
@mutex.synchronize do
|
|
32
|
+
stat :has_lock_on, :item_id => object_id
|
|
33
|
+
chore
|
|
34
|
+
stat :releasing_lock_on, :item_id => object_id
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
error? ? raise(@error) : @result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# If no one else is working this promise, go ahead and do the chore.
|
|
41
|
+
def work
|
|
42
|
+
stat :attempting_lock_on, :item_id => object_id
|
|
43
|
+
if @mutex.try_lock
|
|
44
|
+
stat :has_lock_on, :item_id => object_id
|
|
45
|
+
chore
|
|
46
|
+
stat :releasing_lock_on, :item_id => object_id
|
|
47
|
+
@mutex.unlock
|
|
48
|
+
else
|
|
49
|
+
stat :bailed_on, :item_id => object_id
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Perform the chore promised
|
|
56
|
+
def chore
|
|
57
|
+
if complete?
|
|
58
|
+
stat :found_completed, :item_id => object_id
|
|
59
|
+
return
|
|
60
|
+
end
|
|
61
|
+
stat :will_execute, :item_id => object_id
|
|
62
|
+
begin
|
|
63
|
+
@result = @block.call(*@args)
|
|
64
|
+
rescue Exception => e
|
|
65
|
+
@error = e
|
|
66
|
+
end
|
|
67
|
+
stat :did_execute, :item_id => object_id
|
|
68
|
+
discard
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Do we have a result for the promise
|
|
72
|
+
def result?
|
|
73
|
+
! @result.equal?(NOT_SET)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Did the promise throw an error
|
|
77
|
+
def error?
|
|
78
|
+
! @error.equal?(NOT_SET)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Are we done with the promise
|
|
82
|
+
def complete?
|
|
83
|
+
result? || error?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# free up these items for the GC
|
|
87
|
+
def discard
|
|
88
|
+
@args = nil
|
|
89
|
+
@block = nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Record execution statistics if there is a recorder
|
|
93
|
+
def stat(*args)
|
|
94
|
+
@recorder.call(*args) if @recorder
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
data/lib/rake/rake_module.rb
CHANGED
|
@@ -24,6 +24,21 @@ module Rake
|
|
|
24
24
|
def load_rakefile(path)
|
|
25
25
|
load(path)
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
# Add files to the rakelib list
|
|
29
|
+
def add_rakelib(*files)
|
|
30
|
+
application.options.rakelib ||= []
|
|
31
|
+
files.each do |file|
|
|
32
|
+
application.options.rakelib << file
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get a sorted list of files matching the pattern. This method
|
|
37
|
+
# should be prefered to Dir[pattern] and Dir.glob[pattern] because
|
|
38
|
+
# the files returned are guaranteed to be sorted.
|
|
39
|
+
def glob(pattern, *args)
|
|
40
|
+
Dir.glob(pattern, *args).sort
|
|
41
|
+
end
|
|
27
42
|
end
|
|
28
43
|
|
|
29
44
|
end
|
data/lib/rake/rdoctask.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# rake/rdoctask is deprecated in favor of rdoc/task
|
|
2
2
|
|
|
3
3
|
if Rake.application
|
|
4
|
-
Rake.application.deprecate('require \'rake/rdoctask\'', 'require \'rdoc/task\' (in RDoc 2.4.2+)',
|
|
4
|
+
Rake.application.deprecate('require \'rake/rdoctask\'', 'require \'rdoc/task\' (in RDoc 2.4.2+)', caller.first)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
require 'rubygems'
|
data/lib/rake/runtest.rb
CHANGED
data/lib/rake/task.rb
CHANGED
|
@@ -123,6 +123,7 @@ module Rake
|
|
|
123
123
|
def clear
|
|
124
124
|
clear_prerequisites
|
|
125
125
|
clear_actions
|
|
126
|
+
clear_comments
|
|
126
127
|
self
|
|
127
128
|
end
|
|
128
129
|
|
|
@@ -138,6 +139,13 @@ module Rake
|
|
|
138
139
|
self
|
|
139
140
|
end
|
|
140
141
|
|
|
142
|
+
# Clear the existing comments on a rake task.
|
|
143
|
+
def clear_comments
|
|
144
|
+
@full_comment = nil
|
|
145
|
+
@comment = nil
|
|
146
|
+
self
|
|
147
|
+
end
|
|
148
|
+
|
|
141
149
|
# Invoke the task if it is needed. Prerequisites are invoked first.
|
|
142
150
|
def invoke(*args)
|
|
143
151
|
task_args = TaskArguments.new(arg_names, args)
|
|
@@ -150,7 +158,7 @@ module Rake
|
|
|
150
158
|
new_chain = InvocationChain.append(self, invocation_chain)
|
|
151
159
|
@lock.synchronize do
|
|
152
160
|
if application.options.trace
|
|
153
|
-
|
|
161
|
+
application.trace "** Invoke #{name} #{format_trace_flags}"
|
|
154
162
|
end
|
|
155
163
|
return if @already_invoked
|
|
156
164
|
@already_invoked = true
|
|
@@ -171,10 +179,24 @@ module Rake
|
|
|
171
179
|
|
|
172
180
|
# Invoke all the prerequisites of a task.
|
|
173
181
|
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
if application.options.always_multitask
|
|
183
|
+
invoke_prerequisites_concurrently(task_args, invocation_chain)
|
|
184
|
+
else
|
|
185
|
+
prerequisite_tasks.each { |prereq|
|
|
186
|
+
prereq_args = task_args.new_scope(prereq.arg_names)
|
|
187
|
+
prereq.invoke_with_call_chain(prereq_args, invocation_chain)
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Invoke all the prerequisites of a task in parallel.
|
|
193
|
+
def invoke_prerequisites_concurrently(args, invocation_chain) # :nodoc:
|
|
194
|
+
futures = @prerequisites.collect do |p|
|
|
195
|
+
application.thread_pool.future(p) do |r|
|
|
196
|
+
application[r, @scope].invoke_with_call_chain(args, invocation_chain)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
futures.each { |f| f.value }
|
|
178
200
|
end
|
|
179
201
|
|
|
180
202
|
# Format the trace flags for display.
|
|
@@ -190,11 +212,11 @@ module Rake
|
|
|
190
212
|
def execute(args=nil)
|
|
191
213
|
args ||= EMPTY_TASK_ARGS
|
|
192
214
|
if application.options.dryrun
|
|
193
|
-
|
|
215
|
+
application.trace "** Execute (dry run) #{name}"
|
|
194
216
|
return
|
|
195
217
|
end
|
|
196
218
|
if application.options.trace
|
|
197
|
-
|
|
219
|
+
application.trace "** Execute #{name}"
|
|
198
220
|
end
|
|
199
221
|
application.enhance_with_matching_rule(name) if @actions.empty?
|
|
200
222
|
@actions.each do |act|
|
data/lib/rake/task_arguments.rb
CHANGED
data/lib/rake/task_manager.rb
CHANGED
|
@@ -238,7 +238,7 @@ module Rake
|
|
|
238
238
|
end
|
|
239
239
|
|
|
240
240
|
def trace_rule(level, message)
|
|
241
|
-
|
|
241
|
+
options.trace_output.puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
|
|
242
242
|
end
|
|
243
243
|
|
|
244
244
|
# Attempt to create a rule given the list of prerequisites.
|
data/lib/rake/testtask.rb
CHANGED
|
@@ -96,7 +96,11 @@ module Rake
|
|
|
96
96
|
desc "Run tests" + (@name==:test ? "" : " for #{@name}")
|
|
97
97
|
task @name do
|
|
98
98
|
FileUtilsExt.verbose(@verbose) do
|
|
99
|
-
ruby "#{ruby_opts_string} #{run_code} #{file_list_string} #{option_list}"
|
|
99
|
+
ruby "#{ruby_opts_string} #{run_code} #{file_list_string} #{option_list}" do |ok, status|
|
|
100
|
+
if !ok && status.respond_to?(:signaled?) && status.signaled?
|
|
101
|
+
raise SignalException.new(status.termsig)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
self
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'rake/private_reader'
|
|
2
|
+
|
|
3
|
+
module Rake
|
|
4
|
+
|
|
5
|
+
class ThreadHistoryDisplay # :nodoc: all
|
|
6
|
+
include Rake::PrivateReader
|
|
7
|
+
|
|
8
|
+
private_reader :stats, :items, :threads
|
|
9
|
+
|
|
10
|
+
def initialize(stats)
|
|
11
|
+
@stats = stats
|
|
12
|
+
@items = { :_seq_ => 1 }
|
|
13
|
+
@threads = { :_seq_ => "A" }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def show
|
|
17
|
+
puts "Job History:"
|
|
18
|
+
stats.each do |stat|
|
|
19
|
+
stat[:data] ||= {}
|
|
20
|
+
rename(stat, :thread, threads)
|
|
21
|
+
rename(stat[:data], :item_id, items)
|
|
22
|
+
rename(stat[:data], :new_thread, threads)
|
|
23
|
+
rename(stat[:data], :deleted_thread, threads)
|
|
24
|
+
printf("%8d %2s %-20s %s\n",
|
|
25
|
+
(stat[:time] * 1_000_000).round,
|
|
26
|
+
stat[:thread],
|
|
27
|
+
stat[:event],
|
|
28
|
+
stat[:data].map { |k,v| "#{k}:#{v}" }.join(" "))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def rename(hash, key, renames)
|
|
35
|
+
if hash && hash[key]
|
|
36
|
+
original = hash[key]
|
|
37
|
+
value = renames[original]
|
|
38
|
+
unless value
|
|
39
|
+
value = renames[:_seq_]
|
|
40
|
+
renames[:_seq_] = renames[:_seq_].succ
|
|
41
|
+
renames[original] = value
|
|
42
|
+
end
|
|
43
|
+
hash[key] = value
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
require 'rake/promise'
|
|
5
|
+
|
|
6
|
+
module Rake
|
|
7
|
+
|
|
8
|
+
class ThreadPool # :nodoc: all
|
|
9
|
+
|
|
10
|
+
# Creates a ThreadPool object.
|
|
11
|
+
# The parameter is the size of the pool.
|
|
12
|
+
def initialize(thread_count)
|
|
13
|
+
@max_active_threads = [thread_count, 0].max
|
|
14
|
+
@threads = Set.new
|
|
15
|
+
@threads_mon = Monitor.new
|
|
16
|
+
@queue = Queue.new
|
|
17
|
+
@join_cond = @threads_mon.new_cond
|
|
18
|
+
|
|
19
|
+
@history_start_time = nil
|
|
20
|
+
@history = []
|
|
21
|
+
@history_mon = Monitor.new
|
|
22
|
+
@total_threads_in_play = 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Creates a future executed by the +ThreadPool+.
|
|
26
|
+
#
|
|
27
|
+
# The args are passed to the block when executing (similarly to
|
|
28
|
+
# <tt>Thread#new</tt>) The return value is an object representing
|
|
29
|
+
# a future which has been created and added to the queue in the
|
|
30
|
+
# pool. Sending <tt>#value</tt> to the object will sleep the
|
|
31
|
+
# current thread until the future is finished and will return the
|
|
32
|
+
# result (or raise an exception thrown from the future)
|
|
33
|
+
def future(*args, &block)
|
|
34
|
+
promise = Promise.new(args, &block)
|
|
35
|
+
promise.recorder = lambda { |*stats| stat(*stats) }
|
|
36
|
+
|
|
37
|
+
@queue.enq promise
|
|
38
|
+
stat :queued, :item_id => promise.object_id
|
|
39
|
+
start_thread
|
|
40
|
+
promise
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Waits until the queue of futures is empty and all threads have exited.
|
|
44
|
+
def join
|
|
45
|
+
@threads_mon.synchronize do
|
|
46
|
+
begin
|
|
47
|
+
stat :joining
|
|
48
|
+
@join_cond.wait unless @threads.empty?
|
|
49
|
+
stat :joined
|
|
50
|
+
rescue Exception => e
|
|
51
|
+
stat :joined
|
|
52
|
+
$stderr.puts e
|
|
53
|
+
$stderr.print "Queue contains #{@queue.size} items. Thread pool contains #{@threads.count} threads\n"
|
|
54
|
+
$stderr.print "Current Thread #{Thread.current} status = #{Thread.current.status}\n"
|
|
55
|
+
$stderr.puts e.backtrace.join("\n")
|
|
56
|
+
@threads.each do |t|
|
|
57
|
+
$stderr.print "Thread #{t} status = #{t.status}\n"
|
|
58
|
+
# 1.8 doesn't support Thread#backtrace
|
|
59
|
+
$stderr.puts t.backtrace.join("\n") if t.respond_to? :backtrace
|
|
60
|
+
end
|
|
61
|
+
raise e
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Enable the gathering of history events.
|
|
67
|
+
def gather_history #:nodoc:
|
|
68
|
+
@history_start_time = Time.now if @history_start_time.nil?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Return a array of history events for the thread pool.
|
|
72
|
+
#
|
|
73
|
+
# History gathering must be enabled to be able to see the events
|
|
74
|
+
# (see #gather_history). Best to call this when the job is
|
|
75
|
+
# complete (i.e. after ThreadPool#join is called).
|
|
76
|
+
def history # :nodoc:
|
|
77
|
+
@history_mon.synchronize { @history.dup }.
|
|
78
|
+
sort_by { |i| i[:time] }.
|
|
79
|
+
each { |i| i[:time] -= @history_start_time }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Return a hash of always collected statistics for the thread pool.
|
|
83
|
+
def statistics # :nodoc:
|
|
84
|
+
{
|
|
85
|
+
:total_threads_in_play => @total_threads_in_play,
|
|
86
|
+
:max_active_threads => @max_active_threads,
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# processes one item on the queue. Returns true if there was an
|
|
93
|
+
# item to process, false if there was no item
|
|
94
|
+
def process_queue_item #:nodoc:
|
|
95
|
+
return false if @queue.empty?
|
|
96
|
+
|
|
97
|
+
# Even though we just asked if the queue was empty, it
|
|
98
|
+
# still could have had an item which by this statement
|
|
99
|
+
# is now gone. For this reason we pass true to Queue#deq
|
|
100
|
+
# because we will sleep indefinitely if it is empty.
|
|
101
|
+
promise = @queue.deq(true)
|
|
102
|
+
stat :dequeued, :item_id => promise.object_id
|
|
103
|
+
promise.work
|
|
104
|
+
return true
|
|
105
|
+
|
|
106
|
+
rescue ThreadError # this means the queue is empty
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def start_thread # :nodoc:
|
|
111
|
+
@threads_mon.synchronize do
|
|
112
|
+
next unless @threads.count < @max_active_threads
|
|
113
|
+
|
|
114
|
+
t = Thread.new do
|
|
115
|
+
begin
|
|
116
|
+
while @threads.count <= @max_active_threads
|
|
117
|
+
break unless process_queue_item
|
|
118
|
+
end
|
|
119
|
+
ensure
|
|
120
|
+
@threads_mon.synchronize do
|
|
121
|
+
@threads.delete Thread.current
|
|
122
|
+
stat :ended, :thread_count => @threads.count
|
|
123
|
+
@join_cond.broadcast if @threads.empty?
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
@threads << t
|
|
128
|
+
stat :spawned, :new_thread => t.object_id, :thread_count => @threads.count
|
|
129
|
+
@total_threads_in_play = @threads.count if @threads.count > @total_threads_in_play
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def stat(event, data=nil) # :nodoc:
|
|
134
|
+
return if @history_start_time.nil?
|
|
135
|
+
info = {
|
|
136
|
+
:event => event,
|
|
137
|
+
:data => data,
|
|
138
|
+
:time => Time.now,
|
|
139
|
+
:thread => Thread.current.object_id,
|
|
140
|
+
}
|
|
141
|
+
@history_mon.synchronize { @history << info }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# for testing only
|
|
145
|
+
|
|
146
|
+
def __queue__ # :nodoc:
|
|
147
|
+
@queue
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def __threads__ # :nodoc:
|
|
151
|
+
@threads.dup
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
data/lib/rake/version.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
module Rake
|
|
2
|
-
VERSION = '0.9.2.2'
|
|
3
|
-
|
|
4
2
|
module Version # :nodoc: all
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
NUMBERS = [
|
|
4
|
+
MAJOR = 0,
|
|
5
|
+
MINOR = 9,
|
|
6
|
+
BUILD = 3,
|
|
7
|
+
]
|
|
7
8
|
end
|
|
9
|
+
VERSION = Version::NUMBERS.join('.')
|
|
8
10
|
end
|
data/lib/rake.rb
CHANGED
data/test/helper.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
|
2
3
|
|
|
3
4
|
begin
|
|
4
5
|
gem 'minitest'
|
|
@@ -488,5 +489,34 @@ end
|
|
|
488
489
|
VERBOSE
|
|
489
490
|
end
|
|
490
491
|
|
|
492
|
+
def rakefile_test_signal
|
|
493
|
+
rakefile <<-TEST_SIGNAL
|
|
494
|
+
require 'rake/testtask'
|
|
495
|
+
|
|
496
|
+
Rake::TestTask.new(:a) do |t|
|
|
497
|
+
t.test_files = ['a_test.rb']
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
Rake::TestTask.new(:b) do |t|
|
|
501
|
+
t.test_files = ['b_test.rb']
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
task :test do
|
|
505
|
+
Rake::Task[:a].invoke rescue nil
|
|
506
|
+
Rake::Task[:b].invoke rescue nil
|
|
491
507
|
end
|
|
492
508
|
|
|
509
|
+
task :default => :test
|
|
510
|
+
TEST_SIGNAL
|
|
511
|
+
open 'a_test.rb', 'w' do |io|
|
|
512
|
+
io << 'puts "ATEST"' << "\n"
|
|
513
|
+
io << '$stdout.flush' << "\n"
|
|
514
|
+
io << 'Process.kill("TERM", $$)' << "\n"
|
|
515
|
+
end
|
|
516
|
+
open 'b_test.rb', 'w' do |io|
|
|
517
|
+
io << 'puts "BTEST"' << "\n"
|
|
518
|
+
io << '$stdout.flush' << "\n"
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
|
2
|
+
require 'rake/private_reader'
|
|
3
|
+
|
|
4
|
+
class TestPrivateAttrs < Rake::TestCase
|
|
5
|
+
|
|
6
|
+
class Sample
|
|
7
|
+
include Rake::PrivateReader
|
|
8
|
+
|
|
9
|
+
private_reader :reader, :a
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@reader = :RVALUE
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_reader
|
|
16
|
+
reader
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def setup
|
|
22
|
+
super
|
|
23
|
+
@sample = Sample.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_private_reader_is_private
|
|
27
|
+
assert_private do @sample.reader end
|
|
28
|
+
assert_private do @sample.a end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_private_reader_returns_data
|
|
32
|
+
assert_equal :RVALUE, @sample.get_reader
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def assert_private
|
|
38
|
+
ex = assert_raises(NoMethodError) do yield end
|
|
39
|
+
assert_match(/private/, ex.message)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|