Skip to content
Draft
28 changes: 24 additions & 4 deletions conan/internal/model/cpp_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,40 +335,60 @@ def defines(self, value):

@property
def cflags(self):
if self._cflags is None:
if self._cflags is None or callable(self._cflags):
self._cflags = []
return self._cflags

def cflags_consumer(self, consumer):
if callable(self._cflags):
return self._cflags(consumer)
return self.cflags

@cflags.setter
def cflags(self, value):
self._cflags = value

@property
def cxxflags(self):
if self._cxxflags is None:
if self._cxxflags is None or callable(self._cxxflags): # To not break
self._cxxflags = []
return self._cxxflags

def cxxflags_consumer(self, consumer):
if callable(self._cxxflags):
return self._cxxflags(consumer)
return self.cxxflags

@cxxflags.setter
def cxxflags(self, value):
self._cxxflags = value

@property
def sharedlinkflags(self):
if self._sharedlinkflags is None:
if self._sharedlinkflags is None or callable(self._sharedlinkflags):
self._sharedlinkflags = []
return self._sharedlinkflags

def sharedlinkflags_consumer(self, consumer):
if callable(self._sharedlinkflags):
return self._sharedlinkflags(consumer)
return self.sharedlinkflags

@sharedlinkflags.setter
def sharedlinkflags(self, value):
self._sharedlinkflags = value

@property
def exelinkflags(self):
if self._exelinkflags is None:
if self._exelinkflags is None or callable(self._exelinkflags):
self._exelinkflags = []
return self._exelinkflags

def exelinkflags_consumer(self, consumer):
if callable(self._exelinkflags):
return self._exelinkflags(consumer)
return self.exelinkflags

@exelinkflags.setter
def exelinkflags(self, value):
self._exelinkflags = value
Expand Down
13 changes: 9 additions & 4 deletions conan/tools/cmake/cmakedeps2/target_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,19 @@ def _get_cmake_lib(self, info, components, pkg_folder, pkg_folder_var):
if not self._require.headers: # If not depending on headers, paths and
includedirs = defines = None
sources = [self._path(source, pkg_folder, pkg_folder_var) for source in info.sources]
consumer = self._cmakedeps._conanfile # noqa
cflags = info.cflags_consumer(consumer)
cxxflags = info.cxxflags_consumer(consumer)
sharedlinkflags = info.sharedlinkflags_consumer(consumer)
exelinkflags = info.exelinkflags_consumer(consumer)
target = {"type": "INTERFACE",
"includedirs": includedirs,
"defines": defines,
"requires": requires,
"cxxflags": " ".join(cmake_escape_value(f) for f in info.cxxflags),
"cflags": " ".join(cmake_escape_value(f) for f in info.cflags),
"sharedlinkflags": " ".join(cmake_escape_value(v) for v in info.sharedlinkflags),
"exelinkflags": " ".join(cmake_escape_value(v) for v in info.exelinkflags),
"cxxflags": " ".join(cmake_escape_value(f) for f in cxxflags),
"cflags": " ".join(cmake_escape_value(f) for f in cflags),
"sharedlinkflags": " ".join(cmake_escape_value(v) for v in sharedlinkflags),
"exelinkflags": " ".join(cmake_escape_value(v) for v in exelinkflags),
"system_libs": " ".join(info.system_libs),
"sources": " ".join(sources)
}
Expand Down
145 changes: 145 additions & 0 deletions test/integration/package_id/compatible_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,151 @@ def libc_compat(conanfile):
f"libc_version=2" in tc.out


class TestCompatibleFlags:

def test_compatible_flags(self):
""" The compiler flags depends on the consumer settings, not on the binary compatible
settings used to create that compatible binary. This test shows how the new info
can be used to parameterize on the consumer settings
"""
c = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile

class Pkg(ConanFile):
settings = "os"

def compatibility(self):
if self.settings.os == "Windows" or self.settings.os == "Macos":
return [{"settings": [("os", "Linux")]}]

