buildr 1.3.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. data/CHANGELOG +780 -0
  2. data/DISCLAIMER +7 -0
  3. data/KEYS +151 -0
  4. data/LICENSE +176 -0
  5. data/NOTICE +31 -0
  6. data/README +173 -0
  7. data/Rakefile +63 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/cobertura.rb +232 -0
  10. data/addon/buildr/hibernate.rb +142 -0
  11. data/addon/buildr/javacc.rb +85 -0
  12. data/addon/buildr/jdepend.rb +60 -0
  13. data/addon/buildr/jetty.rb +248 -0
  14. data/addon/buildr/nailgun.rb +892 -0
  15. data/addon/buildr/openjpa.rb +90 -0
  16. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  17. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  18. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  19. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  20. data/addon/buildr/xmlbeans.rb +93 -0
  21. data/bin/buildr +21 -0
  22. data/buildr.gemspec +50 -0
  23. data/doc/css/default.css +225 -0
  24. data/doc/css/print.css +95 -0
  25. data/doc/css/syntax.css +43 -0
  26. data/doc/images/apache-incubator-logo.png +0 -0
  27. data/doc/images/buildr-hires.png +0 -0
  28. data/doc/images/buildr.png +0 -0
  29. data/doc/images/note.png +0 -0
  30. data/doc/images/tip.png +0 -0
  31. data/doc/images/zbuildr.tif +0 -0
  32. data/doc/pages/artifacts.textile +317 -0
  33. data/doc/pages/building.textile +501 -0
  34. data/doc/pages/contributing.textile +178 -0
  35. data/doc/pages/download.textile +25 -0
  36. data/doc/pages/extending.textile +229 -0
  37. data/doc/pages/getting_started.textile +337 -0
  38. data/doc/pages/index.textile +63 -0
  39. data/doc/pages/mailing_lists.textile +17 -0
  40. data/doc/pages/more_stuff.textile +367 -0
  41. data/doc/pages/packaging.textile +592 -0
  42. data/doc/pages/projects.textile +449 -0
  43. data/doc/pages/recipes.textile +127 -0
  44. data/doc/pages/settings_profiles.textile +339 -0
  45. data/doc/pages/testing.textile +475 -0
  46. data/doc/pages/troubleshooting.textile +121 -0
  47. data/doc/pages/whats_new.textile +389 -0
  48. data/doc/print.haml +52 -0
  49. data/doc/print.toc.yaml +28 -0
  50. data/doc/scripts/buildr-git.rb +411 -0
  51. data/doc/scripts/install-jruby.sh +44 -0
  52. data/doc/scripts/install-linux.sh +64 -0
  53. data/doc/scripts/install-osx.sh +52 -0
  54. data/doc/site.haml +55 -0
  55. data/doc/site.toc.yaml +44 -0
  56. data/lib/buildr.rb +47 -0
  57. data/lib/buildr/core.rb +27 -0
  58. data/lib/buildr/core/application.rb +373 -0
  59. data/lib/buildr/core/application_cli.rb +134 -0
  60. data/lib/buildr/core/build.rb +262 -0
  61. data/lib/buildr/core/checks.rb +382 -0
  62. data/lib/buildr/core/common.rb +155 -0
  63. data/lib/buildr/core/compile.rb +594 -0
  64. data/lib/buildr/core/environment.rb +120 -0
  65. data/lib/buildr/core/filter.rb +258 -0
  66. data/lib/buildr/core/generate.rb +195 -0
  67. data/lib/buildr/core/help.rb +118 -0
  68. data/lib/buildr/core/progressbar.rb +156 -0
  69. data/lib/buildr/core/project.rb +890 -0
  70. data/lib/buildr/core/test.rb +690 -0
  71. data/lib/buildr/core/transports.rb +486 -0
  72. data/lib/buildr/core/util.rb +235 -0
  73. data/lib/buildr/ide.rb +19 -0
  74. data/lib/buildr/ide/eclipse.rb +181 -0
  75. data/lib/buildr/ide/idea.ipr.template +300 -0
  76. data/lib/buildr/ide/idea.rb +194 -0
  77. data/lib/buildr/ide/idea7x.ipr.template +290 -0
  78. data/lib/buildr/ide/idea7x.rb +210 -0
  79. data/lib/buildr/java.rb +26 -0
  80. data/lib/buildr/java/ant.rb +71 -0
  81. data/lib/buildr/java/bdd_frameworks.rb +267 -0
  82. data/lib/buildr/java/commands.rb +210 -0
  83. data/lib/buildr/java/compilers.rb +432 -0
  84. data/lib/buildr/java/deprecated.rb +141 -0
  85. data/lib/buildr/java/groovyc.rb +137 -0
  86. data/lib/buildr/java/jruby.rb +99 -0
  87. data/lib/buildr/java/org/apache/buildr/BuildrNail$Main.class +0 -0
  88. data/lib/buildr/java/org/apache/buildr/BuildrNail.class +0 -0
  89. data/lib/buildr/java/org/apache/buildr/BuildrNail.java +41 -0
  90. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  91. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +116 -0
  92. data/lib/buildr/java/packaging.rb +706 -0
  93. data/lib/buildr/java/pom.rb +178 -0
  94. data/lib/buildr/java/rjb.rb +142 -0
  95. data/lib/buildr/java/test_frameworks.rb +290 -0
  96. data/lib/buildr/java/version_requirement.rb +172 -0
  97. data/lib/buildr/packaging.rb +21 -0
  98. data/lib/buildr/packaging/artifact.rb +729 -0
  99. data/lib/buildr/packaging/artifact_namespace.rb +957 -0
  100. data/lib/buildr/packaging/artifact_search.rb +140 -0
  101. data/lib/buildr/packaging/gems.rb +102 -0
  102. data/lib/buildr/packaging/package.rb +233 -0
  103. data/lib/buildr/packaging/tar.rb +104 -0
  104. data/lib/buildr/packaging/zip.rb +719 -0
  105. data/rakelib/apache.rake +126 -0
  106. data/rakelib/changelog.rake +56 -0
  107. data/rakelib/doc.rake +103 -0
  108. data/rakelib/package.rake +44 -0
  109. data/rakelib/release.rake +53 -0
  110. data/rakelib/rspec.rake +81 -0
  111. data/rakelib/rubyforge.rake +45 -0
  112. data/rakelib/scm.rake +49 -0
  113. data/rakelib/setup.rake +59 -0
  114. data/rakelib/stage.rake +45 -0
  115. data/spec/application_spec.rb +316 -0
  116. data/spec/archive_spec.rb +494 -0
  117. data/spec/artifact_namespace_spec.rb +635 -0
  118. data/spec/artifact_spec.rb +738 -0
  119. data/spec/build_spec.rb +193 -0
  120. data/spec/checks_spec.rb +537 -0
  121. data/spec/common_spec.rb +579 -0
  122. data/spec/compile_spec.rb +561 -0
  123. data/spec/groovy_compilers_spec.rb +239 -0
  124. data/spec/java_bdd_frameworks_spec.rb +238 -0
  125. data/spec/java_compilers_spec.rb +446 -0
  126. data/spec/java_packaging_spec.rb +1042 -0
  127. data/spec/java_test_frameworks_spec.rb +414 -0
  128. data/spec/packaging_helper.rb +63 -0
  129. data/spec/packaging_spec.rb +589 -0
  130. data/spec/project_spec.rb +739 -0
  131. data/spec/sandbox.rb +116 -0
  132. data/spec/scala_compilers_spec.rb +239 -0
  133. data/spec/spec.opts +6 -0
  134. data/spec/spec_helpers.rb +283 -0
  135. data/spec/test_spec.rb +871 -0
  136. data/spec/transport_spec.rb +300 -0
  137. data/spec/version_requirement_spec.rb +115 -0
  138. metadata +324 -0
