ivy4r 0.9.14 → 0.9.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -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