From 4aea5f60efd0d100f7ad629bb489a022989e73ef Mon Sep 17 00:00:00 2001 From: SSE4 Date: Mon, 22 Mar 2021 21:58:48 +0700 Subject: [PATCH 1/6] - add CAExcludePath Signed-off-by: SSE4 --- conan/tools/microsoft/msbuilddeps.py | 22 ++++++++- .../functional/generators/msbuild_test.py | 49 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index 8a6f18fcf7c..8e0ceda58db 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -1,3 +1,4 @@ +import fnmatch import os import textwrap from xml.dom import minidom @@ -25,6 +26,7 @@ class MSBuildDeps(object): {bin_dirs} {libs} {system_libs} + {ca_exclude_dirs} """) @@ -42,6 +44,7 @@ class MSBuildDeps(object): PATH=%PATH%;$(Conan{name}BinaryDirectories)$(LocalDebuggerEnvironment) WindowsLocalDebugger + $(Conan{name}CAExcludeDirectories);$(CAExcludePath) @@ -74,6 +77,7 @@ def __init__(self, conanfile): 'x86_64': 'x64'}.get(str(conanfile.settings.arch)) # TODO: this is ugly, improve this self.output_path = os.getcwd() + self.exclude_code_analysis = None def generate(self): # TODO: Apply config from command line, something like @@ -84,6 +88,14 @@ def generate(self): # config_filename # TODO: This is duplicated in write_generators() function, would need to be moved # to generators and called from there + exclude_code_analysis = self._conanfile.conf["tools.microsoft.msbuilddeps"].exclude_code_analysis + if exclude_code_analysis: + if isinstance(exclude_code_analysis, str): + self.exclude_code_analysis = exclude_code_analysis.split(";") + if self.exclude_code_analysis in [["True"], True]: + self.exclude_code_analysis = ["*"] + elif self.exclude_code_analysis in [["False"], False]: + self.exclude_code_analysis = None if self.configuration is None: raise ConanException("MSBuildDeps.configuration is None, it should have a value") if self.platform is None: @@ -152,6 +164,13 @@ def add_valid_ext(libname): ext = os.path.splitext(libname)[1] return '%s;' % libname if ext in VALID_LIB_EXTENSIONS else '%s.lib;' % libname + exclude_code_analisys = False + if self.exclude_code_analysis: + for pattern in self.exclude_code_analysis: + if fnmatch.fnmatch(name, pattern): + exclude_code_analisys = True + break + fields = { 'name': name, 'root_folder': cpp_info.rootpath, @@ -164,7 +183,8 @@ 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), + 'ca_exclude_dirs': '$(Conan%sIncludeDirectories)' % name if exclude_code_analisys else '' } formatted_template = self._vars_conf_props.format(**fields) return formatted_template diff --git a/conans/test/functional/generators/msbuild_test.py b/conans/test/functional/generators/msbuild_test.py index 132dc0baf2f..62cc452c130 100644 --- a/conans/test/functional/generators/msbuild_test.py +++ b/conans/test/functional/generators/msbuild_test.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,50 @@ 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, True), + ("*", True, True), + ("pkga", True, False), + ("pkgb", False, True), + ("pkg*", True, True), + ("pkga;pkgb", True, True), + ("*a;*b", True, True), + ("False", 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.save({"conanfile.py": GenConanfile()}) + 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_release_x64.props") + depb = client.load("conan_pkgb_release_x64.props") + + pattern = "$(ConanpkgaIncludeDirectories)" if exclude_a else "" + pattern = "%s" % pattern + self.assertIn(pattern, depa) + + pattern = "$(ConanpkgbIncludeDirectories)" if exclude_b else "" + pattern = "%s" % pattern + self.assertIn(pattern, depb) From 66ced377916fbf7e738dcb23018015e84dbc8fbf Mon Sep 17 00:00:00 2001 From: SSE4 Date: Mon, 22 Mar 2021 23:39:12 +0700 Subject: [PATCH 2/6] Update conan/tools/microsoft/msbuilddeps.py Co-authored-by: James --- conan/tools/microsoft/msbuilddeps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index 8e0ceda58db..e95ef64a9b8 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -184,7 +184,7 @@ def add_valid_ext(libname): 'compiler_flags': " ".join(cpp_info.cxxflags + cpp_info.cflags), 'linker_flags': " ".join(cpp_info.sharedlinkflags), 'exe_flags': " ".join(cpp_info.exelinkflags), - 'ca_exclude_dirs': '$(Conan%sIncludeDirectories)' % name if exclude_code_analisys else '' + 'ca_exclude_dirs': '$(Conan{}IncludeDirectories)'.format(name) if exclude_code_analysis else '' } formatted_template = self._vars_conf_props.format(**fields) return formatted_template From d3513b8f4ab404efe3fb5434238cf07825799cdd Mon Sep 17 00:00:00 2001 From: SSE4 Date: Mon, 22 Mar 2021 23:39:42 +0700 Subject: [PATCH 3/6] Update conan/tools/microsoft/msbuilddeps.py Co-authored-by: James --- conan/tools/microsoft/msbuilddeps.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index e95ef64a9b8..dff0c255e94 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -164,13 +164,14 @@ def add_valid_ext(libname): ext = os.path.splitext(libname)[1] return '%s;' % libname if ext in VALID_LIB_EXTENSIONS else '%s.lib;' % libname - exclude_code_analisys = False - if self.exclude_code_analysis: - for pattern in self.exclude_code_analysis: - if fnmatch.fnmatch(name, pattern): - exclude_code_analisys = True - break - + exclude_code_analysis = False + if isinstance(self.exclude_code_analysis, list): + for pattern in self.exclude_code_analysis : + if fnmatch.fnmatch(name, pattern): + exclude_code_analysis = True + break + else: + exclude_code_analysis = self.exclude_code_analysis fields = { 'name': name, 'root_folder': cpp_info.rootpath, From b3987be19e14172ffba64553830e7095f7650c56 Mon Sep 17 00:00:00 2001 From: SSE4 Date: Mon, 22 Mar 2021 23:40:08 +0700 Subject: [PATCH 4/6] Update conan/tools/microsoft/msbuilddeps.py Co-authored-by: James --- conan/tools/microsoft/msbuilddeps.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index dff0c255e94..a71e872f7c5 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -89,13 +89,9 @@ def generate(self): # TODO: This is duplicated in write_generators() function, would need to be moved # to generators and called from there exclude_code_analysis = self._conanfile.conf["tools.microsoft.msbuilddeps"].exclude_code_analysis - if exclude_code_analysis: - if isinstance(exclude_code_analysis, str): - self.exclude_code_analysis = exclude_code_analysis.split(";") - if self.exclude_code_analysis in [["True"], True]: - self.exclude_code_analysis = ["*"] - elif self.exclude_code_analysis in [["False"], False]: - self.exclude_code_analysis = None + if exclude_code_analysis is not None: + self.exclude_code_analysis = eval(exclude_code_analysis) + assert isinstance(self.exclude_code_analysis, (bool, list)) if self.configuration is None: raise ConanException("MSBuildDeps.configuration is None, it should have a value") if self.platform is None: From 8eb6746d0d7a48a72c801cb73e5ea22bafc5be77 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 29 Mar 2021 12:21:01 +0200 Subject: [PATCH 5/6] fixing code --- conan/tools/microsoft/msbuilddeps.py | 66 ++++++++++--------- .../toolchains/microsoft/test_msbuilddeps.py | 40 ++++++----- 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index f0df774eed2..9e40ab57c88 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -3,6 +3,7 @@ import textwrap from xml.dom import minidom +from jinja2 import Template from conans.errors import ConanException from conans.model.build_info import DepCppInfo @@ -27,7 +28,6 @@ class MSBuildDeps(object): {bin_dirs} {libs} {system_libs} - {ca_exclude_dirs} """) @@ -40,32 +40,34 @@ class MSBuildDeps(object): - True + True - PATH=%PATH%;$(Conan{name}BinaryDirectories)$(LocalDebuggerEnvironment) + PATH=%PATH%;$(Conan{{name}}BinaryDirectories)$(LocalDebuggerEnvironment) WindowsLocalDebugger - $(Conan{name}CAExcludeDirectories);$(CAExcludePath) + {% 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) @@ -78,7 +80,13 @@ 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) + assert isinstance(self.exclude_code_analysis, (bool, list)) def generate(self): # TODO: Apply config from command line, something like @@ -89,10 +97,6 @@ def generate(self): # config_filename # TODO: This is duplicated in write_generators() function, would need to be moved # to generators and called from there - exclude_code_analysis = self._conanfile.conf["tools.microsoft.msbuilddeps"].exclude_code_analysis - if exclude_code_analysis is not None: - self.exclude_code_analysis = eval(exclude_code_analysis) - assert isinstance(self.exclude_code_analysis, (bool, list)) if self.configuration is None: raise ConanException("MSBuildDeps.configuration is None, it should have a value") if self.platform is None: @@ -161,14 +165,6 @@ def add_valid_ext(libname): ext = os.path.splitext(libname)[1] return '%s;' % libname if ext in VALID_LIB_EXTENSIONS else '%s.lib;' % libname - exclude_code_analysis = False - if isinstance(self.exclude_code_analysis, list): - for pattern in self.exclude_code_analysis : - if fnmatch.fnmatch(name, pattern): - exclude_code_analysis = True - break - else: - exclude_code_analysis = self.exclude_code_analysis fields = { 'name': name, 'root_folder': cpp_info.rootpath, @@ -182,7 +178,6 @@ def add_valid_ext(libname): 'compiler_flags': " ".join(cpp_info.cxxflags + cpp_info.cflags), 'linker_flags': " ".join(cpp_info.sharedlinkflags), 'exe_flags': " ".join(cpp_info.exelinkflags), - 'ca_exclude_dirs': '$(Conan{}IncludeDirectories)'.format(name) if exclude_code_analysis else '' } formatted_template = self._vars_conf_props.format(**fields) return formatted_template @@ -195,7 +190,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) @@ -245,7 +251,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 62cc452c130..83a3282aa2d 100644 --- a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py +++ b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py @@ -610,21 +610,19 @@ def build(self): client.run("create . pkg/0.1@") self.assertIn("Conan_tools.props in deps", client.out) - @parameterized.expand([("True", True, True), - ("*", True, True), - ("pkga", True, False), - ("pkgb", False, True), - ("pkg*", True, True), - ("pkga;pkgb", True, True), - ("*a;*b", True, True), + ("['*']", True, True), + ("['pkga']", True, False), + ("['pkgb']", False, True), + ("['pkg*']", True, True), + ("['pkga', 'pkgb']", True, True), + ("['*a', '*b']", True, True), ("False", 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.save({"conanfile.py": GenConanfile()}) client.run("create . pkgb/1.0@") conanfile = textwrap.dedent(""" @@ -646,13 +644,19 @@ def build(self): client.save({"conanfile.py": conanfile, "profile": profile}) client.run("install . --profile profile") - depa = client.load("conan_pkga_release_x64.props") - depb = client.load("conan_pkgb_release_x64.props") - - pattern = "$(ConanpkgaIncludeDirectories)" if exclude_a else "" - pattern = "%s" % pattern - self.assertIn(pattern, depa) - - pattern = "$(ConanpkgbIncludeDirectories)" if exclude_b else "" - pattern = "%s" % pattern - self.assertIn(pattern, depb) + 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 From 350fc44e77e93c89286654a75f1fd6fb9f958718 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 29 Mar 2021 17:41:49 +0200 Subject: [PATCH 6/6] remove boolean config, force only lists --- conan/tools/microsoft/msbuilddeps.py | 4 +++- .../test/functional/toolchains/microsoft/test_msbuilddeps.py | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py index 9e40ab57c88..d6ca1adb11a 100644 --- a/conan/tools/microsoft/msbuilddeps.py +++ b/conan/tools/microsoft/msbuilddeps.py @@ -86,7 +86,9 @@ def __init__(self, conanfile): if ca_exclude is not None: # TODO: Accept single strings, not lists self.exclude_code_analysis = eval(ca_exclude) - assert isinstance(self.exclude_code_analysis, (bool, list)) + 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 diff --git a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py index 83a3282aa2d..373bfb14a69 100644 --- a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py +++ b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py @@ -610,14 +610,13 @@ def build(self): client.run("create . pkg/0.1@") self.assertIn("Conan_tools.props in deps", client.out) - @parameterized.expand([("True", True, True), - ("['*']", True, True), + @parameterized.expand([("['*']", True, True), ("['pkga']", True, False), ("['pkgb']", False, True), ("['pkg*']", True, True), ("['pkga', 'pkgb']", True, True), ("['*a', '*b']", True, True), - ("False", False, False), + ("['nonexist']", False, False), ]) def test_exclude_code_analysis(self, pattern, exclude_a, exclude_b): client = TestClient()