diff_products.py revision 438217aaa20a8082a7dc924b5de295b7de967beb
1#!/usr/bin/env python 2# 3# Copyright (C) 2013 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the 'License'); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an 'AS IS' BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18# diff_products.py product_mk_1 [product_mk_2] 19# compare two product congifuraitons or analyze one product configuration. 20# List PRODUCT_PACKAGES, PRODUCT_COPY_FILES, and etc. 21 22 23import os 24import sys 25 26 27PRODUCT_KEYWORDS = [ 28 "PRODUCT_PACKAGES", 29 "PRODUCT_COPY_FILES", 30 "PRODUCT_PROPERTY_OVERRIDES" ] 31 32# Top level data 33# { "PRODUCT_PACKAGES": {...}} 34# PRODCT_PACKAGES { "libstagefright": "path_to_the_mk_file" } 35 36def removeTrailingParen(path): 37 if path.endswith(")"): 38 return path[:-1] 39 else: 40 return path 41 42def substPathVars(path, parentPath): 43 path_ = path.replace("$(SRC_TARGET_DIR)", "build/target") 44 path__ = path_.replace("$(LOCAL_PATH)", os.path.dirname(parentPath)) 45 return path__ 46 47 48def parseLine(line, productData, productPath, overrideProperty = False): 49 #print "parse:" + line 50 words = line.split() 51 if len(words) < 2: 52 return 53 if words[0] in PRODUCT_KEYWORDS: 54 # Override only for include 55 if overrideProperty and words[1] == ":=": 56 if len(productData[words[0]]) != 0: 57 print "** Warning: overriding property " + words[0] + " that was:" + \ 58 productData[words[0]] 59 productData[words[0]] = {} 60 d = productData[words[0]] 61 for word in words[2:]: 62 # TODO: parsing those $( cases in better way 63 if word.startswith("$(foreach"): # do not parse complex calls 64 print "** Warning: parseLine too complex line in " + productPath + " : " + line 65 return 66 d[word] = productPath 67 elif words[0] == "$(call" and words[1].startswith("inherit-product"): 68 parseProduct(substPathVars(removeTrailingParen(words[2]), productPath), productData) 69 elif words[0] == "include": 70 parseProduct(substPathVars(words[1], productPath), productData, True) 71 elif words[0] == "-include": 72 parseProduct(substPathVars(words[1], productPath), productData, True) 73 74def parseProduct(productPath, productData, overrideProperty = False): 75 """parse given product mk file and add the result to productData dict""" 76 if not os.path.exists(productPath): 77 print "** Warning cannot find file " + productPath 78 return 79 80 for key in PRODUCT_KEYWORDS: 81 if not key in productData: 82 productData[key] = {} 83 84 multiLineBuffer = [] #for storing multiple lines 85 inMultiLine = False 86 for line in open(productPath): 87 line_ = line.strip() 88 if inMultiLine: 89 if line_.endswith("\\"): 90 multiLineBuffer.append(line_[:-1]) 91 else: 92 parseLine(" ".join(multiLineBuffer), productData, productPath) 93 inMultiLine = False 94 else: 95 if line_.endswith("\\"): 96 inMultiLine = True 97 multiLineBuffer = [] 98 multiLineBuffer.append(line_[:-1]) 99 else: 100 parseLine(line_, productData, productPath) 101 #print productData 102 103def printConf(confList): 104 for key in PRODUCT_KEYWORDS: 105 print " *" + key 106 if key in confList: 107 for (k, path) in confList[key]: 108 print " " + k + ": " + path 109 110def diffTwoProducts(productL, productR): 111 """compare two products and comapre in the order of common, left only, right only items. 112 productL and productR are dictionary""" 113 confCommon = {} 114 confLOnly = {} 115 confROnly = {} 116 for key in PRODUCT_KEYWORDS: 117 dL = productL[key] 118 dR = productR[key] 119 confCommon[key] = [] 120 confLOnly[key] = [] 121 confROnly[key] = [] 122 for keyL in sorted(dL.keys()): 123 if keyL in dR: 124 if dL[keyL] == dR[keyL]: 125 confCommon[key].append((keyL, dL[keyL])) 126 else: 127 confCommon[key].append((keyL, dL[keyL] + "," + dR[keyL])) 128 else: 129 confLOnly[key].append((keyL, dL[keyL])) 130 for keyR in sorted(dR.keys()): 131 if not keyR in dL: # right only 132 confROnly[key].append((keyR, dR[keyR])) 133 print "==Common==" 134 printConf(confCommon) 135 print "==Left Only==" 136 printConf(confLOnly) 137 print "==Right Only==" 138 printConf(confROnly) 139 140def main(argv): 141 if len(argv) < 2: 142 print "diff_products.py product_mk_1 [product_mk_2]" 143 print " compare two product mk files (or just list single product)" 144 print " it must be executed from android source tree root." 145 print " ex) diff_products.py device/asus/grouper/full_grouper.mk " + \ 146 " device/asus/tilapia/full_tilapia.mk" 147 sys.exit(1) 148 149 productLPath = argv[1] 150 productRPath = None 151 if len(argv) == 3: 152 productRPath = argv[2] 153 154 productL = {} 155 productR = {} 156 parseProduct(productLPath, productL) 157 if productRPath is None: 158 for key in PRODUCT_KEYWORDS: 159 productR[key] = {} 160 161 else: 162 parseProduct(productRPath, productR) 163 164 diffTwoProducts(productL, productR) 165 166 167if __name__ == '__main__': 168 main(sys.argv) 169