| # Copyright 2017 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Functions for dealing with determining --tool-prefix.""" |
| |
| import abc |
| import distutils.spawn |
| import logging |
| import os |
| |
| _STATUS_DETECTED = 1 |
| _STATUS_VERIFIED = 2 |
| |
| SRC_ROOT = os.environ.get('CHECKOUT_SOURCE_ROOT', |
| os.path.abspath(os.path.join(os.path.dirname(__file__), |
| os.pardir, os.pardir, os.pardir))) |
| |
| _SAMPLE_TOOL_SUFFIX = 'readelf' |
| |
| |
| class _PathFinder(object): |
| def __init__(self, name, value): |
| self._status = _STATUS_DETECTED if value is not None else 0 |
| self._name = name |
| self._value = value |
| |
| @abc.abstractmethod |
| def Detect(self): |
| pass |
| |
| @abc.abstractmethod |
| def Verify(self): |
| pass |
| |
| def Tentative(self): |
| if self._status < _STATUS_DETECTED: |
| self._value = self.Detect() |
| logging.debug('Detected --%s=%s', self._name, self._value) |
| self._status = _STATUS_DETECTED |
| return self._value |
| |
| def Finalized(self): |
| if self._status < _STATUS_VERIFIED: |
| self.Tentative() |
| self.Verify() |
| logging.info('Using --%s=%s', self._name, self._value) |
| self._status = _STATUS_VERIFIED |
| return self._value |
| |
| |
| class OutputDirectoryFinder(_PathFinder): |
| def __init__(self, value=None, any_path_within_output_directory=None): |
| super(OutputDirectoryFinder, self).__init__( |
| name='output-directory', value=value) |
| self._any_path_within_output_directory = any_path_within_output_directory |
| |
| def Detect(self): |
| # Try and find build.ninja. |
| abs_path = os.path.abspath(self._any_path_within_output_directory) |
| while True: |
| if os.path.exists(os.path.join(abs_path, 'build.ninja')): |
| return os.path.relpath(abs_path) |
| parent_dir = os.path.dirname(abs_path) |
| if parent_dir == abs_path: |
| break |
| abs_path = abs_path = parent_dir |
| |
| # See if CWD=output directory. |
| if os.path.exists('build.ninja'): |
| return '.' |
| return None |
| |
| def Verify(self): |
| if not self._value or not os.path.isdir(self._value): |
| raise Exception('Bad --%s. Path not found: %s' % |
| (self._name, self._value)) |
| |
| |
| class ToolPrefixFinder(_PathFinder): |
| def __init__(self, value=None, output_directory_finder=None, |
| linker_name=None): |
| super(ToolPrefixFinder, self).__init__( |
| name='tool-prefix', value=value) |
| self._output_directory_finder = output_directory_finder |
| self._linker_name = linker_name; |
| |
| def IsLld(self): |
| return self._linker_name.startswith('lld') if self._linker_name else True |
| |
| def Detect(self): |
| output_directory = self._output_directory_finder.Tentative() |
| if output_directory: |
| ret = None |
| if self.IsLld(): |
| ret = os.path.join(SRC_ROOT, 'third_party', 'llvm-build', |
| 'Release+Asserts', 'bin', 'llvm-') |
| else: |
| # Auto-detect from build_vars.txt |
| build_vars = _LoadBuildVars(output_directory) |
| tool_prefix = build_vars.get('android_tool_prefix') |
| if tool_prefix: |
| ret = os.path.normpath(os.path.join(output_directory, tool_prefix)) |
| # Maintain a trailing '/' if needed. |
| if tool_prefix.endswith(os.path.sep): |
| ret += os.path.sep |
| if ret: |
| # Check for output directories that have a stale build_vars.txt. |
| if os.path.isfile(ret + _SAMPLE_TOOL_SUFFIX): |
| return ret |
| else: |
| err_lines = ['tool-prefix not found: %s' % ret] |
| if ret.endswith('llvm-'): |
| err_lines.append('Probably need to run: ' |
| 'tools/clang/scripts/download_objdump.py') |
| raise Exception('\n'.join(err_lines)) |
| from_path = distutils.spawn.find_executable(_SAMPLE_TOOL_SUFFIX) |
| if from_path: |
| return from_path[:-7] |
| return None |
| |
| def Verify(self): |
| if os.path.sep not in self._value: |
| full_path = distutils.spawn.find_executable( |
| self._value + _SAMPLE_TOOL_SUFFIX) |
| else: |
| full_path = self._value + _SAMPLE_TOOL_SUFFIX |
| if not full_path or not os.path.isfile(full_path): |
| raise Exception('Bad --%s. Path not found: %s' % (self._name, full_path)) |
| |
| |
| def _LoadBuildVars(output_directory): |
| build_vars_path = os.path.join(output_directory, 'build_vars.txt') |
| if os.path.exists(build_vars_path): |
| with open(build_vars_path) as f: |
| return dict(l.rstrip().split('=', 1) for l in f if '=' in l) |
| return dict() |
| |
| |
| def FromSrcRootRelative(path): |
| ret = os.path.relpath(os.path.join(SRC_ROOT, path)) |
| # Need to maintain a trailing /. |
| if path.endswith(os.path.sep): |
| ret += os.path.sep |
| return ret |
| |
| |
| def ToSrcRootRelative(path): |
| ret = os.path.relpath(path, SRC_ROOT) |
| # Need to maintain a trailing /. |
| if path.endswith(os.path.sep): |
| ret += os.path.sep |
| return ret |
| |
| |
| def GetCppFiltPath(tool_prefix): |
| if tool_prefix[-5:] == 'llvm-': |
| return tool_prefix + 'cxxfilt' |
| return tool_prefix + 'c++filt' |
| |
| |
| def GetNmPath(tool_prefix): |
| return tool_prefix + 'nm' |
| |
| |
| def GetApkAnalyzerPath(output_directory): |
| build_vars = _LoadBuildVars(output_directory) |
| sdk_analyzer = os.path.normpath(os.path.join( |
| output_directory, build_vars['android_sdk_root'], 'tools', 'bin', |
| 'apkanalyzer')) |
| if os.path.exists(sdk_analyzer): |
| return sdk_analyzer |
| # Older SDKs do not contain the tool, so fall back to the one we know exists. |
| return os.path.join(SRC_ROOT, 'third_party', 'android_sdk', 'public', |
| 'tools', 'bin', 'apkanalyzer') |
| |
| |
| def GetObjDumpPath(tool_prefix): |
| return tool_prefix + 'objdump' |
| |
| |
| def GetReadElfPath(tool_prefix): |
| # Work-around for llvm-readobj bug where 'File: ...' info is not printed: |
| # https://bugs.llvm.org/show_bug.cgi?id=35351 |
| if tool_prefix[-5:] == 'llvm-': |
| return 'readelf' |
| return tool_prefix + 'readelf' |
| |
| |
| def GetBcAnalyzerPath(tool_prefix): |
| if tool_prefix[-5:] != 'llvm-': |
| raise ValueError('BC analyzer is only supported in LLVM.') |
| return tool_prefix + 'bcanalyzer' |