forked from conan-io/conan
/
cmake_toolchain_build_helper.py
151 lines (119 loc) · 6.17 KB
/
cmake_toolchain_build_helper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import os
import platform
from conans.client import tools
from conans.client.build import defs_to_string, join_arguments
from conans.client.build.cmake_flags import is_multi_configuration, get_generator
from conans.client.toolchain.cmake import CMakeToolchain
from conans.client.tools.files import chdir
from conans.client.tools.oss import cpu_count, args_to_string
from conans.errors import ConanException
from conans.model.version import Version
from conans.util.conan_v2_mode import conan_v2_behavior
from conans.util.files import mkdir
def _validate_recipe(conanfile):
forbidden_generators = ["cmake", "cmake_multi", "cmake_paths"]
if any(it in conanfile.generators for it in forbidden_generators):
raise ConanException("Usage of toolchain is only supported with 'cmake_find_package'"
" or 'cmake_find_package_multi' generators")
def _compute_build_flags(conanfile, generator, parallel, msbuild_verbosity):
args = []
compiler_version = conanfile.settings.get_safe("compiler.version")
if generator and parallel:
if ("Makefiles" in generator or "Ninja" in generator) and "NMake" not in generator:
args.append("-j%i" % cpu_count(conanfile.output))
elif "Visual Studio" in generator and compiler_version and Version(compiler_version) >= "10":
# Parallel for building projects in the solution
args.append("/m:%i" % cpu_count(output=conanfile.output))
if generator and msbuild_verbosity:
if "Visual Studio" in generator and compiler_version and Version(compiler_version) >= "10":
args.append("/verbosity:%s" % msbuild_verbosity)
return args
class CMakeToolchainBuildHelper(object):
""" CMake helper to use together with the toolchain feature. It implements a very simple
wrapper to call the cmake executable, but without passing compile flags, preprocessor
definitions... all that is set by the toolchain. Only the generator and the CMAKE_TOOLCHAIN_FILE
are passed to the command line, plus the ``--config Release`` for builds in multi-config
"""
def __init__(self, conanfile, generator=None, build_folder=None, parallel=True,
msbuild_verbosity="minimal"):
_validate_recipe(conanfile)
# assert generator is None, "'generator' is handled by the toolchain"
self._generator = generator or get_generator(conanfile)
self._is_multiconfiguration = is_multi_configuration(self._generator)
# Store a reference to useful data
self._conanfile = conanfile
self._parallel = parallel
self._msbuild_verbosity = msbuild_verbosity
self._build_folder = build_folder
self._cmake_program = "cmake" # Path to CMake should be handled by environment
def configure(self, source_folder=None):
# TODO: environment?
if not self._conanfile.should_configure:
return
source = self._conanfile.source_folder
if source_folder:
source = os.path.join(self._conanfile.source_folder, source_folder)
build_folder = self._conanfile.build_folder
if self._build_folder:
build_folder = os.path.join(self._conanfile.build_folder, self._build_folder)
defs = {"CMAKE_TOOLCHAIN_FILE": CMakeToolchain.filename}
mkdir(build_folder)
arg_list = join_arguments([
defs_to_string(defs),
args_to_string([source])
])
generator = '-G "{}" '.format(self._generator) if self._generator else ""
command = "%s %s%s" % (self._cmake_program, generator, arg_list)
is_windows_mingw = platform.system() == "Windows" and self._generator == "MinGW Makefiles"
self._conanfile.output.info("CMake command: %s" % command)
with chdir(build_folder):
if is_windows_mingw:
with tools.remove_from_path("sh"):
self._conanfile.run(command)
else:
self._conanfile.run(command)
def _build(self, build_type=None, target=None):
bf = self._conanfile.build_folder
if self._build_folder:
bf = os.path.join(self._conanfile.build_folder, self._build_folder)
if build_type and not self._is_multiconfiguration:
self._conanfile.output.error("Don't specify 'build_type' at build time for "
"single-config build systems")
bt = build_type or self._conanfile.settings.get_safe("build_type")
if not bt:
conan_v2_behavior("build_type setting should be defined.",
v1_behavior=self._conanfile.output.warn)
if bt and self._is_multiconfiguration:
build_config = "--config %s" % bt
else:
build_config = ""
args = []
if target is not None:
args = ["--target", target]
build_flags = _compute_build_flags(self._conanfile, self._generator, self._parallel,
self._msbuild_verbosity)
if build_flags:
args += ['--'] + build_flags
arg_list = [args_to_string([bf]), build_config, args_to_string(args)]
command = "%s --build %s" % (self._cmake_program, join_arguments(arg_list))
self._conanfile.output.info("CMake command: %s" % command)
self._conanfile.run(command)
def build(self, build_type=None, target=None):
if not self._conanfile.should_build:
return
self._build(build_type, target)
def install(self, build_type=None):
if not self._conanfile.should_install:
return
mkdir(self._conanfile.package_folder)
self._build(build_type=build_type, target="install")
def test(self, build_type=None, target=None, output_on_failure=False):
if not self._conanfile.should_test:
return
if not target:
target = "RUN_TESTS" if self._is_multiconfiguration else "test"
env = {'CTEST_OUTPUT_ON_FAILURE': '1' if output_on_failure else '0'}
if self._parallel:
env['CTEST_PARALLEL_LEVEL'] = str(cpu_count(self._conanfile.output))
with tools.environment_append(env):
self._build(build_type=build_type, target=target)