普通文本  |  171行  |  5.62 KB

#!/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)