| # 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 getting paths to things.""" |
| |
| import abc |
| import logging |
| import os |
| |
| _STATUS_DETECTED = 1 |
| _STATUS_VERIFIED = 2 |
| |
| # Src root of SuperSize being run. Not to be confused with src root of the input |
| # binary being archived. |
| _TOOLS_SRC_ROOT = os.environ.get( |
| 'CHECKOUT_SOURCE_ROOT', |
| os.path.abspath( |
| os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, |
| os.pardir))) |
| |
| |
| class _PathFinder: |
| 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().__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 = 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( |
| 'Invalid --output-directory. Path not found: {}\n' |
| 'Use --no-output-directory to disable features that rely on it.'. |
| format(self._value)) |
| |
| def GetSrcRootFromOutputDirectory(output_directory): |
| """Returns the source root directory from output directory. |
| |
| Typical case: '/.../chromium/src/out/Release' -> '/.../chromium/src/'. |
| Heuristic: Look for .gn in the current and successive parent directories. |
| |
| Args: |
| output_directory: Starting point of search. This may be relative to CWD. |
| |
| Returns: |
| Source root directory. |
| """ |
| if output_directory: |
| cur_dir = os.path.abspath(output_directory) |
| while True: |
| gn_path = os.path.join(cur_dir, '.gn') |
| if os.path.isfile(gn_path): |
| return cur_dir |
| cur_dir, prev_dir = os.path.dirname(cur_dir), cur_dir |
| if cur_dir == prev_dir: # Reached root. |
| break |
| logging.warning('Cannot deduce src root from output directory. Falling back ' |
| 'to tools src root.') |
| return _TOOLS_SRC_ROOT |
| |
| |
| def FromToolsSrcRoot(*args): |
| ret = os.path.relpath(os.path.join(_TOOLS_SRC_ROOT, *args)) |
| # Need to maintain a trailing /. |
| if args[-1].endswith(os.path.sep): |
| ret += os.path.sep |
| return ret |
| |
| |
| def _LlvmTool(name): |
| default = FromToolsSrcRoot('third_party', 'llvm-build', 'Release+Asserts', |
| 'bin', 'llvm-') |
| actual = os.environ.get('SUPERSIZE_TOOL_PREFIX', default) |
| # abspath since some executions use cwd= argument. |
| return os.path.abspath(actual + name) |
| |
| |
| def CheckLlvmToolsAvailable(): |
| test_path = _LlvmTool('objdump') |
| if not os.path.isfile(test_path): |
| raise Exception( |
| ('File not found: {}\nProbably need to run: ' |
| 'tools/clang/scripts/update.py --package=objdump').format(test_path)) |
| |
| |
| def GetCppFiltPath(): |
| return _LlvmTool('cxxfilt') |
| |
| |
| def GetDwarfdumpPath(): |
| return _LlvmTool('dwarfdump') |
| |
| |
| def GetNmPath(): |
| return _LlvmTool('nm') |
| |
| |
| def GetReadElfPath(): |
| return _LlvmTool('readelf') |
| |
| |
| def GetBcAnalyzerPath(): |
| return _LlvmTool('bcanalyzer') |
| |
| |
| def GetObjDumpPath(): |
| return _LlvmTool('objdump') |
| |
| |
| def GetDisassembleObjDumpPath(arch): |
| path = None |
| if arch == 'arm': |
| path = FromToolsSrcRoot('third_party', 'android_ndk', 'toolchains', |
| 'arm-linux-androideabi-4.9', 'prebuilt', |
| 'linux-x86_64', 'bin', |
| 'arm-linux-androideabi-objdump') |
| elif arch == 'arm64': |
| path = FromToolsSrcRoot('third_party', 'android_ndk', 'toolchains', |
| 'aarch64-linux-android-4.9', 'prebuilt', |
| 'linux-x86_64', 'bin', |
| 'aarch64-linux-android-objdump') |
| if path and os.path.exists(path): |
| return path |
| |
| logging.warning('Falling back to llvm-objdump for arch %s', arch) |
| return GetObjDumpPath() |
| |
| |
| def GetStripPath(): |
| # Chromium's toolchain uses //buildtools/third_party/eu-strip, but first |
| # look for the test-only "fakestrip" for the sake of tests. |
| fake_strip = _LlvmTool('fakestrip') |
| if os.path.exists(fake_strip): |
| return fake_strip |
| return FromToolsSrcRoot('buildtools', 'third_party', 'eu-strip', 'bin', |
| 'eu-strip') |
| |
| |
| def GetApkAnalyzerPath(): |
| default_path = FromToolsSrcRoot('third_party', 'android_sdk', 'public', |
| 'cmdline-tools', 'latest', 'bin', |
| 'apkanalyzer') |
| return os.environ.get('SUPERSIZE_APK_ANALYZER', default_path) |
| |
| |
| def GetAapt2Path(): |
| default_path = FromToolsSrcRoot('third_party', 'android_build_tools', 'aapt2', |
| 'aapt2') |
| return os.environ.get('SUPERSIZE_AAPT2', default_path) |
| |
| |
| def GetJavaHome(): |
| return FromToolsSrcRoot('third_party', 'jdk', 'current') |
| |
| |
| def GetDefaultJsonConfigPath(): |
| return FromToolsSrcRoot('tools', 'binary_size', 'supersize.json') |
| |
| |
| def GetR8Path(): |
| return FromToolsSrcRoot('third_party', 'r8', 'lib', 'r8.jar') |