Commit 1de693d1 authored by Ximin Luo's avatar Ximin Luo

Add a --tool-prefix-binutils CLI flag. (Closes: #869868)

parent de75fa20
......@@ -24,7 +24,7 @@ import subprocess
import collections
from diffoscope.exc import OutputParsingError
from diffoscope.tools import tool_required
from diffoscope.tools import get_tool_name, tool_required
from diffoscope.tempfiles import get_named_temporary_file
from diffoscope.difference import Difference
......@@ -62,7 +62,7 @@ class Readelf(Command):
@tool_required('readelf')
def cmdline(self):
return ['readelf', '--wide'] + self.readelf_options() + [self.path]
return [get_tool_name('readelf'), '--wide'] + self.readelf_options() + [self.path]
def readelf_options(self):
return [] # noqa
......@@ -168,7 +168,7 @@ class ReadElfSection(Readelf):
def base_options():
if not hasattr(ReadElfSection, '_base_options'):
output = subprocess.check_output(
['readelf', '--help'],
[get_tool_name('readelf'), '--help'],
shell=False,
stderr=subprocess.DEVNULL,
).decode('us-ascii', errors='replace')
......@@ -208,7 +208,7 @@ class ObjdumpSection(Command):
@tool_required('objdump')
def cmdline(self):
return [
'objdump',
get_tool_name('objdump'),
] + self.objdump_options() + [
'--section={}'.format(self._section_name),
self.path,
......@@ -344,7 +344,7 @@ class ElfStringSection(ElfSection):
def get_build_id(path):
try:
output = subprocess.check_output(
['readelf', '--notes', path],
[get_tool_name('readelf'), '--notes', path],
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError as e:
......@@ -362,7 +362,7 @@ def get_build_id(path):
def get_debug_link(path):
try:
output = subprocess.check_output(
['readelf', '--string-dump=.gnu_debuglink', path],
[get_tool_name('readelf'), '--string-dump=.gnu_debuglink', path],
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError as e:
......@@ -388,7 +388,7 @@ class ElfContainer(Container):
super().__init__(*args, **kwargs)
logger.debug("Creating ElfContainer for %s", self.source.path)
cmd = ['readelf', '--wide', '--section-headers', self.source.path]
cmd = [get_tool_name('readelf'), '--wide', '--section-headers', self.source.path]
output = subprocess.check_output(cmd, shell=False, stderr=subprocess.DEVNULL)
has_debug_symbols = False
......@@ -489,7 +489,7 @@ class ElfContainer(Container):
def objcopy(*args):
subprocess.check_call(
('objcopy',) + args,
(get_tool_name('objcopy'),) + args,
shell=False,
stderr=subprocess.DEVNULL,
)
......
......@@ -18,7 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
from .tools import get_package_provider
from .tools import get_tool_name, get_package_provider
class OutputParsingError(Exception):
......@@ -28,7 +28,7 @@ class OutputParsingError(Exception):
class RequiredToolNotFound(Exception):
def __init__(self, command):
self.command = command
self.command = get_tool_name(command)
def get_package(self):
return get_package_provider(self.command)
......
......@@ -251,3 +251,8 @@ EXTERNAL_TOOLS = {
'FreeBSD': 'unzip',
},
}
# May be populated at runtime by remapped names like
# readelf -> arm-none-eabi-readelf, etc
# diff -> gdiff, etc
REMAPPED_TOOL_NAMES = {}
......@@ -29,7 +29,7 @@ import traceback
from . import VERSION
from .path import set_path
from .tools import tool_required, OS_NAMES, get_current_os
from .tools import tool_prepend_prefix, tool_required, OS_NAMES, get_current_os
from .config import Config
from .locale import set_locale
from .logging import setup_logging
......@@ -197,6 +197,10 @@ def create_parser():
help='Threshold for fuzzy-matching '
'(0 to disable, %(default)s is default, 400 is high fuzziness)',
default=Config().fuzzy_threshold).completer=RangeCompleter(400)
group3.add_argument('--tool-prefix-binutils', metavar='PREFIX',
help='Prefix for binutils program names, e.g. '
'"aarch64-linux-gnu-" for a foreign-arch binary or "g" '
'if you\'re on a non-GNU system.')
group3.add_argument('--max-diff-input-lines', metavar='LINES', type=int,
help='Maximum number of lines fed to diff(1) '
'(0 to disable, default: %d)' %
......@@ -354,6 +358,7 @@ def run_diffoscope(parsed_args):
Config().exclude_directory_metadata = parsed_args.exclude_directory_metadata
Config().compute_visual_diffs = PresenterManager().compute_visual_diffs()
Config().check_constraints()
tool_prepend_prefix(parsed_args.tool_prefix_binutils, *"ar as ld ld.bfd nm objcopy objdump ranlib readelf strip".split())
set_path()
set_locale()
path1, path2 = parsed_args.path1, parsed_args.path2
......
......@@ -29,7 +29,7 @@ except ImportError:
from distutils.spawn import find_executable
from .profiling import profile
from .external_tools import EXTERNAL_TOOLS
from .external_tools import EXTERNAL_TOOLS, REMAPPED_TOOL_NAMES
# Memoize calls to ``distutils.spawn.find_executable`` to avoid excessive stat
# calls
......@@ -44,6 +44,17 @@ OS_NAMES = collections.OrderedDict([
])
def get_tool_name(tool):
return REMAPPED_TOOL_NAMES.get(tool, tool)
def tool_prepend_prefix(prefix, *tools):
if not prefix:
return
for tool in tools:
REMAPPED_TOOL_NAMES[tool] = prefix + tool
def tool_required(command):
"""
Decorator that checks if the specified tool is installed
......@@ -67,7 +78,7 @@ def tool_required(command):
performed prior to the `find_executable` tests.
"""
if not find_executable(command):
if not find_executable(get_tool_name(command)):
raise RequiredToolNotFound(command)
with profile('command', command):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment