#!/usr/bin/env python # # Copyright (C) 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # diff_products.py product_mk_1 [product_mk_2] # compare two product congifuraitons or analyze one product configuration. # List PRODUCT_PACKAGES, PRODUCT_COPY_FILES, and etc. import os import sys PRODUCT_KEYWORDS = [ "PRODUCT_PACKAGES", "PRODUCT_COPY_FILES", "PRODUCT_PROPERTY_OVERRIDES" ] # Top level data # { "PRODUCT_PACKAGES": {...}} # PRODCT_PACKAGES { "libstagefright": "path_to_the_mk_file" } def removeTrailingParen(path): if path.endswith(")"): return path[:-1] else: return path def substPathVars(path, parentPath): path_ = path.replace("$(SRC_TARGET_DIR)", "build/target") path__ = path_.replace("$(LOCAL_PATH)", os.path.dirname(parentPath)) return path__ def parseLine(line, productData, productPath, overrideProperty = False): #print "parse:" + line words = line.split() if len(words) < 2: return if words[0] in PRODUCT_KEYWORDS: # Override only for include if overrideProperty and words[1] == ":=": if len(productData[words[0]]) != 0: print "** Warning: overriding property " + words[0] + " that was:" + \ productData[words[0]] productData[words[0]] = {} d = productData[words[0]] for word in words[2:]: # TODO: parsing those $( cases in better way if word.startswith("$(foreach"): # do not parse complex calls print "** Warning: parseLine too complex line in " + productPath + " : " + line return d[word] = productPath elif words[0] == "$(call" and words[1].startswith("inherit-product"): parseProduct(substPathVars(removeTrailingParen(words[2]), productPath), productData) elif words[0] == "include": parseProduct(substPathVars(words[1], productPath), productData, True) elif words[0] == "-include": parseProduct(substPathVars(words[1], productPath), productData, True) def parseProduct(productPath, productData, overrideProperty = False): """parse given product mk file and add the result to productData dict""" if not os.path.exists(productPath): print "** Warning cannot find file " + productPath return for key in PRODUCT_KEYWORDS: if not key in productData: productData[key] = {} multiLineBuffer = [] #for storing multiple lines inMultiLine = False for line in open(productPath): line_ = line.strip() if inMultiLine: if line_.endswith("\\"): multiLineBuffer.append(line_[:-1]) else: multiLineBuffer.append(line_) parseLine(" ".join(multiLineBuffer), productData, productPath) inMultiLine = False else: if line_.endswith("\\"): inMultiLine = True multiLineBuffer = [] multiLineBuffer.append(line_[:-1]) else: parseLine(line_, productData, productPath) #print productData def printConf(confList): for key in PRODUCT_KEYWORDS: print " *" + key if key in confList: for (k, path) in confList[key]: print " " + k + ": " + path def diffTwoProducts(productL, productR): """compare two products and comapre in the order of common, left only, right only items. productL and productR are dictionary""" confCommon = {} confLOnly = {} confROnly = {} for key in PRODUCT_KEYWORDS: dL = productL[key] dR = productR[key] confCommon[key] = [] confLOnly[key] = [] confROnly[key] = [] for keyL in sorted(dL.keys()): if keyL in dR: if dL[keyL] == dR[keyL]: confCommon[key].append((keyL, dL[keyL])) else: confCommon[key].append((keyL, dL[keyL] + "," + dR[keyL])) else: confLOnly[key].append((keyL, dL[keyL])) for keyR in sorted(dR.keys()): if not keyR in dL: # right only confROnly[key].append((keyR, dR[keyR])) print "==Common==" printConf(confCommon) print "==Left Only==" printConf(confLOnly) print "==Right Only==" printConf(confROnly) def main(argv): if len(argv) < 2: print "diff_products.py product_mk_1 [product_mk_2]" print " compare two product mk files (or just list single product)" print " it must be executed from android source tree root." print " ex) diff_products.py device/asus/grouper/full_grouper.mk " + \ " device/asus/tilapia/full_tilapia.mk" sys.exit(1) productLPath = argv[1] productRPath = None if len(argv) == 3: productRPath = argv[2] productL = {} productR = {} parseProduct(productLPath, productL) if productRPath is None: for key in PRODUCT_KEYWORDS: productR[key] = {} else: parseProduct(productRPath, productR) diffTwoProducts(productL, productR) if __name__ == '__main__': main(sys.argv)