Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize determine_rpath_dirs #14207

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 65 additions & 60 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,49 @@ def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None,
return vs2022backend.Vs2022Backend(build, interpreter, gen_lite = True)
return None


# Match rpath formats:
# -Wl,-rpath=
# -Wl,-rpath,
# Match solaris style compat runpath formats:
# -Wl,-R
# -Wl,-R,
# Match symbols formats:
# -Wl,--just-symbols=
# -Wl,--just-symbols,
_rpath_regex = re.compile(r'-Wl,(-rpath[=,]|-R,?|--just-symbols[=,])([^,]+)')

@lru_cache(maxsize=None)
def get_rpath_dirs_from_link_args(args: T.Tuple[str]) -> T.Set[str]:
dirs: T.Set[str] = set()
for arg in args:
if not arg.startswith('-Wl,'):
continue

rpath_match = _rpath_regex.match(arg)
if rpath_match is None:
continue

fmt, opts = rpath_match.groups()

if fmt.startswith('-rpath'):
dirs.update(opts.split(':'))

elif fmt.startswith('-R'):
for dir in opts.split(':'):
# The symbols arg is an rpath if the path is a directory
if os.path.isdir(dir):
dirs.add(dir)

else: # fmt.startswith('--just-symbols')
for dir in opts.split(':'):
# Prevent usage of --just-symbols to specify rpath
if os.path.isdir(dir):
raise MesonException(f'Invalid arg for --just-symbols, {dir} is a directory.')

return dirs


# This class contains the basic functionality that is needed by all backends.
# Feel free to move stuff in and out of it as you see fit.
class Backend:
Expand Down Expand Up @@ -712,89 +755,52 @@ def determine_linker_and_stdlib_args(self, target: build.BuildTarget) -> T.Tuple
l, stdlib_args = target.get_clink_dynamic_linker_and_stdlibs()
return l, stdlib_args

@staticmethod
def _libdir_is_system(libdir: str, compilers: T.Mapping[str, 'Compiler'], env: 'Environment') -> bool:
libdir = os.path.normpath(libdir)
for cc in compilers.values():
if libdir in cc.get_library_dirs(env):
return True
return False

def get_external_rpath_dirs(self, target: build.BuildTarget) -> T.Set[str]:
args: T.List[str] = []
for lang in LANGUAGES_USING_LDFLAGS:
try:
e = self.environment.coredata.get_external_link_args(target.for_machine, lang)
if isinstance(e, str):
args.append(e)
else:
args.extend(e)
except Exception:
args += self.environment.coredata.get_external_link_args(target.for_machine, lang)
except KeyError:
pass
return self.get_rpath_dirs_from_link_args(args)

@staticmethod
def get_rpath_dirs_from_link_args(args: T.List[str]) -> T.Set[str]:
dirs: T.Set[str] = set()
# Match rpath formats:
# -Wl,-rpath=
# -Wl,-rpath,
rpath_regex = re.compile(r'-Wl,-rpath[=,]([^,]+)')
# Match solaris style compat runpath formats:
# -Wl,-R
# -Wl,-R,
runpath_regex = re.compile(r'-Wl,-R[,]?([^,]+)')
# Match symbols formats:
# -Wl,--just-symbols=
# -Wl,--just-symbols,
symbols_regex = re.compile(r'-Wl,--just-symbols[=,]([^,]+)')
for arg in args:
rpath_match = rpath_regex.match(arg)
if rpath_match:
for dir in rpath_match.group(1).split(':'):
dirs.add(dir)
runpath_match = runpath_regex.match(arg)
if runpath_match:
for dir in runpath_match.group(1).split(':'):
# The symbols arg is an rpath if the path is a directory
if Path(dir).is_dir():
dirs.add(dir)
symbols_match = symbols_regex.match(arg)
if symbols_match:
for dir in symbols_match.group(1).split(':'):
# Prevent usage of --just-symbols to specify rpath
if Path(dir).is_dir():
raise MesonException(f'Invalid arg for --just-symbols, {dir} is a directory.')
return dirs
return get_rpath_dirs_from_link_args(tuple(args))

@lru_cache(maxsize=None)
def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]':
paths: OrderedSet[str] = OrderedSet()
srcdir = self.environment.get_source_dir()

system_dirs = set()
if exclude_system:
for cc in target.compilers.values():
system_dirs.update(cc.get_library_dirs(self.environment))

external_rpaths = self.get_external_rpath_dirs(target)

for dep in target.external_deps:
if dep.type_name not in {'library', 'pkgconfig', 'cmake'}:
continue
for libpath in dep.link_args:
if libpath.startswith('-'):
continue
# For all link args that are absolute paths to a library file, add RPATH args
if not os.path.isabs(libpath):
continue
libdir = os.path.dirname(libpath)
if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment):
# No point in adding system paths.
continue
# Don't remove rpaths specified in LDFLAGS.
if libdir in self.get_external_rpath_dirs(target):
continue
libdir, libname = os.path.split(libpath)
# Windows doesn't support rpaths, but we use this function to
# emulate rpaths by setting PATH
# .dll is there for mingw gcc
# .so's may be extended with version information, e.g. libxyz.so.1.2.3
if not (
os.path.splitext(libpath)[1] in {'.dll', '.lib', '.so', '.dylib'}
or re.match(r'.+\.so(\.|$)', os.path.basename(libpath))
libname.endswith(('.dll', '.lib', '.so', '.dylib'))
or '.so.' in libname
):
continue
# Don't remove rpaths specified in LDFLAGS.
if libdir in external_rpaths:
continue
if system_dirs and os.path.normpath(libdir) in system_dirs:
# No point in adding system paths.
continue

try:
commonpath = os.path.commonpath((libdir, srcdir))
Expand All @@ -803,12 +809,11 @@ def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTar

if commonpath == srcdir:
rel_to_src = libdir[len(srcdir) + 1:]
assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute'
paths.add(os.path.join(self.build_to_src, rel_to_src))
else:
paths.add(libdir)
# Don't remove rpaths specified by the dependency
paths.difference_update(self.get_rpath_dirs_from_link_args(dep.link_args))
paths.difference_update(get_rpath_dirs_from_link_args(tuple(dep.link_args)))
for i in chain(target.link_targets, target.link_whole_targets):
if isinstance(i, build.BuildTarget):
paths.update(self.rpaths_for_non_system_absolute_shared_libraries(i, exclude_system))
Expand Down
Loading