Home | History | Annotate | Download | only in tools
      1 #
      2 # Copyright 2016 - The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #     http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 
     17 import argparse
     18 import os
     19 import os.path
     20 import sys
     21 import re
     22 import fileinput
     23 import pprint
     24 
     25 AR = 'ar'
     26 CC = 'gcc'
     27 
     28 class MakeParser(object):
     29     '''Parses the output of make --dry-run.
     30 
     31     Attributes:
     32         ltp_root: string, LTP root directory
     33         ar_parser: archive (ar) command argument parser
     34         cc_parser: gcc command argument parser
     35         result: list of string, result string buffer
     36         dir_stack: list of string, directory stack for parsing make commands
     37     '''
     38 
     39     def __init__(self, ltp_root):
     40         self.ltp_root = ltp_root
     41         ar_parser = argparse.ArgumentParser()
     42         ar_parser.add_argument('-r', dest='r', action='store_true')
     43         ar_parser.add_argument('-c', dest='c', action='store')
     44         self.ar_parser = ar_parser
     45 
     46         cc_parser = argparse.ArgumentParser()
     47         cc_parser.add_argument('-D', dest='defines', action='append')
     48         cc_parser.add_argument('-I', dest='includes', action='append')
     49         cc_parser.add_argument('-l', dest='libraries', action='append')
     50         cc_parser.add_argument('-c', dest='compile', action='store_true')
     51         cc_parser.add_argument('-o', dest='target', action='store')
     52         self.cc_parser = cc_parser
     53 
     54         self.result = []
     55         self.dir_stack = []
     56 
     57     def GetRelativePath(self, path):
     58         '''Get relative path toward LTP directory.
     59 
     60         Args:
     61             path: string, a path to convert to relative path
     62         '''
     63         if path[0] == '/':
     64             path = os.path.realpath(path)
     65         else:
     66             path = os.path.realpath(self.ltp_root + os.sep + self.dir_stack[-1]
     67                                     + os.sep + path)
     68         return os.path.realpath(path).replace(self.ltp_root + os.sep, '')
     69 
     70     def GetRelativePathForExtensions(self, paths, extensions):
     71         '''Get relative path toward LTP directory of paths with given extension.
     72 
     73         Args:
     74             paths: list of string, paths to convert to relative path
     75             extensions: list of string, extension include filter
     76         '''
     77         return [self.GetRelativePath(i) for i in paths if i[-1] in extensions]
     78 
     79     def ParseAr(self, line):
     80         '''Parse a archive command line.
     81 
     82         Args:
     83             line: string, a line of ar command to parse
     84         '''
     85         args, unparsed = self.ar_parser.parse_known_args(line.split()[1:])
     86 
     87         sources = self.GetRelativePathForExtensions(unparsed, ['o'])
     88 
     89         # Support 'ar' command line with or without hyphens (-)
     90         #  e.g.:
     91         #    1. ar rcs libfoo.a foo1.o foo2.o
     92         #    2. ar -rc "libfoo.a" foo1.o foo2.o; ranlib "libfoo.a"
     93         target = None
     94         if not args.c and not args.r:
     95             for path in unparsed:
     96                 if path.endswith('.a'):
     97                     target = self.GetRelativePath(path)
     98                     break
     99         else:
    100             target = self.GetRelativePath(args.c.replace('"', ""))
    101 
    102         assert len(sources) > 0
    103         assert target != None
    104 
    105         self.result.append("ar['%s'] = %s" % (target, sources))
    106 
    107     def ParseCc(self, line):
    108         '''Parse a gcc command line.
    109 
    110         Args:
    111             line: string, a line of gcc command to parse
    112         '''
    113         args, unparsed = self.cc_parser.parse_known_args(line.split()[1:])
    114 
    115         sources = self.GetRelativePathForExtensions(unparsed, ['c', 'o'])
    116         includes = [self.GetRelativePath(i)
    117                     for i in args.includes] if args.includes else []
    118         flags = []
    119         defines = args.defines if args.defines else []
    120         target = self.GetRelativePath(args.target)
    121 
    122         if args.defines:
    123             for define in args.defines:
    124                 flags.append('-D%s' % define)
    125 
    126         flags.extend(i for i in unparsed if i.startswith('-Wno'))
    127 
    128         assert len(sources) > 0
    129 
    130         if args.compile:
    131             self.result.append("cc_compile['%s'] = %s" % (target, sources))
    132         else:
    133             libraries = args.libraries if args.libraries else []
    134             if sources[0].endswith('.o'):
    135                 self.result.append("cc_link['%s'] = %s" % (target, sources))
    136             else:
    137                 self.result.append("cc_compilelink['%s'] = %s" %
    138                                    (target, sources))
    139             self.result.append("cc_libraries['%s'] = %s" % (target, libraries))
    140 
    141         self.result.append("cc_flags['%s'] = %s" % (target, flags))
    142         self.result.append("cc_includes['%s'] = %s" % (target, includes))
    143 
    144     def ParseFile(self, input_path):
    145         '''Parses the output of make --dry-run.
    146 
    147         Args:
    148             input_text: string, output of make --dry-run
    149 
    150         Returns:
    151             string, generated directives in the form
    152                     ar['target.a'] = [ 'srcfile1.o, 'srcfile2.o', ... ]
    153                     cc_link['target'] = [ 'srcfile1.o', 'srcfile2.o', ... ]
    154                     cc_compile['target.o'] = [ 'srcfile1.c' ]
    155                     cc_compilelink['target'] = [ 'srcfile1.c' ]
    156                 along with optional flags for the above directives in the form
    157                     cc_flags['target'] = [ '-flag1', '-flag2', ...]
    158                     cc_includes['target'] = [ 'includepath1', 'includepath2', ...]
    159                     cc_libraries['target'] = [ 'library1', 'library2', ...]
    160         '''
    161         self.result = []
    162         self.dir_stack = []
    163 
    164         entering_directory = re.compile(r"make.*: Entering directory [`,'](.*)'")
    165         leaving_directory = re.compile(r"make.*: Leaving directory [`,'](.*)'")
    166 
    167         with open(input_path, 'r') as f:
    168             for line in f:
    169                 line = line.strip()
    170 
    171                 m = entering_directory.match(line)
    172                 if m:
    173                     self.dir_stack.append(self.GetRelativePath(m.group(1)))
    174                     continue
    175 
    176                 m = leaving_directory.match(line)
    177                 if m:
    178                     self.dir_stack.pop()
    179                 elif line.startswith(AR):
    180                     self.ParseAr(line)
    181                 elif line.startswith(CC):
    182                     self.ParseCc(line)
    183 
    184         return self.result
    185 
    186 def main():
    187     arg_parser = argparse.ArgumentParser(
    188         description='Parse the LTP make --dry-run output into a list')
    189     arg_parser.add_argument(
    190         '--ltp-root',
    191         dest='ltp_root',
    192         required=True,
    193         help='LTP Root dir')
    194     arg_parser.add_argument(
    195         '--dry-run-file',
    196         dest='input_path',
    197         required=True,
    198         help='Path to LTP make --dry-run output file')
    199     args = arg_parser.parse_args()
    200 
    201     make_parser = MakeParser(args.ltp_root)
    202     result = make_parser.ParseFile(args.input_path)
    203 
    204     print pprint.pprint(result)
    205 
    206 if __name__ == '__main__':
    207     main()
    208