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/build.rb 452 254
95.8%  
92.9%  
  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/project'
 18 require 'buildr/core/common'
 19 require 'buildr/core/checks'
 20 require 'buildr/core/environment'
 21 
 22 
 23 module Buildr
 24 
 25   class Options
 26 
 27     # Runs the build in parallel when true (defaults to false). You can force a parallel build by
 28     # setting this option directly, or by running the parallel task ahead of the build task.
 29     #
 30     # This option only affects recursive tasks. For example:
 31     #   buildr parallel package
 32     # will run all package tasks (from the sub-projects) in parallel, but each sub-project's package
 33     # task runs its child tasks (prepare, compile, resources, etc) in sequence.
 34     attr_accessor :parallel
 35 
 36   end
 37 
 38   task('parallel') { Buildr.options.parallel = true }
 39 
 40 
 41   module Build
 42 
 43     include Extension
 44 
 45     first_time do
 46       desc 'Build the project'
 47       Project.local_task('build') { |name| "Building #{name}" }
 48       desc 'Clean files generated during a build'
 49       Project.local_task('clean') { |name| "Cleaning #{name}" }
 50 
 51       desc 'The default task is build'
 52       task 'default'=>'build'
 53     end
 54 
 55     before_define do |project|
 56       project.recursive_task 'build'
 57       project.recursive_task 'clean'
 58       project.clean do
 59         rm_rf project.path_to(:target)
 60         rm_rf project.path_to(:reports)
 61       end
 62     end
 63 
 64 
 65     # *Deprecated:* Use +path_to(:target)+ instead.
 66     def target
 67       Buildr.application.deprecated 'Use path_to(:target) instead'
 68       layout.expand(:target)
 69     end
 70 
 71     # *Deprecated:* Use Layout instead.
 72     def target=(dir)
 73       Buildr.application.deprecated 'Use Layout instead'
 74       layout[:target] = _(dir)
 75     end
 76 
 77     # *Deprecated:* Use +path_to(:reports)+ instead.
 78     def reports()
 79       Buildr.application.deprecated 'Use path_to(:reports) instead'
 80       layout.expand(:reports)
 81     end
 82 
 83     # *Deprecated:* Use Layout instead.
 84     def reports=(dir)
 85       Buildr.application.deprecated 'Use Layout instead'
 86       layout[:reports] = _(dir)
 87     end
 88 
 89     # :call-seq:
 90     #    build(*prereqs) => task
 91     #    build { |task| .. } => task
 92     #
 93     # Returns the project's build task. With arguments or block, also enhances that task.
 94     def build(*prereqs, &block)
 95       task('build').enhance prereqs, &block
 96     end
 97 
 98     # :call-seq:
 99     #    clean(*prereqs) => task
