diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 127f86884e0b..e6f45c027b00 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -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, @@ -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, @@ -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, @@ -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,