blob: aaf44b4858a5be416737c7043019f3a2cce69b0c [file] [log] [blame]
# 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'