100     #    clean { |task| .. } => task
101     #
102     # Returns the project's clean task. With arguments or block, also enhances that task.
103     def clean(*prereqs, &block)
104       task('clean').enhance prereqs, &block
105     end
106 
107   end
108 
109 
110   module Git  #:nodoc:
111     module_function
112 
113     # :call-seq:
114     #   git(*args)
115     #
116     # Executes a Git command and returns the output. Throws exception if the exit status
117     # is not zero. For example:
118     #   git 'commit'
119     #   git 'remote', 'show', 'origin'
120     def git(*args)
121       cmd = "git #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}"
122       output = `#{cmd}`
123       fail "GIT command \"#{cmd}\" failed with status #{$?.exitstatus}\n#{output}" unless $?.exitstatus == 0
124       return output
125     end
126 
127     # Returns list of uncommited/untracked files as reported by git status.
128     def uncommitted_files
129       `git status`.scan(/^#(\t|\s{7})(\S.*)$/).map { |match| match.last.split.last }
130     end
131 
132     # Commit the given file with a message.
133     # The file has to be known to Git meaning that it has either to have been already committed in the past
134     # or freshly added to the index. Otherwise it will fail.
135     def commit(file, message)
136       git 'commit', '-m', message, file
137     end
138 
139     # Update the remote refs using local refs
140     #
141     # By default, the "remote" destination of the push is the the remote repo linked to the current branch.
142     # The default remote branch is the current local branch.
143     def push(remote_repo = remote, remote_branch = current_branch)
144       git 'push', remote, current_branch
145     end
146 
147     # Return the name of the remote repository whose branch the current local branch tracks,
148     # or nil if none.
149     def remote(branch = current_branch)
150       remote = git('config', '--get', "branch.#{branch}.remote").to_s.strip
151       remote if !remote.empty? && git('remote').include?(remote)
152     end
153 
154     # Return the name of the current branch
155     def current_branch
156       git('branch')[/^\* (.*)$/, 1]
157     end
158   end
159 
160 
161   module Svn #:nodoc:
162     module_function
163 
164     # :call-seq:
165     #   svn(*args)
166     #
167     # Executes a SVN command and returns the output. Throws exception if the exit status
168     # is not zero. For example:
169     #   svn 'commit'
170     def svn(*args)
171       output = `svn #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}`
172       fail "SVN command failed with status #{$?.exitstatus}" unless $?.exitstatus == 0
173       return output
174     end
175 
176     def tag(tag_name)
177       url = tag_url repo_url, tag_name
178       remove url, 'Removing old copy' rescue nil
179       copy Dir.pwd, url, "Release #{tag_name}"
180     end
181 
182     # Status check reveals modified files, but also SVN externals which we can safely ignore.
183     def uncommitted_files
184       svn('status', '--ignore-externals').split("\n").reject { |line| line =~ /^X\s/ }
185     end
186 
187     def commit(file, message)
188       svn 'commit', '-m', message, file
189     end
190     
191     # :call-seq:
192     #   tag_url(svn_url, version) => tag_url
193     #
194     # Returns the SVN url for the tag.
195     # Can tag from the trunk or from branches.
196     # Can handle the two standard repository layouts.
197     #   - http://my.repo/foo/trunk => http://my.repo/foo/tags/1.0.0
198     #   - http://my.repo/trunk/foo => http://my.repo/tags/foo/1.0.0
199     def tag_url(svn_url, tag)
200       trunk_or_branches = Regexp.union(%r{^(.*)/trunk(.*)$}, %r{^(.*)/branches(.*)/([^/]*)$})
201       match = trunk_or_branches.match(svn_url)
202       prefix = match[1] || match[3]
203       suffix = match[2] || match[4]
204       prefix + '/tags' + suffix + '/' + tag
205     end
206 
207     # Return the current SVN URL
208     def repo_url
209       svn('info', '--xml')[/<url>(.*?)<\/url>/, 1].strip
210     end
211     
212     def copy(dir, url, message)
213       svn 'copy', dir, url, '-m', message
214     end
215     
216     def remove(url, message)
217       svn 'remove', url, '-m', message
218     end
219 
220   end
221   
222 
223   class Release #:nodoc:
224 
225     THIS_VERSION_PATTERN  = /(THIS_VERSION|VERSION_NUMBER)\s*=\s*(["'])(.*)\2/
226 
227     class << self
228       
229       # :call-seq:
230       #     add(MyReleaseClass)
231       #
232       # Add a Release implementation to the list of available Release classes.
233       def add(release)
234         @list ||= []
235         @list |= [release]
236       end
237       alias :<< :add
238 
239       # The list of supported Release implementations
240       def list
241         @list ||= []
242       end
243 
244       # Finds and returns the Release instance for this project.
245       def find
246         unless @release
247           klass = list.detect { |impl| impl.applies_to? }
248           @release = klass.new if klass
249         end
250         @release
251       end
252 
253     end
254  
255     # Use this to specify a different tag name for tagging the release in source control.
256     # You can set the tag name or a proc that will be called with the version number,
257     # for example:
258     #   Release.tag_name = lambda { |ver| "foo-#{ver}" }
259     attr_accessor :tag_name
260 
261     # Use this to specify a different commit message to commit the buildfile with the next version in source control.
262     # You can set the commit message or a proc that will be called with the next version number,
263     # for example:
264     #   Release.commit_message = lambda { |ver| "Changed version number to #{ver}" }
265     attr_accessor :commit_message
266 
267     # :call-seq:
268     #   make()
269     #
270     # Make a release.
271     def make
272       check
273       with_release_candidate_version do |release_candidate_buildfile|
274         args = '-S', 'buildr', "_#{Buildr::VERSION}_", '--buildfile', release_candidate_buildfile
275         args << '--environment' << Buildr.environment unless Buildr.environment.to_s.empty?
276         args << 'clean' << 'upload' << 'DEBUG=no'
277         ruby *args 
278       end
279       tag_release resolve_tag
280       update_version_to_next
281     end
282     
283     # :call-seq:
284     #   extract_version() => this_versin
285     #
286     # Extract the current version number from the buildfile.
287     # Raise an error if not found.
288     def extract_version
289       buildfile = File.read(Buildr.application.buildfile.to_s)
290       buildfile.scan(THIS_VERSION_PATTERN)[0][2]
291     rescue
292       fail 'Looking for THIS_VERSION = "..." in your Buildfile, none found'
293     end
294     
295   protected
296     
297     # :call-seq:
298     #   with_release_candidate_version() { |filename| ... }
299     #
300     # Yields to block with release candidate buildfile, before committing to use it.
301     #
302     # We need a Buildfile with upgraded version numbers to run the build, but we don't want the
303     # Buildfile modified unless the build succeeds. So this method updates the version number in
304     # a separate (Buildfile.next) file, yields to the block with that filename, and if successful
305     # copies the new file over the existing one.
306     #
307     # The release version is the current version without '-SNAPSHOT'.  So:
308     #   THIS_VERSION = 1.1.0-SNAPSHOT
309     # becomes:
310     #   THIS_VERSION = 1.1.0
311     # for the release buildfile.
312     def with_release_candidate_version
313       release_candidate_buildfile = Buildr.application.buildfile.to_s + '.next'
314       release_candidate_buildfile_contents = change_version { |version| version[-1] = version[-1].split('-')[0] }
315       File.open(release_candidate_buildfile, 'w') { |file| file.write release_candidate_buildfile_contents }
316       begin
317         yield release_candidate_buildfile
318         mv release_candidate_buildfile, Buildr.application.buildfile.to_s
319       ensure
320         rm release_candidate_buildfile rescue nil
321       end
322     end
323 
324     # :call-seq:
325     #   change_version() { |this_version| ... } => buildfile
326     #
327     # Change version number in the current Buildfile, but without writing a new file (yet).
328     # Returns the contents of the Buildfile with the modified version number.
329     #
330     # This method yields to the block with the current (this) version number as an array and expects
331     # the block to update it.
332     def change_version
333       this_version = extract_version
334       new_version = this_version.split('.')
335       yield(new_version)
336       new_version = new_version.join('.')
337       buildfile = File.read(Buildr.application.buildfile.to_s)
338       buildfile.gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{new_version}"}) }
339     end
340 
341     # Return the name of the tag to tag the release with.
342     def resolve_tag
343       version = extract_version
344       tag = tag_name || version
345       tag = tag.call(version) if Proc === tag
346       tag
347     end
348 
349     # Move the version to next and save the updated buildfile
350     def update_buildfile
351       buildfile = change_version { |version| version[-1] = sprintf("%0#{version[-1].size}d", version[-1].to_i + 1) + '-SNAPSHOT' }
352       File.open(Buildr.application.buildfile.to_s, 'w') { |file| file.write buildfile }
353     end
354 
355     # Return the message to use to cimmit the buildfile with the next version
356     def message
357       version = extract_version
358       msg = commit_message || "Changed version number to #{version}"
359       msg = msg.call(version) if Proc === msg
360       msg
361     end
362 
363     def update_version_to_next
364       update_buildfile
365     end
366   end
367 
368 
369   class GitRelease < Release
370     class << self
371       def applies_to?
372         if File.exist? '.git/config'
373           true
374         else
375           File.expand_path(Dir.pwd) != '/' && Dir.chdir('..') do
376             applies_to?
377           end
378         end
379       end
380     end
381 
382     # Fails if one of theses 2 conditions are not met:
383     #    1. the repository is clean: no content staged or unstaged
384     #    2. some remote repositories are defined but the current branch does not track any
385     def check
386       uncommitted = Git.uncommitted_files
387       fail "Uncommitted files violate the First Principle Of Release!\n#{uncommitted.join("\n")}" unless uncommitted.empty?
388       fail "You are releasing from a local branch that does not track a remote!" unless Git.remote
389     end
390 
391     # Add a tag reference in .git/refs/tags and push it to the remote if any.
392     # If a tag with the same name already exists it will get deleted (in both local and remote repositories).
393     def tag_release(tag)
394       info "Committing buildfile with version number #{extract_version}"
395       Git.commit File.basename(Buildr.application.buildfile.to_s), message
396       Git.push if Git.remote
397       info "Tagging release #{tag}"
398       Git.git 'tag', '-d', tag rescue nil
399       Git.git 'push', Git.remote, ":refs/tags/#{tag}" rescue nil if Git.remote
400       Git.git 'tag', '-a', tag, '-m', "[buildr] Cutting release #{tag}"
401       Git.git 'push', Git.remote, 'tag', tag if Git.remote
402     end
403 
404     def update_version_to_next
405       super
406       info "Current version is now #{extract_version}"
407       Git.commit File.basename(Buildr.application.buildfile.to_s), message
408       Git.push if Git.remote
409     end
410   end
411   
412 
413   class SvnRelease < Release
414     class << self
415       def applies_to?
416         File.exist?('.svn')
417       end
418     end
419     
420     def check
421       fail "Uncommitted files violate the First Principle Of Release!\n"+Svn.uncommitted_files.join("\n") unless Svn.uncommitted_files.empty?
422       fail "SVN URL must contain 'trunk' or 'branches/...'" unless Svn.repo_url =~ /(trunk)|(branches.*)$/
423     end
424 
425     def tag_release(tag)
426       info "Tagging release #{tag}"
427       Svn.tag tag
428     end
429 
430     def update_version_to_next
431       super
432       info "Current version is now #{extract_version}"
433       Svn.commit Buildr.application.buildfile.to_s, message
434     end
435   end
436   
437   Release.add SvnRelease
438   Release.add GitRelease
439 
440   desc 'Make a release'
441   task 'release' do |task|
442     release = Release.find
443     fail 'Unable to detect the Version Control System.' unless release
444     release.make
445   end
446 
447 end
448 
449 
450 class Buildr::Project
451   include Buildr::Build
452 end

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

Valid XHTML 1.0! Valid CSS!