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,1010 @@
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
+
18
+ # An ArtifactNamespace is a hierarchical dictionary used to manage ArtifactRequirements.
19
+ # It can be used to have different artifact versions per project
20
+ # or to allow users to select a version for addons or modules.
21
+ #
22
+ # Namespaces are opened using the Buildr.artifact_ns method, most important methods are:
23
+ # [need] Used to create a requirement on the namespace.
24
+ # [use] Set the artifact version to use for a requirement.
25
+ # [values_at] Reference requirements by name.
26
+ # [each] Return each ArtifactRequirement in the namespace.
27
+ # The method_missing method for instances provides some syntactic sugar to these.
28
+ # See the following examples, and the methods for ArtifactRequirement.
29
+ #
30
+ # = Avoiding constant pollution on buildfile
31
+ #
32
+ # Each project has its own ArtifactNamespace inheriting the one from the
33
+ # parent project up to the root namespace.
34
+ #
35
+ # Consider the following snippet, as project grows, each subproject
36
+ # may need different artifact combinations and/or versions. Assigning
37
+ # artifact specifications to constants can make it painful to maintain
38
+ # their references even if using structs/hashes.
39
+ #
40
+ # -- buildfile --
41
+ # SPRING = 'org.springframework:spring:jar:2.5'
42
+ # SPRING_OLD = 'org.springframework:spring:jar:1.0'
43
+ # LOGGING = ['comons-logging:commons-logging:jar:1.1.1',
44
+ # 'log4j:log4j:jar:1.2.15']
45
+ # WL_LOGGING = artifact('bea:wlcommons-logging:jar:8.1').from('path/to/wlcommons-logging.jar')
46
+ # LOGGING_WEBLOGIC = ['comons-logging:commons-logging:jar:1.1.1',
47
+ # WL_LOGGING]
48
+ # COMMONS = struct :collections => 'commons-collection:commons-collection:jar:3.1',
49
+ # :net => 'commons-net:commons-net:jar:1.4.0'
50
+ #
51
+ # define 'example1' do
52
+ # define 'one' do
53
+ # compile.with SPRING, LOGGING_WEBLOGIC, COMMONS
54
+ # end
55
+ # define 'two' do
56
+ # compile.with SPRING_OLD, LOGGING, COMMONS
57
+ # end
58
+ # define 'three' do
59
+ # compile.with "commons-collections:commons-collections:jar:2.2"
60
+ # end
61
+ # end
62
+ #
63
+ #
64
+ # With ArtifactNamespace you can do some more advanced stuff, the following
65
+ # annotated snipped could still be reduced if default artifact definitions were
66
+ # loaded from yaml file (see section below and ArtifactNamespace.load).
67
+ #
68
+ # -- buildfile --
69
+ # artifact_ns do |ns| # the current namespace (root if called outside a project)
70
+ # # default artifacts
71
+ # ns.spring = 'org.springframework:spring:jar:2.5'
72
+ # # default logger is log4j
73
+ # ns.logger = 'log4j:log4j:jar:1.2.15'
74
+ #
75
+ # # create a sub namespace by calling the #ns method,
76
+ # # artifacts defined on the sub-namespace can be referenced by
77
+ # # name :commons_net or by calling commons.net
78
+ # ns.ns :commons, :net => 'commons-net:commons-net:jar:1.4.0',
79
+ # :logging => 'comons-logging:commons-logging:jar:1.1.1'
80
+ #
81
+ #
82
+ # # When a child namespace asks for the :log artifact,
83
+ # # these artifacts will be searched starting from the :current namespace.
84
+ # ns.virtual :log, :logger, :commons_logging
85
+ # end
86
+ #
87
+ # artifact_ns('example2:one') do |ns| # namespace for the one subproject
88
+ # ns.logger = artifact('bea:wlcommons-logging:jar:8.1').from('path/to/wlcommons-logging.jar')
89
+ # end
90
+ # artifact_ns('example2:two') do |ns|
91
+ # ns.spring = '1.0' # for project two use an older spring version (just for an example)
92
+ # end
93
+ # artifact_ns('example2:three').commons_collections = 2.2'
94
+ # artifact_ns('example2:four') do |ns|
95
+ # ns.beanutils = 'commons-beanutils:commons-beanutils:jar:1.5' # just for this project
96
+ # ns.ns(:compilation).use :commons_logging, :beanutils, :spring # compile time dependencies
97
+ # ns.ns(:testing).use :log, :beanutils, 'cglib:cglib-nodep:jar:2.1.3' # run time dependencies
98
+ # end
99
+ #
100
+ # define 'example2' do
101
+ # define 'one' do
102
+ # compile.with :spring, :log, :commons # uses weblogic logging
103
+ # end
104
+ # define 'two' do
105
+ # compile.with :spring, :log, :commons # will take old spring
106
+ # end
107
+ # define 'three' do
108
+ # compile.with :commons_collections
109
+ # test.with artifact_ns('example2:two').spring # use spring from project two
110
+ # end
111
+ # define 'four' do
112
+ # compile.with artifact_ns.compilation
113
+ # test.with artifact_ns.testing
114
+ # end
115
+ # task(:down_them_all) do # again, just to fill this space with something ;)
116
+ # parent.projects.map(&method(:artifact_ns)).map(&:artifacts).map(&:invoke)
117
+ # end
118
+ # end
119
+ #
120
+ # = Loading from a yaml file (e. profiles.yaml)
121
+ #
122
+ # If your projects use lots of jars (after all we are using java ;) you may prefer
123
+ # to have constant artifact definitions on an external file.
124
+ # Doing so would allow an external tool (or future Buildr feature) to maintain
125
+ # an artifacts.yaml for you.
126
+ # An example usage is documented on the ArtifactNamespace.load method.
127
+ #
128
+ # = For addon/plugin writers & Customizing artifact versions
129
+ #
130
+ # Sometimes users would need to change the default artifact versions used by some
131
+ # module, for example, the XMLBeans compiler needs this, because of compatibility
132
+ # issues. Another example would be to select the groovy version to use on all our
133
+ # projects so that Buildr modules requiring groovy jars can use user preferred versions.
134
+ #
135
+ # To meet this goal, an ArtifactNamespace allows to specify ArtifactRequirement objects.
136
+ # In fact the only difference with the examples you have already seen is that requirements
137
+ # have an associated VersionRequirement, so that each time a user tries to select a version,
138
+ # buildr checks if it satisfies the requirements.
139
+ #
140
+ # Requirements are declared using the ArtifactNamespace#need method, but again,
141
+ # syntactic sugar is provided by ArtifactNamespace#method_missing.
142
+ #
143
+ # The following example is taken from the XMLBeans compiler module.
144
+ # And illustrates how addon authors should specify their requirements,
145
+ # provide default versions, and document the namespace for users to customize.
146
+ #
147
+ # module Buildr::XMLBeans
148
+ #
149
+ # # You need to document this constant, giving users some hints
150
+ # # about when are (maybe some of) these artifacts used. I mean,
151
+ # # some modules, add jars to the Buildr classpath when its file
152
+ # # is required, you would need to tell your users, so that they
153
+ # # can open the namespace and specify their defaults. Of course
154
+ # # when the requirements are defined, buildr checks if any compatible
155
+ # # version has been already defined, if so, uses it.
156
+ # #
157
+ # # Some things here have been changed to illustrate their meaning.
158
+ # REQUIRES = ArtifactNamespace.for(self).tap do |ns|
159
+ #
160
+ # # This jar requires a >2.0 version, default being 2.3.0
161
+ # ns.xmlbeans! 'org.apache.xmlbeans:xmlbeans:jar:2.3.0', '>2'
162
+ #
163
+ # # Users can customize with Buildr::XMLBeans::REQUIRES.stax_api = '1.2'
164
+ # # This is a non-flexible requirement, only satisfied by version 1.0.1
165
+ # ns.stax_api! 'stax:stax-api:jar:1.0.1'
166
+ #
167
+ # # This one is not part of XMLBeans, but is just another example
168
+ # # illustrating an `artifact requirement spec`.
169
+ #
170
+ # ns.need " some_name -> ar:ti:fact:3.2.5 -> ( >2 & <4)"
171
+ #
172
+ # # As you can see it's just an artifact spec, prefixed with
173
+ # # ' some_name -> ', this means users can use that name to
174
+ # # reference the requirement, also this string has a VersionRequirement
175
+ # # just after another ->.
176
+ # end
177
+ #
178
+ # # The REQUIRES constant is an ArtifactNamespace instance,
179
+ # # that means we can use it directly. Note that calling
180
+ # # Buildr.artifact_ns would lead to the currently executing context,
181
+ # # not the one for this module.
182
+ # def use
183
+ # # test if user specified his own version, if so, we could perform some
184
+ # # functionallity based on this.
185
+ # REQUIRES.some_name.selected? # => false
186
+ #
187
+ # REQUIRES.some_name.satisfied_by?('1.5') # => false
188
+ # puts REQUIRES.some_name.requirement # => ( >2 & <4 )
189
+ #
190
+ # REQUIRES.artifacts # get the Artifact tasks
191
+ # end
192
+ #
193
+ # end
194
+ #
195
+ # A more advanced example using ArtifactRequirement listeners is included
196
+ # in the artifact_namespace_spec.rb description for 'Extension using ArtifactNamespace'
197
+ # That's it for addon writers, now, users can select their preferred version with
198
+ # something like:
199
+ #
200
+ # require 'buildr/xmlbeans'
201
+ # Buildr::XMLBeans::REQUIRES.xmlbeans = '2.2.0'
202
+ #
203
+ # More advanced stuff, if users really need to select an xmlbeans version
204
+ # per project, they can do so letting :current (that is, the currently running
205
+ # namespace) be parent of the REQUIRES namespace:
206
+ #
207
+ # Buildr::XMLBeans::REQUIRES.parent = :current
208
+ #
209
+ # Now, provided that the compiler does not caches its artifacts, it will
210
+ # select the correct version. (See the first section for how to select per project
211
+ # artifacts).
212
+ #
213
+ #
214
+ class ArtifactNamespace
215
+ class << self
216
+ # Forget all namespaces, create a new ROOT
217
+ def clear
218
+ @instances = nil
219
+ remove_const(:ROOT) rescue nil
220
+ const_set(:ROOT, new('root'))
221
+ end
222
+
223
+ # Differs from Artifact.to_hash in that 1) it does not choke when version isn't present
224
+ # and 2) it assumes that if an artifact spec ends with a colon, e.g. "org.example:library:jdk5:"
225
+ # it indicates the last segment ("jdk5") is a classifier.
226
+ def to_hash(spec)
227
+ if spec.respond_to?(:to_spec)
228
+ to_hash spec.to_spec
229
+ elsif Hash === spec
230
+ return spec
231
+ elsif String === spec || Symbol === spec
232
+ spec = spec.to_s
233
+ if spec[-1,1] == ':'
234
+ group, id, type, classifier, *rest = spec.split(':').map { |part| part.empty? ? nil : part }
235
+ else
236
+ group, id, type, version, *rest = spec.split(':').map { |part| part.empty? ? nil : part }
237
+ unless rest.empty?
238
+ # Optional classifier comes before version.
239
+ classifier, version = version, rest.shift
240
+ end
241
+ end
242
+ fail "Expecting <group:id:type:version> or <group:id:type:classifier:version>, found <#{spec}>" unless rest.empty?
243
+ { :group => group, :id => id, :type => type, :version => version, :classifier => classifier }.reject { |k,v| v == nil }
244
+ else
245
+ fail "Unexpected artifact spec: #{spec.inspect}"
246
+ end
247
+ end
248
+
249
+ # Populate namespaces from a hash of hashes.
250
+ # The following example uses the profiles yaml to achieve this.
251
+ #
252
+ # -- profiles.yaml --
253
+ # development:
254
+ # artifacts:
255
+ # root: # root namespace
256
+ # spring: org.springframework:spring:jar:2.5
257
+ # groovy: org.codehaus.groovy:groovy:jar:1.5.4
258
+ # logging: # define a named group
259
+ # - log4j:log4j:jar:1.2.15
260
+ # - commons-logging:commons-logging:jar:1.1.1
261
+ #
262
+ # # open Buildr::XMLBeans namespace
263
+ # Buildr::XMLBeans:
264
+ # xmlbeans: 2.2
265
+ #
266
+ # # for subproject one:oldie
267
+ # one:oldie:
268
+ # spring: org.springframework:spring:jar:1.0
269
+ #
270
+ # -- buildfile --
271
+ # ArtifactNamespace.load(Buildr.settings.profile['artifacts'])
272
+ def load(namespaces = {})
273
+ namespaces.each_pair { |name, uses| instance(name).use(uses) }
274
+ end
275
+
276
+ # :call-seq:
277
+ # ArtifactNamespace.instance { |current_ns| ... } -> current_ns
278
+ # ArtifactNamespace.instance(name) { |ns| ... } -> ns
279
+ # ArtifactNamespace.instance(:current) { |current_ns| ... } -> current_ns
280
+ # ArtifactNamespace.instance(:root) { |root_ns| ... } -> root_ns
281
+ #
282
+ # Obtain an instance for the given name
283
+ def instance(name = nil)
284
+ case name
285
+ when :root, 'root' then return ROOT
286
+ when ArtifactNamespace then return name
287
+ when Array then name = name.join(':')
288
+ when Module, Project then name = name.name
289
+ when :current, 'current', nil then
290
+ task = Thread.current[:rake_chain]
291
+ task = task.instance_variable_get(:@value) if task
292
+ name = case task
293
+ when Project then task.name
294
+ when Rake::Task then task.scope.join(':')
295
+ when nil then Buildr.application.current_scope.join(':')
296
+ end
297
+ end
298
+ name = name.to_s
299
+ if name.size == 0
300
+ instance = ROOT
301
+ else
302
+ name = name.to_s
303
+ @instances ||= Hash.new { |h, k| h[k] = new(k) }
304
+ instance = @instances[name]
305
+ end
306
+ yield(instance) if block_given?
307
+ instance
308
+ end
309
+
310
+ alias_method :[], :instance
311
+ alias_method :for, :instance
312
+
313
+ # :call-seq:
314
+ # ArtifactNamespace.root { |ns| ... } -> ns
315
+ #
316
+ # Obtain the root namespace, returns the ROOT constant
317
+ def root
318
+ yield ROOT if block_given?
319
+ ROOT
320
+ end
321
+ end
322
+
323
+ module DClone #:nodoc:
324
+ def dclone
325
+ clone = self.clone
326
+ clone.instance_variables.each do |i|
327
+ value = clone.instance_variable_get(i)
328
+ value = value.dclone rescue
329
+ clone.instance_variable_set(i, value)
330
+ end
331
+ clone
332
+ end
333
+ end
334
+
335
+ class Registry < Hash #:nodoc:
336
+ include DClone
337
+
338
+ attr_accessor :parent
339
+ def alias(new_name, old_name)
340
+ new_name = new_name.to_sym
341
+ old_name = old_name.to_sym
342
+ if obj = get(old_name, true)
343
+ self[new_name] = obj
344
+ @aliases ||= []
345
+ group = @aliases.find { |a| a.include?(new_name) }
346
+ group.delete(new_name) if group
347
+ group = @aliases.find { |a| a.include?(old_name) }
348
+ @aliases << (group = [old_name]) unless group
349
+ group << new_name unless group.include?(new_name)
350
+ end
351
+ obj
352
+ end
353
+
354
+ def aliases(name)
355
+ return [] unless name
356
+ name = name.to_sym
357
+ ((@aliases ||= []).find { |a| a.include?(name) } || [name]).dup
358
+ end
359
+
360
+ def []=(key, value)
361
+ return unless key
362
+ super(key.to_sym, value)
363
+ end
364
+
365
+ def get(key, include_parent = nil)
366
+ [].tap { |a| aliases(key).select { |n| a[0] = self[n] } }.first ||
367
+ (include_parent && parent && parent.get(key, include_parent))
368
+ end
369
+
370
+ def keys(include_parent = nil)
371
+ (super() | (include_parent && parent && parent.keys(include_parent) || [])).uniq
372
+ end
373
+
374
+ def values(include_parent = nil)
375
+ (super() | (include_parent && parent && parent.values(include_parent) || [])).uniq
376
+ end
377
+
378
+ def key?(key, include_parent = nil)
379
+ return false unless key
380
+ super(key.to_sym) || (include_parent && parent && parent.key?(key, include_parent))
381
+ end
382
+
383
+ def delete(key, include_parent = nil)
384
+ aliases(key).map {|n| super(n) } && include_parent && parent && parent.delete(key, include_parent)
385
+ end
386
+ end
387
+
388
+ # An artifact requirement is an object that ActsAsArtifact and has
389
+ # an associated VersionRequirement. It also knows the name (some times equal to the
390
+ # artifact id) that is used to store it in an ArtifactNamespace.
391
+ class ArtifactRequirement
392
+ attr_accessor :version
393
+ attr_reader :name, :requirement
394
+
395
+ include DClone
396
+
397
+ # Create a requirement from an `artifact requirement spec`.
398
+ # This spec has three parts, separated by ->
399
+ #
400
+ # some_name -> ar:ti:fact:3.2.5 -> ( >2 & <4)
401
+ #
402
+ # As you can see it's just an artifact spec, prefixed with
403
+ # some_name ->
404
+ # the :some_name symbol becomes this object's name and
405
+ # is used to store it on an ArtifactNamespace.
406
+ #
407
+ # ar:ti:fact:3.2.5
408
+ #
409
+ # The second part is an artifact spec by itself, and specifies
410
+ # all remaining attributes, the version of this spec becomes
411
+ # the default version of this requirement.
412
+ #
413
+ # The last part consist of a VersionRequirement.
414
+ # -> ( >2 & <4)
415
+ #
416
+ # VersionRequirement supports RubyGem's comparison operators
417
+ # in addition to parens, logical and, logical or and negation.
418
+ # See the docs for VersionRequirement for more info on operators.
419
+ def initialize(spec)
420
+ self.class.send :include, ActsAsArtifact unless ActsAsArtifact === self
421
+ if ArtifactRequirement === spec
422
+ copy_attrs(spec)
423
+ else
424
+ spec = requirement_hash(spec)
425
+ apply_spec_no_validation(spec[:spec])
426
+ self.name = spec[:name]
427
+ @requirement = spec[:requirement]
428
+ @version = @requirement.default if VersionRequirement.requirement?(@version)
429
+ end
430
+ end
431
+
432
+ def apply_spec_no_validation(spec)
433
+ spec = ArtifactNamespace.to_hash(spec)
434
+ ActsAsArtifact::ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) }
435
+ self
436
+ end
437
+
438
+ # Copy attributes from other to this object
439
+ def copy_attrs(other)
440
+ (ActsAsArtifact::ARTIFACT_ATTRIBUTES + [:name, :requirement]).each do |attr|
441
+ value = other.instance_variable_get("@#{attr}")
442
+ value = value.dup if value && !value.kind_of?(Numeric) && !value.kind_of?(Symbol)
443
+ instance_variable_set("@#{attr}", value)
444
+ end
445
+ end
446
+
447
+ def name=(name)
448
+ @name = name.to_s
449
+ end
450
+
451
+ # Set a the requirement, this must be an string formatted for
452
+ # VersionRequirement#create to parse.
453
+ def requirement=(version_requirement)
454
+ @requirement = VersionRequirement.create(version_requirement.to_s)
455
+ end
456
+
457
+ # Return a hash consisting of :name, :spec, :requirement
458
+ def requirement_hash(spec = self)
459
+ result = {}
460
+ if String === spec
461
+ parts = spec.split(/\s*->\s*/, 3).map(&:strip)
462
+ case parts.size
463
+ when 1
464
+ result[:spec] = ArtifactNamespace.to_hash(parts.first)
465
+ when 2
466
+ if /^\w+$/ === parts.first
467
+ result[:name] = parts.first
468
+ result[:spec] = ArtifactNamespace.to_hash(parts.last)
469
+ else
470
+ result[:spec] = ArtifactNamespace.to_hash(parts.first)
471
+ result[:requirement] = VersionRequirement.create(parts.last)
472
+ end
473
+ when 3
474
+ result[:name] = parts.first
475
+ result[:spec] = ArtifactNamespace.to_hash(parts[1])
476
+ result[:requirement] = VersionRequirement.create(parts.last)
477
+ end
478
+ else
479
+ result[:spec] = ArtifactNamespace.to_hash(spec)
480
+ end
481
+ result[:name] ||= result[:spec][:id].to_s.to_sym
482
+ result[:requirement] ||= VersionRequirement.create(result[:spec][:version])
483
+ result
484
+ end
485
+
486
+ # Test if this requirement is satisfied by an artifact spec.
487
+ def satisfied_by?(spec)
488
+ return false unless requirement
489
+ spec = ArtifactNamespace.to_hash(spec)
490
+ hash = to_spec_hash
491
+ hash.delete(:version)
492
+ version = spec.delete(:version)
493
+ hash == spec && requirement.satisfied_by?(version)
494
+ end
495
+
496
+ # Has user selected a version for this requirement?
497
+ def selected?
498
+ @selected
499
+ end
500
+
501
+ def selected! #:nodoc:
502
+ @selected = true
503
+ @listeners.each { |l| l.call(self) } if @listeners
504
+ self
505
+ end
506
+
507
+ def add_listener(&callback)
508
+ (@listeners ||= []) << callback
509
+ end
510
+
511
+ # Return the Artifact object for the currently selected version
512
+ def artifact
513
+ ::Buildr.artifact(self)
514
+ end
515
+
516
+ # Format this requirement as an `artifact requirement spec`
517
+ def to_requirement_spec
518
+ result = to_spec
519
+ result = "#{name} -> #{result}" if name
520
+ result = "#{result} -> #{requirement}" if requirement
521
+ result
522
+ end
523
+
524
+ def to_s #:nodoc:
525
+ id ? to_requirement_spec : version
526
+ end
527
+
528
+ # Return an artifact spec without the version part.
529
+ def unversioned_spec
530
+ hash = to_spec_hash
531
+ return nil if hash.values.compact.length <= 1
532
+ if hash[:classifier]
533
+ "#{hash[:group]}:#{hash[:id]}:#{hash[:type]}:#{hash[:classifier]}:"
534
+ else
535
+ "#{hash[:group]}:#{hash[:id]}:#{hash[:type]}"
536
+ end
537
+ end
538
+
539
+ class << self
540
+ def unversioned_spec(spec)
541
+ hash = ArtifactNamespace.to_hash(spec)
542
+ return nil if hash.values.compact.length <= 1
543
+ if hash[:classifier]
544
+ "#{hash[:group]}:#{hash[:id]}:#{hash[:type]}:#{hash[:classifier]}:"
545
+ else
546
+ "#{hash[:group]}:#{hash[:id]}:#{hash[:type]}"
547
+ end
548
+ end
549
+ end
550
+ end
551
+
552
+ include DClone
553
+ include Enumerable
554
+ attr_reader :name
555
+
556
+ def initialize(name = nil) #:nodoc:
557
+ @name = name.to_s if name
558
+ end
559
+ clear
560
+
561
+ def root
562
+ yield ROOT if block_given?
563
+ ROOT
564
+ end
565
+
566
+ # ROOT namespace has no parent
567
+ def parent
568
+ if root?
569
+ nil
570
+ elsif @parent.kind_of?(ArtifactNamespace)
571
+ @parent
572
+ elsif @parent
573
+ ArtifactNamespace.instance(@parent)
574
+ elsif name
575
+ parent_name = name.gsub(/::?[^:]+$/, '')
576
+ parent_name == name ? root : ArtifactNamespace.instance(parent_name)
577
+ else
578
+ root
579
+ end
580
+ end
581
+
582
+ # Set the parent for the current namespace, except if it is ROOT
583
+ def parent=(other)
584
+ raise 'Cannot set parent of root namespace' if root?
585
+ @parent = other
586
+ @registry = nil
587
+ end
588
+
589
+ # Is this the ROOT namespace?
590
+ def root?
591
+ ROOT == self
592
+ end
593
+
594
+ # Create a named sub-namespace, sub-namespaces are themselves
595
+ # ArtifactNamespace instances but cannot be referenced by
596
+ # the Buildr.artifact_ns, ArtifactNamespace.instance methods.
597
+ # Reference needs to be through this object using the given +name+
598
+ #
599
+ # artifact_ns('foo').ns(:bar).need :thing => 'some:thing:jar:1.0'
600
+ # artifact_ns('foo').bar # => the sub-namespace 'foo.bar'
601
+ # artifact_ns('foo').bar.thing # => the some thing artifact
602
+ #
603
+ # See the top level ArtifactNamespace documentation for examples
604
+ def ns(name, *uses, &block)
605
+ name = name.to_sym
606
+ sub = registry[name]
607
+ if sub
608
+ raise TypeError.new("#{name} is not a sub namespace of #{self}") unless sub.kind_of?(ArtifactNamespace)
609
+ else
610
+ sub = ArtifactNamespace.new("#{self.name}.#{name}")
611
+ sub.parent = self
612
+ registry[name] = sub
613
+ end
614
+ sub.use(*uses)
615
+ yield sub if block_given?
616
+ sub
617
+ end
618
+
619
+ # Test if a sub-namespace by the given name exists
620
+ def ns?(name)
621
+ sub = registry[name.to_sym]
622
+ ArtifactNamespace === sub
623
+ end
624
+
625
+ # :call-seq:
626
+ # artifact_ns.need 'name -> org:foo:bar:jar:~>1.2.3 -> 1.2.5'
627
+ # artifact_ns.need :name => 'org.foo:bar:jar:1.0'
628
+ #
629
+ # Create a new ArtifactRequirement on this namespace.
630
+ # ArtifactNamespace#method_missing provides syntactic sugar for this.
631
+ def need(*specs)
632
+ named = specs.flatten.inject({}) do |seen, spec|
633
+ if Hash === spec && (spec.keys & ActsAsArtifact::ARTIFACT_ATTRIBUTES).empty?
634
+ spec.each_pair do |name, spec|
635
+ if Array === spec # a group
636
+ seen[name] ||= spec.map { |s| ArtifactRequirement.new(s) }
637
+ else
638
+ artifact = ArtifactRequirement.new(spec)
639
+ artifact.name = name
640
+ seen[artifact.name] ||= artifact
641
+ end
642
+ end
643
+ else
644
+ artifact = ArtifactRequirement.new(spec)
645
+ seen[artifact.name] ||= artifact
646
+ end
647
+ seen
648
+ end
649
+ named.each_pair do |name, artifact|
650
+ if Array === artifact # a group
651
+ artifact.each do |a|
652
+ unvers = a.unversioned_spec
653
+ previous = registry[unvers]
654
+ if previous && previous.selected? && a.satisfied_by?(previous)
655
+ a.version = previous.version
656
+ end
657
+ registry[unvers] = a
658
+ end
659
+ group(name, *(artifact.map { |a| a.unversioned_spec } + [{:namespace => self}]))
660
+ else
661
+ unvers = artifact.unversioned_spec
662
+ previous = registry.get(unvers, true)
663
+ if previous && previous.selected? && artifact.satisfied_by?(previous)
664
+ artifact.version = previous.version
665
+ artifact.selected!
666
+ end
667
+ registry[unvers] = artifact
668
+ registry.alias name, unvers unless name.to_s[/^\s*$/]
669
+ end
670
+ end
671
+ self
672
+ end
673
+
674
+ # :call-seq:
675
+ # artifact_ns.use 'name -> org:foo:bar:jar:1.2.3'
676
+ # artifact_ns.use :name => 'org:foo:bar:jar:1.2.3'
677
+ # artifact_ns.use :name => '2.5.6'
678
+ #
679
+ # First and second form are equivalent, the third is used when an
680
+ # ArtifactRequirement has been previously defined with :name, so it
681
+ # just selects the version.
682
+ #
683
+ # ArtifactNamespace#method_missing provides syntactic sugar for this.
684
+ def use(*specs)
685
+ named = specs.flatten.inject({}) do |seen, spec|
686
+ if Hash === spec && (spec.keys & ActsAsArtifact::ARTIFACT_ATTRIBUTES).empty?
687
+ spec.each_pair do |name, spec|
688
+ if ArtifactNamespace === spec # create as subnamespace
689
+ raise ArgumentError.new("Circular reference") if self == spec
690
+ registry[name.to_sym] = spec
691
+ elsif Numeric === spec || (String === spec && VersionRequirement.version?(spec))
692
+ artifact = ArtifactRequirement.allocate
693
+ artifact.name = name
694
+ artifact.version = spec.to_s
695
+ seen[artifact.name] ||= artifact
696
+ elsif Symbol === spec
697
+ self.alias name, spec
698
+ elsif Array === spec # a group
699
+ seen[name] ||= spec.map { |s| ArtifactRequirement.new(s) }
700
+ else
701
+ artifact = ArtifactRequirement.new(spec)
702
+ artifact.name = name
703
+ seen[artifact.name] ||= artifact
704
+ end
705
+ end
706
+ else
707
+ if Symbol === spec
708
+ artifact = get(spec).dclone
709
+ else
710
+ artifact = ArtifactRequirement.new(spec)
711
+ end
712
+ seen[artifact.name] ||= artifact
713
+ end
714
+ seen
715
+ end
716
+ named.each_pair do |name, artifact|
717
+ is_group = Array === artifact
718
+ artifact = [artifact].flatten.map do |artifact|
719
+ unvers = artifact.unversioned_spec
720
+ previous = get(unvers, false) || get(name, false)
721
+ if previous # have previous on current namespace
722
+ if previous.requirement # we must satisfy the requirement
723
+ if unvers
724
+ satisfied = previous.satisfied_by?(artifact)
725
+ else # we only have the version
726
+ satisfied = previous.requirement.satisfied_by?(artifact.version)
727
+ end
728
+ raise "Unsatisfied dependency #{previous} " +
729
+ "not satisfied by #{artifact}" unless satisfied
730
+ previous.version = artifact.version # OK, set new version
731
+ artifact = previous # use the same object for aliases
732
+ else # not a requirement, set the new values
733
+ unless artifact.id == previous.id && name != previous.name
734
+ previous.copy_attrs(artifact)
735
+ artifact = previous
736
+ end
737
+ end
738
+ else
739
+ if unvers.nil? && # we only have the version
740
+ (previous = get(unvers, true, false, false))
741
+ version = artifact.version
742
+ artifact.copy_attrs(previous)
743
+ artifact.version = version
744
+ end
745
+ artifact.requirement = nil
746
+ end
747
+ artifact.selected!
748
+ end
749
+ artifact = artifact.first unless is_group
750
+ if is_group
751
+ names = artifact.map do |art|
752
+ unv = art.unversioned_spec
753
+ registry[unv] = art
754
+ unv
755
+ end
756
+ group(name, *(names + [{:namespace => self}]))
757
+ elsif artifact.id
758
+ unvers = artifact.unversioned_spec
759
+ registry[name] = artifact
760
+ registry.alias unvers, name
761
+ else
762
+ registry[name] = artifact
763
+ end
764
+ end
765
+ self
766
+ end
767
+
768
+ # Like Hash#fetch
769
+ def fetch(name, default = nil, &block)
770
+ block ||= proc { raise IndexError.new("No artifact found by name #{name.inspect} in namespace #{self}") }
771
+ real_name = name.to_s[/^[\w\-\.]+$/] ? name : ArtifactRequirement.unversioned_spec(name)
772
+ get(real_name.to_sym) || default || block.call(name)
773
+ end
774
+
775
+ # :call-seq:
776
+ # artifact_ns[:name] -> ArtifactRequirement
777
+ # artifact_ns[:many, :names] -> [ArtifactRequirement]
778
+ def [](*names)
779
+ ary = values_at(*names)
780
+ names.size == 1 ? ary.first : ary
781
+ end
782
+
783
+ # :call-seq:
784
+ # artifact_ns[:name] = 'some:cool:jar:1.0.2'
785
+ # artifact_ns[:name] = '1.0.2'
786
+ #
787
+ # Just like the use method
788
+ def []=(*names)
789
+ values = names.pop
790
+ values = [values] unless Array === values
791
+ names.each_with_index do |name, i|
792
+ use name => (values[i] || values.last)
793
+ end
794
+ end
795
+
796
+ # yield each ArtifactRequirement
797
+ def each(&block)
798
+ values.each(&block)
799
+ end
800
+
801
+ # return Artifact objects for each requirement
802
+ def artifacts(*names)
803
+ (names.empty? && values || values_at(*names)).map(&:artifact)
804
+ end
805
+
806
+ # Return all requirements for this namespace
807
+ def values(include_parents = false, include_groups = true)
808
+ seen, dict = {}, registry
809
+ while dict
810
+ dict.each do |k, v|
811
+ v = v.call if v.respond_to?(:call)
812
+ v = v.values if v.kind_of?(ArtifactNamespace)
813
+ if Array === v && include_groups
814
+ v.compact.each { |v| seen[v.name] = v unless seen.key?(v.name) }
815
+ else
816
+ seen[v.name] = v unless seen.key?(v.name)
817
+ end
818
+ end
819
+ dict = include_parents ? dict.parent : nil
820
+ end
821
+ seen.values
822
+ end
823
+
824
+ # Return only the named requirements
825
+ def values_at(*names)
826
+ names.map do |name|
827
+ catch :artifact do
828
+ unless name.to_s[/^[\w\-\.]+$/]
829
+ unvers = ArtifactRequirement.unversioned_spec(name)
830
+ unless unvers.to_s == name.to_s
831
+ req = ArtifactRequirement.new(name)
832
+ reg = self
833
+ while reg
834
+ candidate = reg.send(:get, unvers, false, false, true)
835
+ throw :artifact, candidate if req.satisfied_by?(candidate)
836
+ reg = reg.parent
837
+ end
838
+ end
839
+ end
840
+ get(name.to_sym)
841
+ end
842
+ end
843
+ end
844
+
845
+ def key?(name, include_parents = false)
846
+ name = ArtifactRequirement.unversioned_spec(name) unless name.to_s[/^[\w\-\.]+$/]
847
+ registry.key?(name, include_parents)
848
+ end
849
+
850
+ def keys
851
+ values.map(&:name)
852
+ end
853
+
854
+ def delete(name, include_parents = false)
855
+ registry.delete(name, include_parents)
856
+ self
857
+ end
858
+
859
+ def clear
860
+ keys.each { |k| delete(k) }
861
+ end
862
+
863
+ # :call-seq:
864
+ # group :who, :me, :you
865
+ # group :them, :me, :you, :namespace => ns
866
+ #
867
+ # Create a virtual group on this namespace. When the namespace
868
+ # is asked for the +who+ artifact, it's value is an array made from
869
+ # the remaining names. These names are searched by default from the current
870
+ # namespace.
871
+ # Second form specified the starting namespace to search from.
872
+ def group(group_name, *members)
873
+ namespace = (Hash === members.last && members.pop[:namespace]) || :current
874
+ registry[group_name] = lambda do
875
+ artifacts = self.class[namespace].values_at(*members)
876
+ artifacts = artifacts.first if members.size == 1
877
+ artifacts
878
+ end
879
+ self
880
+ end
881
+
882
+ alias_method :virtual, :group
883
+
884
+ # Create an alias for a named requirement.
885
+ def alias(new_name, old_name)
886
+ registry.alias(new_name, old_name) or
887
+ raise NameError.new("Undefined artifact name: #{old_name}")
888
+ end
889
+
890
+ def to_s #:nodoc:
891
+ name.to_s
892
+ end
893
+
894
+ # :call-seq:
895
+ # artifact_ns.cool_aid!('cool:aid:jar:2.3.4', '~>2.3') -> artifact_requirement
896
+ # artifact_ns.cool_aid = '2.3.5'
897
+ # artifact_ns.cool_aid -> artifact_requirement
898
+ # artifact_ns.cool_aid? -> true | false
899
+ #
900
+ # First form creates an ArtifactRequirement on the namespace.
901
+ # It is equivalent to providing a requirement_spec to the #need method:
902
+ # artifact_ns.need "cool_aid -> cool:aid:jar:2.3.4 -> ~>2.3"
903
+ # the second argument is optional.
904
+ #
905
+ # Second form can be used to select an artifact version
906
+ # and is equivalent to:
907
+ # artifact_ns.use :cool_aid => '1.0'
908
+ #
909
+ # Third form obtains the named ArtifactRequirement, can be
910
+ # used to test if a named requirement has been defined.
911
+ # It is equivalent to:
912
+ # artifact_ns.fetch(:cool_aid) { nil }
913
+ #
914
+ # Last form tests if the ArtifactRequirement has been defined
915
+ # and a version has been selected for use.
916
+ # It is equivalent to:
917
+ #
918
+ # artifact_ns.has_cool_aid?
919
+ # artifact_ns.values_at(:cool_aid).flatten.all? { |a| a && a.selected? }
920
+ #
921
+ def method_missing(name, *args, &block)
922
+ case name.to_s
923
+ when /!$/ then
924
+ name = $`.intern
925
+ if args.size < 1 || args.size > 2
926
+ raise ArgumentError.new("wrong number of arguments for #{name}!(spec, version_requirement?)")
927
+ end
928
+ need name => args.first
929
+ get(name).tap { |r| r.requirement = args.last if args.size == 2 }
930
+ when /=$/ then use $` => args.first
931
+ when /\?$/ then
932
+ name = $`.gsub(/^(has|have)_/, '').intern
933
+ [get(name)].flatten.all? { |a| a && a.selected? }
934
+ else
935
+ if block || args.size > 0
936
+ raise ArgumentError.new("wrong number of arguments #{args.size} for 0 or block given")
937
+ end
938
+ get(name)
939
+ end
940
+ end
941
+
942
+ # Return an anonymous module
943
+ # # first create a requirement
944
+ # artifact_ns.cool_aid! 'cool:aid:jar:>=1.0'
945
+ #
946
+ # # extend an object as a cool_aid delegator
947
+ # jars = Object.new.extend(artifact_ns.accessor(:cool_aid))
948
+ # jars.cool_aid = '2.0'
949
+ #
950
+ # artifact_ns.cool_aid.version # -> '2.0'
951
+ def accessor(*names)
952
+ ns = self
953
+ Module.new do
954
+ names.each do |name|
955
+ define_method("#{name}") { ns.send("#{name}") }
956
+ define_method("#{name}?") { ns.send("#{name}?") }
957
+ define_method("#{name}=") { |vers| ns.send("#{name}=", vers) }
958
+ end
959
+ end
960
+ end
961
+
962
+ private
963
+ def get(name, include_parents = true, include_subs = true, include_self = true) #:nodoc:
964
+ artifact = nil
965
+ if include_subs && name.to_s[/_/] # try sub namespaces first
966
+ sub, parts = self, name.to_s.split('_')
967
+ sub_name = parts.shift.to_sym
968
+ until sub != self || parts.empty?
969
+ if registry[sub_name].kind_of?(ArtifactNamespace)
970
+ sub = registry[sub_name]
971
+ artifact = sub[parts.join('_')]
972
+ else
973
+ sub_name = [sub_name, parts.shift].join('_').to_sym
974
+ end
975
+ end
976
+ end
977
+ unless artifact
978
+ if include_self
979
+ artifact = registry.get(name, include_parents)
980
+ elsif include_parents && registry.parent
981
+ artifact = registry.parent.get(name, true)
982
+ end
983
+ end
984
+ artifact = artifact.call if artifact && artifact.respond_to?(:call)
985
+ artifact
986
+ end
987
+
988
+ def registry
989
+ @registry ||= Registry.new.tap do |m|
990
+ m.parent = parent.send(:registry) unless root?
991
+ end
992
+ end
993
+
994
+ end # ArtifactNamespace
995
+
996
+ # :call-seq:
997
+ # project.artifact_ns -> ArtifactNamespace
998
+ # Buildr.artifact_ns(name) -> ArtifactNamespace
999
+ # Buildr.artifact_ns -> ArtifactNamespace for the currently running Project
1000
+ #
1001
+ # Open an ArtifactNamespace.
1002
+ # If a block is provided, the namespace is yielded to it.
1003
+ #
1004
+ # See also ArtifactNamespace.instance
1005
+ def artifact_ns(name = nil, &block)
1006
+ name = self if name.nil? && self.kind_of?(Project)
1007
+ ArtifactNamespace.instance(name, &block)
1008
+ end
1009
+
1010
+ end