Skip to content

Commit

Permalink
compilers/cpp: try to do a better job of detecting libc++ vs libstdc++
Browse files Browse the repository at this point in the history
Currently, we hardcode libc++ for MacOS (and derivatives), and libstdc++
for all other cases. Clang had some hackery to make this work in many
cases. However, this doesn't always work, namely if you try to to use
Rust as the linker when libc++ is required.

This implementation does, as an optimization, provide a hardcoded list
of OSes we know always use libc++, and otherwise will attempt to detect
it. As a second optimization, the detected values are cached, so the
lookup is only done once
  • Loading branch information
dcbaker committed Jun 29, 2023
1 parent f330705 commit f6c5584
Showing 1 changed file with 44 additions and 5 deletions.
49 changes: 44 additions & 5 deletions mesonbuild/compilers/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,16 +187,53 @@ class _StdCPPLibMixin(CompilerMixinBase):

"""Detect whether to use libc++ or libstdc++."""

def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
def __init__(self) -> None:
self.__stdlib_flags: T.Optional[T.List[str]] = None

def __detect_stdlib_flags(self, env: Environment) -> None:
"""Detect the C++ stdlib and default search dirs
As an optimization, this method will cache the value, to avoid building the same values over and over
:param env: An Environment object
:raises MesonException: If a stdlib cannot be determined
"""
assert self.__stdlib_flags is None, 'detect for c++ stdlib called twice!'

# We need to apply the search prefix here, as these link arguments may
# be passed to a different compiler with a different set of default
# search paths, such as when using Clang for C/C++ and gfortran for
# fortran,
# fortran.
search_dirs = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')]
return search_dirs + ['-lstdc++']

machine = env.machines[self.for_machine]
assert machine is not None, 'for mypy'

# We need to determine whether to us libc++ or libstdc++
# In some cases we know the answer, so we'll hardcode those cases.
# There are other cases where we can't know the answer just by looking
# at the OS, namely on Linux. In that case we have to fallback to
# TODO: need to test dragonfly, netbsd, and openbsd
stdlib: str
if machine.system in {'android', 'darwin', 'dragonfly', 'freebsd', 'netbsd', 'openbsd'}:
stdlib = 'c++'
elif self.find_library('c++', env, []) is not None:
stdlib = 'c++'
elif self.find_library('stdc++', env, []) is not None:
stdlib = 'stdc++'
else:
# TODO: maybe a bug exception?
raise MesonException('Could not detect either libc++ or libstdc++ as your C++ stdlib implementation.')

self.__stdlib_flags = search_dirs + [f'-l{stdlib}']

def language_stdlib_only_link_flags(self, env: Environment) -> T.List[str]:
if self.__stdlib_flags is None:
self.__detect_stdlib_flags(env)
return self.__stdlib_flags


class ClangCPPCompiler(ClangCompiler, CPPCompiler, _StdCPPLibMixin):
class ClangCPPCompiler(_StdCPPLibMixin, ClangCompiler, CPPCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
linker: T.Optional['DynamicLinker'] = None,
Expand All @@ -205,6 +242,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_
CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
info, exe_wrapper, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, defines)
_StdCPPLibMixin.__init__(self)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
'1': default_warn_args,
Expand Down Expand Up @@ -354,7 +392,7 @@ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []


class GnuCPPCompiler(GnuCompiler, CPPCompiler, _StdCPPLibMixin):
class GnuCPPCompiler(_StdCPPLibMixin, GnuCompiler, CPPCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
linker: T.Optional['DynamicLinker'] = None,
Expand All @@ -363,6 +401,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_
CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
info, exe_wrapper, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
_StdCPPLibMixin.__init__(self)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
'1': default_warn_args,
Expand Down

0 comments on commit f6c5584

Please sign in to comment.