Home | History | Annotate | Download | only in util
      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