diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index 4c3b6a03ee5..d6ca1adb11a 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -1,7 +1,9 @@ +import fnmatch import os import textwrap from xml.dom import minidom +from jinja2 import Template from conans.errors import ConanException from conans.model.build_info import DepCppInfo @@ -38,31 +40,34 @@ class MSBuildDeps(object): - True + True - PATH=%PATH%;$(Conan{name}BinaryDirectories)$(LocalDebuggerEnvironment) + PATH=%PATH%;$(Conan{{name}}BinaryDirectories)$(LocalDebuggerEnvironment) WindowsLocalDebugger + {% if ca_exclude -%} + $(Conan{{name}}IncludeDirectories);$(CAExcludePath) + {%- endif %} - $(Conan{name}IncludeDirectories)%(AdditionalIncludeDirectories) - $(Conan{name}PreprocessorDefinitions)%(PreprocessorDefinitions) - $(Conan{name}CompilerFlags) %(AdditionalOptions) + $(Conan{{name}}IncludeDirectories)%(AdditionalIncludeDirectories) + $(Conan{{name}}PreprocessorDefinitions)%(PreprocessorDefinitions) + $(Conan{{name}}CompilerFlags) %(AdditionalOptions) - $(Conan{name}LibraryDirectories)%(AdditionalLibraryDirectories) - $(Conan{name}Libraries)%(AdditionalDependencies) - $(Conan{name}SystemDeps)%(AdditionalDependencies) - $(Conan{name}LinkerFlags) %(AdditionalOptions) + $(Conan{{name}}LibraryDirectories)%(AdditionalLibraryDirectories) + $(Conan{{name}}Libraries)%(AdditionalDependencies) + $(Conan{{name}}SystemDeps)%(AdditionalDependencies) + $(Conan{{name}}LinkerFlags) %(AdditionalOptions) - $(Conan{name}IncludeDirectories)%(AdditionalIncludeDirectories) + $(Conan{{name}}IncludeDirectories)%(AdditionalIncludeDirectories) - $(Conan{name}IncludeDirectories)%(AdditionalIncludeDirectories) - $(Conan{name}PreprocessorDefinitions)%(PreprocessorDefinitions) - $(Conan{name}CompilerFlags) %(AdditionalOptions) + $(Conan{{name}}IncludeDirectories)%(AdditionalIncludeDirectories) + $(Conan{{name}}PreprocessorDefinitions)%(PreprocessorDefinitions) + $(Conan{{name}}CompilerFlags) %(AdditionalOptions) @@ -75,6 +80,15 @@ def __init__(self, conanfile): 'x86_64': 'x64'}.get(str(conanfile.settings.arch)) # TODO: this is ugly, improve this self.output_path = os.getcwd() + # ca_exclude section + self.exclude_code_analysis = None + ca_exclude = self._conanfile.conf["tools.microsoft.msbuilddeps"].exclude_code_analysis + if ca_exclude is not None: + # TODO: Accept single strings, not lists + self.exclude_code_analysis = eval(ca_exclude) + if not isinstance(self.exclude_code_analysis, list): + raise ConanException("tools.microsoft.msbuilddeps:exclude_code_analysis must be a" + " list of package names patterns like ['pkga*']") def generate(self): # TODO: Apply config from command line, something like @@ -165,7 +179,7 @@ def add_valid_ext(libname): 'definitions': "".join("%s;" % d for d in cpp_info.defines), 'compiler_flags': " ".join(cpp_info.cxxflags + cpp_info.cflags), 'linker_flags': " ".join(cpp_info.sharedlinkflags), - 'exe_flags': " ".join(cpp_info.exelinkflags) + 'exe_flags': " ".join(cpp_info.exelinkflags), } formatted_template = self._vars_conf_props.format(**fields) return formatted_template @@ -178,7 +192,18 @@ def _pkg_props(self, name_multi, dep_name, vars_props_name, condition, cpp_info) else: content_multi = self._dep_props - content_multi = content_multi.format(name=dep_name) + # TODO: This must include somehow the user/channel, most likely pattern to exclude/include + # Probably also the negation pattern, exclude all not @mycompany/* + ca_exclude = False + if isinstance(self.exclude_code_analysis, list): + for pattern in self.exclude_code_analysis: + if fnmatch.fnmatch(dep_name, pattern): + ca_exclude = True + break + else: + ca_exclude = self.exclude_code_analysis + + content_multi = Template(content_multi).render(name=dep_name, ca_exclude=ca_exclude) # parse the multi_file and add new import statement if needed dom = minidom.parseString(content_multi) @@ -228,7 +253,7 @@ def _content(self): direct_deps = self._conanfile.dependencies.direct_host_requires result[general_name] = self._deps_props(general_name, direct_deps) for dep in self._conanfile.dependencies.host_requires: - cpp_info = DepCppInfo(dep.cpp_info) # To account for automatic component aggregation + cpp_info = DepCppInfo(dep.cpp_info) # To account for automatic component aggregation # One file per configuration, with just the variables vars_props_name = "conan_%s%s.props" % (dep.name, conf_name) vars_conf_content = self._pkg_config_props(dep.name, cpp_info) diff --git a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py index 132dc0baf2f..373bfb14a69 100644 --- a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py +++ b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py @@ -5,6 +5,8 @@ import pytest +from parameterized import parameterized + from conans.test.assets.cpp_test_files import cpp_hello_conan_files from conans.test.assets.genconanfile import GenConanfile from conans.test.assets.sources import gen_function_cpp @@ -607,3 +609,53 @@ def build(self): self.assertIn("conan_tool.props", deps) client.run("create . pkg/0.1@") self.assertIn("Conan_tools.props in deps", client.out) + + @parameterized.expand([("['*']", True, True), + ("['pkga']", True, False), + ("['pkgb']", False, True), + ("['pkg*']", True, True), + ("['pkga', 'pkgb']", True, True), + ("['*a', '*b']", True, True), + ("['nonexist']", False, False), + ]) + def test_exclude_code_analysis(self, pattern, exclude_a, exclude_b): + client = TestClient() + client.save({"conanfile.py": GenConanfile()}) + client.run("create . pkga/1.0@") + client.run("create . pkgb/1.0@") + + conanfile = textwrap.dedent(""" + from conans import ConanFile, MSBuild + class HelloConan(ConanFile): + settings = "os", "build_type", "compiler", "arch" + requires = "pkgb/1.0@", "pkga/1.0" + generators = "msbuild" + def build(self): + msbuild = MSBuild(self) + msbuild.build("MyProject.sln") + """) + profile = textwrap.dedent(""" + include(default) + [conf] + tools.microsoft.msbuilddeps:exclude_code_analysis = %s + """ % pattern) + + client.save({"conanfile.py": conanfile, + "profile": profile}) + client.run("install . --profile profile") + depa = client.load("conan_pkga.props") + depb = client.load("conan_pkgb.props") + + if exclude_a: + inc = "$(ConanpkgaIncludeDirectories)" + ca_exclude = "%s;$(CAExcludePath)" % inc + assert ca_exclude in depa + else: + assert "CAExcludePath" not in depa + + if exclude_b: + inc = "$(ConanpkgbIncludeDirectories)" + ca_exclude = "%s;$(CAExcludePath)" % inc + assert ca_exclude in depb + else: + assert "CAExcludePath" not in depb