Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conan/tools/gnu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from conan.tools.gnu.pkgconfig import PkgConfig
from conan.tools.gnu.pkgconfigdeps import PkgConfigDeps
from conan.tools.gnu.makedeps import MakeDeps
from conan.tools.gnu.mingw import is_mingw
30 changes: 30 additions & 0 deletions conan/tools/gnu/mingw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
def is_mingw(conanfile, build_context=False):
"""
Validate if the current compiler is a MinGW toolchain (host context by default).

A MinGW toolchain is detected by the following Conan settings:

- ``os == "Windows"`` (a non-Windows host is never MinGW)
- ``os.subsystem != "cygwin"`` (Cygwin uses a POSIX layer, not MinGW)
- ``compiler == "gcc"``, OR
``compiler == "clang"`` with ``compiler.runtime`` unset (MinGW Clang).
``clang-cl`` is detected via ``compiler.runtime`` and is not considered MinGW.

Reference:
https://blog.conan.io/2022/10/13/Different-flavors-Clang-compiler-Windows.html

:param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
:param build_context: If True, will use the settings from the build context, not host ones.
:return: ``bool`` True if the selected context targets a MinGW toolchain, otherwise False.
"""
settings = conanfile.settings_build if build_context else conanfile.settings
if settings.get_safe("os") != "Windows":
return False
if settings.get_safe("os.subsystem") == "cygwin":
return False
compiler = settings.get_safe("compiler")
if compiler == "gcc":
return True
if compiler == "clang" and settings.get_safe("compiler.runtime") is None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessarily true. The msys2 clang compiler is its own "environment", not a MinGW environment https://www.msys2.org/docs/environments/

the most common definition of is_mingw in ConanCenter recipes is:

 @property
    def _is_mingw(self):
        return self.settings.os == "Windows" and self.settings.compiler == "gcc"

So changing and doing it for clang too might be breaking some recipes or use cases?

return True
return False
83 changes: 83 additions & 0 deletions test/unittests/tools/gnu/test_mingw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import pytest

from conan import ConanFile
from conan.tools.gnu import is_mingw
from conan.internal.model.settings import Settings


def _make_conanfile(os_, compiler, version, *, libcxx=None, runtime=None,
runtime_type=None, subsystem=None):
compiler_def = {compiler: {"version": [version]}}
if libcxx is not None:
compiler_def[compiler]["libcxx"] = [libcxx]
if runtime is not None:
compiler_def[compiler]["runtime"] = [runtime]
if runtime_type is not None:
compiler_def[compiler]["runtime_type"] = [runtime_type]
os_def = {os_: {"subsystem": [subsystem]}} if subsystem else [os_]
settings = Settings({
"os": os_def,
"arch": ["x86_64"],
"compiler": compiler_def,
"build_type": ["Release"],
})
conanfile = ConanFile()
conanfile.settings = settings
conanfile.settings.os = os_
if subsystem is not None:
conanfile.settings.os.subsystem = subsystem
conanfile.settings.compiler = compiler
conanfile.settings.compiler.version = version
if libcxx is not None:
conanfile.settings.compiler.libcxx = libcxx
if runtime is not None:
conanfile.settings.compiler.runtime = runtime
if runtime_type is not None:
conanfile.settings.compiler.runtime_type = runtime_type
return conanfile


@pytest.mark.parametrize("os_,compiler,version,kwargs,expected", [
# MinGW with gcc — the canonical case
("Windows", "gcc", "13", {"libcxx": "libstdc++11"}, True),
# MinGW with Clang — `compiler.runtime` is unset
("Windows", "clang", "17", {"libcxx": "libstdc++11"}, True),
# clang-cl on Windows — `compiler.runtime` is set, not MinGW
("Windows", "clang", "17", {"runtime": "dynamic", "runtime_type": "Release"}, False),
# MSVC — never MinGW
("Windows", "msvc", "193", {"runtime": "dynamic", "runtime_type": "Release"}, False),
# Cygwin uses a POSIX layer, not MinGW
("Windows", "gcc", "13", {"subsystem": "cygwin", "libcxx": "libstdc++11"}, False),
# Non-Windows hosts are never MinGW
("Linux", "gcc", "13", {"libcxx": "libstdc++11"}, False),
("Linux", "clang", "17", {"libcxx": "libc++"}, False),
("Macos", "apple-clang", "15", {"libcxx": "libc++"}, False),
])
def test_is_mingw(os_, compiler, version, kwargs, expected):
conanfile = _make_conanfile(os_, compiler, version, **kwargs)
assert is_mingw(conanfile) is expected


def test_is_mingw_build_context():
"""`build_context=True` must consult settings_build, not the host settings."""
host = _make_conanfile("Linux", "gcc", "13", libcxx="libstdc++11")
build = _make_conanfile("Windows", "gcc", "13", libcxx="libstdc++11")
host.settings_build = build.settings
assert is_mingw(host) is False
assert is_mingw(host, build_context=True) is True


def test_is_mingw_libcxx_not_required():
"""`compiler.libcxx` is removed in pure-C recipes; detection must still work without it."""
settings = Settings({
"os": ["Windows"],
"arch": ["x86_64"],
"compiler": {"gcc": {"version": ["13"]}},
"build_type": ["Release"],
})
conanfile = ConanFile()
conanfile.settings = settings
conanfile.settings.os = "Windows"
conanfile.settings.compiler = "gcc"
conanfile.settings.compiler.version = "13"
assert is_mingw(conanfile) is True