Skip to content

Commit

Permalink
Feature/cmaketoolchain multi flags (#15654)
Browse files Browse the repository at this point in the history
* cmaketoolchain multi flags

* wip

* CMakeToolchain multi-config tools.build:xxxx
  • Loading branch information
memsharded authored Feb 14, 2024
1 parent 1940c9f commit 7d93ccc
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 7 deletions.
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

0 comments on commit 7d93ccc

Please sign in to comment.