diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 3f8bf00ec..2605670f6 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -183,7 +183,48 @@ class CPPCompiler(CLikeCompiler, Compiler): return opts -class ClangCPPCompiler(ClangCompiler, CPPCompiler): +class _StdCPPLibMixin(CompilerMixinBase): + + """Detect whether to use libc++ or libstdc++.""" + + @functools.lru_cache(None) + def language_stdlib_only_link_flags(self, env: Environment) -> T.List[str]: + """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 + """ + + # 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. + search_dirs = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] + + 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 manually checking + 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.') + + return search_dirs + [f'-l{stdlib}'] + + +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, @@ -249,16 +290,6 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): return libs return [] - def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: - # 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, - search_dirs: T.List[str] = [] - for d in self.get_compiler_dirs(env, 'libraries'): - search_dirs.append(f'-L{d}') - return search_dirs + ['-lstdc++'] - class ArmLtdClangCPPCompiler(ClangCPPCompiler): @@ -266,15 +297,7 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): class AppleClangCPPCompiler(ClangCPPCompiler): - def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: - # 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, - search_dirs: T.List[str] = [] - for d in self.get_compiler_dirs(env, 'libraries'): - search_dirs.append(f'-L{d}') - return search_dirs + ['-lc++'] + pass class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): @@ -353,7 +376,7 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler): return [] -class GnuCPPCompiler(GnuCompiler, CPPCompiler): +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, @@ -434,16 +457,6 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]: return ['-fpch-preprocess', '-include', os.path.basename(header)] - def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: - # 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, - search_dirs: T.List[str] = [] - for d in self.get_compiler_dirs(env, 'libraries'): - search_dirs.append(f'-L{d}') - return ['-lstdc++'] - class PGICPPCompiler(PGICompiler, CPPCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,