1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/ruby 2*90c8c64dSAndroid Build Coastguard Worker# 3*90c8c64dSAndroid Build Coastguard Worker# Find unused resources in all the apps found recursively under the current directory 4*90c8c64dSAndroid Build Coastguard Worker# Usage: 5*90c8c64dSAndroid Build Coastguard Worker# find_unused_resources.rb [-html] 6*90c8c64dSAndroid Build Coastguard Worker# 7*90c8c64dSAndroid Build Coastguard Worker# If -html is specified, the output will be HTML, otherwise it will be plain text 8*90c8c64dSAndroid Build Coastguard Worker# 9*90c8c64dSAndroid Build Coastguard Worker# Author: [email protected] 10*90c8c64dSAndroid Build Coastguard Worker 11*90c8c64dSAndroid Build Coastguard Workerrequire 'find' 12*90c8c64dSAndroid Build Coastguard Worker 13*90c8c64dSAndroid Build Coastguard Workerdebug = false 14*90c8c64dSAndroid Build Coastguard Worker 15*90c8c64dSAndroid Build Coastguard Worker@@stringIdPattern = Regexp.new("name=\"([@_a-zA-Z0-9 ]*)\"") 16*90c8c64dSAndroid Build Coastguard Worker@@layoutIdPattern = Regexp.new("android:id=\".*id/([_a-zA-Z0-9]*)\"") 17*90c8c64dSAndroid Build Coastguard Worker 18*90c8c64dSAndroid Build Coastguard Worker@@stringXmlPatterns = [ 19*90c8c64dSAndroid Build Coastguard Worker Regexp.new("@string/([_a-zA-Z0-9]*)"), 20*90c8c64dSAndroid Build Coastguard Worker Regexp.new("@array/([_a-zA-Z0-9]*)"), 21*90c8c64dSAndroid Build Coastguard Worker] 22*90c8c64dSAndroid Build Coastguard Worker 23*90c8c64dSAndroid Build Coastguard Worker@@javaIdPatterns = [ 24*90c8c64dSAndroid Build Coastguard Worker Regexp.new("R.id.([_a-zA-Z0-9]+)"), 25*90c8c64dSAndroid Build Coastguard Worker Regexp.new("R.string.([_a-zA-Z0-9]+)"), 26*90c8c64dSAndroid Build Coastguard Worker Regexp.new("R.array.([_a-zA-Z0-9]+)"), 27*90c8c64dSAndroid Build Coastguard Worker Regexp.new("R.color.([_a-zA-Z0-9]+)"), 28*90c8c64dSAndroid Build Coastguard Worker Regexp.new("R.configVarying.([_a-zA-Z0-9]+)"), 29*90c8c64dSAndroid Build Coastguard Worker Regexp.new("R.dimen.([_a-zA-Z0-9]+)"), 30*90c8c64dSAndroid Build Coastguard Worker] 31*90c8c64dSAndroid Build Coastguard Worker 32*90c8c64dSAndroid Build Coastguard Worker 33*90c8c64dSAndroid Build Coastguard Worker@@appDir = "partner/google/apps/Gmail" 34*90c8c64dSAndroid Build Coastguard Worker 35*90c8c64dSAndroid Build Coastguard Workerdef findResDirectories(root) 36*90c8c64dSAndroid Build Coastguard Worker result = Array.new 37*90c8c64dSAndroid Build Coastguard Worker Find.find(root) do |path| 38*90c8c64dSAndroid Build Coastguard Worker if FileTest.directory?(path) 39*90c8c64dSAndroid Build Coastguard Worker if File.basename(path) == "res" 40*90c8c64dSAndroid Build Coastguard Worker result << path 41*90c8c64dSAndroid Build Coastguard Worker else 42*90c8c64dSAndroid Build Coastguard Worker next 43*90c8c64dSAndroid Build Coastguard Worker end 44*90c8c64dSAndroid Build Coastguard Worker end 45*90c8c64dSAndroid Build Coastguard Worker end 46*90c8c64dSAndroid Build Coastguard Worker result 47*90c8c64dSAndroid Build Coastguard Workerend 48*90c8c64dSAndroid Build Coastguard Worker 49*90c8c64dSAndroid Build Coastguard Workerclass UnusedResources 50*90c8c64dSAndroid Build Coastguard Worker attr_accessor :appDir, :unusedLayoutIds, :unusedStringIds 51*90c8c64dSAndroid Build Coastguard Workerend 52*90c8c64dSAndroid Build Coastguard Worker 53*90c8c64dSAndroid Build Coastguard Workerclass FilePosition 54*90c8c64dSAndroid Build Coastguard Worker attr_accessor :file, :lineNumber 55*90c8c64dSAndroid Build Coastguard Worker 56*90c8c64dSAndroid Build Coastguard Worker def initialize(f, ln) 57*90c8c64dSAndroid Build Coastguard Worker @file = f 58*90c8c64dSAndroid Build Coastguard Worker @lineNumber = ln 59*90c8c64dSAndroid Build Coastguard Worker end 60*90c8c64dSAndroid Build Coastguard Worker 61*90c8c64dSAndroid Build Coastguard Worker def to_s 62*90c8c64dSAndroid Build Coastguard Worker "#{file}:#{lineNumber}" 63*90c8c64dSAndroid Build Coastguard Worker end 64*90c8c64dSAndroid Build Coastguard Worker 65*90c8c64dSAndroid Build Coastguard Worker def <=>(other) 66*90c8c64dSAndroid Build Coastguard Worker if @file == other.file 67*90c8c64dSAndroid Build Coastguard Worker @lineNumber - other.lineNumber 68*90c8c64dSAndroid Build Coastguard Worker else 69*90c8c64dSAndroid Build Coastguard Worker @file <=> other.file 70*90c8c64dSAndroid Build Coastguard Worker end 71*90c8c64dSAndroid Build Coastguard Worker end 72*90c8c64dSAndroid Build Coastguard Workerend 73*90c8c64dSAndroid Build Coastguard Worker 74*90c8c64dSAndroid Build Coastguard Worker 75*90c8c64dSAndroid Build Coastguard Workerdef findAllOccurrences(re, string) 76*90c8c64dSAndroid Build Coastguard Worker result = Array.new 77*90c8c64dSAndroid Build Coastguard Worker 78*90c8c64dSAndroid Build Coastguard Worker s = string 79*90c8c64dSAndroid Build Coastguard Worker matchData = re.match(s) 80*90c8c64dSAndroid Build Coastguard Worker while (matchData) 81*90c8c64dSAndroid Build Coastguard Worker result << matchData[1].to_s 82*90c8c64dSAndroid Build Coastguard Worker s = s[matchData.end(1) .. -1] 83*90c8c64dSAndroid Build Coastguard Worker matchData = re.match(s) 84*90c8c64dSAndroid Build Coastguard Worker end 85*90c8c64dSAndroid Build Coastguard Worker 86*90c8c64dSAndroid Build Coastguard Worker result 87*90c8c64dSAndroid Build Coastguard Workerend 88*90c8c64dSAndroid Build Coastguard Worker 89*90c8c64dSAndroid Build Coastguard Worker@@globalJavaIdUses = Hash.new 90*90c8c64dSAndroid Build Coastguard Worker 91*90c8c64dSAndroid Build Coastguard Workerdef recordJavaUses(glob) 92*90c8c64dSAndroid Build Coastguard Worker Dir.glob(glob).each { |filename| 93*90c8c64dSAndroid Build Coastguard Worker File.open(filename) { |file| 94*90c8c64dSAndroid Build Coastguard Worker file.each { |line| 95*90c8c64dSAndroid Build Coastguard Worker @@javaIdPatterns.each { |re| 96*90c8c64dSAndroid Build Coastguard Worker findAllOccurrences(re, line).each { |id| 97*90c8c64dSAndroid Build Coastguard Worker @@globalJavaIdUses[id] = FilePosition.new(filename, file.lineno) 98*90c8c64dSAndroid Build Coastguard Worker } 99*90c8c64dSAndroid Build Coastguard Worker } 100*90c8c64dSAndroid Build Coastguard Worker } 101*90c8c64dSAndroid Build Coastguard Worker } 102*90c8c64dSAndroid Build Coastguard Worker } 103*90c8c64dSAndroid Build Coastguard Workerend 104*90c8c64dSAndroid Build Coastguard Worker 105*90c8c64dSAndroid Build Coastguard Workerdef findUnusedResources(dir) 106*90c8c64dSAndroid Build Coastguard Worker javaIdUses = Hash.new 107*90c8c64dSAndroid Build Coastguard Worker layouts = Hash.new 108*90c8c64dSAndroid Build Coastguard Worker strings = Hash.new 109*90c8c64dSAndroid Build Coastguard Worker xmlIdUses = Hash.new 110*90c8c64dSAndroid Build Coastguard Worker 111*90c8c64dSAndroid Build Coastguard Worker Dir.glob("#{dir}/res/**/*.xml").each { |filename| 112*90c8c64dSAndroid Build Coastguard Worker if ! (filename =~ /attrs.xml$/) 113*90c8c64dSAndroid Build Coastguard Worker File.open(filename) { |file| 114*90c8c64dSAndroid Build Coastguard Worker file.each { |line| 115*90c8c64dSAndroid Build Coastguard Worker findAllOccurrences(@@stringIdPattern, line).each {|id| 116*90c8c64dSAndroid Build Coastguard Worker strings[id] = FilePosition.new(filename, file.lineno) 117*90c8c64dSAndroid Build Coastguard Worker } 118*90c8c64dSAndroid Build Coastguard Worker findAllOccurrences(@@layoutIdPattern, line).each {|id| 119*90c8c64dSAndroid Build Coastguard Worker layouts[id] = FilePosition.new(filename, file.lineno) 120*90c8c64dSAndroid Build Coastguard Worker } 121*90c8c64dSAndroid Build Coastguard Worker @@stringXmlPatterns.each { |re| 122*90c8c64dSAndroid Build Coastguard Worker findAllOccurrences(re, line).each {|id| 123*90c8c64dSAndroid Build Coastguard Worker xmlIdUses[id] = FilePosition.new(filename, file.lineno) 124*90c8c64dSAndroid Build Coastguard Worker } 125*90c8c64dSAndroid Build Coastguard Worker } 126*90c8c64dSAndroid Build Coastguard Worker } 127*90c8c64dSAndroid Build Coastguard Worker } 128*90c8c64dSAndroid Build Coastguard Worker end 129*90c8c64dSAndroid Build Coastguard Worker } 130*90c8c64dSAndroid Build Coastguard Worker 131*90c8c64dSAndroid Build Coastguard Worker Dir.glob("#{dir}/AndroidManifest.xml").each { |filename| 132*90c8c64dSAndroid Build Coastguard Worker File.open(filename) { |file| 133*90c8c64dSAndroid Build Coastguard Worker file.each { |line| 134*90c8c64dSAndroid Build Coastguard Worker @@stringXmlPatterns.each { |re| 135*90c8c64dSAndroid Build Coastguard Worker findAllOccurrences(re, line).each {|id| 136*90c8c64dSAndroid Build Coastguard Worker xmlIdUses[id] = FilePosition.new(filename, file.lineno) 137*90c8c64dSAndroid Build Coastguard Worker } 138*90c8c64dSAndroid Build Coastguard Worker } 139*90c8c64dSAndroid Build Coastguard Worker } 140*90c8c64dSAndroid Build Coastguard Worker } 141*90c8c64dSAndroid Build Coastguard Worker } 142*90c8c64dSAndroid Build Coastguard Worker 143*90c8c64dSAndroid Build Coastguard Worker recordJavaUses("#{dir}/src/**/*.java") 144*90c8c64dSAndroid Build Coastguard Worker 145*90c8c64dSAndroid Build Coastguard Worker @@globalJavaIdUses.each_pair { |id, file| 146*90c8c64dSAndroid Build Coastguard Worker layouts.delete(id) 147*90c8c64dSAndroid Build Coastguard Worker strings.delete(id) 148*90c8c64dSAndroid Build Coastguard Worker } 149*90c8c64dSAndroid Build Coastguard Worker 150*90c8c64dSAndroid Build Coastguard Worker javaIdUses.each_pair { |id, file| 151*90c8c64dSAndroid Build Coastguard Worker layouts.delete(id) 152*90c8c64dSAndroid Build Coastguard Worker strings.delete(id) 153*90c8c64dSAndroid Build Coastguard Worker } 154*90c8c64dSAndroid Build Coastguard Worker 155*90c8c64dSAndroid Build Coastguard Worker xmlIdUses.each_pair { |id, file| 156*90c8c64dSAndroid Build Coastguard Worker layouts.delete(id) 157*90c8c64dSAndroid Build Coastguard Worker strings.delete(id) 158*90c8c64dSAndroid Build Coastguard Worker } 159*90c8c64dSAndroid Build Coastguard Worker 160*90c8c64dSAndroid Build Coastguard Worker result = UnusedResources.new 161*90c8c64dSAndroid Build Coastguard Worker result.appDir = dir 162*90c8c64dSAndroid Build Coastguard Worker result.unusedLayoutIds = layouts 163*90c8c64dSAndroid Build Coastguard Worker result.unusedStringIds = strings 164*90c8c64dSAndroid Build Coastguard Worker 165*90c8c64dSAndroid Build Coastguard Worker result 166*90c8c64dSAndroid Build Coastguard Workerend 167*90c8c64dSAndroid Build Coastguard Worker 168*90c8c64dSAndroid Build Coastguard Workerdef findApps(dir) 169*90c8c64dSAndroid Build Coastguard Worker result = Array.new 170*90c8c64dSAndroid Build Coastguard Worker Dir.glob("#{dir}/**/res").each { |filename| 171*90c8c64dSAndroid Build Coastguard Worker a = filename.split("/") 172*90c8c64dSAndroid Build Coastguard Worker result << a.slice(0, a.size-1).join("/") 173*90c8c64dSAndroid Build Coastguard Worker } 174*90c8c64dSAndroid Build Coastguard Worker result 175*90c8c64dSAndroid Build Coastguard Workerend 176*90c8c64dSAndroid Build Coastguard Worker 177*90c8c64dSAndroid Build Coastguard Workerdef displayText(result) 178*90c8c64dSAndroid Build Coastguard Worker result.each { |unusedResources| 179*90c8c64dSAndroid Build Coastguard Worker puts "=== #{unusedResources.appDir}" 180*90c8c64dSAndroid Build Coastguard Worker 181*90c8c64dSAndroid Build Coastguard Worker puts "----- Unused layout ids" 182*90c8c64dSAndroid Build Coastguard Worker unusedResources.unusedLayoutIds.sort { |id, file| id[1] <=> file[1] }.each {|f| 183*90c8c64dSAndroid Build Coastguard Worker puts " #{f[0]} #{f[1]}" 184*90c8c64dSAndroid Build Coastguard Worker } 185*90c8c64dSAndroid Build Coastguard Worker 186*90c8c64dSAndroid Build Coastguard Worker 187*90c8c64dSAndroid Build Coastguard Worker puts "----- Unused string ids" 188*90c8c64dSAndroid Build Coastguard Worker unusedResources.unusedStringIds.sort { |id, file| id[1] <=> file[1] }.each {|f| 189*90c8c64dSAndroid Build Coastguard Worker puts " #{f[0]} #{f[1]}" 190*90c8c64dSAndroid Build Coastguard Worker } 191*90c8c64dSAndroid Build Coastguard Worker 192*90c8c64dSAndroid Build Coastguard Worker } 193*90c8c64dSAndroid Build Coastguard Workerend 194*90c8c64dSAndroid Build Coastguard Worker 195*90c8c64dSAndroid Build Coastguard Workerdef displayHtmlUnused(unusedResourceIds, title) 196*90c8c64dSAndroid Build Coastguard Worker 197*90c8c64dSAndroid Build Coastguard Worker puts "<h3>#{title}</h3>" 198*90c8c64dSAndroid Build Coastguard Worker puts "<table border='1'>" 199*90c8c64dSAndroid Build Coastguard Worker unusedResourceIds.sort { |id, file| id[1] <=> file[1] }.each {|f| 200*90c8c64dSAndroid Build Coastguard Worker puts "<tr><td><b>#{f[0]}</b></td> <td>#{f[1]}</td></tr>" 201*90c8c64dSAndroid Build Coastguard Worker } 202*90c8c64dSAndroid Build Coastguard Worker puts "</table>" 203*90c8c64dSAndroid Build Coastguard Workerend 204*90c8c64dSAndroid Build Coastguard Worker 205*90c8c64dSAndroid Build Coastguard Workerdef displayHtml(result) 206*90c8c64dSAndroid Build Coastguard Worker title = "Unused resources as of #{Time.now.localtime}" 207*90c8c64dSAndroid Build Coastguard Worker puts "<html><header><title>#{title}</title></header><body>" 208*90c8c64dSAndroid Build Coastguard Worker 209*90c8c64dSAndroid Build Coastguard Worker puts "<h1><p align=\"center\">#{title}</p></h1>" 210*90c8c64dSAndroid Build Coastguard Worker result.each { |unusedResources| 211*90c8c64dSAndroid Build Coastguard Worker puts "<h2>#{unusedResources.appDir}</h2>" 212*90c8c64dSAndroid Build Coastguard Worker displayHtmlUnused(unusedResources.unusedLayoutIds, "Unused layout ids") 213*90c8c64dSAndroid Build Coastguard Worker displayHtmlUnused(unusedResources.unusedStringIds, "Unused other ids") 214*90c8c64dSAndroid Build Coastguard Worker } 215*90c8c64dSAndroid Build Coastguard Worker puts "</body>" 216*90c8c64dSAndroid Build Coastguard Workerend 217*90c8c64dSAndroid Build Coastguard Worker 218*90c8c64dSAndroid Build Coastguard Workerresult = Array.new 219*90c8c64dSAndroid Build Coastguard Worker 220*90c8c64dSAndroid Build Coastguard WorkerrecordJavaUses("java/android/**/*.java") 221*90c8c64dSAndroid Build Coastguard Worker 222*90c8c64dSAndroid Build Coastguard Workerif debug 223*90c8c64dSAndroid Build Coastguard Worker result << findUnusedResources("apps/Browser") 224*90c8c64dSAndroid Build Coastguard Workerelse 225*90c8c64dSAndroid Build Coastguard Worker findApps(".").each { |appDir| 226*90c8c64dSAndroid Build Coastguard Worker result << findUnusedResources(appDir) 227*90c8c64dSAndroid Build Coastguard Worker } 228*90c8c64dSAndroid Build Coastguard Workerend 229*90c8c64dSAndroid Build Coastguard Worker 230*90c8c64dSAndroid Build Coastguard Workerif ARGV[0] == "-html" 231*90c8c64dSAndroid Build Coastguard Worker displayHtml result 232*90c8c64dSAndroid Build Coastguard Workerelse 233*90c8c64dSAndroid Build Coastguard Worker displayText result 234*90c8c64dSAndroid Build Coastguard Workerend 235*90c8c64dSAndroid Build Coastguard Worker 236