def package_info(self):
def myflags(conanfile):
if conanfile.settings.get_safe("os") == "Windows":
return ["-mywinflag"]
elif conanfile.settings.get_safe("os") == "Linux":
return ["-mylinuxflag"]
else:
return ["-other-os-flag"]
self.cpp_info.cxxflags = myflags
self.cpp_info.cflags = myflags
self.cpp_info.sharedlinkflags = myflags
self.cpp_info.exelinkflags = myflags
""")
consumer = textwrap.dedent("""
from conan import ConanFile
class Pkg(ConanFile):
settings = "os", "build_type"
requires = "pkg/0.1"
generators = "CMakeDeps"
""")
c.save({"pkg/conanfile.py": conanfile,
"consumer/conanfile.py": consumer})

c.run("create pkg --name=pkg --version=0.1 -s os=Linux")

def _check(flag, cmake_file):
assert f"$<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:RELEASE>:{flag}>>)" in cmake_file
assert f"$<$<COMPILE_LANGUAGE:C>:$<$<CONFIG:RELEASE>:{flag}>>)" in cmake_file
assert (f"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:"
f"$<$<CONFIG:RELEASE>:{flag}>>") in cmake_file
assert (f"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:"
f"$<$<CONFIG:RELEASE>:{flag}>>") in cmake_file

c.run("install consumer -s os=Linux -c tools.cmake.cmakedeps:new=will_break_next")
cmake = c.load("consumer/pkg-Targets-release.cmake")
_check("-mylinuxflag", cmake)

c.run("install consumer -s os=Windows -c tools.cmake.cmakedeps:new=will_break_next")
cmake = c.load("consumer/pkg-Targets-release.cmake")
_check("-mywinflag", cmake)

c.run("install consumer -s os=Macos -c tools.cmake.cmakedeps:new=will_break_next")
cmake = c.load("consumer/pkg-Targets-release.cmake")
_check("-other-os-flag", cmake)

def test_simple_lambda(self):
""" same as above, but more compact condition
"""
c = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile
from conan.tools.microsoft import is_msvc

class Pkg(ConanFile):
def package_info(self):
self.cpp_info.cxxflags = lambda c: ["-mywinflag"] if is_msvc(c) else []
""")
consumer = textwrap.dedent("""
from conan import ConanFile
class Pkg(ConanFile):
settings = "compiler"
requires = "pkg/0.1"
def generate(self):
cpp_info = self.dependencies["pkg"].cpp_info
self.output.info(f"CXXFLAGS: {cpp_info.cxxflags_consumer(self)}!!!")
""")
c.save({"pkg/conanfile.py": conanfile,
"consumer/conanfile.py": consumer})

settings = "-s compiler=msvc -s compiler.version=193 -s compiler.runtime=dynamic"
c.run(f"create pkg --name=pkg --version=0.1 {settings}")

c.run(f"install consumer {settings}")
assert f"conanfile.py: CXXFLAGS: ['-mywinflag']!!!" in c.out
c.run(f"install consumer {settings} -s &:compiler=clang -s &:compiler.version=19")
assert f"conanfile.py: CXXFLAGS: []!!!" in c.out

def test_compatible_flags_direct(self):
""" same as above but without compatibility
"""
c = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile

class Pkg(ConanFile):
def package_info(self):
def myflags(conanfile):
if conanfile.settings.get_safe("os") == "Windows":
return ["-mywinflag"]
elif conanfile.settings.get_safe("os") == "Linux":
return ["-mylinuxflag"]
else:
return ["-other-os-flag"]
self.cpp_info.cxxflags = myflags
""")
consumer = textwrap.dedent("""
from conan import ConanFile
class Pkg(ConanFile):
settings = "os"
requires = "pkg/0.1"
def generate(self):
flags = self.dependencies["pkg"].cpp_info.cxxflags_consumer(self)
self.output.info(f"FLAGS: {flags}!!!")
""")
c.save({"pkg/conanfile.py": conanfile,
"consumer/conanfile.py": consumer})

c.run("create pkg --name=pkg --version=0.1")
c.run("export consumer --name=dep1 --version=0.1")
c.run("export consumer --name=dep2 --version=0.1")

c.run("install --requires=dep1/0.1 --requires=dep2/0.1 "
"-s os=Macos -s dep1/*:os=Linux -s dep2/*:os=Windows --build=missing")
assert "dep1/0.1: FLAGS: ['-mylinuxflag']!!!" in c.out
assert "dep2/0.1: FLAGS: ['-mywinflag']!!!" in c.out

c.run("install --requires=dep1/0.1 --requires=dep2/0.1 "
"-s os=Macos -s dep1/*:os=Windows -s dep2/*:os=Linux --build=missing")
assert "dep1/0.1: FLAGS: ['-mywinflag']!!!" in c.out
assert "dep2/0.1: FLAGS: ['-mylinuxflag']!!!" in c.out

c.run("install --requires=dep1/0.1 --requires=dep2/0.1 "
"-s os=Macos --build=missing")
assert "dep1/0.1: FLAGS: ['-other-os-flag']!!!" in c.out
assert "dep2/0.1: FLAGS: ['-other-os-flag']!!!" in c.out


def test_compatibility_remove_cppstd():
""" This test tries to reflect the following scenario:
- User recently added compiler.cppstd to their settings
Expand Down
Loading