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

Bugfix libcxx detection when using CC/CXX #15418

Merged
merged 10 commits into from
Jan 10, 2024
82 changes: 44 additions & 38 deletions conan/internal/api/detect_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def _get_e2k_architecture():
}.get(platform.processor())


def detect_libcxx(compiler, version):
def detect_libcxx(compiler, version, compiler_exe=None):
assert isinstance(version, Version)

def _detect_gcc_libcxx(version_, executable):
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -166,7 +166,7 @@ def _detect_gcc_libcxx(version_, executable):
if compiler == "apple-clang":
return "libc++"
elif compiler == "gcc":
libcxx = _detect_gcc_libcxx(version, "g++")
libcxx = _detect_gcc_libcxx(version, compiler_exe or "g++")
return libcxx
elif compiler == "cc":
if platform.system() == "SunOS":
Expand All @@ -179,7 +179,7 @@ def _detect_gcc_libcxx(version_, executable):
elif platform.system() == "Windows":
return # by default windows will assume LLVM/Clang with VS backend
else: # Linux
libcxx = _detect_gcc_libcxx(version, "clang++")
libcxx = _detect_gcc_libcxx(version, compiler_exe or "clang++")
return libcxx
elif compiler == "sun-cc":
return "libCstd"
Expand Down Expand Up @@ -243,17 +243,17 @@ def detect_cppstd(compiler, compiler_version):
return cppstd


