aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/backends.py21
-rw-r--r--mesonbuild/build.py2
-rw-r--r--mesonbuild/compilers/fortran.py6
-rw-r--r--mesonbuild/compilers/mixins/clike.py2
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py3
-rw-r--r--mesonbuild/coredata.py4
-rw-r--r--mesonbuild/interpreter.py61
-rw-r--r--mesonbuild/mlog.py23
-rw-r--r--mesonbuild/modules/gnome.py19
-rw-r--r--mesonbuild/msetup.py4
-rw-r--r--mesonbuild/mtest.py58
-rw-r--r--mesonbuild/scripts/depfixer.py41
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)