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

Feature/cmaketoolchain multi flags #15654

Merged
Merged
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
74 changes: 68 additions & 6 deletions conan/tools/cmake/toolchain/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from conan.tools.build.flags import architecture_flag, libcxx_flags
from conan.tools.build.cross_building import cross_building
from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
from conan.tools.cmake.utils import is_multi_configuration
from conan.tools.intel import IntelCC
from conan.tools.microsoft.visual import msvc_version_to_toolset_version
from conans.client.subsystems import deduce_subsystem, WINDOWS
Expand Down Expand Up @@ -544,25 +545,62 @@ def context(self):
class ExtraFlagsBlock(Block):
"""This block is adding flags directly from user [conf] section"""

template = textwrap.dedent("""
# Extra c, cxx, linkflags and defines
_template = textwrap.dedent("""
# Conan conf flags start: {{config}}
{% if cxxflags %}
string(APPEND CONAN_CXX_FLAGS "{% for cxxflag in cxxflags %} {{ cxxflag }}{% endfor %}")
string(APPEND CONAN_CXX_FLAGS{{suffix}} "{% for cxxflag in cxxflags %} {{ cxxflag }}{% endfor %}")
{% endif %}
{% if cflags %}
string(APPEND CONAN_C_FLAGS "{% for cflag in cflags %} {{ cflag }}{% endfor %}")
string(APPEND CONAN_C_FLAGS{{suffix}} "{% for cflag in cflags %} {{ cflag }}{% endfor %}")
{% endif %}
{% if sharedlinkflags %}
string(APPEND CONAN_SHARED_LINKER_FLAGS "{% for sharedlinkflag in sharedlinkflags %} {{ sharedlinkflag }}{% endfor %}")
string(APPEND CONAN_SHARED_LINKER_FLAGS{{suffix}} "{% for sharedlinkflag in sharedlinkflags %} {{ sharedlinkflag }}{% endfor %}")
{% endif %}
{% if exelinkflags %}
string(APPEND CONAN_EXE_LINKER_FLAGS "{% for exelinkflag in exelinkflags %} {{ exelinkflag }}{% endfor %}")
string(APPEND CONAN_EXE_LINKER_FLAGS{{suffix}} "{% for exelinkflag in exelinkflags %} {{ exelinkflag }}{% endfor %}")
{% endif %}
{% if defines %}
{% if config %}
add_compile_definitions($<$<CONFIG:{{config}}>:{% for define in defines %}" {{ define }}"{% endfor %}>)
{% else %}
add_compile_definitions({% for define in defines %} "{{ define }}"{% endfor %})
{% endif %}
{% endif %}
# Conan conf flags end
""")

@property
def template(self):
if not is_multi_configuration(self._toolchain.generator):
return self._template

sections = {}
if os.path.exists(CONAN_TOOLCHAIN_FILENAME):
existing_toolchain = load(CONAN_TOOLCHAIN_FILENAME)
lines = existing_toolchain.splitlines()
current_section = None
for line in lines:
if line.startswith("# Conan conf flags start: "):
section_name = line.split(":", 1)[1].strip()
current_section = [line]
sections[section_name] = current_section
elif line == "# Conan conf flags end":
current_section.append(line)
current_section = None
elif current_section is not None:
current_section.append(line)
sections.pop("", None) # Just in case it had a single config before

config = self._conanfile.settings.get_safe("build_type")
for k, v in sections.items():
if k != config:
v.insert(0, "{% raw %}")
v.append("{% endraw %}")
sections[config] = [self._template]
sections = ["\n".join(lines) for lines in sections.values()]
sections = "\n".join(sections)
return sections

def context(self):
# Now, it's time to get all the flags defined by the user
cxxflags = self._toolchain.extra_cxxflags + self._conanfile.conf.get("tools.build:cxxflags", default=[], check_type=list)
Expand All @@ -579,7 +617,14 @@ def context(self):
self._conanfile.output.warning("tools.build:cxxflags or cflags are defined, but Android NDK toolchain may be overriding "
"the values. Consider setting tools.android:cmake_legacy_toolchain to False.")

config = ""
suffix = ""
if is_multi_configuration(self._toolchain.generator):
config = self._conanfile.settings.get_safe("build_type")
suffix = f"_{config.upper()}" if config else ""
return {
"config": config,
"suffix": suffix,
"cxxflags": cxxflags,
"cflags": cflags,
"sharedlinkflags": sharedlinkflags,
Expand All @@ -590,6 +635,22 @@ def context(self):

class CMakeFlagsInitBlock(Block):
template = textwrap.dedent("""
foreach(config ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${config} config)
if(DEFINED CONAN_CXX_FLAGS_${config})
string(APPEND CMAKE_CXX_FLAGS_${config}_INIT " ${CONAN_CXX_FLAGS_${config}}")
endif()
if(DEFINED CONAN_C_FLAGS_${config})
string(APPEND CMAKE_C_FLAGS_${config}_INIT " ${CONAN_C_FLAGS_${config}}")
endif()
if(DEFINED CONAN_SHARED_LINKER_FLAGS_${config})
string(APPEND CMAKE_SHARED_LINKER_FLAGS_${config}_INIT " ${CONAN_SHARED_LINKER_FLAGS_${config}}")
endif()
if(DEFINED CONAN_EXE_LINKER_FLAGS_${config})
string(APPEND CMAKE_EXE_LINKER_FLAGS_${config}_INIT " ${CONAN_EXE_LINKER_FLAGS_${config}}")
endif()
endforeach()

if(DEFINED CONAN_CXX_FLAGS)
string(APPEND CMAKE_CXX_FLAGS_INIT " ${CONAN_CXX_FLAGS}")
endif()
Expand All @@ -602,6 +663,7 @@ class CMakeFlagsInitBlock(Block):
if(DEFINED CONAN_EXE_LINKER_FLAGS)
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " ${CONAN_EXE_LINKER_FLAGS}")
endif()

""")


Expand Down
84 changes: 84 additions & 0 deletions conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1686,3 +1686,87 @@ def generate(self):

c.run_command(f"ctest --preset conan-release")
assert "tests passed" in c.out


@pytest.mark.tool("cmake")
@pytest.mark.skipif(platform.system() != "Windows", reason="neeed multi-config")
def test_cmake_toolchain_cxxflags_multi_config():
c = TestClient()
profile_release = textwrap.dedent(r"""
include(default)
[conf]
tools.build:defines=["answer=42"]
tools.build:cxxflags=["/Zc:__cplusplus"]
""")
profile_debug = textwrap.dedent(r"""
include(default)
[settings]
build_type=Debug
[conf]
tools.build:defines=["answer=123"]
tools.build:cxxflags=["/W4"]
""")

conanfile = textwrap.dedent(r'''
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout

class Test(ConanFile):
exports_sources = "CMakeLists.txt", "src/*"
settings = "os", "compiler", "arch", "build_type"
generators = "CMakeToolchain"

def layout(self):
cmake_layout(self)

def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
''')

main = textwrap.dedent(r"""
#include <iostream>
#include <stdio.h>

#define STR(x) #x
#define SHOW_DEFINE(x) printf("%s=%s\n", #x, STR(x))

int main() {
SHOW_DEFINE(answer);
char a = 123L; // to trigger warnings

#if __cplusplus
std::cout << "CPLUSPLUS: __cplusplus" << __cplusplus<< "\n";
#endif
}
""")

cmakelists = textwrap.dedent("""
cmake_minimum_required(VERSION 3.15)
project(Test CXX)
add_executable(example src/main.cpp)
""")

c.save({"conanfile.py": conanfile,
"profile_release": profile_release,
"profile_debug": profile_debug,
"src/main.cpp": main,
"CMakeLists.txt": cmakelists}, clean_first=True)
c.run("install . -pr=./profile_release")
c.run("install . -pr=./profile_debug")

with c.chdir("build"):
c.run_command("cmake .. -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake")
c.run_command("cmake --build . --config Release")
assert "warning C4189" not in c.out
c.run_command("cmake --build . --config Debug")
assert "warning C4189" in c.out

c.run_command(r"build\Release\example.exe")
assert 'answer=42' in c.out
assert "CPLUSPLUS: __cplusplus20" in c.out

c.run_command(r"build\Debug\example.exe")
assert 'answer=123' in c.out
assert "CPLUSPLUS: __cplusplus19" in c.out
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def test_clang_visual_studio_generator(self, client):
'-c tools.cmake.cmaketoolchain:generator="{}"'.format(generator))
assert 'cmake -G "{}"'.format(generator) in client.out
assert "MSVC-like command-line" in client.out
assert "main __clang_major__14" in client.out
assert "main __clang_major__16" in client.out
# Check this! Clang compiler in Windows is reporting MSC_VER and MSVC_LANG!
assert "main _MSC_VER193" in client.out
assert "main _MSVC_LANG2017" in client.out
Expand Down