diff options
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/backend/backends.py | 21 | ||||
-rw-r--r-- | mesonbuild/build.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/fortran.py | 6 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/clike.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/visualstudio.py | 3 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 4 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 61 | ||||
-rw-r--r-- | mesonbuild/mlog.py | 23 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 19 | ||||
-rw-r--r-- | mesonbuild/msetup.py | 4 | ||||
-rw-r--r-- | mesonbuild/mtest.py | 58 | ||||
-rw-r--r-- | mesonbuild/scripts/depfixer.py | 41 |
12 files changed, 156 insertions, 88 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 9624ed6..5ce27c3 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1496,18 +1496,33 @@ class Backend: def get_devenv(self) -> build.EnvironmentVariables: env = build.EnvironmentVariables() extra_paths = set() + library_paths = set() for t in self.build.get_targets().values(): cross_built = not self.environment.machines.matches_build_machine(t.for_machine) can_run = not cross_built or not self.environment.need_exe_wrapper() - in_bindir = t.should_install() and not t.get_install_dir(self.environment)[1] - if isinstance(t, build.Executable) and can_run and in_bindir: + in_default_dir = t.should_install() and not t.get_install_dir(self.environment)[1] + if not can_run or not in_default_dir: + continue + tdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t)) + if isinstance(t, build.Executable): # Add binaries that are going to be installed in bindir into PATH # so they get used by default instead of searching on system when # in developer environment. - extra_paths.add(os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))) + extra_paths.add(tdir) if mesonlib.is_windows() or mesonlib.is_cygwin(): # On windows we cannot rely on rpath to run executables from build # directory. We have to add in PATH the location of every DLL needed. extra_paths.update(self.determine_windows_extra_paths(t, [])) + elif isinstance(t, build.SharedLibrary): + # Add libraries that are going to be installed in libdir into + # LD_LIBRARY_PATH. This allows running system applications using + # that library. + library_paths.add(tdir) + if mesonlib.is_windows() or mesonlib.is_cygwin(): + extra_paths.update(library_paths) + elif mesonlib.is_osx(): + env.prepend('DYLD_LIBRARY_PATH', list(library_paths)) + else: + env.prepend('LD_LIBRARY_PATH', list(library_paths)) env.prepend('PATH', list(extra_paths)) return env diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 547394f..adba208 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -433,7 +433,7 @@ class EnvironmentVariables: def get_env(self, full_env: T.Dict[str, str]) -> T.Dict[str, str]: env = full_env.copy() for method, name, values, separator in self.envvars: - env[name] = method(full_env, name, values, separator) + env[name] = method(env, name, values, separator) return env class Target: diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 0cff60a..8264638 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -336,12 +336,6 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler): def get_preprocess_only_args(self) -> T.List[str]: return ['-cpp', '-EP'] - def get_always_args(self) -> T.List[str]: - """Ifort doesn't have -pipe.""" - val = super().get_always_args() - val.remove('-pipe') - return val - def language_stdlib_only_link_flags(self) -> T.List[str]: return ['-lifcore', '-limf'] diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 3932244..70dde60 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -155,7 +155,7 @@ class CLikeCompiler(Compiler): ''' Args that are always-on for all C compilers other than MSVC ''' - return ['-pipe'] + self.get_largefile_args() + return self.get_largefile_args() def get_no_stdinc_args(self) -> T.List[str]: return ['-nostdinc'] diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 2b173eb..763d030 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -412,6 +412,9 @@ class ClangClCompiler(VisualStudioLikeCompiler): super().__init__(target) self.id = 'clang-cl' + # Assembly + self.can_compile_suffixes.add('s') + def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: str) -> T.Tuple[bool, bool]: if mode != 'link': args = args + ['-Werror=unknown-argument'] diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index aa83794..d4179d2 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -905,9 +905,9 @@ def write_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None: properties = OrderedDict() if options.cross_file: - properties['cross_file'] = [os.path.abspath(f) for f in options.cross_file] + properties['cross_file'] = options.cross_file if options.native_file: - properties['native_file'] = [os.path.abspath(f) for f in options.native_file] + properties['native_file'] = options.native_file config['options'] = {str(k): str(v) for k, v in options.cmd_line_options.items()} config['properties'] = properties diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 5cf3dde..7c02631 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -241,9 +241,9 @@ class ConfigureFileHolder(InterpreterObject, ObjectHolder[build.ConfigureFile]): class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.EnvironmentVariables]): - def __init__(self, initial_values=None): + def __init__(self, initial_values=None, subproject: str = ''): MutableInterpreterObject.__init__(self) - ObjectHolder.__init__(self, build.EnvironmentVariables()) + ObjectHolder.__init__(self, build.EnvironmentVariables(), subproject) self.methods.update({'set': self.set_method, 'append': self.append_method, 'prepend': self.prepend_method, @@ -274,12 +274,10 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En return separator def warn_if_has_name(self, name: str) -> None: - # Warn when someone tries to use append() or prepend() on an env var - # which already has an operation set on it. People seem to think that - # multiple append/prepend operations stack, but they don't. + # Multiple append/prepend operations was not supported until 0.58.0. if self.held_object.has_name(name): - mlog.warning(f'Overriding previous value of environment variable {name!r} with a new one', - location=self.current_node) + m = f'Overriding previous value of environment variable {name!r} with a new one' + FeatureNew('0.58.0', m).use(self.subproject) @stringArgs @permittedKwargs({'separator'}) @@ -1886,7 +1884,6 @@ class MesonMain(InterpreterObject): InterpreterObject.__init__(self) self.build = build self.interpreter = interpreter - self._found_source_scripts = {} self.methods.update({'get_compiler': self.get_compiler_method, 'is_cross_build': self.is_cross_build_method, 'has_exe_wrapper': self.has_exe_wrapper_method, @@ -1917,25 +1914,10 @@ class MesonMain(InterpreterObject): }) def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder], args): + if isinstance(prog, (ExecutableHolder, ExternalProgramHolder)): return self.interpreter.backend.get_executable_serialisation([unholder(prog)] + args) - # Prefer scripts in the current source directory - search_dir = os.path.join(self.interpreter.environment.source_dir, - self.interpreter.subdir) - key = (prog, search_dir) - if key in self._found_source_scripts: - found = self._found_source_scripts[key] - elif isinstance(prog, mesonlib.File): - prog = prog.rel_to_builddir(self.interpreter.environment.source_dir) - found = ExternalProgram(prog, search_dir=self.interpreter.environment.build_dir) - else: - found = ExternalProgram(prog, search_dir=search_dir) - - if found.found(): - self._found_source_scripts[key] = found - else: - m = 'Script or command {!r} not found or not executable' - raise InterpreterException(m.format(prog)) + found = self.interpreter.func_find_program({}, prog, {}).held_object es = self.interpreter.backend.get_executable_serialisation([found] + args) es.subproject = self.interpreter.subproject return es @@ -2911,9 +2893,12 @@ external dependencies (including libraries) must go to "dependencies".''') os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) self.global_args_frozen = True - mlog.log() - with mlog.nested(): - mlog.log('Executing subproject', mlog.bold(subp_name), 'method', mlog.bold(method), '\n') + stack = ':'.join(self.subproject_stack + [subp_name]) + m = ['\nExecuting subproject', mlog.bold(stack)] + if method != 'meson': + m += ['method', mlog.bold(method)] + mlog.log(*m,'\n', nested=False) + try: if method == 'meson': return self._do_subproject_meson(subp_name, subdir, default_options, kwargs) @@ -2926,7 +2911,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise except Exception as e: if not required: - with mlog.nested(): + with mlog.nested(subp_name): # Suppress the 'ERROR:' prefix because this exception is not # fatal and VS CI treat any logs with "ERROR:" as fatal. mlog.exception(e, prefix=mlog.yellow('Exception:')) @@ -2938,7 +2923,7 @@ external dependencies (including libraries) must go to "dependencies".''') ast: T.Optional[mparser.CodeBlockNode] = None, build_def_files: T.Optional[T.List[str]] = None, is_translated: bool = False) -> SubprojectHolder: - with mlog.nested(): + with mlog.nested(subp_name): new_build = self.build.copy() subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir, self.modules, default_options, ast=ast, is_translated=is_translated) @@ -2975,7 +2960,7 @@ external dependencies (including libraries) must go to "dependencies".''') return self.subprojects[subp_name] def _do_subproject_cmake(self, subp_name, subdir, subdir_abs, default_options, kwargs): - with mlog.nested(): + with mlog.nested(subp_name): new_build = self.build.copy() prefix = self.coredata.options[OptionKey('prefix')].value @@ -2995,7 +2980,7 @@ external dependencies (including libraries) must go to "dependencies".''') ast = cm_int.pretend_to_be_meson(options.target_options) mlog.log() - with mlog.nested(): + with mlog.nested('cmake-ast'): mlog.log('Processing generated meson AST') # Debug print the generated meson file @@ -4009,6 +3994,9 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.warning('''Custom target input \'%s\' can\'t be converted to File object(s). This will become a hard error in the future.''' % kwargs['input'], location=self.current_node) kwargs['env'] = self.unpack_env_kwarg(kwargs) + if 'command' in kwargs and isinstance(kwargs['command'], list) and kwargs['command']: + if isinstance(kwargs['command'][0], str): + kwargs['command'][0] = self.func_find_program(node, kwargs['command'][0], {}) tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend), self) self.add_target(name, tg.held_object) return tg @@ -4600,7 +4588,7 @@ different subdirectory. ''') else: try: - self.validate_within_subproject(a, '') + self.validate_within_subproject(self.subdir, a) except InterpreterException: mlog.warning('include_directories sandbox violation!') print(f'''The project is trying to access the directory {a} which belongs to a different @@ -4716,9 +4704,6 @@ This warning will become a hard error in a future Meson release. elif arg == '-g': mlog.warning(f'Consider using the built-in debug option instead of using "{arg}".', location=self.current_node) - elif arg == '-pipe': - mlog.warning("You don't need to add -pipe, Meson will use it automatically when it is available.", - location=self.current_node) elif arg.startswith('-fsanitize'): mlog.warning(f'Consider using the built-in option for sanitizers instead of using "{arg}".', location=self.current_node) @@ -4772,7 +4757,7 @@ This warning will become a hard error in a future Meson release. raise InterpreterException('environment first argument must be a dictionary or a list') else: initial_values = {} - return EnvironmentVariablesHolder(initial_values) + return EnvironmentVariablesHolder(initial_values, self.subproject) @stringArgs @noKwargs @@ -4832,6 +4817,8 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey # /opt/vendorsdk/src/file_with_license_restrictions.c return project_root = Path(srcdir, self.root_subdir) + if norm == project_root: + return if project_root not in norm.parents: raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.') if project_root / self.subproject_dir in norm.parents: diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 15fdb8d..38a4805 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -69,7 +69,7 @@ def setup_console() -> None: log_dir = None # type: T.Optional[str] log_file = None # type: T.Optional[T.TextIO] log_fname = 'meson-log.txt' # type: str -log_depth = 0 # type: int +log_depth = [] # type: T.List[str] log_timestamp_start = None # type: T.Optional[float] log_fatal_warnings = False # type: bool log_disable_stdout = False # type: bool @@ -201,7 +201,7 @@ def process_markup(args: T.Sequence[T.Union[AnsiDecorator, str]], keep: bool) -> arr.append(str(arg)) return arr -def force_print(*args: str, **kwargs: T.Any) -> None: +def force_print(*args: str, nested: str, **kwargs: T.Any) -> None: if log_disable_stdout: return iostr = io.StringIO() @@ -209,9 +209,13 @@ def force_print(*args: str, **kwargs: T.Any) -> None: print(*args, **kwargs) raw = iostr.getvalue() - if log_depth > 0: - prepend = '|' * log_depth - raw = prepend + raw.replace('\n', '\n' + prepend, raw.count('\n') - 1) + if log_depth: + prepend = log_depth[-1] + '| ' if nested else '' + lines = [] + for l in raw.split('\n'): + l = l.strip() + lines.append(prepend + l if l else '') + raw = '\n'.join(lines) # _Something_ is going to get printed. try: @@ -246,6 +250,7 @@ def log(*args: T.Union[str, AnsiDecorator], is_error: bool = False, def _log(*args: T.Union[str, AnsiDecorator], is_error: bool = False, **kwargs: T.Any) -> None: + nested = kwargs.pop('nested', True) arr = process_markup(args, False) if log_file is not None: print(*arr, file=log_file, **kwargs) @@ -253,7 +258,7 @@ def _log(*args: T.Union[str, AnsiDecorator], is_error: bool = False, if colorize_console(): arr = process_markup(args, True) if not log_errors_only or is_error: - force_print(*arr, **kwargs) + force_print(*arr, nested=nested, **kwargs) def log_once(*args: T.Union[str, AnsiDecorator], is_error: bool = False, **kwargs: T.Any) -> None: @@ -370,10 +375,10 @@ def format_list(input_list: T.List[str]) -> str: return '' @contextmanager -def nested() -> T.Generator[None, None, None]: +def nested(name: str = '') -> T.Generator[None, None, None]: global log_depth - log_depth += 1 + log_depth.append(name) try: yield finally: - log_depth -= 1 + log_depth.pop() diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 8a48ca8..dc2979e 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -50,11 +50,13 @@ gresource_dep_needed_version = '>= 2.51.1' native_glib_version = None class GnomeModule(ExtensionModule): - gir_dep = None - - install_glib_compile_schemas = False - install_gio_querymodules = [] - install_gtk_update_icon_cache = False + def __init__(self, interpreter: 'Interpreter') -> None: + super().__init__(interpreter) + self.gir_dep = None + self.install_glib_compile_schemas = False + self.install_gio_querymodules = [] + self.install_gtk_update_icon_cache = False + self.devenv = None @staticmethod def _get_native_glib_version(state): @@ -480,6 +482,12 @@ class GnomeModule(ExtensionModule): return girtarget + def _devenv_append(self, varname: str, value: str) -> None: + if self.devenv is None: + self.devenv = build.EnvironmentVariables() + self.interpreter.build.devenv.append(self.devenv) + self.devenv.append(varname, [value]) + def _get_gir_dep(self, state): if not self.gir_dep: self.gir_dep = self._get_native_dep(state, 'gobject-introspection-1.0') @@ -884,6 +892,7 @@ class GnomeModule(ExtensionModule): typelib_cmd += ["--includedir=" + incdir] typelib_target = self._make_typelib_target(state, typelib_output, typelib_cmd, kwargs) + self._devenv_append('GI_TYPELIB_PATH', os.path.join(state.environment.get_build_dir(), state.subdir)) rv = [scan_target, typelib_target] diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 139b476..5c7bb17 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -246,6 +246,10 @@ class MesonApp: b.devenv.append(intr.backend.get_devenv()) build.save(b, dumpfile) if env.first_invocation: + # Use path resolved by coredata because they could have been + # read from a pipe and wrote into a private file. + self.options.cross_file = env.coredata.cross_files + self.options.native_file = env.coredata.config_files coredata.write_cmd_line_file(self.build_dir, self.options) else: coredata.update_cmd_line_file(self.build_dir, self.options) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 42963ff..e54740e 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1129,11 +1129,9 @@ def check_testdata(objs: T.List[TestSerialisation]) -> T.List[TestSerialisation] # Custom waiting primitives for asyncio async def try_wait_one(*awaitables: T.Any, timeout: T.Optional[T.Union[int, float]]) -> None: - try: - await asyncio.wait(awaitables, - timeout=timeout, return_when=asyncio.FIRST_COMPLETED) - except asyncio.TimeoutError: - pass + """Wait for completion of one of the given futures, ignoring timeouts.""" + await asyncio.wait(awaitables, + timeout=timeout, return_when=asyncio.FIRST_COMPLETED) async def queue_iter(q: 'asyncio.Queue[T.Optional[str]]') -> T.AsyncIterator[str]: while True: @@ -1150,14 +1148,37 @@ async def complete(future: asyncio.Future) -> None: except asyncio.CancelledError: pass -async def complete_all(futures: T.Iterable[asyncio.Future]) -> None: - """Wait for completion of all the given futures, ignoring cancellation.""" - while futures: - done, futures = await asyncio.wait(futures, return_when=asyncio.FIRST_EXCEPTION) - # Raise exceptions if needed for all the "done" futures - for f in done: - if not f.cancelled(): +async def complete_all(futures: T.Iterable[asyncio.Future], + timeout: T.Optional[T.Union[int, float]] = None) -> None: + """Wait for completion of all the given futures, ignoring cancellation. + If timeout is not None, raise an asyncio.TimeoutError after the given + time has passed. asyncio.TimeoutError is only raised if some futures + have not completed and none have raised exceptions, even if timeout + is zero.""" + + def check_futures(futures: T.Iterable[asyncio.Future]) -> None: + # Raise exceptions if needed + left = False + for f in futures: + if not f.done(): + left = True + elif not f.cancelled(): f.result() + if left: + raise asyncio.TimeoutError + + # Python is silly and does not have a variant of asyncio.wait with an + # absolute time as deadline. + deadline = None if timeout is None else asyncio.get_event_loop().time() + timeout + while futures and (timeout is None or timeout > 0): + done, futures = await asyncio.wait(futures, timeout=timeout, + return_when=asyncio.FIRST_EXCEPTION) + check_futures(done) + if deadline: + timeout = deadline - asyncio.get_event_loop().time() + + check_futures(futures) + class TestSubprocess: def __init__(self, p: asyncio.subprocess.Process, @@ -1169,6 +1190,7 @@ class TestSubprocess: self.stdo_task = None # type: T.Optional[asyncio.Future[str]] self.stde_task = None # type: T.Optional[asyncio.Future[str]] self.postwait_fn = postwait_fn # type: T.Callable[[], None] + self.all_futures = [] # type: T.List[asyncio.Future] def stdout_lines(self, console_mode: ConsoleUser) -> T.AsyncIterator[str]: q = asyncio.Queue() # type: asyncio.Queue[T.Optional[str]] @@ -1183,9 +1205,11 @@ class TestSubprocess: if self.stdo_task is None and self.stdout is not None: decode_coro = read_decode(self._process.stdout, console_mode) self.stdo_task = asyncio.ensure_future(decode_coro) + self.all_futures.append(self.stdo_task) if self.stderr is not None and self.stderr != asyncio.subprocess.STDOUT: decode_coro = read_decode(self._process.stderr, console_mode) self.stde_task = asyncio.ensure_future(decode_coro) + self.all_futures.append(self.stde_task) return self.stdo_task, self.stde_task @@ -1238,11 +1262,13 @@ class TestSubprocess: p = self._process result = None additional_error = None + + self.all_futures.append(asyncio.ensure_future(p.wait())) try: - await try_wait_one(p.wait(), timeout=timeout) - if p.returncode is None: - additional_error = await self._kill() - result = TestResult.TIMEOUT + await complete_all(self.all_futures, timeout=timeout) + except asyncio.TimeoutError: + additional_error = await self._kill() + result = TestResult.TIMEOUT except asyncio.CancelledError: # The main loop must have seen Ctrl-C. additional_error = await self._kill() diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 8ce74ee..c215749 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -13,8 +13,12 @@ # limitations under the License. -import sys, struct -import shutil, subprocess +import sys +import os +import stat +import struct +import shutil +import subprocess import typing as T from ..mesonlib import OrderedSet @@ -120,9 +124,9 @@ class Elf(DataSizes): def __init__(self, bfile: str, verbose: bool = True) -> None: self.bfile = bfile self.verbose = verbose - self.bf = open(bfile, 'r+b') self.sections = [] # type: T.List[SectionHeader] self.dynamic = [] # type: T.List[DynamicEntry] + self.open_bf(bfile) try: (self.ptrsize, self.is_le) = self.detect_elf_type() super().__init__(self.ptrsize, self.is_le) @@ -130,19 +134,40 @@ class Elf(DataSizes): self.parse_sections() self.parse_dynamic() except (struct.error, RuntimeError): - self.bf.close() + self.close_bf() raise + def open_bf(self, bfile: str) -> None: + self.bf = None + self.bf_perms = None + try: + self.bf = open(bfile, 'r+b') + except PermissionError as e: + self.bf_perms = stat.S_IMODE(os.lstat(bfile).st_mode) + os.chmod(bfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + try: + self.bf = open(bfile, 'r+b') + except Exception: + os.chmod(bfile, self.bf_perms) + self.bf_perms = None + raise e + + def close_bf(self) -> None: + if self.bf is not None: + if self.bf_perms is not None: + os.fchmod(self.bf.fileno(), self.bf_perms) + self.bf_perms = None + self.bf.close() + self.bf = None + def __enter__(self) -> 'Elf': return self def __del__(self) -> None: - if self.bf: - self.bf.close() + self.close_bf() def __exit__(self, exc_type: T.Any, exc_value: T.Any, traceback: T.Any) -> None: - self.bf.close() - self.bf = None + self.close_bf() def detect_elf_type(self) -> T.Tuple[int, bool]: data = self.bf.read(6) |