Skip to content

Commit

Permalink
definition of custom VS version for msvc compiler in MSBuild (#8603)
Browse files Browse the repository at this point in the history
* definition of custom VS version for msvc compiler in MSBuild

* msbuildtoolchain now needs Windows

* fix test
  • Loading branch information
memsharded committed Mar 10, 2021
1 parent ffd2f3a commit 0fd3e50
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 59 deletions.
21 changes: 4 additions & 17 deletions conan/tools/microsoft/__init__.py
@@ -1,17 +1,4 @@
from .toolchain import MSBuildToolchain
from .msbuild import MSBuild
from .msbuilddeps import MSBuildDeps


def msvc_runtime_flag(conanfile):
settings = conanfile.settings
compiler = settings.get_safe("compiler")
runtime = settings.get_safe("compiler.runtime")
if compiler == "Visual Studio":
return runtime
if compiler == "msvc":
runtime_type = settings.get_safe("compiler.runtime_type")
runtime = "MT" if runtime == "static" else "MD"
if runtime_type == "Debug":
runtime = "{}d".format(runtime)
return runtime
from conan.tools.microsoft.toolchain import MSBuildToolchain
from conan.tools.microsoft.msbuild import MSBuild
from conan.tools.microsoft.msbuilddeps import MSBuildDeps
from conan.tools.microsoft.visual import msvc_runtime_flag
24 changes: 3 additions & 21 deletions conan/tools/microsoft/msbuild.py
@@ -1,5 +1,3 @@
from conan.tools.microsoft.visual import vcvars_arch, vcvars_command
from conans.client.tools import intel_compilervars_command
from conans.errors import ConanException


Expand All @@ -14,17 +12,6 @@ def msbuild_verbosity_cmd_line_arg(conanfile):
class MSBuild(object):
def __init__(self, conanfile):
self._conanfile = conanfile
self.compiler = conanfile.settings.get_safe("compiler")
# This is assuming this is the Visual Studio IDE version, used for the vcvars
self.version = (conanfile.settings.get_safe("compiler.base.version") or
conanfile.settings.get_safe("compiler.version"))
if self.compiler == "msvc":
version = self.version[:4] # Remove the latest version number 19.1X if existing
_visuals = {'19.0': '14', # TODO: This is common to CMake, refactor
'19.1': '15',
'19.2': '16'}
self.version = _visuals[version]
self.vcvars_arch = vcvars_arch(conanfile)
self.build_type = conanfile.settings.get_safe("build_type")
msvc_arch = {'x86': 'x86',
'x86_64': 'x64',
Expand All @@ -39,14 +26,9 @@ def __init__(self, conanfile):
self.platform = msvc_arch

def command(self, sln):
if self.compiler == "intel":
cvars = intel_compilervars_command(self._conanfile)
else:
cvars = vcvars_command(self.version, architecture=self.vcvars_arch,
platform_type=None, winsdk_version=None,
vcvars_ver=None)
cmd = ('%s && msbuild "%s" /p:Configuration=%s /p:Platform=%s'
% (cvars, sln, self.build_type, self.platform))
install_folder = self._conanfile.install_folder
cmd = ('%s/conanvcvars.bat && msbuild "%s" /p:Configuration=%s /p:Platform=%s'
% (install_folder, sln, self.build_type, self.platform))

verbosity = msbuild_verbosity_cmd_line_arg(self._conanfile)
if verbosity:
Expand Down
5 changes: 4 additions & 1 deletion conan/tools/microsoft/msbuilddeps.py
Expand Up @@ -2,10 +2,12 @@
import textwrap
from xml.dom import minidom

from conans.client.tools import VALID_LIB_EXTENSIONS

from conans.errors import ConanException
from conans.util.files import load, save

VALID_LIB_EXTENSIONS = (".so", ".lib", ".a", ".dylib", ".bc")


class MSBuildDeps(object):

Expand Down Expand Up @@ -213,6 +215,7 @@ def _pkg_props(self, name_multi, dep_name, vars_props_name, condition, cpp_info)
return content_multi

def _content(self):
# We cannot use self._conanfile.warn(), because that fails for virtual conanfile
print("*** The 'msbuild' generator is EXPERIMENTAL ***")
if not self._conanfile.settings.get_safe("build_type"):
raise ConanException("The 'msbuild' generator requires a 'build_type' setting value")
Expand Down
35 changes: 35 additions & 0 deletions conan/tools/microsoft/toolchain.py
Expand Up @@ -2,6 +2,8 @@
import textwrap
from xml.dom import minidom

from conan.tools.microsoft.visual import vcvars_command, vcvars_arch
from conans.client.tools import intel_compilervars_command
from conans.errors import ConanException
from conans.util.files import save, load

Expand All @@ -19,6 +21,25 @@ def __init__(self, conanfile):
self.cppstd = conanfile.settings.get_safe("compiler.cppstd")
self.toolset = self._msvs_toolset(conanfile.settings)

# For VCVARS stuff
self.compiler = conanfile.settings.get_safe("compiler")
# This is assuming this is the Visual Studio IDE version, used for the vcvars
compiler_version = (conanfile.settings.get_safe("compiler.base.version") or
conanfile.settings.get_safe("compiler.version"))
self.vcvars_arch = vcvars_arch(conanfile)
if self.compiler == "msvc":
toolset_override = self._conanfile.conf["tools.microsoft.msbuild"].vs_version
if toolset_override:
self.visual_version = toolset_override
else:
version = compiler_version[:4] # Remove the latest version number 19.1X if existing
_visuals = {'19.0': '14', # TODO: This is common to CMake, refactor
'19.1': '15',
'19.2': '16'}
self.visual_version = _visuals[version]
else:
self.visual_version = compiler_version

def _name_condition(self, settings):
props = [("Configuration", self.configuration),
# FIXME: This probably requires mapping ARM architectures
Expand All @@ -34,6 +55,20 @@ def generate(self):
config_filename = "conantoolchain{}.props".format(name)
self._write_config_toolchain(config_filename)
self._write_main_toolchain(config_filename, condition)
self._write_vcvars()

def _write_vcvars(self):
if self.compiler == "intel":
cvars = intel_compilervars_command(self._conanfile)
else:
cvars = vcvars_command(self.visual_version, architecture=self.vcvars_arch,
platform_type=None, winsdk_version=None,
vcvars_ver=None)
content = textwrap.dedent("""\
@echo off
{}
""".format(cvars))
save("conanvcvars.bat", content)

@staticmethod
def _msvs_toolset(settings):
Expand Down
14 changes: 14 additions & 0 deletions conan/tools/microsoft/visual.py
Expand Up @@ -4,6 +4,20 @@
from conans.errors import ConanException


def msvc_runtime_flag(conanfile):
settings = conanfile.settings
compiler = settings.get_safe("compiler")
runtime = settings.get_safe("compiler.runtime")
if compiler == "Visual Studio":
return runtime
if compiler == "msvc":
runtime_type = settings.get_safe("compiler.runtime_type")
runtime = "MT" if runtime == "static" else "MD"
if runtime_type == "Debug":
runtime = "{}d".format(runtime)
return runtime


def vcvars_command(version, architecture=None, platform_type=None, winsdk_version=None,
vcvars_ver=None, start_dir_cd=True):
""" conan-agnostic construction of vcvars command
Expand Down
26 changes: 21 additions & 5 deletions conans/test/functional/toolchains/test_basic.py
@@ -1,4 +1,5 @@
import os
import platform
import textwrap
import unittest

Expand Down Expand Up @@ -34,8 +35,7 @@ def test_declarative(self):
from conans import ConanFile
class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
generators = ("CMakeToolchain", "CMakeDeps", "MesonToolchain", "MakeToolchain",
"MSBuildToolchain")
generators = ("CMakeToolchain", "CMakeDeps", "MesonToolchain", "MakeToolchain")
""")
client = TestClient()
client.save({"conanfile.py": conanfile})
Expand All @@ -44,17 +44,31 @@ class Pkg(ConanFile):
self.assertIn("conanfile.py: Generator 'CMakeToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.py: Generator 'MesonToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.py: Generator 'MakeToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.py: Generator 'MSBuildToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.py: Generator 'CMakeDeps' calling 'generate()'", client.out)
toolchain = client.load("conan_toolchain.cmake")
self.assertIn("Conan automatically generated toolchain file", toolchain)
toolchain = client.load("conantoolchain.props")
self.assertIn("<?xml version", toolchain)
toolchain = client.load("conan_toolchain.mak")
self.assertIn("# Conan generated toolchain file", toolchain)
toolchain = client.load("conan_meson_native.ini")
self.assertIn("[project options]", toolchain)

@pytest.mark.tool_visual_studio
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_declarative_msbuildtoolchain(self):
conanfile = textwrap.dedent("""
from conans import ConanFile
class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
generators = ("MSBuildToolchain", )
""")
client = TestClient()
client.save({"conanfile.py": conanfile})
client.run("install .")

self.assertIn("conanfile.py: Generator 'MSBuildToolchain' calling 'generate()'", client.out)
toolchain = client.load("conantoolchain.props")
self.assertIn("<?xml version", toolchain)

def test_error_missing_settings(self):
conanfile = textwrap.dedent("""
from conans import ConanFile
Expand Down Expand Up @@ -121,6 +135,8 @@ def toolchain(self):
client.run("install .", assert_error=True)
self.assertIn("The 'toolchain' attribute or method has been deprecated", client.out)

@pytest.mark.tool_visual_studio
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_toolchain_windows(self):
client = TestClient()
conanfile = textwrap.dedent("""
Expand Down
27 changes: 19 additions & 8 deletions conans/test/functional/toolchains/test_msbuild.py
Expand Up @@ -393,19 +393,28 @@ def _run_app(client, arch, build_type, shared=None):
shutil.copy(command_str, new_cmd)
client.run_command(new_cmd)

@pytest.mark.tool_cmake
@parameterized.expand([("Visual Studio", "15", "MT"),
("msvc", "19.1", "static")]
@parameterized.expand([("Visual Studio", "15", "MT", "17"),
("msvc", "19.1", "static", "17"),
("msvc", "19.0", "static", "14")]
)
def test_toolchain_win(self, compiler, version, runtime):
@pytest.mark.tool_cmake
def test_toolchain_win(self, compiler, version, runtime, cppstd):
client = TestClient(path_with_spaces=False)
settings = {"compiler": compiler,
"compiler.version": version,
"compiler.cppstd": "17",
"compiler.cppstd": cppstd,
"compiler.runtime": runtime,
"build_type": "Release",
"arch": "x86"}

profile = textwrap.dedent("""
[settings]
os=Windows
[conf]
tools.microsoft.msbuild:vs_version=15
""")
client.save({"myprofile": profile})
# Build the profile according to the settings provided
settings = " ".join('-s %s="%s"' % (k, v) for k, v in settings.items() if v)

Expand All @@ -416,11 +425,12 @@ def test_toolchain_win(self, compiler, version, runtime):
client.save({"conanfile.py": self.conanfile,
"MyProject.sln": sln_file,
"MyApp/MyApp.vcxproj": myapp_vcxproj,
"MyApp/MyApp.cpp": self.app},
"MyApp/MyApp.cpp": self.app,
"myprofile": profile},
clean_first=True)

# Run the configure corresponding to this test case
client.run("install . %s -if=conan" % (settings, ))
client.run("install . %s -if=conan -pr=myprofile" % (settings, ))
self.assertIn("conanfile.py: MSBuildToolchain created conantoolchain_release_win32.props",
client.out)
client.run("build . -if=conan")
Expand All @@ -429,7 +439,8 @@ def test_toolchain_win(self, compiler, version, runtime):

self._run_app(client, "x86", "Release")
self.assertIn("Hello World Release", client.out)
check_exe_run(client.out, "main", "msvc", "19.1", "Release", "x86", "17",
compiler_version = version if compiler == "msvc" else "19.1"
check_exe_run(client.out, "main", "msvc", compiler_version, "Release", "x86", cppstd,
{"DEFINITIONS_BOTH": "True",
"DEFINITIONS_CONFIG": "Release"})
check_vs_runtime("Release/MyApp.exe", client, "15", static=True, build_type="Release")
Expand Down
33 changes: 28 additions & 5 deletions conans/test/functional/toolchains/test_txt_cmdline.py
@@ -1,3 +1,4 @@
import platform
import textwrap
import unittest

Expand All @@ -15,7 +16,6 @@ def test_declarative(self):
CMakeToolchain
MesonToolchain
MakeToolchain
MSBuildToolchain
""")
client = TestClient()
client.save({"conanfile.txt": conanfile})
Expand All @@ -26,11 +26,8 @@ def _check(self, client):
self.assertIn("conanfile.txt: Generator 'CMakeToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.txt: Generator 'MesonToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.txt: Generator 'MakeToolchain' calling 'generate()'", client.out)
self.assertIn("conanfile.txt: Generator 'MSBuildToolchain' calling 'generate()'", client.out)
toolchain = client.load("conan_toolchain.cmake")
self.assertIn("Conan automatically generated toolchain file", toolchain)
toolchain = client.load("conantoolchain.props")
self.assertIn("<?xml version", toolchain)
toolchain = client.load("conan_toolchain.mak")
self.assertIn("# Conan generated toolchain file", toolchain)
toolchain = client.load("conan_meson_native.ini")
Expand All @@ -40,5 +37,31 @@ def test_command_line(self):
client = TestClient()
client.save({"conanfile.txt": ""})
client.run("install . -g CMakeToolchain -g MesonToolchain "
"-g MakeToolchain -g MSBuildToolchain")
"-g MakeToolchain")
self._check(client)


@pytest.mark.tool_visual_studio
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
class TestTxtCommandLineMSBuild(unittest.TestCase):

def test_declarative(self):
conanfile = textwrap.dedent("""
[generators]
MSBuildToolchain
""")
client = TestClient()
client.save({"conanfile.txt": conanfile})
client.run("install .")
self._check(client)

def _check(self, client):
self.assertIn("conanfile.txt: Generator 'MSBuildToolchain' calling 'generate()'", client.out)
toolchain = client.load("conantoolchain.props")
self.assertIn("<?xml version", toolchain)

def test_command_line(self):
client = TestClient()
client.save({"conanfile.txt": ""})
client.run("install . -g MSBuildToolchain")
self._check(client)
Expand Up @@ -106,8 +106,6 @@ def test_config_profile_forbidden(client):
"'cache:verbosity=Minimal' not allowed in profiles" in client.out)


@pytest.mark.tool_visual_studio
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_msbuild_config():
client = TestClient()
conanfile = textwrap.dedent("""
Expand Down Expand Up @@ -137,6 +135,8 @@ def build(self):
assert "/verbosity:Minimal" in client.out


@pytest.mark.tool_visual_studio
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_msbuild_compile_options():
client = TestClient()
conanfile = textwrap.dedent("""
Expand Down
4 changes: 4 additions & 0 deletions conans/test/integration/toolchain/test_msbuild_toolchain.py
@@ -1,5 +1,7 @@
import platform
import textwrap

import pytest
from parameterized import parameterized

from conans.test.utils.tools import TestClient
Expand All @@ -8,6 +10,8 @@
@parameterized.expand([("msvc", "19.0", "dynamic"),
("msvc", "19.1", "static")]
)
@pytest.mark.tool_visual_studio
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_toolchain_win(compiler, version, runtime):
client = TestClient(path_with_spaces=False)
settings = {"compiler": compiler,
Expand Down

0 comments on commit 0fd3e50

Please sign in to comment.