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 17import argparse 18import os 19import os.path 20import sys 21import re 22import fileinput 23import pprint 24 25AR = 'ar' 26CC = 'gcc' 27 28class 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 186def 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 206if __name__ == '__main__': 207 main() 208