Skip to content

Commit

Permalink
Added the ability to output a binary log from MSBuild. (conan-io#3626)
Browse files Browse the repository at this point in the history
* Added the ability to output a binary log from MSBuild.

* Added complex parameter for output_binary_log.

* Added complex parameter for output_binary_log.

* fixes from review

* Moved tests, new get_version() with more tests

* fix py27 test

* build test

* review

* Remove quotes, make it simpler

* revert and fix VS detect

* try fix
  • Loading branch information
luckielordie authored and danimtb committed Nov 29, 2018
1 parent 0ea90d5 commit e58cd55
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 4 deletions.
33 changes: 29 additions & 4 deletions conans/client/build/msbuild.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import copy
import re
import subprocess

from conans.client.build.visual_environment import (VisualStudioBuildEnvironment,
vs_build_type_flags, vs_std_cpp)
from conans.client.tools.oss import cpu_count
from conans.client.tools.win import vcvars_command
from conans.errors import ConanException
from conans.model.version import Version
from conans.util.env_reader import get_env
from conans.util.files import tmp_file
from conans.util.files import tmp_file, decode_text
from conans.model.conan_file import ConanFile
from conans.client import tools

Expand All @@ -27,7 +29,7 @@ def __init__(self, conanfile):

def build(self, project_file, targets=None, upgrade_project=True, build_type=None, arch=None,
parallel=True, force_vcvars=False, toolset=None, platforms=None, use_env=True,
vcvars_ver=None, winsdk_version=None, properties=None):
vcvars_ver=None, winsdk_version=None, properties=None, output_binary_log=None):

self.build_env.parallel = parallel

Expand All @@ -41,13 +43,13 @@ def build(self, project_file, targets=None, upgrade_project=True, build_type=Non
targets=targets, upgrade_project=upgrade_project,
build_type=build_type, arch=arch, parallel=parallel,
toolset=toolset, platforms=platforms,
use_env=use_env, properties=properties)
use_env=use_env, properties=properties, output_binary_log=output_binary_log)
command = "%s && %s" % (vcvars, command)
return self._conanfile.run(command)

def get_command(self, project_file, props_file_path=None, targets=None, upgrade_project=True,
build_type=None, arch=None, parallel=True, toolset=None, platforms=None,
use_env=False, properties=None):
use_env=False, properties=None, output_binary_log=None):

