C0 code coverage information
Generated on Wed Oct 07 08:33:58 -0700 2009 with rcov 0.8.2.1
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
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 # Instantiate callbacks for this project, and setup to call before/after define.
214 # Don't cache list of callbacks, since project may add new callbacks.
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 { |callback| callback[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) #:nodoc:
239 options = args.pop if Hash === args.last
240 rake_check_options options, :scope if options
241 raise ArgumentError, 'Only one project name at a time' unless args.size == 1
242 @projects ||= {}
243 name = args.first.to_s
244 # Make sure parent project is evaluated (e.g. if looking for foo:bar, find foo first)
245 unless @projects[name]
246 parts = name.split(':')
247 project(parts.first, options || {}) if parts.size > 1
248 end
249 if options && options[:scope]
250 # We assume parent project is evaluated.
251 project = options[:scope].split(':').inject([[]]) { |scopes, scope| scopes << (scopes.last + [scope]) }.
252 map { |scope| @projects[(scope + [name]).join(':')] }.
253 select { |project| project }.last
254 end
255 project ||= @projects[name] # Not found in scope.
256 raise "No such project #{name}" unless project
257 project.invoke
258 project
259 end
260
261 # :call-seq:
262 # projects(*names) => projects
263 #
264 # See Buildr#projects.
265 def projects(*names) #:nodoc:
266 options = names.pop if Hash === names.last
267 rake_check_options options, :scope if options
268 @projects ||= {}
269 names = names.flatten
270 if options && options[:scope]
271 # We assume parent project is evaluated.
272 if names.empty?
273 parent = @projects[options[:scope].to_s] or raise "No such project #{options[:scope]}"
274 @projects.values.select { |project| project.parent == parent }.each { |project| project.invoke }.
275 map { |project| [project] + projects(:scope=>project) }.flatten.sort_by(&:name)
276 else
277 names.uniq.map { |name| project(name, :scope=>options[:scope]) }
278 end
279 elsif names.empty?
280 # Parent project(s) not evaluated so we don't know all the projects yet.
281 @projects.values.each(&:invoke)
282 @projects.keys.map { |name| project(name) or raise "No such project #{name}" }.sort_by(&:name)
283 else
284 # Parent project(s) not evaluated, for the sub-projects we may need to find.
285 names.map { |name| name.split(':') }.select { |name| name.size > 1 }.map(&:first).uniq.each { |name| project(name) }
286 names.uniq.map { |name| project(name) or raise "No such project #{name}" }.sort_by(&:name)
287 end
288 end
289
290 # :call-seq:
291 # clear
292 #
293 # Discard all project definitions.
294 def clear
295 @projects.clear if @projects
296 end
297
298 # :call-seq:
299 # local_task(name)
300 # local_task(name) { |name| ... }
301 #
302 # Defines a local task with an optional execution message.
303 #
304 # A local task is a task that executes a task with the same name, defined in the
305 # current project, the project's with a base directory that is the same as the
306 # current directory.
307 #
308 # Complicated? Try this:
309 # buildr build
310 # is the same as:
311 # buildr foo:build
312 # But:
313 # cd bar
314 # buildr build
315 # is the same as:
316 # buildr foo:bar:build
317 #
318 # The optional block is called with the project name when the task executes
319 # and returns a message that, for example "Building project #{name}".
320 def local_task(*args, &block)
321 task *args do |task, args|
322 args = task.arg_names.map {|n| args[n]}
323 local_projects do |project|
324 info block.call(project.name) if block
325 task("#{project.name}:#{task.name}").invoke *args
326 end
327 end
328 end
329
330 # *Deprecated* Check the Extension module to see how extensions are handled.
331 def on_define(&block)
332 Buildr.application.deprecated 'This method is deprecated, see Extension'
333 (@on_define ||= []) << block if block
334 end
335
336 def scope_name(scope, task_name) #:nodoc:
337 task_name
338 end
339
340 def local_projects(dir = nil, &block) #:nodoc:
341 dir = File.expand_path(dir || Buildr.application.original_dir)
342 projects = @projects ? @projects.values : []
343 projects = projects.select { |project| project.base_dir == dir }
344 if projects.empty? && dir != Dir.pwd && File.dirname(dir) != dir
345 local_projects(File.dirname(dir), &block)
346 elsif block
347 if projects.empty?
348 warn "No projects defined for directory #{Buildr.application.original_dir}"
349 else
350 projects.each { |project| block[project] }
351 end
352 else
353 projects
354 end
355 end
356
357 # :call-seq:
358 # parent_task(task_name) => task_name or nil
359 #
360 # Returns a parent task, basically a task in a higher namespace. For example, the parent
361 # of 'foo:test:compile' is 'foo:compile' and the parent of 'foo:compile' is 'compile'.
362 def parent_task(task_name) #:nodoc:
363 namespace = task_name.split(':')
364 last_name = namespace.pop
365 namespace.pop
366 Buildr.application.lookup((namespace + [last_name]).join(':'), []) unless namespace.empty?
367 end
368
369 # :call-seq:
370 # project_from_task(task) => project
371 #
372 # Figure out project associated to this task and return it.
373 def project_from_task(task) #:nodoc:
374 project = Buildr.application.lookup('rake:' + task.to_s.gsub(/:[^:]*$/, ''))
375 project if Project === project
376 end
377
378 # Callback classes.
379 def callbacks #:nodoc:
380 @callbacks ||= []
381 end
382
383 end
384
385
386 # Project has visibility to everything in the Buildr namespace.
387 include Buildr
388
389 # The project name. For example, 'foo' for the top-level project, and 'foo:bar'
390 # for its sub-project.
391 attr_reader :name
392
393 # The parent project if this is a sub-project.
394 attr_reader :parent
395
396 def initialize(*args) #:nodoc:
397 super
398 split = name.split(':')
399 if split.size > 1
400 # Get parent project, but do not invoke it's definition to prevent circular
401 # dependencies (it's being invoked right now, so calling project will fail).
402 @parent = task(split[0...-1].join(':'))
403 raise "No parent project #{split[0...-1].join(':')}" unless @parent && Project === parent
404 end
405 callbacks = Project.callbacks.uniq.map(&:new)
406 @callbacks = [:before_define, :after_define].inject({}) do |hash, state|
407 methods = callbacks.select { |callback| callback.respond_to?(state) }.map { |callback| callback.method(state) }
408 hash.update(state=>methods)
409 end
410 end
411
412 # :call-seq:
413 # base_dir => path
414 #
415 # Returns the project's base directory.
416 #
417 # The Buildfile defines top-level project, so it's logical that the top-level project's
418 # base directory is the one in which we find the Buildfile. And each sub-project has
419 # a base directory that is one level down, with the same name as the sub-project.
420 #
421 # For example:
422 # /home/foo/ <-- base_directory of project 'foo'
423 # /home/foo/Buildfile <-- builds 'foo'
424 # /home/foo/bar <-- sub-project 'foo:bar'
425 def base_dir
426 if @base_dir.nil?
427 if parent
428 # For sub-project, a good default is a directory in the parent's base_dir,
429 # using the same name as the project.
430 @base_dir = File.expand_path(name.split(':').last, parent.base_dir)
431 else
432 # For top-level project, a good default is the directory where we found the Buildfile.
433 @base_dir = Dir.pwd
434 end
435 end
436 @base_dir
437 end
438
439 # Returns the layout associated with this project.
440 def layout
441 @layout ||= (parent ? parent.layout : Layout.default).clone
442 end
443
444 # :call-seq:
445 # path_to(*names) => path
446 #
447 # Returns a path from a combination of name, relative to the project's base directory.
448 # Essentially, joins all the supplied names and expands the path relative to #base_dir.
449 # Symbol arguments are converted to paths based on the layout, so whenever possible stick
450 # to these. For example:
451 # path_to(:source, :main, :java)
452 # => 'src/main/java'
453 #
454 # Keep in mind that all tasks are defined and executed relative to the Buildfile directory,
455 # so you want to use #path_to to get the actual path within the project as a matter of practice.
456 #
457 # For example:
458 # path_to('foo', 'bar')
459 # => foo/bar
460 # path_to('/tmp')
461 # => /tmp
462 # path_to(:base_dir, 'foo') # same as path_to('foo")
463 # => /home/project1/foo
464 def path_to(*names)
465 File.expand_path(layout.expand(*names), base_dir)
466 end
467 alias :_ :path_to
468
469 # :call-seq:
470 # file(path) => Task
471 # file(path=>prereqs) => Task
472 # file(path) { |task| ... } => Task
473 #
474 # Creates and returns a new file task in the project. Similar to calling Rake's
475 # file method, but the path is expanded relative to the project's base directory,
476 # and the task executes in the project's base directory.
477 #
478 # For example:
479 # define 'foo' do
480 # define 'bar' do
481 # file('src') { ... }
482 # end
483 # end
484 #
485 # puts project('foo:bar').file('src').to_s
486 # => '/home/foo/bar/src'
487 def file(*args, &block)
488 task_name, arg_names, deps = Buildr.application.resolve_args(args)
489 task = Rake::FileTask.define_task(path_to(task_name))
490 task.set_arg_names(arg_names) unless arg_names.empty?
491 task.enhance Array(deps), &block
492 end
493
494 # :call-seq:
495 # task(name) => Task
496 # task(name=>prereqs) => Task
497 # task(name) { |task| ... } => Task
498 #
499 # Creates and returns a new task in the project. Similar to calling Rake's task
500 # method, but prefixes the task name with the project name and executes the task
501 # in the project's base directory.
502 #
503 # For example:
504 # define 'foo' do
505 # task 'doda'
506 # end
507 #
508 # puts project('foo').task('doda').name
509 # => 'foo:doda'
510 #
511 # When called from within the project definition, creates a new task if the task
512 # does not already exist. If called from outside the project definition, returns
513 # the named task and raises an exception if the task is not defined.
514 #
515 # As with Rake's task method, calling this method enhances the task with the
516 # prerequisites and optional block.
517 def task(*args, &block)
518 task_name, arg_names, deps = Buildr.application.resolve_args(args)
519 if task_name =~ /^:/
520 task = Buildr.application.switch_to_namespace [] do
521 Rake::Task.define_task(task_name[1..-1])
522 end
523 elsif Buildr.application.current_scope == name.split(':')
524 task = Rake::Task.define_task(task_name)
525 else
526 unless task = Buildr.application.lookup(task_name, name.split(':'))
527 raise "You cannot define a project task outside the project definition, and no task #{name}:#{task_name} defined in the project"
528 end
529 end
530 task.set_arg_names(arg_names) unless arg_names.empty?
531 task.enhance Array(deps), &block
532 end
533
534 # :call-seq:
535 # recursive_task(name=>prereqs) { |task| ... }
536 #
537 # Define a recursive task. A recursive task executes itself and the same task
538 # in all the sub-projects.
539 def recursive_task(*args, &block)
540 task_name, arg_names, deps = Buildr.application.resolve_args(args)
541 task = Buildr.options.parallel ? multitask(task_name) : task(task_name)
542 parent.task(task_name).enhance [task] if parent
543 task.set_arg_names(arg_names) unless arg_names.empty?
544 task.enhance Array(deps), &block
545 end
546
547 # :call-seq:
548 # project(name) => project
549 # project => self
550 #
551 # Same as Buildr#project. This method is called on a project, so a relative name is
552 # sufficient to find a sub-project.
553 #
554 # When called on a project without a name, returns the project itself. You can use that when
555 # setting project properties, for example:
556 # define 'foo' do
557 # project.version = '1.0'
558 # end
559 def project(*args)
560 if Hash === args.last
561 options = args.pop
562 else
563 options = {}
564 end
565 if args.empty?
566 self
567 else
568 Project.project *(args + [{ :scope=>self.name }.merge(options)])
569 end
570 end
571
572 # :call-seq:
573 # projects(*names) => projects
574 #
575 # Same as Buildr#projects. This method is called on a project, so relative names are
576 # sufficient to find sub-projects.
577 def projects(*args)
578 if Hash === args.last
579 options = args.pop
580 else
581 options = {}
582 end
583 Project.projects *(args + [{ :scope=>self.name }.merge(options)])
584 end
585
586 def inspect #:nodoc:
587 %Q{project(#{name.inspect})}
588 end
589
590 protected
591
592 # :call-seq:
593 # base_dir = dir
594 #
595 # Sets the project's base directory. Allows you to specify a base directory by calling
596 # this accessor, or with the :base_dir property when calling #define.
597 #
598 # You can only set the base directory once for a given project, and only before accessing
599 # the base directory (for example, by calling #file or #path_to).
600 # Set the base directory. Note: you can only do this once for a project,
601 # and only before accessing the base directory. If you try reading the
602 # value with #base_dir, the base directory cannot be set again.
603 def base_dir=(dir)
604 raise 'Cannot set base directory twice, or after reading its value' if @base_dir
605 @base_dir = File.expand_path(dir)
606 end
607
608 # Sets the project layout. Accepts Layout object or class (or for that matter, anything
609 # that can expand).
610 def layout=(layout)
611 raise 'Cannot set directory layout twice, or after reading its value' if @layout
612 @layout = layout.is_a?(Class) ? layout.new : layout
613 end
614
615 # :call-seq:
616 # define(name, properties?) { |project| ... } => project
617 #
618 # Define a new sub-project within this project. See Buildr#define.
619 def define(name, properties = nil, &block)
620 Project.define "#{self.name}:#{name}", properties, &block
621 end
622
623 def execute(args) #:nodoc:
624 Buildr.application.switch_to_namespace name.split(':') do
625 super
626 end
627 end
628
629 # Call all callbacks for a particular state, e.g. :before_define, :after_define.
630 def call_callbacks(state) #:nodoc:
631 methods = @callbacks.delete(state) || []
632 methods.each { |method| method.call(project) }
633 end
634
635 def add_callback(callback)
636 @callbacks[:after_define] << callback.method(:after_define) if callback.respond_to?(:after_define)
637 end
638
639 end
640
641
642 # The basic mechanism for extending projects in Buildr are Ruby modules. In fact,
643 # base features like compiling and testing are all developed in the form of modules,
644 # and then added to the core Project class.
645 #
646 # A module defines instance methods that are then mixed into the project and become
647 # instance methods of the project. There are two general ways for extending projects.
648 # You can extend all projects by including the module in Project:
649 # class Project
650 # include MyExtension
651 # end
652 # You can also extend a given project instance and only that instance by extending
653 # it with the module:
654 # define 'foo' do
655 # extend MyExtension
656 # end
657 #
658 # Some extensions require tighter integration with the project, specifically for
659 # setting up tasks and properties, or for configuring tasks based on the project
660 # definition. You can do that by adding callbacks to the process.
661 #
662 # The easiest way to add callbacks is by incorporating the Extension module in your
663 # own extension, and using the various class methods to define callback behavior:
664 # * first_time -- This block will be called once for any particular extension.
665 # You can use this to setup top-level and local tasks.
666 # * before_define -- This block is called once for the project with the project
667 # instance, right before running the project definition. You can use this
668 # to add tasks and set properties that will be used in the project definition.
669 # * after_define -- This block is called once for the project with the project
670 # instance, right after running the project definition. You can use this to
671 # do any post-processing that depends on the project definition.
672 #
673 # This example illustrates how to write a simple extension:
674 # module LinesOfCode
675 # include Extension
676 #
677 # first_time do
678 # # Define task not specific to any projet.
679 # desc 'Count lines of code in current project'
680 # Project.local_task('loc')
681 # end
682 #
683 # before_define do |project|
684 # # Define the loc task for this particular project.
685 # Rake::Task.define_task 'loc' do |task|
686 # lines = task.prerequisites.map { |path| Dir['#{path}/**/*'] }.flatten.uniq.
687 # inject(0) { |total, file| total + File.readlines(file).count }
688 # puts "Project #{project.name} has #{lines} lines of code"
689 # end
690 # end
691 #
692 # after_define do |project|
693 # # Now that we know all the source directories, add them.
694 # task('loc'=>compile.sources + compile.test.sources)
695 # end
696 #
697 # # To use this method in your project:
698 # # loc path_1, path_2
699 # def loc(*paths)
700 # task('loc'=>paths)
701 # end
702 #
703 # end
704 #
705 # class Buildr::Project
706 # include LinesOfCode
707 # end
708 module Extension
709
710 def self.included(base) #:nodoc:
711 base.extend ClassMethods
712 end
713
714 # Methods added to the extension module when including Extension.
715 module ClassMethods
716
717 def included(base) #:nodoc:
718 # When included in Project, add callback and call first_time.
719 if Project == base && !base.callbacks.include?(callbacks)
720 base.callbacks << callbacks
721 callbacks.first_time if callbacks.respond_to?(:first_time)
722 end
723 end
724
725 def extended(base) #:nodoc:
726 # When extending project, add instance and call before_define.
727 if Project === base
728 callbacks = self.send(:callbacks).new
729 callbacks.before_define(base) if callbacks.respond_to?(:before_define)
730 base.send :add_callback, callbacks
731 end
732 end
733
734 # This block will be called once for any particular extension.
735 # You can use this to setup top-level and local tasks.
736 def first_time(&block)
737 meta = class << callbacks ; self ; end
738 meta.send :define_method, :first_time, &block
739 end
740
741 # This block is called once for the project with the project instance,
742 # right before running the project definition. You can use this to add
743 # tasks and set properties that will be used in the project definition.
744 def before_define(&block)
745 callbacks.send :define_method, :before_define, &block
746 end
747
748 # This block is called once for the project with the project instance,
749 # right after running the project definition. You can use this to do
750 # any post-processing that depends on the project definition.
751 def after_define(&block)
752 callbacks.send :define_method, :after_define, &block
753 end
754
755 private
756
757 def callbacks
758 const_get('Callbacks') rescue const_set('Callbacks', Class.new)
759 end
760
761 end
762
763 end
764
765
766 # :call-seq:
767 # define(name, properties?) { |project| ... } => project
768 #
769 # Defines a new project.
770 #
771 # The first argument is the project name. Each project must have a unique name.
772 # For a sub-project, the actual project name is created by prefixing the parent
773 # project's name.
774 #
775 # The second argument is optional and contains a hash or properties that are set
776 # on the project. You can only use properties that are supported by the project
777 # definition, e.g. :group and :version. You can also set these properties from the
778 # project definition.
779 #
780 # You pass a block that is executed in the context of the project definition.
781 # This block is used to define the project and tasks that are part of the project.
782 # Do not perform any work inside the project itself, as it will execute each time
783 # the Buildfile is loaded. Instead, use it to create and extend tasks that are
784 # related to the project.
785 #
786 # For example:
787 # define 'foo', :version=>'1.0' do
788 #
789 # define 'bar' do
790 # compile.with 'org.apache.axis2:axis2:jar:1.1'
791 # end
792 # end
793 #
794 # puts project('foo').version
795 # => '1.0'
796 # puts project('foo:bar').compile.classpath.map(&:to_spec)
797 # => 'org.apache.axis2:axis2:jar:1.1'
798 # % buildr build
799 # => Compiling 14 source files in foo:bar
800 def define(name, properties = nil, &block) #:yields:project
801 Project.define(name, properties, &block)
802 end
803
804 # :call-seq:
805 # project(name) => project
806 #
807 # Returns a project definition.
808 #
809 # When called from outside a project definition, must reference the project by its
810 # full name, e.g. 'foo:bar' to access the sub-project 'bar' in 'foo'. When called
811 # from inside a project, relative names are sufficient, e.g. <code>project('foo').project('bar')</code>
812 # will find the sub-project 'bar' in 'foo'.
813 #
814 # You cannot reference a project before the project is defined. When working with
815 # sub-projects, the project definition is stored by calling #define, and evaluated
816 # before a call to the parent project's #define method returns.
817 #
818 # However, if you call #project with the name of another sub-project, its definition
819 # is evaluated immediately. So the returned project definition is always complete,
820 # and you can access its definition (e.g. to find files relative to the base directory,
821 # or packages created by that project).
822 #
823 # For example:
824 # define 'myapp' do
825 # self.version = '1.1'
826 #
827 # define 'webapp' do
828 # # webapp is defined first, but beans is evaluated first
829 # compile.with project('beans')
830 # package :war
831 # end
832 #
833 # define 'beans' do
834 # package :jar
835 # end
836 # end
837 #
838 # puts project('myapp:beans').version
839 def project(*args)
840 Project.project *args
841 end
842
843 # :call-seq:
844 # projects(*names) => projects
845 #
846 # With no arguments, returns a list of all projects defined so far. When called on a project,
847 # returns all its sub-projects (direct descendants).
848 #
849 # With arguments, returns a list of named projects, fails on any name that does not exist.
850 # As with #project, you can use relative names when calling this method on a project.
851 #
852 # Like #project, this method evaluates the definition of each project before returning it.
853 # Be advised of circular dependencies.
854 #
855 # For example:
856 # files = projects.map { |prj| FileList[prj.path_to('src/**/*.java') }.flatten
857 # puts "There are #{files.size} source files in #{projects.size} projects"
858 #
859 # puts projects('myapp:beans', 'myapp:webapp').map(&:name)
860 # Same as:
861 # puts project('myapp').projects.map(&:name)
862 def projects(*args)
863 Project.projects *args
864 end
865
866 end
Generated using the rcov code coverage analysis tool for Ruby
version 0.8.2.1.