rake 0.9.3.beta.3 → 0.9.3.beta.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rake might be problematic. Click here for more details.
- data/Rakefile +1 -0
- data/doc/command_line_usage.rdoc +51 -14
- data/doc/release_notes/rake-0.9.2.2.rdoc +55 -0
- data/doc/release_notes/rake-0.9.3.rdoc +70 -23
- data/lib/rake/application.rb +24 -15
- data/lib/rake/backtrace.rb +2 -1
- data/lib/rake/ext/string.rb +2 -1
- data/lib/rake/ext/time.rb +0 -1
- data/lib/rake/private_reader.rb +20 -0
- data/lib/rake/promise.rb +99 -0
- data/lib/rake/task.rb +1 -1
- data/lib/rake/thread_history_display.rb +7 -4
- data/lib/rake/thread_pool.rb +42 -76
- data/lib/rake/version.rb +1 -1
- data/test/test_private_reader.rb +42 -0
- data/test/test_rake_application.rb +0 -1
- data/test/test_rake_backtrace.rb +22 -8
- data/test/test_rake_thread_pool.rb +1 -24
- data/test/test_thread_history_display.rb +14 -14
- metadata +12 -6
data/Rakefile
CHANGED
data/doc/command_line_usage.rdoc
CHANGED
@@ -11,6 +11,16 @@ Options are:
|
|
11
11
|
during the execution of the <b>rake</b> command. You can access
|
12
12
|
the value by using ENV['<em>name</em>'].
|
13
13
|
|
14
|
+
[<tt>--all</tt> (-A)]
|
15
|
+
Used in combination with the -T and -D options, will force
|
16
|
+
those options to show all the tasks, even the ones without comments.
|
17
|
+
|
18
|
+
[<tt>--backtrace</tt> _output_ (-n)]
|
19
|
+
Enable a full backtrace (i.e. like --trace, but without the task
|
20
|
+
tracing details). The backtrace output will be directed to stderr
|
21
|
+
by default. If the options _output_ parameter is set to "stdout",
|
22
|
+
then the output will be directed to standard output.
|
23
|
+
|
14
24
|
[<tt>--classic-namespace</tt> (-n)]
|
15
25
|
Import the Task, FileTask, and FileCreateTask into the top-level
|
16
26
|
scope to be compatible with older versions of Rake. Alternatively
|
@@ -18,6 +28,11 @@ Options are:
|
|
18
28
|
'rake/classic_namespace'</code> in your Rakefile to get the
|
19
29
|
classic behavior.
|
20
30
|
|
31
|
+
[<tt>--comments</tt>]
|
32
|
+
Used in combination with the -W options to force the output to
|
33
|
+
contain commented options only. This is the reverse of
|
34
|
+
<tt>--all</tt>.
|
35
|
+
|
21
36
|
[<tt>--describe</tt> _pattern_ (-D)]
|
22
37
|
Describe the tasks (matching optional PATTERN), then exit.
|
23
38
|
|
@@ -38,14 +53,27 @@ Options are:
|
|
38
53
|
Display some help text and exit.
|
39
54
|
|
40
55
|
[<tt>--jobs</tt> _number_ (-j)]
|
41
|
-
Specifies the
|
42
|
-
value is equal to the number of CPUs.
|
43
|
-
|
56
|
+
Specifies the number of active concurrent tasks used. The
|
57
|
+
suggested value is equal to the number of CPUs. The concurrent
|
58
|
+
tasks are used to execute the <tt>multitask</tt> prerequisites.
|
59
|
+
Also see the <tt>-m</tt> option which turns all tasks into
|
60
|
+
multitasks.
|
61
|
+
|
44
62
|
Sample values:
|
45
63
|
(no -j) : unlimited concurrent tasks (standard rake behavior)
|
46
64
|
-j : 2 concurrent tasks (exact number may change)
|
47
65
|
-j 16 : 16 concurrent tasks
|
48
66
|
|
67
|
+
[<tt>--job-stats</tt> _level_]
|
68
|
+
|
69
|
+
Display job statistics at the completion of the run. By default,
|
70
|
+
this will display the requested number of active tasks (from the
|
71
|
+
-j options) and the maximum number of tasks in play at any given
|
72
|
+
time.
|
73
|
+
|
74
|
+
If the optional _level_ is <tt>history</tt>, then a complete trace
|
75
|
+
of task history will be displayed on standard output.
|
76
|
+
|
49
77
|
[<tt>--libdir</tt> _directory_ (-I)]
|
50
78
|
Add _directory_ to the list of directories searched for require.
|
51
79
|
|
@@ -72,6 +100,16 @@ Options are:
|
|
72
100
|
[<tt>--rakelibdir</tt> _rakelibdir_ (-R)]
|
73
101
|
Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')
|
74
102
|
|
103
|
+
[<tt>--reduce-compat</tt>]
|
104
|
+
|
105
|
+
Remove the DSL commands from the Object inheritance hierarchy and
|
106
|
+
do not define top level constants. This reduces the backwards
|
107
|
+
compatibility of Rake, but allows rake to be used with software
|
108
|
+
that would otherwise have conflicting definitions.
|
109
|
+
|
110
|
+
*NOTE:* The next major version of Rake will only be able to be run
|
111
|
+
in "reduce-compat" mode.
|
112
|
+
|
75
113
|
[<tt>--require</tt> _name_ (-r)]
|
76
114
|
Require _name_ before executing the Rakefile.
|
77
115
|
|
@@ -81,6 +119,11 @@ Options are:
|
|
81
119
|
[<tt>--silent (-s)</tt>]
|
82
120
|
Like --quiet, but also suppresses the 'in directory' announcement.
|
83
121
|
|
122
|
+
[<tt>--suppress-backtrace _pattern_ </tt>]
|
123
|
+
Line matching the regular expression _pattern_ will be removed
|
124
|
+
from the backtrace output. Note that the --backtrace option is the
|
125
|
+
full backtrace without these lines suppressed.
|
126
|
+
|
84
127
|
[<tt>--system</tt> (-g)]
|
85
128
|
Use the system wide (global) rakefiles. The project Rakefile is
|
86
129
|
ignored. By default, the system wide rakefiles are used only if no
|
@@ -97,16 +140,6 @@ Options are:
|
|
97
140
|
are defined using the "desc" command. If a pattern is given, then
|
98
141
|
only tasks matching the pattern are displayed.
|
99
142
|
|
100
|
-
[<tt>--no-top-level-dsl</tt> (-X)]
|
101
|
-
Do not put the Rake DSL commands into the top level scope.
|
102
|
-
|
103
|
-
[<tt>--top-level-dsl</tt>]
|
104
|
-
Put the Rake DSL commands into the top level scope (default).
|
105
|
-
|
106
|
-
NOTE: Although currently Rake defaults to its DSL in the top level
|
107
|
-
scope, the plan is to deprecate this in the future and default ot
|
108
|
-
having the DSL commands *not* appear in the top level scope.
|
109
|
-
|
110
143
|
[<tt>--trace</tt> (-t)]
|
111
144
|
Turn on invoke/execute tracing. Also enable full backtrace on
|
112
145
|
errors.
|
@@ -119,7 +152,11 @@ Options are:
|
|
119
152
|
|
120
153
|
[<tt>--where</tt> <em>pattern</em> (-W)]
|
121
154
|
Display tasks that match <em>pattern</em> and the file and line
|
122
|
-
number where the task is defined.
|
155
|
+
number where the task is defined. By default this option will
|
156
|
+
display all tasks, not just the tasks that have descriptions.
|
157
|
+
|
158
|
+
[<tt>--no-deprecation-warnings</tt> (-W)]
|
159
|
+
Do not display the deprecation warnings.
|
123
160
|
|
124
161
|
In addition, any command line option of the form
|
125
162
|
<em>VAR</em>=<em>VALUE</em> will be added to the environment hash
|
@@ -0,0 +1,55 @@
|
|
1
|
+
= Rake 0.9.3 Released
|
2
|
+
|
3
|
+
Rake version 0.9.3 is mainly bug fixes.
|
4
|
+
|
5
|
+
== Changes
|
6
|
+
|
7
|
+
* The rake test loader now removes arguments it has processed. Issue #51
|
8
|
+
* Rake::TaskArguments now responds to #values_at
|
9
|
+
* RakeFileUtils.verbose_flag = nil silences output the same as 0.8.7
|
10
|
+
* Rake tests are now directory-independent
|
11
|
+
* Rake tests are no longer require flexmock
|
12
|
+
* Commands constant is no longer polluting top level namespace.
|
13
|
+
* Show only the interesting portion of the backtrace by default (James M. Lawrence).
|
14
|
+
* Added --reduce-compat option to remove backward compatible DSL hacks (James M. Lawrence).
|
15
|
+
|
16
|
+
== What is Rake
|
17
|
+
|
18
|
+
Rake is a build tool similar to the make program in many ways. But
|
19
|
+
instead of cryptic make recipes, Rake uses standard Ruby code to
|
20
|
+
declare tasks and dependencies. You have the full power of a modern
|
21
|
+
scripting language built right into your build tool.
|
22
|
+
|
23
|
+
== Availability
|
24
|
+
|
25
|
+
The easiest way to get and install rake is via RubyGems ...
|
26
|
+
|
27
|
+
gem install rake (you may need root/admin privileges)
|
28
|
+
|
29
|
+
Otherwise, you can get it from the more traditional places:
|
30
|
+
|
31
|
+
Home Page:: http://rake.rubyforge.org/
|
32
|
+
Download:: http://rubyforge.org/project/showfiles.php?group_id=50
|
33
|
+
GitHub:: git://github.com/jimweirich/rake.git
|
34
|
+
|
35
|
+
== Thanks
|
36
|
+
|
37
|
+
As usual, it was input from users that drove a alot of these changes. The
|
38
|
+
following people either contributed patches, made suggestions or made
|
39
|
+
otherwise helpful comments. Thanks to ...
|
40
|
+
|
41
|
+
* James M. Lawrence (quix)
|
42
|
+
* Roger Pack
|
43
|
+
* Cezary Baginski
|
44
|
+
* Sean Scot August Moon
|
45
|
+
* R.T. Lechow
|
46
|
+
* Alex Chaffee
|
47
|
+
* James Tucker
|
48
|
+
* Matthias Lüdtke
|
49
|
+
* Santiago Pastorino
|
50
|
+
|
51
|
+
Also, bit thanks to Eric Hodel for assisting with getting this release
|
52
|
+
out the door (where "assisting" includes, but is not by any means
|
53
|
+
limited to, "pushing" me to get it done).
|
54
|
+
|
55
|
+
-- Jim Weirich
|
@@ -1,17 +1,59 @@
|
|
1
1
|
= Rake 0.9.3 Released
|
2
2
|
|
3
|
-
Rake version 0.9.3
|
3
|
+
Rake version 0.9.3 contains some new, backwards compatible features and
|
4
|
+
a number of bug fixes.
|
4
5
|
|
5
6
|
== Changes
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
*
|
10
|
-
|
11
|
-
|
12
|
-
*
|
13
|
-
|
14
|
-
*
|
8
|
+
=== New Features
|
9
|
+
|
10
|
+
* Multitask tasks now use a thread pool. Use -j to limit the number of
|
11
|
+
available threads.
|
12
|
+
|
13
|
+
* Use -m to turn regular tasks into multitasks (use at your own risk).
|
14
|
+
|
15
|
+
* You can now do "Rake.add_rakelib 'dir'" in your Rakefile to
|
16
|
+
programatically add rake task libraries.
|
17
|
+
|
18
|
+
* You can specific backtrace suppression patterns (see
|
19
|
+
--supress-backtrace)
|
20
|
+
|
21
|
+
* Directory tasks can now take prerequisites and actions
|
22
|
+
|
23
|
+
* Use --backtrace to request a full backtrace without the task trace.
|
24
|
+
|
25
|
+
* You can say "--backtrace=stdout" and "--trace=stdout" to route trace
|
26
|
+
output to standard output rather than standard error.
|
27
|
+
|
28
|
+
* Optional 'phony' target (enable with 'require 'rake/phony'") for
|
29
|
+
special purpose builds.
|
30
|
+
|
31
|
+
* Task#clear now clears task comments as well as actions and
|
32
|
+
prerequisites. Task#clear_comment will specifically target comments.
|
33
|
+
|
34
|
+
* The --all option will force -T and -D to consider all the tasks,
|
35
|
+
with and without descriptions.
|
36
|
+
|
37
|
+
=== Bug Fixes
|
38
|
+
|
39
|
+
* Semi-colons in windows rakefile paths now work.
|
40
|
+
|
41
|
+
* Improved Control-C support when invoking multiple test suites.
|
42
|
+
|
43
|
+
* egrep method now reads files in text mode (better support for
|
44
|
+
Windows)
|
45
|
+
|
46
|
+
* Better deprecation line number reporting.
|
47
|
+
|
48
|
+
* The -W option now works with all tasks, whether they have a
|
49
|
+
description or not.
|
50
|
+
|
51
|
+
* File globs in rake should not be sorted alphabetically, independent
|
52
|
+
of file system and platform.
|
53
|
+
|
54
|
+
* Numerous internal improvements.
|
55
|
+
|
56
|
+
* Documentation typos and fixes.
|
15
57
|
|
16
58
|
== What is Rake
|
17
59
|
|
@@ -28,7 +70,7 @@ The easiest way to get and install rake is via RubyGems ...
|
|
28
70
|
|
29
71
|
Otherwise, you can get it from the more traditional places:
|
30
72
|
|
31
|
-
Home Page:: http://
|
73
|
+
Home Page:: http://github.com/jimweirich/rake
|
32
74
|
Download:: http://rubyforge.org/project/showfiles.php?group_id=50
|
33
75
|
GitHub:: git://github.com/jimweirich/rake.git
|
34
76
|
|
@@ -38,18 +80,23 @@ As usual, it was input from users that drove a alot of these changes. The
|
|
38
80
|
following people either contributed patches, made suggestions or made
|
39
81
|
otherwise helpful comments. Thanks to ...
|
40
82
|
|
41
|
-
*
|
42
|
-
*
|
43
|
-
*
|
44
|
-
*
|
45
|
-
*
|
46
|
-
*
|
47
|
-
*
|
48
|
-
*
|
49
|
-
*
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
83
|
+
* Aaron Patterson
|
84
|
+
* Dylan Smith
|
85
|
+
* Jo Liss
|
86
|
+
* Jonas Pfenniger
|
87
|
+
* Kazuki Tsujimoto
|
88
|
+
* Michael Bishop
|
89
|
+
* Michael Elufimov
|
90
|
+
* NAKAMURA Usaku
|
91
|
+
* Ryan Davis
|
92
|
+
* Sam Grönblom
|
93
|
+
* Sam Phippen
|
94
|
+
* Sergio Wong
|
95
|
+
* Tay Ray Chuan
|
96
|
+
* grosser
|
97
|
+
* quix
|
98
|
+
|
99
|
+
Also, many thanks to Eric Hodel for assisting with getting this release
|
100
|
+
out the door.
|
54
101
|
|
55
102
|
-- Jim Weirich
|
data/lib/rake/application.rb
CHANGED
@@ -58,7 +58,7 @@ module Rake
|
|
58
58
|
#
|
59
59
|
# * Initialize the command line options (+init+).
|
60
60
|
# * Define the tasks (+load_rakefile+).
|
61
|
-
# * Run the top level tasks (+
|
61
|
+
# * Run the top level tasks (+top_level+).
|
62
62
|
#
|
63
63
|
# If you wish to build a custom rake command, you should call
|
64
64
|
# +init+ on your application. Then define any tasks. Finally,
|
@@ -67,15 +67,7 @@ module Rake
|
|
67
67
|
standard_exception_handling do
|
68
68
|
init
|
69
69
|
load_rakefile
|
70
|
-
thread_pool.gather_history if options.job_stats == :history
|
71
70
|
top_level
|
72
|
-
thread_pool.join
|
73
|
-
if options.job_stats
|
74
|
-
stats = thread_pool.statistics
|
75
|
-
puts "Maximum active threads: #{stats[:max_active_threads]}"
|
76
|
-
puts "Total threads in play: #{stats[:total_threads_in_play]}"
|
77
|
-
end
|
78
|
-
ThreadHistoryDisplay.new(thread_pool.history).show if options.job_stats == :history
|
79
71
|
end
|
80
72
|
end
|
81
73
|
|
@@ -97,13 +89,30 @@ module Rake
|
|
97
89
|
|
98
90
|
# Run the top level tasks of a Rake application.
|
99
91
|
def top_level
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
92
|
+
run_with_threads do
|
93
|
+
if options.show_tasks
|
94
|
+
display_tasks_and_comments
|
95
|
+
elsif options.show_prereqs
|
96
|
+
display_prerequisites
|
97
|
+
else
|
98
|
+
top_level_tasks.each { |task_name| invoke_task(task_name) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Run the given block with the thread startup and shutdown.
|
104
|
+
def run_with_threads
|
105
|
+
thread_pool.gather_history if options.job_stats == :history
|
106
|
+
|
107
|
+
yield
|
108
|
+
|
109
|
+
thread_pool.join
|
110
|
+
if options.job_stats
|
111
|
+
stats = thread_pool.statistics
|
112
|
+
puts "Maximum active threads: #{stats[:max_active_threads]}"
|
113
|
+
puts "Total threads in play: #{stats[:total_threads_in_play]}"
|
106
114
|
end
|
115
|
+
ThreadHistoryDisplay.new(thread_pool.history).show if options.job_stats == :history
|
107
116
|
end
|
108
117
|
|
109
118
|
# Add a loader to handle imported files ending in the extension
|
data/lib/rake/backtrace.rb
CHANGED
@@ -5,8 +5,9 @@ module Rake
|
|
5
5
|
keys.grep(/(prefix|libdir)/)) + [
|
6
6
|
File.join(File.dirname(__FILE__), ".."),
|
7
7
|
].map { |f| Regexp.quote(File.expand_path(f)) }
|
8
|
+
SUPPRESSED_PATHS.reject! { |s| s.nil? || s =~ /^ *$/ }
|
8
9
|
|
9
|
-
SUPPRESS_PATTERN = %r!(\A#{SUPPRESSED_PATHS.join('|')}|bin/rake:\d+)!
|
10
|
+
SUPPRESS_PATTERN = %r!(\A#{SUPPRESSED_PATHS.join('|')}|bin/rake:\d+)!i
|
10
11
|
|
11
12
|
def self.collapse(backtrace)
|
12
13
|
pattern = Rake.application.options.suppress_backtrace_pattern ||
|
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
@@ -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/task.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require 'rake/private_reader'
|
2
|
+
|
1
3
|
module Rake
|
2
4
|
|
3
|
-
class ThreadHistoryDisplay
|
4
|
-
|
5
|
-
|
5
|
+
class ThreadHistoryDisplay # :nodoc: all
|
6
|
+
include Rake::PrivateReader
|
7
|
+
|
8
|
+
private_reader :stats, :items, :threads
|
6
9
|
|
7
10
|
def initialize(stats)
|
8
11
|
@stats = stats
|
@@ -13,7 +16,7 @@ module Rake
|
|
13
16
|
def show
|
14
17
|
puts "Job History:"
|
15
18
|
stats.each do |stat|
|
16
|
-
stat[:data] ||=
|
19
|
+
stat[:data] ||= {}
|
17
20
|
rename(stat, :thread, threads)
|
18
21
|
rename(stat[:data], :item_id, items)
|
19
22
|
rename(stat[:data], :new_thread, threads)
|
data/lib/rake/thread_pool.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'set'
|
3
3
|
|
4
|
+
require 'rake/promise'
|
5
|
+
|
4
6
|
module Rake
|
5
7
|
|
6
8
|
class ThreadPool # :nodoc: all
|
@@ -28,63 +30,12 @@ module Rake
|
|
28
30
|
# pool. Sending <tt>#value</tt> to the object will sleep the
|
29
31
|
# current thread until the future is finished and will return the
|
30
32
|
# result (or raise an exception thrown from the future)
|
31
|
-
def future(*args
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
promise_mutex = Mutex.new
|
36
|
-
promise_result = promise_error = NOT_SET
|
37
|
-
|
38
|
-
# (promise code builds on Ben Lavender's public-domain 'promise' gem)
|
39
|
-
promise = lambda do
|
40
|
-
# return immediately if the future has been executed
|
41
|
-
unless promise_result.equal?(NOT_SET) && promise_error.equal?(NOT_SET)
|
42
|
-
return promise_error.equal?(NOT_SET) ? promise_result : raise(promise_error)
|
43
|
-
end
|
44
|
-
|
45
|
-
# try to get the lock and execute the promise, otherwise, sleep.
|
46
|
-
if promise_mutex.try_lock
|
47
|
-
if promise_result.equal?(NOT_SET) && promise_error.equal?(NOT_SET)
|
48
|
-
#execute the promise
|
49
|
-
begin
|
50
|
-
promise_result = block.call(*local_args)
|
51
|
-
rescue Exception => e
|
52
|
-
promise_error = e
|
53
|
-
end
|
54
|
-
block = local_args = nil # GC can now clean these up
|
55
|
-
end
|
56
|
-
promise_mutex.unlock
|
57
|
-
else
|
58
|
-
# Even if we didn't get the lock, we need to sleep until the
|
59
|
-
# promise has finished executing. If, however, the current
|
60
|
-
# thread is part of the thread pool, we need to free up a
|
61
|
-
# new thread in the pool so there will always be a thread
|
62
|
-
# doing work.
|
63
|
-
|
64
|
-
wait_for_promise = lambda {
|
65
|
-
stat :waiting, item_id: promise.object_id
|
66
|
-
promise_mutex.synchronize {}
|
67
|
-
stat :continue, item_id: promise.object_id
|
68
|
-
}
|
69
|
-
|
70
|
-
unless @threads_mon.synchronize { @threads.include? Thread.current }
|
71
|
-
wait_for_promise.call
|
72
|
-
else
|
73
|
-
@threads_mon.synchronize { @max_active_threads += 1 }
|
74
|
-
start_thread
|
75
|
-
wait_for_promise.call
|
76
|
-
@threads_mon.synchronize { @max_active_threads -= 1 }
|
77
|
-
end
|
78
|
-
end
|
79
|
-
promise_error.equal?(NOT_SET) ? promise_result : raise(promise_error)
|
80
|
-
end
|
81
|
-
|
82
|
-
def promise.value
|
83
|
-
call
|
84
|
-
end
|
33
|
+
def future(*args, &block)
|
34
|
+
promise = Promise.new(args, &block)
|
35
|
+
promise.recorder = lambda { |*stats| stat(*stats) }
|
85
36
|
|
86
37
|
@queue.enq promise
|
87
|
-
stat :
|
38
|
+
stat :queued, :item_id => promise.object_id
|
88
39
|
start_thread
|
89
40
|
promise
|
90
41
|
end
|
@@ -93,14 +44,18 @@ module Rake
|
|
93
44
|
def join
|
94
45
|
@threads_mon.synchronize do
|
95
46
|
begin
|
96
|
-
|
47
|
+
stat :joining
|
48
|
+
@join_cond.wait unless @threads.empty?
|
49
|
+
stat :joined
|
97
50
|
rescue Exception => e
|
51
|
+
stat :joined
|
98
52
|
$stderr.puts e
|
99
53
|
$stderr.print "Queue contains #{@queue.size} items. Thread pool contains #{@threads.count} threads\n"
|
100
54
|
$stderr.print "Current Thread #{Thread.current} status = #{Thread.current.status}\n"
|
101
55
|
$stderr.puts e.backtrace.join("\n")
|
102
56
|
@threads.each do |t|
|
103
57
|
$stderr.print "Thread #{t} status = #{t.status}\n"
|
58
|
+
# 1.8 doesn't support Thread#backtrace
|
104
59
|
$stderr.puts t.backtrace.join("\n") if t.respond_to? :backtrace
|
105
60
|
end
|
106
61
|
raise e
|
@@ -119,45 +74,58 @@ module Rake
|
|
119
74
|
# (see #gather_history). Best to call this when the job is
|
120
75
|
# complete (i.e. after ThreadPool#join is called).
|
121
76
|
def history # :nodoc:
|
122
|
-
@history_mon.synchronize { @history.dup }
|
77
|
+
@history_mon.synchronize { @history.dup }.
|
78
|
+
sort_by { |i| i[:time] }.
|
79
|
+
each { |i| i[:time] -= @history_start_time }
|
123
80
|
end
|
124
81
|
|
125
82
|
# Return a hash of always collected statistics for the thread pool.
|
126
83
|
def statistics # :nodoc:
|
127
84
|
{
|
128
|
-
total_threads_in_play
|
129
|
-
max_active_threads
|
85
|
+
:total_threads_in_play => @total_threads_in_play,
|
86
|
+
:max_active_threads => @max_active_threads,
|
130
87
|
}
|
131
88
|
end
|
132
89
|
|
133
90
|
private
|
134
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
|
+
|
135
110
|
def start_thread # :nodoc:
|
136
111
|
@threads_mon.synchronize do
|
137
112
|
next unless @threads.count < @max_active_threads
|
138
113
|
|
139
114
|
t = Thread.new do
|
140
115
|
begin
|
141
|
-
while @threads.count <= @max_active_threads
|
142
|
-
|
143
|
-
# still could have had an item which by this statement
|
144
|
-
# is now gone. For this reason we pass true to Queue#deq
|
145
|
-
# because we will sleep indefinitely if it is empty.
|
146
|
-
block = @queue.deq(true)
|
147
|
-
stat :item_dequeued, item_id: block.object_id
|
148
|
-
block.call
|
116
|
+
while @threads.count <= @max_active_threads
|
117
|
+
break unless process_queue_item
|
149
118
|
end
|
150
|
-
rescue ThreadError # this means the queue is empty
|
151
119
|
ensure
|
152
120
|
@threads_mon.synchronize do
|
153
121
|
@threads.delete Thread.current
|
154
|
-
stat :
|
122
|
+
stat :ended, :thread_count => @threads.count
|
155
123
|
@join_cond.broadcast if @threads.empty?
|
156
124
|
end
|
157
125
|
end
|
158
126
|
end
|
159
127
|
@threads << t
|
160
|
-
stat :
|
128
|
+
stat :spawned, :new_thread => t.object_id, :thread_count => @threads.count
|
161
129
|
@total_threads_in_play = @threads.count if @threads.count > @total_threads_in_play
|
162
130
|
end
|
163
131
|
end
|
@@ -165,10 +133,10 @@ module Rake
|
|
165
133
|
def stat(event, data=nil) # :nodoc:
|
166
134
|
return if @history_start_time.nil?
|
167
135
|
info = {
|
168
|
-
event
|
169
|
-
data
|
170
|
-
time
|
171
|
-
thread
|
136
|
+
:event => event,
|
137
|
+
:data => data,
|
138
|
+
:time => Time.now,
|
139
|
+
:thread => Thread.current.object_id,
|
172
140
|
}
|
173
141
|
@history_mon.synchronize { @history << info }
|
174
142
|
end
|
@@ -182,8 +150,6 @@ module Rake
|
|
182
150
|
def __threads__ # :nodoc:
|
183
151
|
@threads.dup
|
184
152
|
end
|
185
|
-
|
186
|
-
NOT_SET = Object.new.freeze # :nodoc:
|
187
153
|
end
|
188
154
|
|
189
155
|
end
|
data/lib/rake/version.rb
CHANGED
@@ -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
|
data/test/test_rake_backtrace.rb
CHANGED
@@ -24,8 +24,8 @@ class TestRakeBacktrace < Rake::TestCase
|
|
24
24
|
|
25
25
|
assert_equal "rake aborted!", lines[0]
|
26
26
|
assert_equal "foooo!", lines[1]
|
27
|
-
|
28
|
-
|
27
|
+
assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:3!i, lines
|
28
|
+
assert_something_matches %r!\ATasks:!, lines
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_multi_collapse
|
@@ -42,9 +42,9 @@ class TestRakeBacktrace < Rake::TestCase
|
|
42
42
|
|
43
43
|
assert_equal "rake aborted!", lines[0]
|
44
44
|
assert_equal "barrr!", lines[1]
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:6!i, lines
|
46
|
+
assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:3!i, lines
|
47
|
+
assert_something_matches %r!\ATasks:!, lines
|
48
48
|
end
|
49
49
|
|
50
50
|
def test_suppress_option
|
@@ -57,11 +57,25 @@ class TestRakeBacktrace < Rake::TestCase
|
|
57
57
|
lines = rake("baz").split("\n")
|
58
58
|
assert_equal "rake aborted!", lines[0]
|
59
59
|
assert_equal "bazzz!", lines[1]
|
60
|
-
|
60
|
+
assert_something_matches %r!Rakefile!i, lines
|
61
61
|
|
62
|
-
lines = rake("--suppress-backtrace", "
|
62
|
+
lines = rake("--suppress-backtrace", ".ak.file", "baz").split("\n")
|
63
63
|
assert_equal "rake aborted!", lines[0]
|
64
64
|
assert_equal "bazzz!", lines[1]
|
65
|
-
refute_match %r!Rakefile
|
65
|
+
refute_match %r!Rakefile!i, lines[2]
|
66
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Assert that the pattern matches at least one line in +lines+.
|
71
|
+
def assert_something_matches(pattern, lines)
|
72
|
+
lines.each do |ln|
|
73
|
+
if pattern =~ ln
|
74
|
+
assert_match pattern, ln
|
75
|
+
return
|
76
|
+
end
|
77
|
+
end
|
78
|
+
flunk "expected #{pattern.inspect} to match something in:\n #{lines.join("\n ")}"
|
79
|
+
end
|
80
|
+
|
67
81
|
end
|
@@ -69,7 +69,7 @@ class TestRakeTestThreadPool < Rake::TestCase
|
|
69
69
|
}
|
70
70
|
|
71
71
|
pool.join
|
72
|
-
assert_equal true, pool.__send__(:__queue__).empty
|
72
|
+
assert_equal true, pool.__send__(:__queue__).empty?, "queue should be empty"
|
73
73
|
end
|
74
74
|
|
75
75
|
# test that throwing an exception way down in the blocks propagates
|
@@ -88,29 +88,6 @@ class TestRakeTestThreadPool < Rake::TestCase
|
|
88
88
|
|
89
89
|
end
|
90
90
|
|
91
|
-
def test_pool_always_has_max_threads_doing_work
|
92
|
-
# here we need to test that even if some threads are halted, there
|
93
|
-
# are always at least max_threads that are not sleeping.
|
94
|
-
pool = ThreadPool.new(2)
|
95
|
-
initial_sleep_time = 0.2
|
96
|
-
future1 = pool.future { sleep initial_sleep_time }
|
97
|
-
dependent_futures = 5.times.collect { pool.future{ future1.value } }
|
98
|
-
future2 = pool.future { sleep initial_sleep_time }
|
99
|
-
future3 = pool.future { sleep 0.01 }
|
100
|
-
|
101
|
-
sleep initial_sleep_time / 2.0 # wait for everything to queue up
|
102
|
-
|
103
|
-
# at this point, we should have 5 threads sleeping depending on future1, and
|
104
|
-
# two threads doing work on future1 and future 2.
|
105
|
-
assert_equal pool.__send__(:__threads__).count, 7
|
106
|
-
|
107
|
-
# future 3 is in the queue because there aren't enough active threads to work on it.
|
108
|
-
assert_equal pool.__send__(:__queue__).size, 1
|
109
|
-
|
110
|
-
[future1, dependent_futures, future2, future3].flatten.each { |f| f.value }
|
111
|
-
pool.join
|
112
|
-
end
|
113
|
-
|
114
91
|
def test_pool_prevents_deadlock
|
115
92
|
pool = ThreadPool.new(5)
|
116
93
|
|
@@ -18,7 +18,7 @@ class TestThreadHistoryDisplay < Rake::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_item_queued
|
21
|
-
@stats << event(:item_queued, item_id
|
21
|
+
@stats << event(:item_queued, :item_id => 123)
|
22
22
|
out, _ = capture_io do
|
23
23
|
@display.show
|
24
24
|
end
|
@@ -26,7 +26,7 @@ class TestThreadHistoryDisplay < Rake::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_item_dequeued
|
29
|
-
@stats << event(:item_dequeued, item_id
|
29
|
+
@stats << event(:item_dequeued, :item_id => 123)
|
30
30
|
out, _ = capture_io do
|
31
31
|
@display.show
|
32
32
|
end
|
@@ -34,8 +34,8 @@ class TestThreadHistoryDisplay < Rake::TestCase
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_multiple_items
|
37
|
-
@stats << event(:item_queued, item_id
|
38
|
-
@stats << event(:item_queued, item_id
|
37
|
+
@stats << event(:item_queued, :item_id => 123)
|
38
|
+
@stats << event(:item_queued, :item_id => 124)
|
39
39
|
out, _ = capture_io do
|
40
40
|
@display.show
|
41
41
|
end
|
@@ -44,7 +44,7 @@ class TestThreadHistoryDisplay < Rake::TestCase
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_waiting
|
47
|
-
@stats << event(:waiting, item_id
|
47
|
+
@stats << event(:waiting, :item_id => 123)
|
48
48
|
out, _ = capture_io do
|
49
49
|
@display.show
|
50
50
|
end
|
@@ -52,7 +52,7 @@ class TestThreadHistoryDisplay < Rake::TestCase
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_continue
|
55
|
-
@stats << event(:continue, item_id
|
55
|
+
@stats << event(:continue, :item_id => 123)
|
56
56
|
out, _ = capture_io do
|
57
57
|
@display.show
|
58
58
|
end
|
@@ -60,29 +60,29 @@ class TestThreadHistoryDisplay < Rake::TestCase
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def test_thread_deleted
|
63
|
-
@stats << event(:thread_deleted, deleted_thread
|
63
|
+
@stats << event(:thread_deleted, :deleted_thread => 123456, :thread_count => 12)
|
64
64
|
out, _ = capture_io do
|
65
65
|
@display.show
|
66
66
|
end
|
67
|
-
assert_match(/^ *1000000 +A +thread_deleted +deleted_thread:B +thread_count:12$/, out)
|
67
|
+
assert_match(/^ *1000000 +A +thread_deleted( +deleted_thread:B| +thread_count:12){2}$/, out)
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_thread_created
|
71
|
-
@stats << event(:thread_created, new_thread
|
71
|
+
@stats << event(:thread_created, :new_thread => 123456, :thread_count => 13)
|
72
72
|
out, _ = capture_io do
|
73
73
|
@display.show
|
74
74
|
end
|
75
|
-
assert_match(/^ *1000000 +A +thread_created +new_thread:B +thread_count:13$/, out)
|
75
|
+
assert_match(/^ *1000000 +A +thread_created( +new_thread:B| +thread_count:13){2}$/, out)
|
76
76
|
end
|
77
77
|
|
78
78
|
private
|
79
79
|
|
80
80
|
def event(type, data={})
|
81
81
|
result = {
|
82
|
-
event
|
83
|
-
time
|
84
|
-
data
|
85
|
-
thread
|
82
|
+
:event => type,
|
83
|
+
:time => @time / 1_000_000.0,
|
84
|
+
:data => data,
|
85
|
+
:thread => Thread.current.object_id
|
86
86
|
}
|
87
87
|
@time += 1
|
88
88
|
result
|
metadata
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rake
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.3.beta.3
|
5
4
|
prerelease: 6
|
5
|
+
version: !binary |-
|
6
|
+
MC45LjMuYmV0YS40
|
6
7
|
platform: ruby
|
7
8
|
authors:
|
8
9
|
- Jim Weirich
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
+
date: 2012-11-09 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
|
-
|
16
|
-
|
16
|
+
type: :development
|
17
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ~>
|
20
21
|
- !ruby/object:Gem::Version
|
21
22
|
version: '2.1'
|
22
|
-
type: :development
|
23
23
|
prerelease: false
|
24
|
-
|
24
|
+
name: minitest
|
25
|
+
requirement: !ruby/object:Gem::Requirement
|
25
26
|
none: false
|
26
27
|
requirements:
|
27
28
|
- - ~>
|
@@ -62,6 +63,7 @@ extra_rdoc_files:
|
|
62
63
|
- doc/release_notes/rake-0.8.7.rdoc
|
63
64
|
- doc/release_notes/rake-0.9.0.rdoc
|
64
65
|
- doc/release_notes/rake-0.9.1.rdoc
|
66
|
+
- doc/release_notes/rake-0.9.2.2.rdoc
|
65
67
|
- doc/release_notes/rake-0.9.2.rdoc
|
66
68
|
- doc/release_notes/rake-0.9.3.rdoc
|
67
69
|
files:
|
@@ -107,6 +109,8 @@ files:
|
|
107
109
|
- lib/rake/packagetask.rb
|
108
110
|
- lib/rake/pathmap.rb
|
109
111
|
- lib/rake/phony.rb
|
112
|
+
- lib/rake/private_reader.rb
|
113
|
+
- lib/rake/promise.rb
|
110
114
|
- lib/rake/pseudo_status.rb
|
111
115
|
- lib/rake/rake_module.rb
|
112
116
|
- lib/rake/rake_test_loader.rb
|
@@ -126,6 +130,7 @@ files:
|
|
126
130
|
- lib/rake/win32.rb
|
127
131
|
- test/file_creation.rb
|
128
132
|
- test/helper.rb
|
133
|
+
- test/test_private_reader.rb
|
129
134
|
- test/test_rake.rb
|
130
135
|
- test/test_rake_application.rb
|
131
136
|
- test/test_rake_application_options.rb
|
@@ -201,6 +206,7 @@ files:
|
|
201
206
|
- doc/release_notes/rake-0.8.7.rdoc
|
202
207
|
- doc/release_notes/rake-0.9.0.rdoc
|
203
208
|
- doc/release_notes/rake-0.9.1.rdoc
|
209
|
+
- doc/release_notes/rake-0.9.2.2.rdoc
|
204
210
|
- doc/release_notes/rake-0.9.2.rdoc
|
205
211
|
- doc/release_notes/rake-0.9.3.rdoc
|
206
212
|
homepage: http://rake.rubyforge.org
|