assaf-buildr 1.3.3

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 (163) hide show
  1. data/CHANGELOG +887 -0
  2. data/DISCLAIMER +7 -0
  3. data/LICENSE +176 -0
  4. data/NOTICE +26 -0
  5. data/README.rdoc +146 -0
  6. data/Rakefile +62 -0
  7. data/_buildr +38 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/cobertura.rb +236 -0
  10. data/addon/buildr/emma.rb +238 -0
  11. data/addon/buildr/hibernate.rb +142 -0
  12. data/addon/buildr/javacc.rb +85 -0
  13. data/addon/buildr/jdepend.rb +60 -0
  14. data/addon/buildr/jetty.rb +248 -0
  15. data/addon/buildr/jibx.rb +86 -0
  16. data/addon/buildr/nailgun.rb +817 -0
  17. data/addon/buildr/openjpa.rb +90 -0
  18. data/addon/buildr/org/apache/buildr/BuildrNail$Main.class +0 -0
  19. data/addon/buildr/org/apache/buildr/BuildrNail.class +0 -0
  20. data/addon/buildr/org/apache/buildr/BuildrNail.java +41 -0
  21. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  22. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  23. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  24. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  25. data/addon/buildr/xmlbeans.rb +93 -0
  26. data/bin/buildr +28 -0
  27. data/buildr.buildfile +53 -0
  28. data/buildr.gemspec +58 -0
  29. data/doc/css/default.css +228 -0
  30. data/doc/css/print.css +100 -0
  31. data/doc/css/syntax.css +52 -0
  32. data/doc/images/apache-incubator-logo.png +0 -0
  33. data/doc/images/buildr-hires.png +0 -0
  34. data/doc/images/buildr.png +0 -0
  35. data/doc/images/favicon.png +0 -0
  36. data/doc/images/growl-icon.tiff +0 -0
  37. data/doc/images/note.png +0 -0
  38. data/doc/images/project-structure.png +0 -0
  39. data/doc/images/tip.png +0 -0
  40. data/doc/images/zbuildr.tif +0 -0
  41. data/doc/pages/artifacts.textile +207 -0
  42. data/doc/pages/building.textile +240 -0
  43. data/doc/pages/contributing.textile +208 -0
  44. data/doc/pages/download.textile +62 -0
  45. data/doc/pages/extending.textile +175 -0
  46. data/doc/pages/getting_started.textile +273 -0
  47. data/doc/pages/index.textile +42 -0
  48. data/doc/pages/languages.textile +407 -0
  49. data/doc/pages/mailing_lists.textile +17 -0
  50. data/doc/pages/more_stuff.textile +286 -0
  51. data/doc/pages/packaging.textile +427 -0
  52. data/doc/pages/projects.textile +274 -0
  53. data/doc/pages/recipes.textile +103 -0
  54. data/doc/pages/settings_profiles.textile +274 -0
  55. data/doc/pages/testing.textile +212 -0
  56. data/doc/pages/troubleshooting.textile +103 -0
  57. data/doc/pages/whats_new.textile +323 -0
  58. data/doc/print.haml +51 -0
  59. data/doc/print.toc.yaml +29 -0
  60. data/doc/scripts/buildr-git.rb +412 -0
  61. data/doc/scripts/install-jruby.sh +44 -0
  62. data/doc/scripts/install-linux.sh +64 -0
  63. data/doc/scripts/install-osx.sh +52 -0
  64. data/doc/site.haml +56 -0
  65. data/doc/site.toc.yaml +47 -0
  66. data/etc/KEYS +151 -0
  67. data/etc/git-svn-authors +16 -0
  68. data/lib/buildr.rb +35 -0
  69. data/lib/buildr/core.rb +27 -0
  70. data/lib/buildr/core/application.rb +489 -0
  71. data/lib/buildr/core/application_cli.rb +139 -0
  72. data/lib/buildr/core/build.rb +311 -0
  73. data/lib/buildr/core/checks.rb +382 -0
  74. data/lib/buildr/core/common.rb +154 -0
  75. data/lib/buildr/core/compile.rb +596 -0
  76. data/lib/buildr/core/environment.rb +120 -0
  77. data/lib/buildr/core/filter.rb +362 -0
  78. data/lib/buildr/core/generate.rb +195 -0
  79. data/lib/buildr/core/help.rb +118 -0
  80. data/lib/buildr/core/progressbar.rb +156 -0
  81. data/lib/buildr/core/project.rb +892 -0
  82. data/lib/buildr/core/test.rb +715 -0
  83. data/lib/buildr/core/transports.rb +558 -0
  84. data/lib/buildr/core/util.rb +289 -0
  85. data/lib/buildr/groovy.rb +18 -0
  86. data/lib/buildr/groovy/bdd.rb +105 -0
  87. data/lib/buildr/groovy/compiler.rb +138 -0
  88. data/lib/buildr/ide.rb +19 -0
  89. data/lib/buildr/ide/eclipse.rb +212 -0
  90. data/lib/buildr/ide/idea.ipr.template +300 -0
  91. data/lib/buildr/ide/idea.rb +189 -0
  92. data/lib/buildr/ide/idea7x.ipr.template +290 -0
  93. data/lib/buildr/ide/idea7x.rb +210 -0
  94. data/lib/buildr/java.rb +23 -0
  95. data/lib/buildr/java/ant.rb +92 -0
  96. data/lib/buildr/java/bdd.rb +449 -0
  97. data/lib/buildr/java/commands.rb +211 -0
  98. data/lib/buildr/java/compiler.rb +348 -0
  99. data/lib/buildr/java/deprecated.rb +141 -0
  100. data/lib/buildr/java/jruby.rb +117 -0
  101. data/lib/buildr/java/jtestr_runner.rb.erb +116 -0
  102. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  103. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +119 -0
  104. data/lib/buildr/java/packaging.rb +713 -0
  105. data/lib/buildr/java/pom.rb +178 -0
  106. data/lib/buildr/java/rjb.rb +155 -0
  107. data/lib/buildr/java/test_result.rb +308 -0
  108. data/lib/buildr/java/tests.rb +324 -0
  109. data/lib/buildr/java/version_requirement.rb +172 -0
  110. data/lib/buildr/packaging.rb +21 -0
  111. data/lib/buildr/packaging/artifact.rb +730 -0
  112. data/lib/buildr/packaging/artifact_namespace.rb +972 -0
  113. data/lib/buildr/packaging/artifact_search.rb +140 -0
  114. data/lib/buildr/packaging/gems.rb +102 -0
  115. data/lib/buildr/packaging/package.rb +233 -0
  116. data/lib/buildr/packaging/tar.rb +104 -0
  117. data/lib/buildr/packaging/zip.rb +722 -0
  118. data/lib/buildr/resources/buildr.icns +0 -0
  119. data/lib/buildr/scala.rb +19 -0
  120. data/lib/buildr/scala/compiler.rb +109 -0
  121. data/lib/buildr/scala/tests.rb +203 -0
  122. data/rakelib/apache.rake +191 -0
  123. data/rakelib/changelog.rake +57 -0
  124. data/rakelib/doc.rake +103 -0
  125. data/rakelib/package.rake +73 -0
  126. data/rakelib/release.rake +65 -0
  127. data/rakelib/rspec.rake +83 -0
  128. data/rakelib/rubyforge.rake +53 -0
  129. data/rakelib/scm.rake +49 -0
  130. data/rakelib/setup.rake +86 -0
  131. data/rakelib/stage.rake +48 -0
  132. data/spec/addon/cobertura_spec.rb +77 -0
  133. data/spec/addon/emma_spec.rb +120 -0
  134. data/spec/addon/test_coverage_spec.rb +255 -0
  135. data/spec/core/application_spec.rb +412 -0
  136. data/spec/core/artifact_namespace_spec.rb +646 -0
  137. data/spec/core/build_spec.rb +415 -0
  138. data/spec/core/checks_spec.rb +537 -0
  139. data/spec/core/common_spec.rb +664 -0
  140. data/spec/core/compile_spec.rb +566 -0
  141. data/spec/core/generate_spec.rb +33 -0
  142. data/spec/core/project_spec.rb +754 -0
  143. data/spec/core/test_spec.rb +1091 -0
  144. data/spec/core/transport_spec.rb +500 -0
  145. data/spec/groovy/bdd_spec.rb +80 -0
  146. data/spec/groovy/compiler_spec.rb +239 -0
  147. data/spec/ide/eclipse_spec.rb +243 -0
  148. data/spec/java/ant.rb +28 -0
  149. data/spec/java/bdd_spec.rb +358 -0
  150. data/spec/java/compiler_spec.rb +446 -0
  151. data/spec/java/java_spec.rb +88 -0
  152. data/spec/java/packaging_spec.rb +1103 -0
  153. data/spec/java/tests_spec.rb +466 -0
  154. data/spec/packaging/archive_spec.rb +503 -0
  155. data/spec/packaging/artifact_spec.rb +754 -0
  156. data/spec/packaging/packaging_helper.rb +63 -0
  157. data/spec/packaging/packaging_spec.rb +589 -0
  158. data/spec/sandbox.rb +137 -0
  159. data/spec/scala/compiler_spec.rb +228 -0
  160. data/spec/scala/tests_spec.rb +215 -0
  161. data/spec/spec_helpers.rb +304 -0
  162. data/spec/version_requirement_spec.rb +123 -0
  163. metadata +369 -0
@@ -0,0 +1,324 @@
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/build'
18
+ require 'buildr/core/compile'
19
+ require 'buildr/java/ant'
20
+
21
+
22
+ module Buildr
23
+
24
+ class TestFramework::Java < TestFramework::Base
25
+
26
+ class << self
27
+
28
+ def applies_to?(project) #:nodoc:
29
+ project.test.compile.language == :java
30
+ end
31
+
32
+ end
33
+
34
+ private
35
+
36
+ # Add buildr utilities (JavaTestFilter) to classpath
37
+ Java.classpath << File.join(File.dirname(__FILE__))
38
+
39
+ # :call-seq:
40
+ # filter_classes(dependencies, criteria)
41
+ #
42
+ # Return a list of classnames that match the given criteria.
43
+ # The criteria parameter is a hash that must contain at least one of:
44
+ #
45
+ # * :class_names -- List of patterns to match against class name
46
+ # * :interfaces -- List of java interfaces or java classes
47
+ # * :class_annotations -- List of annotations on class level
48
+ # * :method_annotations -- List of annotations on method level
49
+ #
50
+ def filter_classes(dependencies, criteria = {})
51
+ return [] unless task.compile.target
52
+ target = task.compile.target.to_s
53
+ candidates = Dir["#{target}/**/*.class"].
54
+ map { |file| Util.relative_path(file, target).ext('').gsub(File::SEPARATOR, '.') }.
55
+ reject { |name| name =~ /\$/ }
56
+ result = []
57
+ if criteria[:class_names]
58
+ result.concat candidates.select { |name| criteria[:class_names].flatten.any? { |pat| pat === name } }
59
+ end
60
+ begin
61
+ Java.load
62
+ filter = Java.org.apache.buildr.JavaTestFilter.new(dependencies.to_java(Java.java.lang.String))
63
+ if criteria[:interfaces]
64
+ filter.add_interfaces(criteria[:interfaces].to_java(Java.java.lang.String))
65
+ end
66
+ if criteria[:class_annotations]
67
+ filter.add_class_annotations(criteria[:class_annotations].to_java(Java.java.lang.String))
68
+ end
69
+ if criteria[:method_annotations]
70
+ filter.add_method_annotations(criteria[:method_annotations].to_java(Java.java.lang.String))
71
+ end
72
+ result.concat filter.filter(candidates.to_java(Java.java.lang.String)).map(&:to_s)
73
+ rescue =>ex
74
+ info "#{ex.class}: #{ex.message}"
75
+ raise
76
+ end
77
+ result.uniq
78
+ end
79
+
80
+ end
81
+
82
+
83
+ # JMock is available when using JUnit and TestNG, JBehave.
84
+ module JMock
85
+
86
+ VERSION = '1.2.0'
87
+
88
+ class << self
89
+ def version
90
+ Buildr.settings.build['jmock'] || VERSION
91
+ end
92
+
93
+ def dependencies
94
+ @dependencies ||= ["jmock:jmock:jar:#{version}"]
95
+ end
96
+
97
+ private
98
+ def const_missing(const)
99
+ return super unless const == :REQUIRES # TODO: remove in 1.5
100
+ Buildr.application.deprecated "Please use JMock.dependencies/.version instead of JMock::REQUIRES/VERSION"
101
+ dependencies
102
+ end
103
+ end
104
+ end
105
+
106
+
107
+ # JUnit test framework, the default test framework for Java tests.
108
+ #
109
+ # Support the following options:
110
+ # * :fork -- If true/:once (default), fork for each test class. If :each, fork for each individual
111
+ # test case. If false, run all tests in the same VM (fast, but dangerous).
112
+ # * :clonevm -- If true clone the VM each time it is forked.
113
+ # * :properties -- Hash of system properties available to the test case.
114
+ # * :environment -- Hash of environment variables available to the test case.
115
+ # * :java_args -- Arguments passed as is to the JVM.
116
+ class JUnit < TestFramework::Java
117
+
118
+ # Used by the junit:report task. Access through JUnit#report if you want to set various
119
+ # options for that task, for example:
120
+ # JUnit.report.frames = false
121
+ class Report
122
+
123
+ # Parameters passed to the Ant JUnitReport task.
124
+ attr_reader :params
125
+ # True (default) to produce a report using frames, false to produce a single-page report.
126
+ attr_accessor :frames
127
+ # Directory for the report style (defaults to using the internal style).
128
+ attr_accessor :style_dir
129
+ # Target directory for generated report.
130
+ attr_accessor :target
131
+
132
+ def initialize
133
+ @params = {}
134
+ @frames = true
135
+ @target = 'reports/junit'
136
+ end
137
+
138
+ # :call-seq:
139
+ # generate(projects, target?)
140
+ #
141
+ # Generates a JUnit report for these projects (must run JUnit tests first) into the
142
+ # target directory. You can specify a target, or let it pick the default one from the
143
+ # target attribute.
144
+ def generate(projects, target = @target.to_s)
145
+ html_in = File.join(target, 'html')
146
+ rm_rf html_in ; mkpath html_in
147
+
148
+ Buildr.ant('junit-report') do |ant|
149
+ ant.taskdef :name=>'junitreport', :classname=>'org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator',
150
+ :classpath=>Buildr.artifacts(JUnit.ant_taskdef).each(&:invoke).map(&:to_s).join(File::PATH_SEPARATOR)
151
+ ant.junitreport :todir=>target do
152
+ projects.select { |project| project.test.framework == :junit }.
153
+ map { |project| project.test.report_to.to_s }.select { |path| File.exist?(path) }.
154
+ each { |path| ant.fileset(:dir=>path) { ant.include :name=>'TEST-*.xml' } }
155
+ options = { :format=>frames ? 'frames' : 'noframes' }
156
+ options[:styledir] = style_dir if style_dir
157
+ ant.report options.merge(:todir=>html_in) do
158
+ params.each { |key, value| ant.param :name=>key, :expression=>value }
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ # JUnit version number.
167
+ VERSION = '4.4'
168
+
169
+ class << self
170
+ # :call-seq:
171
+ # report()
172
+ #
173
+ # Returns the Report object used by the junit:report task. You can use this object to set
174
+ # various options that affect your report, for example:
175
+ # JUnit.report.frames = false
176
+ # JUnit.report.params['title'] = 'My App'
177
+ def report
178
+ @report ||= Report.new
179
+ end
180
+
181
+ def version
182
+ Buildr.settings.build['junit'] || VERSION
183
+ end
184
+
185
+ def dependencies
186
+ @dependencies ||= ["junit:junit:jar:#{version}"]+ JMock.dependencies
187
+ end
188
+
189
+ def ant_taskdef #:nodoc:
190
+ "org.apache.ant:ant-junit:jar:#{Ant.version}"
191
+ end
192
+
193
+ private
194
+ def const_missing(const)
195
+ return super unless const == :REQUIRES # TODO: remove in 1.5
196
+ Buildr.application.deprecated "Please use JUnit.dependencies/.version instead of JUnit::REQUIRES/VERSION"
197
+ dependencies
198
+ end
199
+ end
200
+
201
+ def tests(dependencies) #:nodoc:
202
+ filter_classes(dependencies,
203
+ :interfaces => %w{junit.framework.TestCase},
204
+ :class_annotations => %w{org.junit.runner.RunWith},
205
+ :method_annotations => %w{org.junit.Test})
206
+ end
207
+
208
+ def run(tests, dependencies) #:nodoc:
209
+ # Use Ant to execute the Junit tasks, gives us performance and reporting.
210
+ Buildr.ant('junit') do |ant|
211
+ case options[:fork]
212
+ when false
213
+ forking = {}
214
+ when :each
215
+ forking = { :fork=>true, :forkmode=>'perTest' }
216
+ when true, :once
217
+ forking = { :fork=>true, :forkmode=>'once' }
218
+ else
219
+ fail 'Option fork must be :once, :each or false.'
220
+ end
221
+ mkpath task.report_to.to_s
222
+
223
+ taskdef = Buildr.artifact(JUnit.ant_taskdef)
224
+ taskdef.invoke
225
+ ant.taskdef :name=>'junit', :classname=>'org.apache.tools.ant.taskdefs.optional.junit.JUnitTask', :classpath=>taskdef.to_s
226
+
227
+ ant.junit forking.merge(:clonevm=>options[:clonevm] || false, :dir=>task.send(:project).path_to) do
228
+ ant.classpath :path=>dependencies.join(File::PATH_SEPARATOR)
229
+ (options[:properties] || []).each { |key, value| ant.sysproperty :key=>key, :value=>value }
230
+ (options[:environment] || []).each { |key, value| ant.env :key=>key, :value=>value }
231
+ Array(options[:java_args]).each { |value| ant.jvmarg :value=>value }
232
+ ant.formatter :type=>'plain'
233
+ ant.formatter :type=>'plain', :usefile=>false # log test
234
+ ant.formatter :type=>'xml'
235
+ ant.batchtest :todir=>task.report_to.to_s, :failureproperty=>'failed' do
236
+ ant.fileset :dir=>task.compile.target.to_s do
237
+ tests.each { |test| ant.include :name=>File.join(*test.split('.')).ext('class') }
238
+ end
239
+ end
240
+ end
241
+ return tests unless ant.project.getProperty('failed')
242
+ end
243
+ # But Ant doesn't tell us what went kaput, so we'll have to parse the test files.
244
+ tests.inject([]) do |passed, test|
245
+ report_file = File.join(task.report_to.to_s, "TEST-#{test}.txt")
246
+ if File.exist?(report_file)
247
+ report = File.read(report_file)
248
+ # The second line (if exists) is the status line and we scan it for its values.
249
+ status = (report.split("\n")[1] || '').scan(/(run|failures|errors):\s*(\d+)/i).
250
+ inject(Hash.new(0)) { |hash, pair| hash[pair[0].downcase.to_sym] = pair[1].to_i ; hash }
251
+ passed << test if status[:failures] == 0 && status[:errors] == 0
252
+ end
253
+ passed
254
+ end
255
+ end
256
+
257
+ namespace 'junit' do
258
+ desc "Generate JUnit tests report in #{report.target}"
259
+ task('report') do |task|
260
+ report.generate Project.projects
261
+ info "Generated JUnit tests report in #{report.target}"
262
+ end
263
+ end
264
+
265
+ task('clean') { rm_rf report.target.to_s }
266
+
267
+ end
268
+
269
+
270
+ # TestNG test framework. To use in your project:
271
+ # test.using :testng
272
+ #
273
+ # Support the following options:
274
+ # * :properties -- Hash of properties passed to the test suite.
275
+ # * :java_args -- Arguments passed to the JVM.
276
+ class TestNG < TestFramework::Java
277
+
278
+ VERSION = '5.7'
279
+
280
+ class << self
281
+ def version
282
+ Buildr.settings.build['testng'] || VERSION
283
+ end
284
+
285
+ def dependencies
286
+ ["org.testng:testng:jar:jdk15:#{version}"]+ JMock.dependencies
287
+ end
288
+
289
+ private
290
+ def const_missing(const)
291
+ return super unless const == :REQUIRES # TODO: remove in 1.5
292
+ Buildr.application.deprecated "Please use TestNG.dependencies/.version instead of TestNG::REQUIRES/VERSION"
293
+ dependencies
294
+ end
295
+ end
296
+
297
+ def tests(dependencies) #:nodoc:
298
+ filter_classes(dependencies,
299
+ :class_annotations => %w{org.testng.annotations.Test},
300
+ :method_annotations => %w{org.testng.annotations.Test})
301
+ end
302
+
303
+ def run(tests, dependencies) #:nodoc:
304
+ cmd_args = [ 'org.testng.TestNG', '-sourcedir', task.compile.sources.join(';'), '-suitename', task.send(:project).name ]
305
+ cmd_args << '-d' << task.report_to.to_s
306
+ cmd_options = { :properties=>options[:properties], :java_args=>options[:java_args],
307
+ :classpath=>dependencies }
308
+ tests.inject([]) do |passed, test|
309
+ begin
310
+ Java::Commands.java cmd_args, '-testclass', test, cmd_options.merge(:name=>test)
311
+ passed << test
312
+ rescue
313
+ passed
314
+ end
315
+ end
316
+ end
317
+
318
+ end
319
+
320
+ end # Buildr
321
+
322
+
323
+ Buildr::TestFramework << Buildr::JUnit
324
+ Buildr::TestFramework << Buildr::TestNG
@@ -0,0 +1,172 @@
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
+ module Buildr
18
+
19
+ #
20
+ # See ArtifactNamespace#need
21
+ class VersionRequirement
22
+
23
+ CMP_PROCS = Gem::Requirement::OPS.dup
24
+ CMP_REGEX = Gem::Requirement::OP_RE.dup
25
+ CMP_CHARS = CMP_PROCS.keys.join
26
+ BOOL_CHARS = '\|\&\!'
27
+ VER_CHARS = '\w\.\-'
28
+
29
+ class << self
30
+ # is +str+ a version string?
31
+ def version?(str)
32
+ /^\s*[#{VER_CHARS}]+\s*$/ === str
33
+ end
34
+
35
+ # is +str+ a version requirement?
36
+ def requirement?(str)
37
+ /[#{BOOL_CHARS}#{CMP_CHARS}\(\)]/ === str
38
+ end
39
+
40
+ # :call-seq:
41
+ # VersionRequirement.create(" >1 <2 !(1.5) ") -> requirement
42
+ #
43
+ # parse the +str+ requirement
44
+ def create(str)
45
+ instance_eval normalize(str)
46
+ rescue StandardError => e
47
+ raise "Failed to parse #{str.inspect} due to: #{e}"
48
+ end
49
+
50
+ private
51
+ def requirement(req)
52
+ unless req =~ /^\s*(#{CMP_REGEX})?\s*([#{VER_CHARS}]+)\s*$/
53
+ raise "Invalid requirement string: #{req}"
54
+ end
55
+ comparator, version = $1, $2
56
+ version = Gem::Version.new(0).tap { |v| v.version = version }
57
+ VersionRequirement.new(nil, [$1, version])
58
+ end
59
+
60
+ def negate(vreq)
61
+ vreq.negative = !vreq.negative
62
+ vreq
63
+ end
64
+
65
+ def normalize(str)
66
+ str = str.strip
67
+ if str[/[^\s\(\)#{BOOL_CHARS + VER_CHARS + CMP_CHARS}]/]
68
+ raise "version string #{str.inspect} contains invalid characters"
69
+ end
70
+ str.gsub!(/\s+(and|\&\&)\s+/, ' & ')
71
+ str.gsub!(/\s+(or|\|\|)\s+/, ' | ')
72
+ str.gsub!(/(^|\s*)not\s+/, ' ! ')
73
+ pattern = /(#{CMP_REGEX})?\s*[#{VER_CHARS}]+/
74
+ left_pattern = /[#{VER_CHARS}\)]$/
75
+ right_pattern = /^(#{pattern}|\()/
76
+ str = str.split.inject([]) do |ary, i|
77
+ ary << '&' if ary.last =~ left_pattern && i =~ right_pattern
78
+ ary << i
79
+ end
80
+ str = str.join(' ')
81
+ str.gsub!(/!([^=])?/, ' negate \1')
82
+ str.gsub!(pattern) do |expr|
83
+ case expr.strip
84
+ when 'not', 'negate' then 'negate '
85
+ else 'requirement("' + expr + '")'
86
+ end
87
+ end
88
+ str.gsub!(/negate\s+\(/, 'negate(')
89
+ str
90
+ end
91
+ end
92
+
93
+ def initialize(op, *requirements) #:nodoc:
94
+ @op, @requirements = op, requirements
95
+ end
96
+
97
+ # Is this object a composed requirement?
98
+ # VersionRequirement.create('1').composed? -> false
99
+ # VersionRequirement.create('1 | 2').composed? -> true
100
+ # VersionRequirement.create('1 & 2').composed? -> true
101
+ def composed?
102
+ requirements.size > 1
103
+ end
104
+
105
+ # Return the last requirement on this object having an = operator.
106
+ def default
107
+ default = nil
108
+ requirements.reverse.find do |r|
109
+ if Array === r
110
+ if !negative && (r.first.nil? || r.first.include?('='))
111
+ default = r.last.to_s
112
+ end
113
+ else
114
+ default = r.default
115
+ end
116
+ end
117
+ default
118
+ end
119
+
120
+ # Test if this requirement can be satisfied by +version+
121
+ def satisfied_by?(version)
122
+ return false unless version
123
+ unless version.kind_of?(Gem::Version)
124
+ raise "Invalid version: #{version.inspect}" unless self.class.version?(version)
125
+ version = Gem::Version.new(0).tap { |v| v.version = version.strip }
126
+ end
127
+ message = op == :| ? :any? : :all?
128
+ result = requirements.send message do |req|
129
+ if Array === req
130
+ cmp, rv = *req
131
+ CMP_PROCS[cmp || '='].call(version, rv)
132
+ else
133
+ req.satisfied_by?(version)
134
+ end
135
+ end
136
+ negative ? !result : result
137
+ end
138
+
139
+ # Either modify the current requirement (if it's already an or operation)
140
+ # or create a new requirement
141
+ def |(other)
142
+ operation(:|, other)
143
+ end
144
+
145
+ # Either modify the current requirement (if it's already an and operation)
146
+ # or create a new requirement
147
+ def &(other)
148
+ operation(:&, other)
149
+ end
150
+
151
+ # return the parsed expression
152
+ def to_s
153
+ str = requirements.map(&:to_s).join(" " + @op.to_s + " ").to_s
154
+ str = "( " + str + " )" if negative || requirements.size > 1
155
+ str = "!" + str if negative
156
+ str
157
+ end
158
+
159
+ attr_accessor :negative
160
+ protected
161
+ attr_reader :requirements, :op
162
+ def operation(op, other)
163
+ @op ||= op
164
+ if negative == other.negative && @op == op && other.requirements.size == 1
165
+ @requirements << other.requirements.first
166
+ self
167
+ else
168
+ self.class.new(op, self, other)
169
+ end
170
+ end
171
+ end # VersionRequirement
172
+ end