diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py
index d6ca1adb11a..03c070eca22 100644
--- a/conan/tools/microsoft/msbuilddeps.py
+++ b/conan/tools/microsoft/msbuilddeps.py
@@ -14,40 +14,42 @@
class MSBuildDeps(object):
- _vars_conf_props = textwrap.dedent("""\
+ _vars_props = textwrap.dedent("""\
- {root_folder}
- {compiler_flags}
- {linker_flags}
- {definitions}
- {include_dirs}
- {res_dirs}
- {lib_dirs}
- {bin_dirs}
- {libs}
- {system_libs}
+ {{root_folder}}
+ {{compiler_flags}}
+ {{linker_flags}}
+ {{definitions}}
+ {{include_dirs}}
+ {{res_dirs}}
+ {{lib_dirs}}
+ {{bin_dirs}}
+ {{libs}}
+ {{system_libs}}
+ {{dependencies}}
""")
- _dep_props = textwrap.dedent("""\
+ _conf_props = textwrap.dedent("""\
+ {% for dep in deps %}
+
+ {% endfor %}
-
+
+
-
- True
-
PATH=%PATH%;$(Conan{{name}}BinaryDirectories)$(LocalDebuggerEnvironment)
WindowsLocalDebugger
- {% if ca_exclude -%}
+ {% if ca_exclude %}
$(Conan{{name}}IncludeDirectories);$(CAExcludePath)
- {%- endif %}
+ {% endif %}
@@ -58,7 +60,7 @@ class MSBuildDeps(object):
$(Conan{{name}}LibraryDirectories)%(AdditionalLibraryDirectories)
$(Conan{{name}}Libraries)%(AdditionalDependencies)
- $(Conan{{name}}SystemDeps)%(AdditionalDependencies)
+ $(Conan{{name}}SystemLibs)%(AdditionalDependencies)
$(Conan{{name}}LinkerFlags) %(AdditionalOptions)
@@ -73,6 +75,25 @@ class MSBuildDeps(object):
""")
+ _dep_props = textwrap.dedent("""\
+
+
+
+
+
+ True
+
+
+ """)
+
+ _all_props = textwrap.dedent("""\
+
+
+
+
+
+ """)
+
def __init__(self, conanfile):
self._conanfile = conanfile
self.configuration = conanfile.settings.build_type
@@ -121,47 +142,10 @@ def _condition(self):
condition = " And ".join("'$(%s)' == '%s'" % (k, v) for k, v in props)
return condition
- def _deps_props(self, name_general, deps):
- """ this is a .props file including all declared dependencies
+ def _vars_props_file(self, name, cpp_info, deps):
+ """
+ content for conan_vars_poco_x86_release.props, containing the variables
"""
- # read the existing multi_filename or use the template if it doesn't exist
- template = textwrap.dedent("""\
-
-
-
-
-
- """)
- multi_path = os.path.join(self.output_path, name_general)
- if os.path.isfile(multi_path):
- content_multi = load(multi_path)
- else:
- content_multi = template
-
- # parse the multi_file and add a new import statement if needed
- dom = minidom.parseString(content_multi)
- import_group = dom.getElementsByTagName('ImportGroup')[0]
- children = import_group.getElementsByTagName("Import")
- for dep in deps:
- conf_props_name = "conan_%s.props" % dep.name
- for node in children:
- if conf_props_name == node.getAttribute("Project"):
- # the import statement already exists
- break
- else:
- # create a new import statement
- import_node = dom.createElement('Import')
- dep_imported = "'$(conan_%s_props_imported)' != 'True'" % dep.name
- import_node.setAttribute('Project', conf_props_name)
- import_node.setAttribute('Condition', dep_imported)
- # add it to the import group
- import_group.appendChild(import_node)
- content_multi = dom.toprettyxml()
- # To remove all extra blank lines
- content_multi = "\n".join(line for line in content_multi.splitlines() if line.strip())
- return content_multi
-
- def _pkg_config_props(self, name, cpp_info):
# returns a .props file with the variables definition for one package for one configuration
def add_valid_ext(libname):
ext = os.path.splitext(libname)[1]
@@ -180,18 +164,15 @@ 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),
+ 'dependencies': ";".join(deps)
}
- formatted_template = self._vars_conf_props.format(**fields)
+ formatted_template = Template(self._vars_props).render(**fields)
return formatted_template
- def _pkg_props(self, name_multi, dep_name, vars_props_name, condition, cpp_info):
- # read the existing mult_filename or use the template if it doesn't exist
- multi_path = os.path.join(self.output_path, name_multi)
- if os.path.isfile(multi_path):
- content_multi = load(multi_path)
- else:
- content_multi = self._dep_props
-
+ def _conf_props_file(self, dep_name, vars_props_name, deps):
+ """
+ content for conan_poco_x86_release.props, containing the activation
+ """
# 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
@@ -203,43 +184,71 @@ def _pkg_props(self, name_multi, dep_name, vars_props_name, condition, cpp_info)
else:
ca_exclude = self.exclude_code_analysis
- content_multi = Template(content_multi).render(name=dep_name, ca_exclude=ca_exclude)
+ template = Template(self._conf_props, trim_blocks=True, lstrip_blocks=True)
+ content_multi = template.render(name=dep_name, ca_exclude=ca_exclude,
+ vars_filename=vars_props_name, deps=deps)
+ return content_multi
+
+ def _dep_props_file(self, name, name_general, dep_props_filename, condition):
+ multi_path = os.path.join(self.output_path, name_general)
+ if os.path.isfile(multi_path):
+ content_multi = load(multi_path)
+ else:
+ content_multi = self._dep_props
+ content_multi = Template(content_multi).render({"name": name})
# parse the multi_file and add new import statement if needed
dom = minidom.parseString(content_multi)
- import_deps, import_vars = dom.getElementsByTagName('ImportGroup')
-
- # Transitive Deps
- children = import_deps.getElementsByTagName("Import")
- for dep in cpp_info.public_deps:
- dep_props_name = "conan_%s.props" % dep
- dep_imported = "'$(conan_%s_props_imported)' != 'True'" % dep
- for node in children:
- if (dep_props_name == node.getAttribute("Project") and
- dep_imported == node.getAttribute("Condition")):
- break # the import statement already exists
- else: # create a new import statement
- import_node = dom.createElement('Import')
- import_node.setAttribute('Condition', dep_imported)
- import_node.setAttribute('Project', dep_props_name)
- import_deps.appendChild(import_node)
+ import_vars = dom.getElementsByTagName('ImportGroup')[0]
# Current vars
children = import_vars.getElementsByTagName("Import")
for node in children:
- if (vars_props_name == node.getAttribute("Project") and
+ if (dep_props_filename == node.getAttribute("Project") and
condition == node.getAttribute("Condition")):
break # the import statement already exists
else: # create a new import statement
import_node = dom.createElement('Import')
import_node.setAttribute('Condition', condition)
- import_node.setAttribute('Project', vars_props_name)
+ import_node.setAttribute('Project', dep_props_filename)
import_vars.appendChild(import_node)
content_multi = dom.toprettyxml()
content_multi = "\n".join(line for line in content_multi.splitlines() if line.strip())
return content_multi
+ def _all_props_file(self, name_general, deps):
+ """ this is a .props file including all declared dependencies
+ """
+ multi_path = os.path.join(self.output_path, name_general)
+ if os.path.isfile(multi_path):
+ content_multi = load(multi_path)
+ else:
+ content_multi = self._all_props
+
+ # parse the multi_file and add a new import statement if needed
+ dom = minidom.parseString(content_multi)
+ import_group = dom.getElementsByTagName('ImportGroup')[0]
+ children = import_group.getElementsByTagName("Import")
+ for dep in deps:
+ conf_props_name = "conan_%s.props" % dep.name
+ for node in children:
+ if conf_props_name == node.getAttribute("Project"):
+ # the import statement already exists
+ break
+ else:
+ # create a new import statement
+ import_node = dom.createElement('Import')
+ dep_imported = "'$(conan_%s_props_imported)' != 'True'" % dep.name
+ import_node.setAttribute('Project', conf_props_name)
+ import_node.setAttribute('Condition', dep_imported)
+ # add it to the import group
+ import_group.appendChild(import_node)
+ content_multi = dom.toprettyxml()
+ # To remove all extra blank lines
+ content_multi = "\n".join(line for line in content_multi.splitlines() if line.strip())
+ return content_multi
+
def _content(self):
# We cannot use self._conanfile.warn(), because that fails for virtual conanfile
print("*** The 'msbuild' generator is EXPERIMENTAL ***")
@@ -251,17 +260,19 @@ def _content(self):
condition = self._condition()
# Include all direct build_requires for host context. This might change
direct_deps = self._conanfile.dependencies.direct_host_requires
- result[general_name] = self._deps_props(general_name, direct_deps)
+ result[general_name] = self._all_props_file(general_name, direct_deps)
for dep in self._conanfile.dependencies.host_requires:
cpp_info = DepCppInfo(dep.cpp_info) # To account for automatic component aggregation
+ public_deps = [d.name for d in dep.dependencies.direct_host_requires]
# 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)
- result[vars_props_name] = vars_conf_content
+ vars_props_name = "conan_%s_vars%s.props" % (dep.name, conf_name)
+ result[vars_props_name] = self._vars_props_file(dep.name, cpp_info, public_deps)
+ props_name = "conan_%s%s.props" % (dep.name, conf_name)
+ result[props_name] = self._conf_props_file(dep.name, vars_props_name, public_deps)
# The entry point for each package, it will have conditionals to the others
- props_name = "conan_%s.props" % dep.name
- dep_content = self._pkg_props(props_name, dep.name, vars_props_name, condition, cpp_info)
- result[props_name] = dep_content
+ dep_name = "conan_%s.props" % dep.name
+ dep_content = self._dep_props_file(dep.name, dep_name, props_name, condition)
+ result[dep_name] = dep_content
return result
diff --git a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py
index 373bfb14a69..cde67216fc4 100644
--- a/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py
+++ b/conans/test/functional/toolchains/microsoft/test_msbuilddeps.py
@@ -455,7 +455,7 @@ def test_install_reference(self):
client.run("install mypkg/0.1@ -g MSBuildDeps")
self.assertIn("Generator 'MSBuildDeps' calling 'generate()'", client.out)
# https://github.com/conan-io/conan/issues/8163
- props = client.load("conan_mypkg_release_x64.props") # default Release/x64
+ props = client.load("conan_mypkg_vars_release_x64.props") # default Release/x64
folder = props[props.find("")+len("")
:props.find("")]
self.assertTrue(os.path.isfile(os.path.join(folder, "conaninfo.txt")))
@@ -610,6 +610,7 @@ def build(self):
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),
@@ -625,7 +626,9 @@ def test_exclude_code_analysis(self, pattern, exclude_a, exclude_b):
client.run("create . pkgb/1.0@")
conanfile = textwrap.dedent("""
- from conans import ConanFile, MSBuild
+ from conans import ConanFile
+ from conan.tools.microsoft import MSBuild
+
class HelloConan(ConanFile):
settings = "os", "build_type", "compiler", "arch"
requires = "pkgb/1.0@", "pkga/1.0"
@@ -636,6 +639,8 @@ def build(self):
""")
profile = textwrap.dedent("""
include(default)
+ build_type=Release
+ arch=x86_64
[conf]
tools.microsoft.msbuilddeps:exclude_code_analysis = %s
""" % pattern)
@@ -643,8 +648,8 @@ def build(self):
client.save({"conanfile.py": conanfile,
"profile": profile})
client.run("install . --profile profile")
- depa = client.load("conan_pkga.props")
- depb = client.load("conan_pkgb.props")
+ depa = client.load("conan_pkga_release_x64.props")
+ depb = client.load("conan_pkgb_release_x64.props")
if exclude_a:
inc = "$(ConanpkgaIncludeDirectories)"