sprout 0.7.153-darwin

Sign up to get free protection for your applications and to get access to all the features.
@@ -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