C0 code coverage information

Generated on Wed Oct 07 08:33:56 -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/compile.rb 608 283
98.7%  
97.2%  
  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/common'
 18 
 19 
 20 module Buildr
 21 
 22   # The underlying compiler used by CompileTask.
 23   # To add a new compiler, extend Compiler::Base and add your compiler using:
 24   #   Buildr::Compiler.add MyCompiler
 25   module Compiler
 26 
 27     class << self
 28 
 29       # Returns true if the specified compiler exists.
 30       def has?(name)
 31         compilers.any? { |compiler| compiler.to_sym == name.to_sym }
 32       end
 33 
 34       # Select a compiler by its name.
 35       def select(name)
 36         compilers.detect { |compiler| compiler.to_sym == name.to_sym }
 37       end
 38 
 39       # Adds a compiler to the list of supported compiler.
 40       #   
 41       # For example:
 42       #   Buildr::Compiler << Buildr::Javac
 43       def add(compiler)
 44         @compilers ||= []
 45         @compilers |= [compiler]
 46       end
 47       alias :<< :add
 48 
 49       # Returns a list of available compilers.
 50       def compilers
 51         @compilers ||= []
 52       end
 53 
 54     end
 55 
 56     # Base class for all compilers, with common functionality.  Extend and over-ride as you see fit
 57     # (see Javac as an example).
 58     class Base #:nodoc:
 59 
 60       class << self
 61 
 62         # The compiler's identifier (e.g. :javac).  Inferred from the class name.
 63         def to_sym
 64           @symbol ||= name.split('::').last.downcase.to_sym
 65         end
 66 
 67         # The compiled language (e.g. :java).
 68         attr_reader :language
 69         # Source directories to use if none were specified (e.g. 'java').  Defaults to #language.
 70         attr_reader :sources
 71         # Extension for source files (e.g. 'java').  Defaults to language.
 72         attr_reader :source_ext
 73         # The target path (e.g. 'classes')
 74         attr_reader :target
 75         # Extension for target files (e.g. 'class').
 76         attr_reader :target_ext
 77         # The default packaging type (e.g. :jar).
 78         attr_reader :packaging
 79 
 80         # Returns true if this compiler applies to any source code found in the listed source
 81         # directories.  For example, Javac returns true if any of the source directories contains
 82         # a .java file.  The default implementation looks to see if there are any files in the
 83         # specified path with the extension #source_ext.
 84         def applies_to?(project, task)
 85           paths = task.sources + [sources].flatten.map { |src| Array(project.path_to(:source, task.usage, src.to_sym)) }
 86           paths.flatten!
 87           ext_glob = Array(source_ext).join(',')
 88           paths.any? { |path| !Dir["#{path}/**/*.{#{ext_glob}}"].empty? }
 89         end
 90 
 91         # Implementations can use this method to specify various compiler attributes.
 92         # For example:
 93         #   specify :language=>:java, :target=>'classes', :target_ext=>'class', :packaging=>:jar
 94         def specify(attrs)
 95           attrs[:sources] ||= attrs[:language].to_s
 96           attrs[:source_ext] ||= attrs[:language].to_s
 97           attrs.each { |name, value| instance_variable_set("@#{name}", value) }
 98         end
 99 
