jimweirich-rake 0.8.4.99 → 0.8.5

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