Commit based upon d6442850bde61f0c3e7e2ae3247b4a856073c5e0
[librecmc/package-feed.git] / lang / ruby / ruby_find_pkgsdeps
1 #!/usr/bin/ruby -Eutf-8
2 # encoding: utf-8
3 #
4 # Find dependencies between ruby packages
5 #
6 # Must run inside a openwrt with all *ruby* packages installed
7 #
8
9 RUBY_SIMPLE_VERSION = RUBY_VERSION.split(".")[0..1].join(".")
10 failed = false
11
12 puts "Looking for installed ruby packages..."
13 packages=`opkg list-installed '*ruby*' | cut -d' ' -f 1`.split("\n")
14
15 puts "Looking for packages files..."
16 package_files=Hash.new { |h,k| h[k]=[] }
17 packages.each do
18         |pkg|
19         files=`opkg files "#{pkg}" | sed -e 1d`.split("\n")
20         package_files[pkg]=files if files
21 end
22
23 require_regex=/^require ["']([^"']+)["'].*/
24 require_regex_ignore=/^require ([a-zA-Z\$]|["']$|.*\/$)/
25 require_ignore=%w{drb/invokemethod16 foo rubygems/defaults/operating_system win32console java Win32API
26                   builder/xchar json/pure simplecov win32/sspi rdoc/markdown/literals_1_8 enumerator win32/resolv rbtree
27                   nqxml/streamingparser nqxml/treeparser xmlscan/parser xmlscan/scanner xmltreebuilder xml/parser xmlparser xml/encoding-ja xmlencoding-ja
28                   iconv uconv win32ole gettext/po_parser gettext/mo libxml psych.jar psych_jars jar-dependencies thread minitest/proveit}
29
30 builtin_enc=[
31         Encoding.find("ASCII-8BIT"),
32         Encoding.find("UTF-8"),
33         Encoding.find("UTF-7"),
34         Encoding.find("US-ASCII"),
35 ]
36
37 puts "Looking for requires in files..."
38 files_requires=Hash.new { |h,k| h[k]=[] }
39 packages.each do
40         |pkg|
41         package_files[pkg].each do
42                 |file|
43                 next if not File.file?(file)
44
45                 if not file =~ /.rb$/
46                         if File.executable?(file)
47                                 magic=`head -c50 '#{file}' | head -1`
48                                 begin
49                                         if not magic =~ /ruby/
50                                                 next
51                                         end
52                                 rescue
53                                         next
54                                 end
55                         else
56                                 next
57                         end
58                 end
59                 #puts "Checking #{file}..."
60                 File.open(file, "r") do
61                         |f|
62                         lineno=0
63                         while line=f.gets() do
64                                 lineno+=1; encs=[]; requires=[]; need_encdb=false
65
66                                 line=line.chomp.gsub!(/^[[:blank:]]*/,"")
67
68                                 case line
69                                 when /^#.*coding *:/
70                                         if lineno <= 2
71                                                 enc=line.sub(/.*coding *: */,"").sub(/ .*/,"")
72                                                 encs << Encoding.find(enc)
73                                         end
74                                 end
75                                 line.gsub!(/#.*/,"")
76                                 case line
77                                 when "__END__"
78                                         break
79                                 when /^require /
80                                         #puts "#{file}:#{line}"
81                                         if require_regex_ignore =~ line
82                                                 #puts "Ignoring #{line} at #{file}:#{lineno} (REGEX)..."
83                                                 next
84                                         end
85                                         if not require_regex =~ line
86                                                 $stderr.puts "Unknown require: '#{line}' at file #{file}:#{lineno}"
87                                                 failed=true
88                                         end
89                                         require=line.gsub(require_regex,"\\1")
90                                         require.gsub!(/\.(so|rb)$/,"")
91
92                                         if require_ignore.include?(require)
93                                                 #puts "Ignoring #{line} at #{file}:#{lineno} (STR)..."
94                                                 next
95                                         end
96
97                                         files_requires[file] += [require]
98
99                                 when /Encoding::/
100                                         encs=line.scan(/Encoding::[[:alnum:]_]+/).collect {|enc| eval(enc) }.select {|enc| enc.kind_of? Encoding }
101                                         need_encdb=true
102                                 end
103
104                                 next if encs.empty?
105                                 required_encs = (encs - builtin_enc).collect {|enc| "enc/#{enc.name.downcase.gsub("-","_")}" }
106                                 required_encs << "enc/encdb" if need_encdb
107
108                                 files_requires[file] += required_encs
109                         end
110                 end
111         end
112 end
113 exit(1) if failed
114
115 # Add deps from .so
116 package_files.each do |(pkg,files)| files.each do |file|
117         case file
118         when /\/nkf\.so$/
119                 files_requires[file]= files_requires[file] + ["enc/encdb"]
120         end
121 end; end
122
123 puts "Grouping package requirements per package"
124 package_requires_files = Hash.new{|h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
125 package_files.each do |(pkg,files)|
126         package_requires_files[pkg]
127         files.each do |file|
128                 files_requires[file].each do |requires|
129                         package_requires_files[pkg][requires] << file
130                 end
131         end
132 end
133
134 weak_dependency=Hash.new { |h,k| h[k]=[] }
135 weak_dependency.merge!({
136 "ruby-misc"=>["ruby-openssl","ruby-fiddle"],                    #securerandom.rb
137 "ruby-debuglib"=>["ruby-readline"],                             #debug.rb
138 "ruby-drb"=>["ruby-openssl"],                                   #drb/ssl.rb
139 "ruby-irb"=>["ruby-rdoc", "ruby-readline"],                     #irb/cmd/help.rb
140 "ruby-gems"=>["ruby-openssl","ruby-io-console","ruby-webrick"], #rubygems/commands/cert_command.rb rubygems/user_interaction.rb rubygems/server.rb
141 "ruby-mkmf"=>["ruby-webrick"],                                  #un.rb
142 "ruby-net"=>["ruby-openssl","ruby-io-console","ruby-zlib"],     #net/*.rb
143 "ruby-optparse"=>["ruby-uri","ruby-datetime"],                  #optparse/date.rb optparse/uri.rb
144 "ruby-rake"=>["ruby-net","ruby-gems"],                          #rake/contrib/ftptools.rb /usr/bin/rake
145 "ruby-rdoc"=>["ruby-gems","ruby-readline","ruby-webrick",       #/usr/bin/rdoc and others
146                "ruby-io-console"],                              #rdoc/stats/normal.rb
147 "ruby-webrick"=>["ruby-openssl"],                               #webrick/ssl.rb
148 "ruby-testunit"=>["ruby-io-console"],                           #gems/test-unit-3.1.5/lib/test/unit/ui/console/testrunner.rb
149 })
150
151 puts "Preloading gems..."
152 Gem::Specification.all.each{ |x| gem x.name }
153
154 puts "Looking for package dependencies..."
155 package_provides = {}
156 package_dependencies = Hash.new { |h,k| h[k]=[] }
157 package_requires_files.each do
158         |(pkg,requires_files)|
159
160         requires_files.each do
161                 |(require,files)|
162                 if package_provides.include?(require)
163                         found = package_provides[require]
164                 else
165                         found = package_files.detect {|(pkg,files)| files.detect {|file| $:.detect {|path| "#{path}/#{require}" == file.gsub(/\.(so|rb)$/,"") } } }
166                         if not found
167                                 $stderr.puts "#{pkg}: Nothing provides #{require} for #{files.collect {|file| file.sub("/usr/lib/ruby/","") }.join(",")}"
168                                 failed = true
169                                 next
170                         end
171                         found = found.first
172                         package_provides[require] = found
173                 end
174                 if weak_dependency[pkg].include?(found)
175                         puts "#{pkg}: #{found} provides #{require} (weak depedendency ignored)"
176                 else
177                         puts "#{pkg}: #{found} provides #{require} for #{files.collect {|file| file.sub("/usr/lib/ruby/","") }.join(",")}"
178                         package_dependencies[pkg] += [found]
179                 end
180         end
181 end
182 if failed
183         puts "There is some missing requirements not mapped to files in packages."
184         puts "Please, fix the missing files or ignore them on require_ignore var"
185         exit(1)
186 end
187 # Remove self dependency
188 package_dependencies = Hash[package_dependencies.collect {|(pkg,deps)| [pkg,package_dependencies[pkg]=deps.uniq.sort - [pkg]]}]
189 package_dependencies.default = []
190
191 puts "Expanding dependencies..."
192 begin
193         changed=false
194         package_dependencies.each do
195                 |(pkg,deps)|
196                 next if deps.empty?
197                 deps_new = deps.collect {|dep| [dep] + package_dependencies[dep] }.inject([],:+).uniq.sort
198                 if not deps == deps_new
199                         puts "#{pkg}: #{deps.join(",")}"
200                         puts "#{pkg}: #{deps_new.join(",")}"
201                         package_dependencies[pkg]=deps_new
202                         changed=true
203                 end
204         end
205 end if not changed
206
207 puts "Removing redundant dependencies..."
208 package_dependencies.each do
209         |(pkg,deps)|
210         package_dependencies[pkg]=deps.uniq - [pkg]
211 end
212
213 puts "Checking for mutual dependencies..."
214 package_dependencies.each do
215         |(pkg,deps)|
216         if deps.include? pkg
217                 $stderr.puts "#{pkg}: Cycle dependency detected! "
218                 failed = true
219         end
220 end
221 exit(1) if failed
222
223
224 package_dependencies2=package_dependencies.dup
225 package_dependencies.each do
226         |(pkg,deps)|
227
228         # Ignore dependencies that are already required by another dependency
229         deps_clean = deps.reject {|dep_suspect| deps.detect {|dep_provider|
230                         if package_dependencies[dep_provider].include?(dep_suspect)
231                                 puts "#{pkg}: #{dep_suspect} is already required by #{dep_provider}"
232                                 true
233                         end
234                  } }
235
236         if not deps==deps_clean
237                 puts "before: #{deps.join(",")}"
238                 puts "after: #{deps_clean.join(",")}"
239                 package_dependencies2[pkg]=deps_clean
240         end
241 end
242 package_dependencies=package_dependencies2
243
244 puts "Checking current packages dependencies..."
245 ok=true
246 package_dependencies.each do
247         |(pkg,deps)|
248         current_deps=`opkg depends #{pkg} | sed -r -e '1d;s/^[[:blank:]]*//'`.split("\n")
249         current_deps.reject!{|dep| dep =~ /^lib/ }
250         current_deps -= ["ruby"]
251
252         extra_dep = current_deps - deps
253         $stderr.puts "Package #{pkg} does not need to depend on #{extra_dep.join(" ")} " if not extra_dep.empty?
254         missing_dep = deps - current_deps
255         $stderr.puts "Package #{pkg} needs to depend on #{missing_dep.join(" ")} " if not missing_dep.empty?
256
257         if not extra_dep.empty? or not missing_dep.empty?
258                 $stderr.puts "define Package/#{pkg}"
259                 $stderr.puts "  DEPENDS:=ruby#{([""] +deps).join(" +")}"
260                 ok=false
261         end
262 end
263
264 puts "All dependencies are OK." if ok
265
266 __END__