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 23 24AR = 'ar' 25CC = 'gcc' 26 27class MakeParser(object): 28 '''Parses the output of make --dry-run. 29 30 Attributes: 31 ltp_root: string, LTP root directory 32 ar_parser: archive (ar) command argument parser 33 cc_parser: gcc command argument parser 34 result: list of string, result string buffer 35 dir_stack: list of string, directory stack for parsing make commands 36 ''' 37 38 def __init__(self, ltp_root): 39 self.ltp_root = ltp_root 40 ar_parser = argparse.ArgumentParser() 41 ar_parser.add_argument('-r', dest='r', action='store_true') 42 ar_parser.add_argument('-c', dest='c', action='store') 43 self.ar_parser = ar_parser 44 45 cc_parser = argparse.ArgumentParser() 46 cc_parser.add_argument('-D', dest='defines', action='append') 47 cc_parser.add_argument('-I', dest='includes', action='append') 48 cc_parser.add_argument('-l', dest='libraries', action='append') 49 cc_parser.add_argument('-c', dest='compile', action='store_true') 50 cc_parser.add_argument('-o', dest='target', action='store') 51 self.cc_parser = cc_parser 52 53 self.result = [] 54 self.dir_stack = [] 55 56 def GetRelativePath(self, path): 57 '''Get relative path toward LTP directory. 58 59 Args: 60 path: string, a path to convert to relative path 61 ''' 62 if path[0] == '/': 63 path = os.path.realpath(path) 64 else: 65 path = os.path.realpath(self.ltp_root + os.sep + self.dir_stack[-1] 66 + os.sep + path) 67 return os.path.realpath(path).replace(self.ltp_root + os.sep, '') 68 69 def GetRelativePathForExtensions(self, paths, extensions): 70 '''Get relative path toward LTP directory of paths with given extension. 71 72 Args: 73 paths: list of string, paths to convert to relative path 74 extensions: list of string, extension include filter 75 ''' 76 return [self.GetRelativePath(i) for i in paths if i[-1] in extensions] 77 78 def ParseAr(self, line): 79 '''Parse a archive command line. 80 81 Args: 82 line: string, a line of ar command to parse 83 ''' 84 args, unparsed = self.ar_parser.parse_known_args(line.split()[1:]) 85 86 sources = self.GetRelativePathForExtensions(unparsed, ['o']) 87 target = self.GetRelativePath(args.c.replace('"', "")) 88 89 assert len(sources) > 0 90 91 self.result.append("ar['%s'] = %s" % (target, sources)) 92 93 def ParseCc(self, line): 94 '''Parse a gcc command line. 95 96 Args: 97 line: string, a line of gcc command to parse 98 ''' 99 args, unparsed = self.cc_parser.parse_known_args(line.split()[1:]) 100 101 sources = self.GetRelativePathForExtensions(unparsed, ['c', 'o']) 102 includes = [self.GetRelativePath(i) 103 for i in args.includes] if args.includes else [] 104 flags = [] 105 defines = args.defines if args.defines else [] 106 target = self.GetRelativePath(args.target) 107 108 if args.defines: 109 for define in args.defines: 110 flags.append('-D%s' % define) 111 112 flags.extend(i for i in unparsed if i.startswith('-Wno')) 113 114 assert len(sources) > 0 115 116 if args.compile: 117 self.result.append("cc_compile['%s'] = %s" % (target, sources)) 118 else: 119 libraries = args.libraries if args.libraries else [] 120 if sources[0].endswith('.o'): 121 self.result.append("cc_link['%s'] = %s" % (target, sources)) 122 else: 123 self.result.append("cc_compilelink['%s'] = %s" % 124 (target, sources)) 125 self.result.append("cc_libraries['%s'] = %s" % (target, libraries)) 126 127 self.result.append("cc_flags['%s'] = %s" % (target, flags)) 128 self.result.append("cc_includes['%s'] = %s" % (target, includes)) 129 130 def ParseFile(self, input_path): 131 '''Parses the output of make --dry-run. 132 133 Args: 134 input_text: string, output of make --dry-run 135 136 Returns: 137 string, generated directives in the form 138 ar['target.a'] = [ 'srcfile1.o, 'srcfile2.o', ... ] 139 cc_link['target'] = [ 'srcfile1.o', 'srcfile2.o', ... ] 140 cc_compile['target.o'] = [ 'srcfile1.c' ] 141 cc_compilelink['target'] = [ 'srcfile1.c' ] 142 along with optional flags for the above directives in the form 143 cc_flags['target'] = [ '-flag1', '-flag2', ...] 144 cc_includes['target'] = [ 'includepath1', 'includepath2', ...] 145 cc_libraries['target'] = [ 'library1', 'library2', ...] 146 ''' 147 self.result = [] 148 self.dir_stack = [] 149 150 entering_directory = re.compile(r"make.*: Entering directory `(.*)'") 151 leaving_directory = re.compile(r"make.*: Leaving directory `(.*)'") 152 153 with open(input_path, 'r') as f: 154 for line in f: 155 line = line.strip() 156 157 m = entering_directory.match(line) 158 if m: 159 self.dir_stack.append(self.GetRelativePath(m.group(1))) 160 continue 161 162 m = leaving_directory.match(line) 163 if m: 164 self.dir_stack.pop() 165 elif line.startswith(AR): 166 self.ParseAr(line) 167 elif line.startswith(CC): 168 self.ParseCc(line) 169 170 return self.result 171