rake 0.8.7 → 0.9.0

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.
Files changed (124) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGES +77 -9
  3. data/{README → README.rdoc} +14 -10
  4. data/Rakefile +129 -118
  5. data/TODO +1 -1
  6. data/doc/command_line_usage.rdoc +18 -6
  7. data/doc/glossary.rdoc +2 -2
  8. data/doc/jamis.rb +2 -2
  9. data/doc/proto_rake.rdoc +22 -22
  10. data/doc/rake.1.gz +0 -0
  11. data/doc/rakefile.rdoc +55 -32
  12. data/doc/rational.rdoc +6 -6
  13. data/doc/release_notes/rake-0.4.15.rdoc +1 -1
  14. data/doc/release_notes/rake-0.5.0.rdoc +1 -1
  15. data/doc/release_notes/rake-0.7.0.rdoc +1 -1
  16. data/doc/release_notes/rake-0.7.2.rdoc +3 -3
  17. data/doc/release_notes/rake-0.7.3.rdoc +2 -2
  18. data/doc/release_notes/rake-0.8.0.rdoc +1 -1
  19. data/doc/release_notes/rake-0.8.2.rdoc +3 -3
  20. data/doc/release_notes/rake-0.8.3.rdoc +2 -2
  21. data/doc/release_notes/rake-0.8.4.rdoc +1 -1
  22. data/doc/release_notes/rake-0.8.5.rdoc +1 -1
  23. data/doc/release_notes/rake-0.8.6.rdoc +1 -1
  24. data/doc/release_notes/rake-0.8.7.rdoc +1 -1
  25. data/doc/release_notes/rake-0.9.0.rdoc +112 -0
  26. data/install.rb +14 -12
  27. data/lib/rake/alt_system.rb +7 -6
  28. data/lib/rake/application.rb +589 -0
  29. data/lib/rake/classic_namespace.rb +1 -0
  30. data/lib/rake/clean.rb +2 -4
  31. data/lib/rake/cloneable.rb +25 -0
  32. data/lib/rake/contrib/compositepublisher.rb +2 -5
  33. data/lib/rake/contrib/ftptools.rb +5 -8
  34. data/lib/rake/contrib/publisher.rb +2 -8
  35. data/lib/rake/contrib/rubyforgepublisher.rb +2 -4
  36. data/lib/rake/contrib/sshpublisher.rb +4 -6
  37. data/lib/rake/contrib/sys.rb +7 -25
  38. data/lib/rake/default_loader.rb +10 -0
  39. data/lib/rake/dsl.rb +2 -0
  40. data/lib/rake/dsl_definition.rb +143 -0
  41. data/lib/rake/early_time.rb +18 -0
  42. data/lib/rake/ext/core.rb +27 -0
  43. data/lib/rake/ext/module.rb +39 -0
  44. data/lib/rake/ext/string.rb +167 -0
  45. data/lib/rake/ext/time.rb +14 -0
  46. data/lib/rake/file_creation_task.rb +24 -0
  47. data/lib/rake/file_list.rb +403 -0
  48. data/lib/rake/file_task.rb +47 -0
  49. data/lib/rake/file_utils.rb +112 -0
  50. data/lib/rake/file_utils_ext.rb +142 -0
  51. data/lib/rake/gempackagetask.rb +6 -90
  52. data/lib/rake/invocation_chain.rb +51 -0
  53. data/lib/rake/invocation_exception_mixin.rb +16 -0
  54. data/lib/rake/loaders/makefile.rb +13 -15
  55. data/lib/rake/multi_task.rb +16 -0
  56. data/lib/rake/name_space.rb +25 -0
  57. data/lib/rake/packagetask.rb +13 -12
  58. data/lib/rake/pathmap.rb +1 -0
  59. data/lib/rake/pseudo_status.rb +24 -0
  60. data/lib/rake/rake_module.rb +29 -0
  61. data/lib/rake/rake_test_loader.rb +10 -2
  62. data/lib/rake/rdoctask.rb +211 -190
  63. data/lib/rake/ruby182_test_unit_fix.rb +9 -7
  64. data/lib/rake/rule_recursion_overflow_error.rb +20 -0
  65. data/lib/rake/runtest.rb +4 -6
  66. data/lib/rake/task.rb +327 -0
  67. data/lib/rake/task_argument_error.rb +7 -0
  68. data/lib/rake/task_arguments.rb +74 -0
  69. data/lib/rake/task_manager.rb +307 -0
  70. data/lib/rake/tasklib.rb +1 -2
  71. data/lib/rake/testtask.rb +57 -27
  72. data/lib/rake/version.rb +10 -0
  73. data/lib/rake/win32.rb +4 -4
  74. data/lib/rake.rb +29 -2470
  75. data/test/contrib/test_sys.rb +8 -31
  76. data/test/data/access/Rakefile +33 -0
  77. data/test/data/comments/Rakefile +18 -0
  78. data/test/data/default/Rakefile +1 -1
  79. data/test/data/deprecated_import/Rakefile +1 -0
  80. data/test/data/dryrun/Rakefile +1 -1
  81. data/test/data/file_creation_task/Rakefile +1 -1
  82. data/test/data/namespace/Rakefile +9 -0
  83. data/test/data/rakelib/test1.rb +1 -0
  84. data/test/data/verbose/Rakefile +34 -0
  85. data/test/{filecreation.rb → file_creation.rb} +11 -7
  86. data/test/functional/functional_test.rb +25 -0
  87. data/test/{session_functional.rb → functional/session_based_tests.rb} +141 -23
  88. data/test/in_environment.rb +7 -5
  89. data/test/{test_application.rb → lib/application_test.rb} +331 -143
  90. data/test/{test_clean.rb → lib/clean_test.rb} +1 -0
  91. data/test/{test_definitions.rb → lib/definitions_test.rb} +3 -3
  92. data/test/lib/dsl_test.rb +52 -0
  93. data/test/{test_earlytime.rb → lib/earlytime_test.rb} +1 -2
  94. data/test/{test_extension.rb → lib/extension_test.rb} +2 -2
  95. data/test/{test_file_creation_task.rb → lib/file_creation_task_test.rb} +1 -1
  96. data/test/{test_file_task.rb → lib/file_task_test.rb} +4 -4
  97. data/test/{test_filelist.rb → lib/filelist_test.rb} +38 -24
  98. data/test/{test_fileutils.rb → lib/fileutils_test.rb} +27 -22
  99. data/test/{test_multitask.rb → lib/multitask_test.rb} +14 -6
  100. data/test/lib/package_task_test.rb +82 -0
  101. data/test/{test_pathmap.rb → lib/pathmap_test.rb} +3 -2
  102. data/test/{test_rake.rb → lib/rake_test.rb} +1 -1
  103. data/test/{test_rdoc_task.rb → lib/rdoc_task_test.rb} +19 -23
  104. data/test/{test_require.rb → lib/require_test.rb} +8 -2
  105. data/test/{test_rules.rb → lib/rules_test.rb} +2 -3
  106. data/test/{test_task_arguments.rb → lib/task_arguments_test.rb} +5 -5
  107. data/test/{test_task_manager.rb → lib/task_manager_test.rb} +15 -5
  108. data/test/{test_tasks.rb → lib/task_test.rb} +84 -21
  109. data/test/{test_test_task.rb → lib/test_task_test.rb} +3 -3
  110. data/test/lib/testtask_test.rb +49 -0
  111. data/test/{test_top_level_functions.rb → lib/top_level_functions_test.rb} +5 -3
  112. data/test/{test_win32.rb → lib/win32_test.rb} +19 -0
  113. data/test/rake_test_setup.rb +5 -9
  114. data/test/ruby_version_test.rb +3 -0
  115. data/test/test_helper.rb +19 -0
  116. metadata +108 -49
  117. data/test/functional.rb +0 -15
  118. data/test/test_package_task.rb +0 -118
  119. /data/test/{test_ftp.rb → lib/ftp_test.rb} +0 -0
  120. /data/test/{test_invocation_chain.rb → lib/invocation_chain_test.rb} +0 -0
  121. /data/test/{test_makefile_loader.rb → lib/makefile_loader_test.rb} +0 -0
  122. /data/test/{test_namespace.rb → lib/namespace_test.rb} +0 -0
  123. /data/test/{test_pseudo_status.rb → lib/pseudo_status_test.rb} +0 -0
  124. /data/test/{test_tasklib.rb → lib/tasklib_test.rb} +0 -0
data/lib/rake/task.rb ADDED
@@ -0,0 +1,327 @@
1
+ require 'rake/invocation_exception_mixin'
2
+
3
+ module Rake
4
+
5
+ # #########################################################################
6
+ # A Task is the basic unit of work in a Rakefile. Tasks have associated
7
+ # actions (possibly more than one) and a list of prerequisites. When
8
+ # invoked, a task will first ensure that all of its prerequisites have an
9
+ # opportunity to run and then it will execute its own actions.
10
+ #
11
+ # Tasks are not usually created directly using the new method, but rather
12
+ # use the +file+ and +task+ convenience methods.
13
+ #
14
+ class Task
15
+ # List of prerequisites for a task.
16
+ attr_reader :prerequisites
17
+
18
+ # List of actions attached to a task.
19
+ attr_reader :actions
20
+
21
+ # Application owning this task.
22
+ attr_accessor :application
23
+
24
+ # Comment for this task. Restricted to a single line of no more than 50
25
+ # characters.
26
+ attr_reader :comment
27
+
28
+ # Full text of the (possibly multi-line) comment.
29
+ attr_reader :full_comment
30
+
31
+ # Array of nested namespaces names used for task lookup by this task.
32
+ attr_reader :scope
33
+
34
+ # File/Line locations of each of the task definitions for this
35
+ # task (only valid if the task was defined with the detect
36
+ # location option set).
37
+ attr_reader :locations
38
+
39
+ # Return task name
40
+ def to_s
41
+ name
42
+ end
43
+
44
+ def inspect
45
+ "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
46
+ end
47
+
48
+ # List of sources for task.
49
+ attr_writer :sources
50
+ def sources
51
+ @sources ||= []
52
+ end
53
+
54
+ # List of prerequisite tasks
55
+ def prerequisite_tasks
56
+ prerequisites.collect { |pre| lookup_prerequisite(pre) }
57
+ end
58
+
59
+ def lookup_prerequisite(prerequisite_name)
60
+ application[prerequisite_name, @scope]
61
+ end
62
+ private :lookup_prerequisite
63
+
64
+ # First source from a rule (nil if no sources)
65
+ def source
66
+ @sources.first if defined?(@sources)
67
+ end
68
+
69
+ # Create a task named +task_name+ with no actions or prerequisites. Use
70
+ # +enhance+ to add actions and prerequisites.
71
+ def initialize(task_name, app)
72
+ @name = task_name.to_s
73
+ @prerequisites = []
74
+ @actions = []
75
+ @already_invoked = false
76
+ @full_comment = nil
77
+ @comment = nil
78
+ @lock = Monitor.new
79
+ @application = app
80
+ @scope = app.current_scope
81
+ @arg_names = nil
82
+ @locations = []
83
+ end
84
+
85
+ # Enhance a task with prerequisites or actions. Returns self.
86
+ def enhance(deps=nil, &block)
87
+ @prerequisites |= deps if deps
88
+ @actions << block if block_given?
89
+ self
90
+ end
91
+
92
+ # Name of the task, including any namespace qualifiers.
93
+ def name
94
+ @name.to_s
95
+ end
96
+
97
+ # Name of task with argument list description.
98
+ def name_with_args # :nodoc:
99
+ if arg_description
100
+ "#{name}#{arg_description}"
101
+ else
102
+ name
103
+ end
104
+ end
105
+
106
+ # Argument description (nil if none).
107
+ def arg_description # :nodoc:
108
+ @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
109
+ end
110
+
111
+ # Name of arguments for this task.
112
+ def arg_names
113
+ @arg_names || []
114
+ end
115
+
116
+ # Reenable the task, allowing its tasks to be executed if the task
117
+ # is invoked again.
118
+ def reenable
119
+ @already_invoked = false
120
+ end
121
+
122
+ # Clear the existing prerequisites and actions of a rake task.
123
+ def clear
124
+ clear_prerequisites
125
+ clear_actions
126
+ self
127
+ end
128
+
129
+ # Clear the existing prerequisites of a rake task.
130
+ def clear_prerequisites
131
+ prerequisites.clear
132
+ self
133
+ end
134
+
135
+ # Clear the existing actions on a rake task.
136
+ def clear_actions
137
+ actions.clear
138
+ self
139
+ end
140
+
141
+ # Invoke the task if it is needed. Prerequisites are invoked first.
142
+ def invoke(*args)
143
+ task_args = TaskArguments.new(arg_names, args)
144
+ invoke_with_call_chain(task_args, InvocationChain::EMPTY)
145
+ end
146
+
147
+ # Same as invoke, but explicitly pass a call chain to detect
148
+ # circular dependencies.
149
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
150
+ new_chain = InvocationChain.append(self, invocation_chain)
151
+ @lock.synchronize do
152
+ if application.options.trace
153
+ $stderr.puts "** Invoke #{name} #{format_trace_flags}"
154
+ end
155
+ return if @already_invoked
156
+ @already_invoked = true
157
+ invoke_prerequisites(task_args, new_chain)
158
+ execute(task_args) if needed?
159
+ end
160
+ rescue Exception => ex
161
+ add_chain_to(ex, new_chain)
162
+ raise ex
163
+ end
164
+ protected :invoke_with_call_chain
165
+
166
+ def add_chain_to(exception, new_chain)
167
+ exception.extend(InvocationExceptionMixin) unless exception.respond_to?(:chain)
168
+ exception.chain = new_chain if exception.chain.nil?
169
+ end
170
+ private :add_chain_to
171
+
172
+ # Invoke all the prerequisites of a task.
173
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
174
+ prerequisite_tasks.each { |prereq|
175
+ prereq_args = task_args.new_scope(prereq.arg_names)
176
+ prereq.invoke_with_call_chain(prereq_args, invocation_chain)
177
+ }
178
+ end
179
+
180
+ # Format the trace flags for display.
181
+ def format_trace_flags
182
+ flags = []
183
+ flags << "first_time" unless @already_invoked
184
+ flags << "not_needed" unless needed?
185
+ flags.empty? ? "" : "(" + flags.join(", ") + ")"
186
+ end
187
+ private :format_trace_flags
188
+
189
+ # Execute the actions associated with this task.
190
+ def execute(args=nil)
191
+ args ||= EMPTY_TASK_ARGS
192
+ if application.options.dryrun
193
+ $stderr.puts "** Execute (dry run) #{name}"
194
+ return
195
+ end
196
+ if application.options.trace
197
+ $stderr.puts "** Execute #{name}"
198
+ end
199
+ application.enhance_with_matching_rule(name) if @actions.empty?
200
+ @actions.each do |act|
201
+ case act.arity
202
+ when 1
203
+ act.call(self)
204
+ else
205
+ act.call(self, args)
206
+ end
207
+ end
208
+ end
209
+
210
+ # Is this task needed?
211
+ def needed?
212
+ true
213
+ end
214
+
215
+ # Timestamp for this task. Basic tasks return the current time for their
216
+ # time stamp. Other tasks can be more sophisticated.
217
+ def timestamp
218
+ prerequisite_tasks.collect { |pre| pre.timestamp }.max || Time.now
219
+ end
220
+
221
+ # Add a description to the task. The description can consist of an option
222
+ # argument list (enclosed brackets) and an optional comment.
223
+ def add_description(description)
224
+ return if ! description
225
+ comment = description.strip
226
+ add_comment(comment) if comment && ! comment.empty?
227
+ end
228
+
229
+ # Writing to the comment attribute is the same as adding a description.
230
+ def comment=(description)
231
+ add_description(description)
232
+ end
233
+
234
+ # Add a comment to the task. If a comment already exists, separate
235
+ # the new comment with " / ".
236
+ def add_comment(comment)
237
+ if @full_comment
238
+ @full_comment << " / "
239
+ else
240
+ @full_comment = ''
241
+ end
242
+ @full_comment << comment
243
+ if @full_comment =~ /\A([^.]+?\.)( |$)/
244
+ @comment = $1
245
+ else
246
+ @comment = @full_comment
247
+ end
248
+ end
249
+ private :add_comment
250
+
251
+ # Set the names of the arguments for this task. +args+ should be
252
+ # an array of symbols, one for each argument name.
253
+ def set_arg_names(args)
254
+ @arg_names = args.map { |a| a.to_sym }
255
+ end
256
+
257
+ # Return a string describing the internal state of a task. Useful for
258
+ # debugging.
259
+ def investigation
260
+ result = "------------------------------\n"
261
+ result << "Investigating #{name}\n"
262
+ result << "class: #{self.class}\n"
263
+ result << "task needed: #{needed?}\n"
264
+ result << "timestamp: #{timestamp}\n"
265
+ result << "pre-requisites: \n"
266
+ prereqs = prerequisite_tasks
267
+ prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
268
+ prereqs.each do |p|
269
+ result << "--#{p.name} (#{p.timestamp})\n"
270
+ end
271
+ latest_prereq = prerequisite_tasks.collect { |pre| pre.timestamp }.max
272
+ result << "latest-prerequisite time: #{latest_prereq}\n"
273
+ result << "................................\n\n"
274
+ return result
275
+ end
276
+
277
+ # ----------------------------------------------------------------
278
+ # Rake Module Methods
279
+ #
280
+ class << self
281
+
282
+ # Clear the task list. This cause rake to immediately forget all the
283
+ # tasks that have been assigned. (Normally used in the unit tests.)
284
+ def clear
285
+ Rake.application.clear
286
+ end
287
+
288
+ # List of all defined tasks.
289
+ def tasks
290
+ Rake.application.tasks
291
+ end
292
+
293
+ # Return a task with the given name. If the task is not currently
294
+ # known, try to synthesize one from the defined rules. If no rules are
295
+ # found, but an existing file matches the task name, assume it is a file
296
+ # task with no dependencies or actions.
297
+ def [](task_name)
298
+ Rake.application[task_name]
299
+ end
300
+
301
+ # TRUE if the task name is already defined.
302
+ def task_defined?(task_name)
303
+ Rake.application.lookup(task_name) != nil
304
+ end
305
+
306
+ # Define a task given +args+ and an option block. If a rule with the
307
+ # given name already exists, the prerequisites and actions are added to
308
+ # the existing task. Returns the defined task.
309
+ def define_task(*args, &block)
310
+ Rake.application.define_task(self, *args, &block)
311
+ end
312
+
313
+ # Define a rule for synthesizing tasks.
314
+ def create_rule(*args, &block)
315
+ Rake.application.create_rule(*args, &block)
316
+ end
317
+
318
+ # Apply the scope to the task name according to the rules for
319
+ # this kind of task. Generic tasks will accept the scope as
320
+ # part of the name.
321
+ def scope_name(scope, task_name)
322
+ (scope + [task_name]).join(':')
323
+ end
324
+
325
+ end # class << Rake::Task
326
+ end # class Rake::Task
327
+ end
@@ -0,0 +1,7 @@
1
+ module Rake
2
+
3
+ # Error indicating an ill-formed task declaration.
4
+ class TaskArgumentError < ArgumentError
5
+ end
6
+
7
+ end
@@ -0,0 +1,74 @@
1
+ module Rake
2
+
3
+ ####################################################################
4
+ # TaskArguments manage the arguments passed to a task.
5
+ #
6
+ class TaskArguments
7
+ include Enumerable
8
+
9
+ attr_reader :names
10
+
11
+ # Create a TaskArgument object with a list of named arguments
12
+ # (given by :names) and a set of associated values (given by
13
+ # :values). :parent is the parent argument object.
14
+ def initialize(names, values, parent=nil)
15
+ @names = names
16
+ @parent = parent
17
+ @hash = {}
18
+ names.each_with_index { |name, i|
19
+ @hash[name.to_sym] = values[i] unless values[i].nil?
20
+ }
21
+ end
22
+
23
+ # Create a new argument scope using the prerequisite argument
24
+ # names.
25
+ def new_scope(names)
26
+ values = names.collect { |n| self[n] }
27
+ self.class.new(names, values, self)
28
+ end
29
+
30
+ # Find an argument value by name or index.
31
+ def [](index)
32
+ lookup(index.to_sym)
33
+ end
34
+
35
+ # Specify a hash of default values for task arguments. Use the
36
+ # defaults only if there is no specific value for the given
37
+ # argument.
38
+ def with_defaults(defaults)
39
+ @hash = defaults.merge(@hash)
40
+ end
41
+
42
+ def each(&block)
43
+ @hash.each(&block)
44
+ end
45
+
46
+ def method_missing(sym, *args, &block)
47
+ lookup(sym.to_sym)
48
+ end
49
+
50
+ def to_hash
51
+ @hash
52
+ end
53
+
54
+ def to_s
55
+ @hash.inspect
56
+ end
57
+
58
+ def inspect
59
+ to_s
60
+ end
61
+
62
+ protected
63
+
64
+ def lookup(name)
65
+ if @hash.has_key?(name)
66
+ @hash[name]
67
+ elsif @parent
68
+ @parent.lookup(name)
69
+ end
70
+ end
71
+ end
72
+
73
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
74
+ end
@@ -0,0 +1,307 @@
1
+ module Rake
2
+
3
+ # The TaskManager module is a mixin for managing tasks.
4
+ module TaskManager
5
+ # Track the last comment made in the Rakefile.
6
+ attr_accessor :last_description
7
+ alias :last_comment :last_description # Backwards compatibility
8
+
9
+ def initialize
10
+ super
11
+ @tasks = Hash.new
12
+ @rules = Array.new
13
+ @scope = Array.new
14
+ @last_description = nil
15
+ end
16
+
17
+ def create_rule(*args, &block)
18
+ pattern, _, deps = resolve_args(args)
19
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
20
+ @rules << [pattern, deps, block]
21
+ end
22
+
23
+ def define_task(task_class, *args, &block)
24
+ task_name, arg_names, deps = resolve_args(args)
25
+ task_name = task_class.scope_name(@scope, task_name)
26
+ deps = [deps] unless deps.respond_to?(:to_ary)
27
+ deps = deps.collect {|d| d.to_s }
28
+ task = intern(task_class, task_name)
29
+ task.set_arg_names(arg_names) unless arg_names.empty?
30
+ if Rake::TaskManager.record_task_metadata
31
+ add_location(task)
32
+ task.add_description(get_description(task))
33
+ end
34
+ task.enhance(deps, &block)
35
+ end
36
+
37
+ # Lookup a task. Return an existing task if found, otherwise
38
+ # create a task of the current type.
39
+ def intern(task_class, task_name)
40
+ @tasks[task_name.to_s] ||= task_class.new(task_name, self)
41
+ end
42
+
43
+ # Find a matching task for +task_name+.
44
+ def [](task_name, scopes=nil)
45
+ task_name = task_name.to_s
46
+ self.lookup(task_name, scopes) or
47
+ enhance_with_matching_rule(task_name) or
48
+ synthesize_file_task(task_name) or
49
+ fail "Don't know how to build task '#{task_name}'"
50
+ end
51
+
52
+ def synthesize_file_task(task_name)
53
+ return nil unless File.exist?(task_name)
54
+ define_task(Rake::FileTask, task_name)
55
+ end
56
+
57
+ # Resolve the arguments for a task/rule. Returns a triplet of
58
+ # [task_name, arg_name_list, prerequisites].
59
+ def resolve_args(args)
60
+ if args.last.is_a?(Hash)
61
+ deps = args.pop
62
+ resolve_args_with_dependencies(args, deps)
63
+ else
64
+ resolve_args_without_dependencies(args)
65
+ end
66
+ end
67
+
68
+ # Resolve task arguments for a task or rule when there are no
69
+ # dependencies declared.
70
+ #
71
+ # The patterns recognized by this argument resolving function are:
72
+ #
73
+ # task :t
74
+ # task :t, [:a]
75
+ # task :t, :a (deprecated)
76
+ #
77
+ def resolve_args_without_dependencies(args)
78
+ task_name = args.shift
79
+ if args.size == 1 && args.first.respond_to?(:to_ary)
80
+ arg_names = args.first.to_ary
81
+ else
82
+ arg_names = args
83
+ end
84
+ [task_name, arg_names, []]
85
+ end
86
+ private :resolve_args_without_dependencies
87
+
88
+ # Resolve task arguments for a task or rule when there are
89
+ # dependencies declared.
90
+ #
91
+ # The patterns recognized by this argument resolving function are:
92
+ #
93
+ # task :t => [:d]
94
+ # task :t, [a] => [:d]
95
+ # task :t, :needs => [:d] (deprecated)
96
+ # task :t, :a, :needs => [:d] (deprecated)
97
+ #
98
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
99
+ fail "Task Argument Error" if hash.size != 1
100
+ key, value = hash.map { |k, v| [k,v] }.first
101
+ if args.empty?
102
+ task_name = key
103
+ arg_names = []
104
+ deps = value
105
+ elsif key == :needs
106
+ Rake.application.deprecate(
107
+ "task :t, arg, :needs => [deps]",
108
+ "task :t, [args] => [deps]",
109
+ caller.detect { |c| c !~ /\blib\/rake\b/ })
110
+ task_name = args.shift
111
+ arg_names = args
112
+ deps = value
113
+ else
114
+ task_name = args.shift
115
+ arg_names = key
116
+ deps = value
117
+ end
118
+ deps = [deps] unless deps.respond_to?(:to_ary)
119
+ [task_name, arg_names, deps]
120
+ end
121
+ private :resolve_args_with_dependencies
122
+
123
+ # If a rule can be found that matches the task name, enhance the
124
+ # task with the prerequisites and actions from the rule. Set the
125
+ # source attribute of the task appropriately for the rule. Return
126
+ # the enhanced task or nil of no rule was found.
127
+ def enhance_with_matching_rule(task_name, level=0)
128
+ fail Rake::RuleRecursionOverflowError,
129
+ "Rule Recursion Too Deep" if level >= 16
130
+ @rules.each do |pattern, extensions, block|
131
+ if pattern.match(task_name)
132
+ task = attempt_rule(task_name, extensions, block, level)
133
+ return task if task
134
+ end
135
+ end
136
+ nil
137
+ rescue Rake::RuleRecursionOverflowError => ex
138
+ ex.add_target(task_name)
139
+ fail ex
140
+ end
141
+
142
+ # List of all defined tasks in this application.
143
+ def tasks
144
+ @tasks.values.sort_by { |t| t.name }
145
+ end
146
+
147
+ # List of all the tasks defined in the given scope (and its
148
+ # sub-scopes).
149
+ def tasks_in_scope(scope)
150
+ prefix = scope.join(":")
151
+ tasks.select { |t|
152
+ /^#{prefix}:/ =~ t.name
153
+ }
154
+ end
155
+
156
+ # Clear all tasks in this application.
157
+ def clear
158
+ @tasks.clear
159
+ @rules.clear
160
+ end
161
+
162
+ # Lookup a task, using scope and the scope hints in the task name.
163
+ # This method performs straight lookups without trying to
164
+ # synthesize file tasks or rules. Special scope names (e.g. '^')
165
+ # are recognized. If no scope argument is supplied, use the
166
+ # current scope. Return nil if the task cannot be found.
167
+ def lookup(task_name, initial_scope=nil)
168
+ initial_scope ||= @scope
169
+ task_name = task_name.to_s
170
+ if task_name =~ /^rake:/
171
+ scopes = []
172
+ task_name = task_name.sub(/^rake:/, '')
173
+ elsif task_name =~ /^(\^+)/
174
+ scopes = initial_scope[0, initial_scope.size - $1.size]
175
+ task_name = task_name.sub(/^(\^+)/, '')
176
+ else
177
+ scopes = initial_scope
178
+ end
179
+ lookup_in_scope(task_name, scopes)
180
+ end
181
+
182
+ # Lookup the task name
183
+ def lookup_in_scope(name, scope)
184
+ n = scope.size
185
+ while n >= 0
186
+ tn = (scope[0,n] + [name]).join(':')
187
+ task = @tasks[tn]
188
+ return task if task
189
+ n -= 1
190
+ end
191
+ nil
192
+ end
193
+ private :lookup_in_scope
194
+
195
+ # Return the list of scope names currently active in the task
196
+ # manager.
197
+ def current_scope
198
+ @scope.dup
199
+ end
200
+
201
+ # Evaluate the block in a nested namespace named +name+. Create
202
+ # an anonymous namespace if +name+ is nil.
203
+ def in_namespace(name)
204
+ name ||= generate_name
205
+ @scope.push(name)
206
+ ns = NameSpace.new(self, @scope)
207
+ yield(ns)
208
+ ns
209
+ ensure
210
+ @scope.pop
211
+ end
212
+
213
+ private
214
+
215
+ # Add a location to the locations field of the given task.
216
+ def add_location(task)
217
+ loc = find_location
218
+ task.locations << loc if loc
219
+ task
220
+ end
221
+
222
+ # Find the location that called into the dsl layer.
223
+ def find_location
224
+ locations = caller
225
+ i = 0
226
+ while locations[i]
227
+ return locations[i+1] if locations[i] =~ /rake\/dsl_definition.rb/
228
+ i += 1
229
+ end
230
+ nil
231
+ end
232
+
233
+ # Generate an anonymous namespace name.
234
+ def generate_name
235
+ @seed ||= 0
236
+ @seed += 1
237
+ "_anon_#{@seed}"
238
+ end
239
+
240
+ def trace_rule(level, message)
241
+ $stderr.puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
242
+ end
243
+
244
+ # Attempt to create a rule given the list of prerequisites.
245
+ def attempt_rule(task_name, extensions, block, level)
246
+ sources = make_sources(task_name, extensions)
247
+ prereqs = sources.collect { |source|
248
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
249
+ if File.exist?(source) || Rake::Task.task_defined?(source)
250
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
251
+ source
252
+ elsif parent = enhance_with_matching_rule(source, level+1)
253
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
254
+ parent.name
255
+ else
256
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
257
+ return nil
258
+ end
259
+ }
260
+ task = FileTask.define_task({task_name => prereqs}, &block)
261
+ task.sources = prereqs
262
+ task
263
+ end
264
+
265
+ # Make a list of sources from the list of file name extensions /
266
+ # translation procs.
267
+ def make_sources(task_name, extensions)
268
+ result = extensions.collect { |ext|
269
+ case ext
270
+ when /%/
271
+ task_name.pathmap(ext)
272
+ when %r{/}
273
+ ext
274
+ when /^\./
275
+ task_name.ext(ext)
276
+ when String
277
+ ext
278
+ when Proc
279
+ if ext.arity == 1
280
+ ext.call(task_name)
281
+ else
282
+ ext.call
283
+ end
284
+ else
285
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
286
+ end
287
+ }
288
+ result.flatten
289
+ end
290
+
291
+
292
+ private
293
+
294
+ # Return the current description, clearing it in the process.
295
+ def get_description(task)
296
+ desc = @last_description
297
+ @last_description = nil
298
+ desc
299
+ end
300
+
301
+ class << self
302
+ attr_accessor :record_task_metadata
303
+ TaskManager.record_task_metadata = false
304
+ end
305
+ end
306
+
307
+ end