drake 0.8.1.10.0

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