blob: 4b6e3eff915b16f3ae7300d0fec35618e8614248 [file] [log] [blame]
#
# Copyright 2016 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
import os
import os.path
import sys
import re
import fileinput
import pprint
AR = 'ar'
CC = 'gcc'
class MakeParser(object):
'''Parses the output of make --dry-run.
Attributes:
ltp_root: string, LTP root directory
ar_parser: archive (ar) command argument parser
cc_parser: gcc command argument parser
result: list of string, result string buffer
dir_stack: list of string, directory stack for parsing make commands
'''
def __init__(self, ltp_root):
self.ltp_root = ltp_root
ar_parser = argparse.ArgumentParser()
ar_parser.add_argument('-r', dest='r', action='store_true')
ar_parser.add_argument('-c', dest='c', action='store')
self.ar_parser = ar_parser
cc_parser = argparse.ArgumentParser()
cc_parser.add_argument('-D', dest='defines', action='append')
cc_parser.add_argument('-I', dest='includes', action='append')
cc_parser.add_argument('-l', dest='libraries', action='append')
cc_parser.add_argument('-c', dest='compile', action='store_true')
cc_parser.add_argument('-o', dest='target', action='store')
self.cc_parser = cc_parser
self.result = []
self.dir_stack = []
def GetRelativePath(self, path):
'''Get relative path toward LTP directory.
Args:
path: string, a path to convert to relative path
'''
if path[0] == '/':
path = os.path.realpath(path)
else:
path = os.path.realpath(self.ltp_root + os.sep + self.dir_stack[-1]
+ os.sep + path)
return os.path.realpath(path).replace(self.ltp_root + os.sep, '')
def GetRelativePathForExtensions(self, paths, extensions):
'''Get relative path toward LTP directory of paths with given extension.
Args:
paths: list of string, paths to convert to relative path
extensions: list of string, extension include filter
'''
return [self.GetRelativePath(i) for i in paths if i[-1] in extensions]
def ParseAr(self, line):
'''Parse a archive command line.
Args:
line: string, a line of ar command to parse
'''
args, unparsed = self.ar_parser.parse_known_args(line.split()[1:])
sources = self.GetRelativePathForExtensions(unparsed, ['o'])
# Support 'ar' command line with or without hyphens (-)
# e.g.:
# 1. ar rcs libfoo.a foo1.o foo2.o
# 2. ar -rc "libfoo.a" foo1.o foo2.o; ranlib "libfoo.a"
target = None
if not args.c and not args.r:
for path in unparsed:
if path.endswith('.a'):
target = self.GetRelativePath(path)
break
else:
target = self.GetRelativePath(args.c.replace('"', ""))
assert len(sources) > 0
assert target != None
self.result.append("ar['%s'] = %s" % (target, sources))
def ParseCc(self, line):
'''Parse a gcc command line.
Args:
line: string, a line of gcc command to parse
'''
args, unparsed = self.cc_parser.parse_known_args(line.split()[1:])
sources = self.GetRelativePathForExtensions(unparsed, ['c', 'o'])
includes = [self.GetRelativePath(i)
for i in args.includes] if args.includes else []
flags = []
defines = args.defines if args.defines else []
target = self.GetRelativePath(args.target)
if args.defines:
for define in args.defines:
flags.append('-D%s' % define)
flags.extend(i for i in unparsed if i.startswith('-Wno'))
assert len(sources) > 0
if args.compile:
self.result.append("cc_compile['%s'] = %s" % (target, sources))
else:
libraries = args.libraries if args.libraries else []
if sources[0].endswith('.o'):
self.result.append("cc_link['%s'] = %s" % (target, sources))
else:
self.result.append("cc_compilelink['%s'] = %s" %
(target, sources))
self.result.append("cc_libraries['%s'] = %s" % (target, libraries))
self.result.append("cc_flags['%s'] = %s" % (target, flags))
self.result.append("cc_includes['%s'] = %s" % (target, includes))
def ParseFile(self, input_path):
'''Parses the output of make --dry-run.
Args:
input_text: string, output of make --dry-run
Returns:
string, generated directives in the form
ar['target.a'] = [ 'srcfile1.o, 'srcfile2.o', ... ]
cc_link['target'] = [ 'srcfile1.o', 'srcfile2.o', ... ]
cc_compile['target.o'] = [ 'srcfile1.c' ]
cc_compilelink['target'] = [ 'srcfile1.c' ]
along with optional flags for the above directives in the form
cc_flags['target'] = [ '-flag1', '-flag2', ...]
cc_includes['target'] = [ 'includepath1', 'includepath2', ...]
cc_libraries['target'] = [ 'library1', 'library2', ...]
'''
self.result = []
self.dir_stack = []
entering_directory = re.compile(r"make.*: Entering directory [`,'](.*)'")
leaving_directory = re.compile(r"make.*: Leaving directory [`,'](.*)'")
with open(input_path, 'r') as f:
for line in f:
line = line.strip()
m = entering_directory.match(line)
if m:
self.dir_stack.append(self.GetRelativePath(m.group(1)))
continue
m = leaving_directory.match(line)
if m:
self.dir_stack.pop()
elif line.startswith(AR):
self.ParseAr(line)
elif line.startswith(CC):
self.ParseCc(line)
return self.result
def main():
arg_parser = argparse.ArgumentParser(
description='Parse the LTP make --dry-run output into a list')
arg_parser.add_argument(
'--ltp-root',
dest='ltp_root',
required=True,
help='LTP Root dir')
arg_parser.add_argument(
'--dry-run-file',
dest='input_path',
required=True,
help='Path to LTP make --dry-run output file')
args = arg_parser.parse_args()
make_parser = MakeParser(args.ltp_root)
result = make_parser.ParseFile(args.input_path)
print pprint.pprint(result)
if __name__ == '__main__':
main()