craigmarksmith-rake 0.8.3.100

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/CHANGES +427 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +196 -0
  4. data/Rakefile +416 -0
  5. data/TODO +20 -0
  6. data/bin/rake +31 -0
  7. data/doc/command_line_usage.rdoc +102 -0
  8. data/doc/example/Rakefile1 +38 -0
  9. data/doc/example/Rakefile2 +35 -0
  10. data/doc/example/a.c +6 -0
  11. data/doc/example/b.c +6 -0
  12. data/doc/example/main.c +11 -0
  13. data/doc/glossary.rdoc +51 -0
  14. data/doc/jamis.rb +591 -0
  15. data/doc/proto_rake.rdoc +127 -0
  16. data/doc/rake.1.gz +0 -0
  17. data/doc/rakefile.rdoc +534 -0
  18. data/doc/rational.rdoc +151 -0
  19. data/doc/release_notes/rake-0.4.14.rdoc +23 -0
  20. data/doc/release_notes/rake-0.4.15.rdoc +35 -0
  21. data/doc/release_notes/rake-0.5.0.rdoc +53 -0
  22. data/doc/release_notes/rake-0.5.3.rdoc +78 -0
  23. data/doc/release_notes/rake-0.5.4.rdoc +46 -0
  24. data/doc/release_notes/rake-0.6.0.rdoc +141 -0
  25. data/doc/release_notes/rake-0.7.0.rdoc +119 -0
  26. data/doc/release_notes/rake-0.7.1.rdoc +59 -0
  27. data/doc/release_notes/rake-0.7.2.rdoc +121 -0
  28. data/doc/release_notes/rake-0.7.3.rdoc +47 -0
  29. data/doc/release_notes/rake-0.8.0.rdoc +114 -0
  30. data/doc/release_notes/rake-0.8.2.rdoc +165 -0
  31. data/doc/release_notes/rake-0.8.3.rdoc +112 -0
  32. data/doc/release_notes/rake-0.8.4.rdoc +147 -0
  33. data/install.rb +88 -0
  34. data/lib/rake/classic_namespace.rb +8 -0
  35. data/lib/rake/clean.rb +33 -0
  36. data/lib/rake/contrib/compositepublisher.rb +24 -0
  37. data/lib/rake/contrib/ftptools.rb +153 -0
  38. data/lib/rake/contrib/publisher.rb +75 -0
  39. data/lib/rake/contrib/rubyforgepublisher.rb +18 -0
  40. data/lib/rake/contrib/sshpublisher.rb +47 -0
  41. data/lib/rake/contrib/sys.rb +209 -0
  42. data/lib/rake/gempackagetask.rb +97 -0
  43. data/lib/rake/loaders/makefile.rb +42 -0
  44. data/lib/rake/packagetask.rb +184 -0
  45. data/lib/rake/rake_test_loader.rb +5 -0
  46. data/lib/rake/rdoctask.rb +209 -0
  47. data/lib/rake/ruby182_test_unit_fix.rb +23 -0
  48. data/lib/rake/runtest.rb +23 -0
  49. data/lib/rake/tasklib.rb +23 -0
  50. data/lib/rake/testtask.rb +161 -0
  51. data/lib/rake/win32.rb +55 -0
  52. data/lib/rake.rb +2519 -0
  53. data/test/capture_stdout.rb +26 -0
  54. data/test/check_expansion.rb +5 -0
  55. data/test/contrib/test_sys.rb +47 -0
  56. data/test/data/chains/Rakefile +15 -0
  57. data/test/data/default/Rakefile +19 -0
  58. data/test/data/dryrun/Rakefile +22 -0
  59. data/test/data/file_creation_task/Rakefile +33 -0
  60. data/test/data/imports/Rakefile +19 -0
  61. data/test/data/imports/deps.mf +1 -0
  62. data/test/data/multidesc/Rakefile +17 -0
  63. data/test/data/namespace/Rakefile +57 -0
  64. data/test/data/rakelib/test1.rb +3 -0
  65. data/test/data/rbext/rakefile.rb +3 -0
  66. data/test/data/sample.mf +14 -0
  67. data/test/data/statusreturn/Rakefile +8 -0
  68. data/test/data/unittest/Rakefile +1 -0
  69. data/test/filecreation.rb +32 -0
  70. data/test/functional.rb +15 -0
  71. data/test/in_environment.rb +30 -0
  72. data/test/rake_test_setup.rb +24 -0
  73. data/test/reqfile.rb +3 -0
  74. data/test/reqfile2.rb +3 -0
  75. data/test/session_functional.rb +339 -0
  76. data/test/shellcommand.rb +3 -0
  77. data/test/test_application.rb +690 -0
  78. data/test/test_clean.rb +14 -0
  79. data/test/test_definitions.rb +85 -0
  80. data/test/test_earlytime.rb +35 -0
  81. data/test/test_extension.rb +63 -0
  82. data/test/test_file_creation_task.rb +62 -0
  83. data/test/test_file_task.rb +143 -0
  84. data/test/test_filelist.rb +623 -0
  85. data/test/test_fileutils.rb +251 -0
  86. data/test/test_ftp.rb +59 -0
  87. data/test/test_invocation_chain.rb +81 -0
  88. data/test/test_makefile_loader.rb +26 -0
  89. data/test/test_multitask.rb +45 -0
  90. data/test/test_namespace.rb +55 -0
  91. data/test/test_package_task.rb +118 -0
  92. data/test/test_pathmap.rb +210 -0
  93. data/test/test_rake.rb +41 -0
  94. data/test/test_rdoc_task.rb +88 -0
  95. data/test/test_require.rb +35 -0
  96. data/test/test_rules.rb +349 -0
  97. data/test/test_task_arguments.rb +89 -0
  98. data/test/test_task_manager.rb +173 -0
  99. data/test/test_tasklib.rb +12 -0
  100. data/test/test_tasks.rb +374 -0
  101. data/test/test_test_task.rb +77 -0
  102. data/test/test_top_level_functions.rb +86 -0
  103. data/test/test_win32.rb +72 -0
  104. metadata +186 -0
