Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSBuildDeps handle build_requires executables #9686

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions conan/tools/cmake/cmakedeps/cmakedeps.py
@@ -1,5 +1,4 @@
import os
from fnmatch import fnmatch

from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.cmake.cmakedeps.templates.config import ConfigTemplate
Expand All @@ -17,6 +16,7 @@
FIND_MODE_NONE = "none"
FIND_MODE_BOTH = "both"


class CMakeDeps(object):

def __init__(self, conanfile):
Expand Down Expand Up @@ -81,7 +81,8 @@ def content(self):
if dep.is_build_context and dep.ref.name not in self.build_context_activated:
continue

cmake_find_mode = dep.new_cpp_info.get_property("cmake_find_mode", "CMakeDeps") or FIND_MODE_CONFIG
cmake_find_mode = dep.new_cpp_info.get_property("cmake_find_mode", "CMakeDeps")
cmake_find_mode = cmake_find_mode or FIND_MODE_CONFIG
cmake_find_mode = cmake_find_mode.lower()
# Skip from the requirement
if cmake_find_mode == FIND_MODE_NONE:
Expand Down
77 changes: 58 additions & 19 deletions conan/tools/microsoft/msbuilddeps.py
Expand Up @@ -7,7 +7,6 @@

from conan.tools._check_build_profile import check_using_build_profile
from conans.errors import ConanException
from conans.model.build_info import DepCppInfo
from conans.util.files import load, save

VALID_LIB_EXTENSIONS = (".so", ".lib", ".a", ".dylib", ".bc")
Expand All @@ -20,16 +19,18 @@ class MSBuildDeps(object):
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="ConanVariables">
<Conan{{name}}RootFolder>{{root_folder}}</Conan{{name}}RootFolder>
<Conan{{name}}BinaryDirectories>{{bin_dirs}}</Conan{{name}}BinaryDirectories>
<Conan{{name}}Dependencies>{{dependencies}}</Conan{{name}}Dependencies>
{% if host_context %}
<Conan{{name}}CompilerFlags>{{compiler_flags}}</Conan{{name}}CompilerFlags>
<Conan{{name}}LinkerFlags>{{linker_flags}}</Conan{{name}}LinkerFlags>
<Conan{{name}}PreprocessorDefinitions>{{definitions}}</Conan{{name}}PreprocessorDefinitions>
<Conan{{name}}IncludeDirectories>{{include_dirs}}</Conan{{name}}IncludeDirectories>
<Conan{{name}}ResourceDirectories>{{res_dirs}}</Conan{{name}}ResourceDirectories>
<Conan{{name}}LibraryDirectories>{{lib_dirs}}</Conan{{name}}LibraryDirectories>
<Conan{{name}}BinaryDirectories>{{bin_dirs}}</Conan{{name}}BinaryDirectories>
<Conan{{name}}Libraries>{{libs}}</Conan{{name}}Libraries>
<Conan{{name}}SystemLibs>{{system_libs}}</Conan{{name}}SystemLibs>
<Conan{{name}}Dependencies>{{dependencies}}</Conan{{name}}Dependencies>
{% endif %}
</PropertyGroup>
</Project>
""")
Expand All @@ -45,6 +46,7 @@ class MSBuildDeps(object):
<ImportGroup Label="PropertySheets">
<Import Project="{{vars_filename}}"/>
</ImportGroup>
{% if host_context %}
<PropertyGroup>
<LocalDebuggerEnvironment>PATH=%PATH%;$(Conan{{name}}BinaryDirectories)$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
Expand Down Expand Up @@ -73,6 +75,11 @@ class MSBuildDeps(object):
<AdditionalOptions>$(Conan{{name}}CompilerFlags) %(AdditionalOptions)</AdditionalOptions>
</ResourceCompile>
</ItemDefinitionGroup>
{% else %}
<PropertyGroup>
<ExecutablePath>$(Conan{{name}}BinaryDirectories)$(ExecutablePath)</ExecutablePath>
</PropertyGroup>
{% endif %}
</Project>
""")

Expand Down Expand Up @@ -142,7 +149,7 @@ def _condition(self):
condition = " And ".join("'$(%s)' == '%s'" % (k, v) for k, v in props)
return condition

def _vars_props_file(self, name, cpp_info, deps):
def _vars_props_file(self, dep, name, cpp_info, deps, build=False):
"""
content for conan_vars_poco_x86_release.props, containing the variables
"""
Expand All @@ -151,25 +158,30 @@ def add_valid_ext(libname):
ext = os.path.splitext(libname)[1]
return '%s;' % libname if ext in VALID_LIB_EXTENSIONS else '%s.lib;' % libname

