mini_aether 0.0.1a-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +19 -0
- data/README.md +60 -0
- data/lib/mini_aether.rb +57 -0
- data/lib/mini_aether/bootstrap.rb +169 -0
- data/lib/mini_aether/resolver.rb +142 -0
- data/lib/mini_aether/spec.rb +125 -0
- data/lib/mini_aether/xml_parser.rb +39 -0
- data/test/mini_aether/bootstrap_test.rb +67 -0
- data/test/mini_aether/spec_test.rb +74 -0
- data/test/mini_aether_test.rb +43 -0
- data/test/test_helper.rb +3 -0
- metadata +125 -0
data/COPYING
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Patrick Mahoney
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
MiniAether
|
2
|
+
----------
|
3
|
+
|
4
|
+
JRuby wrapper around some parts of
|
5
|
+
[Aether](http://eclipse.org/aether/), the library underlying the
|
6
|
+
[Maven](http://maven.apache.org/) build and dependency management
|
7
|
+
tool.
|
8
|
+
|
9
|
+
* Bootstraps itself by downloading Aether components directly from the
|
10
|
+
Maven Central repository.
|
11
|
+
* Installs JARs into the standard Maven location `~/.m2/repository`.
|
12
|
+
* Loads Aether libs in separate JRuby with own classloader to avoid
|
13
|
+
contaminating the primary classloader.
|
14
|
+
|
15
|
+
I feel that JRuby/maven/rubygems integration is lacking in many areas.
|
16
|
+
There are various attempts to improve this. See
|
17
|
+
[JBunder](https://github.com/mkristian/jbundler) for example. So I'm
|
18
|
+
not sure what role MiniAether will play, if any.
|
19
|
+
|
20
|
+
require 'mini_aether'
|
21
|
+
|
22
|
+
MiniAether.setup do
|
23
|
+
group 'org.sonatype.aether' do
|
24
|
+
version '1.13.1' do
|
25
|
+
jar 'aether-api'
|
26
|
+
jar 'aether-connector-asynchttpclient'
|
27
|
+
jar 'aether-connector-file'
|
28
|
+
jar 'aether-impl'
|
29
|
+
jar 'aether-spi'
|
30
|
+
jar 'aether-util'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
jar 'com.ning:async-http-client:1.6.5'
|
35
|
+
jar 'org.jboss.netty:netty:3.2.5.Final'
|
36
|
+
|
37
|
+
# alternate syntax
|
38
|
+
dep(:group_id => 'org.slf4j',
|
39
|
+
:artifact_id => 'slf4j-api',
|
40
|
+
:version => '1.6.2',
|
41
|
+
:extension => 'jar') # 'jar' is the default if none is given
|
42
|
+
|
43
|
+
group 'org.apache.maven' do
|
44
|
+
version '3.0.4' do
|
45
|
+
jar 'maven-aether-provider'
|
46
|
+
jar 'maven-model'
|
47
|
+
jar 'maven-model-builder'
|
48
|
+
jar 'maven-repository-metadata'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
group 'org.codehaus.plexus' do
|
53
|
+
jar 'plexus-interpolation:1.14'
|
54
|
+
jar 'plexus-component-annotations:1.5.5'
|
55
|
+
jar 'plexus-utils:2.0.6'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
At this point, all those jars and their dependencies will be loaded
|
60
|
+
into the current JRuby.
|
data/lib/mini_aether.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'mini_aether/spec'
|
3
|
+
|
4
|
+
module MiniAether
|
5
|
+
MAVEN_CENTRAL_REPO = 'http://repo1.maven.org/maven2'.freeze
|
6
|
+
|
7
|
+
M2_SETTINGS = File.join(ENV['HOME'], '.m2', 'settings.xml').freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Create a new ScriptingContainer (Java object interface to a
|
11
|
+
# JRuby runtime) in SINGLETHREAD mode, and yield it to the block.
|
12
|
+
# Ensure the runtime is terminated after the block returns.
|
13
|
+
def with_ruby_container
|
14
|
+
scope = Java::OrgJrubyEmbed::LocalContextScope::SINGLETHREAD
|
15
|
+
c = Java::OrgJrubyEmbed::ScriptingContainer.new(scope)
|
16
|
+
begin
|
17
|
+
yield c
|
18
|
+
ensure
|
19
|
+
c.terminate
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Resolve +dependencies+, downloading from +sources+.
|
24
|
+
#
|
25
|
+
# Uses a separate JRuby runtime to avoid polluting the classpath
|
26
|
+
# with Aether and SLF4J classes.
|
27
|
+
#
|
28
|
+
# @param [Array<Hash>] dependencies
|
29
|
+
#
|
30
|
+
# @option dependency [String] :group_id the groupId of the artifact
|
31
|
+
# @option dependency [String] :artifact_id the artifactId of the artifact
|
32
|
+
# @option dependency [String] :version the version (or range of versions) of the artifact
|
33
|
+
# @option dependency [String] :extension default to 'jar'
|
34
|
+
#
|
35
|
+
# @param [Array<String>] repos urls to maven2 repositories
|
36
|
+
#
|
37
|
+
# @return [Array<String>] an array of paths to artifact files
|
38
|
+
# (likely jar files) satisfying the dependencies
|
39
|
+
def resolve(dependencies, sources)
|
40
|
+
with_ruby_container do |c|
|
41
|
+
c.put 'path', File.dirname(__FILE__).to_java
|
42
|
+
c.put 'deps', Marshal.dump(dependencies).to_java
|
43
|
+
c.put 'repos', Marshal.dump(sources).to_java
|
44
|
+
files = c.runScriptlet <<-EOF
|
45
|
+
$LOAD_PATH.push path
|
46
|
+
require 'mini_aether/resolver'
|
47
|
+
MiniAether::Resolver.new.resolve_foreign(deps, repos)
|
48
|
+
EOF
|
49
|
+
files.map { |f| f.to_s }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def setup(&block)
|
54
|
+
MiniAether::Spec.new(&block).require
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'mini_aether/spec'
|
2
|
+
require 'mini_aether/xml_parser'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'net/http'
|
5
|
+
require 'strscan'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
module MiniAether
|
10
|
+
module Bootstrap
|
11
|
+
System = Java::JavaLang::System
|
12
|
+
|
13
|
+
# Pre-resolved dependencies of mini_aether.
|
14
|
+
def dependencies
|
15
|
+
spec = Spec.new do
|
16
|
+
group 'org.sonatype.aether' do
|
17
|
+
version '1.13.1' do
|
18
|
+
jar 'aether-api'
|
19
|
+
jar 'aether-connector-asynchttpclient'
|
20
|
+
jar 'aether-connector-file'
|
21
|
+
jar 'aether-impl'
|
22
|
+
jar 'aether-spi'
|
23
|
+
jar 'aether-util'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
jar 'com.ning:async-http-client:1.6.5'
|
28
|
+
jar 'org.jboss.netty:netty:3.2.5.Final'
|
29
|
+
jar 'org.slf4j:slf4j-api:1.6.2'
|
30
|
+
|
31
|
+
begin
|
32
|
+
Java::OrgSlf4jImpl::StaticLoggerBinder
|
33
|
+
rescue NameError
|
34
|
+
# use logback when no slf4j backend exists
|
35
|
+
jar 'ch.qos.logback:logback-core:1.0.6'
|
36
|
+
jar 'ch.qos.logback:logback-classic:1.0.6'
|
37
|
+
# add dir to classpath since it contains logback.xml
|
38
|
+
$CLASSPATH << File.dirname(__FILE__)
|
39
|
+
end
|
40
|
+
|
41
|
+
group 'org.apache.maven' do
|
42
|
+
version '3.0.4' do
|
43
|
+
jar 'maven-aether-provider'
|
44
|
+
jar 'maven-model'
|
45
|
+
jar 'maven-model-builder'
|
46
|
+
jar 'maven-repository-metadata'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
group 'org.codehaus.plexus' do
|
51
|
+
jar 'plexus-interpolation:1.14'
|
52
|
+
jar 'plexus-component-annotations:1.5.5'
|
53
|
+
jar 'plexus-utils:2.0.6'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
spec.dependencies
|
58
|
+
end
|
59
|
+
|
60
|
+
# Interpolate variables like +${user.home}+ and +${env.HOME}+ from
|
61
|
+
# system properties and environment variables respectively.
|
62
|
+
def interpolate(str)
|
63
|
+
ret = ''
|
64
|
+
|
65
|
+
s = StringScanner.new(str)
|
66
|
+
pos = s.pos
|
67
|
+
|
68
|
+
while s.scan_until(/\$\{[^\s}]+\}/) # match ${stuff}
|
69
|
+
# add the pre_match, but only starting from previous position
|
70
|
+
ret << str.slice(pos, (s.pos - pos - s.matched.size))
|
71
|
+
|
72
|
+
# interpolate
|
73
|
+
var = s.matched.slice(2..-2)
|
74
|
+
ret << case var
|
75
|
+
when /^env\.(.*)/
|
76
|
+
ENV[$1] || ''
|
77
|
+
else
|
78
|
+
System.getProperty(var) || ''
|
79
|
+
end
|
80
|
+
|
81
|
+
pos = s.pos
|
82
|
+
end
|
83
|
+
ret << s.rest
|
84
|
+
|
85
|
+
ret
|
86
|
+
end
|
87
|
+
|
88
|
+
def local_repository_path
|
89
|
+
default_local_repo_path =
|
90
|
+
File.join(System.getProperty('user.home'), '.m2', 'repository')
|
91
|
+
|
92
|
+
if File.exists? M2_SETTINGS
|
93
|
+
xml = File.read M2_SETTINGS
|
94
|
+
begin
|
95
|
+
parser = XmlParser.new(xml)
|
96
|
+
parser.pull_to_path(:settings, :localRepository)
|
97
|
+
interpolate(parser.pull_text_until_end.strip)
|
98
|
+
rescue XmlParser::NotFoundError
|
99
|
+
default_local_repo_path
|
100
|
+
end
|
101
|
+
else
|
102
|
+
default_local_repo_path
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def ensure_dependency(dep)
|
107
|
+
path = jar_path(dep)
|
108
|
+
local_file = File.join(local_repository_path, path)
|
109
|
+
install(path, local_file) unless File.exists?(local_file)
|
110
|
+
local_file
|
111
|
+
end
|
112
|
+
|
113
|
+
# Load the required jar files, downloading them if necessary.
|
114
|
+
#
|
115
|
+
# Ignores any maven config regarding repositories and attempts a
|
116
|
+
# direct download from repo1.maven.org using Net::HTTP.
|
117
|
+
def bootstrap!
|
118
|
+
dependencies.each do |dep|
|
119
|
+
require ensure_dependency(dep)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def install(path, file, repo = MAVEN_CENTRAL_REPO)
|
124
|
+
print "installing #{File.basename(path)}... "
|
125
|
+
$stdout.flush
|
126
|
+
|
127
|
+
remote_base = File.dirname(path) + '/' + File.basename(path, File.extname(path))
|
128
|
+
local_dir = File.dirname(file)
|
129
|
+
local_base = File.join(local_dir, File.basename(file, File.extname(file)))
|
130
|
+
|
131
|
+
exts = [File.extname(path), '.pom', '.pom.sha1']
|
132
|
+
exts.each do |ext|
|
133
|
+
uri = URI("#{repo}/#{remote_base}#{ext}")
|
134
|
+
local_file = local_base + ext
|
135
|
+
|
136
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
137
|
+
request = Net::HTTP::Get.new uri.request_uri
|
138
|
+
|
139
|
+
http.request request do |response|
|
140
|
+
unless response.code == '200'
|
141
|
+
raise "#{response.code} #{response.message}: #{uri}"
|
142
|
+
end
|
143
|
+
|
144
|
+
FileUtils.mkdir_p local_dir
|
145
|
+
open local_file, 'w' do |io|
|
146
|
+
response.read_body do |chunk|
|
147
|
+
io.write chunk
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
print "#{ext} "
|
154
|
+
$stdout.flush
|
155
|
+
end
|
156
|
+
puts
|
157
|
+
end
|
158
|
+
|
159
|
+
def jar_path(dep)
|
160
|
+
group_id = dep[:group_id]
|
161
|
+
group_path = group_id.gsub('.', '/')
|
162
|
+
artifact_id = dep[:artifact_id]
|
163
|
+
version = dep[:version]
|
164
|
+
|
165
|
+
file_name = "#{artifact_id}-#{version}.jar"
|
166
|
+
"#{group_path}/#{artifact_id}/#{version}/#{file_name}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# This file should typically not be required by user code. Requiring
|
2
|
+
# this file pulls in many jars onto the classpath (by running
|
3
|
+
# bootstrap!). MiniAether.resolve creates a separate ruby runtime in
|
4
|
+
# a separate classloader which is uses to load this resolver file and
|
5
|
+
# perform the bootstrapping operation.
|
6
|
+
|
7
|
+
require 'mini_aether'
|
8
|
+
require 'mini_aether/bootstrap'
|
9
|
+
|
10
|
+
MiniAether.extend(MiniAether::Bootstrap)
|
11
|
+
MiniAether.bootstrap!
|
12
|
+
|
13
|
+
module MiniAether
|
14
|
+
class Resolver
|
15
|
+
RepositorySystem =
|
16
|
+
Java::OrgSonatypeAether::RepositorySystem
|
17
|
+
|
18
|
+
MavenRepositorySystemSession =
|
19
|
+
Java::OrgApacheMavenRepositoryInternal::MavenRepositorySystemSession
|
20
|
+
|
21
|
+
LocalRepository =
|
22
|
+
Java::OrgSonatypeAetherRepository::LocalRepository
|
23
|
+
|
24
|
+
DefaultServiceLocator =
|
25
|
+
Java::OrgApacheMavenRepositoryInternal::DefaultServiceLocator
|
26
|
+
|
27
|
+
RepositoryConnectorFactory =
|
28
|
+
Java::OrgSonatypeAetherSpiConnector::RepositoryConnectorFactory
|
29
|
+
|
30
|
+
AsyncRepositoryConnectorFactory =
|
31
|
+
Java::OrgSonatypeAetherConnectorAsync::AsyncRepositoryConnectorFactory
|
32
|
+
|
33
|
+
FileRepositoryConnectorFactory =
|
34
|
+
Java::OrgSonatypeAetherConnectorFile::FileRepositoryConnectorFactory
|
35
|
+
|
36
|
+
Artifact = Java::OrgSonatypeAetherUtilArtifact::DefaultArtifact
|
37
|
+
|
38
|
+
Dependency = Java::OrgSonatypeAetherGraph::Dependency
|
39
|
+
|
40
|
+
RemoteRepository = Java::OrgSonatypeAetherRepository::RemoteRepository
|
41
|
+
|
42
|
+
CollectRequest = Java::OrgSonatypeAetherCollection::CollectRequest
|
43
|
+
|
44
|
+
DependencyRequest = Java::OrgSonatypeAetherResolution::DependencyRequest
|
45
|
+
|
46
|
+
PreorderNodeListGenerator = Java::OrgSonatypeAetherUtilGraph::PreorderNodeListGenerator
|
47
|
+
|
48
|
+
# set up connectors for service locator
|
49
|
+
LOCATOR = DefaultServiceLocator.new
|
50
|
+
|
51
|
+
MiB_PER_BYTE = 1024.0*1024.0
|
52
|
+
|
53
|
+
|
54
|
+
services =
|
55
|
+
[AsyncRepositoryConnectorFactory,
|
56
|
+
FileRepositoryConnectorFactory].map do |klass|
|
57
|
+
obj = klass.new
|
58
|
+
obj.initService LOCATOR
|
59
|
+
obj
|
60
|
+
end
|
61
|
+
LOCATOR.setServices(RepositoryConnectorFactory.java_class, *services)
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
@logger = Java::OrgSlf4j::LoggerFactory.getLogger(self.class.to_s)
|
65
|
+
@system = LOCATOR.getService(RepositorySystem.java_class)
|
66
|
+
@session = MavenRepositorySystemSession.new
|
67
|
+
local_repo = LocalRepository.new(MiniAether.local_repository_path)
|
68
|
+
local_manager = @system.newLocalRepositoryManager(local_repo)
|
69
|
+
@session.setLocalRepositoryManager(local_manager)
|
70
|
+
end
|
71
|
+
|
72
|
+
def new_artifact(hash)
|
73
|
+
Artifact.new(hash[:group_id],
|
74
|
+
hash[:artifact_id],
|
75
|
+
hash[:extension] || 'jar',
|
76
|
+
hash[:version])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Load dumps of the +dep_hashes+ and +repos+ args using
|
80
|
+
# +Marshal.load+ and then call #resolve. Useful for when the
|
81
|
+
# dependencies and repositories are constructed under a different
|
82
|
+
# Java classloader.
|
83
|
+
def resolve_foreign(deps_data, repos_data)
|
84
|
+
resolve(Marshal.load(deps_data), Marshal.load(repos_data))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Resolve a set of dependencies +dep_hashes+ from repositories
|
88
|
+
# +repos+.
|
89
|
+
#
|
90
|
+
# @param [Array<Hash>] dep_hashes
|
91
|
+
#
|
92
|
+
# @option dep_hash [String] :group_id the groupId of the artifact
|
93
|
+
# @option dep_hash [String] :artifact_id the artifactId of the artifact
|
94
|
+
# @option dep_hash [String] :version the version (or range of versions) of the artifact
|
95
|
+
# @option dep_hash [String] :extension default to 'jar'
|
96
|
+
#
|
97
|
+
# @param [Array<String>] repos urls to maven2 repositories
|
98
|
+
#
|
99
|
+
# @return [Java::JavaUtil::List<Java::JavaIo::File>]
|
100
|
+
def resolve(dep_hashes, repos)
|
101
|
+
@logger.info 'resolving dependencies ({})', dep_hashes.size
|
102
|
+
collect_req = CollectRequest.new
|
103
|
+
|
104
|
+
dep_hashes.each do |hash|
|
105
|
+
dep = Dependency.new new_artifact(hash), 'compile'
|
106
|
+
collect_req.addDependency dep
|
107
|
+
@logger.info 'requested {}', dep
|
108
|
+
end
|
109
|
+
|
110
|
+
repos.each do |uri|
|
111
|
+
repo = RemoteRepository.new(uri.object_id.to_s, 'default', uri)
|
112
|
+
collect_req.addRepository repo
|
113
|
+
@logger.info 'added repository {}', repo
|
114
|
+
end
|
115
|
+
|
116
|
+
node = @system.collectDependencies(@session, collect_req).getRoot
|
117
|
+
|
118
|
+
dependency_req = DependencyRequest.new(node, nil)
|
119
|
+
@system.resolveDependencies(@session, dependency_req)
|
120
|
+
|
121
|
+
nlg = PreorderNodeListGenerator.new
|
122
|
+
node.accept nlg
|
123
|
+
files = nlg.getFiles
|
124
|
+
|
125
|
+
@logger.info "resolved with #{files.size} artifacts"
|
126
|
+
|
127
|
+
if @logger.isDebugEnabled
|
128
|
+
total_size = 0
|
129
|
+
nlg.getArtifacts(false).each do |artifact|
|
130
|
+
file = artifact.file
|
131
|
+
size = File.stat(artifact.file.absolute_path).size
|
132
|
+
total_size += size
|
133
|
+
|
134
|
+
@logger.debug("%0.2fMiB %s (%s)" % [size/MiB_PER_BYTE, artifact, file.name])
|
135
|
+
end
|
136
|
+
@logger.debug("%0.3f MiB total" % [total_size/MiB_PER_BYTE])
|
137
|
+
end
|
138
|
+
|
139
|
+
files
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'mini_aether'
|
2
|
+
|
3
|
+
module MiniAether
|
4
|
+
# A DSL to define and resolve dependencies. The Maven central
|
5
|
+
# repository is included by default.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
#
|
10
|
+
class Spec
|
11
|
+
class << self
|
12
|
+
def attr(*syms)
|
13
|
+
syms.each do |sym|
|
14
|
+
_attr(sym)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def _attr(sym)
|
19
|
+
define_method(sym) do |*args, &block|
|
20
|
+
value = if args && args.size > 0
|
21
|
+
args[0]
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
iv = "@#{sym}".to_sym
|
27
|
+
if value
|
28
|
+
raise ArgumentError 'no effect without block' unless block
|
29
|
+
orig = instance_variable_get iv
|
30
|
+
instance_variable_set iv, value
|
31
|
+
begin
|
32
|
+
block.call
|
33
|
+
ensure
|
34
|
+
instance_variable_set iv, orig
|
35
|
+
end
|
36
|
+
else
|
37
|
+
instance_variable_get iv
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :dependencies, :sources
|
44
|
+
|
45
|
+
attr :group, :version
|
46
|
+
|
47
|
+
def initialize(&block)
|
48
|
+
@sources = [MAVEN_CENTRAL_REPO]
|
49
|
+
@dependencies = []
|
50
|
+
|
51
|
+
if block_given?
|
52
|
+
instance_eval &block
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add a maven repository URL to the list of sources.
|
57
|
+
def source(uri)
|
58
|
+
sources << uri
|
59
|
+
end
|
60
|
+
|
61
|
+
# Declare a dependency using an explicit hash of parameters.
|
62
|
+
#
|
63
|
+
# @param [Hash] dependency
|
64
|
+
# @option dependency [String] :group_id the groupId of the artifact
|
65
|
+
# @option dependency [String] :artifact_id the artifactId of the artifact
|
66
|
+
# @option dependency [String] :version the version (or range of versions) of the artifact
|
67
|
+
# @option dependency [String] :extension defaults to 'jar'
|
68
|
+
def dep(dep_hash)
|
69
|
+
dependencies << dep_hash
|
70
|
+
end
|
71
|
+
|
72
|
+
def jar(coords)
|
73
|
+
dependencies << to_hash(coords)
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_hash(coords)
|
77
|
+
components = coords.split(':')
|
78
|
+
case components.size
|
79
|
+
when 1
|
80
|
+
{
|
81
|
+
:group_id => group,
|
82
|
+
:artifact_id => components[0],
|
83
|
+
:version => version
|
84
|
+
}
|
85
|
+
when 2
|
86
|
+
if components[1] =~ /^\d/
|
87
|
+
{
|
88
|
+
:group_id => group,
|
89
|
+
:artifact_id => components[0],
|
90
|
+
:version => components[1]
|
91
|
+
}
|
92
|
+
else
|
93
|
+
{
|
94
|
+
:group_id => components[0],
|
95
|
+
:artifact_id => components[1],
|
96
|
+
:version => version
|
97
|
+
}
|
98
|
+
end
|
99
|
+
when 3
|
100
|
+
{
|
101
|
+
:group_id => components[0],
|
102
|
+
:artifact_id => components[1],
|
103
|
+
:version => components[2]
|
104
|
+
}
|
105
|
+
else
|
106
|
+
raise ArgumentError, "don't understand '#{coords}'"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Resolve the dependencies using +MiniAether.resolve+.
|
111
|
+
#
|
112
|
+
# @return [Array<String>] an array of paths to artifact files
|
113
|
+
# (likely jar files) satisfying the dependencies
|
114
|
+
def resolve
|
115
|
+
MiniAether.resolve(dependencies, sources)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Resolve the dependencies using +MiniAether.resolve+ and then
|
119
|
+
# require each of them (assumes each is a JAR file that may be
|
120
|
+
# required into JRuby).
|
121
|
+
def require
|
122
|
+
resolve.each { |jar| Kernel.require jar }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rexml/parsers/pullparser'
|
2
|
+
|
3
|
+
module MiniAether
|
4
|
+
# Simple helper methods around REXML pull parser.
|
5
|
+
#
|
6
|
+
# @author Patrick Mahoney <pat@polycrystal.org>
|
7
|
+
# @since 0.0.1, 1-June-2012
|
8
|
+
class XmlParser < REXML::Parsers::PullParser
|
9
|
+
class NotFoundError < RuntimeError; end
|
10
|
+
|
11
|
+
# Pull events until the start of an element of tag name +name+ is
|
12
|
+
# reached.
|
13
|
+
def pull_to_start(name)
|
14
|
+
loop do
|
15
|
+
res = pull
|
16
|
+
raise NotFoundError if res.event_type == :end_document
|
17
|
+
next if res.start_element? && res[0] == name.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Pull events to the start of each name, following a path of
|
22
|
+
# +name1/name2/...+.
|
23
|
+
def pull_to_path(*names)
|
24
|
+
names.each { |name| pull_to_start name }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Pull all text nodes until the next +end_element+ event. Return
|
28
|
+
# the concatenation of text nodes.
|
29
|
+
def pull_text_until_end
|
30
|
+
texts = []
|
31
|
+
loop do
|
32
|
+
res = pull
|
33
|
+
break unless res.text?
|
34
|
+
texts << res[0]
|
35
|
+
end
|
36
|
+
texts.join
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'mini_aether/bootstrap'
|
4
|
+
|
5
|
+
module MiniAether
|
6
|
+
class BootstrapTest < MiniTest::Unit::TestCase
|
7
|
+
class InterpolateTest < MiniTest::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@bootstrap = Object.new
|
10
|
+
@bootstrap.extend(Bootstrap)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_passes_through_plain_string
|
14
|
+
strs = [
|
15
|
+
'hello',
|
16
|
+
'$ { nope } ',
|
17
|
+
'text${text',
|
18
|
+
'a a a a'
|
19
|
+
]
|
20
|
+
strs.each do |str|
|
21
|
+
assert_equal str, @bootstrap.interpolate(str)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_interpolates_env_vars
|
26
|
+
key = '__AETHER_INTERPOLATE_TEST__'
|
27
|
+
ENV[key] = 'value'
|
28
|
+
assert_equal 'value', @bootstrap.interpolate("${env.#{key}}")
|
29
|
+
assert_equal 'aaa value', @bootstrap.interpolate("aaa ${env.#{key}}")
|
30
|
+
assert_equal 'value aaa', @bootstrap.interpolate("${env.#{key}} aaa")
|
31
|
+
assert_equal 'value}', @bootstrap.interpolate("${env.#{key}}}")
|
32
|
+
assert_equal 'avaluea', @bootstrap.interpolate("a${env.#{key}}a")
|
33
|
+
|
34
|
+
assert_equal 'a value a value a', @bootstrap.interpolate("a ${env.#{key}} a ${env.#{key}} a")
|
35
|
+
ensure
|
36
|
+
ENV[key] = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_empty_string_when_no_env_var
|
40
|
+
assert_nil ENV['NO_SUCH_ENV_VAR']
|
41
|
+
assert_equal '', @bootstrap.interpolate("${env.NO_SUCH_ENV_VAR}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_interpolates_system_props
|
45
|
+
key = '__AETHER_INTERPOLATE_TEST__'
|
46
|
+
sys = Java::JavaLang::System
|
47
|
+
sys.setProperty(key, 'value')
|
48
|
+
|
49
|
+
assert_equal 'value', @bootstrap.interpolate("${#{key}}")
|
50
|
+
assert_equal 'aaa value', @bootstrap.interpolate("aaa ${#{key}}")
|
51
|
+
assert_equal 'value aaa', @bootstrap.interpolate("${#{key}} aaa")
|
52
|
+
assert_equal 'value}', @bootstrap.interpolate("${#{key}}}")
|
53
|
+
assert_equal 'avaluea', @bootstrap.interpolate("a${#{key}}a")
|
54
|
+
|
55
|
+
assert_equal 'a value a value a', @bootstrap.interpolate("a ${#{key}} a ${#{key}} a")
|
56
|
+
ensure
|
57
|
+
sys.clearProperty(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_empty_string_when_no_sys_prop
|
61
|
+
sys = Java::JavaLang::System
|
62
|
+
assert_nil sys.getProperty 'NO_SUCH_ENV_VAR'
|
63
|
+
assert_equal '', @bootstrap.interpolate("${NO_SUCH_ENV_VAR}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MiniAether
|
4
|
+
class SpecTest < MiniTest::Unit::TestCase
|
5
|
+
def test_hash_arg
|
6
|
+
spec = Spec.new do
|
7
|
+
dep(:group_id => 'group',
|
8
|
+
:artifact_id => 'artifact',
|
9
|
+
:version => '1')
|
10
|
+
end
|
11
|
+
|
12
|
+
hash = {
|
13
|
+
:group_id => 'group',
|
14
|
+
:artifact_id => 'artifact',
|
15
|
+
:version => '1'
|
16
|
+
}
|
17
|
+
assert_equal hash, spec.dependencies.first
|
18
|
+
end
|
19
|
+
|
20
|
+
def assert_deps(spec)
|
21
|
+
hash = {
|
22
|
+
:group_id => 'group',
|
23
|
+
:artifact_id => 'artifact',
|
24
|
+
:version => '1'
|
25
|
+
}
|
26
|
+
|
27
|
+
spec.dependencies.each do |dep|
|
28
|
+
assert_equal hash, dep
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_full_coords
|
33
|
+
assert_deps(Spec.new do
|
34
|
+
jar 'group:artifact:1'
|
35
|
+
end)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_default_group
|
39
|
+
assert_deps(Spec.new do
|
40
|
+
group 'group' do
|
41
|
+
jar 'artifact:1'
|
42
|
+
end
|
43
|
+
end)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_default_group_and_version
|
47
|
+
assert_deps(Spec.new do
|
48
|
+
group 'group' do
|
49
|
+
version '1' do
|
50
|
+
jar 'artifact'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_default_version
|
57
|
+
assert_deps(Spec.new do
|
58
|
+
version '1' do
|
59
|
+
jar 'group:artifact'
|
60
|
+
end
|
61
|
+
end)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_default_version2
|
65
|
+
assert_deps(Spec.new do
|
66
|
+
group 'bad' do
|
67
|
+
version '1' do
|
68
|
+
jar 'group:artifact'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
class MiniAetherTest < MiniTest::Unit::TestCase
|
6
|
+
System = Java::JavaLang::System
|
7
|
+
|
8
|
+
def test_bootstrap
|
9
|
+
orig_home = System.getProperty('user.home')
|
10
|
+
begin
|
11
|
+
Dir.mktmpdir do |dir|
|
12
|
+
System.setProperty('user.home', dir)
|
13
|
+
|
14
|
+
deps = [{
|
15
|
+
:group_id => 'net.jcip',
|
16
|
+
:artifact_id => 'jcip-annotations',
|
17
|
+
:version => '1.0'
|
18
|
+
}]
|
19
|
+
sources = [MiniAether::MAVEN_CENTRAL_REPO]
|
20
|
+
MiniAether.resolve(deps, sources)
|
21
|
+
|
22
|
+
files = Dir["#{dir}/.m2/**/*.jar"].map{|file| File.basename(file, '.jar')}
|
23
|
+
assert_includes files, 'aether-api-1.13.1'
|
24
|
+
end
|
25
|
+
ensure
|
26
|
+
System.setProperty('user.home', orig_home)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_resolves_dependencies
|
31
|
+
deps = [{
|
32
|
+
:group_id => 'org.jboss.resteasy',
|
33
|
+
:artifact_id => 'resteasy-jaxrs',
|
34
|
+
:version => '2.3.4.Final'
|
35
|
+
}]
|
36
|
+
sources = [MiniAether::MAVEN_CENTRAL_REPO]
|
37
|
+
jars = MiniAether.resolve(deps, sources).map{|file| File.basename(file, '.jar')}
|
38
|
+
|
39
|
+
assert_equal 12, jars.size
|
40
|
+
assert_includes jars, 'resteasy-jaxrs-2.3.4.Final'
|
41
|
+
assert_includes jars, 'httpclient-4.1.2'
|
42
|
+
end
|
43
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mini_aether
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: 5
|
5
|
+
version: 0.0.1a
|
6
|
+
platform: java
|
7
|
+
authors:
|
8
|
+
- Patrick Mahoney
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: minitest
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: !binary |-
|
21
|
+
MA==
|
22
|
+
none: false
|
23
|
+
requirement: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: !binary |-
|
28
|
+
MA==
|
29
|
+
none: false
|
30
|
+
prerelease: false
|
31
|
+
type: :development
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: rake
|
34
|
+
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: !binary |-
|
39
|
+
MA==
|
40
|
+
none: false
|
41
|
+
requirement: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: !binary |-
|
46
|
+
MA==
|
47
|
+
none: false
|
48
|
+
prerelease: false
|
49
|
+
type: :development
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: version
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: !binary |-
|
57
|
+
MA==
|
58
|
+
none: false
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: !binary |-
|
64
|
+
MA==
|
65
|
+
none: false
|
66
|
+
prerelease: false
|
67
|
+
type: :development
|
68
|
+
description: Resolves Aether artifact dependencies, downloads, and requires them.
|
69
|
+
email: pat@polycrystal.org
|
70
|
+
executables: []
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files:
|
73
|
+
- README.md
|
74
|
+
- COPYING
|
75
|
+
files:
|
76
|
+
- !binary |-
|
77
|
+
bGliL21pbmlfYWV0aGVyLnJi
|
78
|
+
- !binary |-
|
79
|
+
bGliL21pbmlfYWV0aGVyL2Jvb3RzdHJhcC5yYg==
|
80
|
+
- !binary |-
|
81
|
+
bGliL21pbmlfYWV0aGVyL3Jlc29sdmVyLnJi
|
82
|
+
- !binary |-
|
83
|
+
bGliL21pbmlfYWV0aGVyL3NwZWMucmI=
|
84
|
+
- !binary |-
|
85
|
+
bGliL21pbmlfYWV0aGVyL3htbF9wYXJzZXIucmI=
|
86
|
+
- !binary |-
|
87
|
+
dGVzdC90ZXN0X2hlbHBlci5yYg==
|
88
|
+
- !binary |-
|
89
|
+
dGVzdC9taW5pX2FldGhlcl90ZXN0LnJi
|
90
|
+
- !binary |-
|
91
|
+
dGVzdC9taW5pX2FldGhlci9zcGVjX3Rlc3QucmI=
|
92
|
+
- !binary |-
|
93
|
+
dGVzdC9taW5pX2FldGhlci9ib290c3RyYXBfdGVzdC5yYg==
|
94
|
+
- README.md
|
95
|
+
- COPYING
|
96
|
+
homepage: https://github.com/pmahoney/mini_aether
|
97
|
+
licenses: []
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
hash: 2
|
109
|
+
version: !binary |-
|
110
|
+
MA==
|
111
|
+
none: false
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - !binary |-
|
115
|
+
Pg==
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.3.1
|
118
|
+
none: false
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 1.8.24
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: wrapper around some parts of aether
|
125
|
+
test_files: []
|