-
Notifications
You must be signed in to change notification settings - Fork 949
/
toolchain.py
237 lines (210 loc) · 10.4 KB
/
toolchain.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import textwrap
from jinja2 import Template
from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.apple.apple import to_apple_arch, is_apple_os, apple_min_version_flag
from conan.tools.build.cross_building import cross_building, get_cross_building_settings
from conan.tools.env import VirtualBuildEnv
from conan.tools.meson.helpers import *
from conan.tools.microsoft import VCVars, msvc_runtime_flag
from conans.errors import ConanException
from conans.util.files import save
class MesonToolchain(object):
native_filename = "conan_meson_native.ini"
cross_filename = "conan_meson_cross.ini"
_meson_file_template = textwrap.dedent("""
[constants]
preprocessor_definitions = [{% for it, value in preprocessor_definitions.items() -%}
'-D{{ it }}="{{ value}}"'{%- if not loop.last %}, {% endif %}{% endfor %}]
[project options]
{% for it, value in project_options.items() -%}
{{it}} = {{value}}
{% endfor %}
[binaries]
{% if c %}c = '{{c}}'{% endif %}
{% if cpp %}cpp = '{{cpp}}'{% endif %}
{% if c_ld %}c_ld = '{{c_ld}}'{% endif %}
{% if cpp_ld %}cpp_ld = '{{cpp_ld}}'{% endif %}
{% if ar %}ar = '{{ar}}'{% endif %}
{% if strip %}strip = '{{strip}}'{% endif %}
{% if as %}as = '{{as}}'{% endif %}
{% if windres %}windres = '{{windres}}'{% endif %}
{% if pkgconfig %}pkgconfig = '{{pkgconfig}}'{% endif %}
[built-in options]
{% if buildtype %}buildtype = '{{buildtype}}'{% endif %}
{% if debug %}debug = {{debug}}{% endif %}
{% if default_library %}default_library = '{{default_library}}'{% endif %}
{% if b_vscrt %}b_vscrt = '{{b_vscrt}}' {% endif %}
{% if b_ndebug %}b_ndebug = {{b_ndebug}}{% endif %}
{% if b_staticpic %}b_staticpic = {{b_staticpic}}{% endif %}
{% if cpp_std %}cpp_std = '{{cpp_std}}' {% endif %}
{% if backend %}backend = '{{backend}}' {% endif %}
c_args = {{c_args}} + preprocessor_definitions
c_link_args = {{c_link_args}}
cpp_args = {{cpp_args}} + preprocessor_definitions
cpp_link_args = {{cpp_link_args}}
{% if pkg_config_path %}pkg_config_path = '{{pkg_config_path}}'{% endif %}
{% for context, values in cross_build.items() %}
[{{context}}_machine]
system = '{{values["system"]}}'
cpu_family = '{{values["cpu_family"]}}'
cpu = '{{values["cpu"]}}'
endian = '{{values["endian"]}}'
{% endfor %}
""")
def __init__(self, conanfile, backend=None):
self._conanfile = conanfile
# Values are kept as Python built-ins so users can modify them more easily, and they are
# only converted to Meson file syntax for rendering
# priority: first user conf, then recipe, last one is default "ninja"
self._backend = conanfile.conf.get("tools.meson.mesontoolchain:backend",
default=backend or 'ninja')
build_type = self._conanfile.settings.get_safe("build_type")
self._buildtype = {"Debug": "debug", # Note, it is not "'debug'"
"Release": "release",
"MinSizeRel": "minsize",
"RelWithDebInfo": "debugoptimized"}.get(build_type, build_type)
self._b_ndebug = "true" if self._buildtype != "debug" else "false"
# https://mesonbuild.com/Builtin-options.html#base-options
fpic = self._conanfile.options.get_safe("fPIC")
shared = self._conanfile.options.get_safe("shared")
self._b_staticpic = fpic if (shared is False and fpic is not None) else None
# https://mesonbuild.com/Builtin-options.html#core-options
# Do not adjust "debug" if already adjusted "buildtype"
self._default_library = ("shared" if shared else "static") if shared is not None else None
compiler = self._conanfile.settings.get_safe("compiler")
cppstd = self._conanfile.settings.get_safe("compiler.cppstd")
self._cpp_std = to_cppstd_flag(compiler, cppstd)
if compiler == "Visual Studio":
vscrt = self._conanfile.settings.get_safe("compiler.runtime")
self._b_vscrt = str(vscrt).lower()
elif compiler == "msvc":
vscrt = msvc_runtime_flag(self._conanfile)
self._b_vscrt = str(vscrt).lower()
else:
self._b_vscrt = None
self.project_options = {}
self.preprocessor_definitions = {}
self.pkg_config_path = self._conanfile.generators_folder
check_using_build_profile(self._conanfile)
self.cross_build = {}
default_comp = ""
default_comp_cpp = ""
if cross_building(conanfile, skip_x64_x86=True):
os_build, arch_build, os_host, arch_host = get_cross_building_settings(self._conanfile)
self.cross_build["build"] = to_meson_machine(os_build, arch_build)
self.cross_build["host"] = to_meson_machine(os_host, arch_host)
if hasattr(conanfile, 'settings_target') and conanfile.settings_target:
settings_target = conanfile.settings_target
os_target = settings_target.get_safe("os")
arch_target = settings_target.get_safe("arch")
self.cross_build["target"] = to_meson_machine(os_target, arch_target)
if is_apple_os(os_host): # default cross-compiler in Apple is common
default_comp = "clang"
default_comp_cpp = "clang++"
else:
if "Visual" in compiler or compiler == "msvc":
default_comp = "cl"
default_comp_cpp = "cl"
elif "clang" in compiler:
default_comp = "clang"
default_comp_cpp = "clang++"
elif compiler == "gcc":
default_comp = "gcc"
default_comp_cpp = "g++"
# Read the VirtualBuildEnv to update the variables
build_env = VirtualBuildEnv(self._conanfile).vars()
self.c = build_env.get("CC") or default_comp
self.cpp = build_env.get("CXX") or default_comp_cpp
self.c_ld = build_env.get("CC_LD") or build_env.get("LD")
self.cpp_ld = build_env.get("CXX_LD") or build_env.get("LD")
self.ar = build_env.get("AR")
self.strip = build_env.get("STRIP")
self.as_ = build_env.get("AS")
self.windres = build_env.get("WINDRES")
self.pkgconfig = build_env.get("PKG_CONFIG")
self.c_args = build_env.get("CFLAGS", "")
self.c_link_args = build_env.get("LDFLAGS", "")
self.cpp_args = build_env.get("CXXFLAGS", "")
self.cpp_link_args = build_env.get("LDFLAGS", "")
self._add_apple_flags()
def _add_apple_flags(self):
conanfile = self._conanfile
os_ = conanfile.settings.get_safe("os")
if not is_apple_os(os_):
return
# SDK path is mandatory for cross-building
sdk_path = conanfile.conf.get("tools.apple:sdk_path")
if not sdk_path and self.cross_build:
raise ConanException("You must provide a valid SDK path for cross-compilation.")
# TODO: Delete this os_sdk check whenever the _guess_apple_sdk_name() function disappears
os_sdk = conanfile.settings.get_safe('os.sdk')
if not os_sdk and os_ != "Macos":
raise ConanException("Please, specify a suitable value for os.sdk.")
arch = to_apple_arch(conanfile.settings.get_safe("arch"))
# Calculating the main Apple flags
deployment_target_flag = apple_min_version_flag(conanfile)
sysroot_flag = "-isysroot " + sdk_path if sdk_path else ""
arch_flag = "-arch " + arch if arch else ""
apple_flags = {}
if deployment_target_flag:
flag_ = deployment_target_flag.split("=")[0]
apple_flags[flag_] = deployment_target_flag
if sysroot_flag:
apple_flags["-isysroot"] = sysroot_flag
if arch_flag:
apple_flags["-arch"] = arch_flag
for flag, arg_value in apple_flags.items():
v = " " + arg_value
if flag not in self.c_args:
self.c_args += v
if flag not in self.c_link_args:
self.c_link_args += v
if flag not in self.cpp_args:
self.cpp_args += v
if flag not in self.cpp_link_args:
self.cpp_link_args += v
def _context(self):
return {
# https://mesonbuild.com/Machine-files.html#project-specific-options
"project_options": {k: to_meson_value(v) for k, v in self.project_options.items()},
# https://mesonbuild.com/Builtin-options.html#directories
# TODO : we don't manage paths like libdir here (yet?)
# https://mesonbuild.com/Machine-files.html#binaries
# https://mesonbuild.com/Reference-tables.html#compiler-and-linker-selection-variables
"c": self.c,
"cpp": self.cpp,
"c_ld": self.c_ld,
"cpp_ld": self.cpp_ld,
"ar": self.ar,
"strip": self.strip,
"as": self.as_,
"windres": self.windres,
"pkgconfig": self.pkgconfig,
# https://mesonbuild.com/Builtin-options.html#core-options
"buildtype": self._buildtype,
"default_library": self._default_library,
"backend": self._backend,
# https://mesonbuild.com/Builtin-options.html#base-options
"b_vscrt": self._b_vscrt,
"b_staticpic": to_meson_value(self._b_staticpic), # boolean
"b_ndebug": to_meson_value(self._b_ndebug), # boolean as string
# https://mesonbuild.com/Builtin-options.html#compiler-options
"cpp_std": self._cpp_std,
"c_args": to_meson_value(self.c_args.strip().split()),
"c_link_args": to_meson_value(self.c_link_args.strip().split()),
"cpp_args": to_meson_value(self.cpp_args.strip().split()),
"cpp_link_args": to_meson_value(self.cpp_link_args.strip().split()),
"pkg_config_path": self.pkg_config_path,
"preprocessor_definitions": self.preprocessor_definitions,
"cross_build": self.cross_build
}
@property
def content(self):
context = self._context()
content = Template(self._meson_file_template).render(context)
return content
def generate(self):
filename = self.native_filename if not self.cross_build else self.cross_filename
save(filename, self.content)
# FIXME: Should we check the OS and compiler to call VCVars?
VCVars(self._conanfile).generate()