package_folder = dep.package_folder.replace('\\', '/')\
.replace('$', '\\$').replace('"', '\\"')
pkg_placeholder = "$(Conan{}RootFolder)/".format(name)
fields = {
'name': name,
'root_folder': cpp_info.rootpath,
'bin_dirs': "".join("%s;" % p for p in cpp_info.bin_paths),
'res_dirs': "".join("%s;" % p for p in cpp_info.res_paths),
'include_dirs': "".join("%s;" % p for p in cpp_info.include_paths),
'lib_dirs': "".join("%s;" % p for p in cpp_info.lib_paths),
'root_folder': package_folder,
'bin_dirs': "".join("%s;" % (pkg_placeholder + p) for p in cpp_info.bindirs),
'res_dirs': "".join("%s;" % (pkg_placeholder + p) for p in cpp_info.resdirs),
'include_dirs': "".join("%s;" % (pkg_placeholder + p) for p in cpp_info.includedirs),
'lib_dirs': "".join("%s;" % (pkg_placeholder + p) for p in cpp_info.libdirs),
'libs': "".join([add_valid_ext(lib) for lib in cpp_info.libs]),
'system_libs': "".join([add_valid_ext(sys_dep) for sys_dep in cpp_info.system_libs]),
'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),
'dependencies': ";".join(deps)
'dependencies': ";".join(deps),
'host_context': not build
}
formatted_template = Template(self._vars_props).render(**fields)
formatted_template = Template(self._vars_props, trim_blocks=True,
lstrip_blocks=True).render(**fields)
return formatted_template

def _conf_props_file(self, dep_name, vars_props_name, deps):
def _conf_props_file(self, dep_name, vars_props_name, deps, build=False):
"""
content for conan_poco_x86_release.props, containing the activation
"""
Expand All @@ -185,7 +197,8 @@ def _conf_props_file(self, dep_name, vars_props_name, deps):
ca_exclude = self.exclude_code_analysis

template = Template(self._conf_props, trim_blocks=True, lstrip_blocks=True)
content_multi = template.render(name=dep_name, ca_exclude=ca_exclude,
content_multi = template.render(host_context=not build,
name=dep_name, ca_exclude=ca_exclude,
vars_filename=vars_props_name, deps=deps)
return content_multi

Expand Down Expand Up @@ -232,8 +245,10 @@ def _all_props_file(self, name_general, deps):
dom = minidom.parseString(content_multi)
import_group = dom.getElementsByTagName('ImportGroup')[0]
children = import_group.getElementsByTagName("Import")
for dep in deps:
for req, dep in deps.items():
dep_name = dep.ref.name.replace(".", "_")
if req.build:
dep_name += "_build"
conf_props_name = "conan_%s.props" % dep_name
for node in children:
if conf_props_name == node.getAttribute("Project"):
Expand Down Expand Up @@ -261,21 +276,20 @@ def _content(self):
general_name = "conandeps.props"
conf_name = self._config_filename()
condition = self._condition()
# Include all direct build_requires for host context. This might change
direct_deps = self._conanfile.dependencies.filter({"direct": True, "build": False})

host_req = list(self._conanfile.dependencies.host.values())
test_req = list(self._conanfile.dependencies.test.values())

result[general_name] = self._all_props_file(general_name, direct_deps.values())
for dep in host_req + test_req:
dep_name = dep.ref.name
dep_name = dep_name.replace(".", "_")
cpp_info = DepCppInfo(dep.cpp_info) # To account for automatic component aggregation
cpp_info = dep.new_cpp_info.copy()
cpp_info.aggregate_components()
public_deps = [d.ref.name.replace(".", "_")
for r, d in dep.dependencies.direct_host.items() if r.visible]
# One file per configuration, with just the variables
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)
result[vars_props_name] = self._vars_props_file(dep, 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)

Expand All @@ -284,4 +298,29 @@ def _content(self):
dep_content = self._dep_props_file(dep_name, file_dep_name, props_name, condition)
result[file_dep_name] = dep_content

build_req = list(self._conanfile.dependencies.build.values())
for dep in build_req:
dep_name = dep.ref.name
dep_name = dep_name.replace(".", "_") + "_build"
cpp_info = dep.new_cpp_info.copy()
cpp_info.aggregate_components()
public_deps = [d.ref.name.replace(".", "_")
for r, d in dep.dependencies.direct_host.items() if r.visible]
# One file per configuration, with just the variables
vars_props_name = "conan_%s_vars%s.props" % (dep_name, conf_name)
result[vars_props_name] = self._vars_props_file(dep, dep_name, cpp_info, public_deps,
build=True)
props_name = "conan_%s%s.props" % (dep_name, conf_name)
result[props_name] = self._conf_props_file(dep_name, vars_props_name, public_deps,
build=True)

