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 "PRODUCT_BOOT_JARS" ] 32 33# Top level data 34# { "PRODUCT_PACKAGES": {...}} 35# PRODCT_PACKAGES { "libstagefright": "path_to_the_mk_file" } 36 37def removeTrailingParen(path): 38 if path.endswith(")"): 39 return path[:-1] 40 else: 41 return path 42 43def substPathVars(path, parentPath): 44 path_ = path.replace("$(SRC_TARGET_DIR)", "build/target") 45 path__ = path_.replace("$(LOCAL_PATH)", os.path.dirname(parentPath)) 46 return path__ 47 48 49def parseLine(line, productData, productPath, overrideProperty = False): 50 #print "parse:" + line 51 words = line.split() 52 if len(words) < 2: 53 return 54 if words[0] in PRODUCT_KEYWORDS: 55 # Override only for include 56 if overrideProperty and words[1] == ":=": 57 if len(productData[words[0]]) != 0: 58 print "** Warning: overriding property " + words[0] + " that was:" + \ 59 productData[words[0]] 60 productData[words[0]] = {} 61 d = productData[words[0]] 62 for word in words[2:]: 63 # TODO: parsing those $( cases in better way 64 if word.startswith("$(foreach"): # do not parse complex calls 65 print "** Warning: parseLine too complex line in " + productPath + " : " + line 66 return 67 d[word] = productPath 68 elif words[0] == "$(call" and words[1].startswith("inherit-product"): 69 parseProduct(substPathVars(removeTrailingParen(words[2]), productPath), productData) 70 elif words[0] == "include": 71 parseProduct(substPathVars(words[1], productPath), productData, True) 72 elif words[0] == "-include": 73 parseProduct(substPathVars(words[1], productPath), productData, True) 74 75def parseProduct(productPath, productData, overrideProperty = False): 76 """parse given product mk file and add the result to productData dict""" 77 if not os.path.exists(productPath): 78 print "** Warning cannot find file " + productPath 79 return 80 81 for key in PRODUCT_KEYWORDS: 82 if not key in productData: 83 productData[key] = {} 84 85 multiLineBuffer = [] #for storing multiple lines 86 inMultiLine = False 87 for line in open(productPath): 88 line_ = line.strip() 89 if inMultiLine: 90 if line_.endswith("\\"): 91 multiLineBuffer.append(line_[:-1]) 92 else: 93 multiLineBuffer.append(line_) 94 parseLine(" ".join(multiLineBuffer), productData, productPath) 95 inMultiLine = False 96 else: 97 if line_.endswith("\\"): 98 inMultiLine = True 99 multiLineBuffer = [] 100 multiLineBuffer.append(line_[:-1]) 101 else: 102 parseLine(line_, productData, productPath) 103 #print productData 104 105def printConf(confList): 106 for key in PRODUCT_KEYWORDS: 107 print " *" + key 108 if key in confList: 109 for (k, path) in confList[key]: 110 print " " + k + ": " + path 111 112def diffTwoProducts(productL, productR): 113 """compare two products and comapre in the order of common, left only, right only items. 114 productL and productR are dictionary""" 115 confCommon = {} 116 confLOnly = {} 117 confROnly = {} 118 for key in PRODUCT_KEYWORDS: 119 dL = productL[key] 120 dR = productR[key] 121 confCommon[key] = [] 122 confLOnly[key] = [] 123 confROnly[key] = [] 124 for keyL in sorted(dL.keys()): 125 if keyL in dR: 126 if dL[keyL] == dR[keyL]: 127 confCommon[key].append((keyL, dL[keyL])) 128 else: 129 confCommon[key].append((keyL, dL[keyL] + "," + dR[keyL])) 130 else: 131 confLOnly[key].append((keyL, dL[keyL])) 132 for keyR in sorted(dR.keys()): 133 if not keyR in dL: # right only 134 confROnly[key].append((keyR, dR[keyR])) 135 print "==Common==" 136 printConf(confCommon) 137 print "==Left Only==" 138 printConf(confLOnly) 139 print "==Right Only==" 140 printConf(confROnly) 141 142def main(argv): 143 if len(argv) < 2: 144 print "diff_products.py product_mk_1 [product_mk_2]" 145 print " compare two product mk files (or just list single product)" 146 print " it must be executed from android source tree root." 147 print " ex) diff_products.py device/asus/grouper/full_grouper.mk " + \ 148 " device/asus/tilapia/full_tilapia.mk" 149 sys.exit(1) 150 151 productLPath = argv[1] 152 productRPath = None 153 if len(argv) == 3: 154 productRPath = argv[2] 155 156 productL = {} 157 productR = {} 158 parseProduct(productLPath, productL) 159 if productRPath is None: 160 for key in PRODUCT_KEYWORDS: 161 productR[key] = {} 162 163 else: 164 parseProduct(productRPath, productR) 165 166 diffTwoProducts(productL, productR) 167 168 169if __name__ == '__main__': 170 main(sys.argv) 171