| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 """Extract source file information from .ninja files.""" |
| 5 |
| 6 import logging |
| 7 import os |
| 8 import re |
| 9 |
| 10 |
| 11 # E.g.: |
| 12 # build obj/.../foo.o: cxx gen/.../foo.cc || obj/.../foo.inputdeps.stamp |
| 13 # build obj/.../libfoo.a: alink obj/.../a.o obj/.../b.o | |
| 14 _REGEX = re.compile(r'build ([^:]+?\.[ao]): \w+ (.*?)(?: \||\n|$)') |
| 15 |
| 16 |
| 17 class SourceFileMapper(object): |
| 18 def __init__(self, output_directory): |
| 19 self._output_directory = output_directory |
| 20 self._ninja_files_to_parse = ['build.ninja'] |
| 21 self._seen_ninja_files = set(('build.ninja',)) |
| 22 self._dep_map = {} |
| 23 |
| 24 def _ParseNinja(self, path): |
| 25 with open(os.path.join(self._output_directory, path)) as obj: |
| 26 self._ParseNinjaLines(obj) |
| 27 |
| 28 def _ParseNinjaLines(self, lines): |
| 29 dep_map = self._dep_map |
| 30 sub_ninjas = [] |
| 31 for line in lines: |
| 32 if line.startswith('subninja '): |
| 33 subpath = line[9:-1] |
| 34 assert subpath not in self._seen_ninja_files, ( |
| 35 'Double include of ' + subpath) |
| 36 self._seen_ninja_files.add(subpath) |
| 37 sub_ninjas.append(subpath) |
| 38 continue |
| 39 m = _REGEX.match(line) |
| 40 if m: |
| 41 output, srcs = m.groups() |
| 42 output = output.replace('\\ ', ' ') |
| 43 assert output not in dep_map, 'Duplicate output: ' + output |
| 44 if output[-1] == 'o': |
| 45 dep_map[output] = srcs.replace('\\ ', ' ') |
| 46 else: |
| 47 srcs = srcs.replace('\\ ', '\b') |
| 48 obj_paths = (s.replace('\b', ' ') for s in srcs.split(' ')) |
| 49 dep_map[output] = {os.path.basename(p): p for p in obj_paths} |
| 50 |
| 51 # Add reversed so that the first on encoundered is at the top of the stack. |
| 52 self._ninja_files_to_parse.extend(reversed(sub_ninjas)) |
| 53 |
| 54 def _Lookup(self, path): |
| 55 """Looks for |path| within self._dep_map. |
| 56 |
| 57 If not found, continues to parse subninjas until it is found or there are no |
| 58 more subninjas. |
| 59 """ |
| 60 ret = self._dep_map.get(path) |
| 61 while not ret and self._ninja_files_to_parse: |
| 62 self._ParseNinja(self._ninja_files_to_parse.pop()) |
| 63 ret = self._dep_map.get(path) |
| 64 return ret |
| 65 |
| 66 def FindSourceForPath(self, path): |
| 67 """Returns the source path for the given object path (or None if not found). |
| 68 |
| 69 Paths for objects within archives should be in the format: foo/bar.a(baz.o) |
| 70 """ |
| 71 if not path.endswith(')'): |
| 72 return self._Lookup(path) |
| 73 |
| 74 # foo/bar.a(baz.o) |
| 75 start_idx = path.index('(') |
| 76 lib_name = path[:start_idx] |
| 77 obj_name = path[start_idx + 1:-1] |
| 78 by_basename = self._Lookup(lib_name) |
| 79 if not by_basename: |
| 80 return None |
| 81 obj_path = by_basename.get(obj_name) |
| 82 if not obj_path: |
| 83 # Found the library, but it doesn't list the .o file. |
| 84 logging.warning('no obj basename for %s', path) |
| 85 return None |
| 86 return self._Lookup(obj_path) |
| 87 |
| 88 def GetParsedFileCount(self): |
| 89 return len(self._seen_ninja_files) |
| OLD | NEW |