buildr-dependency-extensions 0.2

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.
@@ -0,0 +1,30 @@
1
+ unless defined?(BuildrDepenencyExtensions::VERSION)
2
+ require File.join(File.dirname(__FILE__), 'lib', 'buildr-dependency-extensions', 'version')
3
+ end
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'buildr-dependency-extensions'
7
+ spec.version = BuildrDependencyExtensions::VERSION.dup
8
+ spec.author = 'John Shahid'
9
+ spec.email = "jvshahid@gmail.com"
10
+ spec.homepage = "https://github.com/jvshahid/transitive-buildr"
11
+ spec.summary = "A Buildr extension that enables transitive dependency resolution by default"
12
+
13
+ # Rakefile needs to create spec for both platforms (ruby and java), using the
14
+ # $platform global variable. In all other cases, we figure it out from RUBY_PLATFORM.
15
+ spec.platform = $platform || RUBY_PLATFORM[/java/] || 'ruby'
16
+
17
+ spec.files = Dir['{lib,spec}/**/*', '*.{gemspec}']
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.has_rdoc = false
21
+
22
+ # Tested against these dependencies.
23
+ spec.add_dependency 'rake', '0.8.7'
24
+ spec.add_dependency 'buildr', '>= 1.4.5'
25
+ spec.add_dependency 'xml-simple', '~> 1.0.12'
26
+ spec.add_dependency 'rspec-expectations', '2.1.0'
27
+ spec.add_dependency 'rspec-mocks', '2.1.0'
28
+ spec.add_dependency 'rspec-core', '2.1.0'
29
+ spec.add_dependency 'rspec', '2.1.0'
30
+ end
@@ -0,0 +1,9 @@
1
+ require 'buildr-dependency-extensions/helper'
2
+ require 'buildr-dependency-extensions/resolver'
3
+ require 'buildr-dependency-extensions/transitive-buildr'
4
+ require 'buildr-dependency-extensions/pom-generator'
5
+ require 'buildr-dependency-extensions/artifact'
6
+ require 'buildr-dependency-extensions/pom'
7
+ require 'buildr-dependency-extensions/dependency_caching'
8
+
9
+ include BuildrDependencyExtensions
@@ -0,0 +1,9 @@
1
+ require 'buildr/packaging/artifact.rb'
2
+
3
+ class Buildr::Artifact
4
+ def excludes(*artifacts)
5
+ @excludes ||= []
6
+ @excludes = @excludes + artifacts
7
+ self
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ module BuildrDependencyExtensions
4
+ class DependencyCaching
5
+ def initialize project
6
+ @project = project
7
+ end
8
+
9
+ def write_cache
10
+ dependencies_cache = {}
11
+ dependencies_cache['runtime'] = @project.run.classpath.
12
+ select {|dep| HelperFunctions.is_artifact?(dep)}.
13
+ map {|dep| dep.to_spec}
14
+
15
+ dependencies_cache['compile'] = @project.compile.dependencies.to_a.
16
+ select {|dep| HelperFunctions.is_artifact?(dep)}.
17
+ map {|dep| dep.to_spec}
18
+
19
+ dependencies_cache['test'] = @project.test.dependencies.to_a.
20
+ select {|dep| HelperFunctions.is_artifact?(dep)}.
21
+ map {|dep| dep.to_spec}
22
+
23
+ dependency_caching_filename = @project.path_to('dependency.cache')
24
+ FileUtils.mkdir_p(File.dirname(dependency_caching_filename))
25
+ f = File.new(dependency_caching_filename, 'w')
26
+ f.write(dependencies_cache.to_yaml)
27
+ f.close
28
+ end
29
+
30
+ def read_cache
31
+ begin
32
+ dependency_caching_filename = @project.path_to('dependency.cache')
33
+ dependdencies_cache = YAML.load_file(dependency_caching_filename)
34
+
35
+ runtime_dependencies = dependdencies_cache['runtime']
36
+ compile_dependencies = dependdencies_cache['compile']
37
+ test_dependencies = dependdencies_cache['test']
38
+
39
+ dependdencies_cache['runtime'] = runtime_dependencies.map {|dep| @project.artifact(dep)}
40
+ dependdencies_cache['compile'] = compile_dependencies.map {|dep| @project.artifact(dep)}
41
+ dependdencies_cache['test'] = test_dependencies.map {|dep| @project.artifact(dep)}
42
+ dependdencies_cache
43
+ rescue Errno::ENOENT
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,80 @@
1
+ require 'buildr/packaging/artifact'
2
+
3
+ module BuildrDependencyExtensions
4
+ module HelperFunctions
5
+ # Change the version number to 0 and invoke uniq on the resulting array to get a unique set of artifacts (ignoring the version number)
6
+ def HelperFunctions.get_unique_group_artifact set
7
+ new_set = set.map { |artifact| hash = Artifact.to_hash(artifact); hash[:version] = 0; Artifact.to_spec(hash) }
8
+ new_set.uniq
9
+ end
10
+
11
+ # returns all versions of artifact in original_set sorted using the Version sorting order
12
+ def HelperFunctions.get_all_versions artifact, original_set
13
+ original_artifact_hash = Artifact.to_hash(artifact)
14
+
15
+ new_set = original_set.select do |candidate_artifact|
16
+ candidate_hash = Artifact.to_hash(candidate_artifact)
17
+ candidate_hash[:group] == original_artifact_hash[:group] &&
18
+ candidate_hash[:id] == original_artifact_hash[:id] &&
19
+ candidate_hash[:type] == original_artifact_hash[:type] &&
20
+ candidate_hash[:classifier] == original_artifact_hash[:classifier]
21
+ end
22
+
23
+ new_set = new_set.uniq.sort.reverse
24
+ new_set.
25
+ map { |artifact| Artifact.to_hash(artifact)[:version] }.
26
+ map { |version_string| Version.new version_string}
27
+ end
28
+
29
+ def HelperFunctions.get_all_versions_sorted_by_depth artifact, original_set
30
+ original_artifact_hash = Artifact.to_hash(artifact)
31
+
32
+ new_set = original_set.select do |candidate_artifact|
33
+ candidate_hash = Artifact.to_hash(candidate_artifact)
34
+ candidate_hash[:group] == original_artifact_hash[:group] &&
35
+ candidate_hash[:id] == original_artifact_hash[:id] &&
36
+ candidate_hash[:type] == original_artifact_hash[:type] &&
37
+ candidate_hash[:classifier] == original_artifact_hash[:classifier]
38
+ end
39
+
40
+ new_set = new_set.uniq
41
+ # Sort using the version first, so in case of conflict we use the higher version
42
+ new_set = new_set.sort {|x,y| Version.new(x.to_hash[:version]) <=> Version.new(x.to_hash[:version])}.reverse
43
+ new_set = new_set.sort {|x,y| x.depth <=> y.depth}
44
+ new_set.
45
+ map { |artifact| Artifact.to_hash(artifact)[:version] }.
46
+ map { |version_string| Version.new version_string}
47
+ end
48
+
49
+ def HelperFunctions.is_artifact? task
50
+ task.respond_to?(:to_spec) && task.respond_to?(:to_hash)
51
+ end
52
+
53
+ end
54
+
55
+ # Parses the version string and provides a natural ordering for versions
56
+ class Version
57
+ include Comparable
58
+
59
+ attr_reader :version_array, :version_string
60
+
61
+ def initialize version_string
62
+ @version_string = version_string
63
+ @version_array = version_string.split('.')
64
+ end
65
+
66
+ def <=> other_version
67
+ @version_array <=> other_version.version_array
68
+ end
69
+
70
+ alias_method :eql?, :==
71
+
72
+ def hash
73
+ @version_string.hash
74
+ end
75
+
76
+ def to_s
77
+ @version_string
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,80 @@
1
+ require 'buildr/core/project'
2
+ require 'rubygems'
3
+ require 'xmlsimple'
4
+
5
+ module BuildrDependencyExtensions
6
+ module PomGenerator
7
+
8
+ include Extension
9
+
10
+ def self.extended(base)
11
+ class << base
12
+ def extra_pom_sections
13
+ @extra_pom_sections ||= {}
14
+ end
15
+ end
16
+ super
17
+ end
18
+
19
+ # We have to run the pom generator first before the dependencies are
20
+ # changed in the compile, test and run after_define
21
+ after_define(:compile => :'pom-generator')
22
+ after_define(:'pom-generator') do |project|
23
+ generate_pom project
24
+ end
25
+
26
+ module_function
27
+
28
+ def compile_dependencies project
29
+ project.compile.dependencies.select {|dep| HelperFunctions.is_artifact? dep}
30
+ end
31
+
32
+ def runtime_dependencies project
33
+ project.run.classpath.select {|dep| HelperFunctions.is_artifact? dep}
34
+ end
35
+
36
+ def test_dependencies project
37
+ project.test.dependencies.select {|dep| HelperFunctions.is_artifact? dep}
38
+ end
39
+
40
+ def generate_dependencies_hash dependencies, scope
41
+ dependencies.map do |dep|
42
+ { 'groupId' => dep.to_hash[:group],
43
+ 'artifactId' => dep.to_hash[:id],
44
+ 'version' => dep.to_hash[:version],
45
+ 'scope' => scope,
46
+ 'type' => dep.to_hash[:type]
47
+ }
48
+ end
49
+ end
50
+
51
+ def generate_pom project
52
+ compile_dependencies = compile_dependencies project
53
+ runtime_dependencies = runtime_dependencies project
54
+ test_dependencies = test_dependencies project
55
+
56
+ dependencies_hashes = generate_dependencies_hash compile_dependencies, 'compile'
57
+ dependencies_hashes += generate_dependencies_hash runtime_dependencies, 'runtime'
58
+ dependencies_hashes += generate_dependencies_hash test_dependencies, 'test'
59
+
60
+ pom_hash = {
61
+ 'modelVersion' => '4.0.0',
62
+ 'groupId' => project.group,
63
+ 'artifactId' => project.name,
64
+ 'version' => project.version,
65
+ 'dependencies' => {'dependency' => dependencies_hashes.to_a}
66
+ }
67
+
68
+ project.extra_pom_sections.each {|key, value| pom_hash[key] = value}
69
+
70
+ my_pom = file(project.path_to(:target, 'pom.xml')) do |f|
71
+ FileUtils.mkdir_p(File.dirname(f.name)) unless f.exist?
72
+ File.open(f.name, 'w') do |file|
73
+ file.write(XmlSimple.xml_out(pom_hash, {'RootName' => 'project', 'NoAttr' => true}))
74
+ end
75
+ end
76
+
77
+ project.package.pom.from my_pom
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,32 @@
1
+ require 'buildr/java/pom'
2
+
3
+ class Buildr::POM
4
+ def declared_dependencies(scopes)
5
+ #try to cache dependencies also
6
+ @declared_depends_for_scopes ||= {}
7
+ unless depends = @declared_depends_for_scopes[scopes]
8
+ declared = project["dependencies"].first["dependency"] rescue nil
9
+ depends = (declared || []).reject { |dep| value_of(dep["optional"]) =~ /true/ }
10
+ depends = depends.map do |dep|
11
+ spec = pom_to_hash(dep, properties)
12
+ apply = managed(spec)
13
+ spec = apply.merge(spec) if apply
14
+
15
+ #calculate transitive dependencies
16
+ if scopes.include?(spec[:scope])
17
+ exclusions = dep["exclusions"]["exclusion"] rescue nil
18
+ artifact = Artifact.to_spec(spec)
19
+
20
+ if exclusions
21
+ excuded_artifacts = exclusions.map {|exclusion| artifact("#{exclusion['groupId']}:#{exclusion['artifactId']}:jar:1.0")}
22
+ artifact.excludes(*excuded_artifacts)
23
+ end
24
+ end
25
+ artifact
26
+ end
27
+ depends = depends.compact
28
+ @declared_depends_for_scopes[scopes] = depends
29
+ end
30
+ depends
31
+ end
32
+ end
@@ -0,0 +1,44 @@
1
+ module BuildrDependencyExtensions
2
+ class ResolverBase
3
+ def initialize
4
+ @hash = {}
5
+ end
6
+
7
+ def resolve_from_hash artifact
8
+ @hash[artifact]
9
+ end
10
+
11
+ def resolved artifact, version
12
+ # @hash[artifact] = version
13
+ end
14
+ end
15
+
16
+ class HighestVersionConflictResolver < ResolverBase
17
+ def resolve artifact, all_artifacts
18
+ version = resolve_from_hash artifact
19
+ if version
20
+ version
21
+ else
22
+ all_versions = HelperFunctions.get_all_versions(artifact, all_artifacts).uniq
23
+ if all_versions.size > 1
24
+ puts $terminal.color("Warning: found versions #{all_versions.join(', ')} for artifact #{artifact}. Choosing #{all_versions[0]}", :yellow)
25
+ end
26
+ resolved artifact, all_versions[0]
27
+ all_versions[0]
28
+ end
29
+ end
30
+ end
31
+
32
+ class MavenVersionConflictResolver < ResolverBase
33
+ def resolve artifact, all_artifacts
34
+ version = resolve_from_hash artifact
35
+ if version
36
+ version
37
+ else
38
+ all_versions = HelperFunctions.get_all_versions_sorted_by_depth(artifact, all_artifacts).uniq
39
+ resolved artifact, all_versions[0]
40
+ all_versions[0]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,125 @@
1
+ require 'buildr/core/project'
2
+ require 'yaml'
3
+
4
+ module BuildrDependencyExtensions
5
+ module TransitiveDependencies
6
+
7
+ @dependency_pom_cache = {}
8
+
9
+ include Extension
10
+
11
+ def self.extended(base)
12
+ class << base
13
+
14
+ attr_accessor :transitive_scopes
15
+ attr_accessor :cache_dependencies
16
+
17
+ def conflict_resolver
18
+ # @conflict_resolver ||= HighestVersionConflictResolver.new
19
+ @conflict_resolver ||= MavenVersionConflictResolver.new
20
+ end
21
+ end
22
+ super
23
+ end
24
+
25
+ after_define(:'transitive-dependencies' => :run) do |project|
26
+ now = Time.now
27
+ if project.transitive_scopes
28
+ dependency_caching = DependencyCaching.new(project)
29
+ dependency_cache = dependency_caching.read_cache
30
+ unless dependency_cache && project.cache_dependencies
31
+ resolve_compile_dependencies project if project.transitive_scopes.include? :compile
32
+ resolve_runtime_dependencies project if project.transitive_scopes.include? :run
33
+ resolve_test_dependencies project if project.transitive_scopes.include? :test
34
+ dependency_caching.write_cache
35
+ else
36
+ if project.transitive_scopes.include? :run
37
+ project.run.classpath = project.run.classpath.reject {|dependency| HelperFunctions.is_artifact?(dependency)}
38
+ project.run.classpath += dependency_cache['runtime']
39
+ end
40
+ if project.transitive_scopes.include? :compile
41
+ project.compile.dependencies = project.compile.dependencies.reject {|dependency| HelperFunctions.is_artifact?(dependency)}
42
+ project.compile.dependencies += dependency_cache['compile']
43
+ end
44
+ if project.transitive_scopes.include? :test
45
+ project.test.dependencies = project.test.dependencies.reject {|dependency| HelperFunctions.is_artifact?(dependency)}
46
+ project.test.dependencies += dependency_cache['test']
47
+
48
+ project.test.compile.dependencies = project.test.compile.dependencies.reject {|dependency| HelperFunctions.is_artifact?(dependency)}
49
+ project.test.compile.dependencies += dependency_cache['test']
50
+ end
51
+ end
52
+ end
53
+ trace "Adding transitive dependencies took #{Time.now - now} seconds"
54
+ end
55
+
56
+ module_function
57
+
58
+ def resolve_compile_dependencies project
59
+ original_file_tasks = project.compile.dependencies.reject {|dep| HelperFunctions.is_artifact? dep }
60
+ original_dependencies = project.compile.dependencies.select {|dep| HelperFunctions.is_artifact? dep }
61
+ new_dependencies = []
62
+ original_dependencies.each do |dependency|
63
+ add_dependency project, new_dependencies, dependency, [nil, "compile"]
64
+ end
65
+ new_dependencies = resolve_conflicts(project, new_dependencies.uniq)
66
+ project.compile.dependencies = new_dependencies + original_file_tasks
67
+ end
68
+
69
+ def resolve_runtime_dependencies project
70
+ original_file_tasks = project.run.classpath.reject {|dep| HelperFunctions.is_artifact? dep }
71
+ original_dependencies = project.run.classpath.select {|dep| HelperFunctions.is_artifact? dep }
72
+ new_dependencies = []
73
+ original_dependencies.each do |dependency|
74
+ add_dependency project, new_dependencies, dependency, [nil, "compile", "runtime"]
75
+ end
76
+ new_dependencies = resolve_conflicts(project, new_dependencies.uniq)
77
+ project.run.classpath = new_dependencies + original_file_tasks
78
+ end
79
+
80
+ def resolve_test_dependencies project
81
+ original_file_tasks = project.test.dependencies.reject {|dep| HelperFunctions.is_artifact? dep }
82
+ original_test_compile_file_tasks = project.test.compile.dependencies.reject {|dep| HelperFunctions.is_artifact? dep }
83
+ original_dependencies = project.test.dependencies.select {|dep| HelperFunctions.is_artifact? dep }
84
+ new_dependencies = []
85
+ original_dependencies.each do |dependency|
86
+ add_dependency project, new_dependencies, dependency, [nil, "compile", "runtime"]
87
+ end
88
+ new_dependencies = resolve_conflicts(project, new_dependencies.uniq)
89
+ project.test.dependencies = new_dependencies + original_file_tasks
90
+ project.test.compile.dependencies = new_dependencies + original_test_compile_file_tasks
91
+ end
92
+
93
+ def add_dependency project, new_dependencies, dependency, scopes, depth = 0
94
+ scopes.each do |scope|
95
+ if (!@dependency_pom_cache[dependency])
96
+ @dependency_pom_cache[dependency] = POM.load(dependency.pom)
97
+ end
98
+ @dependency_pom_cache[dependency].declared_dependencies([scope]).each do |dep|
99
+ artifact = project.artifact(dep)
100
+ excludes = dependency.instance_variable_get(:@excludes) || []
101
+ matching_dependency = excludes.select do |excluded_dep|
102
+ excluded_dep.to_hash[:id] == artifact.to_hash[:id] &&
103
+ excluded_dep.to_hash[:group] == artifact.to_hash[:group]
104
+ end
105
+ add_dependency project, new_dependencies, artifact, scopes, (depth + 1) if matching_dependency.empty?
106
+ end
107
+ end
108
+ class << dependency
109
+ attr_accessor :depth
110
+ end
111
+ dependency.depth = depth
112
+ new_dependencies << dependency
113
+ end
114
+
115
+ def resolve_conflicts project, dependencies
116
+ unique_transitive_artifacts = HelperFunctions.get_unique_group_artifact(dependencies)
117
+ new_scope_artifacts = unique_transitive_artifacts.map do |artifact|
118
+ artifact_hash = Artifact.to_hash(artifact)
119
+ artifact_hash[:version] = project.conflict_resolver.resolve(artifact, dependencies)
120
+ project.artifact(Artifact.to_spec(artifact_hash))
121
+ end
122
+ new_scope_artifacts
123
+ end
124
+ end
125
+ end