targets = targets or []
properties = properties or {}
Expand Down Expand Up @@ -88,6 +90,15 @@ def get_command(self, project_file, props_file_path=None, targets=None, upgrade_
self._output.warn("***** The configuration %s does not exist in this solution *****" % config)
self._output.warn("Use 'platforms' argument to define your architectures")

if output_binary_log:
msbuild_version = MSBuild.get_version(self._settings)
if msbuild_version >= "15.3": # http://msbuildlog.com/
command.append('/bl' if isinstance(output_binary_log, bool)
else '/bl:"%s"' % output_binary_log)
else:
raise ConanException("MSBuild version detected (%s) does not support "
"'output_binary_log' ('/bl')" % msbuild_version)

if use_env:
command.append('/p:UseEnv=true')

Expand Down Expand Up @@ -145,3 +156,17 @@ def _get_props_file_contents(self):
</Project>""".format(**{"runtime_node": runtime_node,
"additional_node": additional_node})
return template

@staticmethod
def get_version(settings):
msbuild_cmd = "msbuild -version"
vcvars = vcvars_command(settings)
command = "%s && %s" % (vcvars, msbuild_cmd)
try:
out, err = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()
version_line = decode_text(out).split("\n")[-1]
prog = re.compile("(\d+\.){2,3}\d+")
result = prog.match(version_line).group()
return Version(result)
except Exception as e:
raise ConanException("Error retrieving MSBuild version: '{}'".format(e))
116 changes: 116 additions & 0 deletions conans/test/build_helpers/msbuild_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import os
import platform
import unittest

import mock
from nose.plugins.attrib import attr
from parameterized import parameterized

from conans.client.build.msbuild import MSBuild
from conans.errors import ConanException
from conans.model.version import Version
from conans.paths import CONANFILE
from conans.test.utils.conanfile import MockSettings, MockConanfile
from conans.test.utils.tools import TestClient
Expand Down Expand Up @@ -151,3 +156,114 @@ def build(self):
client.save(files)
client.run("create . danimtb/testing")
self.assertIn("build() completed", client.out)

@unittest.skipUnless(platform.system() == "Windows", "Requires MSBuild")
def binary_logging_on_test(self):
settings = MockSettings({"build_type": "Debug",
"compiler": "Visual Studio",
"compiler.version": "15",
"arch": "x86_64",
"compiler.runtime": "MDd"})
conanfile = MockConanfile(settings)
msbuild = MSBuild(conanfile)
command = msbuild.get_command("dummy.sln", output_binary_log=True)
self.assertIn("/bl", command)

@unittest.skipUnless(platform.system() == "Windows", "Requires MSBuild")
def binary_logging_on_with_filename_test(self):
bl_filename = "a_special_log.log"
settings = MockSettings({"build_type": "Debug",
"compiler": "Visual Studio",
"compiler.version": "15",
"arch": "x86_64",
"compiler.runtime": "MDd"})
conanfile = MockConanfile(settings)
msbuild = MSBuild(conanfile)
command = msbuild.get_command("dummy.sln", output_binary_log=bl_filename)
expected_command = '/bl:"%s"' % bl_filename
self.assertIn(expected_command, command)

def binary_logging_off_explicit_test(self):
settings = MockSettings({"build_type": "Debug",
"compiler": "Visual Studio",
"compiler.version": "15",
"arch": "x86_64",
"compiler.runtime": "MDd"})
conanfile = MockConanfile(settings)
msbuild = MSBuild(conanfile)
command = msbuild.get_command("dummy.sln", output_binary_log=False)
self.assertNotIn("/bl", command)

def binary_logging_off_implicit_test(self):
settings = MockSettings({"build_type": "Debug",
"compiler": "Visual Studio",
"compiler.version": "15",
"arch": "x86_64",
"compiler.runtime": "MDd"})
conanfile = MockConanfile(settings)
msbuild = MSBuild(conanfile)
command = msbuild.get_command("dummy.sln")
self.assertNotIn("/bl", command)

@unittest.skipUnless(platform.system() == "Windows", "Requires MSBuild")
@mock.patch("conans.client.build.msbuild.MSBuild.get_version")
def binary_logging_not_supported_test(self, mock_get_version):
mock_get_version.return_value = Version("14")

mocked_settings = MockSettings({"build_type": "Debug",
"compiler": "Visual Studio",
"compiler.version": "15",
"arch": "x86_64",
"compiler.runtime": "MDd"})
conanfile = MockConanfile(mocked_settings)
except_text = "MSBuild version detected (14) does not support 'output_binary_log' ('/bl')"
msbuild = MSBuild(conanfile)

with self.assertRaises(ConanException) as exc:
msbuild.get_command("dummy.sln", output_binary_log=True)
self.assertIn(except_text, str(exc.exception))

@unittest.skipUnless(platform.system() == "Windows", "Requires MSBuild")
def get_version_test(self):
settings = MockSettings({"build_type": "Debug",
"compiler": "Visual Studio",
"compiler.version": "15",
"arch": "x86_64",
"compiler.runtime": "MDd"})
version = MSBuild.get_version(settings)
self.assertRegexpMatches(version, "(\d+\.){2,3}\d+")
self.assertGreater(version, "15.1")

@parameterized.expand([("True",), ("'my_log.binlog'",)])
@unittest.skipUnless(platform.system() == "Windows", "Requires MSBuild")
def binary_log_build_test(self, value):
conan_build_vs = """
from conans import ConanFile, MSBuild
class HelloConan(ConanFile):
name = "Hello"
version = "1.2.1"
exports = "*"
settings = "os", "build_type", "arch", "compiler"
def build(self):
msbuild = MSBuild(self)
msbuild.build("MyProject.sln", output_binary_log=%s)
"""
client = TestClient()
files = get_vs_project_files()
files[CONANFILE] = conan_build_vs % value
client.save(files)
client.run("install . -s compiler=\"Visual Studio\" -s compiler.version=15")
client.run("build .")

if value == "'my_log.binlog'":
log_name = value[1:1]
flag = "/bl:%s" % log_name
else:
log_name = "msbuild.binlog"
flag = "/bl"

self.assertIn(flag, client.out)
log_path = os.path.join(client.current_folder, log_name)
self.assertTrue(os.path.exists(log_path))

0 comments on commit e58cd55

Please sign in to comment.