ivy4r 0.9.14 → 0.9.15

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.
@@ -1,10 +1,3 @@
1
- === 0.9.14 / 2010-03-16
2
-
3
- * Fix for circular build dependency errors in buildr thanks to Pepijn Van Eeckhoudt for the patch.
4
- The culprit is the call to Buildr.projects in add_copy_tasks_for_publish. This can cause a call
5
- to project.invoke on the project being defined itself. Buildr.projects should not be called from
6
- before/after define callbacks as Pepijn found out from the Buildr developers.
7
-
8
1
  === 0.9.13 / 2010-02-16
9
2
 
10
3
  * Fix problems with Facets and ActiveSupport. We now only include the required core extensions,
@@ -1,701 +1,703 @@
1
- require 'ivy4r'
2
-
3
- module Buildr
4
- module Ivy
5
-
6
- class << self
7
- def setting(*keys)
8
- setting = Buildr.settings.build['ivy']
9
- keys.each { |key| setting = setting[key] unless setting.nil? }
10
- setting
11
- end
12
- end
13
-
14
- class IvyConfig
15
- TARGETS = [:compile, :test, :package]
16
- TYPES = [:conf, :type, :include, :exclude]
17
-
18
- attr_accessor :extension_dir, :resolved
19
-
20
- attr_reader :post_resolve_task_list
21
-
22
- # Hash of all artifacts to publish with mapping from artifact name to ivy publish name
23
- attr_reader :publish_mappings
24
-
25
- # Store the current project and initialize ivy ant wrapper
26
- def initialize(project)
27
- @project = project
28
- if project.parent.nil?
29
- @extension_dir = @project.base_dir
30
- @post_resolve_task_list = []
31
- else
32
- @extension_dir = @project.parent.ivy.extension_dir
33
- @base_ivy = @project.parent.ivy unless own_file?
34
- end
35
- @target_config = Hash.new do
36
- |hash, key| hash[key] = {}
37
- end
38
- end
39
-
40
- def enabled?
41
- @enabled ||= Ivy.setting('enabled') || true
42
- end
43
-
44
- def own_file?
45
- @own_file ||= File.exists?(@project.path_to(file))
46
- end
47
-
48
- # Returns the correct ivy4r instance to use, if project has its own ivy file uses the ivy file
49
- # of project, if not uses the ivy file of parent project.
50
- # Use this for low-level access to ivy functions as needed, i.e. in +post_resolve+
51
- def ivy4r
52
- unless @ivy4r
53
- if own_file?
54
- @ivy4r = ::Ivy4r.new(@project.ant('ivy'))
55
- @ivy4r.lib_dir = lib_dir if lib_dir
56
- @ivy4r.project_dir = @extension_dir
57
- else
58
- @ivy4r = @project.parent.ivy.ivy4r
59
- end
60
- end
61
- @ivy4r
62
- end
63
-
64
- # Returns name of the project the ivy file belongs to.
65
- def file_project
66
- own_file? ? @project : @base_ivy.file_project
67
- end
68
-
69
- # Returns the artifacts for given configurations as array
70
- # this is a post resolve task.
71
- # the arguments are checked for the following:
72
- # 1. if an Hash is given :conf is used for confs and :type is used for types
73
- # 2. if exactly two arrays are given args[0] is used for confs and args[1] is used for types
74
- # 3. if not exactly two arrays all args are used as confs
75
- def deps(*args)
76
- if args.size == 1 && args[0].kind_of?(Hash)
77
- confs, types = [args[0][:conf]].flatten, [args[0][:type]].flatten
78
- elsif args.size == 2 && args[0].kind_of?(Array) && args[1].kind_of?(Array)
79
- confs, types = args[0], args[1]
80
- else
81
- confs, types = args.flatten, []
82
- end
83
-
84
- [confs, types].each do |t|
85
- t.reject! {|c| c.nil? || c.blank? }
86
- end
87
-
88
- unless confs.empty?
89
- pathid = "ivy.deps." + confs.join('.') + '.' + types.join('.')
90
- params = {:conf => confs.join(','), :pathid => pathid}
91
- params[:type] = types.join(',') unless types.nil? || types.size == 0
92
-
93
- ivy4r.cachepath params
94
- end
95
- end
96
-
97
- # Returns ivy info for configured ivy file using a new ivy4r instance.
98
- def info
99
- if @base_ivy
100
- @base_ivy.info
101
- else
102
- ivy4r.settings :id => 'ivy.info.settingsref'
103
- result = ivy4r.info :file => file, :settingsRef => 'ivy.info.settingsref'
104
- @ivy4r = nil
105
- result
106
- end
107
- end
108
-
109
- # Configures the ivy instance with additional properties and loading the settings file if it was provided
110
- def configure
111
- if @base_ivy
112
- @base_ivy.configure
113
- else
114
- unless @configured
115
- ivy4r.property['ivy.status'] = status
116
- ivy4r.property['ivy.home'] = home
117
- properties.each {|key, value| ivy4r.property[key.to_s] = value }
118
- @configured = ivy4r.settings :file => settings if settings
119
- end
120
- end
121
- end
122
-
123
- # Resolves the configured file once.
124
- def __resolve__
125
- if @base_ivy
126
- @base_ivy.__resolve__
127
- else
128
- unless @resolved
129
- @resolved = ivy4r.resolve :file => file
130
- @project.send(:info, "Calling '#{post_resolve_tasks.size}' post_resolve tasks for '#{@project.name}'")
131
- post_resolve_tasks.each { |p| p.call(self) }
132
- end
133
- end
134
- end
135
-
136
- # Returns the additional infos for the manifest file.
137
- def manifest
138
- if @base_ivy
139
- @base_ivy.manifest
140
- else
141
- {
142
- 'organisation' => @resolved['ivy.organisation'],
143
- 'module' => @resolved['ivy.organisation'],
144
- 'revision' => revision
145
- }
146
- end
147
- end
148
-
149
- # Creates the standard ivy dependency report
150
- def report
151
- ivy4r.report :todir => report_dir
152
- end
153
-
154
- # Cleans the ivy cache
155
- def cleancache
156
- ivy4r.cleancache
157
- end
158
-
159
-
160
- # Publishs the project as defined in ivy file if it has not been published already
161
- def __publish__
162
- if @base_ivy
163
- @base_ivy.__publish__
164
- else
165
- unless @published
166
- options = {:status => status, :pubrevision => revision, :artifactspattern => "#{publish_from}/[artifact].[ext]"}
167
- options = publish_options * options
168
- ivy4r.publish options
169
- @published = true
170
- end
171
- end
172
- end
173
-
174
- def home
175
- @ivy_home_dir ||= Ivy.setting('home.dir') || "#{@extension_dir}/ivy-home"
176
- end
177
-
178
- def lib_dir
179
- @lib_dir ||= Ivy.setting('lib.dir')
180
- end
181
-
182
- def settings
183
- @settings ||= Ivy.setting('settings.file') || "#{@extension_dir}/ant-scripts/ivysettings.xml"
184
- end
185
-
186
- def file
187
- @ivy_file ||= Ivy.setting('ivy.file') || 'ivy.xml'
188
- end
189
-
190
- # Sets the revision to use for the project, this is useful for development revisions that
191
- # have an appended timestamp or any other dynamic revisioning.
192
- #
193
- # To set a different revision this method can be used in different ways.
194
- # 1. project.ivy.revision(revision) to set the revision directly
195
- # 2. project.ivy.revision { |ivy| [calculate revision] } use the block for dynamic
196
- # calculation of the revision. You can access ivy4r via <tt>ivy.ivy4r.[method]</tt>
197
- def revision(*revision, &block)
198
- raise "Invalid call with parameters and block!" if revision.size > 0 && block
199
- if revision.empty? && block.nil?
200
- if @revision_calc
201
- @revision ||= @revision_calc.call(self)
202
- else
203
- @revision ||= @project.version
204
- end
205
- elsif block.nil?
206
- raise "revision value invalid #{revision.join(', ')}" unless revision.size == 1
207
- @revision = revision[0]
208
- self
209
- else
210
- @revision_calc = block
211
- self
212
- end
213
- end
214
-
215
- # Sets the status to use for the project, this is useful for custom status handling
216
- # like integration, alpha, gold.
217
- #
218
- # To set a different status this method can be used in different ways.
219
- # 1. project.ivy.status(status) to set the status directly
220
- # 2. project.ivy.status { |ivy| [calculate status] } use the block for dynamic
221
- # calculation of the status. You can access ivy4r via <tt>ivy.ivy4r.[method]</tt>
222
- def status(*status, &block)
223
- raise "Invalid call with parameters and block!" if status.size > 0 && block
224
- if status.empty? && block.nil?
225
- if @status_calc
226
- @status ||= @status_calc.call(self)
227
- else
228
- @status ||= Ivy.setting('status') || 'integration'
229
- end
230
- elsif status.empty? && block.nil?
231
- raise "status value invalid #{status.join(', ')}" unless status.size == 1
232
- @status = status[0]
233
- self
234
- else
235
- @status_calc = block
236
- self
237
- end
238
- end
239
-
240
- # Sets the publish options to use for the project. The options are merged with the default
241
- # options including value set via #publish_from and overwrite all of them.
242
- #
243
- # To set the options this method can be used in different ways.
244
- # 1. project.ivy.publish_options(options) to set the options directly
245
- # 2. project.ivy.publish_options { |ivy| [calculate options] } use the block for dynamic
246
- # calculation of options. You can access ivy4r via <tt>ivy.ivy4r.[method]</tt>
247
- def publish_options(*options, &block)
248
- raise "Invalid call with parameters and block!" if options.size > 0 && block
249
- if options.empty? && block.nil?
250
- if @publish_options_calc
251
- @publish_options ||= @publish_options_calc.call(self)
252
- else
253
- @publish_options ||= Ivy.setting('publish.options')
254
- end
255
- else
256
- raise "Could not set 'publish_options' for '#{@project.name}' without own ivy file!" unless own_file?
257
- if options.size > 0 && block.nil?
258
- raise "publish options value invalid #{options.join(', ')}" unless options.size == 1
259
- @publish_options = options[0]
260
- self
261
- else
262
- @publish_options_calc = block
263
- self
264
- end
265
- end
266
- end
267
-
268
- # Sets the additional properties for the ivy process use a Hash with the properties to set.
269
- def properties(*properties)
270
- if properties.empty?
271
- @properties ||= {}
272
- else
273
- raise "properties value invalid #{properties.join(', ')}" unless properties.size == 1
274
- @properties = properties[0]
275
- self
276
- end
277
- end
278
-
279
- # Sets the local repository for ivy files
280
- def local_repository(*local_repository)
281
- if local_repository.empty?
282
- if own_file?
283
- @local_repository ||= Ivy.setting('local.repository.dir') || "#{home}/repository"
284
- else
285
- @project.parent.ivy.local_repository
286
- end
287
- else
288
- raise "Could not set 'local_repository' for '#{@project.name}' without own ivy file!" unless own_file?
289
- raise "local_repository value invalid #{local_repository.join(', ')}" unless local_repository.size == 1
290
- @local_repository = local_repository[0]
291
- self
292
- end
293
- end
294
-
295
- # :call-seq:
296
- # ivy.publish(package(:jar) => 'new_name_without_version_number.jar')
297
- # #deprecated! ivy.name(package(:jar) => 'new_name_without_version_number.jar')
298
- #
299
- # Maps a package to a different name for publishing. This name is used instead of the default name
300
- # for publishing use a hash with the +package+ as key and the newly mapped name as value. I.e.
301
- # <tt>ivy.name(package(:jar) => 'new_name_without_version_number.jar')</tt>
302
- # Note that this method is additive, a second call adds the names to the first.
303
- def publish(*publish_mappings)
304
- if publish_mappings.empty?
305
- @publish_mappings ||= {}
306
- else
307
- raise "publish_mappings value invalid #{publish_mappings.join(', ')}" unless publish_mappings.size == 1
308
- @publish_mappings = @publish_mappings ? @publish_mappings + publish_mappings[0] : publish_mappings[0].dup
309
- self
310
- end
311
- end
312
-
313
- def name(*args)
314
- puts "name(*args) is deprecated use publish(*args)!"
315
- publish(*args)
316
- end
317
-
318
- # Sets the directory to publish artifacts from.
319
- def publish_from(*publish_dir)
320
- if publish_dir.empty?
321
- if own_file?
322
- @publish_from ||= Ivy.setting('publish.from') || @project.path_to(:target)
323
- else
324
- @project.parent.ivy.publish_from
325
- end
326
- else
327
- raise "Could not set 'publish_from' for '#{@project.name}' without own ivy file!" unless own_file?
328
- raise "publish_from value invalid #{publish_dir.join(', ')}" unless publish_dir.size == 1
329
- @publish_from = publish_dir[0]
330
- self
331
- end
332
- end
333
-
334
- # Sets the directory to create dependency reports in.
335
- def report_dir(*report_dir)
336
- if report_dir.empty?
337
- if own_file?
338
- @report_dir ||= Ivy.setting('report.dir') || @project.path_to(:reports, 'ivy')
339
- else
340
- @project.parent.ivy.report_dir
341
- end
342
- else
343
- raise "Could not set 'report_dir' for '#{@project.name}' without own ivy file!" unless own_file?
344
- raise "publish_from value invalid #{report_dir.join(', ')}" unless report_dir.size == 1
345
- @report_dir = report_dir[0]
346
- self
347
- end
348
- end
349
-
350
- # Adds given block as post resolve action that is executed directly after #resolve has been called.
351
- # Yields this ivy config object into block.
352
- # <tt>project.ivy.post_resolve { |ivy| p "all deps:" + ivy.deps('all').join(", ") }</tt>
353
- def post_resolve(&block)
354
- post_resolve_tasks << block if block
355
- end
356
-
357
- # Filter artifacts for given configuration with provided filter values, this is a post resolve
358
- # task like #deps.
359
- # <tt>project.ivy.filter('server', 'client', :include => /b.*.jar/, :exclude => [/a\.jar/, /other.*\.jar/])</tt>
360
- def filter(*confs)
361
- filter = confs.last.kind_of?(Hash) ? confs.pop : {}
362
- unless (filter.keys - (TYPES - [:conf])).empty?
363
- raise ArgumentError, "Invalid filter use :include and/or :exclude only: given #{filter.keys.inspect}"
364
- end
365
- includes, excludes, types = filter[:include] || [], filter[:exclude] || [], filter[:type] || []
366
-
367
- artifacts = deps(confs.flatten, types.flatten)
368
- if artifacts
369
- artifacts = artifacts.find_all do |lib|
370
- lib = File.basename(lib)
371
- includes = includes.reject {|i| i.nil? || i.blank? }
372
- should_include = includes.empty? || includes.any? {|include| include === lib }
373
- should_include && !excludes.any? {|exclude| exclude === lib}
374
- end
375
- end
376
-
377
- artifacts
378
- end
379
-
380
- # :call-seq:
381
- # for types:
382
- # project.ivy.include(:compile => [/\.jar/, /\.gz/], :package => 'cglib.jar')
383
- # project.ivy.exclude(:test => 'cglib.jar')
384
- # project.ivy.conf(:compile => 'compile', :test => 'test', :package => 'prod')
385
- # for targets:
386
- # project.ivy.compile(:conf => 'compile', :exclude => /cglib.jar/)
387
- # project.ivy.test(:conf => 'test')
388
- # project.ivy.package(:conf => 'prod', :include => /.*.jar/, :exclude => /cglib.jar/)
389
- # or verbose:
390
- # project.ivy.compile_conf or project.ivy.conf_compile
391
- # project.ivy.compile_include or project.ivy.include_compile
392
- # the same for the other possible options.
393
- #
394
- # Uses #method_missing to handle the options.
395
- # Generic handling of settings for +target+ and +type+. All calls in the form
396
- # <tt>target_type({})</tt> or <tt>type_target({})</tt> are handled via this method see
397
- # #TARGETS #TYPES for more information about valid targets and types.
398
- def method_missing(methodname, *args, &block)
399
- if block.nil? && valid_config_call?(methodname)
400
- target, type = target(methodname), type(methodname)
401
- if target && type
402
- handle_variable(target, type, *args)
403
- elsif target && args.size == 1 && args.last.kind_of?(Hash)
404
- args[0].each { |type, value| handle_variable(target, type, *value) }
405
- self
406
- elsif type && args.size == 1 && args.last.kind_of?(Hash)
407
- args[0].each { |target, value| handle_variable(target, type, *value) }
408
- self
409
- else
410
- raise "Could not recognize config call for method '#{methodname}', args=#{args.inspect}"
411
- end
412
- else
413
- super.method_missing(methodname, *args, &block)
414
- end
415
- end
416
-
417
- private
418
- def target(targets)
419
- t = targets.to_s.split('_').find { |target| TARGETS.member? target.to_sym }
420
- t ? t.to_sym : nil
421
- end
422
-
423
- def type(types)
424
- t = types.to_s.split('_').find { |type| TYPES.member? type.to_sym }
425
- t ? t.to_sym : nil
426
- end
427
-
428
- def valid_config_call?(method_name)
429
- valid_calls = []
430
- TYPES.each do|type|
431
- TARGETS.each do|target|
432
- valid_calls << type.to_s << target.to_s << "#{type}_#{target}" << "#{target}_#{type}"
433
- end
434
- end
435
- valid_calls.member? method_name.to_s
436
- end
437
-
438
- # Sets a variable for given basename and type to given values. If values are empty returns the
439
- # current value.
440
- # I.e. <tt>handle_variable(:package, :include, /blua.*\.jar/, /da.*\.jar/)</tt>
441
- def handle_variable(target, type, *values)
442
- unless TARGETS.member?(target) && TYPES.member?(type)
443
- raise ArgumentError, "Unknown config value for target #{target.inspect} and type #{type.inspect}"
444
- end
445
- if values.empty?
446
- @target_config[target][type] ||= [Ivy.setting("#{target.to_s}.#{type.to_s}") || ''].flatten.uniq
447
- else
448
- @target_config[target][type] = [values].flatten.uniq
449
- self
450
- end
451
- end
452
-
453
- def post_resolve_tasks
454
- @base_ivy ? @base_ivy.post_resolve_task_list : post_resolve_task_list
455
- end
456
- end
457
-
458
- =begin rdoc
459
- The Ivy Buildr extension adding the new tasks for ivy.
460
-
461
- To use ivy in a +buildfile+ do something like:
462
- ENV['BUILDR_EXT_DIR'] ||= '../Ivy'
463
- require 'buildr/ivy_extension'
464
- define 'ivy_project' do
465
- [...]
466
- ivy.compile_conf('compile').test_conf('test').package_conf('prod', 'server')
467
- [...]
468
- end
469
-
470
- - This will add the +compile+ configuration to compile and test tasks
471
- - Add the +test+ configuration to test compilation and execution
472
- - include the artifacts from +prod+ and +server+ to any generated war or ear
473
- - The ENV variable is needed to automatically configure the load path for ivy libs.
474
- It assumes that you have the following dir structure <tt>[BUILDR_EXT_DIR]/ivy-home/jars</tt>
475
-
476
- For more configuration options see IvyConfig.
477
- =end
478
- module IvyExtension
479
- include Buildr::Extension
480
-
481
- class << self
482
-
483
- def add_ivy_deps_to_java_tasks(project)
484
- resolve_target = project.ivy.file_project.task('ivy:resolve')
485
- project.task :compiledeps => resolve_target do
486
- includes = project.ivy.compile_include
487
- excludes = project.ivy.compile_exclude
488
- types = project.ivy.compile_type
489
- confs = [project.ivy.compile_conf].flatten
490
- if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
491
- project.compile.with [deps, project.compile.dependencies].flatten
492
- sort_dependencies(project.compile.dependencies, deps, project.path_to(''))
493
- info "Ivy adding compile dependencies '#{confs.join(', ')}' to project '#{project.name}'"
494
- end
495
- end
496
-
497
- project.task :compile => "#{project.name}:compiledeps"
498
-
499
- project.task :testdeps => resolve_target do
500
- includes = project.ivy.test_include
501
- excludes = project.ivy.test_exclude
502
- types = project.ivy.test_type
503
- confs = [project.ivy.test_conf, project.ivy.compile_conf].flatten.uniq
504
- if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
505
- project.test.with [deps, project.test.dependencies].flatten
506
- sort_dependencies(project.test.dependencies, deps, project.path_to(''))
507
- sort_dependencies(project.test.compile.dependencies, deps, project.path_to(''))
508
- info "Ivy adding test dependencies '#{confs.join(', ')}' to project '#{project.name}'"
509
- end
510
- end
511
- project.task "test:compile" => "#{project.name}:testdeps"
512
-
513
- project.task :javadocdeps => resolve_target do
514
- confs = [project.ivy.test_conf, project.ivy.compile_conf].flatten.uniq
515
- if deps = project.ivy.deps(confs)
516
- project.javadoc.with deps
517
- info "Ivy adding javadoc dependencies '#{confs.join(', ')}' to project '#{project.name}'"
518
- end
519
- end
520
- project.task :javadoc => "#{project.name}:javadocdeps"
521
-
522
- [project.task(:eclipse), project.task(:idea), project.task(:idea7x)].each do |task|
523
- task.prerequisites.each{|p| p.enhance ["#{project.name}:compiledeps", "#{project.name}:testdeps"]}
524
- end
525
- end
526
-
527
- # Sorts the dependencies in #deps replacing the old order.
528
- # Sorting is done as follows:
529
- # 1. all dependencies that belong to the project identified by #project_path,
530
- # .i.e. instrumented-classes, resources in the order the are contained in the array
531
- # 2. all ivy dependencies identified by #ivy_deps
532
- # 3. all dependencies added automatically by buildr
533
- def sort_dependencies(deps, ivy_deps, project_path)
534
- old_deps = deps.dup
535
- belongs_to_project = /#{project_path}/
536
- deps.sort! do |a, b|
537
- a_belongs_to_project = belongs_to_project.match(a.to_s)
538
- b_belongs_to_project = belongs_to_project.match(b.to_s)
539
- a_ivy = ivy_deps.member? a
540
- b_ivy = ivy_deps.member? b
541
-
542
- if a_belongs_to_project && !b_belongs_to_project
543
- -1
544
- elsif !a_belongs_to_project && b_belongs_to_project
545
- 1
546
- elsif a_ivy && !b_ivy
547
- -1
548
- elsif !a_ivy && b_ivy
549
- 1
550
- else
551
- old_deps.index(a) <=> old_deps.index(b)
552
- end
553
- end
554
- end
555
-
556
- def add_manifest_to_distributeables(project)
557
- pkgs = project.packages.find_all { |pkg| ['jar', 'war', 'ear'].member? pkg.type.to_s }
558
- pkgs.each do |pkg|
559
- name = "#{pkg.name}manifest"
560
- task = project.task name => project.ivy.file_project.task('ivy:resolve') do
561
- pkg.with :manifest => pkg.manifest.merge(project.manifest.merge(project.ivy.manifest))
562
- info "Adding manifest entries to package '#{pkg.name}'"
563
- end
564
- project.task :build => task
565
- end
566
- end
567
-
568
- def add_prod_libs_to_distributeables(project)
569
- pkgs = project.packages.find_all { |pkg| ['war'].member? pkg.type.to_s }
570
- pkgs.each do |pkg|
571
- task = project.task "#{pkg.name}deps" => project.ivy.file_project.task('ivy:resolve') do
572
- includes = project.ivy.package_include
573
- excludes = project.ivy.package_exclude
574
- types = project.ivy.package_type
575
- confs = project.ivy.package_conf
576
- if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
577
- pkg.with :libs => [deps, pkg.libs].flatten
578
- info "Adding production libs from conf '#{confs.join(', ')}' to WAR '#{pkg.name}' in project '#{project.name}'"
579
- end
580
- end
581
- project.task :build => task
582
- end
583
-
584
- pkgs = project.packages.find_all { |pkg| ['ear'].member? pkg.type.to_s }
585
- pkgs.each do |pkg|
586
- task = project.task "#{pkg.name}deps" => project.ivy.file_project.task('ivy:resolve') do
587
- includes = project.ivy.package_include
588
- excludes = project.ivy.package_exclude
589
- types = project.ivy.package_type
590
- confs = project.ivy.package_conf
591
- if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
592
- pkg.add deps, :type => :lib, :path => ''
593
- info "Adding production libs from conf '#{confs.join(', ')}' to EAR '#{pkg.name}' in project '#{project.name}'"
594
- end
595
- end
596
- project.task :build => task
597
- end
598
- end
599
-
600
- def add_copy_tasks_for_publish(project)
601
- ivy_project = project.ivy.file_project
602
-
603
- project.packages.each do |pkg|
604
- target_file = project.ivy.publish[pkg] || ivy_project.ivy.publish[pkg] || File.basename(pkg.name).gsub(/-#{project.version}/, '')
605
- taskname = ivy_project.path_to(ivy_project.ivy.publish_from, target_file)
606
- if taskname != pkg.name
607
- ivy_project.file taskname => pkg.name do
608
- verbose "Ivy copying '#{pkg.name}' to '#{taskname}' for publishing"
609
- FileUtils.mkdir File.dirname(taskname) unless File.directory?(File.dirname(taskname))
610
- FileUtils.cp pkg.name, taskname
611
- end
612
- end
613
- ivy_project.task 'ivy:publish' => taskname
614
- end
615
- end
616
- end
617
-
618
- # Returns the +ivy+ configuration for the project. Use this to configure Ivy.
619
- # see IvyConfig for more details about configuration options.
620
- def ivy
621
- @ivy_config ||= IvyConfig.new(self)
622
- end
623
-
624
- first_time do
625
- namespace 'ivy' do
626
- desc 'Resolves the ivy dependencies'
627
- task :resolve
628
-
629
- desc 'Publish the artifacts to ivy repository as defined by environment'
630
- task :publish
631
-
632
- desc 'Creates a dependency report for the project'
633
- task :report
634
-
635
- desc 'Clean the local Ivy cache and the local ivy repository'
636
- task :clean
637
- end
638
- end
639
-
640
- after_define do |project|
641
- if project.ivy.enabled?
642
- IvyExtension.add_ivy_deps_to_java_tasks(project)
643
- IvyExtension.add_manifest_to_distributeables(project)
644
- IvyExtension.add_prod_libs_to_distributeables(project)
645
- IvyExtension.add_copy_tasks_for_publish(project)
646
-
647
- namespace 'ivy' do
648
- task :configure do
649
- project.ivy.configure
650
- end
651
-
652
- task :clean => :configure do
653
- # TODO This is redundant, refactor ivy_ant_wrap and this to use a single config object
654
- rm_rf project.path_to(:reports, 'ivy')
655
- project.ivy.cleancache
656
- end
657
-
658
- task :resolve => "#{project.name}:ivy:configure" do
659
- project.ivy.__resolve__
660
- end
661
-
662
- task :report => "#{project.name}:ivy:resolve" do
663
- project.ivy.report
664
- end
665
-
666
- task :publish => "#{project.name}:ivy:resolve" do
667
- project.ivy.__publish__
668
- end
669
- end
670
- end
671
- end
672
- end
673
-
674
- # Global targets that are not bound to a project
675
- namespace 'ivy' do
676
- task :clean do
677
- Buildr.projects.find_all{ |p| p.ivy.own_file? }.each do |project|
678
- project.task('ivy:clean').invoke
679
- end
680
- end
681
-
682
- task :resolve do
683
- info "Resolving all distinct ivy files"
684
- Buildr.projects.find_all{ |p| p.ivy.own_file? }.each do |project|
685
- project.task('ivy:resolve').invoke
686
- end
687
- end
688
-
689
- task :publish => :package do
690
- info "Publishing all distinct ivy files"
691
- Buildr.projects.find_all{ |p| p.ivy.own_file? }.each do |project|
692
- project.task('ivy:publish').invoke
693
- end
694
- end
695
- end
696
-
697
- class Buildr::Project # :nodoc:
698
- include IvyExtension
699
- end
700
- end
701
- end
1
+ require 'ivy4r'
2
+
3
+ module Buildr
4
+ module Ivy
5
+
6
+ class << self
7
+ def setting(*keys)
8
+ setting = Buildr.settings.build['ivy']
9
+ keys.each { |key| setting = setting[key] unless setting.nil? }
10
+ setting
11
+ end
12
+ end
13
+
14
+ class IvyConfig
15
+ TARGETS = [:compile, :test, :package]
16
+ TYPES = [:conf, :type, :include, :exclude]
17
+
18
+ attr_accessor :extension_dir, :resolved
19
+
20
+ attr_reader :post_resolve_task_list
21
+
22
+ # Hash of all artifacts to publish with mapping from artifact name to ivy publish name
23
+ attr_reader :publish_mappings
24
+
25
+ # Store the current project and initialize ivy ant wrapper
26
+ def initialize(project)
27
+ @project = project
28
+ if project.parent.nil?
29
+ @extension_dir = @project.base_dir
30
+ @post_resolve_task_list = []
31
+ else
32
+ @extension_dir = @project.parent.ivy.extension_dir
33
+ @base_ivy = @project.parent.ivy unless own_file?
34
+ end
35
+ @target_config = Hash.new do
36
+ |hash, key| hash[key] = {}
37
+ end
38
+ end
39
+
40
+ def enabled?
41
+ @enabled ||= Ivy.setting('enabled') || true
42
+ end
43
+
44
+ def own_file?
45
+ @own_file ||= File.exists?(@project.path_to(file))
46
+ end
47
+
48
+ # Returns the correct ivy4r instance to use, if project has its own ivy file uses the ivy file
49
+ # of project, if not uses the ivy file of parent project.
50
+ # Use this for low-level access to ivy functions as needed, i.e. in +post_resolve+
51
+ def ivy4r
52
+ unless @ivy4r
53
+ if own_file?
54
+ @ivy4r = ::Ivy4r.new(@project.ant('ivy'))
55
+ @ivy4r.lib_dir = lib_dir if lib_dir
56
+ @ivy4r.project_dir = @extension_dir
57
+ else
58
+ @ivy4r = @project.parent.ivy.ivy4r
59
+ end
60
+ end
61
+ @ivy4r
62
+ end
63
+
64
+ # Returns name of the project the ivy file belongs to.
65
+ def file_project
66
+ own_file? ? @project : @base_ivy.file_project
67
+ end
68
+
69
+ # Returns the artifacts for given configurations as array
70
+ # this is a post resolve task.
71
+ # the arguments are checked for the following:
72
+ # 1. if an Hash is given :conf is used for confs and :type is used for types
73
+ # 2. if exactly two arrays are given args[0] is used for confs and args[1] is used for types
74
+ # 3. if not exactly two arrays all args are used as confs
75
+ def deps(*args)
76
+ if args.size == 1 && args[0].kind_of?(Hash)
77
+ confs, types = [args[0][:conf]].flatten, [args[0][:type]].flatten
78
+ elsif args.size == 2 && args[0].kind_of?(Array) && args[1].kind_of?(Array)
79
+ confs, types = args[0], args[1]
80
+ else
81
+ confs, types = args.flatten, []
82
+ end
83
+
84
+ [confs, types].each do |t|
85
+ t.reject! {|c| c.nil? || c.blank? }
86
+ end
87
+
88
+ unless confs.empty?
89
+ pathid = "ivy.deps." + confs.join('.') + '.' + types.join('.')
90
+ params = {:conf => confs.join(','), :pathid => pathid}
91
+ params[:type] = types.join(',') unless types.nil? || types.size == 0
92
+
93
+ ivy4r.cachepath params
94
+ end
95
+ end
96
+
97
+ # Returns ivy info for configured ivy file using a new ivy4r instance.
98
+ def info
99
+ if @base_ivy
100
+ @base_ivy.info
101
+ else
102
+ ivy4r.settings :id => 'ivy.info.settingsref'
103
+ result = ivy4r.info :file => file, :settingsRef => 'ivy.info.settingsref'
104
+ @ivy4r = nil
105
+ result
106
+ end
107
+ end
108
+
109
+ # Configures the ivy instance with additional properties and loading the settings file if it was provided
110
+ def configure
111
+ if @base_ivy
112
+ @base_ivy.configure
113
+ else
114
+ unless @configured
115
+ ivy4r.property['ivy.status'] = status
116
+ ivy4r.property['ivy.home'] = home
117
+ properties.each {|key, value| ivy4r.property[key.to_s] = value }
118
+ @configured = ivy4r.settings :file => settings if settings
119
+ end
120
+ end
121
+ end
122
+
123
+ # Resolves the configured file once.
124
+ def __resolve__
125
+ if @base_ivy
126
+ @base_ivy.__resolve__
127
+ else
128
+ unless @resolved
129
+ @resolved = ivy4r.resolve :file => file
130
+ @project.send(:info, "Calling '#{post_resolve_tasks.size}' post_resolve tasks for '#{@project.name}'")
131
+ post_resolve_tasks.each { |p| p.call(self) }
132
+ end
133
+ end
134
+ end
135
+
136
+ # Returns the additional infos for the manifest file.
137
+ def manifest
138
+ if @base_ivy
139
+ @base_ivy.manifest
140
+ else
141
+ {
142
+ 'organisation' => @resolved['ivy.organisation'],
143
+ 'module' => @resolved['ivy.organisation'],
144
+ 'revision' => revision
145
+ }
146
+ end
147
+ end
148
+
149
+ # Creates the standard ivy dependency report
150
+ def report
151
+ ivy4r.report :todir => report_dir
152
+ end
153
+
154
+ # Cleans the ivy cache
155
+ def cleancache
156
+ ivy4r.cleancache
157
+ end
158
+
159
+
160
+ # Publishs the project as defined in ivy file if it has not been published already
161
+ def __publish__
162
+ if @base_ivy
163
+ @base_ivy.__publish__
164
+ else
165
+ unless @published
166
+ options = {:status => status, :pubrevision => revision, :artifactspattern => "#{publish_from}/[artifact].[ext]"}
167
+ options = publish_options * options
168
+ ivy4r.publish options
169
+ @published = true
170
+ end
171
+ end
172
+ end
173
+
174
+ def home
175
+ @ivy_home_dir ||= Ivy.setting('home.dir') || "#{@extension_dir}/ivy-home"
176
+ end
177
+
178
+ def lib_dir
179
+ @lib_dir ||= Ivy.setting('lib.dir')
180
+ end
181
+
182
+ def settings
183
+ @settings ||= Ivy.setting('settings.file') || "#{@extension_dir}/ant-scripts/ivysettings.xml"
184
+ end
185
+
186
+ def file
187
+ @ivy_file ||= Ivy.setting('ivy.file') || 'ivy.xml'
188
+ end
189
+
190
+ # Sets the revision to use for the project, this is useful for development revisions that
191
+ # have an appended timestamp or any other dynamic revisioning.
192
+ #
193
+ # To set a different revision this method can be used in different ways.
194
+ # 1. project.ivy.revision(revision) to set the revision directly
195
+ # 2. project.ivy.revision { |ivy| [calculate revision] } use the block for dynamic
196
+ # calculation of the revision. You can access ivy4r via <tt>ivy.ivy4r.[method]</tt>
197
+ def revision(*revision, &block)
198
+ raise "Invalid call with parameters and block!" if revision.size > 0 && block
199
+ if revision.empty? && block.nil?
200
+ if @revision_calc
201
+ @revision ||= @revision_calc.call(self)
202
+ else
203
+ @revision ||= @project.version
204
+ end
205
+ elsif block.nil?
206
+ raise "revision value invalid #{revision.join(', ')}" unless revision.size == 1
207
+ @revision = revision[0]
208
+ self
209
+ else
210
+ @revision_calc = block
211
+ self
212
+ end
213
+ end
214
+
215
+ # Sets the status to use for the project, this is useful for custom status handling
216
+ # like integration, alpha, gold.
217
+ #
218
+ # To set a different status this method can be used in different ways.
219
+ # 1. project.ivy.status(status) to set the status directly
220
+ # 2. project.ivy.status { |ivy| [calculate status] } use the block for dynamic
221
+ # calculation of the status. You can access ivy4r via <tt>ivy.ivy4r.[method]</tt>
222
+ def status(*status, &block)
223
+ raise "Invalid call with parameters and block!" if status.size > 0 && block
224
+ if status.empty? && block.nil?
225
+ if @status_calc
226
+ @status ||= @status_calc.call(self)
227
+ else
228
+ @status ||= Ivy.setting('status') || 'integration'
229
+ end
230
+ elsif status.empty? && block.nil?
231
+ raise "status value invalid #{status.join(', ')}" unless status.size == 1
232
+ @status = status[0]
233
+ self
234
+ else
235
+ @status_calc = block
236
+ self
237
+ end
238
+ end
239
+
240
+ # Sets the publish options to use for the project. The options are merged with the default
241
+ # options including value set via #publish_from and overwrite all of them.
242
+ #
243
+ # To set the options this method can be used in different ways.
244
+ # 1. project.ivy.publish_options(options) to set the options directly
245
+ # 2. project.ivy.publish_options { |ivy| [calculate options] } use the block for dynamic
246
+ # calculation of options. You can access ivy4r via <tt>ivy.ivy4r.[method]</tt>
247
+ def publish_options(*options, &block)
248
+ raise "Invalid call with parameters and block!" if options.size > 0 && block
249
+ if options.empty? && block.nil?
250
+ if @publish_options_calc
251
+ @publish_options ||= @publish_options_calc.call(self)
252
+ else
253
+ @publish_options ||= Ivy.setting('publish.options')
254
+ end
255
+ else
256
+ raise "Could not set 'publish_options' for '#{@project.name}' without own ivy file!" unless own_file?
257
+ if options.size > 0 && block.nil?
258
+ raise "publish options value invalid #{options.join(', ')}" unless options.size == 1
259
+ @publish_options = options[0]
260
+ self
261
+ else
262
+ @publish_options_calc = block
263
+ self
264
+ end
265
+ end
266
+ end
267
+
268
+ # Sets the additional properties for the ivy process use a Hash with the properties to set.
269
+ def properties(*properties)
270
+ if properties.empty?
271
+ @properties ||= {}
272
+ else
273
+ raise "properties value invalid #{properties.join(', ')}" unless properties.size == 1
274
+ @properties = properties[0]
275
+ self
276
+ end
277
+ end
278
+
279
+ # Sets the local repository for ivy files
280
+ def local_repository(*local_repository)
281
+ if local_repository.empty?
282
+ if own_file?
283
+ @local_repository ||= Ivy.setting('local.repository.dir') || "#{home}/repository"
284
+ else
285
+ @project.parent.ivy.local_repository
286
+ end
287
+ else
288
+ raise "Could not set 'local_repository' for '#{@project.name}' without own ivy file!" unless own_file?
289
+ raise "local_repository value invalid #{local_repository.join(', ')}" unless local_repository.size == 1
290
+ @local_repository = local_repository[0]
291
+ self
292
+ end
293
+ end
294
+
295
+ # :call-seq:
296
+ # ivy.publish(package(:jar) => 'new_name_without_version_number.jar')
297
+ # #deprecated! ivy.name(package(:jar) => 'new_name_without_version_number.jar')
298
+ #
299
+ # Maps a package to a different name for publishing. This name is used instead of the default name
300
+ # for publishing use a hash with the +package+ as key and the newly mapped name as value. I.e.
301
+ # <tt>ivy.name(package(:jar) => 'new_name_without_version_number.jar')</tt>
302
+ # Note that this method is additive, a second call adds the names to the first.
303
+ def publish(*publish_mappings)
304
+ if publish_mappings.empty?
305
+ @publish_mappings ||= {}
306
+ else
307
+ raise "publish_mappings value invalid #{publish_mappings.join(', ')}" unless publish_mappings.size == 1
308
+ @publish_mappings = @publish_mappings ? @publish_mappings + publish_mappings[0] : publish_mappings[0].dup
309
+ self
310
+ end
311
+ end
312
+
313
+ def name(*args)
314
+ puts "name(*args) is deprecated use publish(*args)!"
315
+ publish(*args)
316
+ end
317
+
318
+ # Sets the directory to publish artifacts from.
319
+ def publish_from(*publish_dir)
320
+ if publish_dir.empty?
321
+ if own_file?
322
+ @publish_from ||= Ivy.setting('publish.from') || @project.path_to(:target)
323
+ else
324
+ @project.parent.ivy.publish_from
325
+ end
326
+ else
327
+ raise "Could not set 'publish_from' for '#{@project.name}' without own ivy file!" unless own_file?
328
+ raise "publish_from value invalid #{publish_dir.join(', ')}" unless publish_dir.size == 1
329
+ @publish_from = publish_dir[0]
330
+ self
331
+ end
332
+ end
333
+
334
+ # Sets the directory to create dependency reports in.
335
+ def report_dir(*report_dir)
336
+ if report_dir.empty?
337
+ if own_file?
338
+ @report_dir ||= Ivy.setting('report.dir') || @project.path_to(:reports, 'ivy')
339
+ else
340
+ @project.parent.ivy.report_dir
341
+ end
342
+ else
343
+ raise "Could not set 'report_dir' for '#{@project.name}' without own ivy file!" unless own_file?
344
+ raise "publish_from value invalid #{report_dir.join(', ')}" unless report_dir.size == 1
345
+ @report_dir = report_dir[0]
346
+ self
347
+ end
348
+ end
349
+
350
+ # Adds given block as post resolve action that is executed directly after #resolve has been called.
351
+ # Yields this ivy config object into block.
352
+ # <tt>project.ivy.post_resolve { |ivy| p "all deps:" + ivy.deps('all').join(", ") }</tt>
353
+ def post_resolve(&block)
354
+ post_resolve_tasks << block if block
355
+ end
356
+
357
+ # Filter artifacts for given configuration with provided filter values, this is a post resolve
358
+ # task like #deps.
359
+ # <tt>project.ivy.filter('server', 'client', :include => /b.*.jar/, :exclude => [/a\.jar/, /other.*\.jar/])</tt>
360
+ def filter(*confs)
361
+ filter = confs.last.kind_of?(Hash) ? confs.pop : {}
362
+ unless (filter.keys - (TYPES - [:conf])).empty?
363
+ raise ArgumentError, "Invalid filter use :include and/or :exclude only: given #{filter.keys.inspect}"
364
+ end
365
+ includes, excludes, types = filter[:include] || [], filter[:exclude] || [], filter[:type] || []
366
+
367
+ artifacts = deps(confs.flatten, types.flatten)
368
+ if artifacts
369
+ artifacts = artifacts.find_all do |lib|
370
+ lib = File.basename(lib)
371
+ includes = includes.reject {|i| i.nil? || i.blank? }
372
+ should_include = includes.empty? || includes.any? {|include| include === lib }
373
+ should_include && !excludes.any? {|exclude| exclude === lib}
374
+ end
375
+ end
376
+
377
+ artifacts
378
+ end
379
+
380
+ # :call-seq:
381
+ # for types:
382
+ # project.ivy.include(:compile => [/\.jar/, /\.gz/], :package => 'cglib.jar')
383
+ # project.ivy.exclude(:test => 'cglib.jar')
384
+ # project.ivy.conf(:compile => 'compile', :test => 'test', :package => 'prod')
385
+ # for targets:
386
+ # project.ivy.compile(:conf => 'compile', :exclude => /cglib.jar/)
387
+ # project.ivy.test(:conf => 'test')
388
+ # project.ivy.package(:conf => 'prod', :include => /.*.jar/, :exclude => /cglib.jar/)
389
+ # or verbose:
390
+ # project.ivy.compile_conf or project.ivy.conf_compile
391
+ # project.ivy.compile_include or project.ivy.include_compile
392
+ # the same for the other possible options.
393
+ #
394
+ # Uses #method_missing to handle the options.
395
+ # Generic handling of settings for +target+ and +type+. All calls in the form
396
+ # <tt>target_type({})</tt> or <tt>type_target({})</tt> are handled via this method see
397
+ # #TARGETS #TYPES for more information about valid targets and types.
398
+ def method_missing(methodname, *args, &block)
399
+ if block.nil? && valid_config_call?(methodname)
400
+ target, type = target(methodname), type(methodname)
401
+ if target && type
402
+ handle_variable(target, type, *args)
403
+ elsif target && args.size == 1 && args.last.kind_of?(Hash)
404
+ args[0].each { |type, value| handle_variable(target, type, *value) }
405
+ self
406
+ elsif type && args.size == 1 && args.last.kind_of?(Hash)
407
+ args[0].each { |target, value| handle_variable(target, type, *value) }
408
+ self
409
+ else
410
+ raise "Could not recognize config call for method '#{methodname}', args=#{args.inspect}"
411
+ end
412
+ else
413
+ super.method_missing(methodname, *args, &block)
414
+ end
415
+ end
416
+
417
+ private
418
+ def target(targets)
419
+ t = targets.to_s.split('_').find { |target| TARGETS.member? target.to_sym }
420
+ t ? t.to_sym : nil
421
+ end
422
+
423
+ def type(types)
424
+ t = types.to_s.split('_').find { |type| TYPES.member? type.to_sym }
425
+ t ? t.to_sym : nil
426
+ end
427
+
428
+ def valid_config_call?(method_name)
429
+ valid_calls = []
430
+ TYPES.each do|type|
431
+ TARGETS.each do|target|
432
+ valid_calls << type.to_s << target.to_s << "#{type}_#{target}" << "#{target}_#{type}"
433
+ end
434
+ end
435
+ valid_calls.member? method_name.to_s
436
+ end
437
+
438
+ # Sets a variable for given basename and type to given values. If values are empty returns the
439
+ # current value.
440
+ # I.e. <tt>handle_variable(:package, :include, /blua.*\.jar/, /da.*\.jar/)</tt>
441
+ def handle_variable(target, type, *values)
442
+ unless TARGETS.member?(target) && TYPES.member?(type)
443
+ raise ArgumentError, "Unknown config value for target #{target.inspect} and type #{type.inspect}"
444
+ end
445
+ if values.empty?
446
+ @target_config[target][type] ||= [Ivy.setting("#{target.to_s}.#{type.to_s}") || ''].flatten.uniq
447
+ else
448
+ @target_config[target][type] = [values].flatten.uniq
449
+ self
450
+ end
451
+ end
452
+
453
+ def post_resolve_tasks
454
+ @base_ivy ? @base_ivy.post_resolve_task_list : post_resolve_task_list
455
+ end
456
+ end
457
+
458
+ =begin rdoc
459
+ The Ivy Buildr extension adding the new tasks for ivy.
460
+
461
+ To use ivy in a +buildfile+ do something like:
462
+ ENV['BUILDR_EXT_DIR'] ||= '../Ivy'
463
+ require 'buildr/ivy_extension'
464
+ define 'ivy_project' do
465
+ [...]
466
+ ivy.compile_conf('compile').test_conf('test').package_conf('prod', 'server')
467
+ [...]
468
+ end
469
+
470
+ - This will add the +compile+ configuration to compile and test tasks
471
+ - Add the +test+ configuration to test compilation and execution
472
+ - include the artifacts from +prod+ and +server+ to any generated war or ear
473
+ - The ENV variable is needed to automatically configure the load path for ivy libs.
474
+ It assumes that you have the following dir structure <tt>[BUILDR_EXT_DIR]/ivy-home/jars</tt>
475
+
476
+ For more configuration options see IvyConfig.
477
+ =end
478
+ module IvyExtension
479
+ include Buildr::Extension
480
+
481
+ class << self
482
+
483
+ def add_ivy_deps_to_java_tasks(project)
484
+ resolve_target = project.ivy.file_project.task('ivy:resolve')
485
+ project.task :compiledeps => resolve_target do
486
+ includes = project.ivy.compile_include
487
+ excludes = project.ivy.compile_exclude
488
+ types = project.ivy.compile_type
489
+ confs = [project.ivy.compile_conf].flatten
490
+ if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
491
+ project.compile.with [deps, project.compile.dependencies].flatten
492
+ sort_dependencies(project.compile.dependencies, deps, project.path_to(''))
493
+ info "Ivy adding compile dependencies '#{confs.join(', ')}' to project '#{project.name}'"
494
+ end
495
+ end
496
+
497
+ project.task :compile => "#{project.name}:compiledeps"
498
+
499
+ project.task :testdeps => resolve_target do
500
+ includes = project.ivy.test_include
501
+ excludes = project.ivy.test_exclude
502
+ types = project.ivy.test_type
503
+ confs = [project.ivy.test_conf, project.ivy.compile_conf].flatten.uniq
504
+ if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
505
+ project.test.with [deps, project.test.dependencies].flatten
506
+ sort_dependencies(project.test.dependencies, deps, project.path_to(''))
507
+ sort_dependencies(project.test.compile.dependencies, deps, project.path_to(''))
508
+ info "Ivy adding test dependencies '#{confs.join(', ')}' to project '#{project.name}'"
509
+ end
510
+ end
511
+ project.task "test:compile" => "#{project.name}:testdeps"
512
+
513
+ project.task :javadocdeps => resolve_target do
514
+ confs = [project.ivy.test_conf, project.ivy.compile_conf].flatten.uniq
515
+ if deps = project.ivy.deps(confs)
516
+ project.javadoc.with deps
517
+ info "Ivy adding javadoc dependencies '#{confs.join(', ')}' to project '#{project.name}'"
518
+ end
519
+ end
520
+ project.task :javadoc => "#{project.name}:javadocdeps"
521
+
522
+ [project.task(:eclipse), project.task(:idea), project.task(:idea7x)].each do |task|
523
+ task.prerequisites.each{|p| p.enhance ["#{project.name}:compiledeps", "#{project.name}:testdeps"]}
524
+ end
525
+ end
526
+
527
+ # Sorts the dependencies in #deps replacing the old order.
528
+ # Sorting is done as follows:
529
+ # 1. all dependencies that belong to the project identified by #project_path,
530
+ # .i.e. instrumented-classes, resources in the order the are contained in the array
531
+ # 2. all ivy dependencies identified by #ivy_deps
532
+ # 3. all dependencies added automatically by buildr
533
+ def sort_dependencies(deps, ivy_deps, project_path)
534
+ old_deps = deps.dup
535
+ belongs_to_project = /#{project_path}/
536
+ deps.sort! do |a, b|
537
+ a_belongs_to_project = belongs_to_project.match(a.to_s)
538
+ b_belongs_to_project = belongs_to_project.match(b.to_s)
539
+ a_ivy = ivy_deps.member? a
540
+ b_ivy = ivy_deps.member? b
541
+
542
+ if a_belongs_to_project && !b_belongs_to_project
543
+ -1
544
+ elsif !a_belongs_to_project && b_belongs_to_project
545
+ 1
546
+ elsif a_ivy && !b_ivy
547
+ -1
548
+ elsif !a_ivy && b_ivy
549
+ 1
550
+ else
551
+ old_deps.index(a) <=> old_deps.index(b)
552
+ end
553
+ end
554
+ end
555
+
556
+ def add_manifest_to_distributeables(project)
557
+ pkgs = project.packages.find_all { |pkg| ['jar', 'war', 'ear'].member? pkg.type.to_s }
558
+ pkgs.each do |pkg|
559
+ name = "#{pkg.name}manifest"
560
+ task = project.task name => project.ivy.file_project.task('ivy:resolve') do
561
+ pkg.with :manifest => pkg.manifest.merge(project.manifest.merge(project.ivy.manifest))
562
+ info "Adding manifest entries to package '#{pkg.name}'"
563
+ end
564
+ project.task :build => task
565
+ end
566
+ end
567
+
568
+ def add_prod_libs_to_distributeables(project)
569
+ pkgs = project.packages.find_all { |pkg| ['war'].member? pkg.type.to_s }
570
+ pkgs.each do |pkg|
571
+ task = project.task "#{pkg.name}deps" => project.ivy.file_project.task('ivy:resolve') do
572
+ includes = project.ivy.package_include
573
+ excludes = project.ivy.package_exclude
574
+ types = project.ivy.package_type
575
+ confs = project.ivy.package_conf
576
+ if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
577
+ pkg.with :libs => [deps, pkg.libs].flatten
578
+ info "Adding production libs from conf '#{confs.join(', ')}' to WAR '#{pkg.name}' in project '#{project.name}'"
579
+ end
580
+ end
581
+ project.task :build => task
582
+ end
583
+
584
+ pkgs = project.packages.find_all { |pkg| ['ear'].member? pkg.type.to_s }
585
+ pkgs.each do |pkg|
586
+ task = project.task "#{pkg.name}deps" => project.ivy.file_project.task('ivy:resolve') do
587
+ includes = project.ivy.package_include
588
+ excludes = project.ivy.package_exclude
589
+ types = project.ivy.package_type
590
+ confs = project.ivy.package_conf
591
+ if deps = project.ivy.filter(confs, :type => types, :include => includes, :exclude => excludes)
592
+ pkg.add deps, :type => :lib, :path => ''
593
+ info "Adding production libs from conf '#{confs.join(', ')}' to EAR '#{pkg.name}' in project '#{project.name}'"
594
+ end
595
+ end
596
+ project.task :build => task
597
+ end
598
+ end
599
+
600
+ def add_copy_tasks_for_publish(project)
601
+ if project.ivy.own_file?
602
+ Buildr.projects.each do |current|
603
+ current.packages.each do |pkg|
604
+ target_file = current.ivy.name[pkg] || File.basename(pkg.name).gsub(/-#{project.version}/, '')
605
+ taskname = current.path_to(project.ivy.publish_from, target_file)
606
+ if taskname != pkg.name
607
+ project.file taskname => pkg.name do
608
+ verbose "Ivy copying '#{pkg.name}' to '#{taskname}' for publishing"
609
+ FileUtils.mkdir File.dirname(taskname) unless File.directory?(File.dirname(taskname))
610
+ FileUtils.cp pkg.name, taskname
611
+ end
612
+ end
613
+ project.task 'ivy:publish' => taskname
614
+ end
615
+ end
616
+ end
617
+ end
618
+ end
619
+
620
+ # Returns the +ivy+ configuration for the project. Use this to configure Ivy.
621
+ # see IvyConfig for more details about configuration options.
622
+ def ivy
623
+ @ivy_config ||= IvyConfig.new(self)
624
+ end
625
+
626
+ first_time do
627
+ namespace 'ivy' do
628
+ desc 'Resolves the ivy dependencies'
629
+ task :resolve
630
+
631
+ desc 'Publish the artifacts to ivy repository as defined by environment'
632
+ task :publish
633
+
634
+ desc 'Creates a dependency report for the project'
635
+ task :report
636
+
637
+ desc 'Clean the local Ivy cache and the local ivy repository'
638
+ task :clean
639
+ end
640
+ end
641
+
642
+ after_define do |project|
643
+ if project.ivy.enabled?
644
+ IvyExtension.add_ivy_deps_to_java_tasks(project)
645
+ IvyExtension.add_manifest_to_distributeables(project)
646
+ IvyExtension.add_prod_libs_to_distributeables(project)
647
+ IvyExtension.add_copy_tasks_for_publish(project)
648
+
649
+ namespace 'ivy' do
650
+ task :configure do
651
+ project.ivy.configure
652
+ end
653
+
654
+ task :clean => :configure do
655
+ # TODO This is redundant, refactor ivy_ant_wrap and this to use a single config object
656
+ rm_rf project.path_to(:reports, 'ivy')
657
+ project.ivy.cleancache
658
+ end
659
+
660
+ task :resolve => "#{project.name}:ivy:configure" do
661
+ project.ivy.__resolve__
662
+ end
663
+
664
+ task :report => "#{project.name}:ivy:resolve" do
665
+ project.ivy.report
666
+ end
667
+
668
+ task :publish => "#{project.name}:ivy:resolve" do
669
+ project.ivy.__publish__
670
+ end
671
+ end
672
+ end
673
+ end
674
+ end
675
+
676
+ # Global targets that are not bound to a project
677
+ namespace 'ivy' do
678
+ task :clean do
679
+ Buildr.projects.find_all{ |p| p.ivy.own_file? }.each do |project|
680
+ project.task('ivy:clean').invoke
681
+ end
682
+ end
683
+
684
+ task :resolve do
685
+ info "Resolving all distinct ivy files"
686
+ Buildr.projects.find_all{ |p| p.ivy.own_file? }.each do |project|
687
+ project.task('ivy:resolve').invoke
688
+ end
689
+ end
690
+
691
+ task :publish => :package do
692
+ info "Publishing all distinct ivy files"
693
+ Buildr.projects.find_all{ |p| p.ivy.own_file? }.each do |project|
694
+ project.task('ivy:publish').invoke
695
+ end
696
+ end
697
+ end
698
+
699
+ class Buildr::Project # :nodoc:
700
+ include IvyExtension
701
+ end
702
+ end
703
+ end