| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| lib/buildr/core/build.rb | 516 | 290 | 41.67%
|
55.17%
|
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/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(:build => [:compile, :test]) 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 after_define(:build) |
65 |
66 # *Deprecated:* Use +path_to(:target)+ instead. |
67 def target |
68 Buildr.application.deprecated 'Use path_to(:target) instead' |
69 layout.expand(:target) |
70 end |
71 |
72 # *Deprecated:* Use Layout instead. |
73 def target=(dir) |
74 Buildr.application.deprecated 'Use Layout instead' |
75 layout[:target] = _(dir) |
76 end |
77 |
78 # *Deprecated:* Use +path_to(:reports)+ instead. |
79 def reports() |
80 Buildr.application.deprecated 'Use path_to(:reports) instead' |
81 layout.expand(:reports) |
82 end |
83 |
84 # *Deprecated:* Use Layout instead. |
85 def reports=(dir) |
86 Buildr.application.deprecated 'Use Layout instead' |
87 layout[:reports] = _(dir) |
88 end |
89 |
90 # :call-seq: |
91 # build(*prereqs) => task |
92 # build { |task| .. } => task |
93 # |
94 # Returns the project's build task. With arguments or block, also enhances that task. |
95 def build(*prereqs, &block) |
96 task('build').enhance prereqs, &block |
97 end |
98 |
99 # :call-seq: |
100 # clean(*prereqs) => task |
101 # clean { |task| .. } => task |
102 # |
103 # Returns the project's clean task. With arguments or block, also enhances that task. |
104 def clean(*prereqs, &block) |
105 task('clean').enhance prereqs, &block |
106 end |
107 |
108 end |
109 |
110 |
111 module Git #:nodoc: |
112 module_function |
113 |
114 # :call-seq: |
115 # git(*args) |
116 # |
117 # Executes a Git command and returns the output. Throws exception if the exit status |
118 # is not zero. For example: |
119 # git 'commit' |
120 # git 'remote', 'show', 'origin' |
121 def git(*args) |
122 cmd = "git #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}" |
123 output = `#{cmd}` |
124 fail "GIT command \"#{cmd}\" failed with status #{$?.exitstatus}\n#{output}" unless $?.exitstatus == 0 |
125 return output |
126 end |
127 |
128 # Returns list of uncommited/untracked files as reported by git status. |
129 def uncommitted_files |
130 `git status`.scan(/^#(\t|\s{7})(\S.*)$/).map { |match| match.last.split.last } |
131 end |
132 |
133 # Commit the given file with a message. |
134 # The file has to be known to Git meaning that it has either to have been already committed in the past |
135 # or freshly added to the index. Otherwise it will fail. |
136 def commit(file, message) |
137 git 'commit', '-m', message, file |
138 end |
139 |
140 # Update the remote refs using local refs |
141 # |
142 # By default, the "remote" destination of the push is the the remote repo linked to the current branch. |
143 # The default remote branch is the current local branch. |
144 def push(remote_repo = remote, remote_branch = current_branch) |
145 git 'push', remote, current_branch |
146 end |
147 |
148 # Return the name of the remote repository whose branch the current local branch tracks, |
149 # or nil if none. |
150 def remote(branch = current_branch) |
151 remote = git('config', '--get', "branch.#{branch}.remote").to_s.strip |
152 remote if !remote.empty? && git('remote').include?(remote) |
153 end |
154 |
155 # Return the name of the current branch |
156 def current_branch |
157 git('branch')[/^\* (.*)$/, 1] |
158 end |
159 end |
160 |
161 |
162 module Svn #:nodoc: |
163 module_function |
164 |
165 # :call-seq: |
166 # svn(*args) |
167 # |
168 # Executes a SVN command and returns the output. Throws exception if the exit status |
169 # is not zero. For example: |
170 # svn 'commit' |
171 def svn(*args) |
172 output = `svn #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}` |
173 fail "SVN command failed with status #{$?.exitstatus}" unless $?.exitstatus == 0 |
174 return output |
175 end |
176 |
177 def tag(tag_name) |
178 url = tag_url repo_url, tag_name |
179 remove url, 'Removing old copy' rescue nil |
180 copy Dir.pwd, url, "Release #{tag_name}" |
181 end |
182 |
183 # Status check reveals modified files, but also SVN externals which we can safely ignore. |
184 def uncommitted_files |
185 svn('status', '--ignore-externals').split("\n").reject { |line| line =~ /^X\s/ } |
186 end |
187 |
188 def commit(file, message) |
189 svn 'commit', '-m', message, file |
190 end |
191 |
192 # :call-seq: |
193 # tag_url(svn_url, version) => tag_url |
194 # |
195 # Returns the SVN url for the tag. |
196 # Can tag from the trunk or from branches. |
197 # Can handle the two standard repository layouts. |
198 # - http://my.repo/foo/trunk => http://my.repo/foo/tags/1.0.0 |
199 # - http://my.repo/trunk/foo => http://my.repo/tags/foo/1.0.0 |
200 def tag_url(svn_url, tag) |
201 trunk_or_branches = Regexp.union(%r{^(.*)/trunk(.*)$}, %r{^(.*)/branches(.*)/([^/]*)$}) |
202 match = trunk_or_branches.match(svn_url) |
203 prefix = match[1] || match[3] |
204 suffix = match[2] || match[4] |
205 prefix + '/tags' + suffix + '/' + tag |
206 end |
207 |
208 # Return the current SVN URL |
209 def repo_url |
210 svn('info', '--xml')[/<url>(.*?)<\/url>/, 1].strip |
211 end |
212 |
213 def copy(dir, url, message) |
214 svn 'copy', '--parents', dir, url, '-m', message |
215 end |
216 |
217 def remove(url, message) |
218 svn 'remove', url, '-m', message |
219 end |
220 |
221 end |
222 |
223 |
224 class Release #:nodoc: |
225 |
226 THIS_VERSION_PATTERN = /(THIS_VERSION|VERSION_NUMBER)\s*=\s*(["'])(.*)\2/ |
227 |
228 class << self |
229 |
230 # Use this to specify a different tag name for tagging the release in source control. |
231 # You can set the tag name or a proc that will be called with the version number, |
232 # for example: |
233 # Release.tag_name = lambda { |ver| "foo-#{ver}" } |
234 attr_accessor :tag_name |
235 |
236 # Use this to specify a different commit message to commit the buildfile with the next version in source control. |
237 # You can set the commit message or a proc that will be called with the next version number, |
238 # for example: |
239 # Release.commit_message = lambda { |ver| "Changed version number to #{ver}" } |
240 attr_accessor :commit_message |
241 |
242 # Use this to specify the next version number to replace VERSION_NUMBER with in the buildfile. |
243 # You can set the next version or a proc that will be called with the current version number. |
244 # For example, with the following buildfile: |
245 # THIS_VERSION = "1.0.0-rc1" |
246 # Release.next_version = lambda { |version| |
247 # version[-1] = version[-1].to_i + 1 |
248 # version |
249 # } |
250 # |
251 # Release.next_version will return "1.0.0-rc2", so at the end of the release, the buildfile will contain VERSION_NUMBER = "1.0.0-rc2" |
252 # |
253 attr_accessor :next_version |
254 |
255 # :call-seq: |
256 # add(MyReleaseClass) |
257 # |
258 # Add a Release implementation to the list of available Release classes. |
259 def add(release) |
260 @list ||= [] |
261 @list |= [release] |
262 end |
263 alias :<< :add |
264 |
265 # The list of supported Release implementations |
266 def list |
267 @list ||= [] |
268 end |
269 |
270 # Finds and returns the Release instance for this project. |
271 def find |
272 unless @release |
273 klass = list.detect { |impl| impl.applies_to? } |
274 @release = klass.new if klass |
275 end |
276 @release |
277 end |
278 |
279 end |
280 |
281 # :call-seq: |
282 # make() |
283 # |
284 # Make a release. |
285 def make |
286 @this_version = extract_version |
287 check |
288 with_release_candidate_version do |release_candidate_buildfile| |
289 args = '-S', 'buildr', "_#{Buildr::VERSION}_", '--buildfile', release_candidate_buildfile |
290 args << '--environment' << Buildr.environment unless Buildr.environment.to_s.empty? |
291 args << 'clean' << 'upload' << 'DEBUG=no' |
292 ruby *args |
293 end |
294 tag_release resolve_tag |
295 update_version_to_next if this_version != resolve_next_version(this_version) |
296 end |
297 |
298 def check |
299 if this_version == resolve_next_version(this_version) && this_version.match(/-SNAPSHOT$/) |
300 fail "The next version can't be equal to the current version #{this_version}.\nUpdate THIS_VERSION/VERSION_NUMBER, specify Release.next_version or use NEXT_VERSION env var" |
301 end |
302 end |
303 |
304 # :call-seq: |
305 # extract_version() => this_version |
306 # |
307 # Extract the current version number from the buildfile. |
308 # Raise an error if not found. |
309 def extract_version |
310 buildfile = File.read(Buildr.application.buildfile.to_s) |
311 buildfile.scan(THIS_VERSION_PATTERN)[0][2] |
312 rescue |
313 fail 'Looking for THIS_VERSION = "..." in your Buildfile, none found' |
314 end |
315 |
316 # Use this to specify a different tag name for tagging the release in source control. |
317 # You can set the tag name or a proc that will be called with the version number, |
318 # for example: |
319 # Release.find.tag_name = lambda { |ver| "foo-#{ver}" } |
320 # Deprecated: you should use Release.tag_name instead |
321 def tag_name=(tag_proc) |
322 Buildr.application.deprecated "Release.find.tag_name is deprecated. You should use Release.tag_name instead" |
323 Release.tag_name=(tag_proc) |
324 end |
325 |
326 protected |
327 |
328 # the initial value of THIS_VERSION |
329 attr_accessor :this_version |
330 |
331 # :call-seq: |
332 # with_release_candidate_version() { |filename| ... } |
333 # |
334 # Yields to block with release candidate buildfile, before committing to use it. |
335 # |
336 # We need a Buildfile with upgraded version numbers to run the build, but we don't want the |
337 # Buildfile modified unless the build succeeds. So this method updates the version number in |
338 # a separate (Buildfile.next) file, yields to the block with that filename, and if successful |
339 # copies the new file over the existing one. |
340 # |
341 # The release version is the current version without '-SNAPSHOT'. So: |
342 # THIS_VERSION = 1.1.0-SNAPSHOT |
343 # becomes: |
344 # THIS_VERSION = 1.1.0 |
345 # for the release buildfile. |
346 def with_release_candidate_version |
347 release_candidate_buildfile = Buildr.application.buildfile.to_s + '.next' |
348 |
349 release_candidate_buildfile_contents = change_version { |version| |
350 version.gsub(/-SNAPSHOT$/, "") |
351 } |
352 File.open(release_candidate_buildfile, 'w') { |file| file.write release_candidate_buildfile_contents } |
353 begin |
354 yield release_candidate_buildfile |
355 mv release_candidate_buildfile, Buildr.application.buildfile.to_s |
356 ensure |
357 rm release_candidate_buildfile rescue nil |
358 end |
359 end |
360 |
361 # :call-seq: |
362 # change_version() { |this_version| ... } => buildfile |
363 # |
364 # Change version number in the current Buildfile, but without writing a new file (yet). |
365 # Returns the contents of the Buildfile with the modified version number. |
366 # |
367 # This method yields to the block with the current (this) version number and expects |
368 # the block to return the updated version. |
369 def change_version |
370 current_version = extract_version |
371 new_version = yield(current_version) |
372 buildfile = File.read(Buildr.application.buildfile.to_s) |
373 buildfile.gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{new_version}"}) } |
374 end |
375 |
376 # Return the name of the tag to tag the release with. |
377 def resolve_tag |
378 version = extract_version |
379 tag = Release.tag_name || version |
380 tag = tag.call(version) if Proc === tag |
381 tag |
382 end |
383 |
384 # Return the new value of THIS_VERSION based on the version passed. |
385 # |
386 # This method receives the existing value of THIS_VERSION |
387 def resolve_next_version(current_version) |
388 next_version = Release.next_version |
389 next_version ||= lambda { |v| |
390 snapshot = v.match(/-SNAPSHOT$/) |
391 version = v.gsub(/-SNAPSHOT$/, "").split(/\./) |
392 if snapshot |
393 version[-1] = sprintf("%0#{version[-1].size}d", version[-1].to_i + 1) + '-SNAPSHOT' |
394 end |
395 version.join('.') |
396 } |
397 next_version = ENV['NEXT_VERSION'] if ENV['NEXT_VERSION'] |
398 next_version = ENV['next_version'] if ENV['next_version'] |
399 next_version = next_version.call(current_version) if Proc === next_version |
400 next_version |
401 end |
402 |
403 # Move the version to next and save the updated buildfile |
404 def update_buildfile |
405 buildfile = change_version { |version| # THIS_VERSION minus SNAPSHOT |
406 resolve_next_version(this_version) # THIS_VERSION |
407 } |
408 File.open(Buildr.application.buildfile.to_s, 'w') { |file| file.write buildfile } |
409 end |
410 |
411 # Return the message to use to commit the buildfile with the next version |
412 def message |
413 version = extract_version |
414 msg = Release.commit_message || "Changed version number to #{version}" |
415 msg = msg.call(version) if Proc === msg |
416 msg |
417 end |
418 |
419 def update_version_to_next |
420 update_buildfile |
421 end |
422 end |
423 |
424 |
425 class GitRelease < Release |
426 class << self |
427 def applies_to? |
428 if File.exist? '.git/config' |
429 true |
430 else |
431 curr_pwd = Dir.pwd |
432 Dir.chdir('..') do |
433 return false if curr_pwd == Dir.pwd # Means going up one level is not possible. |
434 applies_to? |
435 end |
436 end |
437 end |
438 end |
439 |
440 # Fails if one of theses 2 conditions are not met: |
441 # 1. the repository is clean: no content staged or unstaged |
442 # 2. some remote repositories are defined but the current branch does not track any |
443 def check |
444 super |
445 uncommitted = Git.uncommitted_files |
446 fail "Uncommitted files violate the First Principle Of Release!\n#{uncommitted.join("\n")}" unless uncommitted.empty? |
447 fail "You are releasing from a local branch that does not track a remote!" unless Git.remote |
448 end |
449 |
450 # Add a tag reference in .git/refs/tags and push it to the remote if any. |
451 # If a tag with the same name already exists it will get deleted (in both local and remote repositories). |
452 def tag_release(tag) |
453 unless this_version == extract_version |
454 info "Committing buildfile with version number #{extract_version}" |
455 Git.commit File.basename(Buildr.application.buildfile.to_s), message |
456 Git.push if Git.remote |
457 end |
458 info "Tagging release #{tag}" |
459 Git.git 'tag', '-d', tag rescue nil |
460 Git.git 'push', Git.remote, ":refs/tags/#{tag}" rescue nil if Git.remote |
461 Git.git 'tag', '-a', tag, '-m', "[buildr] Cutting release #{tag}" |
462 Git.git 'push', Git.remote, 'tag', tag if Git.remote |
463 end |
464 |
465 def update_version_to_next |
466 super |
467 info "Current version is now #{extract_version}" |
468 Git.commit File.basename(Buildr.application.buildfile.to_s), message |
469 Git.push if Git.remote |
470 end |
471 end |
472 |
473 |
474 class SvnRelease < Release |
475 class << self |
476 def applies_to? |
477 File.exist?('.svn') |
478 end |
479 end |
480 |
481 def check |
482 super |
483 fail "Uncommitted files violate the First Principle Of Release!\n"+Svn.uncommitted_files.join("\n") unless Svn.uncommitted_files.empty? |
484 fail "SVN URL must contain 'trunk' or 'branches/...'" unless Svn.repo_url =~ /(trunk)|(branches.*)$/ |
485 end |
486 |
487 def tag_release(tag) |
488 # Unlike Git, committing the buildfile with the released version is not necessary. |
489 # svn tag does commit & tag. |
490 info "Tagging release #{tag}" |
491 Svn.tag tag |
492 end |
493 |
494 def update_version_to_next |
495 super |
496 info "Current version is now #{extract_version}" |
497 Svn.commit Buildr.application.buildfile.to_s, message |
498 end |
499 end |
500 |
501 Release.add SvnRelease |
502 Release.add GitRelease |
503 |
504 desc 'Make a release' |
505 task 'release' do |task| |
506 release = Release.find |
507 fail 'Unable to detect the Version Control System.' unless release |
508 release.make |
509 end |
510 |
511 end |
512 |
513 |
514 class Buildr::Project |
515 include Buildr::Build |
516 end |
Generated on 2011-07-06 23:35:37 -0700 with rcov 0.9.8