Skip to content
Merged
35 changes: 28 additions & 7 deletions conan/tools/apple/xcodetoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class XcodeToolchain:
{cflags}
{cppflags}
{ldflags}
{extra}
""")

_agreggated_xconfig = textwrap.dedent("""\
Expand All @@ -45,13 +46,15 @@ def __init__(self, conanfile):
sharedlinkflags = self._conanfile.conf.get("tools.build:sharedlinkflags", default=[], check_type=list)
exelinkflags = self._conanfile.conf.get("tools.build:exelinkflags", default=[], check_type=list)
self._global_ldflags = sharedlinkflags + exelinkflags
self.extra_xcconfig = {}

def generate(self):
check_duplicated_generator(self, self._conanfile)
save(self._agreggated_xconfig_filename, self._agreggated_xconfig_content)
save(self._vars_xconfig_filename, self._vars_xconfig_content)
if self._check_if_extra_flags:
save(self._flags_xcconfig_filename, self._flags_xcconfig_content)
save(self._flags_props_xconfig_filename, self._flags_xcconfig_content)
save(self._flags_xcconfig_filename, self._flags_xcconfig_aggregated_content)
save(GLOBAL_XCCONFIG_FILENAME, self._global_xconfig_content)

@property
Expand Down Expand Up @@ -114,17 +117,35 @@ def _agreggated_xconfig_filename(self):

@property
def _check_if_extra_flags(self):
return self._global_cflags or self._global_cxxflags or self._global_ldflags or self._global_defines
return (self._global_cflags or self._global_cxxflags or self._global_ldflags
or self._global_defines or self.extra_xcconfig)

@property
def _flags_xcconfig_content(self):
defines = "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) {}".format(" ".join(self._global_defines)) if self._global_defines else ""
cflags = "OTHER_CFLAGS = $(inherited) {}".format(" ".join(self._global_cflags)) if self._global_cflags else ""
cppflags = "OTHER_CPLUSPLUSFLAGS = $(inherited) {}".format(" ".join(self._global_cxxflags)) if self._global_cxxflags else ""
ldflags = "OTHER_LDFLAGS = $(inherited) {}".format(" ".join(self._global_ldflags)) if self._global_ldflags else ""
ret = self._flags_xconfig.format(defines=defines, cflags=cflags, cppflags=cppflags, ldflags=ldflags)
# Conditioned like the vars props file, so flags from different installed
# (config, arch, sdk) combinations don't overwrite each other.
condition = _xcconfig_conditional(self._conanfile.settings, self.configuration)
defines = "GCC_PREPROCESSOR_DEFINITIONS{} = $(inherited) {}".format(condition, " ".join(self._global_defines)) if self._global_defines else ""
cflags = "OTHER_CFLAGS{} = $(inherited) {}".format(condition, " ".join(self._global_cflags)) if self._global_cflags else ""
cppflags = "OTHER_CPLUSPLUSFLAGS{} = $(inherited) {}".format(condition, " ".join(self._global_cxxflags)) if self._global_cxxflags else ""
ldflags = "OTHER_LDFLAGS{} = $(inherited) {}".format(condition, " ".join(self._global_ldflags)) if self._global_ldflags else ""
extra = "\n".join("{}{} = {}".format(k, condition, v) for k, v in self.extra_xcconfig.items())
ret = self._flags_xconfig.format(defines=defines, cflags=cflags, cppflags=cppflags, ldflags=ldflags,
extra=extra)
return ret

@property
def _flags_props_xconfig_filename(self):
return "conan_global_flags{}{}".format(_xcconfig_settings_filename(self._conanfile.settings,
self.configuration),
self.extension)

@property
def _flags_xcconfig_aggregated_content(self):
return _add_includes_to_file_or_create(self._flags_xcconfig_filename,
self._agreggated_xconfig,
[self._flags_props_xconfig_filename])

