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.
Name Total lines Lines of code Total coverage Code coverage
lib/buildr/core/project.rb 866 301
98.7%  
96.3%  
  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.

Valid XHTML 1.0! Valid CSS!