jimweirich-rake 0.8.4.99 → 0.8.5

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.
@@ -0,0 +1,533 @@
1
+ require 'rake/task_manager'
2
+ require 'rake/win32'
3
+
4
+ module Rake
5
+
6
+ ######################################################################
7
+ # Rake main application object. When invoking +rake+ from the
8
+ # command line, a Rake::Application object is created and run.
9
+ #
10
+ class Application
11
+ include TaskManager
12
+
13
+ # The name of the application (typically 'rake')
14
+ attr_reader :name
15
+
16
+ # The original directory where rake was invoked.
17
+ attr_reader :original_dir
18
+
19
+ # Name of the actual rakefile used.
20
+ attr_reader :rakefile
21
+
22
+ # List of the top level task names (task names from the command line).
23
+ attr_reader :top_level_tasks
24
+
25
+ DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
26
+
27
+ # Initialize a Rake::Application object.
28
+ def initialize
29
+ super
30
+ @name = 'rake'
31
+ @rakefiles = DEFAULT_RAKEFILES.dup
32
+ @rakefile = nil
33
+ @pending_imports = []
34
+ @imported = []
35
+ @loaders = {}
36
+ @default_loader = Rake::DefaultLoader.new
37
+ @original_dir = Dir.pwd
38
+ @top_level_tasks = []
39
+ add_loader('rb', DefaultLoader.new)
40
+ add_loader('rf', DefaultLoader.new)
41
+ add_loader('rake', DefaultLoader.new)
42
+ @tty_output = STDOUT.tty?
43
+ end
44
+
45
+ # Run the Rake application. The run method performs the following three steps:
46
+ #
47
+ # * Initialize the command line options (+init+).
48
+ # * Define the tasks (+load_rakefile+).
49
+ # * Run the top level tasks (+run_tasks+).
50
+ #
51
+ # If you wish to build a custom rake command, you should call +init+ on your
52
+ # application. The define any tasks. Finally, call +top_level+ to run your top
53
+ # level tasks.
54
+ def run
55
+ standard_exception_handling do
56
+ init
57
+ load_rakefile
58
+ top_level
59
+ end
60
+ end
61
+
62
+ # Initialize the command line parameters and app name.
63
+ def init(app_name='rake')
64
+ standard_exception_handling do
65
+ @name = app_name
66
+ handle_options
67
+ collect_tasks
68
+ end
69
+ end
70
+
71
+ # Find the rakefile and then load it and any pending imports.
72
+ def load_rakefile
73
+ standard_exception_handling do
74
+ raw_load_rakefile
75
+ end
76
+ end
77
+
78
+ # Run the top level tasks of a Rake application.
79
+ def top_level
80
+ standard_exception_handling do
81
+ if options.show_tasks
82
+ display_tasks_and_comments
83
+ elsif options.show_prereqs
84
+ display_prerequisites
85
+ else
86
+ top_level_tasks.each { |task_name| invoke_task(task_name) }
87
+ end
88
+ end
89
+ end
90
+
91
+ # Add a loader to handle imported files ending in the extension
92
+ # +ext+.
93
+ def add_loader(ext, loader)
94
+ ext = ".#{ext}" unless ext =~ /^\./
95
+ @loaders[ext] = loader
96
+ end
97
+
98
+ # Application options from the command line
99
+ def options
100
+ @options ||= OpenStruct.new
101
+ end
102
+
103
+ # private ----------------------------------------------------------------
104
+
105
+ def invoke_task(task_string)
106
+ name, args = parse_task_string(task_string)
107
+ t = self[name]
108
+ t.invoke(*args)
109
+ end
110
+
111
+ def parse_task_string(string)
112
+ if string =~ /^([^\[]+)(\[(.*)\])$/
113
+ name = $1
114
+ args = $3.split(/\s*,\s*/)
115
+ else
116
+ name = string
117
+ args = []
118
+ end
119
+ [name, args]
120
+ end
121
+
122
+ # Provide standard execption handling for the given block.
123
+ def standard_exception_handling
124
+ begin
125
+ yield
126
+ rescue SystemExit => ex
127
+ # Exit silently with current status
128
+ raise
129
+ rescue OptionParser::InvalidOption => ex
130
+ # Exit silently
131
+ exit(false)
132
+ rescue Exception => ex
133
+ # Exit with error message
134
+ $stderr.puts "#{name} aborted!"
135
+ $stderr.puts ex.message
136
+ if options.trace
137
+ $stderr.puts ex.backtrace.join("\n")
138
+ else
139
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
140
+ $stderr.puts "(See full trace by running task with --trace)"
141
+ end
142
+ exit(false)
143
+ end
144
+ end
145
+
146
+ # True if one of the files in RAKEFILES is in the current directory.
147
+ # If a match is found, it is copied into @rakefile.
148
+ def have_rakefile
149
+ @rakefiles.each do |fn|
150
+ if File.exist?(fn)
151
+ others = Dir.glob(fn, File::FNM_CASEFOLD)
152
+ return others.size == 1 ? others.first : fn
153
+ elsif fn == ''
154
+ return fn
155
+ end
156
+ end
157
+ return nil
158
+ end
159
+
160
+ # True if we are outputting to TTY, false otherwise
161
+ def tty_output?
162
+ @tty_output
163
+ end
164
+
165
+ # Override the detected TTY output state (mostly for testing)
166
+ def tty_output=( tty_output_state )
167
+ @tty_output = tty_output_state
168
+ end
169
+
170
+ # We will truncate output if we are outputting to a TTY or if we've been
171
+ # given an explicit column width to honor
172
+ def truncate_output?
173
+ tty_output? || ENV['RAKE_COLUMNS']
174
+ end
175
+
176
+ # Display the tasks and comments.
177
+ def display_tasks_and_comments
178
+ displayable_tasks = tasks.select { |t|
179
+ t.comment && t.name =~ options.show_task_pattern
180
+ }
181
+ if options.full_description
182
+ displayable_tasks.each do |t|
183
+ puts "#{name} #{t.name_with_args}"
184
+ t.full_comment.split("\n").each do |line|
185
+ puts " #{line}"
186
+ end
187
+ puts
188
+ end
189
+ else
190
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
191
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
192
+ displayable_tasks.each do |t|
193
+ printf "#{name} %-#{width}s # %s\n",
194
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
195
+ end
196
+ end
197
+ end
198
+
199
+ def terminal_width
200
+ if ENV['RAKE_COLUMNS']
201
+ result = ENV['RAKE_COLUMNS'].to_i
202
+ else
203
+ result = unix? ? dynamic_width : 80
204
+ end
205
+ (result < 10) ? 80 : result
206
+ rescue
207
+ 80
208
+ end
209
+
210
+ # Calculate the dynamic width of the
211
+ def dynamic_width
212
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
213
+ end
214
+
215
+ def dynamic_width_stty
216
+ %x{stty size 2>/dev/null}.split[1].to_i
217
+ end
218
+
219
+ def dynamic_width_tput
220
+ %x{tput cols 2>/dev/null}.to_i
221
+ end
222
+
223
+ def unix?
224
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
225
+ end
226
+
227
+ def windows?
228
+ Win32.windows?
229
+ end
230
+
231
+ def truncate(string, width)
232
+ if string.length <= width
233
+ string
234
+ else
235
+ ( string[0, width-3] || "" ) + "..."
236
+ end
237
+ end
238
+
239
+ # Display the tasks and prerequisites
240
+ def display_prerequisites
241
+ tasks.each do |t|
242
+ puts "#{name} #{t.name}"
243
+ t.prerequisites.each { |pre| puts " #{pre}" }
244
+ end
245
+ end
246
+
247
+ # A list of all the standard options used in rake, suitable for
248
+ # passing to OptionParser.
249
+ def standard_rake_options
250
+ [
251
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
252
+ lambda { |value|
253
+ require 'rake/classic_namespace'
254
+ options.classic_namespace = true
255
+ }
256
+ ],
257
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
258
+ lambda { |value|
259
+ options.show_tasks = true
260
+ options.full_description = true
261
+ options.show_task_pattern = Regexp.new(value || '')
262
+ }
263
+ ],
264
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
265
+ lambda { |value|
266
+ verbose(true)
267
+ nowrite(true)
268
+ options.dryrun = true
269
+ options.trace = true
270
+ }
271
+ ],
272
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
273
+ lambda { |value|
274
+ eval(value)
275
+ exit
276
+ }
277
+ ],
278
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
279
+ lambda { |value|
280
+ puts eval(value)
281
+ exit
282
+ }
283
+ ],
284
+ ['--execute-continue', '-E CODE',
285
+ "Execute some Ruby code, then continue with normal task processing.",
286
+ lambda { |value| eval(value) }
287
+ ],
288
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
289
+ lambda { |value| $:.push(value) }
290
+ ],
291
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
292
+ lambda { |value| options.show_prereqs = true }
293
+ ],
294
+ ['--quiet', '-q', "Do not log messages to standard output.",
295
+ lambda { |value| verbose(false) }
296
+ ],
297
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
298
+ lambda { |value|
299
+ value ||= ''
300
+ @rakefiles.clear
301
+ @rakefiles << value
302
+ }
303
+ ],
304
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
305
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
306
+ lambda { |value| options.rakelib = value.split(':') }
307
+ ],
308
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
309
+ lambda { |value|
310
+ begin
311
+ require value
312
+ rescue LoadError => ex
313
+ begin
314
+ rake_require value
315
+ rescue LoadError => ex2
316
+ raise ex
317
+ end
318
+ end
319
+ }
320
+ ],
321
+ ['--rules', "Trace the rules resolution.",
322
+ lambda { |value| options.trace_rules = true }
323
+ ],
324
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
325
+ lambda { |value| options.nosearch = true }
326
+ ],
327
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
328
+ lambda { |value|
329
+ verbose(false)
330
+ options.silent = true
331
+ }
332
+ ],
333
+ ['--system', '-g',
334
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
335
+ lambda { |value| options.load_system = true }
336
+ ],
337
+ ['--no-system', '--nosystem', '-G',
338
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
339
+ lambda { |value| options.ignore_system = true }
340
+ ],
341
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
342
+ lambda { |value|
343
+ options.show_tasks = true
344
+ options.show_task_pattern = Regexp.new(value || '')
345
+ options.full_description = false
346
+ }
347
+ ],
348
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
349
+ lambda { |value|
350
+ options.trace = true
351
+ verbose(true)
352
+ }
353
+ ],
354
+ ['--verbose', '-v', "Log message to standard output.",
355
+ lambda { |value| verbose(true) }
356
+ ],
357
+ ['--version', '-V', "Display the program version.",
358
+ lambda { |value|
359
+ puts "rake, version #{RAKEVERSION}"
360
+ exit
361
+ }
362
+ ]
363
+ ]
364
+ end
365
+
366
+ # Read and handle the command line options.
367
+ def handle_options
368
+ options.rakelib = ['rakelib']
369
+
370
+ OptionParser.new do |opts|
371
+ opts.banner = "rake [-f rakefile] {options} targets..."
372
+ opts.separator ""
373
+ opts.separator "Options are ..."
374
+
375
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
376
+ puts opts
377
+ exit
378
+ end
379
+
380
+ standard_rake_options.each { |args| opts.on(*args) }
381
+ end.parse!
382
+
383
+ # If class namespaces are requested, set the global options
384
+ # according to the values in the options structure.
385
+ if options.classic_namespace
386
+ $show_tasks = options.show_tasks
387
+ $show_prereqs = options.show_prereqs
388
+ $trace = options.trace
389
+ $dryrun = options.dryrun
390
+ $silent = options.silent
391
+ end
392
+ end
393
+
394
+ # Similar to the regular Ruby +require+ command, but will check
395
+ # for *.rake files in addition to *.rb files.
396
+ def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
397
+ return false if loaded.include?(file_name)
398
+ paths.each do |path|
399
+ fn = file_name + ".rake"
400
+ full_path = File.join(path, fn)
401
+ if File.exist?(full_path)
402
+ load full_path
403
+ loaded << fn
404
+ return true
405
+ end
406
+ end
407
+ fail LoadError, "Can't find #{file_name}"
408
+ end
409
+
410
+ def find_rakefile_location
411
+ here = Dir.pwd
412
+ while ! (fn = have_rakefile)
413
+ Dir.chdir("..")
414
+ if Dir.pwd == here || options.nosearch
415
+ return nil
416
+ end
417
+ here = Dir.pwd
418
+ end
419
+ [fn, here]
420
+ ensure
421
+ Dir.chdir(Rake.original_dir)
422
+ end
423
+
424
+ def raw_load_rakefile # :nodoc:
425
+ rakefile, location = find_rakefile_location
426
+ if (! options.ignore_system) &&
427
+ (options.load_system || rakefile.nil?) &&
428
+ system_dir && File.directory?(system_dir)
429
+ puts "(in #{Dir.pwd})" unless options.silent
430
+ glob("#{system_dir}/*.rake") do |name|
431
+ add_import name
432
+ end
433
+ else
434
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
435
+ rakefile.nil?
436
+ @rakefile = rakefile
437
+ Dir.chdir(location)
438
+ puts "(in #{Dir.pwd})" unless options.silent
439
+ $rakefile = @rakefile if options.classic_namespace
440
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
441
+ options.rakelib.each do |rlib|
442
+ glob("#{rlib}/*.rake") do |name|
443
+ add_import name
444
+ end
445
+ end
446
+ end
447
+ load_imports
448
+ end
449
+
450
+ def glob(path, &block)
451
+ Dir[path.gsub("\\", '/')].each(&block)
452
+ end
453
+ private :glob
454
+
455
+ # The directory path containing the system wide rakefiles.
456
+ def system_dir
457
+ @system_dir ||=
458
+ begin
459
+ if ENV['RAKE_SYSTEM']
460
+ ENV['RAKE_SYSTEM']
461
+ else
462
+ standard_system_dir
463
+ end
464
+ end
465
+ end
466
+
467
+ # The standard directory containing system wide rake files.
468
+ if Win32.windows?
469
+ def standard_system_dir #:nodoc:
470
+ Win32.win32_system_dir
471
+ end
472
+ else
473
+ def standard_system_dir #:nodoc:
474
+ File.join(File.expand_path('~'), '.rake')
475
+ end
476
+ end
477
+ private :standard_system_dir
478
+
479
+ # Collect the list of tasks on the command line. If no tasks are
480
+ # given, return a list containing only the default task.
481
+ # Environmental assignments are processed at this time as well.
482
+ def collect_tasks
483
+ @top_level_tasks = []
484
+ ARGV.each do |arg|
485
+ if arg =~ /^(\w+)=(.*)$/
486
+ ENV[$1] = $2
487
+ else
488
+ @top_level_tasks << arg unless arg =~ /^-/
489
+ end
490
+ end
491
+ @top_level_tasks.push("default") if @top_level_tasks.size == 0
492
+ end
493
+
494
+ # Add a file to the list of files to be imported.
495
+ def add_import(fn)
496
+ @pending_imports << fn
497
+ end
498
+
499
+ # Load the pending list of imported files.
500
+ def load_imports
501
+ while fn = @pending_imports.shift
502
+ next if @imported.member?(fn)
503
+ if fn_task = lookup(fn)
504
+ fn_task.invoke
505
+ end
506
+ ext = File.extname(fn)
507
+ loader = @loaders[ext] || @default_loader
508
+ loader.load(fn)
509
+ @imported << fn
510
+ end
511
+ end
512
+
513
+ # Warn about deprecated use of top level constant names.
514
+ def const_warning(const_name)
515
+ @const_warning ||= false
516
+ if ! @const_warning
517
+ $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
518
+ %{found at: #{rakefile_location}} # '
519
+ $stderr.puts %{ Use --classic-namespace on rake command}
520
+ $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
521
+ end
522
+ @const_warning = true
523
+ end
524
+
525
+ def rakefile_location
526
+ begin
527
+ fail
528
+ rescue RuntimeError => ex
529
+ ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
530
+ end
531
+ end
532
+ end
533
+ end