100         # Returns additional dependencies required by this language.  For example, since the
101         # test framework picks on these, you can use the JUnit framework with Scala.
102         # Defaults to obtaining a list of artifact specifications from the REQUIRES constant.
103         def dependencies
104           []
105         end
106 
107       end
108 
109       # Construct a new compiler with the specified options.  Note that options may
110       # change before the compiler is run.
111       def initialize(project, options)
112         @project = project
113         @options = options
114       end
115 
116       # Options for this compiler.
117       attr_reader :options
118 
119       # Determines if the compiler needs to run by checking if the target files exist,
120       # and if any source files or dependencies are newer than corresponding target files.
121       def needed?(sources, target, dependencies)
122         map = compile_map(sources, target)
123         return false if map.empty?
124         return true unless File.exist?(target.to_s)
125         source_files_not_yet_compiled = map.select { |source, target| !File.exist?(target) }.to_a
126         trace "Compile needed because source file #{source_files_not_yet_compiled[0][0]} has no corresponding #{source_files_not_yet_compiled[0][1]}" unless source_files_not_yet_compiled.empty?
127         return true if map.any? { |source, target| !File.exist?(target) || File.stat(source).mtime > File.stat(target).mtime }
128         oldest = map.map { |source, target| File.stat(target).mtime }.min
129         return dependencies.any? { |path| file(path).timestamp > oldest }
130       end
131 
132       # Compile all files lists in sources (files and directories) into target using the
133       # specified dependencies.
134       def compile(sources, target, dependencies)
135         raise 'Not implemented'
136       end
137 
138       # Returns additional dependencies required by this language.  For example, since the
139       # test framework picks on these, you can use the JUnit framework with Scala.
140       def dependencies
141         self.class.dependencies
142       end
143 
144     protected
145 
146       # Use this to complain about CompileTask options not supported by this compiler.
147       #
148       # For example:
149       #   def compile(files, task)
150       #     check_options task, OPTIONS
151       #     . . .
152       #   end
153       def check_options(options, *supported)
154         unsupported = options.to_hash.keys - supported.flatten
155         raise ArgumentError, "No such option: #{unsupported.join(' ')}" unless unsupported.empty?
156       end
157 
158       # Expands a list of source directories/files into a list of files that have the #source_ext extension.
159       def files_from_sources(sources)
160         ext_glob = Array(self.class.source_ext).join(',')
161         sources.flatten.map { |source| File.directory?(source) ? FileList["#{source}/**/*.{#{ext_glob}}"] : source }.
162           flatten.reject { |file| File.directory?(file) }.map { |file| File.expand_path(file) }.uniq
163       end
164 
165       # The compile map is a hash that associates source files with target files based
166       # on a list of source directories and target directory.  The compile task uses this
167       # to determine if there are source files to compile, and which source files to compile.
168       # The default method maps all files in the source directories with #source_ext into
169       # paths in the target directory with #target_ext (e.g. 'source/foo.java'=>'target/foo.class').
170       def compile_map(sources, target)
171         target_ext = self.class.target_ext
172         ext_glob = Array(self.class.source_ext).join(',')
173         sources.flatten.map{|f| File.expand_path(f)}.inject({}) do |map, source|
174           if File.directory?(source)
175             FileList["#{source}/**/*.{#{ext_glob}}"].reject { |file| File.directory?(file) }.
176               each { |file| map[file] = File.join(target, Util.relative_path(file, source).ext(target_ext)) }
177           else
178             # try to extract package name from .java or .scala files
179             if ['.java', '.scala', '.groovy'].include? File.extname(source)
180               package = findFirst(source, /^\s*package\s+(\S+)\s*;?\s*$/)
181               map[source] = package ? File.join(target, package[1].gsub('.', '/'), File.basename(source).ext(target_ext)) : target
182             elsif
183               map[source] = target
184             end
185           end
186           map
187         end
188       end
189 
190     private
191       
192       def findFirst(file, pattern)
193         match = nil
194         File.open(file, "r") do |infile|
195           while (line = infile.gets)
196             match = line.match(pattern)
197             break if match
198           end
199         end
200         match
201       end
202 
203     end
204   end
205 
206 
207   # Compile task.
208   #
209   # Attempts to determine which compiler to use based on the project layout, for example,
210   # uses the Javac compiler if it finds any .java files in src/main/java.  You can also
211   # select the compiler explicitly:
212   #   compile.using(:scalac)
213   #
214   # Accepts multiple source directories that are invoked as prerequisites before compilation.
215   # You can pass a task as a source directory:
216   #   compile.from(apt)
217   #
218   # Likewise, dependencies are invoked before compiling. All dependencies are evaluated as
219   # #artifacts, so you can pass artifact specifications and even projects:
220   #   compile.with('module1.jar', 'log4j:log4j:jar:1.0', project('foo'))
221   #
222   # Creates a file task for the target directory, so executing that task as a dependency will
223   # execute the compile task first.
224   #
225   # Compiler options are inherited form a parent task, e.g. the foo:bar:compile task inherits
226   # its options from the foo:compile task. Even if foo is an empty project that does not compile
227   # any classes itself, you can use it to set compile options for all its sub-projects.
228   #
229   # Normally, the project will take care of setting the source and target directory, and you
230   # only need to set options and dependencies. See Project#compile.
231   class CompileTask < Rake::Task
232 
233     def initialize(*args) #:nodoc:
234       super
235       parent_task = Project.parent_task(name)
236       inherit = lambda { |hash, key| parent_task.options[key] } if parent_task.respond_to?(:options)
237       @options = OpenObject.new &inherit
238       @sources = FileList[]
239       @dependencies = FileList[]
240 
241       enhance do |task|
242         unless sources.empty?
243           raise 'No compiler selected and can\'t determine which compiler to use' unless compiler
244           raise 'No target directory specified' unless target
245           mkpath target.to_s
246           info "Compiling #{task.name.gsub(/:[^:]*$/, '')} into #{target.to_s}"
247           @compiler.compile(sources.map(&:to_s), target.to_s, dependencies.map(&:to_s))
248           # By touching the target we let other tasks know we did something,
249           # and also prevent recompiling again for dependencies.
250           touch target.to_s
251         end
252       end
253     end
254 
255     # Source directories.
256     attr_accessor :sources
257 
258     # :call-seq:
259     #   from(*sources) => self
260     #
261     # Adds source directories and files to compile, and returns self.
262     #
263     # For example:
264     #   compile.from('src/java').into('classes').with('module1.jar')
265     def from(*sources)
266       @sources |= sources.flatten
267       guess_compiler if @compiler.nil? && sources.flatten.any? { |source| File.exist?(source) }
268       self
269     end
270 
271     # *Deprecated*: Use dependencies instead.
272     def classpath
273       Buildr.application.deprecated 'Use dependencies instead.'
274       dependencies
275     end
276 
277     # *Deprecated*: Use dependencies= instead.
278     def classpath=(artifacts)
279       Buildr.application.deprecated 'Use dependencies= instead.'
280       self.dependencies = artifacts
281     end
282 
283     # Compilation dependencies.
284     attr_accessor :dependencies
285 
286     # :call-seq:
287     #   with(*artifacts) => self
288     #
289     # Adds files and artifacts as dependencies, and returns self.
290     #
291     # Calls #artifacts on the arguments, so you can pass artifact specifications,
292     # tasks, projects, etc. Use this rather than setting the dependencies array directly.
293     #
294     # For example:
295     #   compile.with('module1.jar', 'log4j:log4j:jar:1.0', project('foo'))
296     def with(*specs)
297       @dependencies |= Buildr.artifacts(specs.flatten).uniq
298       self
299     end
300 
301     # The target directory for the compiled code.
302     attr_reader :target
303 
304     # :call-seq:
305     #   into(path) => self
306     #
307     # Sets the target directory and returns self. This will also set the compile task
308     # as a prerequisite to a file task on the target directory.
309     #
310     # For example:
311     #   compile(src_dir).into(target_dir).with(artifacts)
312     # Both compile.invoke and file(target_dir).invoke will compile the source files.
313     def into(path)
314       @target = file(path.to_s).enhance([self]) unless @target.to_s == path.to_s
315       self
316     end
317 
318     # Returns the compiler options.
319     attr_reader :options
320 
321     # :call-seq:
322     #   using(options) => self
323     #
324     # Sets the compiler options from a hash and returns self.  Can also be used to
325     # select the compiler.
326     #
327     # For example:
328     #   compile.using(:warnings=>true, :source=>'1.5')
329     #   compile.using(:scala)
330     def using(*args)
331       args.pop.each { |key, value| options.send "#{key}=", value } if Hash === args.last
332       self.compiler = args.pop until args.empty?
333       self
334     end
335 
336     # Returns the compiler if known.  The compiler is either automatically selected
337     # based on existing source directories (e.g. src/main/java), or by requesting
338     # a specific compiler (see #using).
339     def compiler
340       guess_compiler unless @compiler
341       @compiler && @compiler.class.to_sym
342     end
343 
344     # Returns the compiled language, if known.  See also #compiler.
345     def language
346       compiler && @compiler.class.language
347     end
348 
349     # Returns the default packaging type for this compiler, if known.
350     def packaging
351       compiler && @compiler.class.packaging
352     end
353 
354     def timestamp #:nodoc:
355       # If we compiled successfully, then the target directory reflects that.
356       # If we didn't, see needed?
357       target ? target.timestamp : Rake::EARLY
358     end
359 
360     # The project this task belongs to.
361     attr_reader :project
362 
363     # The usage, one of :main or :test.
364     attr_reader :usage
365 
366   protected
367 
368     # Selects which compiler to use.
369     def compiler=(name) #:nodoc:
370       cls = Compiler.select(name) or raise ArgumentError, "No #{name} compiler available. Did you install it?"
371       return self if cls === @compiler
372       raise "#{compiler} compiler already selected for this project" if @compiler
373       @compiler = cls.new(project, options)
374       from Array(cls.sources).map { |path| project.path_to(:source, usage, path) }.
375         select { |path| File.exist?(path) } if sources.empty?
376       into project.path_to(:target, usage, cls.target) unless target
377       with Array(@compiler.dependencies)
378       self
379     end
380 
381     # Associates this task with project and particular usage (:main, :test).
382     def associate_with(project, usage) #:nodoc:
383       @project, @usage = project, usage
384       guess_compiler
385     end
386     
387     # Try to guess if we have a compiler to match source files.
388     def guess_compiler #:nodoc:
389       candidate = Compiler.compilers.detect { |cls| cls.applies_to?(project, self) }
390       self.compiler = candidate if candidate
391     end
392 
393   private
394 
395     def needed? #:nodoc:
396       return false if sources.empty?
397       # Fail during invoke.
398       return true unless @compiler && target
399       return @compiler.needed?(sources.map(&:to_s), target.to_s, dependencies.map(&:to_s))
400     end
401 
402     def invoke_prerequisites(args, chain) #:nodoc:
403       @sources = Array(@sources).map(&:to_s).uniq
404       @dependencies = FileList[@dependencies.uniq]
405       @prerequisites |= @dependencies + @sources
406       super
407     end
408 
409   end
410 
411 
412   # The resources task is executed by the compile task to copy resource files over
413   # to the target directory. You can enhance this task in the normal way, but mostly
414   # you will use the task's filter.
415   #
416   # For example:
417   #   resources.filter.using 'Copyright'=>'Acme Inc, 2007'
418   class ResourcesTask < Rake::Task
419 
420     # Returns the filter used to copy resources over. See Buildr::Filter.
421     attr_reader :filter
422 
423     def initialize(*args) #:nodoc:
424       super
425       @filter = Buildr::Filter.new
426       @filter.using Buildr.settings.profile['filter'] if Hash === Buildr.settings.profile['filter']
427       enhance do
428         target.invoke if target
429       end
430     end
431 
432     # :call-seq:
433     #   include(*files) => self
434     #
435     # Includes the specified files in the filter and returns self.
436     def include(*files)
437       filter.include *files
438       self
439     end
440 
441     # :call-seq:
442     #   exclude(*files) => self
443     #
444     # Excludes the specified files in the filter and returns self.
445     def exclude(*files)
446       filter.exclude *files
447       self
448     end
449 
450     # :call-seq:
451     #   from(*sources) => self
452     #
453     # Adds additional directories from which to copy resources.
454     #
455     # For example:
456     #   resources.from _('src/etc')
457     def from(*sources)
458       filter.from *sources
459       self
460     end
461 
462     # Returns the list of source directories (each being a file task).
463     def sources
464       filter.sources
465     end
466 
467     # :call-seq:
468     #   target => task
469     #
470     # Returns the filter's target directory as a file task.
471     def target
472       filter.into @project.path_to(:target, @usage, :resources) unless filter.target || sources.empty?
473       filter.target
474     end
475 
476     def prerequisites #:nodoc:
477       super + filter.sources.flatten
478     end
479 
480   protected
481 
482     # Associates this task with project and particular usage (:main, :test).
483     def associate_with(project, usage) #:nodoc:
484       @project, @usage = project, usage
485     end
486 
487   end
488 
489 
490   # Methods added to Project for compiling, handling of resources and generating source documentation.
491   module Compile
492 
493     include Extension
494 
495     first_time do
496       desc 'Compile all projects'
497       Project.local_task('compile') { |name| "Compiling #{name}" }
498     end
499 
500     before_define do |project|
501       resources = ResourcesTask.define_task('resources')
502       resources.send :associate_with, project, :main
503       project.path_to(:source, :main, :resources).tap { |dir| resources.from dir if File.exist?(dir) }
504 
505       compile = CompileTask.define_task('compile'=>resources)
506       compile.send :associate_with, project, :main
507       project.recursive_task('compile')
508     end
509 
510     after_define do |project|
511       if project.compile.target
512         # This comes last because the target path is set inside the project definition.
513         project.build project.compile.target
514         project.clean do
515           rm_rf project.compile.target.to_s, :verbose=>false
516         end
517       end
518     end
519 
520       
521     # :call-seq:
522     #   compile(*sources) => CompileTask
523     #   compile(*sources) { |task| .. } => CompileTask
524     #
525     # The compile task does what its name suggests. This method returns the project's
526     # CompileTask. It also accepts a list of source directories and files to compile
527     # (equivalent to calling CompileTask#from on the task), and a block for any
528     # post-compilation work.
529     #
530     # The compile task attempts to guess which compiler to use.  For example, if it finds
531     # any Java files in the src/main/java directory, it will use the Java compiler and
532     # create class files in the target/classes directory.
533     #
534     # You can also configure it yourself by telling it which compiler to use, pointing
535     # it as source directories and chooing a different target directory.
536     #
537     # For example:
538     #   # Include Log4J and the api sub-project artifacts.
539     #   compile.with 'log4j:log4j:jar:1.2', project('api')
540     #   # Include Apt-generated source files.
541     #   compile.from apt
542     #   # For JavaC, force target compatibility.
543     #   compile.options.source = '1.6'
544     #   # Run the OpenJPA bytecode enhancer after compilation.
545     #   compile { open_jpa_enhance }
546     #   # Pick a given compiler.
547     #   compile.using(:scalac).from('src/scala')
548     #
549     # For more information, see CompileTask.
550     def compile(*sources, &block)
551       task('compile').from(sources).enhance &block
552     end
553 
554     # :call-seq:
555     #   resources(*prereqs) => ResourcesTask
556     #   resources(*prereqs) { |task| .. } => ResourcesTask
557     #
558     # The resources task is executed by the compile task to copy resources files
559     # from the resource directory into the target directory. By default the resources
560     # task copies files from the src/main/resources into the target/resources directory.
561     #
562     # This method returns the project's resources task. It also accepts a list of
563     # prerequisites and a block, used to enhance the resources task.
564     #
565     # Resources files are copied and filtered (see Buildr::Filter for more information).
566     # The default filter uses the profile properties for the current environment.
567     #
568     # For example:
569     #   resources.from _('src/etc')
570     #   resources.filter.using 'Copyright'=>'Acme Inc, 2007'
571     #
572     # Or in your profiles.yaml file:
573     #   common:
574     #     Copyright: Acme Inc, 2007
575     def resources(*prereqs, &block)
576       task('resources').enhance prereqs, &block
577     end
578 
579   end
580 
581 
582   class Options
583 
584     # Returns the debug option (environment variable DEBUG).
585     def debug
586       (ENV['DEBUG'] || ENV['debug']) !~ /(no|off|false)/
587     end
588 
589     # Sets the debug option (environment variable DEBUG).
590     #
591     # You can turn this option off directly, or by setting the environment variable
592     # DEBUG to +no+. For example:
593     #   buildr build DEBUG=no
594     #
595     # The release tasks runs a build with <tt>DEBUG=no</tt>.
596     def debug=(flag)
597       ENV['debug'] = nil
598       ENV['DEBUG'] = flag.to_s
599     end
600 
601   end
602 
603 end
604 
605 
606 class Buildr::Project
607   include Buildr::Compile
608 end

Generated using the rcov code coverage analysis tool for Ruby version 0.8.2.1.

Valid XHTML 1.0! Valid CSS!