sprout 0.7.153-darwin

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sprout might be problematic. Click here for more details.

@@ -0,0 +1,541 @@
1
+
2
+ module Sprout
3
+
4
+ class ToolTaskError < StandardError #:nodoc:
5
+ end
6
+
7
+ # The ToolTask provides some base functionality for any Command Line Interface (CLI) tool.
8
+ # It also provides support for GUI tools that you would like to expose from the
9
+ # Command Line (Like the Flash Player for example).
10
+ #
11
+ # ToolTask extends Rake::FileTask, and should be thought of in the same way.
12
+ # Martin Fowler did a much better job of describing Rake and specifically FileTasks than
13
+ # I can in his (now classic) Rake article[http://martinfowler.com/articles/rake.html#FileTasks] from 2005.
14
+ #
15
+ # What this means is that most tool task instances should be named for the file that they will create.
16
+ # For example, an Sprout::MXMLCTask instance should be named for the SWF that it will generate.
17
+ #
18
+ # mxmlc 'bin/SomeProject.swf' => :corelib do |t|
19
+ # t.input = 'src/SomeProject.as'
20
+ # t.default_size = '800 600'
21
+ # end
22
+ #
23
+ # In general, a tool task will only be executed if it's output file (name) does not exist or
24
+ # if the output file is older than any file identified as a prerequisite.
25
+ #
26
+ # Many of the compiler tasks take advantage of this feature by opting out of unnecessary compilation.
27
+ #
28
+ # Subclasses can add and configure command line parameters by calling the protected add_param method
29
+ # that is implemented on this class.
30
+ #
31
+ class ToolTask < Rake::FileTask
32
+
33
+ def initialize(name, app) # :nodoc:
34
+ super
35
+ @default_gem_name = nil
36
+ @default_gem_path = nil
37
+ initialize_task
38
+ end
39
+
40
+ def self.define_task(args, &block)
41
+ t = super
42
+ if(t.is_a?(ToolTask))
43
+ yield t if block_given?
44
+ t.define
45
+ t.prepare
46
+ end
47
+ return t
48
+ end
49
+
50
+ # Full name of the sprout tool gem that this tool task will use. For example, the MXMLCTask
51
+ # uses the sprout-flex2sdk-tool at the time of this writing, but will at some point
52
+ # change to use the sprout-flex3sdk-tool. You can combine this value with gem_version
53
+ # in order to specify exactly which gem your tool task is executing.
54
+ def gem_name
55
+ return @gem_name ||= @default_gem_name
56
+ end
57
+
58
+ def gem_name=(name)
59
+ @gem_name = name
60
+ end
61
+
62
+ # The exact gem version that you would like the ToolTask to execute. By default this value
63
+ # should be nil and will download the latest version of the gem that is available unless
64
+ # there is a version already installed on your system.
65
+ #
66
+ # This attribute could be an easy
67
+ # way to update your local gem to the latest version without leaving your build file,
68
+ # but it's primary purpose is to allow you to specify very specific versions of the tools
69
+ # that your project depends on. This way your team can rest assured that they are all
70
+ # working with the same tools.
71
+ def gem_version
72
+ return @gem_version ||= nil
73
+ end
74
+
75
+ def gem_version=(version)
76
+ @gem_version = version
77
+ end
78
+
79
+ # The path inside the installed gem where an executable can be found. For the MXMLCTask, this
80
+ # value is 'bin/mxmlc'.
81
+ def gem_path
82
+ return @gem_path ||= @default_gem_path
83
+ end
84
+
85
+ # Create a string that can be turned into a file
86
+ # that rdoc can parse to describe the customized
87
+ # or generated task using param name, type and
88
+ # description
89
+ def to_rdoc
90
+ result = ''
91
+ parts = self.class.to_s.split('::')
92
+ class_name = parts.pop
93
+ module_count = 0
94
+ while(module_name = parts.shift)
95
+ result << "module #{module_name}\n"
96
+ module_count += 1
97
+ end
98
+
99
+ result << "class #{class_name} < ToolTask\n"
100
+
101
+ params.each do |param|
102
+ result << param.to_rdoc
103
+ end
104
+
105
+ while((module_count -= 1) >= 0)
106
+ result << "end\nend\n"
107
+ end
108
+
109
+ return result
110
+ end
111
+
112
+ def execute(*args)
113
+ #puts ">> Executing #{File.basename(exe)} #{to_shell}"
114
+ exe = Sprout.get_executable(gem_name, gem_path, gem_version)
115
+ User.execute(exe, to_shell)
116
+ end
117
+
118
+ # Create a string that represents this configured tool for shell execution
119
+ def to_shell
120
+ result = []
121
+ params.each do |param|
122
+ if(param.visible?)
123
+ result << param.to_shell
124
+ end
125
+ end
126
+ return result.join(' ')
127
+ end
128
+
129
+ # An Array of all parameters that have been added to this Tool.
130
+ def params
131
+ @params ||= []
132
+ end
133
+
134
+ # Called after initialize and define, usually subclasses should
135
+ # only override define.
136
+ def prepare
137
+ # Get each added param to inject prerequisites as necessary
138
+ params.each do |param|
139
+ param.prepare_prerequisites
140
+ end
141
+ # Ensure there are no duplicates in the prerequisite collection
142
+ @prerequisites = prerequisites.uniq
143
+ end
144
+
145
+ def define
146
+ resolve_libraries(prerequisites)
147
+ end
148
+
149
+ # The default file expression to append to each PathParam
150
+ # in order to build file change prerequisites.
151
+ #
152
+ # Defaults to '/**/**/*'
153
+ #
154
+ def default_file_expression
155
+ @default_file_expression ||= '/**/**/*'
156
+ end
157
+
158
+ protected
159
+
160
+ def initialize_task
161
+ end
162
+
163
+ def validate
164
+ params.each do |param|
165
+ param.validate
166
+ end
167
+ end
168
+
169
+ # +add_param+ is the workhorse of the ToolTask.
170
+ # This method is used to add new shell parameters to the task.
171
+ # +name+ is a symbol or string that represents the parameter that you would like to add
172
+ # such as :debug or :source_path.
173
+ # +type+ is usually sent as a Ruby symbol and can be one of the following:
174
+ #
175
+ # [:string] Any string value
176
+ # [:boolean] true or false
177
+ # [:number] Any number
178
+ # [:file] Path to a file
179
+ # [:url] Basic URL
180
+ # [:path] Path to a directory
181
+ # [:files] Collection of files
182
+ # [:paths] Collection of directories
183
+ # [:strings] Collection of arbitrary strings
184
+ # [:urls] Collection of URLs
185
+ #
186
+ # Be sure to check out the Sprout::TaskParam class to learn more about
187
+ # block editing the parameters.
188
+ #
189
+ # Once parameters have been added using the +add_param+ method, clients
190
+ # can set and get those parameters from the newly created task.
191
+ #
192
+ def add_param(name, type, &block) # :yields: Sprout::TaskParam
193
+ name = name.to_s
194
+
195
+ # First ensure the named accessor doesn't yet exist...
196
+ if(param_hash[name])
197
+ raise TaskBaseError.new("TaskBase.add_param called with existing parameter name: #{name}")
198
+ end
199
+
200
+ param = create_param(type)
201
+ param.init do |p|
202
+ p.belongs_to = self
203
+ p.name = name
204
+ p.type = type
205
+ yield p if block_given?
206
+ end
207
+
208
+ param_hash[name] = param
209
+ params << param
210
+ end
211
+
212
+ # Alias an existing parameter with another name. For example, the
213
+ # existing parameter :source_path might be given an alias '-sp' as follows:
214
+ #
215
+ # add_param_alias(:sp, :source_path)
216
+ #
217
+ # Alias parameters cannot be configured differently from the parameter
218
+ # that they alias
219
+ #
220
+ def add_param_alias(name, other_param)
221
+ if(param_hash.has_key? other_param.to_s)
222
+ param_hash[name.to_s] = param_hash[other_param.to_s]
223
+ else
224
+ raise TaskBaseError.new("TaskBase.add_param_alis called with")
225
+ end
226
+ end
227
+
228
+ protected
229
+
230
+ def create_param(type)
231
+ return eval("#{type.to_s.capitalize}Param.new")
232
+ end
233
+
234
+ def param_hash
235
+ @param_hash ||= {}
236
+ end
237
+
238
+ def respond_to?(name)
239
+ result = super
240
+ if(!result)
241
+ result = param_hash.has_key? name
242
+ end
243
+ return result
244
+ end
245
+
246
+ def clean_name(name)
247
+ name.gsub(/=$/, '')
248
+ end
249
+
250
+ def method_missing(name,*args)
251
+ name = name.to_s
252
+ cleaned = clean_name(name)
253
+ if(!respond_to?(cleaned))
254
+ raise NoMethodError.new("undefined method '#{name}' for #{self.class}")
255
+ end
256
+ param = param_hash[cleaned]
257
+
258
+ if(name =~ /=$/)
259
+ param.value = args.shift
260
+ elsif(param)
261
+ param.value
262
+ else
263
+ raise ToolTaskError.new("method_missing called with undefined parameter [#{name}]")
264
+ end
265
+ end
266
+
267
+ # Iterate over all prerequisites looking for any
268
+ # that are a LibraryTask.
269
+ # Concrete ToolTask implementations should
270
+ # override resolve_library in order to add
271
+ # the library sources or binaries appropriately.
272
+ def resolve_libraries(prerequisites)
273
+ prerequisites.each do |prereq|
274
+ instance = Rake::application[prereq]
275
+ if(instance.is_a?(LibraryTask))
276
+ resolve_library(instance)
277
+ end
278
+ end
279
+ end
280
+
281
+ # Concrete ToolTasks should override this method
282
+ # and add any dependent libraries appropriately
283
+ def resolve_library(library_task)
284
+ end
285
+
286
+ # If the provided path contains spaces, wrap it in quotes so that
287
+ # shell tools won't choke on the spaces
288
+ def clean_path(path)
289
+ if(path.index(' '))
290
+ path = %{"#{path}"}
291
+ end
292
+ return path
293
+ end
294
+ end
295
+
296
+ #######################################################
297
+ # Parameter Implementations
298
+
299
+ # The base class for all ToolTask parameters. This class is extended by a variety
300
+ # of concrete implementations.
301
+ #
302
+ # At the time of this writing, only the :boolean TaskParam modifies the interface by
303
+ # adding the +show_on_false+ attribute.
304
+ #
305
+ # Some other helpful features are as follows:
306
+ #
307
+ # :file, :files, :path and :paths will all add any items that have been added to
308
+ # their values as file task prerequisites. This is especially helpful when writing
309
+ # rake tasks for Command Line Interface (CLI) compilers.
310
+ #
311
+ class TaskParam
312
+ attr_accessor :name
313
+ attr_accessor :type
314
+ attr_accessor :validator
315
+ attr_accessor :description
316
+ attr_accessor :visible
317
+ attr_accessor :hidden_name
318
+ attr_accessor :hidden_value
319
+ attr_accessor :required
320
+ attr_accessor :belongs_to
321
+
322
+ attr_writer :prefix
323
+ attr_writer :value
324
+ attr_writer :delimiter
325
+
326
+ # Set the file_expression (blob) to append to each path
327
+ # in order to build the prerequisites FileList.
328
+ #
329
+ # Defaults to parent ToolTask.default_file_expression
330
+ attr_writer :file_expression
331
+
332
+ def init
333
+ yield self if block_given?
334
+ end
335
+
336
+ # By default, ToolParams only appear in the shell
337
+ # output when they are not nil
338
+ def visible?
339
+ @visible ||= value
340
+ end
341
+
342
+ def required?
343
+ (required == true)
344
+ end
345
+
346
+ def validate
347
+ if(required? && !visible?)
348
+ raise TaskBaseError.new("#{name} is required and must not be nil")
349
+ end
350
+ end
351
+
352
+ def prepare_prerequisites
353
+ end
354
+
355
+ # Should the param name be hidden from the shell?
356
+ # Used for params like 'input' on mxmlc
357
+ def hidden_name?
358
+ @hidden_name ||= false
359
+ end
360
+
361
+ # Should the param value be hidden from the shell?
362
+ # Usually used for Boolean toggles like '-debug'
363
+ def hidden_value?
364
+ @hidden_value ||= false
365
+ end
366
+
367
+ # Leading character for each parameter
368
+ # Can sometimes be an empty string,
369
+ # other times it's a double dash '--'
370
+ # but usually it's just a single dash '-'
371
+ def prefix
372
+ @prefix ||= '-'
373
+ end
374
+
375
+ def value
376
+ @value ||= nil
377
+ end
378
+
379
+ def shell_value
380
+ value.to_s
381
+ end
382
+
383
+ def file_expression # :nodoc:
384
+ @file_expression ||= belongs_to.default_file_expression
385
+ end
386
+
387
+ # ToolParams join their name/value pair with an
388
+ # equals sign by default, this can be modified
389
+ # To a space or whatever you wish
390
+ def delimiter
391
+ @delimiter ||= '='
392
+ end
393
+
394
+ # Return the name with a single leading dash
395
+ # and underscores replaced with dashes
396
+ def shell_name
397
+ @shell_name ||= prefix + name.split('_').join('-')
398
+ end
399
+
400
+ def to_shell
401
+ if(hidden_name?)
402
+ return shell_value
403
+ elsif(hidden_value?)
404
+ return shell_name
405
+ else
406
+ return "#{shell_name}#{delimiter}#{shell_value}"
407
+ end
408
+ end
409
+
410
+ # Create a string that can be turned into a file
411
+ # that rdoc can parse to describe the customized
412
+ # or generated task using param name, type and
413
+ # description
414
+ def to_rdoc
415
+ result = ''
416
+ parts = description.split("\n") unless description.nil?
417
+ result << "# #{parts.join("\n# ")}\n" unless description.nil?
418
+ result << "def #{name}=(#{type})\n @#{name} = #{type}\nend\n\n"
419
+ return result
420
+ end
421
+
422
+ end
423
+
424
+ # Concrete param object for :string values
425
+ class StringParam < TaskParam # :nodoc:
426
+ end
427
+
428
+ # Concrete param object for :symbol values
429
+ class SymbolParam < TaskParam # :nodoc:
430
+ end
431
+
432
+ # Concrete param object for :url values
433
+ class UrlParam < TaskParam # :nodoc:
434
+ end
435
+
436
+ # Concrete param object for :number values
437
+ class NumberParam < TaskParam # :nodoc:
438
+ end
439
+
440
+ # Concrete param object for :file values
441
+ class FileParam < TaskParam # :nodoc:
442
+ def prepare_prerequisites
443
+ if(value && value != belongs_to.name.to_s)
444
+ file value
445
+ belongs_to.prerequisites << value
446
+ end
447
+ end
448
+ end
449
+
450
+ # Concrete param object for :path values
451
+ class PathParam < FileParam # :nodoc:
452
+ end
453
+
454
+ # Concrete param object for :boolean values
455
+ class BooleanParam < TaskParam # :nodoc:
456
+ attr_writer :show_on_false
457
+
458
+ def visible?
459
+ @visible ||= value
460
+ if(show_on_false)
461
+ return true unless value
462
+ else
463
+ return @visible
464
+ end
465
+ end
466
+
467
+ def show_on_false
468
+ @show_on_false ||= false
469
+ end
470
+
471
+ def value
472
+ @value ||= false
473
+ end
474
+
475
+ end
476
+
477
+ # Concrete param object for collections of strings
478
+ class StringsParam < TaskParam # :nodoc:
479
+
480
+ # Files lists are initialized to an empty array by default
481
+ def value
482
+ @value ||= []
483
+ end
484
+
485
+ # By default, the FilesParams will not appear in the shell
486
+ # output if there are zero items in the collection
487
+ def visible?
488
+ @visible ||= (value && value.size > 0)
489
+ end
490
+
491
+ # Default delimiter is +=
492
+ # This is what will appear between each name/value pair as in:
493
+ # "source_path+=src source_path+=test source_path+=lib"
494
+ def delimiter
495
+ @delimiter ||= "+="
496
+ end
497
+
498
+ # Returns a shell formatted string of the collection
499
+ def to_shell
500
+ result = []
501
+ value.each do |str|
502
+ result << "#{shell_name}#{delimiter}#{str}"
503
+ end
504
+ return result.join(' ')
505
+ end
506
+ end
507
+
508
+ # Concrete param object for collections of symbols (like class names)
509
+ class SymbolsParam < StringsParam # :nodoc:
510
+ end
511
+
512
+ # Concrete param object for collections of files
513
+ class FilesParam < StringsParam # :nodoc:
514
+
515
+ def prepare_prerequisites
516
+ value.each do |f|
517
+ file f
518
+ belongs_to.prerequisites << f
519
+ end
520
+ end
521
+ end
522
+
523
+ # Concrete param object for collections of paths
524
+ class PathsParam < StringsParam # :nodoc:
525
+
526
+ def prepare_prerequisites
527
+ value.each do |f|
528
+ files = FileList[f + file_expression]
529
+ files.each do |req_file|
530
+ file req_file
531
+ belongs_to.prerequisites << req_file
532
+ end
533
+ end
534
+ end
535
+ end
536
+
537
+ # Concrete param object for collections of files
538
+ class UrlsParam < StringsParam # :nodoc:
539
+ end
540
+
541
+ end