From 59cfbf68e00aa774a9868101f423bd662938c15d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Jun 2023 09:00:24 -0700 Subject: [PATCH 1/5] compilers/cpp: Actually add the search dirs to for gcc We calculate them, but then don't use them. Clang does use them, so this looks like a simple oversight --- mesonbuild/compilers/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 3f8bf00ec..16e337b27 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -442,7 +442,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): search_dirs: T.List[str] = [] for d in self.get_compiler_dirs(env, 'libraries'): search_dirs.append(f'-L{d}') - return ['-lstdc++'] + return search_dirs + ['-lstdc++'] class PGICPPCompiler(PGICompiler, CPPCompiler): From a4b597a7b7b8c9d2129fbd93a985021c7d6742d6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Jun 2023 09:00:46 -0700 Subject: [PATCH 2/5] compilers/cpp: use a list comprehension instead of a for loop It's slightly faster, and less code --- mesonbuild/compilers/cpp.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 16e337b27..6eaa6c87e 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -254,9 +254,7 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): # 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}') + search_dirs = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] return search_dirs + ['-lstdc++'] @@ -271,9 +269,7 @@ class AppleClangCPPCompiler(ClangCPPCompiler): # 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}') + search_dirs = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] return search_dirs + ['-lc++'] @@ -439,9 +435,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): # 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}') + search_dirs = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] return search_dirs + ['-lstdc++'] From f58bd2ae11429ee7aa911de631faece718fbfe14 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Jun 2023 09:05:50 -0700 Subject: [PATCH 3/5] compilers/cpp: use a Mixin to share the stdlib flags between clang++ and g++ Which will make subsequent work easier --- mesonbuild/compilers/cpp.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 6eaa6c87e..bec452916 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -183,7 +183,20 @@ class CPPCompiler(CLikeCompiler, Compiler): return opts -class ClangCPPCompiler(ClangCompiler, CPPCompiler): +class _StdCPPLibMixin(CompilerMixinBase): + + """Detect whether to use libc++ or libstdc++.""" + + 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 = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] + return search_dirs + ['-lstdc++'] + + +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,14 +262,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 = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] - return search_dirs + ['-lstdc++'] - class ArmLtdClangCPPCompiler(ClangCPPCompiler): @@ -349,7 +354,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, @@ -430,14 +435,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 = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] - return search_dirs + ['-lstdc++'] - class PGICPPCompiler(PGICompiler, CPPCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, From a227768b378caa7a5f9faf42936bdad8cd6123dd Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Jun 2023 09:28:19 -0700 Subject: [PATCH 4/5] compilers/cpp: try to do a better job of detecting libc++ vs libstdc++ 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 fixes: #11921 --- mesonbuild/compilers/cpp.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index bec452916..081f656c1 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -187,13 +187,41 @@ class _StdCPPLibMixin(CompilerMixinBase): """Detect whether to use libc++ or libstdc++.""" - def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: + @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, + # 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 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): From 9067139acb5f133e08ed15eba8a6d41246200b5b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Jun 2023 09:43:07 -0700 Subject: [PATCH 5/5] compilers/cpp: remove special libc++ handling from the AppleClangCPPCompiler The base implementation handles this already, with the added bonuses of caching, and having one less code path to test. --- mesonbuild/compilers/cpp.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 081f656c1..2605670f6 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -297,13 +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 = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')] - return search_dirs + ['-lc++'] + pass class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler):