mini_aether 0.0.1a-java
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|