• 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('-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