@@ -0,0 +1,134 @@
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
+ # Portion of this file derived from Rake.
17
+ # Copyright (c) 2003, 2004 Jim Weirich
18
+ #
19
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
20
+ # of this software and associated documentation files (the "Software"), to deal
21
+ # in the Software without restriction, including without limitation the rights
22
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23
+ # copies of the Software, and to permit persons to whom the Software is
24
+ # furnished to do so, subject to the following conditions:
25
+ #
26
+ # The above copyright notice and this permission notice shall be included in
27
+ # all copies or substantial portions of the Software.
28
+ #
29
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+ # SOFTWARE.
36
+
37
+
38
+ require 'getoptlong'
39
+
40
+
41
+ module Buildr
42
+ module CommandLineInterface
43
+
44
+ OPTIONS = [ # :nodoc:
45
+ ['--help', '-h', GetoptLong::NO_ARGUMENT,
46
+ 'Display this help message.'],
47
+ ['--nosearch', '-n', GetoptLong::NO_ARGUMENT,
48
+ 'Do not search parent directories for the buildfile.'],
49
+ ['--quiet', '-q', GetoptLong::NO_ARGUMENT,
50
+ 'Do not log messages to standard output.'],
51
+ ['--buildfile', '-f', GetoptLong::REQUIRED_ARGUMENT,
52
+ 'Use FILE as the buildfile.'],
53
+ ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
54
+ 'Require MODULE before executing buildfile.'],
55
+ ['--trace', '-t', GetoptLong::NO_ARGUMENT,
56
+ 'Turn on invoke/execute tracing, enable full backtrace.'],
57
+ ['--version', '-v', GetoptLong::NO_ARGUMENT,
58
+ 'Display the program version.'],
59
+ ['--environment', '-e', GetoptLong::REQUIRED_ARGUMENT,
60
+ 'Environment name (e.g. development, test, production).']
61
+ ]
62
+
63
+ def collect_tasks
64
+ top_level_tasks.clear
65
+ ARGV.each do |arg|
66
+ if arg =~ /^(\w+)=(.*)$/
67
+ ENV[$1.upcase] = $2
68
+ else
69
+ top_level_tasks << arg
70
+ end
71
+ end
72
+ top_level_tasks.push("default") if top_level_tasks.size == 0
73
+ end
74
+
75
+ def parse_options
76
+ opts = GetoptLong.new(*command_line_options)
77
+ opts.each { |opt, value| do_option(opt, value) }
78
+ end
79
+
80
+ def do_option(opt, value)
81
+ case opt
82
+ when '--help'
83
+ help
84
+ exit
85
+ when '--buildfile'
86
+ rakefiles.clear
87
+ rakefiles << value
88
+ when '--version'
89
+ puts version
90
+ exit
91
+ when '--environment'
92
+ ENV['BUILDR_ENV'] = value
93
+ when '--require'
94
+ requires << value
95
+ when '--nosearch', '--quiet', '--trace'
96
+ super
97
+ end
98
+ end
99
+
100
+ def command_line_options
101
+ OPTIONS.collect { |lst| lst[0..-2] }
102
+ end
103
+
104
+ def version
105
+ "Buildr #{Buildr::VERSION} #{RUBY_PLATFORM[/java/] && '(JRuby '+JRUBY_VERSION+')'}"
106
+ end
107
+
108
+ def usage
109
+ puts version
110
+ puts
111
+ puts 'Usage:'
112
+ puts ' buildr [options] [tasks] [name=value]'
113
+ end
114
+
115
+ def help
116
+ usage
117
+ puts
118
+ puts 'Options:'
119
+ OPTIONS.sort.each do |long, short, mode, desc|
120
+ if mode == GetoptLong::REQUIRED_ARGUMENT
121
+ if desc =~ /\b([A-Z]{2,})\b/
122
+ long = long + "=#{$1}"
123
+ end
124
+ end
125
+ printf " %-20s (%s)\n", long, short
126
+ printf " %s\n", desc
127
+ end
128
+ puts
129
+ puts 'For help with your buildfile:'
130
+ puts ' buildr help'
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,262 @@
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 recurvise 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 it 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
+ verbose(true) do
60
+ rm_rf project.path_to(:target)
61
+ rm_rf project.path_to(:reports)
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+ # *Deprecated:* Use +path_to(:target)+ instead.
68
+ def target
69
+ Buildr.application.deprecated 'Use path_to(:target) instead'
70
+ layout.expand(:target)
71
+ end
72
+
73
+ # *Deprecated:* Use Layout instead.
74
+ def target=(dir)
75
+ Buildr.application.deprecated 'Use Layout instead'
76
+ layout[:target] = _(dir)
77
+ end
78
+
79
+ # *Deprecated:* Use +path_to(:reports)+ instead.
80
+ def reports()
81
+ Buildr.application.deprecated 'Use path_to(:reports) instead'
82
+ layout.expand(:reports)
83
+ end
84
+
85
+ # *Deprecated:* Use Layout instead.
86
+ def reports=(dir)
87
+ Buildr.application.deprecated 'Use Layout instead'
88
+ layout[:reports] = _(dir)
89
+ end
90
+
91
+ # :call-seq:
92
+ # build(*prereqs) => task
93
+ # build { |task| .. } => task
94
+ #
95
+ # Returns the project's build task. With arguments or block, also enhances that task.
96
+ def build(*prereqs, &block)
97
+ task('build').enhance prereqs, &block
98
+ end
99
+
100
+ # :call-seq:
101
+ # clean(*prereqs) => task
102
+ # clean { |task| .. } => task
103
+ #
104
+ # Returns the project's clean task. With arguments or block, also enhances that task.
105
+ def clean(*prereqs, &block)
106
+ task('clean').enhance prereqs, &block
107
+ end
108
+
109
+ end
110
+
111
+
112
+ class Release
113
+
114
+ THIS_VERSION_PATTERN = /THIS_VERSION|VERSION_NUMBER\s*=\s*(["'])(.*)\1/
115
+ NEXT_VERSION_PATTERN = /NEXT_VERSION\s*=\s*(["'])(.*)\1/
116
+
117
+ class << self
118
+
119
+ # :call-seq:
120
+ # make()
121
+ #
122
+ # Make a release.
123
+ def make()
124
+ check
125
+ version = with_next_version do |filename, version|
126
+ options = ['--buildfile', filename, 'DEBUG=no']
127
+ options << '--environment' << Buildr.environment unless Buildr.environment.to_s.empty?
128
+ sh "#{command} clean upload #{options.join(' ')}"
129
+ end
130
+ tag version
131
+ commit version + '-SNAPSHOT'
132
+ end
133
+
134
+ protected
135
+
136
+ def command() #:nodoc:
137
+ Config::CONFIG['arch'] =~ /dos|win32/i ? $PROGRAM_NAME.ext('cmd') : $PROGRAM_NAME
138
+ end
139
+
140
+ # :call-seq:
141
+ # check()
142
+ #
143
+ # Check that we don't have any local changes in the working copy. Fails if it finds anything
144
+ # in the working copy that is not checked into source control.
145
+ def check()
146
+ fail "SVN URL must end with 'trunk' or 'branches/...'" unless svn_url =~ /(trunk)|(branches.*)$/
147
+ # Status check reveals modified file, but also SVN externals which we can safely ignore.
148
+ status = svn('status', '--ignore-externals').reject { |line| line =~ /^X\s/ }
149
+ fail "Uncommitted SVN files violate the First Principle Of Release!\n#{status}" unless
150
+ status.empty?
151
+ end
152
+
153
+ # :call-seq:
154
+ # with_next_version() { |filename| ... } => version
155
+ #
156
+ # Yields to block with upgraded version number, before committing to use it. Returns the *new*
157
+ # current version number.
158
+ #
159
+ # We need a Buildfile with upgraded version numbers to run the build, but we don't want the
160
+ # Buildfile modified unless the build succeeds. So this method updates the version numbers in
161
+ # a separate (Buildfile.next) file, yields to the block with that filename, and if successful
162
+ # copies the new file over the existing one.
163
+ #
164
+ # Version numbers are updated as follows. The next release version becomes the current one,
165
+ # and the next version is upgraded by one to become the new next version. So:
166
+ # THIS_VERSION = 1.1.0
167
+ # NEXT_VERSION = 1.2.0
168
+ # becomes:
169
+ # THIS_VERSION = 1.2.0
170
+ # NEXT_VERSION = 1.2.1
171
+ # and the method will return 1.2.0.
172
+ def with_next_version()
173
+ new_filename = Buildr.application.buildfile + '.next'
174
+ modified = change_version do |this_version, next_version|
175
+ one_after = next_version.split('.')
176
+ one_after[-1] = one_after[-1].to_i + 1
177
+ [ next_version, one_after.join('.') ]
178
+ end
179
+ File.open(new_filename, 'w') { |file| file.write modified }
180
+ begin
181
+ yield new_filename
182
+ mv new_filename, Buildr.application.buildfile
183
+ ensure
184
+ rm new_filename rescue nil
185
+ end
186
+ File.read(Buildr.application.buildfile).scan(THIS_VERSION_PATTERN)[0][1]
187
+ end
188
+
189
+ # :call-seq:
190
+ # change_version() { |this, next| ... } => buildfile
191
+ #
192
+ # Change version numbers in the current Buildfile, but without writing a new file (yet).
193
+ # Returns the contents of the Buildfile with the modified version numbers.
194
+ #
195
+ # This method yields to the block with the current (this) and next version numbers and expects
196
+ # an array with the new this and next version numbers.
197
+ def change_version()
198
+ buildfile = File.read(Buildr.application.buildfile)
199
+ this_version = buildfile.scan(THIS_VERSION_PATTERN)[0][1] or
200
+ fail "Looking for THIS_VERSION = \"...\" in your Buildfile, none found"
201
+ next_version = buildfile.scan(NEXT_VERSION_PATTERN)[0][1] or
202
+ fail "Looking for NEXT_VERSION = \"...\" in your Buildfile, none found"
203
+ this_version, next_version = yield(this_version, next_version)
204
+ if verbose
205
+ puts 'Upgrading version numbers:'
206
+ puts " This: #{this_version}"
207
+ puts " Next: #{next_version}"
208
+ end
209
+ buildfile.gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{this_version}"}) }.
210
+ gsub(NEXT_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{next_version}"}) }
211
+ end
212
+
213
+ # :call-seq:
214
+ # tag(version)
215
+ #
216
+ # Tags the current working copy with the release version number.
217
+ def tag(version)
218
+ url = svn_url.sub(/(trunk$)|(branches.*)$/, "tags/#{version}")
219
+ svn 'remove', url, '-m', 'Removing old copy' rescue nil
220
+ svn 'copy', Dir.pwd, url, '-m', "Release #{version}"
221
+ end
222
+
223
+ # :call-seq:
224
+ # commit(version)
225
+ #
226
+ # Last, we commit what we currently have in the working copy.
227
+ def commit(version)
228
+ buildfile = File.read(Buildr.application.buildfile).
229
+ gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{version}"}) }
230
+ File.open(Buildr.application.buildfile, 'w') { |file| file.write buildfile }
231
+ svn 'commit', '-m', "Changed version number to #{version}", Buildr.application.buildfile
232
+ end
233
+
234
+ # :call-seq:
235
+ # svn(*args)
236
+ #
237
+ # Executes SVN command and returns the output.
238
+ def svn(*args)
239
+ cmd = 'svn ' + args.map { |arg| arg[' '] ? %Q{"#{arg}"} : arg }.join(' ')
240
+ puts cmd if verbose
241
+ `#{cmd}`.tap { fail 'SVN command failed' unless $?.exitstatus == 0 }
242
+ end
243
+
244
+ # Return the current SVN URL
245
+ def svn_url
246
+ url = svn('info').scan(/URL: (.*)/)[0][0]
247
+ end
248
+ end
249
+
250
+ end
251
+
252
+ desc 'Make a release'
253
+ task 'release' do |task|
254
+ Release.make
255
+ end
256
+
257
+ end
258
+
259
+
260
+ class Buildr::Project
261
+ include Buildr::Build
262
+ end
@@ -0,0 +1,382 @@
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/packaging/zip'
19
+ require 'test/unit'
20
+ require 'spec/matchers'
21
+ require 'spec/expectations'
22
+
23
+
24
+ module Buildr
25
+ # Methods added to Project to allow checking the build.
26
+ module Checks
27
+
28
+ module Matchers #:nodoc:
29
+
30
+ class << self
31
+
32
+ # Define matchers that operate by calling a method on the tested object.
33
+ # For example:
34
+ # foo.should contain(bar)
35
+ # calls:
36
+ # foo.contain(bar)
37
+ def match_using(*names)
38
+ names.each do |name|
39
+ matcher = Class.new do
40
+ # Initialize with expected arguments (i.e. contain(bar) initializes with bar).
41
+ define_method(:initialize) { |*args| @expects = args }
42
+ # Matches against actual value (i.e. foo.should exist called with foo).
43
+ define_method(:matches?) do |actual|
44
+ @actual = actual
45
+ return actual.send("#{name}?", *@expects) if actual.respond_to?("#{name}?")
46
+ return actual.send(name, *@expects) if actual.respond_to?(name)
47
+ raise "You can't check #{actual}, it doesn't respond to #{name}."
48
+ end
49
+ # Some matchers have arguments, others don't, treat appropriately.
50
+ define_method :failure_message do
51
+ args = " " + @expects.map{ |arg| "'#{arg}'" }.join(", ") unless @expects.empty?
52
+ "Expected #{@actual} to #{name}#{args}"
53
+ end
54
+ define_method :negative_failure_message do
55
+ args = " " + @expects.map{ |arg| "'#{arg}'" }.join(", ") unless @expects.empty?
56
+ "Expected #{@actual} to not #{name}#{args}"
57
+ end
58
+ end
59
+ # Define method to create matcher.
60
+ define_method(name) { |*args| matcher.new(*args) }
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ # Define delegate matchers for exist and contain methods.
67
+ match_using :exist, :contain
68
+
69
+ end
70
+
71
+
72
+ # An expectation has subject, description and block. The expectation is validated by running the block,
73
+ # and can access the subject from the method #it. The description is used for reporting.
74
+ #
75
+ # The expectation is run by calling #run_against. You can share expectations by running them against
76
+ # different projects (or any other context for that matter).
77
+ #
78
+ # If the subject is missing, it is set to the argument of #run_against, typically the project itself.
79
+ # If the description is missing, it is set from the project. If the block is missing, the default behavior
80
+ # prints "Pending" followed by the description. You can use this to write place holders and fill them later.
81
+ class Expectation
82
+
83
+ attr_reader :description, :subject, :block
84
+
85
+ # :call-seq:
86
+ # initialize(subject, description?) { .... }
87
+ # initialize(description?) { .... }
88
+ #
89
+ # First argument is subject (returned from it method), second argument is description. If you omit the
90
+ # description, it will be set from the subject. If you omit the subject, it will be set from the object
91
+ # passed to run_against.
92
+ def initialize(*args, &block)
93
+ @description = args.pop if String === args.last
94
+ @subject = args.shift
95
+ raise ArgumentError, "Expecting subject followed by description, and either one is optional. Not quite sure what to do with this list of arguments." unless args.empty?
96
+ @block = block || lambda { puts "Pending: #{description}" if verbose }
97
+ end
98
+
99
+ # :call-seq:
100
+ # run_against(context)
101
+ #
102
+ # Runs this expectation against the context object. The context object is different from the subject,
103
+ # but used as the subject if no subject specified (i.e. returned from the it method).
104
+ #
105
+ # This method creates a new context object modeled after the context argument, but a separate object
106
+ # used strictly for running this expectation, and used only once. The context object will pass methods
107
+ # to the context argument, so you can call any method, e.g. package(:jar).
108
+ #
109
+ # It also adds all matchers defined in Buildr and RSpec, and two additional methods:
110
+ # * it() -- Returns the subject.
111
+ # * description() -- Returns the description.
112
+ def run_against(context)
113
+ subject = @subject || context
114
+ description = @description ? "#{subject} #{@description}" : subject.to_s
115
+ # Define anonymous class and load it with:
116
+ # - All instance methods defined in context, so we can pass method calls to the context.
117
+ # - it() method to return subject, description() method to return description.
118
+ # - All matchers defined by Buildr and RSpec.
119
+ klass = Class.new
120
+ klass.instance_eval do
121
+ context.class.instance_methods(false).each do |method|
122
+ define_method(method) { |*args| context.send(method, *args) }
123
+ end
124
+ define_method(:it) { subject }
125
+ define_method(:description) { description }
126
+ include Spec::Matchers
127
+ include Matchers
128
+ end
129
+
130
+ # Run the expectation. We only print the expectation name when tracing (to know they all ran),
131
+ # or when we get a failure.
132
+ begin
133
+ puts description if Buildr.application.options.trace
134
+ klass.new.instance_eval &@block
135
+ rescue Exception=>error
136
+ raise error.exception("#{description}\n#{error}").tap { |wrapped| wrapped.set_backtrace(error.backtrace) }
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+
143
+ include Extension
144
+
145
+ before_define do |project|
146
+ # The check task can do any sort of interesting things, but the most important is running expectations.
147
+ project.task("check") do |task|
148
+ project.expectations.inject(true) do |passed, expect|
149
+ begin
150
+ expect.run_against project
151
+ passed
152
+ rescue Exception=>error
153
+ if verbose
154
+ puts error.backtrace.detect { |line| line =~ /#{Buildr.application.buildfile}/ } || ""
155
+ puts error
156
+ end
157
+ false
158
+ end
159
+ end or fail "Checks failed for project #{project.name} (see errors above)."
160
+ end
161
+ project.task("package").enhance do |task|
162
+ # Run all actions before checks.
163
+ task.enhance { project.task("check").invoke }
164
+ end
165
+ end
166
+
167
+
168
+ # :call-seq:
169
+ # check(description) { ... }
170
+ # check(subject, description) { ... }
171
+ #
172
+ # Adds an expectation. The expectation is run against the project by the check task, executed after packaging.
173
+ # You can access any package created by the project.
174
+ #
175
+ # An expectation is written using a subject, description and block to validate the expectation. For example:
176
+ #
177
+ # For example:
178
+ # check package(:jar), "should exist" do
179
+ # it.should exist
180
+ # end
181
+ # check package(:jar), "should contain a manifest" do
182
+ # it.should contain("META-INF/MANIFEST.MF")
183
+ # end
184
+ # check package(:jar).path("com/acme"), "should contain classes" do
185
+ # it.should_not be_empty
186
+ # end
187
+ # check package(:jar).entry("META-INF/MANIFEST"), "should be a recent license" do
188
+ # it.should contain(/Copyright (C) 2007/)
189
+ # end
190
+ #
191
+ # If you omit the subject, the project is used as the subject. If you omit the description, the subject is
192
+ # used as description.
193
+ #
194
+ # During development you can write placeholder expectations by omitting the block. This will simply report
195
+ # the expectation as pending.
196
+ def check(*args, &block)
197
+ expectations << Checks::Expectation.new(*args, &block)
198
+ end
199
+
200
+ # :call-seq:
201
+ # expectations() => Expectation*
202
+ #
203
+ # Returns a list of expectations (see #check).
204
+ def expectations()
205
+ @expectations ||= []
206
+ end
207
+
208
+ end
209
+
210
+ end
211
+
212
+
213
+ module Rake #:nodoc:
214
+ class FileTask
215
+
216
+ # :call-seq:
217
+ # exist?() => boolean
218
+ #
219
+ # Returns true if this file exists.
220
+ def exist?()
221
+ File.exist?(name)
222
+ end
223
+
224
+ # :call-seq:
225
+ # empty?() => boolean
226
+ #
227
+ # Returns true if file/directory is empty.
228
+ def empty?()
229
+ File.directory?(name) ? Dir.glob("#{name}/*").empty? : File.read(name).empty?
230
+ end
231
+
232
+ # :call-seq:
233
+ # contain?(pattern*) => boolean
234
+ # contain?(file*) => boolean
235
+ #
236
+ # For a file, returns true if the file content matches against all the arguments. An argument may be
237
+ # a string or regular expression.
238
+ #
239
+ # For a directory, return true if the directory contains the specified files. You can use relative
240
+ # file names and glob patterns (using *, **, etc).
241
+ def contain?(*patterns)
242
+ if File.directory?(name)
243
+ patterns.map { |pattern| "#{name}/#{pattern}" }.all? { |pattern| !Dir[pattern].empty? }
244
+ else
245
+ contents = File.read(name)
246
+ patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
247
+ all? { |pattern| contents =~ pattern }
248
+ end
249
+ end
250
+
251
+ end
252
+ end
253
+
254
+
255
+ module Zip #:nodoc:
256
+ class ZipEntry
257
+
258
+ # :call-seq:
259
+ # exist() => boolean
260
+ #
261
+ # Returns true if this entry exists.
262
+ def exist?()
263
+ Zip::ZipFile.open(zipfile) { |zip| zip.file.exist?(@name) }
264
+ end
265
+
266
+ # :call-seq:
267
+ # empty?() => boolean
268
+ #
269
+ # Returns true if this entry is empty.
270
+ def empty?()
271
+ Zip::ZipFile.open(zipfile) { |zip| zip.file.read(@name) }.empty?
272
+ end
273
+
274
+ # :call-seq:
275
+ # contain(patterns*) => boolean
276
+ #
277
+ # Returns true if this ZIP file entry matches against all the arguments. An argument may be
278
+ # a string or regular expression.
279
+ def contain?(*patterns)
280
+ content = Zip::ZipFile.open(zipfile) { |zip| zip.file.read(@name) }
281
+ patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
282
+ all? { |pattern| content =~ pattern }
283
+ end
284
+
285
+ end
286
+ end
287
+
288
+
289
+ class Buildr::ArchiveTask
290
+
291
+ class Path #:nodoc:
292
+
293
+ # :call-seq:
294
+ # exist() => boolean
295
+ #
296
+ # Returns true if this path exists. This only works if the path has any entries in it,
297
+ # so exist on path happens to be the opposite of empty.
298
+ def exist?()
299
+ !entries.empty?
300
+ end
301
+
302
+ # :call-seq:
303
+ # empty?() => boolean
304
+ #
305
+ # Returns true if this path is empty (has no other entries inside).
306
+ def empty?()
307
+ entries.all? { |entry| entry.empty? }
308
+ end
309
+
310
+ # :call-seq:
311
+ # contain(file*) => boolean
312
+ #
313
+ # Returns true if this ZIP file path contains all the specified files. You can use relative
314
+ # file names and glob patterns (using *, **, etc).
315
+ def contain?(*files)
316
+ files.all? { |file| entries.detect { |entry| File.fnmatch(file, entry.to_s, File::FNM_PATHNAME) } }
317
+ end
318
+
319
+ # :call-seq:
320
+ # entry(name) => ZipEntry
321
+ #
322
+ # Returns a ZIP file entry. You can use this to check if the entry exists and its contents,
323
+ # for example:
324
+ # package(:jar).path("META-INF").entry("LICENSE").should contain(/Apache Software License/)
325
+ def entry(name)
326
+ root.entry("#{@path}#{name}")
327
+ end
328
+
329
+ protected
330
+
331
+ def entries() #:nodoc:
332
+ return root.entries unless @path
333
+ @entries ||= root.entries.inject([]) { |selected, entry|
334
+ selected << entry.name.sub(@path, "") if entry.name.index(@path) == 0
335
+ selected
336
+ }
337
+ end
338
+
339
+ end
340
+
341
+ # :call-seq:
342
+ # empty?() => boolean
343
+ #
344
+ # Returns true if this ZIP file is empty (has no other entries inside).
345
+ def empty?()
346
+ path("").empty
347
+ end
348
+
349
+ # :call-seq:
350
+ # contain(file*) => boolean
351
+ #
352
+ # Returns true if this ZIP file contains all the specified files. You can use absolute
353
+ # file names and glob patterns (using *, **, etc).
354
+ def contain?(*files)
355
+ path("").contain?(*files)
356
+ end
357
+
358
+ end
359
+
360
+
361
+ class Buildr::ZipTask #:nodoc:
362
+
363
+ # :call-seq:
364
+ # entry(name) => Entry
365
+ #
366
+ # Returns a ZIP file entry. You can use this to check if the entry exists and its contents,
367
+ # for example:
368
+ # package(:jar).entry("META-INF/LICENSE").should contain(/Apache Software License/)
369
+ def entry(entry_name)
370
+ ::Zip::ZipEntry.new(name, entry_name)
371
+ end
372
+
373
+ def entries() #:nodoc:
374
+ @entries ||= Zip::ZipFile.open(name) { |zip| zip.entries }
375
+ end
376
+
377
+ end
378
+
379
+
380
+ class Buildr::Project
381
+ include Buildr::Checks
382
+ end