@property
def _flags_xcconfig_filename(self):
return "conan_global_flags" + self.extension
69 changes: 57 additions & 12 deletions test/integration/toolchains/apple/test_xcodetoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,76 @@ def test_toolchain_files(configuration, os_version, cppstd, libcxx, arch, sdk_ve
assert 'CLANG_CXX_LANGUAGE_STANDARD{}={}'.format(condition, clang_cppstd) in toolchain_vars


@pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS")
def test_toolchain_flags():
client = TestClient()
client.save({"conanfile.txt": "[generators]\nXcodeToolchain\n"})
cmd = "install . -c 'tools.build:cxxflags=[\"flag1\"]' " \
"-c 'tools.build:defines=[\"MYDEFINITION\"]' " \
"-c 'tools.build:cflags=[\"flag2\"]' " \
"-c 'tools.build:sharedlinkflags=[\"flag3\"]' " \
"-c 'tools.build:exelinkflags=[\"flag4\"]'"
cmd = "install . -s build_type=Release -s arch=x86_64 " \
"-c 'tools.build:cxxflags=[\"cxxflags_release\"]' " \
"-c 'tools.build:defines=[\"defines_release\"]' " \
"-c 'tools.build:cflags=[\"cflags_release\"]' " \
"-c 'tools.build:sharedlinkflags=[\"sharedlinkflags_release\"]' " \
"-c 'tools.build:exelinkflags=[\"exelinkflags_release\"]'"
client.run(cmd)
filename = _get_filename("Release", "x86_64", None)
condition = _condition("Release", "x86_64", None)

conan_global_flags_props = client.load("conan_global_flags{}.xcconfig".format(filename))
assert "GCC_PREPROCESSOR_DEFINITIONS{} = $(inherited) defines_release".format(condition) in conan_global_flags_props
assert "OTHER_CFLAGS{} = $(inherited) cflags_release".format(condition) in conan_global_flags_props
assert "OTHER_CPLUSPLUSFLAGS{} = $(inherited) cxxflags_release".format(condition) in conan_global_flags_props
assert "OTHER_LDFLAGS{} = $(inherited) sharedlinkflags_release exelinkflags_release".format(condition) in conan_global_flags_props

# A second install, for a different configuration, must not overwrite the
# first one's flags -- each stays reachable under its own condition.
client.run("install . -s build_type=Debug -s arch=x86_64 -c 'tools.build:cxxflags=[\"cxxflags_debug\"]'")
debug_filename = _get_filename("Debug", "x86_64", None)
debug_flags = client.load("conan_global_flags{}.xcconfig".format(debug_filename))
assert "cxxflags_debug" in debug_flags
assert "cxxflags_release" not in debug_flags

conan_global_flags = client.load("conan_global_flags.xcconfig")
assert "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) MYDEFINITION" in conan_global_flags
assert "OTHER_CFLAGS = $(inherited) flag2" in conan_global_flags
assert "OTHER_CPLUSPLUSFLAGS = $(inherited) flag1" in conan_global_flags
assert "OTHER_LDFLAGS = $(inherited) flag3 flag4" in conan_global_flags
assert '#include "conan_global_flags{}.xcconfig"'.format(filename) in conan_global_flags
assert '#include "conan_global_flags{}.xcconfig"'.format(debug_filename) in conan_global_flags
conan_global_file = client.load("conan_config.xcconfig")
assert '#include "conan_global_flags.xcconfig"' in conan_global_file


@pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS")
def test_toolchain_extra_xcconfig():
# Escape hatch for settings with no dedicated conf, e.g. Swift's
# OTHER_SWIFT_FLAGS: the recipe sets it directly on the toolchain.
client = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile
from conan.tools.apple import XcodeToolchain
class Demo(ConanFile):
settings = "os", "arch", "compiler", "build_type"
def generate(self):
tc = XcodeToolchain(self)
tc.extra_xcconfig["OTHER_SWIFT_FLAGS"] = "$(inherited) -cxx-interoperability-mode=default"
tc.generate()
""")
client.save({"conanfile.py": conanfile})
client.run("install . -s build_type=Release -s arch=x86_64")
filename = _get_filename("Release", "x86_64", None)
condition = _condition("Release", "x86_64", None)

conan_global_flags_props = client.load("conan_global_flags{}.xcconfig".format(filename))
assert "OTHER_SWIFT_FLAGS{} = $(inherited) -cxx-interoperability-mode=default".format(condition) in conan_global_flags_props


@pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS")
def test_flags_generated_if_only_defines():
# https://github.com/conan-io/conan/issues/16422
client = TestClient()
client.save({"conanfile.txt": "[generators]\nXcodeToolchain\n"})
client.run("install . -c 'tools.build:defines=[\"MYDEFINITION\"]'")
conan_global_flags = client.load("conan_global_flags.xcconfig")
assert "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) MYDEFINITION" in conan_global_flags
client.run("install . -s build_type=Release -s arch=x86_64 -c 'tools.build:defines=[\"MYDEFINITION\"]'")
filename = _get_filename("Release", "x86_64", None)
condition = _condition("Release", "x86_64", None)

conan_global_flags_props = client.load("conan_global_flags{}.xcconfig".format(filename))
assert "GCC_PREPROCESSOR_DEFINITIONS{} = $(inherited) MYDEFINITION".format(condition) in conan_global_flags_props
conan_global_file = client.load("conan_config.xcconfig")
assert '#include "conan_global_flags.xcconfig"' in conan_global_file

Expand Down
Loading