realityforge-buildr 1.5.9

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 (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE +176 -0
  4. data/NOTICE +26 -0
  5. data/README.md +3 -0
  6. data/Rakefile +50 -0
  7. data/addon/buildr/checkstyle-report.xsl +104 -0
  8. data/addon/buildr/checkstyle.rb +254 -0
  9. data/addon/buildr/git_auto_version.rb +36 -0
  10. data/addon/buildr/gpg.rb +90 -0
  11. data/addon/buildr/gwt.rb +413 -0
  12. data/addon/buildr/jacoco.rb +161 -0
  13. data/addon/buildr/pmd.rb +185 -0
  14. data/addon/buildr/single_intermediate_layout.rb +71 -0
  15. data/addon/buildr/spotbugs.rb +265 -0
  16. data/addon/buildr/top_level_generate_dir.rb +37 -0
  17. data/addon/buildr/wsgen.rb +192 -0
  18. data/bin/buildr +20 -0
  19. data/buildr.gemspec +61 -0
  20. data/lib/buildr.rb +86 -0
  21. data/lib/buildr/core/application.rb +705 -0
  22. data/lib/buildr/core/assets.rb +96 -0
  23. data/lib/buildr/core/build.rb +587 -0
  24. data/lib/buildr/core/common.rb +167 -0
  25. data/lib/buildr/core/compile.rb +599 -0
  26. data/lib/buildr/core/console.rb +124 -0
  27. data/lib/buildr/core/doc.rb +275 -0
  28. data/lib/buildr/core/environment.rb +128 -0
  29. data/lib/buildr/core/filter.rb +405 -0
  30. data/lib/buildr/core/help.rb +114 -0
  31. data/lib/buildr/core/progressbar.rb +161 -0
  32. data/lib/buildr/core/project.rb +994 -0
  33. data/lib/buildr/core/test.rb +776 -0
  34. data/lib/buildr/core/transports.rb +456 -0
  35. data/lib/buildr/core/util.rb +77 -0
  36. data/lib/buildr/ide/idea.rb +1664 -0
  37. data/lib/buildr/java/commands.rb +230 -0
  38. data/lib/buildr/java/compiler.rb +85 -0
  39. data/lib/buildr/java/custom_pom.rb +300 -0
  40. data/lib/buildr/java/doc.rb +62 -0
  41. data/lib/buildr/java/packaging.rb +393 -0
  42. data/lib/buildr/java/pom.rb +191 -0
  43. data/lib/buildr/java/test_result.rb +54 -0
  44. data/lib/buildr/java/tests.rb +111 -0
  45. data/lib/buildr/packaging/archive.rb +586 -0
  46. data/lib/buildr/packaging/artifact.rb +1113 -0
  47. data/lib/buildr/packaging/artifact_namespace.rb +1010 -0
  48. data/lib/buildr/packaging/artifact_search.rb +138 -0
  49. data/lib/buildr/packaging/package.rb +237 -0
  50. data/lib/buildr/packaging/version_requirement.rb +189 -0
  51. data/lib/buildr/packaging/zip.rb +189 -0
  52. data/lib/buildr/packaging/ziptask.rb +387 -0
  53. data/lib/buildr/version.rb +18 -0
  54. data/rakelib/release.rake +99 -0
  55. data/spec/addon/checkstyle_spec.rb +58 -0
  56. data/spec/core/application_spec.rb +576 -0
  57. data/spec/core/build_spec.rb +922 -0
  58. data/spec/core/common_spec.rb +670 -0
  59. data/spec/core/compile_spec.rb +656 -0
  60. data/spec/core/console_spec.rb +65 -0
  61. data/spec/core/doc_spec.rb +194 -0
  62. data/spec/core/extension_spec.rb +200 -0
  63. data/spec/core/project_spec.rb +736 -0
  64. data/spec/core/test_spec.rb +1131 -0
  65. data/spec/core/transport_spec.rb +452 -0
  66. data/spec/core/util_spec.rb +154 -0
  67. data/spec/ide/idea_spec.rb +1952 -0
  68. data/spec/java/commands_spec.rb +79 -0
  69. data/spec/java/compiler_spec.rb +274 -0
  70. data/spec/java/custom_pom_spec.rb +165 -0
  71. data/spec/java/doc_spec.rb +55 -0
  72. data/spec/java/packaging_spec.rb +786 -0
  73. data/spec/java/pom_spec.rb +162 -0
  74. data/spec/java/test_coverage_helper.rb +257 -0
  75. data/spec/java/tests_spec.rb +224 -0
  76. data/spec/packaging/archive_spec.rb +686 -0
  77. data/spec/packaging/artifact_namespace_spec.rb +757 -0
  78. data/spec/packaging/artifact_spec.rb +1351 -0
  79. data/spec/packaging/packaging_helper.rb +63 -0
  80. data/spec/packaging/packaging_spec.rb +690 -0
  81. data/spec/sandbox.rb +166 -0
  82. data/spec/spec_helpers.rb +420 -0
  83. data/spec/version_requirement_spec.rb +145 -0
  84. data/spec/xpath_matchers.rb +123 -0
  85. metadata +295 -0
@@ -0,0 +1,138 @@
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
+ autoload :Hpricot, 'hpricot'
17
+
18
+ module Buildr #:nodoc:
19
+
20
+ # Search best artifact version from remote repositories
21
+ module ArtifactSearch
22
+ extend self
23
+
24
+ def include(method = nil)
25
+ (@includes ||= []).tap { push method if method }
26
+ end
27
+
28
+ def exclude(method = nil)
29
+ (@excludes ||= []).tap { push method if method }
30
+ end
31
+
32
+ # TODO: return the url for best matching repo
33
+ def best_version(spec, *methods)
34
+ spec = Artifact.to_hash(spec)
35
+ spec[:version] = requirement = VersionRequirement.create(spec[:version])
36
+ select = lambda do |candidates|
37
+ candidates.find { |candidate| requirement.satisfied_by?(candidate) }
38
+ end
39
+ result = nil
40
+ methods = search_methods if methods.empty?
41
+ if requirement.composed?
42
+ until result || methods.empty?
43
+ method = methods.shift
44
+ type = method.keys.first
45
+ from = method[type]
46
+ if (include.empty? || !(include & [:all, type, from]).empty?) &&
47
+ (exclude & [:all, type, from]).empty?
48
+ if from.respond_to?(:call)
49
+ versions = from.call(spec.dup)
50
+ else
51
+ versions = send("#{type}_versions", spec.dup, *from)
52
+ end
53
+ result = select[versions]
54
+ end
55
+ end
56
+ end
57
+ result ||= requirement.default
58
+ raise "Could not find #{Artifact.to_spec(spec)}" +
59
+ "\n You may need to use an specific version instead of a requirement" unless result
60
+ spec.merge :version => result
61
+ end
62
+
63
+ def requirement?(spec)
64
+ VersionRequirement.requirement?(spec[:version])
65
+ end
66
+
67
+ private
68
+ def search_methods
69
+ [].tap do
70
+ push :runtime => [Artifact.list]
71
+ push :local => Buildr.repositories.local
72
+ Buildr.repositories.remote.each { |remote| push :remote => remote }
73
+ push :mvnrepository => []
74
+ end
75
+ end
76
+
77
+ def depend_version(spec)
78
+ spec[:version][/[\w\.]+/]
79
+ end
80
+
81
+ def runtime_versions(spec, artifacts)
82
+ spec_classif = spec.values_at(:group, :id, :type)
83
+ artifacts.inject([]) do |in_memory, str|
84
+ candidate = Artifact.to_hash(str)
85
+ if spec_classif == candidate.values_at(:group, :id, :type)
86
+ in_memory << candidate[:version]
87
+ end
88
+ in_memory
89
+ end
90
+ end
91
+
92
+ def local_versions(spec, repo)
93
+ path = (spec[:group].split(/\./) + [spec[:id]]).flatten.join('/')
94
+ Dir[File.expand_path(path + "/*", repo)].map { |d| d.pathmap("%f") }.sort.reverse
95
+ end
96
+
97
+ def remote_versions(art, base, from = :metadata, fallback = true)
98
+ path = (art[:group].split(/\./) + [art[:id]]).flatten.join('/')
99
+ base ||= "https://repo1.maven.org/maven2"
100
+ uris = {:metadata => "#{base}/#{path}/maven-metadata.xml"}
101
+ uris[:listing] = "#{base}/#{path}/" if base =~ /^https?:/
102
+ xml = nil
103
+ until xml || uris.empty?
104
+ begin
105
+ xml = URI.read(uris.delete(from))
106
+ rescue URI::NotFoundError => e
107
+ from = fallback ? uris.keys.first : nil
108
+ end
109
+ end
110
+ return [] unless xml
111
+ doc = Hpricot(xml)
112
+ case from
113
+ when :metadata then
114
+ doc.search("versions/version").map(&:innerHTML).reverse
115
+ when :listing then
116
+ doc.search("a[@href]").inject([]) { |vers, a|
117
+ vers << a.innerHTML.chop if a.innerHTML[-1..-1] == '/'
118
+ vers
119
+ }.sort.reverse
120
+ else
121
+ fail "Don't know how to parse #{from}: \n#{xml.inspect}"
122
+ end
123
+ end
124
+
125
+ def mvnrepository_versions(art)
126
+ uri = "http://www.mvnrepository.com/artifact/#{art[:group]}/#{art[:id]}"
127
+ xml = begin
128
+ URI.read(uri)
129
+ rescue URI::NotFoundError => e
130
+ puts e.class, e
131
+ return []
132
+ end
133
+ doc = Hpricot(xml)
134
+ doc.search("table.grid/tr/td[1]/a").map(&:innerHTML)
135
+ end
136
+
137
+ end # ArtifactSearch
138
+ end
@@ -0,0 +1,237 @@
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
+ module Buildr #:nodoc:
17
+ # Methods added to Project to support packaging and tasks for packaging,
18
+ # installing and uploading packages.
19
+ module Package
20
+
21
+ include Extension
22
+
23
+ first_time do
24
+ desc 'Create packages'
25
+ Project.local_task('package'=>'build') { |name| "Packaging #{name}" }
26
+ desc 'Install packages created by the project'
27
+ Project.local_task('install'=>'package') { |name| "Installing packages from #{name}" }
28
+ desc 'Remove previously installed packages'
29
+ Project.local_task('uninstall') { |name| "Uninstalling packages from #{name}" }
30
+ desc 'Upload packages created by the project'
31
+ Project.local_task('upload'=>'package') { |name| "Deploying packages from #{name}" }
32
+ # Anything that comes after local packaging (install, upload) executes the integration tests,
33
+ # which do not conflict with integration invoking the project's own packaging (package=>
34
+ # integration=>foo:package is not circular, just confusing to debug.)
35
+ task 'package' do
36
+ task('integration').invoke if Buildr.options.test && Buildr.application.original_dir == Dir.pwd
37
+ end
38
+ end
39
+
40
+ before_define(:package => :build) do |project|
41
+ [ :package, :install, :uninstall, :upload ].each { |name| project.recursive_task name }
42
+ # Need to run build before package, since package is often used as a dependency by tasks that
43
+ # expect build to happen.
44
+ project.task('package'=>project.task('build'))
45
+ project.group ||= project.parent && project.parent.group || project.name
46
+ project.version ||= project.parent && project.parent.version
47
+ end
48
+
49
+ after_define(:package)
50
+
51
+ # The project's identifier. Same as the project name, with colons replaced by dashes.
52
+ # The ID for project foo:bar is foo-bar.
53
+ def id
54
+ name.gsub(':', '-')
55
+ end
56
+
57
+ # Group used for packaging. Inherited from parent project. Defaults to the top-level project name.
58
+ attr_accessor :group
59
+
60
+ # Version used for packaging. Inherited from parent project.
61
+ attr_accessor :version
62
+
63
+ # :call-seq:
64
+ # package(type, spec?) => task
65
+ #
66
+ # Defines and returns a package created by this project.
67
+ #
68
+ # The first argument declares the package type. For example, :jar to create a JAR file.
69
+ # The package is an artifact that takes its artifact specification from the project.
70
+ # You can override the artifact specification by passing various options in the second
71
+ # argument, for example:
72
+ # package(:zip, :classifier=>'sources')
73
+ #
74
+ # Packages that are ZIP files provides various ways to include additional files, directories,
75
+ # and even merge ZIPs together. Have a look at ZipTask for more information. In case you're
76
+ # wondering, JAR and WAR packages are ZIP files.
77
+ #
78
+ # You can also enhance a JAR package using the ZipTask#with method that accepts the following options:
79
+ # * :manifest -- Specifies how to create the MANIFEST.MF. By default, uses the project's
80
+ # #manifest property.
81
+ # * :meta_inf -- Specifies files to be included in the META-INF directory. By default,
82
+ # uses the project's #meta-inf property.
83
+ #
84
+ # The WAR package supports the same options and adds a few more:
85
+ # * :classes -- Directories of class files to include in WEB-INF/classes. Includes the compile
86
+ # target directory by default.
87
+ # * :libs -- Artifacts and files to include in WEB-INF/libs. Includes the compile classpath
88
+ # dependencies by default.
89
+ #
90
+ # For example:
91
+ # define 'project' do
92
+ # define 'beans' do
93
+ # package :jar
94
+ # end
95
+ # define 'webapp' do
96
+ # compile.with project('beans')
97
+ # package(:war).with :libs=>MYSQL_JDBC
98
+ # end
99
+ # package(:zip, :classifier=>'sources').include path_to('.')
100
+ # end
101
+ #
102
+ # Two other packaging types are:
103
+ # * package :sources -- Creates a JAR file with the source code and classifier 'sources', for use by IDEs.
104
+ # * package :javadoc -- Creates a ZIP file with the Javadocs and classifier 'javadoc'. You can use the
105
+ # javadoc method to further customize it.
106
+ #
107
+ # A package is also an artifact. The following tasks operate on packages created by the project:
108
+ # buildr upload # Upload packages created by the project
109
+ # buildr install # Install packages created by the project
110
+ # buildr package # Create packages
111
+ # buildr uninstall # Remove previously installed packages
112
+ #
113
+ # If you want to add additional packaging types, implement a method with the name package_as_[type]
114
+ # that accepts a file name and returns an appropriate Rake task. For example:
115
+ # def package_as_zip(file_name) #:nodoc:
116
+ # ZipTask.define_task(file_name)
117
+ # end
118
+ #
119
+ # The file name is determined from the specification passed to the package method, however, some
120
+ # packagers need to override this. For example, package(:sources) produces a file with the extension
121
+ # 'jar' and the classifier 'sources'. If you need to overwrite the default implementation, you should
122
+ # also include a method named package_as_[type]_spec. For example:
123
+ # def package_as_sources_spec(spec) #:nodoc:
124
+ # # Change the source distribution to .zip extension
125
+ # spec.merge({ :type=>:zip, :classifier=>'sources' })
126
+ # end
127
+ def package(*args)
128
+ spec = Hash === args.last ? args.pop.dup : {}
129
+ no_options = spec.empty? # since spec is mutated
130
+ if spec[:file]
131
+ rake_check_options spec, :file, :type
132
+ spec[:type] = args.shift || spec[:type] || spec[:file].split('.').last.to_sym
133
+ file_name = spec[:file]
134
+ else
135
+ rake_check_options spec, *ActsAsArtifact::ARTIFACT_ATTRIBUTES
136
+ spec[:id] ||= self.id
137
+ spec[:group] ||= self.group
138
+ spec[:version] ||= self.version
139
+ spec[:type] = args.shift || spec[:type] || compile.packaging || :zip
140
+ end
141
+
142
+ packager = method("package_as_#{spec[:type]}") rescue fail("Don't know how to create a package of type #{spec[:type]}")
143
+ if packager.arity == 1
144
+ unless file_name
145
+ spec = send("package_as_#{spec[:type]}_spec", spec) if respond_to?("package_as_#{spec[:type]}_spec")
146
+ file_name = path_to(:target, Artifact.hash_to_file_name(spec))
147
+ end
148
+ package = (no_options && packages.detect { |pkg| pkg.type == spec[:type] && (pkg.id.nil? || pkg.id == spec[:id]) &&
149
+ (pkg.respond_to?(:classifier) ? pkg.classifier : nil) == spec[:classifier]}) ||
150
+ packages.find { |pkg| pkg.name == file_name } ||
151
+ packager.call(file_name)
152
+ else
153
+ Buildr.application.deprecated "We changed the way package_as methods are implemented. See the package method documentation for more details."
154
+ file_name ||= path_to(:target, Artifact.hash_to_file_name(spec))
155
+ package = packager.call(file_name, spec)
156
+ end
157
+
158
+ # First time: prepare package for install, uninstall and upload tasks.
159
+ unless packages.include?(package)
160
+ # We already run build before package, but we also need to do so if the package itself is
161
+ # used as a dependency, before we get to run the package task.
162
+ task 'package'=>package
163
+ package.enhance [task('build')]
164
+ package.enhance { info "Packaging #{File.basename(file_name)}" }
165
+ if spec[:file]
166
+ class << package ; self ; end.send(:define_method, :type) { spec[:type] }
167
+ class << package ; self ; end.send(:define_method, :id) { nil }
168
+ else
169
+ # Make it an artifact using the specifications, and tell it how to create a POM.
170
+ package.extend ActsAsArtifact
171
+ package.buildr_project = self
172
+ package.send :apply_spec, spec.dup.delete_if{|key, _|!ActsAsArtifact::ARTIFACT_ATTRIBUTES.include?(key)}
173
+
174
+ # Create pom associated with package
175
+ class << package
176
+ def pom
177
+ unless @pom
178
+ pom_filename = self.name.sub(/\.[^.]+\z/, '.pom')
179
+ spec = {:group=>group, :id=>id, :version=>version, :type=>:pom}
180
+ @pom = Buildr.artifact(spec, pom_filename)
181
+ @pom.content Buildr::CustomPom.pom_xml(self.buildr_project, self)
182
+ end
183
+ @pom
184
+ end
185
+ end if package.classifier.nil?
186
+
187
+ file(Buildr.repositories.locate(package)=>package) { package.install }
188
+
189
+ # Add the package to the list of packages created by this project, and
190
+ # register it as an artifact. The later is required so if we look up the spec
191
+ # we find the package in the project's target directory, instead of finding it
192
+ # in the local repository and attempting to install it.
193
+ Artifact.register package
194
+ Artifact.register package.pom if package.classifier.nil?
195
+ end
196
+
197
+ task('install') { package.install if package.respond_to?(:install) }
198
+ task('uninstall') { package.uninstall if package.respond_to?(:uninstall) }
199
+ task('upload') { package.upload if package.respond_to?(:upload) }
200
+
201
+ packages << package
202
+ end
203
+ package
204
+ end
205
+
206
+ # :call-seq:
207
+ # packages => tasks
208
+ #
209
+ # Returns all packages created by this project. A project may create any number of packages.
210
+ #
211
+ # This method is used whenever you pass a project to Buildr#artifact or any other method
212
+ # that accepts artifact specifications and projects. You can use it to list all packages
213
+ # created by the project. If you want to return a specific package, it is often more
214
+ # convenient to call #package with the type.
215
+ def packages
216
+ @packages ||= []
217
+ end
218
+
219
+ def package_as_zip(file_name) #:nodoc:
220
+ ZipTask.define_task(file_name)
221
+ end
222
+
223
+ def package_as_sources_spec(spec) #:nodoc:
224
+ spec.merge(:type=>:jar, :classifier=>'sources')
225
+ end
226
+
227
+ def package_as_sources(file_name) #:nodoc:
228
+ ZipTask.define_task(file_name).tap do |zip|
229
+ zip.include :from=>[compile.sources, resources.target].compact
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ class Buildr::Project
236
+ include Buildr::Package
237
+ end
@@ -0,0 +1,189 @@
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
+ # Rubygems 1.3.6 removed the 'version' accessor so monkey-patch it back to
17
+ # circumvent version validation. This is needed because Gem::Version doesn't
18
+ # accept version specs with dashes.
19
+ unless Gem::Version.new("0").respond_to?(:version=)
20
+ class Gem::Version
21
+ def version=(version)
22
+ @version = version.to_s.strip
23
+
24
+ # re-prime @segments
25
+ @segments = nil
26
+ @canonical_segments = nil
27
+ segments
28
+ end
29
+ end
30
+ end
31
+
32
+ module Buildr #:nodoc:
33
+
34
+ #
35
+ # See ArtifactNamespace#need
36
+ class VersionRequirement
37
+
38
+ CMP_PROCS = Gem::Requirement::OPS.dup
39
+ CMP_REGEX = Gem::Requirement::OPS.keys.map { |k| Regexp.quote k }.join "|"
40
+ CMP_CHARS = CMP_PROCS.keys.join
41
+ BOOL_CHARS = '\|\&\!'
42
+ VER_CHARS = '\w\.\-'
43
+
44
+ class << self
45
+ # is +str+ a version string?
46
+ def version?(str)
47
+ /^\s*r?\d[#{VER_CHARS}]*\s*$/ === str
48
+ end
49
+
50
+ # is +str+ a version requirement?
51
+ def requirement?(str)
52
+ /[#{BOOL_CHARS}#{CMP_CHARS}\(\)]/ === str
53
+ end
54
+
55
+ # :call-seq:
56
+ # VersionRequirement.create(" >1 <2 !(1.5) ") -> requirement
57
+ #
58
+ # parse the +str+ requirement
59
+ def create(str)
60
+ instance_eval normalize(str)
61
+ rescue StandardError => e
62
+ raise "Failed to parse #{str.inspect} due to: #{e}"
63
+ end
64
+
65
+ private
66
+ def requirement(req)
67
+ unless req =~ /^\s*(#{CMP_REGEX})?\s*([#{VER_CHARS}]+)\s*$/
68
+ raise "Invalid requirement string: #{req}"
69
+ end
70
+ comparator, version = $1, $2
71
+ # dup required due to jruby 1.7.13 bug/feature that caches versions?
72
+ version = Gem::Version.new(0).dup.tap { |v| v.version = version }
73
+ VersionRequirement.new(nil, [$1, version])
74
+ end
75
+
76
+ def negate(vreq)
77
+ vreq.negative = !vreq.negative
78
+ vreq
79
+ end
80
+
81
+ def normalize(str)
82
+ str = str.strip
83
+ if str[/[^\s\(\)#{BOOL_CHARS + VER_CHARS + CMP_CHARS}]/]
84
+ raise "version string #{str.inspect} contains invalid characters"
85
+ end
86
+ str.gsub!(/\s+(and|&&)\s+/, ' & ')
87
+ str.gsub!(/\s+(or|\|\|)\s+/, ' | ')
88
+ str.gsub!(/(^|\s*)not\s+/, ' ! ')
89
+ pattern = /(#{CMP_REGEX})?\s*[#{VER_CHARS}]+/
90
+ left_pattern = /[#{VER_CHARS}\)]$/
91
+ right_pattern = /^(#{pattern}|\()/
92
+ str = str.split.inject([]) do |ary, i|
93
+ ary << '&' if ary.last =~ left_pattern && i =~ right_pattern
94
+ ary << i
95
+ end
96
+ str = str.join(' ')
97
+ str.gsub!(/!([^=])?/, ' negate \1')
98
+ str.gsub!(pattern) do |expr|
99
+ case expr.strip
100
+ when 'not', 'negate' then 'negate '
101
+ else 'requirement("' + expr + '")'
102
+ end
103
+ end
104
+ str.gsub!(/negate\s+\(/, 'negate(')
105
+ str
106
+ end
107
+ end
108
+
109
+ def initialize(op, *requirements) #:nodoc:
110
+ @op, @requirements = op, requirements
111
+ end
112
+
113
+ # Is this object a composed requirement?
114
+ # VersionRequirement.create('1').composed? -> false
115
+ # VersionRequirement.create('1 | 2').composed? -> true
116
+ # VersionRequirement.create('1 & 2').composed? -> true
117
+ def composed?
118
+ requirements.size > 1
119
+ end
120
+
121
+ # Return the last requirement on this object having an = operator.
122
+ def default
123
+ default = nil
124
+ requirements.reverse.find do |r|
125
+ if Array === r
126
+ if !negative && (r.first.nil? || r.first.include?('='))
127
+ default = r.last.to_s
128
+ end
129
+ else
130
+ default = r.default
131
+ end
132
+ end
133
+ default
134
+ end
135
+
136
+ # Test if this requirement can be satisfied by +version+
137
+ def satisfied_by?(version)
138
+ return false unless version
139
+ unless version.kind_of?(Gem::Version)
140
+ raise "Invalid version: #{version.inspect}" unless self.class.version?(version)
141
+ # dup required due to jruby 1.7.13 bug/feature that caches versions?
142
+ version = Gem::Version.new(0).dup.tap { |v| v.version = version.strip }
143
+ end
144
+ message = op == :| ? :any? : :all?
145
+ result = requirements.send message do |req|
146
+ if Array === req
147
+ cmp, rv = *req
148
+ CMP_PROCS[cmp || '='].call(version, rv)
149
+ else
150
+ req.satisfied_by?(version)
151
+ end
152
+ end
153
+ negative ? !result : result
154
+ end
155
+
156
+ # Either modify the current requirement (if it's already an or operation)
157
+ # or create a new requirement
158
+ def |(other)
159
+ operation(:|, other)
160
+ end
161
+
162
+ # Either modify the current requirement (if it's already an and operation)
163
+ # or create a new requirement
164
+ def &(other)
165
+ operation(:&, other)
166
+ end
167
+
168
+ # return the parsed expression
169
+ def to_s
170
+ str = requirements.map(&:to_s).join(" " + @op.to_s + " ").to_s
171
+ str = "( " + str + " )" if negative || requirements.size > 1
172
+ str = "!" + str if negative
173
+ str
174
+ end
175
+
176
+ attr_accessor :negative
177
+ protected
178
+ attr_reader :requirements, :op
179
+ def operation(op, other)
180
+ @op ||= op
181
+ if negative == other.negative && @op == op && other.requirements.size == 1
182
+ @requirements << other.requirements.first
183
+ self
184
+ else
185
+ self.class.new(op, self, other)
186
+ end
187
+ end
188
+ end # VersionRequirement
189
+ end