@@ -0,0 +1,23 @@
1
+ module Test
2
+ module Unit
3
+ module Collector
4
+ class Dir
5
+ undef collect_file
6
+ def collect_file(name, suites, already_gathered)
7
+ # loadpath = $:.dup
8
+ dir = File.dirname(File.expand_path(name))
9
+ $:.unshift(dir) unless $:.first == dir
10
+ if(@req)
11
+ @req.require(name)
12
+ else
13
+ require(name)
14
+ end
15
+ find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)}
16
+ ensure
17
+ # $:.replace(loadpath)
18
+ $:.delete_at $:.rindex(dir)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'test/unit/assertions'
5
+
6
+ module Rake
7
+ include Test::Unit::Assertions
8
+
9
+ def run_tests(pattern='test/test*.rb', log_enabled=false)
10
+ Dir["#{pattern}"].each { |fn|
11
+ puts fn if log_enabled
12
+ begin
13
+ load fn
14
+ rescue Exception => ex
15
+ puts "Error in #{fn}: #{ex.message}"
16
+ puts ex.backtrace
17
+ assert false
18
+ end
19
+ }
20
+ end
21
+
22
+ extend self
23
+ end
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+
5
+ module Rake
6
+
7
+ # Base class for Task Libraries.
8
+ class TaskLib
9
+ include Cloneable
10
+
11
+ # Make a symbol by pasting two strings together.
12
+ #
13
+ # NOTE: DEPRECATED! This method is kinda stupid. I don't know why
14
+ # I didn't just use string interpolation. But now other task
15
+ # libraries depend on this so I can't remove it without breaking
16
+ # other people's code. So for now it stays for backwards
17
+ # compatibility. BUT DON'T USE IT.
18
+ def paste(a,b) # :nodoc:
19
+ (a.to_s + b.to_s).intern
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Define a task library for running unit tests.
4
+
5
+ require 'rake'
6
+ require 'rake/tasklib'
7
+
8
+ module Rake
9
+
10
+ # Create a task that runs a set of tests.
11
+ #
12
+ # Example:
13
+ #
14
+ # Rake::TestTask.new do |t|
15
+ # t.libs << "test"
16
+ # t.test_files = FileList['test/test*.rb']
17
+ # t.verbose = true
18
+ # end
19
+ #
20
+ # If rake is invoked with a "TEST=filename" command line option,
21
+ # then the list of test files will be overridden to include only the
22
+ # filename specified on the command line. This provides an easy way
23
+ # to run just one test.
24
+ #
25
+ # If rake is invoked with a "TESTOPTS=options" command line option,
26
+ # then the given options are passed to the test process after a
27
+ # '--'. This allows Test::Unit options to be passed to the test
28
+ # suite.
29
+ #
30
+ # Examples:
31
+ #
32
+ # rake test # run tests normally
33
+ # rake test TEST=just_one_file.rb # run just one test file.
34
+ # rake test TESTOPTS="-v" # run in verbose mode
35
+ # rake test TESTOPTS="--runner=fox" # use the fox test runner
36
+ #
37
+ class TestTask < TaskLib
38
+
39
+ # Name of test task. (default is :test)
40
+ attr_accessor :name
41
+
42
+ # List of directories to added to $LOAD_PATH before running the
43
+ # tests. (default is 'lib')
44
+ attr_accessor :libs
45
+
46
+ # True if verbose test output desired. (default is false)
47
+ attr_accessor :verbose
48
+
49
+ # Test options passed to the test suite. An explicit
50
+ # TESTOPTS=opts on the command line will override this. (default
51
+ # is NONE)
52
+ attr_accessor :options
53
+
54
+ # Request that the tests be run with the warning flag set.
55
+ # E.g. warning=true implies "ruby -w" used to run the tests.
56
+ attr_accessor :warning
57
+
58
+ # Glob pattern to match test files. (default is 'test/test*.rb')
59
+ attr_accessor :pattern
60
+
61
+ # Style of test loader to use. Options are:
62
+ #
63
+ # * :rake -- Rake provided test loading script (default).
64
+ # * :testrb -- Ruby provided test loading script.
65
+ # * :direct -- Load tests using command line loader.
66
+ #
67
+ attr_accessor :loader
68
+
69
+ # Array of commandline options to pass to ruby when running test loader.
70
+ attr_accessor :ruby_opts
71
+
72
+ # Explicitly define the list of test files to be included in a
73
+ # test. +list+ is expected to be an array of file names (a
74
+ # FileList is acceptable). If both +pattern+ and +test_files+ are
75
+ # used, then the list of test files is the union of the two.
76
+ def test_files=(list)
77
+ @test_files = list
78
+ end
79
+
80
+ # Create a testing task.
81
+ def initialize(name=:test)
82
+ @name = name
83
+ @libs = ["lib"]
84
+ @pattern = nil
85
+ @options = nil
86
+ @test_files = nil
87
+ @verbose = false
88
+ @warning = false
89
+ @loader = :rake
90
+ @ruby_opts = []
91
+ yield self if block_given?
92
+ @pattern = 'test/test*.rb' if @pattern.nil? && @test_files.nil?
93
+ define
94
+ end
95
+
96
+ # Create the tasks defined by this task lib.
97
+ def define
98
+ lib_path = @libs.join(File::PATH_SEPARATOR)
99
+ desc "Run tests" + (@name==:test ? "" : " for #{@name}")
100
+ task @name do
101
+ run_code = ''
102
+ RakeFileUtils.verbose(@verbose) do
103
+ run_code =
104
+ case @loader
105
+ when :direct
106
+ "-e 'ARGV.each{|f| load f}'"
107
+ when :testrb
108
+ "-S testrb #{fix}"
109
+ when :rake
110
+ rake_loader
111
+ end
112
+ @ruby_opts.unshift( "-I\"#{lib_path}\"" )
113
+ @ruby_opts.unshift( "-w" ) if @warning
114
+ ruby @ruby_opts.join(" ") +
115
+ " \"#{run_code}\" " +
116
+ file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
117
+ " #{option_list}"
118
+ end
119
+ end
120
+ self
121
+ end
122
+
123
+ def option_list # :nodoc:
124
+ ENV['TESTOPTS'] || @options || ""
125
+ end
126
+
127
+ def file_list # :nodoc:
128
+ if ENV['TEST']
129
+ FileList[ ENV['TEST'] ]
130
+ else
131
+ result = []
132
+ result += @test_files.to_a if @test_files
133
+ result += FileList[ @pattern ].to_a if @pattern
134
+ FileList[result]
135
+ end
136
+ end
137
+
138
+ def fix # :nodoc:
139
+ case RUBY_VERSION
140
+ when '1.8.2'
141
+ find_file 'rake/ruby182_test_unit_fix'
142
+ else
143
+ nil
144
+ end || ''
145
+ end
146
+
147
+ def rake_loader # :nodoc:
148
+ find_file('rake/rake_test_loader') or
149
+ fail "unable to find rake test loader"
150
+ end
151
+
152
+ def find_file(fn) # :nodoc:
153
+ $LOAD_PATH.each do |path|
154
+ file_path = File.join(path, "#{fn}.rb")
155
+ return file_path if File.exist? file_path
156
+ end
157
+ nil
158
+ end
159
+
160
+ end
161
+ end
data/lib/rake/win32.rb ADDED
@@ -0,0 +1,55 @@
1
+
2
+ module Rake
3
+ require 'rake/alt_system'
4
+
5
+ # Win 32 interface methods for Rake. Windows specific functionality
6
+ # will be placed here to collect that knowledge in one spot.
7
+ module Win32
8
+
9
+ # Error indicating a problem in locating the home directory on a
10
+ # Win32 system.
11
+ class Win32HomeError < RuntimeError
12
+ end
13
+
14
+ class << self
15
+ # True if running on a windows system.
16
+ def windows?
17
+ AltSystem::WINDOWS
18
+ end
19
+
20
+ # Run a command line on windows.
21
+ def rake_system(*cmd)
22
+ AltSystem.system(*cmd)
23
+ end
24
+
25
+ # The standard directory containing system wide rake files on
26
+ # Win 32 systems. Try the following environment variables (in
27
+ # order):
28
+ #
29
+ # * HOME
30
+ # * HOMEDRIVE + HOMEPATH
31
+ # * APPDATA
32
+ # * USERPROFILE
33
+ #
34
+ # If the above are not defined, the return nil.
35
+ def win32_system_dir #:nodoc:
36
+ win32_shared_path = ENV['HOME']
37
+ if win32_shared_path.nil? && ENV['HOMEDRIVE'] && ENV['HOMEPATH']
38
+ win32_shared_path = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
39
+ end
40
+
41
+ win32_shared_path ||= ENV['APPDATA']
42
+ win32_shared_path ||= ENV['USERPROFILE']
43
+ raise Win32HomeError, "Unable to determine home path environment variable." if
44
+ win32_shared_path.nil? or win32_shared_path.empty?
45
+ normalize(File.join(win32_shared_path, 'Rake'))
46
+ end
47
+
48
+ # Normalize a win32 path so that the slashes are all forward slashes.
49
+ def normalize(path)
50
+ path.gsub(/\\/, '/')
51
+ end
52
+
53
+ end
54
+ end
55
+ end
data/lib/rake.rb ADDED
@@ -0,0 +1,2519 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+
5
+ # Copyright 2003, 2004, 2005, 2006, 2007, 2008 by Jim Weirich (jim@weirichhouse.org)
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to
9
+ # deal in the Software without restriction, including without limitation the
10
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
+ # sell copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
+ # IN THE SOFTWARE.
24
+ #++
25
+ #
26
+ # = Rake -- Ruby Make
27
+ #
28
+ # This is the main file for the Rake application. Normally it is referenced
29
+ # as a library via a require statement, but it can be distributed
30
+ # independently as an application.
31
+
32
+ RAKEVERSION = '0.8.4.1'
33
+
34
+ require 'rbconfig'
35
+ require 'fileutils'
36
+ require 'singleton'
37
+ require 'monitor'
38
+ require 'optparse'
39
+ require 'ostruct'
40
+
41
+ require 'rake/win32'
42
+
43
+ $trace = false
44
+
45
+ ######################################################################
46
+ # Rake extensions to Module.
47
+ #
48
+ class Module
49
+ # Check for an existing method in the current class before extending. IF
50
+ # the method already exists, then a warning is printed and the extension is
51
+ # not added. Otherwise the block is yielded and any definitions in the
52
+ # block will take effect.
53
+ #
54
+ # Usage:
55
+ #
56
+ # class String
57
+ # rake_extension("xyz") do
58
+ # def xyz
59
+ # ...
60
+ # end
61
+ # end
62
+ # end
63
+ #
64
+ def rake_extension(method)
65
+ if method_defined?(method)
66
+ $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
67
+ else
68
+ yield
69
+ end
70
+ end
71
+ end # module Module
72
+
73
+
74
+ ######################################################################
75
+ # User defined methods to be added to String.
76
+ #
77
+ class String
78
+ rake_extension("ext") do
79
+ # Replace the file extension with +newext+. If there is no extension on
80
+ # the string, append the new extension to the end. If the new extension
81
+ # is not given, or is the empty string, remove any existing extension.
82
+ #
83
+ # +ext+ is a user added method for the String class.
84
+ def ext(newext='')
85
+ return self.dup if ['.', '..'].include? self
86
+ if newext != ''
87
+ newext = (newext =~ /^\./) ? newext : ("." + newext)
88
+ end
89
+ self.chomp(File.extname(self)) << newext
90
+ end
91
+ end
92
+
93
+ rake_extension("pathmap") do
94
+ # Explode a path into individual components. Used by +pathmap+.
95
+ def pathmap_explode
96
+ head, tail = File.split(self)
97
+ return [self] if head == self
98
+ return [tail] if head == '.' || tail == '/'
99
+ return [head, tail] if head == '/'
100
+ return head.pathmap_explode + [tail]
101
+ end
102
+ protected :pathmap_explode
103
+
104
+ # Extract a partial path from the path. Include +n+ directories from the
105
+ # front end (left hand side) if +n+ is positive. Include |+n+|
106
+ # directories from the back end (right hand side) if +n+ is negative.
107
+ def pathmap_partial(n)
108
+ dirs = File.dirname(self).pathmap_explode
109
+ partial_dirs =
110
+ if n > 0
111
+ dirs[0...n]
112
+ elsif n < 0
113
+ dirs.reverse[0...-n].reverse
114
+ else
115
+ "."
116
+ end
117
+ File.join(partial_dirs)
118
+ end
119
+ protected :pathmap_partial
120
+
121
+ # Preform the pathmap replacement operations on the given path. The
122
+ # patterns take the form 'pat1,rep1;pat2,rep2...'.
123
+ def pathmap_replace(patterns, &block)
124
+ result = self
125
+ patterns.split(';').each do |pair|
126
+ pattern, replacement = pair.split(',')
127
+ pattern = Regexp.new(pattern)
128
+ if replacement == '*' && block_given?
129
+ result = result.sub(pattern, &block)
130
+ elsif replacement
131
+ result = result.sub(pattern, replacement)
132
+ else
133
+ result = result.sub(pattern, '')
134
+ end
135
+ end
136
+ result
137
+ end
138
+ protected :pathmap_replace
139
+
140
+ # Map the path according to the given specification. The specification
141
+ # controls the details of the mapping. The following special patterns are
142
+ # recognized:
143
+ #
144
+ # * <b>%p</b> -- The complete path.
145
+ # * <b>%f</b> -- The base file name of the path, with its file extension,
146
+ # but without any directories.
147
+ # * <b>%n</b> -- The file name of the path without its file extension.
148
+ # * <b>%d</b> -- The directory list of the path.
149
+ # * <b>%x</b> -- The file extension of the path. An empty string if there
150
+ # is no extension.
151
+ # * <b>%X</b> -- Everything *but* the file extension.
152
+ # * <b>%s</b> -- The alternate file separater if defined, otherwise use
153
+ # the standard file separator.
154
+ # * <b>%%</b> -- A percent sign.
155
+ #
156
+ # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
157
+ # number is positive, only return (up to) +n+ directories in the path,
158
+ # starting from the left hand side. If +n+ is negative, return (up to)
159
+ # |+n+| directories from the right hand side of the path.
160
+ #
161
+ # Examples:
162
+ #
163
+ # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
164
+ # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
165
+ #
166
+ # Also the %d, %p, %f, %n, %x, and %X operators can take a
167
+ # pattern/replacement argument to perform simple string substititions on a
168
+ # particular part of the path. The pattern and replacement are speparated
169
+ # by a comma and are enclosed by curly braces. The replacement spec comes
170
+ # after the % character but before the operator letter. (e.g.
171
+ # "%{old,new}d"). Muliple replacement specs should be separated by
172
+ # semi-colons (e.g. "%{old,new;src,bin}d").
173
+ #
174
+ # Regular expressions may be used for the pattern, and back refs may be
175
+ # used in the replacement text. Curly braces, commas and semi-colons are
176
+ # excluded from both the pattern and replacement text (let's keep parsing
177
+ # reasonable).
178
+ #
179
+ # For example:
180
+ #
181
+ # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
182
+ #
183
+ # returns:
184
+ #
185
+ # "bin/org/onestepback/proj/A.class"
186
+ #
187
+ # If the replacement text is '*', then a block may be provided to perform
188
+ # some arbitrary calculation for the replacement.
189
+ #
190
+ # For example:
191
+ #
192
+ # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
193
+ # ext.downcase
194
+ # }
195
+ #
196
+ # Returns:
197
+ #
198
+ # "/path/to/file.txt"
199
+ #
200
+ def pathmap(spec=nil, &block)
201
+ return self if spec.nil?
202
+ result = ''
203
+ spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
204
+ case frag
205
+ when '%f'
206
+ result << File.basename(self)
207
+ when '%n'
208
+ result << File.basename(self).ext
209
+ when '%d'
210
+ result << File.dirname(self)
211
+ when '%x'
212
+ result << File.extname(self)
213
+ when '%X'
214
+ result << self.ext
215
+ when '%p'
216
+ result << self
217
+ when '%s'
218
+ result << (File::ALT_SEPARATOR || File::SEPARATOR)
219
+ when '%-'
220
+ # do nothing
221
+ when '%%'
222
+ result << "%"
223
+ when /%(-?\d+)d/
224
+ result << pathmap_partial($1.to_i)
225
+ when /^%\{([^}]*)\}(\d*[dpfnxX])/
226
+ patterns, operator = $1, $2
227
+ result << pathmap('%' + operator).pathmap_replace(patterns, &block)
228
+ when /^%/
229
+ fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
230
+ else
231
+ result << frag
232
+ end
233
+ end
234
+ result
235
+ end
236
+ end
237
+ end # class String
238
+
239
+ ##############################################################################
240
+ module Rake
241
+
242
+ # Errors -----------------------------------------------------------
243
+
244
+ # Error indicating an ill-formed task declaration.
245
+ class TaskArgumentError < ArgumentError
246
+ end
247
+
248
+ # Error indicating a recursion overflow error in task selection.
249
+ class RuleRecursionOverflowError < StandardError
250
+ def initialize(*args)
251
+ super
252
+ @targets = []
253
+ end
254
+
255
+ def add_target(target)
256
+ @targets << target
257
+ end
258
+
259
+ def message
260
+ super + ": [" + @targets.reverse.join(' => ') + "]"
261
+ end
262
+ end
263
+
264
+ # --------------------------------------------------------------------------
265
+ # Rake module singleton methods.
266
+ #
267
+ class << self
268
+ # Current Rake Application
269
+ def application
270
+ @application ||= Rake::Application.new
271
+ end
272
+
273
+ # Set the current Rake application object.
274
+ def application=(app)
275
+ @application = app
276
+ end
277
+
278
+ # Return the original directory where the Rake application was started.
279
+ def original_dir
280
+ application.original_dir
281
+ end
282
+
283
+ end
284
+
285
+ # ##########################################################################
286
+ # Mixin for creating easily cloned objects.
287
+ #
288
+ module Cloneable
289
+ # Clone an object by making a new object and setting all the instance
290
+ # variables to the same values.
291
+ def dup
292
+ sibling = self.class.new
293
+ instance_variables.each do |ivar|
294
+ value = self.instance_variable_get(ivar)
295
+ new_value = value.clone rescue value
296
+ sibling.instance_variable_set(ivar, new_value)
297
+ end
298
+ sibling.taint if tainted?
299
+ sibling
300
+ end
301
+
302
+ def clone
303
+ sibling = dup
304
+ sibling.freeze if frozen?
305
+ sibling
306
+ end
307
+ end
308
+
309
+ ####################################################################
310
+ # Exit status class for times the system just gives us a nil.
311
+ class PseudoStatus
312
+ attr_reader :exitstatus
313
+ def initialize(code=0)
314
+ @exitstatus = code
315
+ end
316
+ def to_i
317
+ @exitstatus << 8
318
+ end
319
+ def >>(n)
320
+ to_i >> n
321
+ end
322
+ def stopped?
323
+ false
324
+ end
325
+ def exited?
326
+ true
327
+ end
328
+ end
329
+
330
+ ####################################################################
331
+ # TaskAguments manage the arguments passed to a task.
332
+ #
333
+ class TaskArguments
334
+ include Enumerable
335
+
336
+ attr_reader :names
337
+
338
+ # Create a TaskArgument object with a list of named arguments
339
+ # (given by :names) and a set of associated values (given by
340
+ # :values). :parent is the parent argument object.
341
+ def initialize(names, values, parent=nil)
342
+ @names = names
343
+ @parent = parent
344
+ @hash = {}
345
+ names.each_with_index { |name, i|
346
+ @hash[name.to_sym] = values[i] unless values[i].nil?
347
+ }
348
+ end
349
+
350
+ # Create a new argument scope using the prerequisite argument
351
+ # names.
352
+ def new_scope(names)
353
+ values = names.collect { |n| self[n] }
354
+ self.class.new(names, values, self)
355
+ end
356
+
357
+ # Find an argument value by name or index.
358
+ def [](index)
359
+ lookup(index.to_sym)
360
+ end
361
+
362
+ # Specify a hash of default values for task arguments. Use the
363
+ # defaults only if there is no specific value for the given
364
+ # argument.
365
+ def with_defaults(defaults)
366
+ @hash = defaults.merge(@hash)
367
+ end
368
+
369
+ def each(&block)
370
+ @hash.each(&block)
371
+ end
372
+
373
+ def method_missing(sym, *args, &block)
374
+ lookup(sym.to_sym)
375
+ end
376
+
377
+ def to_hash
378
+ @hash
379
+ end
380
+
381
+ def to_s
382
+ @hash.inspect
383
+ end
384
+
385
+ def inspect
386
+ to_s
387
+ end
388
+
389
+ protected
390
+
391
+ def lookup(name)
392
+ if @hash.has_key?(name)
393
+ @hash[name]
394
+ elsif ENV.has_key?(name.to_s)
395
+ ENV[name.to_s]
396
+ elsif ENV.has_key?(name.to_s.upcase)
397
+ ENV[name.to_s.upcase]
398
+ elsif @parent
399
+ @parent.lookup(name)
400
+ end
401
+ end
402
+ end
403
+
404
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
405
+
406
+ ####################################################################
407
+ # InvocationChain tracks the chain of task invocations to detect
408
+ # circular dependencies.
409
+ class InvocationChain
410
+ def initialize(value, tail)
411
+ @value = value
412
+ @tail = tail
413
+ end
414
+
415
+ def member?(obj)
416
+ @value == obj || @tail.member?(obj)
417
+ end
418
+
419
+ def append(value)
420
+ if member?(value)
421
+ fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}"
422
+ end
423
+ self.class.new(value, self)
424
+ end
425
+
426
+ def to_s
427
+ "#{prefix}#{@value}"
428
+ end
429
+
430
+ def self.append(value, chain)
431
+ chain.append(value)
432
+ end
433
+
434
+ private
435
+
436
+ def prefix
437
+ "#{@tail.to_s} => "
438
+ end
439
+
440
+ class EmptyInvocationChain
441
+ def member?(obj)
442
+ false
443
+ end
444
+ def append(value)
445
+ InvocationChain.new(value, self)
446
+ end
447
+ def to_s
448
+ "TOP"
449
+ end
450
+ end
451
+
452
+ EMPTY = EmptyInvocationChain.new
453
+
454
+ end # class InvocationChain
455
+
456
+ end # module Rake
457
+
458
+ module Rake
459
+
460
+ # #########################################################################
461
+ # A Task is the basic unit of work in a Rakefile. Tasks have associated
462
+ # actions (possibly more than one) and a list of prerequisites. When
463
+ # invoked, a task will first ensure that all of its prerequisites have an
464
+ # opportunity to run and then it will execute its own actions.
465
+ #
466
+ # Tasks are not usually created directly using the new method, but rather
467
+ # use the +file+ and +task+ convenience methods.
468
+ #
469
+ class Task
470
+ # List of prerequisites for a task.
471
+ attr_reader :prerequisites
472
+
473
+ # List of actions attached to a task.
474
+ attr_reader :actions
475
+
476
+ # Application owning this task.
477
+ attr_accessor :application
478
+
479
+ # Comment for this task. Restricted to a single line of no more than 50
480
+ # characters.
481
+ attr_reader :comment
482
+
483
+ # Full text of the (possibly multi-line) comment.
484
+ attr_reader :full_comment
485
+
486
+ # Array of nested namespaces names used for task lookup by this task.
487
+ attr_reader :scope
488
+
489
+ # Return task name
490
+ def to_s
491
+ name
492
+ end
493
+
494
+ def inspect
495
+ "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
496
+ end
497
+
498
+ # List of sources for task.
499
+ attr_writer :sources
500
+ def sources
501
+ @sources ||= []
502
+ end
503
+
504
+ # First source from a rule (nil if no sources)
505
+ def source
506
+ @sources.first if defined?(@sources)
507
+ end
508
+
509
+ # Create a task named +task_name+ with no actions or prerequisites. Use
510
+ # +enhance+ to add actions and prerequisites.
511
+ def initialize(task_name, app)
512
+ @name = task_name.to_s
513
+ @prerequisites = []
514
+ @actions = []
515
+ @already_invoked = false
516
+ @full_comment = nil
517
+ @comment = nil
518
+ @lock = Monitor.new
519
+ @application = app
520
+ @scope = app.current_scope
521
+ @arg_names = nil
522
+ end
523
+
524
+ # Enhance a task with prerequisites or actions. Returns self.
525
+ def enhance(deps=nil, &block)
526
+ @prerequisites |= deps if deps
527
+ @actions << block if block_given?
528
+ self
529
+ end
530
+
531
+ # Name of the task, including any namespace qualifiers.
532
+ def name
533
+ @name.to_s
534
+ end
535
+
536
+ # Name of task with argument list description.
537
+ def name_with_args # :nodoc:
538
+ if arg_description
539
+ "#{name}#{arg_description}"
540
+ else
541
+ name
542
+ end
543
+ end
544
+
545
+ # Argument description (nil if none).
546
+ def arg_description # :nodoc:
547
+ @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
548
+ end
549
+
550
+ # Name of arguments for this task.
551
+ def arg_names
552
+ @arg_names || []
553
+ end
554
+
555
+ # Reenable the task, allowing its tasks to be executed if the task
556
+ # is invoked again.
557
+ def reenable
558
+ @already_invoked = false
559
+ end
560
+
561
+ # Clear the existing prerequisites and actions of a rake task.
562
+ def clear
563
+ clear_prerequisites
564
+ clear_actions
565
+ self
566
+ end
567
+
568
+ # Clear the existing prerequisites of a rake task.
569
+ def clear_prerequisites
570
+ prerequisites.clear
571
+ self
572
+ end
573
+
574
+ # Clear the existing actions on a rake task.
575
+ def clear_actions
576
+ actions.clear
577
+ self
578
+ end
579
+
580
+ # Invoke the task if it is needed. Prerequites are invoked first.
581
+ def invoke(*args)
582
+ task_args = TaskArguments.new(arg_names, args)
583
+ invoke_with_call_chain(task_args, InvocationChain::EMPTY)
584
+ end
585
+
586
+ # Same as invoke, but explicitly pass a call chain to detect
587
+ # circular dependencies.
588
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
589
+ new_chain = InvocationChain.append(self, invocation_chain)
590
+ @lock.synchronize do
591
+ if application.options.trace
592
+ puts "** Invoke #{name} #{format_trace_flags}"
593
+ end
594
+ return if @already_invoked
595
+ @already_invoked = true
596
+ invoke_prerequisites(task_args, new_chain)
597
+ execute(task_args) if needed?
598
+ end
599
+ end
600
+ protected :invoke_with_call_chain
601
+
602
+ # Invoke all the prerequisites of a task.
603
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
604
+ @prerequisites.each { |n|
605
+ prereq = application[n, @scope]
606
+ prereq_args = task_args.new_scope(prereq.arg_names)
607
+ prereq.invoke_with_call_chain(prereq_args, invocation_chain)
608
+ }
609
+ end
610
+
611
+ # Format the trace flags for display.
612
+ def format_trace_flags
613
+ flags = []
614
+ flags << "first_time" unless @already_invoked
615
+ flags << "not_needed" unless needed?
616
+ flags.empty? ? "" : "(" + flags.join(", ") + ")"
617
+ end
618
+ private :format_trace_flags
619
+
620
+ # Execute the actions associated with this task.
621
+ def execute(args=nil)
622
+ args ||= EMPTY_TASK_ARGS
623
+ if application.options.dryrun
624
+ puts "** Execute (dry run) #{name}"
625
+ return
626
+ end
627
+ if application.options.trace
628
+ puts "** Execute #{name}"
629
+ end
630
+ application.enhance_with_matching_rule(name) if @actions.empty?
631
+ @actions.each do |act|
632
+ case act.arity
633
+ when 1
634
+ act.call(self)
635
+ else
636
+ act.call(self, args)
637
+ end
638
+ end
639
+ end
640
+
641
+ # Is this task needed?
642
+ def needed?
643
+ true
644
+ end
645
+
646
+ # Timestamp for this task. Basic tasks return the current time for their
647
+ # time stamp. Other tasks can be more sophisticated.
648
+ def timestamp
649
+ @prerequisites.collect { |p| application[p].timestamp }.max || Time.now
650
+ end
651
+
652
+ # Add a description to the task. The description can consist of an option
653
+ # argument list (enclosed brackets) and an optional comment.
654
+ def add_description(description)
655
+ return if ! description
656
+ comment = description.strip
657
+ add_comment(comment) if comment && ! comment.empty?
658
+ end
659
+
660
+ # Writing to the comment attribute is the same as adding a description.
661
+ def comment=(description)
662
+ add_description(description)
663
+ end
664
+
665
+ # Add a comment to the task. If a comment alread exists, separate
666
+ # the new comment with " / ".
667
+ def add_comment(comment)
668
+ if @full_comment
669
+ @full_comment << " / "
670
+ else
671
+ @full_comment = ''
672
+ end
673
+ @full_comment << comment
674
+ if @full_comment =~ /\A([^.]+?\.)( |$)/
675
+ @comment = $1
676
+ else
677
+ @comment = @full_comment
678
+ end
679
+ end
680
+ private :add_comment
681
+
682
+ # Set the names of the arguments for this task. +args+ should be
683
+ # an array of symbols, one for each argument name.
684
+ def set_arg_names(args)
685
+ @arg_names = args.map { |a| a.to_sym }
686
+ end
687
+
688
+ # Return a string describing the internal state of a task. Useful for
689
+ # debugging.
690
+ def investigation
691
+ result = "------------------------------\n"
692
+ result << "Investigating #{name}\n"
693
+ result << "class: #{self.class}\n"
694
+ result << "task needed: #{needed?}\n"
695
+ result << "timestamp: #{timestamp}\n"
696
+ result << "pre-requisites: \n"
697
+ prereqs = @prerequisites.collect {|name| application[name]}
698
+ prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
699
+ prereqs.each do |p|
700
+ result << "--#{p.name} (#{p.timestamp})\n"
701
+ end
702
+ latest_prereq = @prerequisites.collect{|n| application[n].timestamp}.max
703
+ result << "latest-prerequisite time: #{latest_prereq}\n"
704
+ result << "................................\n\n"
705
+ return result
706
+ end
707
+
708
+ # ----------------------------------------------------------------
709
+ # Rake Module Methods
710
+ #
711
+ class << self
712
+
713
+ # Clear the task list. This cause rake to immediately forget all the
714
+ # tasks that have been assigned. (Normally used in the unit tests.)
715
+ def clear
716
+ Rake.application.clear
717
+ end
718
+
719
+ # List of all defined tasks.
720
+ def tasks
721
+ Rake.application.tasks
722
+ end
723
+
724
+ # Return a task with the given name. If the task is not currently
725
+ # known, try to synthesize one from the defined rules. If no rules are
726
+ # found, but an existing file matches the task name, assume it is a file
727
+ # task with no dependencies or actions.
728
+ def [](task_name)
729
+ Rake.application[task_name]
730
+ end
731
+
732
+ # TRUE if the task name is already defined.
733
+ def task_defined?(task_name)
734
+ Rake.application.lookup(task_name) != nil
735
+ end
736
+
737
+ # Define a task given +args+ and an option block. If a rule with the
738
+ # given name already exists, the prerequisites and actions are added to
739
+ # the existing task. Returns the defined task.
740
+ def define_task(*args, &block)
741
+ Rake.application.define_task(self, *args, &block)
742
+ end
743
+
744
+ # Define a rule for synthesizing tasks.
745
+ def create_rule(*args, &block)
746
+ Rake.application.create_rule(*args, &block)
747
+ end
748
+
749
+ # Apply the scope to the task name according to the rules for
750
+ # this kind of task. Generic tasks will accept the scope as
751
+ # part of the name.
752
+ def scope_name(scope, task_name)
753
+ (scope + [task_name]).join(':')
754
+ end
755
+
756
+ end # class << Rake::Task
757
+ end # class Rake::Task
758
+
759
+
760
+ # #########################################################################
761
+ # A FileTask is a task that includes time based dependencies. If any of a
762
+ # FileTask's prerequisites have a timestamp that is later than the file
763
+ # represented by this task, then the file must be rebuilt (using the
764
+ # supplied actions).
765
+ #
766
+ class FileTask < Task
767
+
768
+ # Is this file task needed? Yes if it doesn't exist, or if its time stamp
769
+ # is out of date.
770
+ def needed?
771
+ ! File.exist?(name) || out_of_date?(timestamp)
772
+ end
773
+
774
+ # Time stamp for file task.
775
+ def timestamp
776
+ if File.exist?(name)
777
+ File.mtime(name.to_s)
778
+ else
779
+ Rake::EARLY
780
+ end
781
+ end
782
+
783
+ private
784
+
785
+ # Are there any prerequisites with a later time than the given time stamp?
786
+ def out_of_date?(stamp)
787
+ @prerequisites.any? { |n| application[n].timestamp > stamp}
788
+ end
789
+
790
+ # ----------------------------------------------------------------
791
+ # Task class methods.
792
+ #
793
+ class << self
794
+ # Apply the scope to the task name according to the rules for this kind
795
+ # of task. File based tasks ignore the scope when creating the name.
796
+ def scope_name(scope, task_name)
797
+ task_name
798
+ end
799
+ end
800
+ end # class Rake::FileTask
801
+
802
+ # #########################################################################
803
+ # A FileCreationTask is a file task that when used as a dependency will be
804
+ # needed if and only if the file has not been created. Once created, it is
805
+ # not re-triggered if any of its dependencies are newer, nor does trigger
806
+ # any rebuilds of tasks that depend on it whenever it is updated.
807
+ #
808
+ class FileCreationTask < FileTask
809
+ # Is this file task needed? Yes if it doesn't exist.
810
+ def needed?
811
+ ! File.exist?(name)
812
+ end
813
+
814
+ # Time stamp for file creation task. This time stamp is earlier
815
+ # than any other time stamp.
816
+ def timestamp
817
+ Rake::EARLY
818
+ end
819
+ end
820
+
821
+ # #########################################################################
822
+ # Same as a regular task, but the immediate prerequisites are done in
823
+ # parallel using Ruby threads.
824
+ #
825
+ class MultiTask < Task
826
+ private
827
+ def invoke_prerequisites(args, invocation_chain)
828
+ threads = @prerequisites.collect { |p|
829
+ Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
830
+ }
831
+ threads.each { |t| t.join }
832
+ end
833
+ end
834
+ end # module Rake
835
+
836
+ # ###########################################################################
837
+ # Task Definition Functions ...
838
+
839
+ # Declare a basic task.
840
+ #
841
+ # Example:
842
+ # task :clobber => [:clean] do
843
+ # rm_rf "html"
844
+ # end
845
+ #
846
+ def task(*args, &block)
847
+ Rake::Task.define_task(*args, &block)
848
+ end
849
+
850
+
851
+ # Declare a file task.
852
+ #
853
+ # Example:
854
+ # file "config.cfg" => ["config.template"] do
855
+ # open("config.cfg", "w") do |outfile|
856
+ # open("config.template") do |infile|
857
+ # while line = infile.gets
858
+ # outfile.puts line
859
+ # end
860
+ # end
861
+ # end
862
+ # end
863
+ #
864
+ def file(*args, &block)
865
+ Rake::FileTask.define_task(*args, &block)
866
+ end
867
+
868
+ # Declare a file creation task.
869
+ # (Mainly used for the directory command).
870
+ def file_create(args, &block)
871
+ Rake::FileCreationTask.define_task(args, &block)
872
+ end
873
+
874
+ # Declare a set of files tasks to create the given directories on demand.
875
+ #
876
+ # Example:
877
+ # directory "testdata/doc"
878
+ #
879
+ def directory(dir)
880
+ Rake.each_dir_parent(dir) do |d|
881
+ file_create d do |t|
882
+ mkdir_p t.name if ! File.exist?(t.name)
883
+ end
884
+ end
885
+ end
886
+
887
+ # Declare a task that performs its prerequisites in parallel. Multitasks does
888
+ # *not* guarantee that its prerequisites will execute in any given order
889
+ # (which is obvious when you think about it)
890
+ #
891
+ # Example:
892
+ # multitask :deploy => [:deploy_gem, :deploy_rdoc]
893
+ #
894
+ def multitask(args, &block)
895
+ Rake::MultiTask.define_task(args, &block)
896
+ end
897
+
898
+ # Create a new rake namespace and use it for evaluating the given block.
899
+ # Returns a NameSpace object that can be used to lookup tasks defined in the
900
+ # namespace.
901
+ #
902
+ # E.g.
903
+ #
904
+ # ns = namespace "nested" do
905
+ # task :run
906
+ # end
907
+ # task_run = ns[:run] # find :run in the given namespace.
908
+ #
909
+ def namespace(name=nil, &block)
910
+ Rake.application.in_namespace(name, &block)
911
+ end
912
+
913
+ # Declare a rule for auto-tasks.
914
+ #
915
+ # Example:
916
+ # rule '.o' => '.c' do |t|
917
+ # sh %{cc -o #{t.name} #{t.source}}
918
+ # end
919
+ #
920
+ def rule(*args, &block)
921
+ Rake::Task.create_rule(*args, &block)
922
+ end
923
+
924
+ # Describe the next rake task.
925
+ #
926
+ # Example:
927
+ # desc "Run the Unit Tests"
928
+ # task :test => [:build]
929
+ # runtests
930
+ # end
931
+ #
932
+ def desc(description)
933
+ Rake.application.last_description = description
934
+ end
935
+
936
+ # Import the partial Rakefiles +fn+. Imported files are loaded _after_ the
937
+ # current file is completely loaded. This allows the import statement to
938
+ # appear anywhere in the importing file, and yet allowing the imported files
939
+ # to depend on objects defined in the importing file.
940
+ #
941
+ # A common use of the import statement is to include files containing
942
+ # dependency declarations.
943
+ #
944
+ # See also the --rakelibdir command line option.
945
+ #
946
+ # Example:
947
+ # import ".depend", "my_rules"
948
+ #
949
+ def import(*fns)
950
+ fns.each do |fn|
951
+ Rake.application.add_import(fn)
952
+ end
953
+ end
954
+
955
+ # ###########################################################################
956
+ # This a FileUtils extension that defines several additional commands to be
957
+ # added to the FileUtils utility functions.
958
+ #
959
+ module FileUtils
960
+ RUBY = File.join(
961
+ Config::CONFIG['bindir'],
962
+ Config::CONFIG['ruby_install_name'] + Config::CONFIG['EXEEXT']).
963
+ sub(/.*\s.*/m, '"\&"')
964
+
965
+ OPT_TABLE['sh'] = %w(noop verbose)
966
+ OPT_TABLE['ruby'] = %w(noop verbose)
967
+
968
+ # Run the system command +cmd+. If multiple arguments are given the command
969
+ # is not run with the shell (same semantics as Kernel::exec and
970
+ # Kernel::system).
971
+ #
972
+ # Example:
973
+ # sh %{ls -ltr}
974
+ #
975
+ # sh 'ls', 'file with spaces'
976
+ #
977
+ # # check exit status after command runs
978
+ # sh %{grep pattern file} do |ok, res|
979
+ # if ! ok
980
+ # puts "pattern not found (status = #{res.exitstatus})"
981
+ # end
982
+ # end
983
+ #
984
+ def sh(*cmd, &block)
985
+ options = (Hash === cmd.last) ? cmd.pop : {}
986
+ unless block_given?
987
+ show_command = cmd.join(" ")
988
+ show_command = show_command[0,42] + "..." unless $trace
989
+ # TODO code application logic heref show_command.length > 45
990
+ block = lambda { |ok, status|
991
+ ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
992
+ }
993
+ end
994
+ if RakeFileUtils.verbose_flag == :default
995
+ options[:verbose] = true
996
+ else
997
+ options[:verbose] ||= RakeFileUtils.verbose_flag
998
+ end
999
+ options[:noop] ||= RakeFileUtils.nowrite_flag
1000
+ rake_check_options options, :noop, :verbose
1001
+ rake_output_message cmd.join(" ") if options[:verbose]
1002
+ unless options[:noop]
1003
+ res = rake_system(*cmd)
1004
+ status = $?
1005
+ status = PseudoStatus.new(1) if !res && status.nil?
1006
+ block.call(res, status)
1007
+ end
1008
+ end
1009
+
1010
+ def rake_system(*cmd)
1011
+ Rake::AltSystem.system(*cmd)
1012
+ end
1013
+ private :rake_system
1014
+
1015
+ # Run a Ruby interpreter with the given arguments.
1016
+ #
1017
+ # Example:
1018
+ # ruby %{-pe '$_.upcase!' <README}
1019
+ #
1020
+ def ruby(*args,&block)
1021
+ options = (Hash === args.last) ? args.pop : {}
1022
+ if args.length > 1 then
1023
+ sh(*([RUBY] + args + [options]), &block)
1024
+ else
1025
+ sh("#{RUBY} #{args.first}", options, &block)
1026
+ end
1027
+ end
1028
+
1029
+ LN_SUPPORTED = [true]
1030
+
1031
+ # Attempt to do a normal file link, but fall back to a copy if the link
1032
+ # fails.
1033
+ def safe_ln(*args)
1034
+ unless LN_SUPPORTED[0]
1035
+ cp(*args)
1036
+ else
1037
+ begin
1038
+ ln(*args)
1039
+ rescue StandardError, NotImplementedError => ex
1040
+ LN_SUPPORTED[0] = false
1041
+ cp(*args)
1042
+ end
1043
+ end
1044
+ end
1045
+
1046
+ # Split a file path into individual directory names.
1047
+ #
1048
+ # Example:
1049
+ # split_all("a/b/c") => ['a', 'b', 'c']
1050
+ #
1051
+ def split_all(path)
1052
+ head, tail = File.split(path)
1053
+ return [tail] if head == '.' || tail == '/'
1054
+ return [head, tail] if head == '/'
1055
+ return split_all(head) + [tail]
1056
+ end
1057
+ end
1058
+
1059
+ # ###########################################################################
1060
+ # RakeFileUtils provides a custom version of the FileUtils methods that
1061
+ # respond to the <tt>verbose</tt> and <tt>nowrite</tt> commands.
1062
+ #
1063
+ module RakeFileUtils
1064
+ include FileUtils
1065
+
1066
+ class << self
1067
+ attr_accessor :verbose_flag, :nowrite_flag
1068
+ end
1069
+ RakeFileUtils.verbose_flag = :default
1070
+ RakeFileUtils.nowrite_flag = false
1071
+
1072
+ $fileutils_verbose = true
1073
+ $fileutils_nowrite = false
1074
+
1075
+ FileUtils::OPT_TABLE.each do |name, opts|
1076
+ default_options = []
1077
+ if opts.include?(:verbose) || opts.include?("verbose")
1078
+ default_options << ':verbose => RakeFileUtils.verbose_flag'
1079
+ end
1080
+ if opts.include?(:noop) || opts.include?("noop")
1081
+ default_options << ':noop => RakeFileUtils.nowrite_flag'
1082
+ end
1083
+
1084
+ next if default_options.empty?
1085
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1086
+ def #{name}( *args, &block )
1087
+ super(
1088
+ *rake_merge_option(args,
1089
+ #{default_options.join(', ')}
1090
+ ), &block)
1091
+ end
1092
+ EOS
1093
+ end
1094
+
1095
+ # Get/set the verbose flag controlling output from the FileUtils utilities.
1096
+ # If verbose is true, then the utility method is echoed to standard output.
1097
+ #
1098
+ # Examples:
1099
+ # verbose # return the current value of the verbose flag
1100
+ # verbose(v) # set the verbose flag to _v_.
1101
+ # verbose(v) { code } # Execute code with the verbose flag set temporarily to _v_.
1102
+ # # Return to the original value when code is done.
1103
+ def verbose(value=nil)
1104
+ oldvalue = RakeFileUtils.verbose_flag
1105
+ RakeFileUtils.verbose_flag = value unless value.nil?
1106
+ if block_given?
1107
+ begin
1108
+ yield
1109
+ ensure
1110
+ RakeFileUtils.verbose_flag = oldvalue
1111
+ end
1112
+ end
1113
+ RakeFileUtils.verbose_flag
1114
+ end
1115
+
1116
+ # Get/set the nowrite flag controlling output from the FileUtils utilities.
1117
+ # If verbose is true, then the utility method is echoed to standard output.
1118
+ #
1119
+ # Examples:
1120
+ # nowrite # return the current value of the nowrite flag
1121
+ # nowrite(v) # set the nowrite flag to _v_.
1122
+ # nowrite(v) { code } # Execute code with the nowrite flag set temporarily to _v_.
1123
+ # # Return to the original value when code is done.
1124
+ def nowrite(value=nil)
1125
+ oldvalue = RakeFileUtils.nowrite_flag
1126
+ RakeFileUtils.nowrite_flag = value unless value.nil?
1127
+ if block_given?
1128
+ begin
1129
+ yield
1130
+ ensure
1131
+ RakeFileUtils.nowrite_flag = oldvalue
1132
+ end
1133
+ end
1134
+ oldvalue
1135
+ end
1136
+
1137
+ # Use this function to prevent protentially destructive ruby code from
1138
+ # running when the :nowrite flag is set.
1139
+ #
1140
+ # Example:
1141
+ #
1142
+ # when_writing("Building Project") do
1143
+ # project.build
1144
+ # end
1145
+ #
1146
+ # The following code will build the project under normal conditions. If the
1147
+ # nowrite(true) flag is set, then the example will print:
1148
+ # DRYRUN: Building Project
1149
+ # instead of actually building the project.
1150
+ #
1151
+ def when_writing(msg=nil)
1152
+ if RakeFileUtils.nowrite_flag
1153
+ puts "DRYRUN: #{msg}" if msg
1154
+ else
1155
+ yield
1156
+ end
1157
+ end
1158
+
1159
+ # Merge the given options with the default values.
1160
+ def rake_merge_option(args, defaults)
1161
+ if Hash === args.last
1162
+ defaults.update(args.last)
1163
+ args.pop
1164
+ end
1165
+ args.push defaults
1166
+ args
1167
+ end
1168
+ private :rake_merge_option
1169
+
1170
+ # Send the message to the default rake output (which is $stderr).
1171
+ def rake_output_message(message)
1172
+ $stderr.puts(message)
1173
+ end
1174
+ private :rake_output_message
1175
+
1176
+ # Check that the options do not contain options not listed in +optdecl+. An
1177
+ # ArgumentError exception is thrown if non-declared options are found.
1178
+ def rake_check_options(options, *optdecl)
1179
+ h = options.dup
1180
+ optdecl.each do |name|
1181
+ h.delete name
1182
+ end
1183
+ raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
1184
+ end
1185
+ private :rake_check_options
1186
+
1187
+ extend self
1188
+ end
1189
+
1190
+ # ###########################################################################
1191
+ # Include the FileUtils file manipulation functions in the top level module,
1192
+ # but mark them private so that they don't unintentionally define methods on
1193
+ # other objects.
1194
+
1195
+ include RakeFileUtils
1196
+ private(*FileUtils.instance_methods(false))
1197
+ private(*RakeFileUtils.instance_methods(false))
1198
+
1199
+ ######################################################################
1200
+ module Rake
1201
+
1202
+ # #########################################################################
1203
+ # A FileList is essentially an array with a few helper methods defined to
1204
+ # make file manipulation a bit easier.
1205
+ #
1206
+ # FileLists are lazy. When given a list of glob patterns for possible files
1207
+ # to be included in the file list, instead of searching the file structures
1208
+ # to find the files, a FileList holds the pattern for latter use.
1209
+ #
1210
+ # This allows us to define a number of FileList to match any number of
1211
+ # files, but only search out the actual files when then FileList itself is
1212
+ # actually used. The key is that the first time an element of the
1213
+ # FileList/Array is requested, the pending patterns are resolved into a real
1214
+ # list of file names.
1215
+ #
1216
+ class FileList
1217
+
1218
+ include Cloneable
1219
+
1220
+ # == Method Delegation
1221
+ #
1222
+ # The lazy evaluation magic of FileLists happens by implementing all the
1223
+ # array specific methods to call +resolve+ before delegating the heavy
1224
+ # lifting to an embedded array object (@items).
1225
+ #
1226
+ # In addition, there are two kinds of delegation calls. The regular kind
1227
+ # delegates to the @items array and returns the result directly. Well,
1228
+ # almost directly. It checks if the returned value is the @items object
1229
+ # itself, and if so will return the FileList object instead.
1230
+ #
1231
+ # The second kind of delegation call is used in methods that normally
1232
+ # return a new Array object. We want to capture the return value of these
1233
+ # methods and wrap them in a new FileList object. We enumerate these
1234
+ # methods in the +SPECIAL_RETURN+ list below.
1235
+
1236
+ # List of array methods (that are not in +Object+) that need to be
1237
+ # delegated.
1238
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
1239
+
1240
+ # List of additional methods that must be delegated.
1241
+ MUST_DEFINE = %w[to_a inspect]
1242
+
1243
+ # List of methods that should not be delegated here (we define special
1244
+ # versions of them explicitly below).
1245
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
1246
+
1247
+ # List of delegated methods that return new array values which need
1248
+ # wrapping.
1249
+ SPECIAL_RETURN = %w[
1250
+ map collect sort sort_by select find_all reject grep
1251
+ compact flatten uniq values_at
1252
+ + - & |
1253
+ ]
1254
+
1255
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
1256
+
1257
+ # Now do the delegation.
1258
+ DELEGATING_METHODS.each_with_index do |sym, i|
1259
+ if SPECIAL_RETURN.include?(sym)
1260
+ ln = __LINE__+1
1261
+ class_eval %{
1262
+ def #{sym}(*args, &block)
1263
+ resolve
1264
+ result = @items.send(:#{sym}, *args, &block)
1265
+ FileList.new.import(result)
1266
+ end
1267
+ }, __FILE__, ln
1268
+ else
1269
+ ln = __LINE__+1
1270
+ class_eval %{
1271
+ def #{sym}(*args, &block)
1272
+ resolve
1273
+ result = @items.send(:#{sym}, *args, &block)
1274
+ result.object_id == @items.object_id ? self : result
1275
+ end
1276
+ }, __FILE__, ln
1277
+ end
1278
+ end
1279
+
1280
+ # Create a file list from the globbable patterns given. If you wish to
1281
+ # perform multiple includes or excludes at object build time, use the
1282
+ # "yield self" pattern.
1283
+ #
1284
+ # Example:
1285
+ # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
1286
+ #
1287
+ # pkg_files = FileList.new('lib/**/*') do |fl|
1288
+ # fl.exclude(/\bCVS\b/)
1289
+ # end
1290
+ #
1291
+ def initialize(*patterns)
1292
+ @pending_add = []
1293
+ @pending = false
1294
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1295
+ @exclude_procs = DEFAULT_IGNORE_PROCS.dup
1296
+ @exclude_re = nil
1297
+ @items = []
1298
+ patterns.each { |pattern| include(pattern) }
1299
+ yield self if block_given?
1300
+ end
1301
+
1302
+ # Add file names defined by glob patterns to the file list. If an array
1303
+ # is given, add each element of the array.
1304
+ #
1305
+ # Example:
1306
+ # file_list.include("*.java", "*.cfg")
1307
+ # file_list.include %w( math.c lib.h *.o )
1308
+ #
1309
+ def include(*filenames)
1310
+ # TODO: check for pending
1311
+ filenames.each do |fn|
1312
+ if fn.respond_to? :to_ary
1313
+ include(*fn.to_ary)
1314
+ else
1315
+ @pending_add << fn
1316
+ end
1317
+ end
1318
+ @pending = true
1319
+ self
1320
+ end
1321
+ alias :add :include
1322
+
1323
+ # Register a list of file name patterns that should be excluded from the
1324
+ # list. Patterns may be regular expressions, glob patterns or regular
1325
+ # strings. In addition, a block given to exclude will remove entries that
1326
+ # return true when given to the block.
1327
+ #
1328
+ # Note that glob patterns are expanded against the file system. If a file
1329
+ # is explicitly added to a file list, but does not exist in the file
1330
+ # system, then an glob pattern in the exclude list will not exclude the
1331
+ # file.
1332
+ #
1333
+ # Examples:
1334
+ # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
1335
+ # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
1336
+ #
1337
+ # If "a.c" is a file, then ...
1338
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
1339
+ #
1340
+ # If "a.c" is not a file, then ...
1341
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
1342
+ #
1343
+ def exclude(*patterns, &block)
1344
+ patterns.each do |pat|
1345
+ @exclude_patterns << pat
1346
+ end
1347
+ if block_given?
1348
+ @exclude_procs << block
1349
+ end
1350
+ resolve_exclude if ! @pending
1351
+ self
1352
+ end
1353
+
1354
+
1355
+ # Clear all the exclude patterns so that we exclude nothing.
1356
+ def clear_exclude
1357
+ @exclude_patterns = []
1358
+ @exclude_procs = []
1359
+ calculate_exclude_regexp if ! @pending
1360
+ self
1361
+ end
1362
+
1363
+ # Define equality.
1364
+ def ==(array)
1365
+ to_ary == array
1366
+ end
1367
+
1368
+ # Return the internal array object.
1369
+ def to_a
1370
+ resolve
1371
+ @items
1372
+ end
1373
+
1374
+ # Return the internal array object.
1375
+ def to_ary
1376
+ to_a
1377
+ end
1378
+
1379
+ # Lie about our class.
1380
+ def is_a?(klass)
1381
+ klass == Array || super(klass)
1382
+ end
1383
+ alias kind_of? is_a?
1384
+
1385
+ # Redefine * to return either a string or a new file list.
1386
+ def *(other)
1387
+ result = @items * other
1388
+ case result
1389
+ when Array
1390
+ FileList.new.import(result)
1391
+ else
1392
+ result
1393
+ end
1394
+ end
1395
+
1396
+ # Resolve all the pending adds now.
1397
+ def resolve
1398
+ if @pending
1399
+ @pending = false
1400
+ @pending_add.each do |fn| resolve_add(fn) end
1401
+ @pending_add = []
1402
+ resolve_exclude
1403
+ end
1404
+ self
1405
+ end
1406
+
1407
+ def calculate_exclude_regexp
1408
+ ignores = []
1409
+ @exclude_patterns.each do |pat|
1410
+ case pat
1411
+ when Regexp
1412
+ ignores << pat
1413
+ when /[*?]/
1414
+ Dir[pat].each do |p| ignores << p end
1415
+ else
1416
+ ignores << Regexp.quote(pat)
1417
+ end
1418
+ end
1419
+ if ignores.empty?
1420
+ @exclude_re = /^$/
1421
+ else
1422
+ re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
1423
+ @exclude_re = Regexp.new(re_str)
1424
+ end
1425
+ end
1426
+
1427
+ def resolve_add(fn)
1428
+ case fn
1429
+ when %r{[*?\[\{]}
1430
+ add_matching(fn)
1431
+ else
1432
+ self << fn
1433
+ end
1434
+ end
1435
+ private :resolve_add
1436
+
1437
+ def resolve_exclude
1438
+ calculate_exclude_regexp
1439
+ reject! { |fn| exclude?(fn) }
1440
+ self
1441
+ end
1442
+ private :resolve_exclude
1443
+
1444
+ # Return a new FileList with the results of running +sub+ against each
1445
+ # element of the oringal list.
1446
+ #
1447
+ # Example:
1448
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
1449
+ #
1450
+ def sub(pat, rep)
1451
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
1452
+ end
1453
+
1454
+ # Return a new FileList with the results of running +gsub+ against each
1455
+ # element of the original list.
1456
+ #
1457
+ # Example:
1458
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
1459
+ # => ['lib\\test\\file', 'x\\y']
1460
+ #
1461
+ def gsub(pat, rep)
1462
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
1463
+ end
1464
+
1465
+ # Same as +sub+ except that the oringal file list is modified.
1466
+ def sub!(pat, rep)
1467
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
1468
+ self
1469
+ end
1470
+
1471
+ # Same as +gsub+ except that the original file list is modified.
1472
+ def gsub!(pat, rep)
1473
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
1474
+ self
1475
+ end
1476
+
1477
+ # Apply the pathmap spec to each of the included file names, returning a
1478
+ # new file list with the modified paths. (See String#pathmap for
1479
+ # details.)
1480
+ def pathmap(spec=nil)
1481
+ collect { |fn| fn.pathmap(spec) }
1482
+ end
1483
+
1484
+ # Return a new FileList with <tt>String#ext</tt> method applied
1485
+ # to each member of the array.
1486
+ #
1487
+ # This method is a shortcut for:
1488
+ #
1489
+ # array.collect { |item| item.ext(newext) }
1490
+ #
1491
+ # +ext+ is a user added method for the Array class.
1492
+ def ext(newext='')
1493
+ collect { |fn| fn.ext(newext) }
1494
+ end
1495
+
1496
+
1497
+ # Grep each of the files in the filelist using the given pattern. If a
1498
+ # block is given, call the block on each matching line, passing the file
1499
+ # name, line number, and the matching line of text. If no block is given,
1500
+ # a standard emac style file:linenumber:line message will be printed to
1501
+ # standard out.
1502
+ def egrep(pattern, *options)
1503
+ each do |fn|
1504
+ open(fn, "rb", *options) do |inf|
1505
+ count = 0
1506
+ inf.each do |line|
1507
+ count += 1
1508
+ if pattern.match(line)
1509
+ if block_given?
1510
+ yield fn, count, line
1511
+ else
1512
+ puts "#{fn}:#{count}:#{line}"
1513
+ end
1514
+ end
1515
+ end
1516
+ end
1517
+ end
1518
+ end
1519
+
1520
+ # Return a new file list that only contains file names from the current
1521
+ # file list that exist on the file system.
1522
+ def existing
1523
+ select { |fn| File.exist?(fn) }
1524
+ end
1525
+
1526
+ # Modify the current file list so that it contains only file name that
1527
+ # exist on the file system.
1528
+ def existing!
1529
+ resolve
1530
+ @items = @items.select { |fn| File.exist?(fn) }
1531
+ self
1532
+ end
1533
+
1534
+ # FileList version of partition. Needed because the nested arrays should
1535
+ # be FileLists in this version.
1536
+ def partition(&block) # :nodoc:
1537
+ resolve
1538
+ result = @items.partition(&block)
1539
+ [
1540
+ FileList.new.import(result[0]),
1541
+ FileList.new.import(result[1]),
1542
+ ]
1543
+ end
1544
+
1545
+ # Convert a FileList to a string by joining all elements with a space.
1546
+ def to_s
1547
+ resolve
1548
+ self.join(' ')
1549
+ end
1550
+
1551
+ # Add matching glob patterns.
1552
+ def add_matching(pattern)
1553
+ Dir[pattern].each do |fn|
1554
+ self << fn unless exclude?(fn)
1555
+ end
1556
+ end
1557
+ private :add_matching
1558
+
1559
+ # Should the given file name be excluded?
1560
+ def exclude?(fn)
1561
+ calculate_exclude_regexp unless @exclude_re
1562
+ fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
1563
+ end
1564
+
1565
+ DEFAULT_IGNORE_PATTERNS = [
1566
+ /(^|[\/\\])CVS([\/\\]|$)/,
1567
+ /(^|[\/\\])\.svn([\/\\]|$)/,
1568
+ /\.bak$/,
1569
+ /~$/
1570
+ ]
1571
+ DEFAULT_IGNORE_PROCS = [
1572
+ proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
1573
+ ]
1574
+ # @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1575
+
1576
+ def import(array)
1577
+ @items = array
1578
+ self
1579
+ end
1580
+
1581
+ class << self
1582
+ # Create a new file list including the files listed. Similar to:
1583
+ #
1584
+ # FileList.new(*args)
1585
+ def [](*args)
1586
+ new(*args)
1587
+ end
1588
+ end
1589
+ end # FileList
1590
+ end
1591
+
1592
+ module Rake
1593
+ class << self
1594
+
1595
+ # Yield each file or directory component.
1596
+ def each_dir_parent(dir) # :nodoc:
1597
+ old_length = nil
1598
+ while dir != '.' && dir.length != old_length
1599
+ yield(dir)
1600
+ old_length = dir.length
1601
+ dir = File.dirname(dir)
1602
+ end
1603
+ end
1604
+ end
1605
+ end # module Rake
1606
+
1607
+ # Alias FileList to be available at the top level.
1608
+ FileList = Rake::FileList
1609
+
1610
+ # ###########################################################################
1611
+ module Rake
1612
+
1613
+ # Default Rakefile loader used by +import+.
1614
+ class DefaultLoader
1615
+ def load(fn)
1616
+ Kernel.load(File.expand_path(fn))
1617
+ end
1618
+ end
1619
+
1620
+ # EarlyTime is a fake timestamp that occurs _before_ any other time value.
1621
+ class EarlyTime
1622
+ include Comparable
1623
+ include Singleton
1624
+
1625
+ def <=>(other)
1626
+ -1
1627
+ end
1628
+
1629
+ def to_s
1630
+ "<EARLY TIME>"
1631
+ end
1632
+ end
1633
+
1634
+ EARLY = EarlyTime.instance
1635
+ end # module Rake
1636
+
1637
+ # ###########################################################################
1638
+ # Extensions to time to allow comparisons with an early time class.
1639
+ #
1640
+ class Time
1641
+ alias rake_original_time_compare :<=>
1642
+ def <=>(other)
1643
+ if Rake::EarlyTime === other
1644
+ - other.<=>(self)
1645
+ else
1646
+ rake_original_time_compare(other)
1647
+ end
1648
+ end
1649
+ end # class Time
1650
+
1651
+ module Rake
1652
+
1653
+ ####################################################################
1654
+ # The NameSpace class will lookup task names in the the scope
1655
+ # defined by a +namespace+ command.
1656
+ #
1657
+ class NameSpace
1658
+
1659
+ # Create a namespace lookup object using the given task manager
1660
+ # and the list of scopes.
1661
+ def initialize(task_manager, scope_list)
1662
+ @task_manager = task_manager
1663
+ @scope = scope_list.dup
1664
+ end
1665
+
1666
+ # Lookup a task named +name+ in the namespace.
1667
+ def [](name)
1668
+ @task_manager.lookup(name, @scope)
1669
+ end
1670
+
1671
+ # Return the list of tasks defined in this and nested namespaces.
1672
+ def tasks
1673
+ @task_manager.tasks_in_scope(@scope)
1674
+ end
1675
+ end # NameSpace
1676
+
1677
+
1678
+ ####################################################################
1679
+ # The TaskManager module is a mixin for managing tasks.
1680
+ module TaskManager
1681
+ # Track the last comment made in the Rakefile.
1682
+ attr_accessor :last_description
1683
+ alias :last_comment :last_description # Backwards compatibility
1684
+
1685
+ def initialize
1686
+ super
1687
+ @tasks = Hash.new
1688
+ @rules = Array.new
1689
+ @scope = Array.new
1690
+ @last_description = nil
1691
+ end
1692
+
1693
+ def create_rule(*args, &block)
1694
+ pattern, arg_names, deps = resolve_args(args)
1695
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
1696
+ @rules << [pattern, deps, block]
1697
+ end
1698
+
1699
+ def define_task(task_class, *args, &block)
1700
+ task_name, arg_names, deps = resolve_args(args)
1701
+ task_name = task_class.scope_name(@scope, task_name)
1702
+ deps = [deps] unless deps.respond_to?(:to_ary)
1703
+ deps = deps.collect {|d| d.to_s }
1704
+ task = intern(task_class, task_name)
1705
+ task.set_arg_names(arg_names) unless arg_names.empty?
1706
+ task.add_description(@last_description)
1707
+ @last_description = nil
1708
+ task.enhance(deps, &block)
1709
+ task
1710
+ end
1711
+
1712
+ # Lookup a task. Return an existing task if found, otherwise
1713
+ # create a task of the current type.
1714
+ def intern(task_class, task_name)
1715
+ @tasks[task_name.to_s] ||= task_class.new(task_name, self)
1716
+ end
1717
+
1718
+ # Find a matching task for +task_name+.
1719
+ def [](task_name, scopes=nil)
1720
+ task_name = task_name.to_s
1721
+ self.lookup(task_name, scopes) or
1722
+ enhance_with_matching_rule(task_name) or
1723
+ synthesize_file_task(task_name) or
1724
+ fail "Don't know how to build task '#{task_name}'"
1725
+ end
1726
+
1727
+ def synthesize_file_task(task_name)
1728
+ return nil unless File.exist?(task_name)
1729
+ define_task(Rake::FileTask, task_name)
1730
+ end
1731
+
1732
+ # Resolve the arguments for a task/rule. Returns a triplet of
1733
+ # [task_name, arg_name_list, prerequisites].
1734
+ def resolve_args(args)
1735
+ if args.last.is_a?(Hash)
1736
+ deps = args.pop
1737
+ resolve_args_with_dependencies(args, deps)
1738
+ else
1739
+ resolve_args_without_dependencies(args)
1740
+ end
1741
+ end
1742
+
1743
+ # Resolve task arguments for a task or rule when there are no
1744
+ # dependencies declared.
1745
+ #
1746
+ # The patterns recognized by this argument resolving function are:
1747
+ #
1748
+ # task :t
1749
+ # task :t, [:a]
1750
+ # task :t, :a (deprecated)
1751
+ #
1752
+ def resolve_args_without_dependencies(args)
1753
+ task_name = args.shift
1754
+ if args.size == 1 && args.first.respond_to?(:to_ary)
1755
+ arg_names = args.first.to_ary
1756
+ else
1757
+ arg_names = args
1758
+ end
1759
+ [task_name, arg_names, []]
1760
+ end
1761
+ private :resolve_args_without_dependencies
1762
+
1763
+ # Resolve task arguments for a task or rule when there are
1764
+ # dependencies declared.
1765
+ #
1766
+ # The patterns recognized by this argument resolving function are:
1767
+ #
1768
+ # task :t => [:d]
1769
+ # task :t, [a] => [:d]
1770
+ # task :t, :needs => [:d] (deprecated)
1771
+ # task :t, :a, :needs => [:d] (deprecated)
1772
+ #
1773
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
1774
+ fail "Task Argument Error" if hash.size != 1
1775
+ key, value = hash.map { |k, v| [k,v] }.first
1776
+ if args.empty?
1777
+ task_name = key
1778
+ arg_names = []
1779
+ deps = value
1780
+ elsif key == :needs
1781
+ task_name = args.shift
1782
+ arg_names = args
1783
+ deps = value
1784
+ else
1785
+ task_name = args.shift
1786
+ arg_names = key
1787
+ deps = value
1788
+ end
1789
+ deps = [deps] unless deps.respond_to?(:to_ary)
1790
+ [task_name, arg_names, deps]
1791
+ end
1792
+ private :resolve_args_with_dependencies
1793
+
1794
+ # If a rule can be found that matches the task name, enhance the
1795
+ # task with the prerequisites and actions from the rule. Set the
1796
+ # source attribute of the task appropriately for the rule. Return
1797
+ # the enhanced task or nil of no rule was found.
1798
+ def enhance_with_matching_rule(task_name, level=0)
1799
+ fail Rake::RuleRecursionOverflowError,
1800
+ "Rule Recursion Too Deep" if level >= 16
1801
+ @rules.each do |pattern, extensions, block|
1802
+ if md = pattern.match(task_name)
1803
+ task = attempt_rule(task_name, extensions, block, level)
1804
+ return task if task
1805
+ end
1806
+ end
1807
+ nil
1808
+ rescue Rake::RuleRecursionOverflowError => ex
1809
+ ex.add_target(task_name)
1810
+ fail ex
1811
+ end
1812
+
1813
+ # List of all defined tasks in this application.
1814
+ def tasks
1815
+ @tasks.values.sort_by { |t| t.name }
1816
+ end
1817
+
1818
+ # List of all the tasks defined in the given scope (and its
1819
+ # sub-scopes).
1820
+ def tasks_in_scope(scope)
1821
+ prefix = scope.join(":")
1822
+ tasks.select { |t|
1823
+ /^#{prefix}:/ =~ t.name
1824
+ }
1825
+ end
1826
+
1827
+ # Clear all tasks in this application.
1828
+ def clear
1829
+ @tasks.clear
1830
+ @rules.clear
1831
+ end
1832
+
1833
+ # Lookup a task, using scope and the scope hints in the task name.
1834
+ # This method performs straight lookups without trying to
1835
+ # synthesize file tasks or rules. Special scope names (e.g. '^')
1836
+ # are recognized. If no scope argument is supplied, use the
1837
+ # current scope. Return nil if the task cannot be found.
1838
+ def lookup(task_name, initial_scope=nil)
1839
+ initial_scope ||= @scope
1840
+ task_name = task_name.to_s
1841
+ if task_name =~ /^rake:/
1842
+ scopes = []
1843
+ task_name = task_name.sub(/^rake:/, '')
1844
+ elsif task_name =~ /^(\^+)/
1845
+ scopes = initial_scope[0, initial_scope.size - $1.size]
1846
+ task_name = task_name.sub(/^(\^+)/, '')
1847
+ else
1848
+ scopes = initial_scope
1849
+ end
1850
+ lookup_in_scope(task_name, scopes)
1851
+ end
1852
+
1853
+ # Lookup the task name
1854
+ def lookup_in_scope(name, scope)
1855
+ n = scope.size
1856
+ while n >= 0
1857
+ tn = (scope[0,n] + [name]).join(':')
1858
+ task = @tasks[tn]
1859
+ return task if task
1860
+ n -= 1
1861
+ end
1862
+ nil
1863
+ end
1864
+ private :lookup_in_scope
1865
+
1866
+ # Return the list of scope names currently active in the task
1867
+ # manager.
1868
+ def current_scope
1869
+ @scope.dup
1870
+ end
1871
+
1872
+ # Evaluate the block in a nested namespace named +name+. Create
1873
+ # an anonymous namespace if +name+ is nil.
1874
+ def in_namespace(name)
1875
+ name ||= generate_name
1876
+ @scope.push(name)
1877
+ ns = NameSpace.new(self, @scope)
1878
+ yield(ns)
1879
+ ns
1880
+ ensure
1881
+ @scope.pop
1882
+ end
1883
+
1884
+ private
1885
+
1886
+ # Generate an anonymous namespace name.
1887
+ def generate_name
1888
+ @seed ||= 0
1889
+ @seed += 1
1890
+ "_anon_#{@seed}"
1891
+ end
1892
+
1893
+ def trace_rule(level, message)
1894
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
1895
+ end
1896
+
1897
+ # Attempt to create a rule given the list of prerequisites.
1898
+ def attempt_rule(task_name, extensions, block, level)
1899
+ sources = make_sources(task_name, extensions)
1900
+ prereqs = sources.collect { |source|
1901
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
1902
+ if File.exist?(source) || Rake::Task.task_defined?(source)
1903
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
1904
+ source
1905
+ elsif parent = enhance_with_matching_rule(source, level+1)
1906
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
1907
+ parent.name
1908
+ else
1909
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
1910
+ return nil
1911
+ end
1912
+ }
1913
+ task = FileTask.define_task({task_name => prereqs}, &block)
1914
+ task.sources = prereqs
1915
+ task
1916
+ end
1917
+
1918
+ # Make a list of sources from the list of file name extensions /
1919
+ # translation procs.
1920
+ def make_sources(task_name, extensions)
1921
+ extensions.collect { |ext|
1922
+ case ext
1923
+ when /%/
1924
+ task_name.pathmap(ext)
1925
+ when %r{/}
1926
+ ext
1927
+ when /^\./
1928
+ task_name.ext(ext)
1929
+ when String
1930
+ ext
1931
+ when Proc
1932
+ if ext.arity == 1
1933
+ ext.call(task_name)
1934
+ else
1935
+ ext.call
1936
+ end
1937
+ else
1938
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
1939
+ end
1940
+ }.flatten
1941
+ end
1942
+
1943
+ end # TaskManager
1944
+
1945
+ ######################################################################
1946
+ # Rake main application object. When invoking +rake+ from the
1947
+ # command line, a Rake::Application object is created and run.
1948
+ #
1949
+ class Application
1950
+ include TaskManager
1951
+
1952
+ # The name of the application (typically 'rake')
1953
+ attr_reader :name
1954
+
1955
+ # The original directory where rake was invoked.
1956
+ attr_reader :original_dir
1957
+
1958
+ # Name of the actual rakefile used.
1959
+ attr_reader :rakefile
1960
+
1961
+ # List of the top level task names (task names from the command line).
1962
+ attr_reader :top_level_tasks
1963
+
1964
+ DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
1965
+
1966
+ # Initialize a Rake::Application object.
1967
+ def initialize
1968
+ super
1969
+ @name = 'rake'
1970
+ @rakefiles = DEFAULT_RAKEFILES.dup
1971
+ @rakefile = nil
1972
+ @pending_imports = []
1973
+ @imported = []
1974
+ @loaders = {}
1975
+ @default_loader = Rake::DefaultLoader.new
1976
+ @original_dir = Dir.pwd
1977
+ @top_level_tasks = []
1978
+ add_loader('rb', DefaultLoader.new)
1979
+ add_loader('rf', DefaultLoader.new)
1980
+ add_loader('rake', DefaultLoader.new)
1981
+ @tty_output = STDOUT.tty?
1982
+ end
1983
+
1984
+ # Run the Rake application. The run method performs the following three steps:
1985
+ #
1986
+ # * Initialize the command line options (+init+).
1987
+ # * Define the tasks (+load_rakefile+).
1988
+ # * Run the top level tasks (+run_tasks+).
1989
+ #
1990
+ # If you wish to build a custom rake command, you should call +init+ on your
1991
+ # application. The define any tasks. Finally, call +top_level+ to run your top
1992
+ # level tasks.
1993
+ def run
1994
+ standard_exception_handling do
1995
+ init
1996
+ load_rakefile
1997
+ top_level
1998
+ end
1999
+ end
2000
+
2001
+ # Initialize the command line parameters and app name.
2002
+ def init(app_name='rake')
2003
+ standard_exception_handling do
2004
+ @name = app_name
2005
+ handle_options
2006
+ collect_tasks
2007
+ end
2008
+ end
2009
+
2010
+ # Find the rakefile and then load it and any pending imports.
2011
+ def load_rakefile
2012
+ standard_exception_handling do
2013
+ raw_load_rakefile
2014
+ end
2015
+ end
2016
+
2017
+ # Run the top level tasks of a Rake application.
2018
+ def top_level
2019
+ standard_exception_handling do
2020
+ if options.show_tasks
2021
+ display_tasks_and_comments
2022
+ elsif options.show_prereqs
2023
+ display_prerequisites
2024
+ else
2025
+ top_level_tasks.each { |task_name| invoke_task(task_name) }
2026
+ end
2027
+ end
2028
+ end
2029
+
2030
+ # Add a loader to handle imported files ending in the extension
2031
+ # +ext+.
2032
+ def add_loader(ext, loader)
2033
+ ext = ".#{ext}" unless ext =~ /^\./
2034
+ @loaders[ext] = loader
2035
+ end
2036
+
2037
+ # Application options from the command line
2038
+ def options
2039
+ @options ||= OpenStruct.new
2040
+ end
2041
+
2042
+ # private ----------------------------------------------------------------
2043
+
2044
+ def invoke_task(task_string)
2045
+ name, args = parse_task_string(task_string)
2046
+ t = self[name]
2047
+ t.invoke(*args)
2048
+ end
2049
+
2050
+ def parse_task_string(string)
2051
+ if string =~ /^([^\[]+)(\[(.*)\])$/
2052
+ name = $1
2053
+ args = $3.split(/\s*,\s*/)
2054
+ else
2055
+ name = string
2056
+ args = []
2057
+ end
2058
+ [name, args]
2059
+ end
2060
+
2061
+ # Provide standard execption handling for the given block.
2062
+ def standard_exception_handling
2063
+ begin
2064
+ yield
2065
+ rescue SystemExit => ex
2066
+ # Exit silently with current status
2067
+ raise
2068
+ rescue OptionParser::InvalidOption => ex
2069
+ # Exit silently
2070
+ exit(false)
2071
+ rescue Exception => ex
2072
+ # Exit with error message
2073
+ $stderr.puts "#{name} aborted!"
2074
+ $stderr.puts ex.message
2075
+ if options.trace
2076
+ $stderr.puts ex.backtrace.join("\n")
2077
+ else
2078
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2079
+ $stderr.puts "(See full trace by running task with --trace)"
2080
+ end
2081
+ exit(false)
2082
+ end
2083
+ end
2084
+
2085
+ # True if one of the files in RAKEFILES is in the current directory.
2086
+ # If a match is found, it is copied into @rakefile.
2087
+ def have_rakefile
2088
+ @rakefiles.each do |fn|
2089
+ if File.exist?(fn)
2090
+ others = Dir.glob(fn, File::FNM_CASEFOLD)
2091
+ return others.size == 1 ? others.first : fn
2092
+ elsif fn == ''
2093
+ return fn
2094
+ end
2095
+ end
2096
+ return nil
2097
+ end
2098
+
2099
+ # True if we are outputting to TTY, false otherwise
2100
+ def tty_output?
2101
+ @tty_output
2102
+ end
2103
+
2104
+ # Override the detected TTY output state (mostly for testing)
2105
+ def tty_output=( tty_output_state )
2106
+ @tty_output = tty_output_state
2107
+ end
2108
+
2109
+ # We will truncate output if we are outputting to a TTY or if we've been
2110
+ # given an explicit column width to honor
2111
+ def truncate_output?
2112
+ tty_output? || ENV['RAKE_COLUMNS']
2113
+ end
2114
+
2115
+ # Display the tasks and comments.
2116
+ def display_tasks_and_comments
2117
+ displayable_tasks = tasks.select { |t|
2118
+ t.comment && t.name =~ options.show_task_pattern
2119
+ }
2120
+ if options.full_description
2121
+ displayable_tasks.each do |t|
2122
+ puts "#{name} #{t.name_with_args}"
2123
+ t.full_comment.split("\n").each do |line|
2124
+ puts " #{line}"
2125
+ end
2126
+ puts
2127
+ end
2128
+ else
2129
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
2130
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
2131
+ displayable_tasks.each_with_index do |t,index|
2132
+ if options.interactive
2133
+ printf "#{index}) #{name} %-#{width}s # %s\n",
2134
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
2135
+ else
2136
+ printf "#{name} %-#{width}s # %s\n",
2137
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
2138
+ end
2139
+ end
2140
+ if options.interactive
2141
+ printf "Choose a task: "
2142
+ displayable_tasks[readline.to_i].invoke
2143
+ end
2144
+ end
2145
+ end
2146
+
2147
+ def terminal_width
2148
+ if ENV['RAKE_COLUMNS']
2149
+ result = ENV['RAKE_COLUMNS'].to_i
2150
+ else
2151
+ result = unix? ? dynamic_width : 80
2152
+ end
2153
+ (result < 10) ? 80 : result
2154
+ rescue
2155
+ 80
2156
+ end
2157
+
2158
+ # Calculate the dynamic width of the
2159
+ def dynamic_width
2160
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
2161
+ end
2162
+
2163
+ def dynamic_width_stty
2164
+ %x{stty size 2>/dev/null}.split[1].to_i
2165
+ end
2166
+
2167
+ def dynamic_width_tput
2168
+ %x{tput cols 2>/dev/null}.to_i
2169
+ end
2170
+
2171
+ def unix?
2172
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
2173
+ end
2174
+
2175
+ def windows?
2176
+ Win32.windows?
2177
+ end
2178
+
2179
+ def truncate(string, width)
2180
+ if string.length <= width
2181
+ string
2182
+ else
2183
+ ( string[0, width-3] || "" ) + "..."
2184
+ end
2185
+ end
2186
+
2187
+ # Display the tasks and prerequisites
2188
+ def display_prerequisites
2189
+ tasks.each do |t|
2190
+ puts "#{name} #{t.name}"
2191
+ t.prerequisites.each { |pre| puts " #{pre}" }
2192
+ end
2193
+ end
2194
+
2195
+ # A list of all the standard options used in rake, suitable for
2196
+ # passing to OptionParser.
2197
+ def standard_rake_options
2198
+ [
2199
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
2200
+ lambda { |value|
2201
+ require 'rake/classic_namespace'
2202
+ options.classic_namespace = true
2203
+ }
2204
+ ],
2205
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
2206
+ lambda { |value|
2207
+ options.show_tasks = true
2208
+ options.full_description = true
2209
+ options.show_task_pattern = Regexp.new(value || '')
2210
+ }
2211
+ ],
2212
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
2213
+ lambda { |value|
2214
+ verbose(true)
2215
+ nowrite(true)
2216
+ options.dryrun = true
2217
+ options.trace = true
2218
+ }
2219
+ ],
2220
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
2221
+ lambda { |value|
2222
+ eval(value)
2223
+ exit
2224
+ }
2225
+ ],
2226
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
2227
+ lambda { |value|
2228
+ puts eval(value)
2229
+ exit
2230
+ }
2231
+ ],
2232
+ ['--execute-continue', '-E CODE',
2233
+ "Execute some Ruby code, then continue with normal task processing.",
2234
+ lambda { |value| eval(value) }
2235
+ ],
2236
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
2237
+ lambda { |value| $:.push(value) }
2238
+ ],
2239
+ ['--interactive', '-i [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then choose the task you'd like to run.",
2240
+ lambda { |value|
2241
+ options.show_tasks = true
2242
+ options.show_task_pattern = Regexp.new(value || '')
2243
+ options.full_description = false
2244
+ options.interactive = true
2245
+ }
2246
+ ],
2247
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
2248
+ lambda { |value| options.show_prereqs = true }
2249
+ ],
2250
+ ['--quiet', '-q', "Do not log messages to standard output.",
2251
+ lambda { |value| verbose(false) }
2252
+ ],
2253
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
2254
+ lambda { |value|
2255
+ value ||= ''
2256
+ @rakefiles.clear
2257
+ @rakefiles << value
2258
+ }
2259
+ ],
2260
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
2261
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
2262
+ lambda { |value| options.rakelib = value.split(':') }
2263
+ ],
2264
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
2265
+ lambda { |value|
2266
+ begin
2267
+ require value
2268
+ rescue LoadError => ex
2269
+ begin
2270
+ rake_require value
2271
+ rescue LoadError => ex2
2272
+ raise ex
2273
+ end
2274
+ end
2275
+ }
2276
+ ],
2277
+ ['--rules', "Trace the rules resolution.",
2278
+ lambda { |value| options.trace_rules = true }
2279
+ ],
2280
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
2281
+ lambda { |value| options.nosearch = true }
2282
+ ],
2283
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
2284
+ lambda { |value|
2285
+ verbose(false)
2286
+ options.silent = true
2287
+ }
2288
+ ],
2289
+ ['--system', '-g',
2290
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
2291
+ lambda { |value| options.load_system = true }
2292
+ ],
2293
+ ['--no-system', '--nosystem', '-G',
2294
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
2295
+ lambda { |value| options.ignore_system = true }
2296
+ ],
2297
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
2298
+ lambda { |value|
2299
+ options.show_tasks = true
2300
+ options.show_task_pattern = Regexp.new(value || '')
2301
+ options.full_description = false
2302
+ }
2303
+ ],
2304
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
2305
+ lambda { |value|
2306
+ options.trace = true
2307
+ verbose(true)
2308
+ }
2309
+ ],
2310
+ ['--verbose', '-v', "Log message to standard output.",
2311
+ lambda { |value| verbose(true) }
2312
+ ],
2313
+ ['--version', '-V', "Display the program version.",
2314
+ lambda { |value|
2315
+ puts "rake, version #{RAKEVERSION}"
2316
+ exit
2317
+ }
2318
+ ]
2319
+ ]
2320
+ end
2321
+
2322
+ # Read and handle the command line options.
2323
+ def handle_options
2324
+ options.rakelib = ['rakelib']
2325
+
2326
+ OptionParser.new do |opts|
2327
+ opts.banner = "rake [-f rakefile] {options} targets..."
2328
+ opts.separator ""
2329
+ opts.separator "Options are ..."
2330
+
2331
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
2332
+ puts opts
2333
+ exit
2334
+ end
2335
+
2336
+ standard_rake_options.each { |args| opts.on(*args) }
2337
+ end.parse!
2338
+
2339
+ # If class namespaces are requested, set the global options
2340
+ # according to the values in the options structure.
2341
+ if options.classic_namespace
2342
+ $show_tasks = options.show_tasks
2343
+ $show_prereqs = options.show_prereqs
2344
+ $trace = options.trace
2345
+ $dryrun = options.dryrun
2346
+ $silent = options.silent
2347
+ end
2348
+ end
2349
+
2350
+ # Similar to the regular Ruby +require+ command, but will check
2351
+ # for *.rake files in addition to *.rb files.
2352
+ def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
2353
+ return false if loaded.include?(file_name)
2354
+ paths.each do |path|
2355
+ fn = file_name + ".rake"
2356
+ full_path = File.join(path, fn)
2357
+ if File.exist?(full_path)
2358
+ load full_path
2359
+ loaded << fn
2360
+ return true
2361
+ end
2362
+ end
2363
+ fail LoadError, "Can't find #{file_name}"
2364
+ end
2365
+
2366
+ def find_rakefile_location
2367
+ here = Dir.pwd
2368
+ while ! (fn = have_rakefile)
2369
+ Dir.chdir("..")
2370
+ if Dir.pwd == here || options.nosearch
2371
+ return nil
2372
+ end
2373
+ here = Dir.pwd
2374
+ end
2375
+ [fn, here]
2376
+ ensure
2377
+ Dir.chdir(Rake.original_dir)
2378
+ end
2379
+
2380
+ def raw_load_rakefile # :nodoc:
2381
+ rakefile, location = find_rakefile_location
2382
+ if (! options.ignore_system) &&
2383
+ (options.load_system || rakefile.nil?) &&
2384
+ system_dir && File.directory?(system_dir)
2385
+ puts "(in #{Dir.pwd})" unless options.silent
2386
+ glob("#{system_dir}/*.rake") do |name|
2387
+ add_import name
2388
+ end
2389
+ else
2390
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
2391
+ rakefile.nil?
2392
+ @rakefile = rakefile
2393
+ Dir.chdir(location)
2394
+ puts "(in #{Dir.pwd})" unless options.silent
2395
+ $rakefile = @rakefile if options.classic_namespace
2396
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
2397
+ options.rakelib.each do |rlib|
2398
+ glob("#{rlib}/*.rake") do |name|
2399
+ add_import name
2400
+ end
2401
+ end
2402
+ end
2403
+ load_imports
2404
+ end
2405
+
2406
+ def glob(path, &block)
2407
+ Dir[path.gsub("\\", '/')].each(&block)
2408
+ end
2409
+ private :glob
2410
+
2411
+ # The directory path containing the system wide rakefiles.
2412
+ def system_dir
2413
+ @system_dir ||=
2414
+ begin
2415
+ if ENV['RAKE_SYSTEM']
2416
+ ENV['RAKE_SYSTEM']
2417
+ else
2418
+ standard_system_dir
2419
+ end
2420
+ end
2421
+ end
2422
+
2423
+ # The standard directory containing system wide rake files.
2424
+ if Win32.windows?
2425
+ def standard_system_dir #:nodoc:
2426
+ Win32.win32_system_dir
2427
+ end
2428
+ else
2429
+ def standard_system_dir #:nodoc:
2430
+ File.join(File.expand_path('~'), '.rake')
2431
+ end
2432
+ end
2433
+ private :standard_system_dir
2434
+
2435
+ # Collect the list of tasks on the command line. If no tasks are
2436
+ # given, return a list containing only the default task.
2437
+ # Environmental assignments are processed at this time as well.
2438
+ def collect_tasks
2439
+ @top_level_tasks = []
2440
+ ARGV.each do |arg|
2441
+ if arg =~ /^(\w+)=(.*)$/
2442
+ ENV[$1] = $2
2443
+ else
2444
+ @top_level_tasks << arg unless arg =~ /^-/
2445
+ end
2446
+ end
2447
+ @top_level_tasks.push("default") if @top_level_tasks.size == 0
2448
+ end
2449
+
2450
+ # Add a file to the list of files to be imported.
2451
+ def add_import(fn)
2452
+ @pending_imports << fn
2453
+ end
2454
+
2455
+ # Load the pending list of imported files.
2456
+ def load_imports
2457
+ while fn = @pending_imports.shift
2458
+ next if @imported.member?(fn)
2459
+ if fn_task = lookup(fn)
2460
+ fn_task.invoke
2461
+ end
2462
+ ext = File.extname(fn)
2463
+ loader = @loaders[ext] || @default_loader
2464
+ loader.load(fn)
2465
+ @imported << fn
2466
+ end
2467
+ end
2468
+
2469
+ # Warn about deprecated use of top level constant names.
2470
+ def const_warning(const_name)
2471
+ @const_warning ||= false
2472
+ if ! @const_warning
2473
+ $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
2474
+ %{found at: #{rakefile_location}} # '
2475
+ $stderr.puts %{ Use --classic-namespace on rake command}
2476
+ $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
2477
+ end
2478
+ @const_warning = true
2479
+ end
2480
+
2481
+ def rakefile_location
2482
+ begin
2483
+ fail
2484
+ rescue RuntimeError => ex
2485
+ ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2486
+ end
2487
+ end
2488
+ end
2489
+ end
2490
+
2491
+
2492
+ class Module
2493
+ # Rename the original handler to make it available.
2494
+ alias :rake_original_const_missing :const_missing
2495
+
2496
+ # Check for deprecated uses of top level (i.e. in Object) uses of
2497
+ # Rake class names. If someone tries to reference the constant
2498
+ # name, display a warning and return the proper object. Using the
2499
+ # --classic-namespace command line option will define these
2500
+ # constants in Object and avoid this handler.
2501
+ def const_missing(const_name)
2502
+ case const_name
2503
+ when :Task
2504
+ Rake.application.const_warning(const_name)
2505
+ Rake::Task
2506
+ when :FileTask
2507
+ Rake.application.const_warning(const_name)
2508
+ Rake::FileTask
2509
+ when :FileCreationTask
2510
+ Rake.application.const_warning(const_name)
2511
+ Rake::FileCreationTask
2512
+ when :RakeApp
2513
+ Rake.application.const_warning(const_name)
2514
+ Rake::Application
2515
+ else
2516
+ rake_original_const_missing(const_name)
2517
+ end
2518
+ end
2519
+ end