Skip to content

Commit

Permalink
Bugfix libcxx detection when using CC/CXX (#15418)
Browse files Browse the repository at this point in the history
* Bugfix libxx detection when using CC/CXX

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Make compiler_exe in detect_libcxx optional

* Create new version not to break existing users

* Prettier error when jinja2 rendering fails

* Update conan/internal/api/detect_api.py

Co-authored-by: Carlos Zoido <mrgalleta@gmail.com>

* Add exception msg and profile path to output

---------

Co-authored-by: Carlos Zoido <mrgalleta@gmail.com>
  • Loading branch information
AbrilRBS and czoido authored Jan 10, 2024
1 parent 40c3d99 commit 56e981c
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 45 deletions.
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):
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
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()", warn_tag="deprecated")
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
8 changes: 7 additions & 1 deletion conans/client/profile_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,14 @@ def _load_profile(self, profile_name, cwd):
"profile_name": file_path,
"conan_version": conan_version,
"detect_api": detect_api}

rtemplate = Environment(loader=FileSystemLoader(base_path)).from_string(text)
text = rtemplate.render(context)

try:
text = rtemplate.render(context)
except Exception as e:
raise ConanException(f"Error while rendering the profile template file '{profile_path}'. "
f"Check your Jinja2 syntax: {str(e)}")

try:
return self._recurse_load_profile(text, profile_path)
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

0 comments on commit 56e981c

Please sign in to comment.