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,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
@@ -0,0 +1,21 @@
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/packaging/zip'
18
+ require 'buildr/packaging/tar'
19
+ require 'buildr/packaging/artifact'
20
+ require 'buildr/packaging/package'
21
+ require 'buildr/packaging/gems'
@@ -0,0 +1,729 @@
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 'builder'
18
+ require 'buildr/core/project'
19
+ require 'buildr/core/transports'
20
+ require 'buildr/packaging/artifact_namespace'
21
+
22
+
23
+ module Buildr
24
+
25
+ desc 'Download all artifacts'
26
+ task 'artifacts'
27
+
28
+ # Mixin with a task to make it behave like an artifact. Implemented by the packaging tasks.
29
+ #
30
+ # An artifact has an identifier, group identifier, type, version number and
31
+ # optional classifier. All can be used to locate it in the local repository,
32
+ # download from or upload to a remote repository.
33
+ #
34
+ # The #to_spec and #to_hash methods allow it to be used everywhere an artifact is
35
+ # accepted.
36
+ module ActsAsArtifact
37
+
38
+ ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version]
39
+
40
+ class << self
41
+ private
42
+ def included(mod)
43
+ mod.extend self
44
+ end
45
+ end
46
+
47
+ # The artifact identifier.
48
+ attr_reader :id
49
+ # The group identifier.
50
+ attr_reader :group
51
+ # The file type. (Symbol)
52
+ attr_reader :type
53
+ # The version number.
54
+ attr_reader :version
55
+ # Optional artifact classifier.
56
+ attr_reader :classifier
57
+
58
+ def snapshot?
59
+ version =~ /-SNAPSHOT$/
60
+ end
61
+
62
+ # :call-seq:
63
+ # to_spec_hash => Hash
64
+ #
65
+ # Returns the artifact specification as a hash. For example:
66
+ # com.example:app:jar:1.2
67
+ # becomes:
68
+ # { :group=>'com.example',
69
+ # :id=>'app',
70
+ # :type=>:jar,
71
+ # :version=>'1.2' }
72
+ def to_spec_hash
73
+ base = { :group=>group, :id=>id, :type=>type, :version=>version }
74
+ classifier ? base.merge(:classifier=>classifier) : base
75
+ end
76
+ alias_method :to_hash, :to_spec_hash
77
+
78
+ # :call-seq:
79
+ # to_spec => String
80
+ #
81
+ # Returns the artifact specification, in the structure:
82
+ # <group>:<artifact>:<type>:<version>
83
+ # or
84
+ # <group>:<artifact>:<type>:<classifier><:version>
85
+ def to_spec
86
+ classifier ? "#{group}:#{id}:#{type}:#{classifier}:#{version}" : "#{group}:#{id}:#{type}:#{version}"
87
+ end
88
+
89
+ # :call-seq:
90
+ # pom => Artifact
91
+ #
92
+ # Convenience method that returns a POM artifact.
93
+ def pom
94
+ return self if type == :pom
95
+ Buildr.artifact(:group=>group, :id=>id, :version=>version, :type=>:pom)
96
+ end
97
+
98
+ # :call-seq:
99
+ # pom_xml => string
100
+ #
101
+ # Creates POM XML for this artifact.
102
+ def pom_xml
103
+ xml = Builder::XmlMarkup.new(:indent=>2)
104
+ xml.instruct!
105
+ xml.project do
106
+ xml.modelVersion '4.0.0'
107
+ xml.groupId group
108
+ xml.artifactId id
109
+ xml.version version
110
+ xml.classifier classifier if classifier
111
+ end
112
+ end
113
+
114
+ def install
115
+ pom.install if pom && pom != self
116
+ invoke
117
+ installed = Buildr.repositories.locate(self)
118
+ unless installed == name # If not already in local repository.
119
+ verbose(Buildr.application.options.trace || false) do
120
+ mkpath File.dirname(installed)
121
+ cp name, installed
122
+ end
123
+ puts "Installed #{installed}" if verbose
124
+ end
125
+ end
126
+
127
+ def uninstall
128
+ verbose(Buildr.application.options.trace || false) do
129
+ installed = Buildr.repositories.locate(self)
130
+ rm installed if File.exist?(installed)
131
+ pom.uninstall if pom && pom != self
132
+ end
133
+ end
134
+
135
+ # :call-seq:
136
+ # upload
137
+ # upload(url)
138
+ # upload(options)
139
+ #
140
+ # Uploads the artifact, its POM and digital signatures to remote server.
141
+ #
142
+ # In the first form, uses the upload options specified by repositories.release_to.
143
+ # In the second form, uses a URL that includes all the relevant information.
144
+ # In the third form, uses a hash with the options :url, :username, :password,
145
+ # and :permissions. All but :url are optional.
146
+ def upload(upload_to = nil)
147
+ # Where do we release to?
148
+ upload_to ||= Buildr.repositories.release_to
149
+ upload_to = { :url=>upload_to } unless Hash === upload_to
150
+ raise ArgumentError, 'Don\'t know where to upload, perhaps you forgot to set repositories.release_to' unless upload_to[:url]
151
+ invoke # Make sure we exist.
152
+
153
+ # Upload POM ahead of package, so we don't fail and find POM-less package (the horror!)
154
+ pom.upload(upload_to) if pom && pom != self
155
+
156
+ # Set the upload URI, including mandatory slash (we expect it to be the base directory).
157
+ # Username/password may be part of URI, or separate entities.
158
+ uri = URI.parse(upload_to[:url].clone)
159
+ uri.path = uri.path + '/' unless uri.path[-1] == '/'
160
+ uri.user = upload_to[:username] if upload_to[:username]
161
+ uri.password = upload_to[:password] if upload_to[:password]
162
+
163
+ # Upload artifact relative to base URL, need to create path before uploading.
164
+ puts "Deploying #{to_spec}" if verbose
165
+ path = group.gsub('.', '/') + "/#{id}/#{version}/#{File.basename(name)}"
166
+ URI.upload uri + path, name, :permissions=>upload_to[:permissions]
167
+ end
168
+
169
+ protected
170
+
171
+ # Apply specification to this artifact.
172
+ def apply_spec(spec)
173
+ spec = Artifact.to_hash(spec)
174
+ ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) }
175
+ self
176
+ end
177
+
178
+ def group_path
179
+ group.gsub('.', '/')
180
+ end
181
+
182
+ end
183
+
184
+
185
+ # A file task referencing an artifact in the local repository.
186
+ #
187
+ # This task includes all the artifact attributes (group, id, version, etc). It points
188
+ # to the artifact's path in the local repository. When invoked, it will download the
189
+ # artifact into the local repository if the artifact does not already exist.
190
+ #
191
+ # Note: You can enhance this task to create the artifact yourself, e.g. download it from
192
+ # a site that doesn't have a remote repository structure, copy it from a different disk, etc.
193
+ class Artifact < Rake::FileCreationTask
194
+
195
+ # The default artifact type.
196
+ DEFAULT_TYPE = :jar
197
+
198
+ include ActsAsArtifact
199
+
200
+ class << self
201
+
202
+ # :call-seq:
203
+ # lookup(spec) => Artifact
204
+ #
205
+ # Lookup a previously registered artifact task based on its specification (String or Hash).
206
+ def lookup(spec)
207
+ @artifacts ||= {}
208
+ @artifacts[to_spec(spec)]
209
+ end
210
+
211
+ # :call-seq:
212
+ # list => specs
213
+ #
214
+ # Returns an array of specs for all the registered artifacts. (Anything created from artifact, or package).
215
+ def list
216
+ @artifacts ||= {}
217
+ @artifacts.keys
218
+ end
219
+
220
+ # :call-seq:
221
+ # register(artifacts) => artifacts
222
+ #
223
+ # Register an artifact task(s) for later lookup (see #lookup).
224
+ def register(*tasks)
225
+ @artifacts ||= {}
226
+ fail 'You can only register an artifact task, one of the arguments is not a Task that responds to to_spec' unless
227
+ tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) }
228
+ tasks.each { |task| @artifacts[task.to_spec] = task }
229
+ tasks
230
+ end
231
+
232
+ # :call-seq:
233
+ # to_hash(spec_hash) => spec_hash
234
+ # to_hash(spec_string) => spec_hash
235
+ # to_hash(artifact) => spec_hash
236
+ #
237
+ # Turn a spec into a hash. This method accepts a String, Hash or any object that responds to
238
+ # the method to_spec. There are several reasons to use this method:
239
+ # * You can pass anything that could possibly be a spec, and get a hash.
240
+ # * It will check that the spec includes the group identifier, artifact
241
+ # identifier and version number and set the file type, if missing.
242
+ # * It will always return a new specs hash.
243
+ def to_hash(spec)
244
+ if spec.respond_to?(:to_spec)
245
+ to_hash spec.to_spec
246
+ elsif Hash === spec
247
+ rake_check_options spec, :id, :group, :type, :classifier, :version
248
+ # Sanitize the hash and check it's valid.
249
+ spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k].to_s if spec[k] ; h }
250
+ fail "Missing group identifier for #{spec.inspect}" unless spec[:group]
251
+ fail "Missing artifact identifier for #{spec.inspect}" unless spec[:id]
252
+ fail "Missing version for #{spec.inspect}" unless spec[:version]
253
+ spec[:type] = (spec[:type] || DEFAULT_TYPE).to_sym
254
+ spec
255
+ elsif String === spec
256
+ group, id, type, version, *rest = spec.split(':').map { |part| part.empty? ? nil : part }
257
+ unless rest.empty?
258
+ # Optional classifier comes before version.
259
+ classifier, version = version, rest.shift
260
+ fail "Expecting <group:id:type:version> or <group:id:type:classifier:version>, found <#{spec}>" unless rest.empty?
261
+ end
262
+ to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier
263
+ else
264
+ fail 'Expecting a String, Hash or object that responds to to_spec'
265
+ end
266
+ end
267
+
268
+ # :call-seq:
269
+ # to_spec(spec_hash) => spec_string
270
+ #
271
+ # Convert a hash back to a spec string. This method accepts
272
+ # a string, hash or any object that responds to to_spec.
273
+ def to_spec(hash)
274
+ hash = to_hash(hash) unless Hash === hash
275
+ version = ":#{hash[:version]}" if hash[:version]
276
+ classifier = ":#{hash[:classifier]}" if hash[:classifier]
277
+ "#{hash[:group]}:#{hash[:id]}:#{hash[:type] || DEFAULT_TYPE}#{classifier}#{version}"
278
+ end
279
+
280
+ # :call-seq:
281
+ # hash_to_file_name(spec_hash) => file_name
282
+ #
283
+ # Convert a hash spec to a file name.
284
+ def hash_to_file_name(hash)
285
+ version = "-#{hash[:version]}" if hash[:version]
286
+ classifier = "-#{hash[:classifier]}" if hash[:classifier]
287
+ "#{hash[:id]}#{version}#{classifier}.#{hash[:type] || DEFAULT_TYPE}"
288
+ end
289
+
290
+ end
291
+
292
+ def initialize(*args) #:nodoc:
293
+ super
294
+ enhance do |task|
295
+ # Default behavior: download the artifact from one of the remote repositories
296
+ # if the file does not exist. But this default behavior is counter productive
297
+ # if the artifact knows how to build itself (e.g. download from a different location),
298
+ # so don't perform it if the task found a different way to create the artifact.
299
+ task.enhance do
300
+ unless File.exist?(name)
301
+ puts "Downloading #{to_spec}" if verbose
302
+ download
303
+ pom.invoke rescue nil if pom && pom != self
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ # :call-seq:
310
+ # from(path) => self
311
+ #
312
+ # Use this when you want to install or upload an artifact from a given file, for example:
313
+ # test = artifact('group:id:jar:1.0').from('test.jar')
314
+ # install test
315
+ # See also Buildr#install and Buildr#deploy.
316
+ def from(path)
317
+ path = File.expand_path(path.to_s)
318
+ enhance [path] do
319
+ verbose false do
320
+ mkpath File.dirname(name)
321
+ pom.invoke unless type == :pom
322
+ cp path, name
323
+ puts "Installed #{path} as #{to_spec}" if verbose
324
+ end
325
+ end
326
+ unless type == :pom
327
+ pom.enhance do
328
+ verbose false do
329
+ mkpath File.dirname(pom.name)
330
+ File.open(pom.name, 'w') { |file| file.write pom.pom_xml }
331
+ end
332
+ end
333
+ end
334
+ self
335
+ end
336
+
337
+ protected
338
+
339
+ # :call-seq:
340
+ # download
341
+ #
342
+ # Downloads an artifact from one of the remote repositories, and stores it in the local
343
+ # repository. Accepts a String or Hash artifact specification, and returns a path to the
344
+ # artifact in the local repository. Raises an exception if the artifact is not found.
345
+ #
346
+ # This method attempts to download the artifact from each repository in the order in
347
+ # which they are returned from #remote, until successful. It always downloads the POM first.
348
+ def download
349
+ puts "Downloading #{to_spec}" if Buildr.application.options.trace
350
+ remote = Buildr.repositories.remote.map { |repo_url| URI === repo_url ? repo_url : URI.parse(repo_url) }
351
+ remote = remote.each { |repo_url| repo_url.path += '/' unless repo_url.path[-1] == '/' }
352
+ fail 'No remote repositories defined!' if remote.empty?
353
+ exact_success = remote.find do |repo_url|
354
+ begin
355
+ path = "#{group_path}/#{id}/#{version}/#{File.basename(name)}"
356
+ URI.download repo_url + path, name
357
+ true
358
+ rescue URI::NotFoundError
359
+ false
360
+ rescue Exception=>error
361
+ puts error if verbose
362
+ puts error.backtrace.join("\n") if Buildr.application.options.trace
363
+ false
364
+ end
365
+ end
366
+
367
+ if exact_success
368
+ return
369
+ elsif snapshot?
370
+ download_m2_snapshot(remote)
371
+ else
372
+ fail_download(remote)
373
+ end
374
+ end
375
+
376
+ def download_m2_snapshot(remote_uris)
377
+ remote_uris.find do |repo_url|
378
+ snapshot_url = current_snapshot_repo_url(repo_url)
379
+ if snapshot_url
380
+ begin
381
+ URI.download snapshot_url, name
382
+ rescue URI::NotFoundError
383
+ false
384
+ end
385
+ else
386
+ false
387
+ end
388
+ end or fail_download(remote_uris)
389
+ end
390
+
391
+ def current_snapshot_repo_url(repo_url)
392
+ begin
393
+ metadata_path = "#{group_path}/#{id}/#{version}/maven-metadata.xml"
394
+ metadata_xml = StringIO.new
395
+ URI.download repo_url + metadata_path, metadata_xml
396
+ metadata = REXML::Document.new(metadata_xml.string).root
397
+ timestamp = REXML::XPath.first(metadata, '//timestamp').text
398
+ build_number = REXML::XPath.first(metadata, '//buildNumber').text
399
+ snapshot_of = version[0, version.size - 9]
400
+ repo_url + "#{group_path}/#{id}/#{version}/#{id}-#{snapshot_of}-#{timestamp}-#{build_number}.#{type}"
401
+ rescue URI::NotFoundError
402
+ nil
403
+ end
404
+ end
405
+
406
+ def fail_download(remote_uris)
407
+ fail "Failed to download #{to_spec}, tried the following repositories:\n#{remote_uris.join("\n")}"
408
+ end
409
+ end
410
+
411
+
412
+ # Holds the path to the local repository, URLs for remote repositories, and settings for release server.
413
+ #
414
+ # You can access this object from the #repositories method. For example:
415
+ # puts repositories.local
416
+ # repositories.remote << 'http://example.com/repo'
417
+ # repositories.release_to = 'sftp://example.com/var/www/public/repo'
418
+ class Repositories
419
+ include Singleton
420
+
421
+ # :call-seq:
422
+ # local => path
423
+ #
424
+ # Returns the path to the local repository.
425
+ #
426
+ # The default path is .m2/repository relative to the home directory.
427
+ # You can set this using the M2_REPO environment variable or the repositories/local
428
+ # value in your settings.yaml file.
429
+ def local
430
+ @local ||= File.expand_path(ENV['M2_REPO'] || ENV['local_repo'] ||
431
+ (Buildr.settings.user['repositories'] && Buildr.settings.user['repositories']['local']) ||
432
+ File.join(ENV['HOME'], '.m2/repository'))
433
+ end
434
+
435
+ # :call-seq:
436
+ # local = path
437
+ #
438
+ # Sets the path to the local repository.
439
+ #
440
+ # The best place to set the local repository path is from a buildr.rb file
441
+ # located in your home directory. That way all your projects will share the same
442
+ # path, without affecting other developers collaborating on these projects.
443
+ def local=(dir)
444
+ @local = dir ? File.expand_path(dir) : nil
445
+ end
446
+
447
+ # :call-seq:
448
+ # locate(spec) => path
449
+ #
450
+ # Locates an artifact in the local repository based on its specification, and returns
451
+ # a file path.
452
+ #
453
+ # For example:
454
+ # locate :group=>'log4j', :id=>'log4j', :version=>'1.1'
455
+ # => ~/.m2/repository/log4j/log4j/1.1/log4j-1.1.jar
456
+ def locate(spec)
457
+ spec = Artifact.to_hash(spec)
458
+ File.join(local, spec[:group].split('.'), spec[:id], spec[:version], Artifact.hash_to_file_name(spec))
459
+ end
460
+
461
+ # :call-seq:
462
+ # remote => Array
463
+ #
464
+ # Returns an array of all the remote repository URLs.
465
+ #
466
+ # When downloading artifacts, repositories are accessed in the order in which they appear here.
467
+ # The best way is to add repositories individually, for example:
468
+ # repositories.remote << 'http://example.com/repo'
469
+ #
470
+ # You can also specify remote repositories in the settings.yaml (per user) and build.yaml (per build)
471
+ # files. Both sets of URLs are loaded by default into this array, URLs from the personal setting
472
+ # showing first.
473
+ #
474
+ # For example:
475
+ # repositories:
476
+ # remote:
477
+ # - http://example.com/repo
478
+ # - http://elsewhere.com/repo
479
+ def remote
480
+ unless @remote
481
+ @remote = [Buildr.settings.user, Buildr.settings.build].inject([]) { |repos, hash|
482
+ repos | Array(hash['repositories'] && hash['repositories']['remote'])
483
+ }
484
+ end
485
+ @remote
486
+ end
487
+
488
+ # :call-seq:
489
+ # remote = Array
490
+ # remote = url
491
+ # remote = nil
492
+ #
493
+ # With a String argument, clears the array and set it to that single URL.
494
+ #
495
+ # With an Array argument, clears the array and set it to these specific URLs.
496
+ #
497
+ # With nil, clears the array.
498
+ def remote=(urls)
499
+ case urls
500
+ when nil then @remote = nil
501
+ when Array then @remote = urls.dup
502
+ else @remote = [urls.to_s]
503
+ end
504
+ end
505
+
506
+ # :call-seq:
507
+ # release_to = url
508
+ # release_to = hash
509
+ #
510
+ # Specifies the release server. Accepts a Hash with different repository settings
511
+ # (e.g. url, username, password), or a String to only set the repository URL.
512
+ #
513
+ # Besides the URL, all other settings depend on the transport protocol in use.
514
+ #
515
+ # For example:
516
+ # repositories.release_to = 'sftp://john:secret@example.com/var/www/repo/'
517
+ #
518
+ # repositories.release_to = { :url=>'sftp://example.com/var/www/repo/',
519
+ # :username='john', :password=>'secret' }
520
+ # Or in the settings.yaml file:
521
+ # repositories:
522
+ # release_to: sftp://john:secret@example.com/var/www/repo/
523
+ #
524
+ # repositories:
525
+ # release_to:
526
+ # url: sftp://example.com/var/www/repo/
527
+ # username: john
528
+ # password: secret
529
+ def release_to=(options)
530
+ options = { :url=>options } unless Hash === options
531
+ @release_to = options
532
+ end
533
+
534
+ # :call-seq:
535
+ # release_to => hash
536
+ #
537
+ # Returns the current release server setting as a Hash. This is a more convenient way to
538
+ # configure the settings, as it allows you to specify the settings progressively.
539
+ #
540
+ # For example, the Buildfile will contain the repository URL used by all developers:
541
+ # repositories.release_to[:url] ||= 'sftp://example.com/var/www/repo'
542
+ # Your private buildr.rb will contain your credentials:
543
+ # repositories.release_to[:username] = 'john'
544
+ # repositories.release_to[:password] = 'secret'
545
+ def release_to
546
+ unless @release_to
547
+ value = Buildr.settings.user['repositories'] && Buildr.settings.user['repositories']['release_to']
548
+ @release_to = Hash === value ? value.inject({}) { |hash, (key, value)| hash.update(key.to_sym=>value) } : { :url=>Array(value).first }
549
+ end
550
+ @release_to
551
+ end
552
+
553
+ end
554
+
555
+ # :call-seq:
556
+ # repositories => Repositories
557
+ #
558
+ # Returns an object you can use for setting the local repository path, remote repositories
559
+ # URL and release server settings.
560
+ #
561
+ # See Repositories.
562
+ def repositories
563
+ Repositories.instance
564
+ end
565
+
566
+ # :call-seq:
567
+ # artifact(spec) => Artifact
568
+ # artifact(spec) { |task| ... } => Artifact
569
+ #
570
+ # Creates a file task to download and install the specified artifact in the local repository.
571
+ #
572
+ # You can use a String or a Hash for the artifact specification. The file task will point at
573
+ # the artifact's path inside the local repository. You can then use this tasks as a prerequisite
574
+ # for other tasks.
575
+ #
576
+ # This task will download and install the artifact only once. In fact, it will download and
577
+ # install the artifact if the artifact does not already exist. You can enhance it if you have
578
+ # a different way of creating the artifact in the local repository. See Artifact for more details.
579
+ #
580
+ # For example, to specify an artifact:
581
+ # artifact('log4j:log4j:jar:1.1')
582
+ #
583
+ # To use the artifact in a task:
584
+ # compile.with artifact('log4j:log4j:jar:1.1')
585
+ #
586
+ # To specify an artifact and the means for creating it:
587
+ # download(artifact('dojo:dojo-widget:zip:2.0')=>
588
+ # 'http://download.dojotoolkit.org/release-2.0/dojo-2.0-widget.zip')
589
+ def artifact(spec, &block) #:yields:task
590
+ spec = artifact_ns.fetch(spec) if spec.kind_of?(Symbol)
591
+ spec = Artifact.to_hash(spec)
592
+ unless task = Artifact.lookup(spec)
593
+ task = Artifact.define_task(repositories.locate(spec))
594
+ task.send :apply_spec, spec
595
+ Rake::Task['rake:artifacts'].enhance [task]
596
+ Artifact.register(task)
597
+ end
598
+ task.enhance &block
599
+ end
600
+
601
+ # :call-seq:
602
+ # artifacts(*spec) => artifacts
603
+ #
604
+ # Handles multiple artifacts at a time. This method is the plural equivalent of
605
+ # #artifacts, but can do more things.
606
+ #
607
+ # Returns an array of artifacts built using the supplied
608
+ # specifications, each of which can be:
609
+ # * An artifact specification (String or Hash). Returns the appropriate Artifact task.
610
+ # * An artifact of any other task. Returns the task as is.
611
+ # * A project. Returns all artifacts created (packaged) by that project.
612
+ # * A string. Returns that string, assumed to be a file name.
613
+ # * An array of artifacts or a Struct.
614
+ # * A symbol. Returns the named artifact from the current ArtifactNamespace
615
+ #
616
+ # For example, handling a collection of artifacts:
617
+ # xml = [ xerces, xalan, jaxp ]
618
+ # ws = [ axis, jax-ws, jaxb ]
619
+ # db = [ jpa, mysql, sqltools ]
620
+ # artifacts(xml, ws, db)
621
+ #
622
+ # Using artifacts created by a project:
623
+ # artifacts project('my-app') # All packages
624
+ # artifacts project('my-app').package(:war) # Only the WAR
625
+ def artifacts(*specs, &block)
626
+ specs.flatten.inject([]) do |set, spec|
627
+ case spec
628
+ when ArtifactNamespace
629
+ set |= spec.artifacts
630
+ when Symbol, Hash
631
+ set |= [artifact(spec)]
632
+ when /([^:]+:){2,4}/ # A spec as opposed to a file name.
633
+ set |= [artifact(spec)]
634
+ when String # Must always expand path.
635
+ set |= [File.expand_path(spec)]
636
+ when Project
637
+ set |= artifacts(spec.packages)
638
+ when Rake::Task
639
+ set |= [spec]
640
+ when Struct
641
+ set |= artifacts(spec.values)
642
+ else
643
+ fail "Invalid artifact specification in #{specs.inspect}"
644
+ end
645
+ end
646
+ end
647
+
648
+ def transitive(*specs)
649
+ specs.flatten.inject([]) do |set, spec|
650
+ case spec
651
+ when /([^:]+:){2,4}/ # A spec as opposed to a file name.
652
+ artifact = artifact(spec)
653
+ set |= [artifact] unless artifact.type == :pom
654
+ set |= POM.load(artifact.pom).dependencies.map { |spec| artifact(spec) }
655
+ when Hash
656
+ set |= [transitive(spec)]
657
+ when String # Must always expand path.
658
+ set |= transitive(file(File.expand_path(spec)))
659
+ when Project
660
+ set |= transitive(spec.packages)
661
+ when Rake::Task
662
+ set |= spec.respond_to?(:to_spec) ? transitive(spec.to_spec) : [spec]
663
+ when Struct
664
+ set |= transitive(spec.values)
665
+ else
666
+ fail "Invalid artifact specification in: #{specs.to_s}"
667
+ end
668
+ end
669
+ end
670
+
671
+ # :call-seq:
672
+ # group(ids, :under=>group_name, :version=>number) => artifacts
673
+ #
674
+ # Convenience method for defining multiple artifacts that belong to the same group and version.
675
+ # Accepts multiple artifact identifiers follows by two hash values:
676
+ # * :under -- The group identifier
677
+ # * :version -- The version number
678
+ #
679
+ # For example:
680
+ # group 'xbean', 'xbean_xpath', 'xmlpublic', :under=>'xmlbeans', :version=>'2.1.0'
681
+ # Or:
682
+ # group %w{xbean xbean_xpath xmlpublic}, :under=>'xmlbeans', :version=>'2.1.0'
683
+ def group(*args)
684
+ hash = args.pop
685
+ args.flatten.map { |id| artifact :group=>hash[:under], :version=>hash[:version], :id=>id }
686
+ end
687
+
688
+ # :call-seq:
689
+ # install(artifacts)
690
+ #
691
+ # Installs the specified artifacts in the local repository as part of the install task.
692
+ #
693
+ # You can use this to install various files in the local repository, for example:
694
+ # install artifact('group:id:jar:1.0').from('some_jar.jar')
695
+ # $ buildr install
696
+ def install(*args, &block)
697
+ artifacts = artifacts(args)
698
+ raise ArgumentError, 'This method can only install artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) }
699
+ all = (artifacts + artifacts.map { |artifact| artifact.pom }).uniq
700
+ task('install').tap do |task|
701
+ task.enhance all, &block
702
+ task 'uninstall' do
703
+ verbose false do
704
+ all.map(&:to_s ).each { |file| rm file if File.exist?(file) }
705
+ end
706
+ end
707
+ end
708
+ end
709
+
710
+ # :call-seq:
711
+ # upload(artifacts)
712
+ #
713
+ # Uploads the specified artifacts to the release server as part of the upload task.
714
+ #
715
+ # You can use this to upload various files to the release server, for example:
716
+ # upload artifact('group:id:jar:1.0').from('some_jar.jar')
717
+ # $ buildr upload
718
+ def upload(*args, &block)
719
+ artifacts = artifacts(args)
720
+ raise ArgumentError, 'This method can only upload artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) }
721
+ task('upload').tap do |task|
722
+ task.enhance &block if block
723
+ task.enhance artifacts do
724
+ artifacts.each { |artifact| artifact.upload }
725
+ end
726
+ end
727
+ end
728
+
729
+ end