Files
revng-revng/python/revng/cli/support.py
T
Giacomo Vercesi e4adbe8134 revng.support: add get_root
Add the get_root function which will return the absolute path of the
root, relatively to the file location in support
2022-10-24 09:58:55 +02:00

145 lines
4.2 KiB
Python

#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
import os
import shlex
import signal
import subprocess
import sys
from dataclasses import dataclass, field
from typing import Any, Dict, Iterable, List, NoReturn, Optional, Union
from revng.support import get_root
from revng.support.collect import collect_files, collect_libraries
from revng.support.elf import is_executable
try:
from shutil import which
except ImportError:
from backports.shutil_which import which # type: ignore
script_path = os.path.dirname(os.path.realpath(__file__))
root_path = str(get_root())
@dataclass
class Options:
parsed_args: Any
remaining_args: List[str]
command_prefix: List[str]
verbose: bool
dry_run: bool
keep_temporaries: bool
search_prefixes: List[str] = field(default_factory=lambda: [root_path])
def shlex_join(split_command: Iterable[str]) -> str:
return " ".join(shlex.quote(arg) for arg in split_command)
def log_error(msg: str):
sys.stderr.write(msg + "\n")
def wrap(args: List[str], command_prefix: List[str]):
return command_prefix + args
def relative(path: str) -> str:
return os.path.relpath(path, os.getcwd())
def try_run(
command, options: Options, environment: Optional[Dict[str, str]] = None, do_exec=False
) -> Union[int, NoReturn]:
if is_executable(command[0]):
command = wrap(command, options.command_prefix)
if options.verbose:
program_path = relative(command[0])
sys.stderr.write("{}\n\n".format(" \\\n ".join([program_path] + command[1:])))
if options.dry_run:
return 0
if environment is None:
environment = dict(os.environ)
if do_exec:
return os.execvpe(command[0], command, environment)
else:
signal.signal(signal.SIGINT, signal.SIG_IGN)
process = subprocess.Popen(
command,
preexec_fn=lambda: signal.signal(signal.SIGINT, signal.SIG_DFL),
env=environment,
)
return_code = process.wait()
signal.signal(signal.SIGINT, signal.SIG_DFL)
return return_code
def run(command, options: Options, environment: Optional[Dict[str, str]] = None, do_exec=False):
result = try_run(command, options, environment, do_exec)
if result != 0:
sys.exit(result)
def get_command(command: str, search_prefixes: Iterable[str]) -> str:
if command.startswith("revng-"):
for executable in collect_files(search_prefixes, ["libexec", "revng"], command):
return executable
log_error(f'Couldn\'t find "{command}"')
assert False
path = which(command)
if not path:
log_error(f'Couldn\'t find "{command}".')
assert False
return os.path.abspath(path)
def interleave(base: List[str], repeat: str):
return list(sum(zip([repeat] * len(base), base), ()))
def handle_asan(dependencies: Iterable[str], search_prefixes: Iterable[str]) -> List[str]:
libasan = [name for name in dependencies if ("libasan." in name or "libclang_rt.asan" in name)]
if len(libasan) != 1:
return []
libasan_path = relative(libasan[0])
original_asan_options = os.environ.get("ASAN_OPTIONS", "")
if original_asan_options:
asan_options = dict([option.split("=") for option in original_asan_options.split(":")])
else:
asan_options = {}
asan_options["abort_on_error"] = "1"
new_asan_options = ":".join(["=".join(option) for option in asan_options.items()])
# Use `sh` instead of `env` since `env` sometimes is not a real executable
# but a shebang script spawning /usr/bin/coreutils, which makes gdb unhappy
return [
get_command("sh", search_prefixes),
"-c",
f'ASAN_OPTIONS={new_asan_options} ld.so --preload {libasan_path} "$0" "$@"',
]
def build_command_with_loads(command: str, args: Iterable[str], options: Options) -> List[str]:
(to_load, dependencies) = collect_libraries(options.search_prefixes)
prefix = handle_asan(dependencies, options.search_prefixes)
return (
prefix
+ [relative(get_command(command, options.search_prefixes))]
+ interleave(to_load, "-load")
+ args
)
def executable_name() -> str:
return os.path.basename(sys.argv[0])