mguymon-buildr 1.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (220) hide show
  1. data/CHANGELOG +1291 -0
  2. data/LICENSE +176 -0
  3. data/NOTICE +26 -0
  4. data/README.rdoc +134 -0
  5. data/Rakefile +44 -0
  6. data/_buildr +35 -0
  7. data/_jbuildr +35 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/bnd.rb +147 -0
  10. data/addon/buildr/cobertura.rb +22 -0
  11. data/addon/buildr/drb.rb +281 -0
  12. data/addon/buildr/emma.rb +22 -0
  13. data/addon/buildr/hibernate.rb +149 -0
  14. data/addon/buildr/javacc.rb +85 -0
  15. data/addon/buildr/jaxb_xjc.rb +72 -0
  16. data/addon/buildr/jdepend.rb +60 -0
  17. data/addon/buildr/jetty.rb +248 -0
  18. data/addon/buildr/jibx.rb +86 -0
  19. data/addon/buildr/nailgun.rb +221 -0
  20. data/addon/buildr/openjpa.rb +88 -0
  21. data/addon/buildr/org/apache/buildr/BuildrNail$Main.class +0 -0
  22. data/addon/buildr/org/apache/buildr/BuildrNail.class +0 -0
  23. data/addon/buildr/org/apache/buildr/BuildrNail.java +41 -0
  24. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  25. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  26. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  27. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  28. data/addon/buildr/protobuf.rb +88 -0
  29. data/addon/buildr/xmlbeans.rb +93 -0
  30. data/bin/buildr +19 -0
  31. data/buildr.buildfile +58 -0
  32. data/buildr.gemspec +78 -0
  33. data/doc/_config.yml +1 -0
  34. data/doc/_layouts/default.html +90 -0
  35. data/doc/_layouts/preface.html +22 -0
  36. data/doc/artifacts.textile +217 -0
  37. data/doc/building.textile +276 -0
  38. data/doc/contributing.textile +268 -0
  39. data/doc/css/default.css +236 -0
  40. data/doc/css/print.css +101 -0
  41. data/doc/css/syntax.css +23 -0
  42. data/doc/download.textile +151 -0
  43. data/doc/extending.textile +212 -0
  44. data/doc/images/1442160941-frontcover.jpg +0 -0
  45. data/doc/images/asf-logo.gif +0 -0
  46. data/doc/images/asf-logo.png +0 -0
  47. data/doc/images/buildr-hires.png +0 -0
  48. data/doc/images/buildr.png +0 -0
  49. data/doc/images/favicon.png +0 -0
  50. data/doc/images/growl-icon.tiff +0 -0
  51. data/doc/images/note.png +0 -0
  52. data/doc/images/project-structure.png +0 -0
  53. data/doc/images/tip.png +0 -0
  54. data/doc/images/zbuildr.png +0 -0
  55. data/doc/images/zbuildr.tif +0 -0
  56. data/doc/index.textile +109 -0
  57. data/doc/installing.textile +284 -0
  58. data/doc/languages.textile +599 -0
  59. data/doc/mailing_lists.textile +29 -0
  60. data/doc/more_stuff.textile +845 -0
  61. data/doc/packaging.textile +618 -0
  62. data/doc/preface.textile +54 -0
  63. data/doc/projects.textile +276 -0
  64. data/doc/quick_start.textile +210 -0
  65. data/doc/releasing.textile +117 -0
  66. data/doc/scripts/buildr-git.rb +512 -0
  67. data/doc/scripts/gitflow.rb +296 -0
  68. data/doc/scripts/install-jruby.sh +44 -0
  69. data/doc/scripts/install-linux.sh +73 -0
  70. data/doc/scripts/install-osx.sh +52 -0
  71. data/doc/settings_profiles.textile +287 -0
  72. data/doc/testing.textile +247 -0
  73. data/etc/KEYS +189 -0
  74. data/lib/buildr.rb +44 -0
  75. data/lib/buildr/clojure.rb +34 -0
  76. data/lib/buildr/clojure/shell.rb +52 -0
  77. data/lib/buildr/core.rb +34 -0
  78. data/lib/buildr/core/application.rb +700 -0
  79. data/lib/buildr/core/build.rb +516 -0
  80. data/lib/buildr/core/cc.rb +166 -0
  81. data/lib/buildr/core/checks.rb +253 -0
  82. data/lib/buildr/core/common.rb +151 -0
  83. data/lib/buildr/core/compile.rb +622 -0
  84. data/lib/buildr/core/doc.rb +276 -0
  85. data/lib/buildr/core/environment.rb +129 -0
  86. data/lib/buildr/core/filter.rb +404 -0
  87. data/lib/buildr/core/generate.rb +197 -0
  88. data/lib/buildr/core/help.rb +119 -0
  89. data/lib/buildr/core/jrebel.rb +42 -0
  90. data/lib/buildr/core/linux.rb +30 -0
  91. data/lib/buildr/core/osx.rb +46 -0
  92. data/lib/buildr/core/progressbar.rb +161 -0
  93. data/lib/buildr/core/project.rb +975 -0
  94. data/lib/buildr/core/run.rb +43 -0
  95. data/lib/buildr/core/shell.rb +137 -0
  96. data/lib/buildr/core/test.rb +843 -0
  97. data/lib/buildr/core/transports.rb +575 -0
  98. data/lib/buildr/core/util.rb +537 -0
  99. data/lib/buildr/groovy.rb +20 -0
  100. data/lib/buildr/groovy/bdd.rb +106 -0
  101. data/lib/buildr/groovy/compiler.rb +153 -0
  102. data/lib/buildr/groovy/doc.rb +76 -0
  103. data/lib/buildr/groovy/shell.rb +57 -0
  104. data/lib/buildr/ide.rb +19 -0
  105. data/lib/buildr/ide/eclipse.rb +427 -0
  106. data/lib/buildr/ide/eclipse/java.rb +53 -0
  107. data/lib/buildr/ide/eclipse/plugin.rb +71 -0
  108. data/lib/buildr/ide/eclipse/scala.rb +68 -0
  109. data/lib/buildr/ide/idea.rb +576 -0
  110. data/lib/buildr/java.rb +25 -0
  111. data/lib/buildr/java/ant.rb +94 -0
  112. data/lib/buildr/java/bdd.rb +460 -0
  113. data/lib/buildr/java/cobertura.rb +297 -0
  114. data/lib/buildr/java/commands.rb +223 -0
  115. data/lib/buildr/java/compiler.rb +135 -0
  116. data/lib/buildr/java/deprecated.rb +141 -0
  117. data/lib/buildr/java/doc.rb +86 -0
  118. data/lib/buildr/java/ecj.rb +69 -0
  119. data/lib/buildr/java/emma.rb +244 -0
  120. data/lib/buildr/java/external.rb +73 -0
  121. data/lib/buildr/java/jruby.rb +122 -0
  122. data/lib/buildr/java/jtestr_result.rb +295 -0
  123. data/lib/buildr/java/jtestr_runner.rb.erb +116 -0
  124. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  125. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +142 -0
  126. data/lib/buildr/java/packaging.rb +734 -0
  127. data/lib/buildr/java/pom.rb +178 -0
  128. data/lib/buildr/java/rjb.rb +154 -0
  129. data/lib/buildr/java/test_result.rb +101 -0
  130. data/lib/buildr/java/tests.rb +362 -0
  131. data/lib/buildr/java/version_requirement.rb +172 -0
  132. data/lib/buildr/packaging.rb +25 -0
  133. data/lib/buildr/packaging/archive.rb +535 -0
  134. data/lib/buildr/packaging/artifact.rb +904 -0
  135. data/lib/buildr/packaging/artifact_namespace.rb +984 -0
  136. data/lib/buildr/packaging/artifact_search.rb +140 -0
  137. data/lib/buildr/packaging/gems.rb +105 -0
  138. data/lib/buildr/packaging/package.rb +249 -0
  139. data/lib/buildr/packaging/repository_array.rb +108 -0
  140. data/lib/buildr/packaging/tar.rb +189 -0
  141. data/lib/buildr/packaging/version_requirement.rb +192 -0
  142. data/lib/buildr/packaging/zip.rb +178 -0
  143. data/lib/buildr/packaging/ziptask.rb +356 -0
  144. data/lib/buildr/resources/buildr.icns +0 -0
  145. data/lib/buildr/resources/completed.png +0 -0
  146. data/lib/buildr/resources/failed.png +0 -0
  147. data/lib/buildr/resources/icons-license.txt +17 -0
  148. data/lib/buildr/run.rb +195 -0
  149. data/lib/buildr/scala.rb +26 -0
  150. data/lib/buildr/scala/bdd.rb +118 -0
  151. data/lib/buildr/scala/compiler.rb +242 -0
  152. data/lib/buildr/scala/doc.rb +142 -0
  153. data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner.class +0 -0
  154. data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner.java +57 -0
  155. data/lib/buildr/scala/shell.rb +52 -0
  156. data/lib/buildr/scala/tests.rb +171 -0
  157. data/lib/buildr/shell.rb +185 -0
  158. data/lib/buildr/version.rb +18 -0
  159. data/rakelib/all-in-one.rake +113 -0
  160. data/rakelib/checks.rake +57 -0
  161. data/rakelib/doc.rake +137 -0
  162. data/rakelib/metrics.rake +39 -0
  163. data/rakelib/package.rake +73 -0
  164. data/rakelib/release.rake +160 -0
  165. data/rakelib/rspec.rake +91 -0
  166. data/rakelib/setup.rake +66 -0
  167. data/rakelib/stage.rake +220 -0
  168. data/spec/addon/bnd_spec.rb +330 -0
  169. data/spec/addon/drb_spec.rb +328 -0
  170. data/spec/addon/jaxb_xjc_spec.rb +125 -0
  171. data/spec/core/application_spec.rb +631 -0
  172. data/spec/core/build_spec.rb +837 -0
  173. data/spec/core/cc_spec.rb +224 -0
  174. data/spec/core/checks_spec.rb +519 -0
  175. data/spec/core/common_spec.rb +725 -0
  176. data/spec/core/compile_spec.rb +658 -0
  177. data/spec/core/doc_spec.rb +195 -0
  178. data/spec/core/extension_spec.rb +201 -0
  179. data/spec/core/generate_spec.rb +33 -0
  180. data/spec/core/project_spec.rb +772 -0
  181. data/spec/core/run_spec.rb +93 -0
  182. data/spec/core/shell_spec.rb +146 -0
  183. data/spec/core/test_spec.rb +1320 -0
  184. data/spec/core/transport_spec.rb +544 -0
  185. data/spec/core/util_spec.rb +141 -0
  186. data/spec/groovy/bdd_spec.rb +80 -0
  187. data/spec/groovy/compiler_spec.rb +251 -0
  188. data/spec/groovy/doc_spec.rb +65 -0
  189. data/spec/ide/eclipse_spec.rb +739 -0
  190. data/spec/ide/idea_spec.rb +1145 -0
  191. data/spec/java/ant_spec.rb +37 -0
  192. data/spec/java/bdd_spec.rb +374 -0
  193. data/spec/java/cobertura_spec.rb +112 -0
  194. data/spec/java/commands_spec.rb +93 -0
  195. data/spec/java/compiler_spec.rb +252 -0
  196. data/spec/java/doc_spec.rb +56 -0
  197. data/spec/java/ecj_spec.rb +115 -0
  198. data/spec/java/emma_spec.rb +121 -0
  199. data/spec/java/external_spec.rb +56 -0
  200. data/spec/java/java_spec.rb +132 -0
  201. data/spec/java/packaging_spec.rb +1266 -0
  202. data/spec/java/run_spec.rb +78 -0
  203. data/spec/java/test_coverage_helper.rb +257 -0
  204. data/spec/java/tests_spec.rb +497 -0
  205. data/spec/packaging/archive_spec.rb +775 -0
  206. data/spec/packaging/artifact_namespace_spec.rb +743 -0
  207. data/spec/packaging/artifact_spec.rb +1074 -0
  208. data/spec/packaging/packaging_helper.rb +63 -0
  209. data/spec/packaging/packaging_spec.rb +719 -0
  210. data/spec/packaging/repository_array_spec.rb +109 -0
  211. data/spec/sandbox.rb +165 -0
  212. data/spec/scala/bdd_spec.rb +124 -0
  213. data/spec/scala/compiler_spec.rb +289 -0
  214. data/spec/scala/doc_spec.rb +88 -0
  215. data/spec/scala/scala.rb +38 -0
  216. data/spec/scala/tests_spec.rb +283 -0
  217. data/spec/spec_helpers.rb +369 -0
  218. data/spec/version_requirement_spec.rb +143 -0
  219. data/spec/xpath_matchers.rb +121 -0
  220. metadata +649 -0
@@ -0,0 +1,975 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+
17
+ require 'buildr/core/util'
18
+
19
+
20
+ module Buildr
21
+
22
+ # Symbolic mapping for directory layout. Used for both the default and custom layouts.
23
+ #
24
+ # For example, the default layout maps [:source, :main, :java] to 'src/main/java', and
25
+ # [:target, :main, :classes] to 'target/classes'. You can use this to change the layout
26
+ # of your projects.
27
+ #
28
+ # To map [:source, :main] into the 'sources' directory:
29
+ # my_layout = Layout.new
30
+ # my_layout[:source, :main] = 'sources'
31
+ #
32
+ # define 'foo', :layout=>my_layout do
33
+ # ...
34
+ # end
35
+ #
36
+ # To map [:source, :main, :java] to 'java/main':
37
+ # class MainLast < Layout
38
+ # def expand(*args)
39
+ # if args[0..1] == [:source, :main]
40
+ # super args[2], :main, *args[3,]
41
+ # else
42
+ # super
43
+ # end
44
+ # end
45
+ # end
46
+ #
47
+ # define 'foo', :layout=>MainLast do
48
+ # ...
49
+ # end
50
+ class Layout
51
+
52
+ class << self
53
+
54
+ # Default layout used by new projects.
55
+ attr_accessor :default
56
+
57
+ end
58
+
59
+ def initialize #:nodoc:
60
+ @mapping = {}
61
+ end
62
+
63
+ # Expands list of symbols and path names into a full path, for example:
64
+ # puts default.expand(:source, :main, :java)
65
+ # => "src/main/java"
66
+ def expand(*args)
67
+ args = args.compact.reject { |s| s.to_s.empty? }.map(&:to_sym)
68
+ return '' if args.empty?
69
+ @mapping[args] ||= File.join(*[expand(*args[0..-2]), args.last.to_s].reject(&:empty?)) if args.size > 1
70
+ return @mapping[args] || args.first.to_s
71
+ end
72
+
73
+ # Resolves a list of symbols into a path.
74
+ def [](*args)
75
+ @mapping[args.map(&:to_sym)]
76
+ end
77
+
78
+ # Specifies the path resolved from a list of symbols.
79
+ def []=(*args)
80
+ @mapping[args[0...-1].map(&:to_sym)] = args.last
81
+ end
82
+
83
+ def initialize_copy(copy)
84
+ copy.instance_variable_set :@mapping, @mapping.clone
85
+ end
86
+
87
+ # Default layout has the following properties:
88
+ # * :source maps to the 'src' directory.
89
+ # * Anything under :source maps verbatim (e.g. :source, :main becomes 'src/main')
90
+ # * :target maps to the 'target' directory.
91
+ # * :target, :main maps to the 'target' directory as well.
92
+ # * Anything under :target, :main maps verbatim (e.g. :target, :main, :classes becomes 'target/classes')
93
+ # * Anything else under :target also maps verbatim (e.g. :target, :test becomes 'target/test')
94
+ class Default < Layout
95
+
96
+ def initialize
97
+ super
98
+ self[:source] = 'src'
99
+ self[:target, :main] = 'target'
100
+ end
101
+
102
+ end
103
+
104
+ self.default = Default.new
105
+
106
+ end
107
+
108
+
109
+ # A project definition is where you define all the tasks associated with
110
+ # the project you're building.
111
+ #
112
+ # The project itself will define several life cycle tasks for you. For example,
113
+ # it automatically creates a compile task that will compile all the source files
114
+ # found in src/main/java into target/classes, a test task that will compile source
115
+ # files from src/test/java and run all the JUnit tests found there, and a build
116
+ # task to compile and then run the tests.
117
+ #
118
+ # You use the project definition to enhance these tasks, for example, telling the
119
+ # compile task which class path dependencies to use. Or telling the project how
120
+ # to package an artifact, e.g. creating a JAR using <tt>package :jar</tt>.
121
+ #
122
+ # You can also define additional tasks that are executed by project tasks,
123
+ # or invoked from rake.
124
+ #
125
+ # Tasks created by the project are all prefixed with the project name, e.g.
126
+ # the project foo creates the task foo:compile. If foo contains a sub-project bar,
127
+ # the later will define the task foo:bar:compile. Since the compile task is
128
+ # recursive, compiling foo will also compile foo:bar.
129
+ #
130
+ # If you run:
131
+ # buildr compile
132
+ # from the command line, it will execute the compile task of the current project.
133
+ #
134
+ # Projects and sub-projects follow a directory heirarchy. The Buildfile is assumed to
135
+ # reside in the same directory as the top-level project, and each sub-project is
136
+ # contained in a sub-directory in the same name. For example:
137
+ # /home/foo
138
+ # |__ Buildfile
139
+ # |__ src/main/java
140
+ # |__ foo
141
+ # |__ src/main/java
142
+ #
143
+ # The default structure of each project is assumed to be:
144
+ # src
145
+ # |__main
146
+ # | |__java <-- Source files to compile
147
+ # | |__resources <-- Resources to copy
148
+ # | |__webapp <-- For WARs
149
+ # |__test
150
+ # | |__java <-- Source files to compile (tests)
151
+ # | |__resources <-- Resources to copy (tests)
152
+ # |__target <-- Packages created here
153
+ # | |__classes <-- Generated when compiling
154
+ # | |__resources <-- Copied (and filtered) from resources
155
+ # | |__test/classes <-- Generated when compiling tests
156
+ # | |__test/resources <-- Copied (and filtered) from resources
157
+ # |__reports <-- Test, coverage and other reports
158
+ #
159
+ # You can change the project layout by passing a new Layout to the project definition.
160
+ #
161
+ # You can only define a project once using #define. Afterwards, you can obtain the project
162
+ # definition using #project. The order in which you define projects is not important,
163
+ # project definitions are evaluated when you ask for them. Circular dependencies will not
164
+ # work. Rake tasks are only created after the project is evaluated, so if you need to access
165
+ # a task (e.g. compile) use <code>project('foo').compile</code> instead of <code>task('foo:compile')</code>.
166
+ #
167
+ # For example:
168
+ # define 'myapp', :version=>'1.1' do
169
+ #
170
+ # define 'wepapp' do
171
+ # compile.with project('myapp:beans')
172
+ # package :war
173
+ # end
174
+ #
175
+ # define 'beans' do
176
+ # compile.with DEPENDS
177
+ # package :jar
178
+ # end
179
+ # end
180
+ #
181
+ # puts projects.map(&:name)
182
+ # => [ 'myapp', 'myapp:beans', 'myapp:webapp' ]
183
+ # puts project('myapp:webapp').parent.name
184
+ # => 'myapp'
185
+ # puts project('myapp:webapp').compile.classpath.map(&:to_spec)
186
+ # => 'myapp:myapp-beans:jar:1.1'
187
+ class Project < Rake::Task
188
+
189
+ class << self
190
+
191
+ # :call-seq:
192
+ # define(name, properties?) { |project| ... } => project
193
+ #
194
+ # See Buildr#define.
195
+ def define(name, properties, &block) #:nodoc:
196
+ # Make sure a sub-project is only defined within the parent project,
197
+ # to prevent silly mistakes that lead to inconsistencies (e.g.
198
+ # namespaces will be all out of whack).
199
+ Buildr.application.current_scope == name.split(':')[0...-1] or
200
+ raise "You can only define a sub project (#{name}) within the definition of its parent project"
201
+
202
+ @projects ||= {}
203
+ raise "You cannot define the same project (#{name}) more than once" if @projects[name]
204
+ # Projects with names like: compile, test, build are invalid, so we have
205
+ # to make sure the project has not the name of an already defined task
206
+ raise "Invalid project name: #{name.inspect} is already used for a task" if Buildr.application.lookup(name)
207
+
208
+ Project.define_task(name).tap do |project|
209
+ # Define the project to prevent duplicate definition.
210
+ @projects[name] = project
211
+ # Set the project properties first, actions may use them.
212
+ properties.each { |name, value| project.send "#{name}=", value } if properties
213
+ # Setup to call before/after define extension callbacks
214
+ # Don't cache list of extensions, since project may add new extensions.
215
+ project.enhance do |project|
216
+ project.send :call_callbacks, :before_define
217
+ project.enhance do |project|
218
+ project.send :call_callbacks, :after_define
219
+ end
220
+ end
221
+ project.enhance do |project|
222
+ @on_define.each { |extension| extension[project] }
223
+ end if @on_define
224
+ # Enhance the project using the definition block.
225
+ project.enhance { project.instance_exec project, &block } if block
226
+
227
+ # Top-level project? Invoke the project definition. Sub-project? We don't invoke
228
+ # the project definiton yet (allow project calls to establish order of evaluation),
229
+ # but must do so before the parent project's definition is done.
230
+ project.parent.enhance { project.invoke } if project.parent
231
+ end
232
+ end
233
+
234
+ # :call-seq:
235
+ # project(name) => project
236
+ #
237
+ # See Buildr#project.
238
+ def project(*args, &block) #:nodoc:
239
+ options = args.pop if Hash === args.last
240
+ return define(args.first, options, &block) if block
241
+ rake_check_options options, :scope if options
242
+ raise ArgumentError, 'Only one project name at a time' unless args.size == 1
243
+ @projects ||= {}
244
+ name = args.first.to_s
245
+ # Make sure parent project is evaluated (e.g. if looking for foo:bar, find foo first)
246
+ unless @projects[name]
247
+ parts = name.split(':')
248
+ project(parts.first, options || {}) if parts.size > 1
249
+ end
250
+ if options && options[:scope]
251
+ # We assume parent project is evaluated.
252
+ project = options[:scope].split(':').inject([[]]) { |scopes, scope| scopes << (scopes.last + [scope]) }.
253
+ map { |scope| @projects[(scope + [name]).join(':')] }.
254
+ select { |project| project }.last
255
+ end
256
+ project ||= @projects[name] # Not found in scope.
257
+ raise "No such project #{name}" unless project
258
+ project.invoke
259
+ project
260
+ end
261
+
262
+ # :call-seq:
263
+ # projects(*names) => projects
264
+ #
265
+ # See Buildr#projects.
266
+ def projects(*names) #:nodoc:
267
+ options = names.pop if Hash === names.last
268
+ rake_check_options options, :scope if options
269
+ @projects ||= {}
270
+ names = names.flatten
271
+ if options && options[:scope]
272
+ # We assume parent project is evaluated.
273
+ if names.empty?
274
+ parent = @projects[options[:scope].to_s] or raise "No such project #{options[:scope]}"
275
+ @projects.values.select { |project| project.parent == parent }.each { |project| project.invoke }.
276
+ map { |project| [project] + projects(:scope=>project) }.flatten.sort_by(&:name)
277
+ else
278
+ names.uniq.map { |name| project(name, :scope=>options[:scope]) }
279
+ end
280
+ elsif names.empty?
281
+ # Parent project(s) not evaluated so we don't know all the projects yet.
282
+ @projects.values.each(&:invoke)
283
+ @projects.keys.map { |name| project(name) or raise "No such project #{name}" }.sort_by(&:name)
284
+ else
285
+ # Parent project(s) not evaluated, for the sub-projects we may need to find.
286
+ names.map { |name| name.split(':') }.select { |name| name.size > 1 }.map(&:first).uniq.each { |name| project(name) }
287
+ names.uniq.map { |name| project(name) or raise "No such project #{name}" }.sort_by(&:name)
288
+ end
289
+ end
290
+
291
+ # :call-seq:
292
+ # clear
293
+ #
294
+ # Discard all project definitions.
295
+ def clear
296
+ @projects.clear if @projects
297
+ end
298
+
299
+ # :call-seq:
300
+ # local_task(name)
301
+ # local_task(name) { |name| ... }
302
+ #
303
+ # Defines a local task with an optional execution message.
304
+ #
305
+ # A local task is a task that executes a task with the same name, defined in the
306
+ # current project, the project's with a base directory that is the same as the
307
+ # current directory.
308
+ #
309
+ # Complicated? Try this:
310
+ # buildr build
311
+ # is the same as:
312
+ # buildr foo:build
313
+ # But:
314
+ # cd bar
315
+ # buildr build
316
+ # is the same as:
317
+ # buildr foo:bar:build
318
+ #
319
+ # The optional block is called with the project name when the task executes
320
+ # and returns a message that, for example "Building project #{name}".
321
+ def local_task(*args, &block)
322
+ task *args do |task, args|
323
+ args = task.arg_names.map {|n| args[n]}
324
+ local_projects do |project|
325
+ info block.call(project.name) if block
326
+ task("#{project.name}:#{task.name}").invoke *args
327
+ end
328
+ end
329
+ end
330
+
331
+ # *Deprecated* Check the Extension module to see how extensions are handled.
332
+ def on_define(&block)
333
+ Buildr.application.deprecated 'This method is deprecated, see Extension'
334
+ (@on_define ||= []) << block if block
335
+ end
336
+
337
+ def scope_name(scope, task_name) #:nodoc:
338
+ task_name
339
+ end
340
+
341
+ def local_projects(dir = nil, &block) #:nodoc:
342
+ dir = File.expand_path(dir || Buildr.application.original_dir)
343
+ projects = @projects ? @projects.values : []
344
+ projects = projects.select { |project| project.base_dir == dir }
345
+ if projects.empty? && dir != Dir.pwd && File.dirname(dir) != dir
346
+ local_projects(File.dirname(dir), &block)
347
+ elsif block
348
+ if projects.empty?
349
+ warn "No projects defined for directory #{Buildr.application.original_dir}"
350
+ else
351
+ projects.each { |project| block[project] }
352
+ end
353
+ else
354
+ projects
355
+ end
356
+ end
357
+
358
+ # :call-seq:
359
+ # parent_task(task_name) => task_name or nil
360
+ #
361
+ # Returns a parent task, basically a task in a higher namespace. For example, the parent
362
+ # of 'foo:test:compile' is 'foo:compile' and the parent of 'foo:compile' is 'compile'.
363
+ def parent_task(task_name) #:nodoc:
364
+ namespace = task_name.split(':')
365
+ last_name = namespace.pop
366
+ namespace.pop
367
+ Buildr.application.lookup((namespace + [last_name]).join(':'), []) unless namespace.empty?
368
+ end
369
+
370
+ # :call-seq:
371
+ # project_from_task(task) => project
372
+ #
373
+ # Figure out project associated to this task and return it.
374
+ def project_from_task(task) #:nodoc:
375
+ project = Buildr.application.lookup('rake:' + task.to_s.gsub(/:[^:]*$/, ''))
376
+ project if Project === project
377
+ end
378
+
379
+ # Loaded extension modules.
380
+ def extension_modules #:nodoc:
381
+ @extension_modules ||= []
382
+ end
383
+
384
+ # Extension callbacks that apply to all projects
385
+ def global_callbacks #:nodoc:
386
+ @global_callbacks ||= []
387
+ end
388
+ end
389
+
390
+
391
+ # Project has visibility to everything in the Buildr namespace.
392
+ include Buildr
393
+
394
+ # The project name. For example, 'foo' for the top-level project, and 'foo:bar'
395
+ # for its sub-project.
396
+ attr_reader :name
397
+
398
+ # The parent project if this is a sub-project.
399
+ attr_reader :parent
400
+
401
+ def initialize(*args) #:nodoc:
402
+ super
403
+ split = name.split(':')
404
+ if split.size > 1
405
+ # Get parent project, but do not invoke it's definition to prevent circular
406
+ # dependencies (it's being invoked right now, so calling project will fail).
407
+ @parent = task(split[0...-1].join(':'))
408
+ raise "No parent project #{split[0...-1].join(':')}" unless @parent && Project === parent
409
+ end
410
+ # Inherit all global callbacks
411
+ @callbacks = Project.global_callbacks.dup
412
+ end
413
+
414
+ # :call-seq:
415
+ # base_dir => path
416
+ #
417
+ # Returns the project's base directory.
418
+ #
419
+ # The Buildfile defines top-level project, so it's logical that the top-level project's
420
+ # base directory is the one in which we find the Buildfile. And each sub-project has
421
+ # a base directory that is one level down, with the same name as the sub-project.
422
+ #
423
+ # For example:
424
+ # /home/foo/ <-- base_directory of project 'foo'
425
+ # /home/foo/Buildfile <-- builds 'foo'
426
+ # /home/foo/bar <-- sub-project 'foo:bar'
427
+ def base_dir
428
+ if @base_dir.nil?
429
+ if parent
430
+ # For sub-project, a good default is a directory in the parent's base_dir,
431
+ # using the same name as the project.
432
+ @base_dir = File.expand_path(name.split(':').last, parent.base_dir)
433
+ else
434
+ # For top-level project, a good default is the directory where we found the Buildfile.
435
+ @base_dir = Dir.pwd
436
+ end
437
+ end
438
+ @base_dir
439
+ end
440
+
441
+ # Returns the layout associated with this project.
442
+ def layout
443
+ @layout ||= (parent ? parent.layout : Layout.default).clone
444
+ end
445
+
446
+ # :call-seq:
447
+ # path_to(*names) => path
448
+ #
449
+ # Returns a path from a combination of name, relative to the project's base directory.
450
+ # Essentially, joins all the supplied names and expands the path relative to #base_dir.
451
+ # Symbol arguments are converted to paths based on the layout, so whenever possible stick
452
+ # to these. For example:
453
+ # path_to(:source, :main, :java)
454
+ # => 'src/main/java'
455
+ #
456
+ # Keep in mind that all tasks are defined and executed relative to the Buildfile directory,
457
+ # so you want to use #path_to to get the actual path within the project as a matter of practice.
458
+ #
459
+ # For example:
460
+ # path_to('foo', 'bar')
461
+ # => foo/bar
462
+ # path_to('/tmp')
463
+ # => /tmp
464
+ # path_to(:base_dir, 'foo') # same as path_to('foo")
465
+ # => /home/project1/foo
466
+ def path_to(*names)
467
+ File.expand_path(layout.expand(*names), base_dir)
468
+ end
469
+ alias :_ :path_to
470
+
471
+ # :call-seq:
472
+ # file(path) => Task
473
+ # file(path=>prereqs) => Task
474
+ # file(path) { |task| ... } => Task
475
+ #
476
+ # Creates and returns a new file task in the project. Similar to calling Rake's
477
+ # file method, but the path is expanded relative to the project's base directory,
478
+ # and the task executes in the project's base directory.
479
+ #
480
+ # For example:
481
+ # define 'foo' do
482
+ # define 'bar' do
483
+ # file('src') { ... }
484
+ # end
485
+ # end
486
+ #
487
+ # puts project('foo:bar').file('src').to_s
488
+ # => '/home/foo/bar/src'
489
+ def file(*args, &block)
490
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
491
+ task = Rake::FileTask.define_task(path_to(task_name))
492
+ task.set_arg_names(arg_names) unless arg_names.empty?
493
+ task.enhance Array(deps), &block
494
+ end
495
+
496
+ # :call-seq:
497
+ # task(name) => Task
498
+ # task(name=>prereqs) => Task
499
+ # task(name) { |task| ... } => Task
500
+ #
501
+ # Creates and returns a new task in the project. Similar to calling Rake's task
502
+ # method, but prefixes the task name with the project name and executes the task
503
+ # in the project's base directory.
504
+ #
505
+ # For example:
506
+ # define 'foo' do
507
+ # task 'doda'
508
+ # end
509
+ #
510
+ # puts project('foo').task('doda').name
511
+ # => 'foo:doda'
512
+ #
513
+ # When called from within the project definition, creates a new task if the task
514
+ # does not already exist. If called from outside the project definition, returns
515
+ # the named task and raises an exception if the task is not defined.
516
+ #
517
+ # As with Rake's task method, calling this method enhances the task with the
518
+ # prerequisites and optional block.
519
+ def task(*args, &block)
520
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
521
+ if task_name =~ /^:/
522
+ task = Buildr.application.switch_to_namespace [] do
523
+ Rake::Task.define_task(task_name[1..-1])
524
+ end
525
+ elsif Buildr.application.current_scope == name.split(':')
526
+ task = Rake::Task.define_task(task_name)
527
+ else
528
+ unless task = Buildr.application.lookup(task_name, name.split(':'))
529
+ raise "You cannot define a project task outside the project definition, and no task #{name}:#{task_name} defined in the project"
530
+ end
531
+ end
532
+ task.set_arg_names(arg_names) unless arg_names.empty?
533
+ task.enhance Array(deps), &block
534
+ end
535
+
536
+ # :call-seq:
537
+ # recursive_task(name=>prereqs) { |task| ... }
538
+ #
539
+ # Define a recursive task. A recursive task executes itself and the same task
540
+ # in all the sub-projects.
541
+ def recursive_task(*args, &block)
542
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
543
+ task = Buildr.options.parallel ? multitask(task_name) : task(task_name)
544
+ parent.task(task_name).enhance [task] if parent
545
+ task.set_arg_names(arg_names) unless arg_names.empty?
546
+ task.enhance Array(deps), &block
547
+ end
548
+
549
+ # :call-seq:
550
+ # project(name) => project
551
+ # project => self
552
+ #
553
+ # Same as Buildr#project. This method is called on a project, so a relative name is
554
+ # sufficient to find a sub-project.
555
+ #
556
+ # When called on a project without a name, returns the project itself. You can use that when
557
+ # setting project properties, for example:
558
+ # define 'foo' do
559
+ # project.version = '1.0'
560
+ # end
561
+ def project(*args, &block)
562
+ if Hash === args.last
563
+ options = args.pop
564
+ else
565
+ options = {}
566
+ end
567
+ if args.empty?
568
+ self
569
+ else
570
+ Project.project *(args + [{ :scope=>self.name }.merge(options)]), &block
571
+ end
572
+ end
573
+
574
+ # :call-seq:
575
+ # projects(*names) => projects
576
+ #
577
+ # Same as Buildr#projects. This method is called on a project, so relative names are
578
+ # sufficient to find sub-projects.
579
+ def projects(*args)
580
+ if Hash === args.last
581
+ options = args.pop
582
+ else
583
+ options = {}
584
+ end
585
+ Project.projects *(args + [{ :scope=>self.name }.merge(options)])
586
+ end
587
+
588
+ def inspect #:nodoc:
589
+ %Q{project(#{name.inspect})}
590
+ end
591
+
592
+ def callbacks #:nodoc:
593
+ # global + project_local callbacks for this project
594
+ @callbacks ||= []
595
+ end
596
+
597
+ def calledback #:nodoc:
598
+ # project-local callbacks that have been called
599
+ @calledback ||= {}
600
+ end
601
+
602
+ protected
603
+
604
+ # :call-seq:
605
+ # base_dir = dir
606
+ #
607
+ # Sets the project's base directory. Allows you to specify a base directory by calling
608
+ # this accessor, or with the :base_dir property when calling #define.
609
+ #
610
+ # You can only set the base directory once for a given project, and only before accessing
611
+ # the base directory (for example, by calling #file or #path_to).
612
+ # Set the base directory. Note: you can only do this once for a project,
613
+ # and only before accessing the base directory. If you try reading the
614
+ # value with #base_dir, the base directory cannot be set again.
615
+ def base_dir=(dir)
616
+ raise 'Cannot set base directory twice, or after reading its value' if @base_dir
617
+ @base_dir = File.expand_path(dir)
618
+ end
619
+
620
+ # Sets the project layout. Accepts Layout object or class (or for that matter, anything
621
+ # that can expand).
622
+ def layout=(layout)
623
+ raise 'Cannot set directory layout twice, or after reading its value' if @layout
624
+ @layout = layout.is_a?(Class) ? layout.new : layout
625
+ end
626
+
627
+ # :call-seq:
628
+ # define(name, properties?) { |project| ... } => project
629
+ #
630
+ # Define a new sub-project within this project. See Buildr#define.
631
+ def define(name, properties = nil, &block)
632
+ Project.define "#{self.name}:#{name}", properties, &block
633
+ end
634
+
635
+ def execute(args) #:nodoc:
636
+ Buildr.application.switch_to_namespace name.split(':') do
637
+ super
638
+ end
639
+ end
640
+
641
+ # Call all extension callbacks for a particular phase, e.g. :before_define, :after_define.
642
+ def call_callbacks(phase) #:nodoc:
643
+ remaining = @callbacks.select { |cb| cb.phase == phase }
644
+ known_callbacks = remaining.map { |cb| cb.name }
645
+
646
+ # call each extension in order
647
+ until remaining.empty?
648
+ callback = first_satisfied(remaining, known_callbacks)
649
+ if callback.nil?
650
+ hash = remaining.map { |cb| { cb.name => cb.dependencies} }
651
+ fail "Unsatisfied dependencies in extensions for #{phase}: #{hash.inspect}"
652
+ end
653
+ callback.blocks.each { |b| b.call(self) }
654
+ end
655
+ end
656
+
657
+ private
658
+
659
+ # find first callback with satisfied dependencies
660
+ def first_satisfied(r, known_callbacks)
661
+ remaining_names = r.map { |cb| cb.name }
662
+ res = r.find do |cb|
663
+ cb.dependencies.each do |dep|
664
+ fail "Unknown #{phase.inspect} extension dependency: #{dep.inspect}" unless known_callbacks.index(dep)
665
+ end
666
+ satisfied = cb.dependencies.find { |dep| remaining_names.index(dep) } == nil
667
+ cb if satisfied
668
+ end
669
+ r.delete res
670
+ end
671
+
672
+ end
673
+
674
+
675
+ # The basic mechanism for extending projects in Buildr are Ruby modules. In fact,
676
+ # base features like compiling and testing are all developed in the form of modules,
677
+ # and then added to the core Project class.
678
+ #
679
+ # A module defines instance methods that are then mixed into the project and become
680
+ # instance methods of the project. There are two general ways for extending projects.
681
+ # You can extend all projects by including the module in Project:
682
+ # class Project
683
+ # include MyExtension
684
+ # end
685
+ # You can also extend a given project instance and only that instance by extending
686
+ # it with the module:
687
+ # define 'foo' do
688
+ # extend MyExtension
689
+ # end
690
+ #
691
+ # Some extensions require tighter integration with the project, specifically for
692
+ # setting up tasks and properties, or for configuring tasks based on the project
693
+ # definition. You can do that by adding callbacks to the process.
694
+ #
695
+ # The easiest way to add callbacks is by incorporating the Extension module in your
696
+ # own extension, and using the various class methods to define callback behavior:
697
+ # * first_time -- This block will be called once for any particular extension.
698
+ # You can use this to setup top-level and local tasks.
699
+ # * before_define -- This block is called once for the project with the project
700
+ # instance, right before running the project definition. You can use this
701
+ # to add tasks and set properties that will be used in the project definition.
702
+ # * after_define -- This block is called once for the project with the project
703
+ # instance, right after running the project definition. You can use this to
704
+ # do any post-processing that depends on the project definition.
705
+ #
706
+ # This example illustrates how to write a simple extension:
707
+ # module LinesOfCode
708
+ # include Extension
709
+ #
710
+ # first_time do
711
+ # # Define task not specific to any projet.
712
+ # desc 'Count lines of code in current project'
713
+ # Project.local_task('loc')
714
+ # end
715
+ #
716
+ # before_define do |project|
717
+ # # Define the loc task for this particular project.
718
+ # Rake::Task.define_task 'loc' do |task|
719
+ # lines = task.prerequisites.map { |path| Dir['#{path}/**/*'] }.flatten.uniq.
720
+ # inject(0) { |total, file| total + File.readlines(file).count }
721
+ # puts "Project #{project.name} has #{lines} lines of code"
722
+ # end
723
+ # end
724
+ #
725
+ # after_define do |project|
726
+ # # Now that we know all the source directories, add them.
727
+ # task('loc'=>compile.sources + compile.test.sources)
728
+ # end
729
+ #
730
+ # # To use this method in your project:
731
+ # # loc path_1, path_2
732
+ # def loc(*paths)
733
+ # task('loc'=>paths)
734
+ # end
735
+ #
736
+ # end
737
+ #
738
+ # class Buildr::Project
739
+ # include LinesOfCode
740
+ # end
741
+ module Extension
742
+
743
+ # Extension callback details
744
+ class Callback #:nodoc:
745
+ attr_accessor :phase, :name, :dependencies, :blocks
746
+
747
+ def initialize(phase, name, dependencies, blocks)
748
+ @phase = phase
749
+ @name = name
750
+ @dependencies = dependencies
751
+ @blocks = (blocks ? (Array === blocks ? blocks : [blocks]) : [])
752
+ end
753
+
754
+ def merge(callback)
755
+ Callback.new(phase, name, @dependencies + callback.dependencies, @blocks + callback.blocks)
756
+ end
757
+ end
758
+
759
+ def self.included(base) #:nodoc:
760
+ base.extend ClassMethods
761
+ end
762
+
763
+ # Methods added to the extension module when including Extension.
764
+ module ClassMethods
765
+
766
+ def included(base) #:nodoc:
767
+ # When included in Project, add module instance, merge callbacks and call first_time.
768
+ if Project == base && !base.extension_modules.include?(module_callbacks)
769
+ base.extension_modules << module_callbacks
770
+ merge_callbacks(base.global_callbacks, module_callbacks)
771
+ first_time = module_callbacks.select { |c| c.phase == :first_time }
772
+ first_time.each do |c|
773
+ c.blocks.each { |b| b.call }
774
+ end
775
+ end
776
+ end
777
+
778
+ def extended(base) #:nodoc:
779
+ # When extending project, merge after_define callbacks and call before_define callback(s)
780
+ # immediately
781
+ if Project === base
782
+ merge_callbacks(base.callbacks, module_callbacks.select { |cb| cb.phase == :after_define })
783
+ calls = module_callbacks.select { |cb| cb.phase == :before_define }
784
+ calls.each do |cb|
785
+ cb.blocks.each { |b| b.call(base) } unless base.calledback[cb]
786
+ base.calledback[cb] = cb
787
+ end
788
+ end
789
+ end
790
+
791
+ # This block will be called once for any particular extension included in Project.
792
+ # You can use this to setup top-level and local tasks.
793
+ def first_time(&block)
794
+ module_callbacks << Callback.new(:first_time, self.name, [], block)
795
+ end
796
+
797
+ # This block is called once for the project with the project instance,
798
+ # right before running the project definition. You can use this to add
799
+ # tasks and set properties that will be used in the project definition.
800
+ #
801
+ # The block may be named and dependencies may be declared similar to Rake
802
+ # task dependencies:
803
+ #
804
+ # before_define(:my_setup) do |project|
805
+ # # do stuff on project
806
+ # end
807
+ #
808
+ # # my_setup code must run before :compile
809
+ # before_define(:compile => :my_setup)
810
+ #
811
+ def before_define(*args, &block)
812
+ if args.empty?
813
+ name = self.name
814
+ deps = []
815
+ else
816
+ name, args, deps = Buildr.application.resolve_args(args)
817
+ end
818
+ module_callbacks << Callback.new(:before_define, name, deps, block)
819
+ end
820
+
821
+ # This block is called once for the project with the project instance,
822
+ # right after running the project definition. You can use this to do
823
+ # any post-processing that depends on the project definition.
824
+ #
825
+ # The block may be named and dependencies may be declared similar to Rake
826
+ # task dependencies:
827
+ #
828
+ # after_define(:my_setup) do |project|
829
+ # # do stuff on project
830
+ # end
831
+ #
832
+ # # my_setup code must run before :compile (but only after project is defined)
833
+ # after_define(:compile => :my_setup)
834
+ #
835
+ def after_define(*args, &block)
836
+ if args.empty?
837
+ name = self.name
838
+ deps = []
839
+ else
840
+ name, args, deps = Buildr.application.resolve_args(args)
841
+ end
842
+ module_callbacks << Callback.new(:after_define, name, deps, block)
843
+ end
844
+
845
+ private
846
+
847
+ def module_callbacks
848
+ begin
849
+ const_get('Callbacks')
850
+ rescue
851
+ callbacks = []
852
+ const_set('Callbacks', callbacks)
853
+ end
854
+ end
855
+
856
+ def merge_callbacks(base, merge)
857
+ # index by phase and name
858
+ index = base.inject({}) { |hash,cb| { [cb.phase, cb.name] => cb } }
859
+ merge.each do |cb|
860
+ existing = index[[cb.phase, cb.name]]
861
+ if existing
862
+ base[base.index(existing)] = existing.merge(cb)
863
+ else
864
+ base << cb
865
+ end
866
+ index[[cb.phase, cb.name]] = cb
867
+ end
868
+ base
869
+ end
870
+ end
871
+
872
+ end
873
+
874
+
875
+ # :call-seq:
876
+ # define(name, properties?) { |project| ... } => project
877
+ #
878
+ # Defines a new project.
879
+ #
880
+ # The first argument is the project name. Each project must have a unique name.
881
+ # For a sub-project, the actual project name is created by prefixing the parent
882
+ # project's name.
883
+ #
884
+ # The second argument is optional and contains a hash or properties that are set
885
+ # on the project. You can only use properties that are supported by the project
886
+ # definition, e.g. :group and :version. You can also set these properties from the
887
+ # project definition.
888
+ #
889
+ # You pass a block that is executed in the context of the project definition.
890
+ # This block is used to define the project and tasks that are part of the project.
891
+ # Do not perform any work inside the project itself, as it will execute each time
892
+ # the Buildfile is loaded. Instead, use it to create and extend tasks that are
893
+ # related to the project.
894
+ #
895
+ # For example:
896
+ # define 'foo', :version=>'1.0' do
897
+ #
898
+ # define 'bar' do
899
+ # compile.with 'org.apache.axis2:axis2:jar:1.1'
900
+ # end
901
+ # end
902
+ #
903
+ # puts project('foo').version
904
+ # => '1.0'
905
+ # puts project('foo:bar').compile.classpath.map(&:to_spec)
906
+ # => 'org.apache.axis2:axis2:jar:1.1'
907
+ # % buildr build
908
+ # => Compiling 14 source files in foo:bar
909
+ def define(name, properties = nil, &block) #:yields:project
910
+ Project.define(name, properties, &block)
911
+ end
912
+
913
+ # :call-seq:
914
+ # project(name) => project
915
+ #
916
+ # Returns a project definition.
917
+ #
918
+ # When called from outside a project definition, must reference the project by its
919
+ # full name, e.g. 'foo:bar' to access the sub-project 'bar' in 'foo'. When called
920
+ # from inside a project, relative names are sufficient, e.g. <code>project('foo').project('bar')</code>
921
+ # will find the sub-project 'bar' in 'foo'.
922
+ #
923
+ # You cannot reference a project before the project is defined. When working with
924
+ # sub-projects, the project definition is stored by calling #define, and evaluated
925
+ # before a call to the parent project's #define method returns.
926
+ #
927
+ # However, if you call #project with the name of another sub-project, its definition
928
+ # is evaluated immediately. So the returned project definition is always complete,
929
+ # and you can access its definition (e.g. to find files relative to the base directory,
930
+ # or packages created by that project).
931
+ #
932
+ # For example:
933
+ # define 'myapp' do
934
+ # self.version = '1.1'
935
+ #
936
+ # define 'webapp' do
937
+ # # webapp is defined first, but beans is evaluated first
938
+ # compile.with project('beans')
939
+ # package :war
940
+ # end
941
+ #
942
+ # define 'beans' do
943
+ # package :jar
944
+ # end
945
+ # end
946
+ #
947
+ # puts project('myapp:beans').version
948
+ def project(*args, &block)
949
+ Project.project *args, &block
950
+ end
951
+
952
+ # :call-seq:
953
+ # projects(*names) => projects
954
+ #
955
+ # With no arguments, returns a list of all projects defined so far. When called on a project,
956
+ # returns all its sub-projects (direct descendants).
957
+ #
958
+ # With arguments, returns a list of named projects, fails on any name that does not exist.
959
+ # As with #project, you can use relative names when calling this method on a project.
960
+ #
961
+ # Like #project, this method evaluates the definition of each project before returning it.
962
+ # Be advised of circular dependencies.
963
+ #
964
+ # For example:
965
+ # files = projects.map { |prj| FileList[prj.path_to('src/**/*.java') }.flatten
966
+ # puts "There are #{files.size} source files in #{projects.size} projects"
967
+ #
968
+ # puts projects('myapp:beans', 'myapp:webapp').map(&:name)
969
+ # Same as:
970
+ # puts project('myapp').projects.map(&:name)
971
+ def projects(*args)
972
+ Project.projects *args
973
+ end
974
+
975
+ end