You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.6 KiB
171 lines
5.6 KiB
4 months ago
|
#!/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",
|
||
|
"PRODUCT_BOOT_JARS" ]
|
||
|
|
||
|
# 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)
|