#!/usr/bin/ruby -Eutf-8
|
|
# encoding: utf-8
|
|
#
|
|
# Find dependencies between ruby packages
|
|
#
|
|
# Must run inside a openwrt with all *ruby* packages installed
|
|
#
|
|
|
|
RUBY_SIMPLE_VERSION = RUBY_VERSION.split(".")[0..1].join(".")
|
|
failed = false
|
|
|
|
puts "Looking for installed ruby packages..."
|
|
packages=`opkg list-installed '*ruby*' | cut -d' ' -f 1`.split("\n")
|
|
|
|
puts "Looking for packages files..."
|
|
package_files=Hash.new { |h,k| h[k]=[] }
|
|
packages.each do
|
|
|pkg|
|
|
files=`opkg files "#{pkg}" | sed -e 1d`.split("\n")
|
|
package_files[pkg]=files if files
|
|
end
|
|
|
|
require_regex=/^require ["']([^"']+)["'].*/
|
|
require_regex_ignore=/^require ([a-zA-Z\$]|["']$|.*\/$)/
|
|
require_ignore=%w{drb/invokemethod16 foo rubygems/defaults/operating_system win32console java Win32API
|
|
builder/xchar json/pure simplecov win32/sspi rdoc/markdown/literals_1_8 enumerator win32/resolv rbtree
|
|
nqxml/streamingparser nqxml/treeparser xmlscan/parser xmlscan/scanner xmltreebuilder xml/parser xmlparser xml/encoding-ja xmlencoding-ja
|
|
iconv uconv win32ole gettext/po_parser gettext/mo libxml psych.jar psych_jars jar-dependencies thread minitest/proveit}
|
|
|
|
builtin_enc=[
|
|
Encoding.find("ASCII-8BIT"),
|
|
Encoding.find("UTF-8"),
|
|
Encoding.find("UTF-7"),
|
|
Encoding.find("US-ASCII"),
|
|
]
|
|
|
|
puts "Looking for requires in files..."
|
|
files_requires=Hash.new { |h,k| h[k]=[] }
|
|
packages.each do
|
|
|pkg|
|
|
package_files[pkg].each do
|
|
|file|
|
|
next if not File.file?(file)
|
|
|
|
if not file =~ /.rb$/
|
|
if File.executable?(file)
|
|
magic=`head -c50 '#{file}' | head -1`
|
|
begin
|
|
if not magic =~ /ruby/
|
|
next
|
|
end
|
|
rescue
|
|
next
|
|
end
|
|
else
|
|
next
|
|
end
|
|
end
|
|
#puts "Checking #{file}..."
|
|
File.open(file, "r") do
|
|
|f|
|
|
lineno=0
|
|
while line=f.gets() do
|
|
lineno+=1; encs=[]; requires=[]; need_encdb=false
|
|
|
|
line=line.chomp.gsub!(/^[[:blank:]]*/,"")
|
|
|
|
case line
|
|
when /^#.*coding *:/
|
|
if lineno <= 2
|
|
enc=line.sub(/.*coding *: */,"").sub(/ .*/,"")
|
|
encs << Encoding.find(enc)
|
|
end
|
|
end
|
|
line.gsub!(/#.*/,"")
|
|
case line
|
|
when "__END__"
|
|
break
|
|
when /^require /
|
|
#puts "#{file}:#{line}"
|
|
if require_regex_ignore =~ line
|
|
#puts "Ignoring #{line} at #{file}:#{lineno} (REGEX)..."
|
|
next
|
|
end
|
|
if not require_regex =~ line
|
|
$stderr.puts "Unknown require: '#{line}' at file #{file}:#{lineno}"
|
|
failed=true
|
|
end
|
|
require=line.gsub(require_regex,"\\1")
|
|
require.gsub!(/\.(so|rb)$/,"")
|
|
|
|
if require_ignore.include?(require)
|
|
#puts "Ignoring #{line} at #{file}:#{lineno} (STR)..."
|
|
next
|
|
end
|
|
|
|
files_requires[file] += [require]
|
|
|
|
when /Encoding::/
|
|
encs=line.scan(/Encoding::[[:alnum:]_]+/).collect {|enc| eval(enc) }.select {|enc| enc.kind_of? Encoding }
|
|
need_encdb=true
|
|
end
|
|
|
|
next if encs.empty?
|
|
required_encs = (encs - builtin_enc).collect {|enc| "enc/#{enc.name.downcase.gsub("-","_")}" }
|
|
required_encs << "enc/encdb" if need_encdb
|
|
|
|
files_requires[file] += required_encs
|
|
end
|
|
end
|
|
end
|
|
end
|
|
exit(1) if failed
|
|
|
|
# Add deps from .so
|
|
package_files.each do |(pkg,files)| files.each do |file|
|
|
case file
|
|
when /\/nkf\.so$/
|
|
files_requires[file]= files_requires[file] + ["enc/encdb"]
|
|
end
|
|
end; end
|
|
|
|
puts "Grouping package requirements per package"
|
|
package_requires_files = Hash.new{|h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
|
|
package_files.each do |(pkg,files)|
|
|
package_requires_files[pkg]
|
|
files.each do |file|
|
|
files_requires[file].each do |requires|
|
|
package_requires_files[pkg][requires] << file
|
|
end
|
|
end
|
|
end
|
|
|
|
weak_dependency=Hash.new { |h,k| h[k]=[] }
|
|
weak_dependency.merge!({
|
|
"ruby-misc"=>["ruby-openssl","ruby-fiddle"], #securerandom.rb
|
|
"ruby-debuglib"=>["ruby-readline"], #debug.rb
|
|
"ruby-drb"=>["ruby-openssl"], #drb/ssl.rb
|
|
"ruby-irb"=>["ruby-rdoc", "ruby-readline"], #irb/cmd/help.rb
|
|
"ruby-gems"=>["ruby-openssl","ruby-io-console","ruby-webrick"], #rubygems/commands/cert_command.rb rubygems/user_interaction.rb rubygems/server.rb
|
|
"ruby-mkmf"=>["ruby-webrick"], #un.rb
|
|
"ruby-net"=>["ruby-openssl","ruby-io-console","ruby-zlib"], #net/*.rb
|
|
"ruby-optparse"=>["ruby-uri","ruby-datetime"], #optparse/date.rb optparse/uri.rb
|
|
"ruby-rake"=>["ruby-net","ruby-gems"], #rake/contrib/ftptools.rb /usr/bin/rake
|
|
"ruby-rdoc"=>["ruby-gems","ruby-readline","ruby-webrick", #/usr/bin/rdoc and others
|
|
"ruby-io-console"], #rdoc/stats/normal.rb
|
|
"ruby-webrick"=>["ruby-openssl"], #webrick/ssl.rb
|
|
"ruby-testunit"=>["ruby-io-console"], #gems/test-unit-3.1.5/lib/test/unit/ui/console/testrunner.rb
|
|
})
|
|
|
|
puts "Preloading gems..."
|
|
Gem::Specification.all.each{ |x| gem x.name }
|
|
|
|
puts "Looking for package dependencies..."
|
|
package_provides = {}
|
|
package_dependencies = Hash.new { |h,k| h[k]=[] }
|
|
package_requires_files.each do
|
|
|(pkg,requires_files)|
|
|
|
|
requires_files.each do
|
|
|(require,files)|
|
|
if package_provides.include?(require)
|
|
found = package_provides[require]
|
|
else
|
|
found = package_files.detect {|(pkg,files)| files.detect {|file| $:.detect {|path| "#{path}/#{require}" == file.gsub(/\.(so|rb)$/,"") } } }
|
|
if not found
|
|
$stderr.puts "#{pkg}: Nothing provides #{require} for #{files.collect {|file| file.sub("/usr/lib/ruby/","") }.join(",")}"
|
|
failed = true
|
|
next
|
|
end
|
|
found = found.first
|
|
package_provides[require] = found
|
|
end
|
|
if weak_dependency[pkg].include?(found)
|
|
puts "#{pkg}: #{found} provides #{require} (weak depedendency ignored)"
|
|
else
|
|
puts "#{pkg}: #{found} provides #{require} for #{files.collect {|file| file.sub("/usr/lib/ruby/","") }.join(",")}"
|
|
package_dependencies[pkg] += [found]
|
|
end
|
|
end
|
|
end
|
|
if failed
|
|
puts "There is some missing requirements not mapped to files in packages."
|
|
puts "Please, fix the missing files or ignore them on require_ignore var"
|
|
exit(1)
|
|
end
|
|
# Remove self dependency
|
|
package_dependencies = Hash[package_dependencies.collect {|(pkg,deps)| [pkg,package_dependencies[pkg]=deps.uniq.sort - [pkg]]}]
|
|
package_dependencies.default = []
|
|
|
|
puts "Expanding dependencies..."
|
|
begin
|
|
changed=false
|
|
package_dependencies.each do
|
|
|(pkg,deps)|
|
|
next if deps.empty?
|
|
deps_new = deps.collect {|dep| [dep] + package_dependencies[dep] }.inject([],:+).uniq.sort
|
|
if not deps == deps_new
|
|
puts "#{pkg}: #{deps.join(",")}"
|
|
puts "#{pkg}: #{deps_new.join(",")}"
|
|
package_dependencies[pkg]=deps_new
|
|
changed=true
|
|
end
|
|
end
|
|
end if not changed
|
|
|
|
puts "Removing redundant dependencies..."
|
|
package_dependencies.each do
|
|
|(pkg,deps)|
|
|
package_dependencies[pkg]=deps.uniq - [pkg]
|
|
end
|
|
|
|
puts "Checking for mutual dependencies..."
|
|
package_dependencies.each do
|
|
|(pkg,deps)|
|
|
if deps.include? pkg
|
|
$stderr.puts "#{pkg}: Cycle dependency detected! "
|
|
failed = true
|
|
end
|
|
end
|
|
exit(1) if failed
|
|
|
|
|
|
package_dependencies2=package_dependencies.dup
|
|
package_dependencies.each do
|
|
|(pkg,deps)|
|
|
|
|
# Ignore dependencies that are already required by another dependency
|
|
deps_clean = deps.reject {|dep_suspect| deps.detect {|dep_provider|
|
|
if package_dependencies[dep_provider].include?(dep_suspect)
|
|
puts "#{pkg}: #{dep_suspect} is already required by #{dep_provider}"
|
|
true
|
|
end
|
|
} }
|
|
|
|
if not deps==deps_clean
|
|
puts "before: #{deps.join(",")}"
|
|
puts "after: #{deps_clean.join(",")}"
|
|
package_dependencies2[pkg]=deps_clean
|
|
end
|
|
end
|
|
package_dependencies=package_dependencies2
|
|
|
|
puts "Checking current packages dependencies..."
|
|
ok=true
|
|
package_dependencies.each do
|
|
|(pkg,deps)|
|
|
current_deps=`opkg depends #{pkg} | sed -r -e '1d;s/^[[:blank:]]*//'`.split("\n")
|
|
current_deps.reject!{|dep| dep =~ /^lib/ }
|
|
current_deps -= ["ruby"]
|
|
|
|
extra_dep = current_deps - deps
|
|
$stderr.puts "Package #{pkg} does not need to depend on #{extra_dep.join(" ")} " if not extra_dep.empty?
|
|
missing_dep = deps - current_deps
|
|
$stderr.puts "Package #{pkg} needs to depend on #{missing_dep.join(" ")} " if not missing_dep.empty?
|
|
|
|
if not extra_dep.empty? or not missing_dep.empty?
|
|
$stderr.puts "define Package/#{pkg}"
|
|
$stderr.puts " DEPENDS:=ruby#{([""] +deps).join(" +")}"
|
|
ok=false
|
|
end
|
|
end
|
|
|
|
puts "All dependencies are OK." if ok
|
|
|
|
__END__
|