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     "PRODUCT_BOOT_JARS" ]
     32 
     33 # Top level data
     34 # { "PRODUCT_PACKAGES": {...}}
     35 # PRODCT_PACKAGES { "libstagefright": "path_to_the_mk_file" }
     36 
     37 def removeTrailingParen(path):
     38     if path.endswith(")"):
     39         return path[:-1]
     40     else:
     41         return path
     42 
     43 def 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 
     49 def 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 
     75 def 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 
    105 def 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 
    112 def 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 
    142 def 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 
    169 if __name__ == '__main__':
    170     main(sys.argv)
    171