# The entry point for each package, it will have conditionals to the others
file_dep_name = "conan_%s.props" % dep_name
dep_content = self._dep_props_file(dep_name, file_dep_name, props_name, condition)
result[file_dep_name] = dep_content

# Include all direct build_requires for host context. This might change
direct_deps = self._conanfile.dependencies.filter({"direct": True})
result[general_name] = self._all_props_file(general_name, direct_deps)

return result
129 changes: 129 additions & 0 deletions conans/test/functional/toolchains/microsoft/test_msbuilddeps.py
Expand Up @@ -857,3 +857,132 @@ def test_private_transitive():

pkg_data_props = client.load("conan_pkg_release_x64.props")
assert "conan_dep.props" not in pkg_data_props


@pytest.mark.skipif(platform.system() != "Windows", reason="Requires MSBuild")
def test_build_requires():
# https://github.com/conan-io/conan/issues/9545
client = TestClient()
package = "self.copy('*', src=str(self.settings.arch), dst='bin')"
dep = GenConanfile().with_exports("*").with_settings("arch").with_package(package)
consumer = textwrap.dedent("""
from conans import ConanFile
from conan.tools.microsoft import MSBuild
class Pkg(ConanFile):
settings = "os", "compiler", "build_type", "arch"
build_requires = "dep/0.1"
generators = "MSBuildDeps", "MSBuildToolchain"
def build(self):
msbuild = MSBuild(self)
msbuild.build("hello.sln")
""")
hello_vcxproj = textwrap.dedent( r"""<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{6F392A05-B151-490C-9505-B2A49720C4D9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>MyProject</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

<ImportGroup Label="PropertySheets">
<Import Project="..\conandeps.props" />
</ImportGroup>

<PropertyGroup Label="UserMacros" />

<ItemGroup>
<CustomBuild Include="data.proto">
<FileType>Document</FileType>
<Outputs>data.proto.h</Outputs>
<Command>dep1tool</Command>
</CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>""")

hello_sln = textwrap.dedent(r"""
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.757
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MyProject", "MyProject\MyProject.vcxproj", "{6F392A05-B151-490C-9505-B2A49720C4D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6F392A05-B151-490C-9505-B2A49720C4D9}.Release|x64.ActiveCfg = Release|x64
{6F392A05-B151-490C-9505-B2A49720C4D9}.Release|x64.Build.0 = Release|x64
{6F392A05-B151-490C-9505-B2A49720C4D9}.Release|x86.ActiveCfg = Release|Win32
{6F392A05-B151-490C-9505-B2A49720C4D9}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DE6E462F-E299-4F9C-951A-F9404EB51521}
EndGlobalSection
EndGlobal
""")
client.save({"dep/conanfile.py": dep,
"dep/x86/dep1tool.bat": "@echo Invoking 32bit dep_1 build tool",
"dep/x86_64/dep1tool.bat": "@echo Invoking 64bit dep_1 build tool",
"consumer/conanfile.py": consumer,
"consumer/hello.sln": hello_sln,
"consumer/MyProject/MyProject.vcxproj": hello_vcxproj,
"consumer/MyProject/data.proto": "dataproto"})
client.run("create dep dep/0.1@ -s arch=x86")
client.run("create dep dep/0.1@ -s arch=x86_64")
with client.chdir("consumer"):
client.run('install . -s compiler="Visual Studio" -s compiler.version=15 '
" -s arch=x86_64 -s build_type=Release")
assert "dep/0.1:c0519e2d9702ec12d057bb15adb7a02baaf18107 - Cache" in client.out
deps_props = client.load("conandeps.props")
assert "conan_dep_build.props" in deps_props
client.run("build .")
assert "Invoking 64bit dep_1 build tool" in client.out

client.run('install . -s compiler="Visual Studio" -s compiler.version=15 '
" -s arch=x86 -s build_type=Release")
client.run("build .")
assert "Invoking 32bit dep_1 build tool" in client.out

# Make sure it works with 2 profiles too
client.run('install . -s compiler="Visual Studio" -s compiler.version=15 '
" -s arch=x86_64 -s build_type=Release -s:b os=Windows -s:h os=Windows")
client.run("build .")
assert "Invoking 64bit dep_1 build tool" in client.out
Empty file removed origin)
Empty file.