def detect_compiler():
"""
find the default compiler on the build machine
search order and priority:
1. CC and CXX environment variables are always top priority
2. Visual Studio detection (Windows only) via vswhere or registry or environment variables
3. Apple Clang (Mac only)
4. cc executable
5. gcc executable
6. clang executable
def detect_default_compiler():
"""
find the default compiler on the build machine
search order and priority:
1. CC and CXX environment variables are always top priority
2. Visual Studio detection (Windows only) via vswhere or registry or environment variables
3. Apple Clang (Mac only)
4. cc executable
5. gcc executable
6. clang executable
"""
output = ConanOutput(scope="detect_api")
cc = os.environ.get("CC", "")
cxx = os.environ.get("CXX", "")
Expand All @@ -263,11 +263,11 @@ def detect_compiler():
if "clang" in command.lower():
return _clang_compiler(command)
if "gcc" in command or "g++" in command or "c++" in command:
gcc, gcc_version = _gcc_compiler(command)
gcc, gcc_version, compiler_exe = _gcc_compiler(command)
if platform.system() == "Darwin" and gcc is None:
output.error("%s detected as a frontend using apple-clang. "
"Compiler not supported" % command)
return gcc, gcc_version
return gcc, gcc_version, compiler_exe
if platform.system() == "SunOS" and command.lower() == "cc":
return _sun_cc_compiler(command)
if (platform.system() == "Windows" and command.rstrip('"').endswith(("cl", "cl.exe"))
Expand All @@ -276,28 +276,28 @@ def detect_compiler():

# I am not able to find its version
output.error("Not able to automatically detect '%s' version" % command)
return None, None
return None, None, None

if platform.system() == "Windows":
version = _detect_vs_ide_version()
version = {"17": "193", "16": "192", "15": "191"}.get(str(version)) # Map to compiler
if version:
return 'msvc', Version(version)
return 'msvc', Version(version), None

if platform.system() == "SunOS":
sun_cc, sun_cc_version = _sun_cc_compiler()
sun_cc, sun_cc_version, compiler_exe = _sun_cc_compiler()
if sun_cc:
return sun_cc, sun_cc_version
return sun_cc, sun_cc_version, compiler_exe

if platform.system() in ["Darwin", "FreeBSD"]:
clang, clang_version = _clang_compiler() # prioritize clang
clang, clang_version, compiler_exe = _clang_compiler() # prioritize clang
if clang:
return clang, clang_version
return clang, clang_version, compiler_exe
return
else:
gcc, gcc_version = _gcc_compiler()
gcc, gcc_version, compiler_exe = _gcc_compiler()
if gcc:
return gcc, gcc_version
return gcc, gcc_version, compiler_exe
return _clang_compiler()


Expand Down Expand Up @@ -326,21 +326,27 @@ def _gcc_compiler(compiler_exe="gcc"):
_, out = detect_runner("%s --version" % compiler_exe)
out = out.lower()
if "clang" in out:
return None, None
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
return None, None, None

ret, out = detect_runner('%s -dumpversion' % compiler_exe)
if ret != 0:
return None, None
return None, None, None
compiler = "gcc"
installed_version = re.search(r"([0-9]+(\.[0-9])?)", out).group()
# Since GCC 7.1, -dumpversion return the major version number
# only ("7"). We must use -dumpfullversion to get the full version
# number ("7.1.1").
if installed_version:
ConanOutput(scope="detect_api").info("Found %s %s" % (compiler, installed_version))
return compiler, Version(installed_version)
return compiler, Version(installed_version), compiler_exe
except (Exception,): # to disable broad-except
return None, None
return None, None, None


def detect_compiler():
ConanOutput(scope="detect_api").warning("detect_compiler() is deprecated, use detect_default_compiler()")
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
compiler, version, _ = detect_default_compiler()
return compiler, version


def _sun_cc_compiler(compiler_exe="cc"):
Expand All @@ -354,28 +360,28 @@ def _sun_cc_compiler(compiler_exe="cc"):
installed_version = re.search(r"([0-9]+\.[0-9]+)", out).group()
if installed_version:
ConanOutput(scope="detect_api").info("Found %s %s" % (compiler, installed_version))
return compiler, Version(installed_version)
return compiler, Version(installed_version), compiler_exe
except (Exception,): # to disable broad-except
return None, None
return None, None, None


def _clang_compiler(compiler_exe="clang"):
try:
ret, out = detect_runner('%s --version' % compiler_exe)
if ret != 0:
return None, None
return None, None, None
if "Apple" in out:
compiler = "apple-clang"
elif "clang version" in out:
compiler = "clang"
else:
return None, None
return None, None, None
installed_version = re.search(r"([0-9]+\.[0-9])", out).group()
if installed_version:
ConanOutput(scope="detect_api").info("Found %s %s" % (compiler, installed_version))
return compiler, Version(installed_version)
return compiler, Version(installed_version), compiler_exe
except (Exception,): # to disable broad-except
return None, None
return None, None, None


def _msvc_cl_compiler(compiler_exe="cl"):
Expand All @@ -386,20 +392,20 @@ def _msvc_cl_compiler(compiler_exe="cl"):
compiler_exe = compiler_exe.strip('"')
ret, out = detect_runner(f'"{compiler_exe}" /?')
if ret != 0:
return None, None
return None, None, None
first_line = out.splitlines()[0]
if "Microsoft" not in first_line:
return None, None
return None, None, None
compiler = "msvc"
version_regex = re.search(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)\.([0-9]+)\.?([0-9]+)?",
first_line)
if not version_regex:
return None, None
return None, None, None
# 19.36.32535 -> 193
version = f"{version_regex.group('major')}{version_regex.group('minor')[0]}"
return compiler, Version(version)
return compiler, Version(version), compiler_exe
except (Exception,): # to disable broad-except
return None, None
return None, None, None


def default_compiler_version(compiler, version):
Expand Down
6 changes: 3 additions & 3 deletions conans/client/conf/detect.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from conan.api.output import ConanOutput
from conan.internal.api.detect_api import detect_os, detect_arch, default_msvc_runtime, \
detect_libcxx, detect_cppstd, detect_compiler, default_compiler_version
detect_libcxx, detect_cppstd, detect_default_compiler, default_compiler_version


def detect_defaults_settings():
Expand All @@ -14,7 +14,7 @@ def detect_defaults_settings():
arch = detect_arch()
if arch:
result.append(("arch", arch))
compiler, version = detect_compiler()
compiler, version, compiler_exe = detect_default_compiler()
if not compiler:
result.append(("build_type", "Release"))
ConanOutput().warning("No compiler was detected (one may not be needed)")
Expand All @@ -28,7 +28,7 @@ def detect_defaults_settings():
result.append(("compiler.runtime", runtime))
if runtime_version:
result.append(("compiler.runtime_version", runtime_version))
libcxx = detect_libcxx(compiler, version)
libcxx = detect_libcxx(compiler, version, compiler_exe)
if libcxx:
result.append(("compiler.libcxx", libcxx))
cppstd = detect_cppstd(compiler, version)
Expand Down
3 changes: 2 additions & 1 deletion conans/test/functional/test_profile_detect_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ def test_profile_detect_compiler(self):

client = TestClient()
tpl1 = textwrap.dedent("""
{% set compiler, version = detect_api.detect_compiler() %}
{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %}
{% set runtime, _ = detect_api.default_msvc_runtime(compiler) %}
[settings]
compiler={{compiler}}
compiler.version={{detect_api.default_compiler_version(compiler, version)}}
compiler.runtime={{runtime}}
compiler.libcxx={{detect_api.detect_libcxx(compiler, version, compiler_exe)}}
compiler.cppstd={{detect_api.default_cppstd(compiler, version)}}

[conf]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class GCCCompilerTestCase(unittest.TestCase):
def test_detect_gcc_10(self, version):
with mock.patch("platform.system", return_value="Linux"):
with mock.patch("conan.internal.api.detect_api.detect_runner", return_value=(0, version)):
compiler, installed_version = _gcc_compiler()
compiler, installed_version, compiler_exe = _gcc_compiler()
self.assertEqual(compiler, 'gcc')
self.assertEqual(installed_version, version)
self.assertEqual(compiler_exe, 'gcc')
2 changes: 1 addition & 1 deletion conans/test/unittests/util/detect_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test_detect_arch(self, machine, expected_arch):
self.assertEqual(expected_arch, result['arch'])

@mock.patch("conan.internal.api.detect_api._clang_compiler",
return_value=("clang", Version("9")))
return_value=("clang", Version("9"), "clang"))
def test_detect_clang_gcc_toolchain(self, _):
output = RedirectedTestOutput()
with redirect_output(output):
Expand Down