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 23 import os 24 import sys 25 26 27 PRODUCT_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 36 def removeTrailingParen(path): 37 if path.endswith(")"): 38 return path[:-1] 39 else: 40 return path 41 42 def 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 48 def 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 74 def 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 multiLineBuffer.append(line_) 93 parseLine(" ".join(multiLineBuffer), productData, productPath) 94 inMultiLine = False 95 else: 96 if line_.endswith("\\"): 97 inMultiLine = True 98 multiLineBuffer = [] 99 multiLineBuffer.append(line_[:-1]) 100 else: 101 parseLine(line_, productData, productPath) 102 #print productData 103 104 def printConf(confList): 105 for key in PRODUCT_KEYWORDS: 106 print " *" + key 107 if key in confList: 108 for (k, path) in confList[key]: 109 print " " + k + ": " + path 110 111 def diffTwoProducts(productL, productR): 112 """compare two products and comapre in the order of common, left only, right only items. 113 productL and productR are dictionary""" 114 confCommon = {} 115 confLOnly = {} 116 confROnly = {} 117 for key in PRODUCT_KEYWORDS: 118 dL = productL[key] 119 dR = productR[key] 120 confCommon[key] = [] 121 confLOnly[key] = [] 122 confROnly[key] = [] 123 for keyL in sorted(dL.keys()): 124 if keyL in dR: 125 if dL[keyL] == dR[keyL]: 126 confCommon[key].append((keyL, dL[keyL])) 127 else: 128 confCommon[key].append((keyL, dL[keyL] + "," + dR[keyL])) 129 else: 130 confLOnly[key].append((keyL, dL[keyL])) 131 for keyR in sorted(dR.keys()): 132 if not keyR in dL: # right only 133 confROnly[key].append((keyR, dR[keyR])) 134 print "==Common==" 135 printConf(confCommon) 136 print "==Left Only==" 137 printConf(confLOnly) 138 print "==Right Only==" 139 printConf(confROnly) 140 141 def main(argv): 142 if len(argv) < 2: 143 print "diff_products.py product_mk_1 [product_mk_2]" 144 print " compare two product mk files (or just list single product)" 145 print " it must be executed from android source tree root." 146 print " ex) diff_products.py device/asus/grouper/full_grouper.mk " + \ 147 " device/asus/tilapia/full_tilapia.mk" 148 sys.exit(1) 149 150 productLPath = argv[1] 151 productRPath = None 152 if len(argv) == 3: 153 productRPath = argv[2] 154 155 productL = {} 156 productR = {} 157 parseProduct(productLPath, productL) 158 if productRPath is None: 159 for key in PRODUCT_KEYWORDS: 160 productR[key] = {} 161 162 else: 163 parseProduct(productRPath, productR) 164 165 diffTwoProducts(productL, productR) 166 167 168 if __name__ == '__main__': 169 main(sys.argv) 170