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('-L', dest='libraries_path', action='append') 51 cc_parser.add_argument('-c', dest='compile', action='store_true') 52 cc_parser.add_argument('-o', dest='target', action='store') 53 self.cc_parser = cc_parser 54 55 self.result = [] 56 self.dir_stack = [] 57 58 def GetRelativePath(self, path): 59 '''Get relative path toward LTP directory. 60 61 Args: 62 path: string, a path to convert to relative path 63 ''' 64 if path[0] == '/': 65 path = os.path.realpath(path) 66 else: 67 path = os.path.realpath(self.ltp_root + os.sep + self.dir_stack[-1] 68 + os.sep + path) 69 return os.path.realpath(path).replace(self.ltp_root + os.sep, '') 70 71 def GetRelativePathForExtensions(self, paths, extensions): 72 '''Get relative path toward LTP directory of paths with given extension. 73 74 Args: 75 paths: list of string, paths to convert to relative path 76 extensions: list of string, extension include filter 77 ''' 78 return [self.GetRelativePath(i) for i in paths if i[-1] in extensions] 79 80 def ParseAr(self, line): 81 '''Parse a archive command line. 82 83 Args: 84 line: string, a line of ar command to parse 85 ''' 86 args, unparsed = self.ar_parser.parse_known_args(line.split()[1:]) 87 88 sources = self.GetRelativePathForExtensions(unparsed, ['o']) 89 90 # Support 'ar' command line with or without hyphens (-) 91 # e.g.: 92 # 1. ar rcs libfoo.a foo1.o foo2.o 93 # 2. ar -rc "libfoo.a" foo1.o foo2.o; ranlib "libfoo.a" 94 target = None 95 if not args.c and not args.r: 96 for path in unparsed: 97 if path.endswith('.a'): 98 target = self.GetRelativePath(path) 99 break 100 else: 101 target = self.GetRelativePath(args.c.replace('"', "")) 102 103 assert len(sources) > 0 104 assert target != None 105 106 self.result.append("ar['%s'] = %s" % (target, sources)) 107 108 def ParseCc(self, line): 109 '''Parse a gcc command line. 110 111 Args: 112 line: string, a line of gcc command to parse 113 ''' 114 args, unparsed = self.cc_parser.parse_known_args(line.split()[1:]) 115 116 sources = self.GetRelativePathForExtensions(unparsed, ['c', 'o']) 117 includes = [self.GetRelativePath(i) 118 for i in args.includes] if args.includes else [] 119 flags = [] 120 defines = args.defines if args.defines else [] 121 target = self.GetRelativePath(args.target) 122 123 if args.defines: 124 for define in args.defines: 125 flags.append('-D%s' % define) 126 127 flags.extend(i for i in unparsed if i.startswith('-Wno')) 128 129 assert len(sources) > 0 130 131 if args.compile: 132 self.result.append("cc_compile['%s'] = %s" % (target, sources)) 133 else: 134 libraries = args.libraries if args.libraries else [] 135 if sources[0].endswith('.o'): 136 self.result.append("cc_link['%s'] = %s" % (target, sources)) 137 else: 138 self.result.append("cc_compilelink['%s'] = %s" % 139 (target, sources)) 140 self.result.append("cc_libraries['%s'] = %s" % (target, libraries)) 141 142 self.result.append("cc_flags['%s'] = %s" % (target, flags)) 143 self.result.append("cc_includes['%s'] = %s" % (target, includes)) 144 145 def ParseFile(self, input_path): 146 '''Parses the output of make --dry-run. 147 148 Args: 149 input_text: string, output of make --dry-run 150 151 Returns: 152 string, generated directives in the form 153 ar['target.a'] = [ 'srcfile1.o, 'srcfile2.o', ... ] 154 cc_link['target'] = [ 'srcfile1.o', 'srcfile2.o', ... ] 155 cc_compile['target.o'] = [ 'srcfile1.c' ] 156 cc_compilelink['target'] = [ 'srcfile1.c' ] 157 along with optional flags for the above directives in the form 158 cc_flags['target'] = [ '-flag1', '-flag2', ...] 159 cc_includes['target'] = [ 'includepath1', 'includepath2', ...] 160 cc_libraries['target'] = [ 'library1', 'library2', ...] 161 ''' 162 self.result = [] 163 self.dir_stack = [] 164 165 entering_directory = re.compile(r"make.*: Entering directory [`,'](.*)'") 166 leaving_directory = re.compile(r"make.*: Leaving directory [`,'](.*)'") 167 168 with open(input_path, 'r') as f: 169 for line in f: 170 line = line.strip() 171 172 m = entering_directory.match(line) 173 if m: 174 self.dir_stack.append(self.GetRelativePath(m.group(1))) 175 continue 176 177 m = leaving_directory.match(line) 178 if m: 179 self.dir_stack.pop() 180 elif line.startswith(AR): 181 self.ParseAr(line) 182 elif line.startswith(CC): 183 self.ParseCc(line) 184 185 return self.result 186 187def main(): 188 arg_parser = argparse.ArgumentParser( 189 description='Parse the LTP make --dry-run output into a list') 190 arg_parser.add_argument( 191 '--ltp-root', 192 dest='ltp_root', 193 required=True, 194 help='LTP Root dir') 195 arg_parser.add_argument( 196 '--dry-run-file', 197 dest='input_path', 198 required=True, 199 help='Path to LTP make --dry-run output file') 200 args = arg_parser.parse_args() 201 202 make_parser = MakeParser(args.ltp_root) 203 result = make_parser.ParseFile(args.input_path) 204 205 print(pprint.pprint(result)) 206 207if __name__ == '__main__': 208 main() 209