-
Notifications
You must be signed in to change notification settings - Fork 948
/
detect.py
300 lines (260 loc) · 11.2 KB
/
detect.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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
import os
import platform
import re
import tempfile
import textwrap
from conans.client.conf.compiler_id import UNKNOWN_COMPILER, LLVM_GCC, detect_compiler_id
from conans.client.output import Color
from conans.client.tools import detected_os, detected_architecture
from conans.client.tools.win import latest_visual_studio_version_installed
from conans.model.version import Version
from conans.util.conan_v2_mode import CONAN_V2_MODE_ENVVAR
from conans.util.env_reader import get_env
from conans.util.files import save
from conans.util.runners import detect_runner
def _get_compiler_and_version(output, compiler_exe):
compiler_id = detect_compiler_id(compiler_exe)
if compiler_id.name == LLVM_GCC:
output.error("%s detected as a frontend using apple-clang. "
"Compiler not supported" % compiler_exe)
return None
if compiler_id != UNKNOWN_COMPILER:
output.success("Found %s %s" % (compiler_id.name, compiler_id.major_minor))
return compiler_id.name, compiler_id.major_minor
return None
def _gcc_compiler(output, compiler_exe="gcc"):
try:
if platform.system() == "Darwin":
# In Mac OS X check if gcc is a fronted using apple-clang
_, out = detect_runner("%s --version" % compiler_exe)
out = out.lower()
if "clang" in out:
return None
ret, out = detect_runner('%s -dumpversion' % compiler_exe)
if ret != 0:
return None
compiler = "gcc"
installed_version = re.search(r"([0-9]+(\.[0-9])?)", out).group()
# Since GCC 7.1, -dumpversion return the major version number
# only ("7"). We must use -dumpfullversion to get the full version
# number ("7.1.1").
if installed_version:
output.success("Found %s %s" % (compiler, installed_version))
return compiler, installed_version
except Exception:
return None
def _clang_compiler(output, compiler_exe="clang"):
try:
ret, out = detect_runner('%s --version' % compiler_exe)
if ret != 0:
return None
if "Apple" in out:
compiler = "apple-clang"
elif "clang version" in out:
compiler = "clang"
installed_version = re.search(r"([0-9]+\.[0-9])", out).group()
if installed_version:
output.success("Found %s %s" % (compiler, installed_version))
return compiler, installed_version
except Exception:
return None
def _sun_cc_compiler(output, compiler_exe="cc"):
try:
_, out = detect_runner('%s -V' % compiler_exe)
compiler = "sun-cc"
installed_version = re.search(r"Sun C.*([0-9]+\.[0-9]+)", out)
if installed_version:
installed_version = installed_version.group(1)
else:
installed_version = re.search(r"([0-9]+\.[0-9]+)", out).group()
if installed_version:
output.success("Found %s %s" % (compiler, installed_version))
return compiler, installed_version
except Exception:
return None
def _get_default_compiler(output):
"""
find the default compiler on the build machine
search order and priority:
1. CC and CXX environment variables are always top priority
2. Visual Studio detection (Windows only) via vswhere or registry or environment variables
3. Apple Clang (Mac only)
4. cc executable
5. gcc executable
6. clang executable
"""
v2_mode = get_env(CONAN_V2_MODE_ENVVAR, False)
cc = os.environ.get("CC", "")
cxx = os.environ.get("CXX", "")
if cc or cxx: # Env defined, use them
output.info("CC and CXX: %s, %s " % (cc or "None", cxx or "None"))
command = cc or cxx
if v2_mode:
compiler = _get_compiler_and_version(output, command)
if compiler:
return compiler
else:
if "gcc" in command:
gcc = _gcc_compiler(output, command)
if platform.system() == "Darwin" and gcc is None:
output.error("%s detected as a frontend using apple-clang. "
"Compiler not supported" % command)
return gcc
if "clang" in command.lower():
return _clang_compiler(output, command)
if platform.system() == "SunOS" and command.lower() == "cc":
return _sun_cc_compiler(output, command)
# I am not able to find its version
output.error("Not able to automatically detect '%s' version" % command)
return None
vs = cc = sun_cc = None
if detected_os() == "Windows":
version = latest_visual_studio_version_installed(output)
vs = ('Visual Studio', version) if version else None
if v2_mode:
cc = _get_compiler_and_version(output, "cc")
gcc = _get_compiler_and_version(output, "gcc")
clang = _get_compiler_and_version(output, "clang")
else:
gcc = _gcc_compiler(output)
clang = _clang_compiler(output)
if platform.system() == "SunOS":
sun_cc = _sun_cc_compiler(output)
if detected_os() == "Windows":
return vs or cc or gcc or clang
elif platform.system() == "Darwin":
return clang or cc or gcc
elif platform.system() == "SunOS":
return sun_cc or cc or gcc or clang
else:
return cc or gcc or clang
def _get_profile_compiler_version(compiler, version, output):
tokens = version.split(".")
major = tokens[0]
minor = tokens[1] if len(tokens) > 1 else 0
if compiler == "clang" and int(major) >= 8:
output.info("clang>=8, using the major as version")
return major
elif compiler == "gcc" and int(major) >= 5:
output.info("gcc>=5, using the major as version")
return major
elif compiler == "Visual Studio":
return major
elif compiler == "intel" and (int(major) < 19 or (int(major) == 19 and int(minor) == 0)):
return major
return version
def _detect_gcc_libcxx(executable, version, output, profile_name, profile_path):
# Assumes a working g++ executable
new_abi_available = Version(version) >= Version("5.1")
if not new_abi_available:
return "libstdc++"
if not get_env(CONAN_V2_MODE_ENVVAR, False):
msg = textwrap.dedent("""
Conan detected a GCC version > 5 but has adjusted the 'compiler.libcxx' setting to
'libstdc++' for backwards compatibility.
Your compiler is likely using the new CXX11 ABI by default (libstdc++11).
If you want Conan to use the new ABI for the {profile} profile, run:
$ conan profile update settings.compiler.libcxx=libstdc++11 {profile}
Or edit '{profile_path}' and set compiler.libcxx=libstdc++11
""".format(profile=profile_name, profile_path=profile_path))
output.writeln("\n************************* WARNING: GCC OLD ABI COMPATIBILITY "
"***********************\n %s\n************************************"
"************************************************\n\n\n" % msg,
Color.BRIGHT_RED)
return "libstdc++"
main = textwrap.dedent("""
#include <string>
using namespace std;
static_assert(sizeof(std::string) != sizeof(void*), "using libstdc++");
int main(){}
""")
t = tempfile.mkdtemp()
filename = os.path.join(t, "main.cpp")
save(filename, main)
old_path = os.getcwd()
os.chdir(t)
try:
error, out_str = detect_runner("%s main.cpp -std=c++11" % executable)
if error:
if "using libstdc++" in out_str:
output.info("gcc C++ standard library: libstdc++")
return "libstdc++"
# Other error, but can't know, lets keep libstdc++11
output.warn("compiler.libcxx check error: %s" % out_str)
output.warn("Couldn't deduce compiler.libcxx for gcc>=5.1, assuming libstdc++11")
else:
output.info("gcc C++ standard library: libstdc++11")
return "libstdc++11"
finally:
os.chdir(old_path)
def _detect_compiler_version(result, output, profile_path):
try:
compiler, version = _get_default_compiler(output)
except Exception:
compiler, version = None, None
if not compiler or not version:
output.error("Unable to find a working compiler")
return
result.append(("compiler", compiler))
result.append(("compiler.version", _get_profile_compiler_version(compiler, version, output)))
# Get compiler C++ stdlib
if compiler == "apple-clang":
result.append(("compiler.libcxx", "libc++"))
elif compiler == "gcc":
profile_name = os.path.basename(profile_path)
libcxx = _detect_gcc_libcxx("g++", version, output, profile_name, profile_path)
result.append(("compiler.libcxx", libcxx))
elif compiler == "cc":
if platform.system() == "SunOS":
result.append(("compiler.libstdcxx", "libstdcxx4"))
elif compiler == "clang":
if platform.system() == "FreeBSD":
result.append(("compiler.libcxx", "libc++"))
else:
result.append(("compiler.libcxx", "libstdc++"))
elif compiler == "sun-cc":
result.append(("compiler.libcxx", "libCstd"))
elif compiler == "mcst-lcc":
result.append(("compiler.base", "gcc")) # do the same for Intel?
result.append(("compiler.base.libcxx", "libstdc++"))
version = Version(version)
if version >= "1.24":
result.append(("compiler.base.version", "7.3"))
elif version >= "1.23":
result.append(("compiler.base.version", "5.5"))
elif version >= "1.21":
result.append(("compiler.base.version", "4.8"))
else:
result.append(("compiler.base.version", "4.4"))
def _detect_os_arch(result, output):
from conans.client.conf import get_default_settings_yml
from conans.model.settings import Settings
the_os = detected_os()
result.append(("os", the_os))
result.append(("os_build", the_os))
arch = detected_architecture()
if arch:
if arch.startswith('arm'):
settings = Settings.loads(get_default_settings_yml())
defined_architectures = settings.arch.values_range
defined_arm_architectures = [v for v in defined_architectures if v.startswith("arm")]
for a in defined_arm_architectures:
if arch.startswith(a):
arch = a
break
else:
output.error("Your ARM '%s' architecture is probably not defined in settings.yml\n"
"Please check your conan.conf and settings.yml files" % arch)
result.append(("arch", arch))
result.append(("arch_build", arch))
def detect_defaults_settings(output, profile_path):
""" try to deduce current machine values without any constraints at all
:param output: Conan Output instance
:param profile_path: Conan profile file path
:return: A list with default settings
"""
result = []
_detect_os_arch(result, output)
_detect_compiler_version(result, output, profile_path)
result.append(("build_type", "Release"))
return result