You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
5.1 KiB
236 lines
5.1 KiB
#!/usr/bin/ruby
|
|
#
|
|
# Find unused resources in all the apps found recursively under the current directory
|
|
# Usage:
|
|
# find_unused_resources.rb [-html]
|
|
#
|
|
# If -html is specified, the output will be HTML, otherwise it will be plain text
|
|
#
|
|
# Author: cbeust@google.com
|
|
|
|
require 'find'
|
|
|
|
debug = false
|
|
|
|
@@stringIdPattern = Regexp.new("name=\"([@_a-zA-Z0-9 ]*)\"")
|
|
@@layoutIdPattern = Regexp.new("android:id=\".*id/([_a-zA-Z0-9]*)\"")
|
|
|
|
@@stringXmlPatterns = [
|
|
Regexp.new("@string/([_a-zA-Z0-9]*)"),
|
|
Regexp.new("@array/([_a-zA-Z0-9]*)"),
|
|
]
|
|
|
|
@@javaIdPatterns = [
|
|
Regexp.new("R.id.([_a-zA-Z0-9]+)"),
|
|
Regexp.new("R.string.([_a-zA-Z0-9]+)"),
|
|
Regexp.new("R.array.([_a-zA-Z0-9]+)"),
|
|
Regexp.new("R.color.([_a-zA-Z0-9]+)"),
|
|
Regexp.new("R.configVarying.([_a-zA-Z0-9]+)"),
|
|
Regexp.new("R.dimen.([_a-zA-Z0-9]+)"),
|
|
]
|
|
|
|
|
|
@@appDir = "partner/google/apps/Gmail"
|
|
|
|
def findResDirectories(root)
|
|
result = Array.new
|
|
Find.find(root) do |path|
|
|
if FileTest.directory?(path)
|
|
if File.basename(path) == "res"
|
|
result << path
|
|
else
|
|
next
|
|
end
|
|
end
|
|
end
|
|
result
|
|
end
|
|
|
|
class UnusedResources
|
|
attr_accessor :appDir, :unusedLayoutIds, :unusedStringIds
|
|
end
|
|
|
|
class FilePosition
|
|
attr_accessor :file, :lineNumber
|
|
|
|
def initialize(f, ln)
|
|
@file = f
|
|
@lineNumber = ln
|
|
end
|
|
|
|
def to_s
|
|
"#{file}:#{lineNumber}"
|
|
end
|
|
|
|
def <=>(other)
|
|
if @file == other.file
|
|
@lineNumber - other.lineNumber
|
|
else
|
|
@file <=> other.file
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
def findAllOccurrences(re, string)
|
|
result = Array.new
|
|
|
|
s = string
|
|
matchData = re.match(s)
|
|
while (matchData)
|
|
result << matchData[1].to_s
|
|
s = s[matchData.end(1) .. -1]
|
|
matchData = re.match(s)
|
|
end
|
|
|
|
result
|
|
end
|
|
|
|
@@globalJavaIdUses = Hash.new
|
|
|
|
def recordJavaUses(glob)
|
|
Dir.glob(glob).each { |filename|
|
|
File.open(filename) { |file|
|
|
file.each { |line|
|
|
@@javaIdPatterns.each { |re|
|
|
findAllOccurrences(re, line).each { |id|
|
|
@@globalJavaIdUses[id] = FilePosition.new(filename, file.lineno)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
def findUnusedResources(dir)
|
|
javaIdUses = Hash.new
|
|
layouts = Hash.new
|
|
strings = Hash.new
|
|
xmlIdUses = Hash.new
|
|
|
|
Dir.glob("#{dir}/res/**/*.xml").each { |filename|
|
|
if ! (filename =~ /attrs.xml$/)
|
|
File.open(filename) { |file|
|
|
file.each { |line|
|
|
findAllOccurrences(@@stringIdPattern, line).each {|id|
|
|
strings[id] = FilePosition.new(filename, file.lineno)
|
|
}
|
|
findAllOccurrences(@@layoutIdPattern, line).each {|id|
|
|
layouts[id] = FilePosition.new(filename, file.lineno)
|
|
}
|
|
@@stringXmlPatterns.each { |re|
|
|
findAllOccurrences(re, line).each {|id|
|
|
xmlIdUses[id] = FilePosition.new(filename, file.lineno)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end
|
|
}
|
|
|
|
Dir.glob("#{dir}/AndroidManifest.xml").each { |filename|
|
|
File.open(filename) { |file|
|
|
file.each { |line|
|
|
@@stringXmlPatterns.each { |re|
|
|
findAllOccurrences(re, line).each {|id|
|
|
xmlIdUses[id] = FilePosition.new(filename, file.lineno)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
recordJavaUses("#{dir}/src/**/*.java")
|
|
|
|
@@globalJavaIdUses.each_pair { |id, file|
|
|
layouts.delete(id)
|
|
strings.delete(id)
|
|
}
|
|
|
|
javaIdUses.each_pair { |id, file|
|
|
layouts.delete(id)
|
|
strings.delete(id)
|
|
}
|
|
|
|
xmlIdUses.each_pair { |id, file|
|
|
layouts.delete(id)
|
|
strings.delete(id)
|
|
}
|
|
|
|
result = UnusedResources.new
|
|
result.appDir = dir
|
|
result.unusedLayoutIds = layouts
|
|
result.unusedStringIds = strings
|
|
|
|
result
|
|
end
|
|
|
|
def findApps(dir)
|
|
result = Array.new
|
|
Dir.glob("#{dir}/**/res").each { |filename|
|
|
a = filename.split("/")
|
|
result << a.slice(0, a.size-1).join("/")
|
|
}
|
|
result
|
|
end
|
|
|
|
def displayText(result)
|
|
result.each { |unusedResources|
|
|
puts "=== #{unusedResources.appDir}"
|
|
|
|
puts "----- Unused layout ids"
|
|
unusedResources.unusedLayoutIds.sort { |id, file| id[1] <=> file[1] }.each {|f|
|
|
puts " #{f[0]} #{f[1]}"
|
|
}
|
|
|
|
|
|
puts "----- Unused string ids"
|
|
unusedResources.unusedStringIds.sort { |id, file| id[1] <=> file[1] }.each {|f|
|
|
puts " #{f[0]} #{f[1]}"
|
|
}
|
|
|
|
}
|
|
end
|
|
|
|
def displayHtmlUnused(unusedResourceIds, title)
|
|
|
|
puts "<h3>#{title}</h3>"
|
|
puts "<table border='1'>"
|
|
unusedResourceIds.sort { |id, file| id[1] <=> file[1] }.each {|f|
|
|
puts "<tr><td><b>#{f[0]}</b></td> <td>#{f[1]}</td></tr>"
|
|
}
|
|
puts "</table>"
|
|
end
|
|
|
|
def displayHtml(result)
|
|
title = "Unused resources as of #{Time.now.localtime}"
|
|
puts "<html><header><title>#{title}</title></header><body>"
|
|
|
|
puts "<h1><p align=\"center\">#{title}</p></h1>"
|
|
result.each { |unusedResources|
|
|
puts "<h2>#{unusedResources.appDir}</h2>"
|
|
displayHtmlUnused(unusedResources.unusedLayoutIds, "Unused layout ids")
|
|
displayHtmlUnused(unusedResources.unusedStringIds, "Unused other ids")
|
|
}
|
|
puts "</body>"
|
|
end
|
|
|
|
result = Array.new
|
|
|
|
recordJavaUses("java/android/**/*.java")
|
|
|
|
if debug
|
|
result << findUnusedResources("apps/Browser")
|
|
else
|
|
findApps(".").each { |appDir|
|
|
result << findUnusedResources(appDir)
|
|
}
|
|
end
|
|
|
|
if ARGV[0] == "-html"
|
|
displayHtml result
|
|
else
|
|
displayText result
|
|
end
|
|
|