• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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