jar-dependencies 0.6.0-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.
@@ -0,0 +1,408 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (C) 2014 Christian Meier
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ # this software and associated documentation files (the "Software"), to deal in
8
+ # the Software without restriction, including without limitation the rights to
9
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ # the Software, and to permit persons to whom the Software is furnished to do so,
11
+ # subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module Jars
25
+ # rubocop:disable Style/RedundantFreeze
26
+ MAVEN_SETTINGS = 'JARS_MAVEN_SETTINGS'.freeze
27
+ LOCAL_MAVEN_REPO = 'JARS_LOCAL_MAVEN_REPO'.freeze
28
+ # lock file to use
29
+ LOCK = 'JARS_LOCK'.freeze
30
+ # where the locally stored jars are search for or stored
31
+ HOME = 'JARS_HOME'.freeze
32
+ # skip the gem post install hook
33
+ SKIP = 'JARS_SKIP'.freeze
34
+ # skip Jars.lock mainly to run lock_jars
35
+ SKIP_LOCK = 'JARS_SKIP_LOCK'.freeze
36
+ # do not require any jars if set to false
37
+ REQUIRE = 'JARS_REQUIRE'.freeze
38
+ # @private
39
+ NO_REQUIRE = 'JARS_NO_REQUIRE'.freeze
40
+ # no more warnings on conflict. this still requires jars but will
41
+ # not warn. it is needed to load jars from (default) gems which
42
+ # do contribute to any dependency manager (maven, gradle, jbundler)
43
+ QUIET = 'JARS_QUIET'.freeze
44
+ # show resolver output
45
+ VERBOSE = 'JARS_VERBOSE'.freeze
46
+ # show jar-dependencies debug output
47
+ DEBUG = 'JARS_DEBUG'.freeze
48
+ # vendor jars inside gem when installing gem
49
+ VENDOR = 'JARS_VENDOR'.freeze
50
+ # string used when the version is unknown
51
+ UNKNOWN = 'unknown'.freeze
52
+ # rubocop:enable Style/RedundantFreeze
53
+
54
+ autoload :Classpath, 'jars/classpath'
55
+ autoload :MavenSettings, 'jars/maven_settings'
56
+ autoload :Mima, 'jars/mima'
57
+
58
+ @jars = {}
59
+ @jars_lock = false
60
+ @jars_locked = nil
61
+
62
+ class JarLoadError < LoadError; end
63
+
64
+ class << self
65
+ def lock_down(debug: nil, verbose: nil, **kwargs)
66
+ previous_debug = ENV[DEBUG]
67
+ previous_verbose = ENV[VERBOSE]
68
+ previous_skip_lock = ENV[SKIP_LOCK]
69
+ ENV[DEBUG] = debug.to_s unless debug.nil?
70
+ ENV[VERBOSE] = verbose.to_s unless verbose.nil?
71
+ ENV[SKIP_LOCK] = 'true'
72
+ require 'jars/lock_down' # do this lazy to keep things clean
73
+ Jars::LockDown.new.lock_down(kwargs.delete(:vendor_dir), **kwargs)
74
+ ensure
75
+ ENV[DEBUG] = previous_debug unless debug.nil?
76
+ ENV[VERBOSE] = previous_verbose unless verbose.nil?
77
+ ENV[SKIP_LOCK] = previous_skip_lock
78
+ end
79
+
80
+ if defined? JRUBY_VERSION
81
+ def to_prop(key)
82
+ key = key.tr('_', '.')
83
+ ENV_JAVA[(key.downcase!
84
+ key)] ||
85
+ ENV[(key.tr!('.', '_')
86
+ key.upcase!
87
+ key)]
88
+ end
89
+ else
90
+ def to_prop(key)
91
+ ENV[key.tr('.', '_').upcase]
92
+ end
93
+ end
94
+
95
+ def to_boolean(key)
96
+ return nil if (prop = to_prop(key)).nil?
97
+
98
+ prop.empty? || prop.eql?('true')
99
+ end
100
+
101
+ def skip?
102
+ to_boolean(SKIP)
103
+ end
104
+
105
+ def require?
106
+ if @require.nil?
107
+ if (require = to_boolean(REQUIRE)).nil?
108
+ no_require = to_boolean(NO_REQUIRE)
109
+ @require = no_require.nil? || !no_require
110
+ else
111
+ @require = require
112
+ end
113
+ end
114
+ @require
115
+ end
116
+ attr_writer :require
117
+
118
+ def quiet?
119
+ @quiet = to_boolean(QUIET) if @quiet.nil?
120
+ @quiet
121
+ end
122
+
123
+ # @deprecated
124
+ def no_more_warnings
125
+ @quiet = true
126
+ end
127
+
128
+ def locked_jar?(coordinate)
129
+ @jars_locked&.include?(coordinate)
130
+ end
131
+
132
+ def jarfile
133
+ ENV['JARFILE'] || ENV_JAVA['jarfile'] || ENV['JBUNDLER_JARFILE'] || ENV_JAVA['jbundler.jarfile'] || 'Jarfile'
134
+ end
135
+
136
+ def verbose?
137
+ verbose = to_boolean(VERBOSE)
138
+ verbose.nil? ? debug? : (verbose || !!debug?)
139
+ end
140
+
141
+ def debug?
142
+ to_boolean(DEBUG)
143
+ end
144
+
145
+ def vendor?
146
+ to_boolean(VENDOR)
147
+ end
148
+
149
+ def freeze_loading
150
+ self.require = false
151
+ end
152
+
153
+ def skip_lock?
154
+ to_prop(SKIP_LOCK) || false
155
+ end
156
+
157
+ def lock
158
+ @lock ||= to_prop(LOCK) || 'Jars.lock'
159
+ end
160
+
161
+ def jars_lock_from_class_loader
162
+ return unless defined?(JRUBY_VERSION)
163
+
164
+ JRuby::Util.class_loader_resources('Jars.lock')
165
+ end
166
+
167
+ def lock_path(basedir = nil)
168
+ return lock if File.exist?(lock)
169
+
170
+ basedir ||= '.'
171
+ ['.', 'jars', 'vendor/jars'].each do |dir|
172
+ file = File.join(basedir, dir, lock)
173
+ return file if File.exist?(file)
174
+ end
175
+ nil
176
+ end
177
+
178
+ def reset
179
+ instance_variables.each do |var|
180
+ next if var == :@jars_lock
181
+
182
+ instance_variable_set(var, nil)
183
+ end
184
+ Jars::MavenSettings.reset
185
+ @jars = {}
186
+ @jars_locked = nil
187
+ end
188
+
189
+ def maven_local_settings
190
+ Jars::MavenSettings.local_settings
191
+ end
192
+
193
+ def maven_user_settings
194
+ Jars::MavenSettings.user_settings
195
+ end
196
+
197
+ def maven_settings
198
+ Jars::MavenSettings.settings
199
+ end
200
+
201
+ def maven_global_settings
202
+ Jars::MavenSettings.global_settings
203
+ end
204
+
205
+ def local_maven_repo
206
+ @local_maven_repo ||= absolute(to_prop(LOCAL_MAVEN_REPO)) ||
207
+ detect_local_repository(maven_local_settings) ||
208
+ detect_local_repository(maven_user_settings) ||
209
+ detect_local_repository(maven_global_settings) ||
210
+ File.join(user_home, '.m2', 'repository')
211
+ end
212
+
213
+ def home
214
+ @home ||= absolute(to_prop(HOME)) || local_maven_repo
215
+ end
216
+
217
+ def require_jars_lock!(scope = :runtime)
218
+ urls = jars_lock_from_class_loader if to_prop(LOCK).nil?
219
+ pre_lock_jars = @jars.keys.dup
220
+ if urls && !urls.empty?
221
+ @jars_lock = true
222
+
223
+ done = []
224
+ while done != urls
225
+ urls.each do |url|
226
+ next if done.member?(url)
227
+
228
+ Jars.debug { "--- load jars from uri #{url}" }
229
+ classpath = Jars::Classpath.new(nil, "uri:#{url}")
230
+ classpath.require(scope)
231
+ done << url
232
+ end
233
+ end
234
+ elsif (jars_lock = Jars.lock_path)
235
+ Jars.debug { "--- load jars from #{jars_lock}" }
236
+ @jars_lock = jars_lock
237
+
238
+ classpath = Jars::Classpath.new(nil, jars_lock)
239
+ classpath.require(scope)
240
+ end
241
+
242
+ return unless @jars_lock
243
+
244
+ @jars_locked = (@jars.keys - pre_lock_jars).freeze
245
+ return if @jars_locked.empty?
246
+
247
+ Jars.debug do
248
+ loaded = @jars.collect { |k, v| "#{k}:#{v}" }
249
+ "--- loaded jars ---\n\t#{loaded.join("\n\t")}"
250
+ end
251
+ end
252
+
253
+ def setup(options = nil)
254
+ case options
255
+ when Symbol
256
+ require_jars_lock!(options)
257
+ when Hash
258
+ @home = options[:jars_home]
259
+ @lock = options[:jars_lock]
260
+ require_jars_lock!(options[:scope] || :runtime)
261
+ else
262
+ require_jars_lock!
263
+ end
264
+ end
265
+
266
+ def require_jars_lock
267
+ return if @jars_lock
268
+
269
+ require_jars_lock!
270
+ @jars_lock ||= true # rubocop:disable Naming/MemoizedInstanceVariableName
271
+ end
272
+
273
+ def mark_as_required(group_id, artifact_id, *classifier_version)
274
+ require_jar_with_block(group_id, artifact_id, *classifier_version) do
275
+ # ignore
276
+ end
277
+ end
278
+
279
+ def require_jar(group_id, artifact_id, *classifier_version)
280
+ require_jars_lock unless skip_lock?
281
+ if classifier_version.empty? && block_given?
282
+ classifier_version = [yield].compact
283
+ return mark_as_required(group_id, artifact_id, UNKNOWN) || false if classifier_version.empty?
284
+ end
285
+ require_jar_with_block(group_id, artifact_id, *classifier_version) do |gid, aid, version, classifier|
286
+ do_require(gid, aid, version, classifier)
287
+ end
288
+ end
289
+
290
+ def info(msg = nil, newline: true)
291
+ return if quiet?
292
+
293
+ msg = yield if msg.nil? && block_given?
294
+ Kernel.print("#{msg}#{"\n" if newline}")
295
+ end
296
+
297
+ def warn(msg = nil)
298
+ return if quiet? && !debug?
299
+
300
+ Kernel.warn(msg || yield)
301
+ end
302
+
303
+ def debug(msg = nil)
304
+ return unless debug?
305
+
306
+ msg = "#{msg.inspect}\n\t#{(msg.backtrace || []).join("\n\t")}" if msg.is_a?(Exception)
307
+ Kernel.warn(msg || yield)
308
+ end
309
+
310
+ def absolute(file)
311
+ File.expand_path(file) if file
312
+ end
313
+
314
+ def user_home
315
+ ENV['HOME'] || begin
316
+ user_home = Dir.home if Dir.respond_to?(:home)
317
+ user_home = ENV_JAVA['user.home'] if !user_home && Object.const_defined?(:ENV_JAVA)
318
+ user_home
319
+ end
320
+ end
321
+
322
+ private
323
+
324
+ def require_jar_with_block(group_id, artifact_id, *classifier_version)
325
+ version = classifier_version[-1]
326
+ classifier = classifier_version[-2]
327
+
328
+ coordinate = "#{group_id}:#{artifact_id}#{":#{classifier}" if classifier}"
329
+ if @jars.key?(coordinate)
330
+ if @jars[coordinate] == version
331
+ false
332
+ else
333
+ @jars[coordinate] # version of already registered jar
334
+ end
335
+ else
336
+ yield group_id, artifact_id, version, classifier
337
+ @jars[coordinate] = version
338
+ true
339
+ end
340
+ end
341
+
342
+ def detect_local_repository(settings)
343
+ return nil unless settings
344
+
345
+ doc = File.read(settings)
346
+ # TODO: filter out xml comments
347
+ local_repo = doc.sub(%r{</localRepository>.*}m, '').sub(/.*<localRepository>/m, '')
348
+ # replace maven like system properties embedded into the string
349
+ local_repo.gsub!(/\$\{[a-zA-Z.]+\}/) do |a|
350
+ ENV_JAVA[a[2..-2]] || a
351
+ end
352
+ local_repo = nil if local_repo.empty? || !File.exist?(local_repo)
353
+ local_repo
354
+ rescue => e
355
+ Jars.warn "error reading or parsing local settings from: #{settings}"
356
+ Jars.debug(e)
357
+ nil
358
+ end
359
+
360
+ def to_jar(group_id, artifact_id, version, classifier = nil)
361
+ file = +"#{group_id.tr('.', '/')}/#{artifact_id}/#{version}/#{artifact_id}-#{version}"
362
+ file << "-#{classifier}" if classifier
363
+ file << '.jar'
364
+ file
365
+ end
366
+
367
+ def do_require(group_id, artifact_id, version, classifier)
368
+ jar = to_jar(group_id, artifact_id, version, classifier)
369
+ # use jar from PWD/vendor/jars if exists
370
+ if File.exist?(vendor = File.join(Dir.pwd, 'vendor', 'jars', jar))
371
+ require vendor
372
+ # use jar from PWD/jars if exists
373
+ elsif File.exist?(local = File.join(Dir.pwd, 'jars', jar))
374
+ require local
375
+ # use jar from local repository if exists
376
+ elsif File.exist?(file = File.join(home, jar))
377
+ require file
378
+ else
379
+ # otherwise try to find it on the load path
380
+ require jar
381
+ end
382
+ rescue LoadError => e
383
+ Jars.warn "failed to load jar: #{jar} (#{e.message})"
384
+ Jars.debug(e)
385
+ raise JarLoadError, "failed to load jar: #{jar}; run `lock_jars` or reinstall the gem"
386
+ end
387
+ end
388
+ end
389
+
390
+ def require_jar(*args, &block)
391
+ return unless Jars.require?
392
+
393
+ result = Jars.require_jar(*args, &block)
394
+ if result.is_a?(String)
395
+ args << (yield || Jars::UNKNOWN) if args.size == 2 && block
396
+ coordinate = args[0..-2].join(':')
397
+ unless Jars.locked_jar?(coordinate)
398
+ Jars.warn do
399
+ "jar conflict: #{coordinate} already loaded with version #{result}; " \
400
+ "skipping requested version #{args[-1]}"
401
+ end
402
+ end
403
+ Jars.debug("\n\t#{caller.join("\n\t")}") if Jars.debug?
404
+ return false
405
+ end
406
+ Jars.debug { "jar registration: #{args.inspect}; loaded=#{result}" }
407
+ result
408
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (C) 2014 Christian Meier
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ # this software and associated documentation files (the "Software"), to deal in
8
+ # the Software without restriction, including without limitation the rights to
9
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ # the Software, and to permit persons to whom the Software is furnished to do so,
11
+ # subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ require 'jars/post_install_hook'
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jars/maven_exec'
4
+ require 'jars/lock'
5
+ require 'fileutils'
6
+
7
+ module Jars
8
+ class Classpath
9
+ # convenient method
10
+ def self.require(scope = nil)
11
+ new.require(scope)
12
+ end
13
+
14
+ # convenient method
15
+ def self.classpath(scope = nil)
16
+ new.classpath(scope)
17
+ end
18
+
19
+ # convenient method
20
+ def self.classpath_string(scope = nil)
21
+ new.classpath_string(scope)
22
+ end
23
+
24
+ def initialize(spec = nil, deps = nil)
25
+ @spec = spec
26
+ @deps = deps
27
+ end
28
+
29
+ def mvn
30
+ @mvn ||= MavenExec.new(@spec)
31
+ end
32
+
33
+ def workdir(dirname)
34
+ dir = File.join(mvn.basedir, dirname)
35
+ dir if File.directory?(dir)
36
+ end
37
+
38
+ def dependencies_list
39
+ if @deps.nil?
40
+ deps = Jars.lock_path(mvn.basedir)
41
+ @deps = deps if deps && File.exist?(deps)
42
+ end
43
+ @deps || resolve_dependencies
44
+ end
45
+ private :dependencies_list
46
+
47
+ DEPENDENCY_LIST = 'dependencies.list'
48
+ def resolve_dependencies
49
+ basedir = workdir('pkg') || workdir('target') || workdir('')
50
+ deps = File.join(basedir, DEPENDENCY_LIST)
51
+ mvn.resolve_dependencies_list(deps)
52
+ deps
53
+ end
54
+ private :resolve_dependencies
55
+
56
+ def require(scope = nil)
57
+ process(scope) do |jar|
58
+ if jar.scope == :system
59
+ Kernel.require jar.path
60
+ else
61
+ require_jar(*jar.gacv)
62
+ end
63
+ end
64
+ return unless scope.nil? || scope == :runtime
65
+
66
+ process(:provided) do |jar|
67
+ Jars.mark_as_required(*jar.gacv)
68
+ end
69
+ end
70
+
71
+ def classpath(scope = nil)
72
+ classpath = []
73
+ process(scope) do |jar|
74
+ classpath << jar.file
75
+ end
76
+ classpath
77
+ end
78
+
79
+ def process(scope, &block)
80
+ deps = dependencies_list
81
+ Lock.new(deps).process(scope, &block)
82
+ ensure
83
+ # just delete the temporary file if it exists
84
+ FileUtils.rm_f(DEPENDENCY_LIST)
85
+ end
86
+ private :process
87
+
88
+ def classpath_string(scope = nil)
89
+ classpath(scope).join(File::PATH_SEPARATOR)
90
+ end
91
+ end
92
+ end