From abc66fb7332a7304aed417255a063333f7ea28b5 Mon Sep 17 00:00:00 2001 From: Carlos Zoido Date: Thu, 13 Aug 2026 08:55:32 +0200 Subject: [PATCH 1/4] add test --- .../toolchains/apple/test_xcodetoolchain.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/integration/toolchains/apple/test_xcodetoolchain.py b/test/integration/toolchains/apple/test_xcodetoolchain.py index 8941f162beb..e3e58a7d73b 100644 --- a/test/integration/toolchains/apple/test_xcodetoolchain.py +++ b/test/integration/toolchains/apple/test_xcodetoolchain.py @@ -87,6 +87,26 @@ def test_flags_generated_if_only_defines(): assert '#include "conan_global_flags.xcconfig"' in conan_global_file +def test_toolchain_flags_lost_across_configurations(): + # conan_global_flags.xcconfig has a fixed name and is fully overwritten on + # every install, unlike conantoolchain__.xcconfig, which is + # named per [config][arch][sdk] and accumulated. Installing Debug and then + # Release for the same project silently drops the Debug flags: the file on + # disk, and therefore what Xcode sees regardless of the active + # configuration, only ever reflects the *last* install. + client = TestClient() + client.save({"conanfile.txt": "[generators]\nXcodeToolchain\n"}) + client.run("install . -s build_type=Debug -c 'tools.build:cxxflags=[\"-DDEBUG_FLAG\"]'") + client.run("install . -s build_type=Release -c 'tools.build:cxxflags=[\"-DRELEASE_FLAG\"]'") + + conan_global_flags = client.load("conan_global_flags.xcconfig") + # Both configurations were installed, so both flags should still be + # reachable, each scoped to its own configuration -- the same guarantee + # the vars file already gives CLANG_CXX_LANGUAGE_STANDARD/CLANG_CXX_LIBRARY. + assert "-DDEBUG_FLAG" in conan_global_flags + assert "-DRELEASE_FLAG" in conan_global_flags + + @pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS") @pytest.mark.parametrize("os_name, sdk, min_version, deployment_target_flag", [ ("Macos", None, "11.0", "MACOSX_DEPLOYMENT_TARGET"), From 84e37bd33c1111dc4bc84158b3f549a58ecbfdd1 Mon Sep 17 00:00:00 2001 From: Carlos Zoido Date: Thu, 13 Aug 2026 09:23:27 +0200 Subject: [PATCH 2/4] fix issue --- conan/tools/apple/xcodetoolchain.py | 26 +++++++-- .../toolchains/apple/test_xcodetoolchain.py | 57 ++++++++++--------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/conan/tools/apple/xcodetoolchain.py b/conan/tools/apple/xcodetoolchain.py index 76d8c79180a..d46dca4cd17 100644 --- a/conan/tools/apple/xcodetoolchain.py +++ b/conan/tools/apple/xcodetoolchain.py @@ -51,7 +51,8 @@ def generate(self): 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 @@ -118,13 +119,28 @@ def _check_if_extra_flags(self): @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 "" + # 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 "" ret = self._flags_xconfig.format(defines=defines, cflags=cflags, cppflags=cppflags, ldflags=ldflags) 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 diff --git a/test/integration/toolchains/apple/test_xcodetoolchain.py b/test/integration/toolchains/apple/test_xcodetoolchain.py index e3e58a7d73b..120f52c96dc 100644 --- a/test/integration/toolchains/apple/test_xcodetoolchain.py +++ b/test/integration/toolchains/apple/test_xcodetoolchain.py @@ -58,55 +58,56 @@ 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\"]' " \ + cmd = "install . -s build_type=Release -s arch=x86_64 " \ + "-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\"]'" 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) MYDEFINITION".format(condition) in conan_global_flags_props + assert "OTHER_CFLAGS{} = $(inherited) flag2".format(condition) in conan_global_flags_props + assert "OTHER_CPLUSPLUSFLAGS{} = $(inherited) flag1".format(condition) in conan_global_flags_props + assert "OTHER_LDFLAGS{} = $(inherited) flag3 flag4".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=[\"debugflag\"]'") + debug_filename = _get_filename("Debug", "x86_64", None) + debug_flags = client.load("conan_global_flags{}.xcconfig".format(debug_filename)) + assert "debugflag" in debug_flags + assert "flag1" 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_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 -def test_toolchain_flags_lost_across_configurations(): - # conan_global_flags.xcconfig has a fixed name and is fully overwritten on - # every install, unlike conantoolchain__.xcconfig, which is - # named per [config][arch][sdk] and accumulated. Installing Debug and then - # Release for the same project silently drops the Debug flags: the file on - # disk, and therefore what Xcode sees regardless of the active - # configuration, only ever reflects the *last* install. - client = TestClient() - client.save({"conanfile.txt": "[generators]\nXcodeToolchain\n"}) - client.run("install . -s build_type=Debug -c 'tools.build:cxxflags=[\"-DDEBUG_FLAG\"]'") - client.run("install . -s build_type=Release -c 'tools.build:cxxflags=[\"-DRELEASE_FLAG\"]'") - - conan_global_flags = client.load("conan_global_flags.xcconfig") - # Both configurations were installed, so both flags should still be - # reachable, each scoped to its own configuration -- the same guarantee - # the vars file already gives CLANG_CXX_LANGUAGE_STANDARD/CLANG_CXX_LIBRARY. - assert "-DDEBUG_FLAG" in conan_global_flags - assert "-DRELEASE_FLAG" in conan_global_flags - - @pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS") @pytest.mark.parametrize("os_name, sdk, min_version, deployment_target_flag", [ ("Macos", None, "11.0", "MACOSX_DEPLOYMENT_TARGET"), From 66dbbdc781cde98d644934acb3a6e7fd6665e1be Mon Sep 17 00:00:00 2001 From: Carlos Zoido Date: Thu, 13 Aug 2026 09:37:05 +0200 Subject: [PATCH 3/4] wip --- test/integration/toolchains/apple/test_xcodetoolchain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/toolchains/apple/test_xcodetoolchain.py b/test/integration/toolchains/apple/test_xcodetoolchain.py index 120f52c96dc..7fde617b0b1 100644 --- a/test/integration/toolchains/apple/test_xcodetoolchain.py +++ b/test/integration/toolchains/apple/test_xcodetoolchain.py @@ -63,7 +63,7 @@ def test_toolchain_flags(): client = TestClient() client.save({"conanfile.txt": "[generators]\nXcodeToolchain\n"}) cmd = "install . -s build_type=Release -s arch=x86_64 " \ - "-c 'tools.build:cxxflags=[\"flag1\"]' " \ + "-c 'tools.build:cxxflags=[\"releaseflag\"]' " \ "-c 'tools.build:defines=[\"MYDEFINITION\"]' " \ "-c 'tools.build:cflags=[\"flag2\"]' " \ "-c 'tools.build:sharedlinkflags=[\"flag3\"]' " \ @@ -75,7 +75,7 @@ def test_toolchain_flags(): 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 assert "OTHER_CFLAGS{} = $(inherited) flag2".format(condition) in conan_global_flags_props - assert "OTHER_CPLUSPLUSFLAGS{} = $(inherited) flag1".format(condition) in conan_global_flags_props + assert "OTHER_CPLUSPLUSFLAGS{} = $(inherited) releaseflag".format(condition) in conan_global_flags_props assert "OTHER_LDFLAGS{} = $(inherited) flag3 flag4".format(condition) in conan_global_flags_props # A second install, for a different configuration, must not overwrite the @@ -84,7 +84,7 @@ def test_toolchain_flags(): debug_filename = _get_filename("Debug", "x86_64", None) debug_flags = client.load("conan_global_flags{}.xcconfig".format(debug_filename)) assert "debugflag" in debug_flags - assert "flag1" not in debug_flags + assert "releaseflag" not in debug_flags conan_global_flags = client.load("conan_global_flags.xcconfig") assert '#include "conan_global_flags{}.xcconfig"'.format(filename) in conan_global_flags From db6d6de00389ef3d9065f698fd9836a2cc773a76 Mon Sep 17 00:00:00 2001 From: Carlos Zoido Date: Thu, 13 Aug 2026 09:39:29 +0200 Subject: [PATCH 4/4] wip --- .../toolchains/apple/test_xcodetoolchain.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/integration/toolchains/apple/test_xcodetoolchain.py b/test/integration/toolchains/apple/test_xcodetoolchain.py index 7fde617b0b1..4f8c2844175 100644 --- a/test/integration/toolchains/apple/test_xcodetoolchain.py +++ b/test/integration/toolchains/apple/test_xcodetoolchain.py @@ -63,28 +63,28 @@ def test_toolchain_flags(): client = TestClient() client.save({"conanfile.txt": "[generators]\nXcodeToolchain\n"}) cmd = "install . -s build_type=Release -s arch=x86_64 " \ - "-c 'tools.build:cxxflags=[\"releaseflag\"]' " \ - "-c 'tools.build:defines=[\"MYDEFINITION\"]' " \ - "-c 'tools.build:cflags=[\"flag2\"]' " \ - "-c 'tools.build:sharedlinkflags=[\"flag3\"]' " \ - "-c 'tools.build:exelinkflags=[\"flag4\"]'" + "-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) MYDEFINITION".format(condition) in conan_global_flags_props - assert "OTHER_CFLAGS{} = $(inherited) flag2".format(condition) in conan_global_flags_props - assert "OTHER_CPLUSPLUSFLAGS{} = $(inherited) releaseflag".format(condition) in conan_global_flags_props - assert "OTHER_LDFLAGS{} = $(inherited) flag3 flag4".format(condition) in conan_global_flags_props + 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=[\"debugflag\"]'") + 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 "debugflag" in debug_flags - assert "releaseflag" not in debug_flags + assert "cxxflags_debug" in debug_flags + assert "cxxflags_release" not in debug_flags conan_global_flags = client.load("conan_global_flags.xcconfig") assert '#include "conan_global_flags{}.xcconfig"'.format(filename) in conan_global_flags