abuild/apkbuild-gem-resolver.in

324 lines
7.2 KiB
Plaintext
Raw Normal View History

2014-12-08 15:56:25 +00:00
#!/usr/bin/ruby
# APKBUILD dependency resolver for RubyGems
# Copyright (C) 2014-2015 Kaarle Ritvanen
2014-12-08 15:56:25 +00:00
require 'augeas'
require 'optparse'
2014-12-08 15:56:25 +00:00
require 'rubygems/dependency'
require 'rubygems/resolver'
require 'rubygems/spec_fetcher'
class Package
@@packages = {}
2014-12-08 15:56:25 +00:00
def self.initialize testing
2014-12-08 15:56:25 +00:00
Augeas::open(nil, nil, Augeas::NO_MODL_AUTOLOAD) do |aug|
dir = Dir.pwd
aug.transform(:lens => 'Shellvars.lns', :incl => dir + '/*/ruby*/APKBUILD')
aug.load
apath = '/files' + dir
fail if aug.match("/augeas#{apath}//error").length > 0
repos = ['main']
repos << 'testing' if testing
for repo in repos
for pkg in aug.match "#{apath}/#{repo}/*"
Aport.new(aug, pkg) unless pkg.end_with? '/ruby'
2014-12-08 15:56:25 +00:00
end
end
Subpackage.initialize aug.get("#{apath}/main/ruby/APKBUILD/pkgver")
2014-12-08 15:56:25 +00:00
end
@@packages.each_value do |pkg|
pkg.depends do |dep|
dep.add_user pkg
2014-12-08 15:56:25 +00:00
end
end
end
def self.get name
pkg = @@packages[name]
raise 'Invalid package name: ' + name unless pkg
pkg
2014-12-08 15:56:25 +00:00
end
def initialize name, gem, version
@name = name
@gem = gem
@version = version
@depends = []
@users = []
@@packages[name] = self
2014-12-08 15:56:25 +00:00
end
def add_dependency name
@depends << name
2014-12-08 15:56:25 +00:00
end
attr_reader :gem, :name, :version
def depends
for dep in @depends
unless @@packages.has_key? dep
2014-12-08 15:56:25 +00:00
raise "Dependency for #{@name} does not exist: #{dep}"
end
yield @@packages[dep]
2014-12-08 15:56:25 +00:00
end
end
def users
for user in @users
yield user
end
end
def add_user user
@users << user
2014-12-08 15:56:25 +00:00
end
end
class Aport < Package
2014-12-08 15:56:25 +00:00
def initialize aug, path
name = path.split('/')[-1]
get = proc{ |param|
res = aug.get(path + '/APKBUILD/' + param)
raise param + ' not defined for ' + name unless res
res
}
super name, get.call('_gemname'), get.call('pkgver')
for dep in `echo #{get.call('depends')}`.split
# ruby-gems: workaround for v2.6
add_dependency dep if dep.start_with?('ruby-') && dep != 'ruby-gems'
2014-12-08 15:56:25 +00:00
end
end
end
class Subpackage < Package
RUBY_SUBPACKAGES = {
'2.0.0_p353' => {
'ruby-minitest' => ['minitest', '4.3.2'],
'ruby-rake' => ['rake', '0.9.6'],
'ruby-rdoc' => ['rdoc', '4.0.0', 'ruby-json']
},
'2.0.0_p481' => {
'ruby-minitest' => ['minitest', '4.3.2'],
'ruby-rake' => ['rake', '0.9.6'],
'ruby-rdoc' => ['rdoc', '4.0.0', 'ruby-json']
},
'2.1.5' => {
'ruby-json' => ['json', '1.8.1'],
'ruby-minitest' => ['minitest', '4.7.5'],
'ruby-rake' => ['rake', '10.1.0'],
'ruby-rdoc' => ['rdoc', '4.1.0', 'ruby-json']
},
'2.2.1' => {
# it's actually 0.4.3 but that version is not published on network
'ruby-io-console' => ['io-console', '0.4.2'],
'ruby-json' => ['json', '1.8.1'],
'ruby-minitest' => ['minitest', '5.4.3'],
'ruby-rake' => ['rake', '10.4.2'],
'ruby-rdoc' => ['rdoc', '4.2.0', 'ruby-json']
},
'2.2.2' => {
# it's actually 0.4.3 but that version is not published on network
'ruby-io-console' => ['io-console', '0.4.2'],
'ruby-json' => ['json', '1.8.1'],
'ruby-minitest' => ['minitest', '5.4.3'],
'ruby-rake' => ['rake', '10.4.2'],
'ruby-rdoc' => ['rdoc', '4.2.0', 'ruby-json']
}
}
@@subpackages = []
def self.initialize version
for name, attrs in RUBY_SUBPACKAGES[version]
gem, version, *deps = attrs
pkg = new name, gem, version
for dep in deps
pkg.add_dependency dep
end
@@subpackages << pkg
end
end
def self.each
for pkg in @@subpackages
yield pkg
end
end
end
2014-12-08 15:56:25 +00:00
class Update
def initialize
@gems = {}
@deps = []
end
def require_version name, version
gem = assign(Package.get(name).gem, name)
@deps << gem.dependency if gem.require_version version
2014-12-08 15:56:25 +00:00
end
def resolve
for pkg in Subpackage
2014-12-08 15:56:25 +00:00
require_version pkg.name, pkg.version unless @gems[pkg.gem]
end
def check_deps
@gems.clone.each_value do |gem|
gem.check_deps
end
end
check_deps
for req in Gem::Resolver.new(@deps).resolve
spec = req.spec
gem = @gems[spec.name]
gem.require_version spec.version.version if gem
end
check_deps
for name, gem in @gems
if gem.updated?
gem.package.users do |user|
2014-12-08 15:56:25 +00:00
ugem = @gems[user.gem]
if !ugem || ugem.package.name != user.name
2014-12-08 15:56:25 +00:00
Gem::Resolver.new(
[gem.dependency, Gem::Dependency.new(user.gem, user.version)]
).resolve
end
end
end
end
end
def each
@gems.each_value do |gem|
update = gem.update
yield update if update
2014-12-08 15:56:25 +00:00
end
end
def assign name, package
pkg = Package.get package
2014-12-08 15:56:25 +00:00
if @gems.has_key? name
gem = @gems[name]
return gem if pkg == gem.package
raise "Conflicting packages for gem #{name}: #{gem.package.name} and #{pkg.name}"
2014-12-08 15:56:25 +00:00
end
gem = PackagedGem.new self, name, pkg
2014-12-08 15:56:25 +00:00
@gems[name] = gem
gem
end
private
class PackagedGem
def initialize update, name, package
2014-12-08 15:56:25 +00:00
@update = update
@name = name
@package = package
2014-12-08 15:56:25 +00:00
end
attr_reader :package
2014-12-08 15:56:25 +00:00
def require_version version
if @version
return false if version == @version
raise "Conflicting versions for gem #{@name}: #{@version} and #{version}"
end
@version = version
true
end
def version
@version || @package.version
2014-12-08 15:56:25 +00:00
end
def updated?
version != @package.version
2014-12-08 15:56:25 +00:00
end
def dependency
Gem::Dependency.new(@name, version)
end
def check_deps
specs, errors = Gem::SpecFetcher::fetcher.spec_for_dependency(dependency)
raise "Invalid gem: #{@name}-#{version}" if specs.length == 0
fail if specs.length > 1
deps = specs[0][0].runtime_dependencies
@obsolete_deps = []
@package.depends do |dep|
2014-12-08 15:56:25 +00:00
gem = @update.assign(dep.gem, dep.name)
gem.check_deps
unless deps.reject! { |sdep| sdep.match? dep.gem, gem.version }
@obsolete_deps << dep.name
2014-12-08 15:56:25 +00:00
end
end
if deps.length > 0
raise 'Undeclared dependencies in ' + @package.name + deps.inject('') {
2014-12-08 15:56:25 +00:00
|s, dep| "#{s}\n#{dep.name} #{dep.requirements_list.join ' '}"
}
end
end
def update
updated? || @obsolete_deps.length > 0 ? (
{
:name => @package.name,
:version => version,
:obsolete_deps => @obsolete_deps.clone
}
) : nil
end
2014-12-08 15:56:25 +00:00
end
end
testing = false
OptionParser.new do |opts|
opts.on('-t', '--testing') do |t|
testing = t
end
end.parse! ARGV
Package.initialize testing
2014-12-08 15:56:25 +00:00
latest = {}
for source, gems in Gem::SpecFetcher::fetcher.available_specs(:latest)[0]
for gem in gems
latest[gem.name] = gem.version.version
end
end
update = Update.new
for arg in ARGV
match = /^(([^-]|-[^\d])+)(-(\d.*))?/.match arg
name = match[1]
update.require_version name, match[4] || latest[Package.get(name).gem]
2014-12-08 15:56:25 +00:00
end
update.resolve
for pkg in update
obs = pkg[:obsolete_deps]
obs = obs.length == 0 ? nil : " (obsolete dependencies: #{obs.join ', '})"
puts "#{pkg[:name]}-#{pkg[:version]}#{obs}"
2014-12-08 15:56:25 +00:00
end