diff --git a/conan/tools/apple/xcodedeps.py b/conan/tools/apple/xcodedeps.py index 5a57ac051b6..45cbe072450 100644 --- a/conan/tools/apple/xcodedeps.py +++ b/conan/tools/apple/xcodedeps.py @@ -122,7 +122,7 @@ def generate(self): for generator_file, content in generator_files.items(): save(generator_file, content) - def _conf_xconfig_file(self, pkg_name, comp_name, transitive_cpp_infos): + def _conf_xconfig_file(self, require, pkg_name, comp_name, transitive_cpp_infos): """ content for conan_poco_x86_release.xcconfig, containing the activation """ @@ -147,6 +147,23 @@ def _merged_vars(name): 'condition': _xcconfig_conditional(self._conanfile.settings) } + if not require.headers: + fields["include_dirs"] = "" + + if not require.libs: + fields["lib_dirs"] = "" + fields["libs"] = "" + fields["system_libs"] = "" + fields["frameworkdirs"] = "" + fields["frameworks"] = "" + + if not require.libs and not require.headers: + fields["definitions"] = "" + fields["c_compiler_flags"] = "" + fields["cxx_compiler_flags"] = "" + fields["linker_flags"] = "" + fields["exe_flags"] = "" + template = Template(self._conf_xconfig) content_multi = template.render(**fields) return content_multi @@ -197,13 +214,13 @@ def _global_xconfig_content(self): GLOBAL_XCCONFIG_TEMPLATE, [self.general_name]) - def get_content_for_component(self, pkg_name, component_name, transitive_internal, transitive_external): + def get_content_for_component(self, require, pkg_name, component_name, transitive_internal, transitive_external): result = {} conf_name = _xcconfig_settings_filename(self._conanfile.settings) props_name = "conan_{}_{}{}.xcconfig".format(pkg_name, component_name, conf_name) - result[props_name] = self._conf_xconfig_file(pkg_name, component_name, transitive_internal) + result[props_name] = self._conf_xconfig_file(require, pkg_name, component_name, transitive_internal) # The entry point for each package file_dep_name = "conan_{}_{}.xcconfig".format(pkg_name, component_name) @@ -220,8 +237,8 @@ def _content(self): # All components are included in the conan_pkgname.xcconfig file host_req = self._conanfile.dependencies.host test_req = self._conanfile.dependencies.test - all_deps = list(host_req.values()) + list(test_req.values()) - for dep in all_deps: + + for require, dep in list(host_req.items()) + list(test_req.items()): dep_name = _format_name(dep.ref.name) @@ -260,7 +277,7 @@ def _transitive_components(component): transitive_internal = list(OrderedDict.fromkeys(transitive_internal).keys()) transitive_external = list(OrderedDict.fromkeys(transitive_external).keys()) - component_content = self.get_content_for_component(dep_name, comp_name, + component_content = self.get_content_for_component(require, dep_name, comp_name, transitive_internal, transitive_external) include_components_names.append((dep_name, comp_name)) @@ -269,7 +286,7 @@ def _transitive_components(component): public_deps = [(_format_name(d.ref.name),) * 2 for r, d in dep.dependencies.direct_host.items() if r.visible] required_components = dep.cpp_info.required_components if dep.cpp_info.required_components else public_deps - root_content = self.get_content_for_component(dep_name, dep_name, [dep.cpp_info], + root_content = self.get_content_for_component(require, dep_name, dep_name, [dep.cpp_info], required_components) include_components_names.append((dep_name, dep_name)) result.update(root_content) diff --git a/conans/test/integration/toolchains/apple/test_xcodedeps.py b/conans/test/integration/toolchains/apple/test_xcodedeps.py index ec8d810a38a..2113a7dd92c 100644 --- a/conans/test/integration/toolchains/apple/test_xcodedeps.py +++ b/conans/test/integration/toolchains/apple/test_xcodedeps.py @@ -176,6 +176,126 @@ def package_info(self): assert "mylibdir" in component4_vars +@pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS") +def test_xcodedeps_traits(): + client = TestClient() + conanfile_py = textwrap.dedent(""" + from conan import ConanFile + class LibConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + {package_info} + {requirements} + """) + + package_info = """ + def package_info(self): + self.cpp_info.components["cmp1"].includedirs = ["cmp1_includedir"] + self.cpp_info.components["cmp2"].includedirs = ["cmp2_includedir"] + + self.cpp_info.components["cmp1"].libdirs = ["cmp1_libdir"] + self.cpp_info.components["cmp2"].libdirs = ["cmp2_libdir"] + self.cpp_info.components["cmp1"].libs = ["cmp1_lib"] + self.cpp_info.components["cmp2"].libs = ["cmp2_lib"] + self.cpp_info.components["cmp1"].system_libs = ["cmp1_system_lib"] + self.cpp_info.components["cmp2"].system_libs = ["cmp2_system_lib"] + self.cpp_info.components["cmp1"].frameworkdirs = ["cmp1_frameworkdir"] + self.cpp_info.components["cmp2"].frameworkdirs = ["cmp2_frameworkdir"] + self.cpp_info.components["cmp1"].frameworks = ["cmp1_framework"] + self.cpp_info.components["cmp2"].frameworks = ["cmp2_framework"] + + self.cpp_info.components["cmp1"].defines = ["cmp1_define"] + self.cpp_info.components["cmp2"].defines = ["cmp2_define"] + self.cpp_info.components["cmp1"].cflags = ["cmp1_cflag"] + self.cpp_info.components["cmp2"].cflags = ["cmp2_cflag"] + self.cpp_info.components["cmp1"].cxxflags = ["cmp1_cxxflag"] + self.cpp_info.components["cmp2"].cxxflags = ["cmp2_cxxflag"] + self.cpp_info.components["cmp1"].sharedlinkflags = ["cmp1_sharedlinkflag"] + self.cpp_info.components["cmp2"].sharedlinkflags = ["cmp2_sharedlinkflag"] + self.cpp_info.components["cmp1"].exelinkflags = ["cmp1_exelinkflag"] + self.cpp_info.components["cmp2"].exelinkflags = ["cmp2_exelinkflag"] + """ + + client.save({"lib_a.py": conanfile_py.format(requirements="", package_info=package_info)}) + + client.run("create lib_a.py --name=lib_a --version=1.0") + + requirements = """ + def requirements(self): + self.requires("lib_a/1.0", headers=False) + """ + + client.save({"lib_b.py": conanfile_py.format(requirements=requirements, package_info="")}, + clean_first=True) + + client.run("install lib_b.py -g XcodeDeps") + + comp1_info = client.load("conan_lib_a_cmp1_release_x86_64.xcconfig") + comp2_info = client.load("conan_lib_a_cmp2_release_x86_64.xcconfig") + + assert "cmp1_include" not in comp1_info + assert "cmp2_include" not in comp2_info + + requirements = """ + def requirements(self): + self.requires("lib_a/1.0", libs=False) + """ + + client.save({"lib_b.py": conanfile_py.format(requirements=requirements, package_info="")}, + clean_first=True) + client.run("install lib_b.py -g XcodeDeps") + + comp1_info = client.load("conan_lib_a_cmp1_release_x86_64.xcconfig") + comp2_info = client.load("conan_lib_a_cmp2_release_x86_64.xcconfig") + + assert "cmp1_frameworkdir" not in comp1_info + assert "cmp2_frameworkdir" not in comp2_info + + assert "-lcmp1_lib -lcmp1_system_lib -framework cmp1_framework" not in comp1_info + assert "-lcmp2_lib -lcmp2_system_lib -framework cmp2_framework" not in comp2_info + + requirements = """ + def requirements(self): + self.requires("lib_a/1.0", headers=False, libs=False) + """ + + client.save({"lib_b.py": conanfile_py.format(requirements=requirements, package_info="")}, + clean_first=True) + client.run("install lib_b.py -g XcodeDeps") + + not_existing = ["conan_lib_a_cmp1_release_x86_64.xcconfig", "conan_lib_a_cmp1.xcconfig", + "conan_lib_a_cmp2_release_x86_64.xcconfig", "conan_lib_a_cmp2.xcconfig", + "conan_lib_a.xcconfig"] + + for file in not_existing: + assert not os.path.exists(os.path.join(client.current_folder, file)) + + assert '#include "conan_lib_a.xcconfig"' not in client.load("conandeps.xcconfig") + + requirements = """ + def requirements(self): + self.requires("lib_a/1.0", headers=False, libs=False, run=True) + """ + + client.save({"lib_b.py": conanfile_py.format(requirements=requirements, package_info="")}, + clean_first=True) + + client.run("install lib_b.py -g XcodeDeps") + + comp1_info = client.load("conan_lib_a_cmp1_release_x86_64.xcconfig") + comp2_info = client.load("conan_lib_a_cmp2_release_x86_64.xcconfig") + + assert "cmp1_define" not in comp1_info + assert "cmp2_define" not in comp2_info + assert "cmp1_cflag" not in comp1_info + assert "cmp2_cflag" not in comp2_info + assert "cmp1_cxxflag" not in comp1_info + assert "cmp2_cxxflag" not in comp2_info + assert "cmp1_sharedlinkflag" not in comp1_info + assert "cmp2_sharedlinkflag" not in comp2_info + assert "cmp1_exelinkflag" not in comp1_info + assert "cmp2_exelinkflag" not in comp2_info + + @pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS") def test_xcodedeps_frameworkdirs(): client = TestClient()