forked from conan-io/conan
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconf.py
556 lines (474 loc) · 23.7 KB
/
conf.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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
import fnmatch
import os
from collections import OrderedDict
import six
from conans.errors import ConanException
BUILT_IN_CONFS = {
"core:required_conan_version": "Raise if current version does not match the defined range",
"core.package_id:msvc_visual_incompatible": "Allows opting-out the fallback from the new msvc compiler to the Visual Studio compiler existing binaries",
"core:default_profile": "Defines the default host profile ('default' by default)",
"core:default_build_profile": "Defines the default build profile (None by default)",
"tools.android:ndk_path": "Argument for the CMAKE_ANDROID_NDK",
"tools.build:skip_test": "Do not execute CMake.test() and Meson.test() when enabled",
"tools.build:jobs": "Default compile jobs number -jX Ninja, Make, /MP VS (default: max CPUs)",
"tools.build:sysroot": "Pass the --sysroot=<tools.build:sysroot> flag if available. (None by default)",
"tools.cmake.cmaketoolchain:generator": "User defined CMake generator to use instead of default",
"tools.cmake.cmaketoolchain:find_package_prefer_config": "Argument for the CMAKE_FIND_PACKAGE_PREFER_CONFIG",
"tools.cmake.cmaketoolchain:toolchain_file": "Use other existing file rather than conan_toolchain.cmake one",
"tools.cmake.cmaketoolchain:user_toolchain": "Inject existing user toolchains at the beginning of conan_toolchain.cmake",
"tools.cmake.cmaketoolchain:system_name": "Define CMAKE_SYSTEM_NAME in CMakeToolchain",
"tools.cmake.cmaketoolchain:system_version": "Define CMAKE_SYSTEM_VERSION in CMakeToolchain",
"tools.cmake.cmaketoolchain:system_processor": "Define CMAKE_SYSTEM_PROCESSOR in CMakeToolchain",
"tools.cmake.cmaketoolchain.presets:max_schema_version": "Generate CMakeUserPreset.json compatible with the supplied schema version",
"tools.env.virtualenv:auto_use": "Automatically activate virtualenv file generation",
"tools.cmake.cmake_layout:build_folder_vars": "Settings and Options that will produce a different build folder and different CMake presets names",
"tools.files.download:retry": "Number of retries in case of failure when downloading",
"tools.files.download:retry_wait": "Seconds to wait between download attempts",
"tools.gnu:make_program": "Indicate path to make program",
"tools.gnu:define_libcxx11_abi": "Force definition of GLIBCXX_USE_CXX11_ABI=1 for libstdc++11",
"tools.gnu:host_triplet": "Custom host triplet to pass to Autotools scripts",
"tools.google.bazel:configs": "Define Bazel config file",
"tools.google.bazel:bazelrc_path": "Defines Bazel rc-path",
"tools.microsoft.msbuild:verbosity": "Verbosity level for MSBuild: 'Quiet', 'Minimal', 'Normal', 'Detailed', 'Diagnostic'",
"tools.microsoft.msbuild:vs_version": "Defines the IDE version when using the new msvc compiler",
"tools.microsoft.msbuild:max_cpu_count": "Argument for the /m when running msvc to build parallel projects",
"tools.microsoft.msbuild:installation_path": "VS install path, to avoid auto-detect via vswhere, like C:/Program Files (x86)/Microsoft Visual Studio/2019/Community. Use empty string to disable",
"tools.microsoft.msbuilddeps:exclude_code_analysis": "Suppress MSBuild code analysis for patterns",
"tools.microsoft.msbuildtoolchain:compile_options": "Dictionary with MSBuild compiler options",
"tools.intel:installation_path": "Defines the Intel oneAPI installation root path",
"tools.intel:setvars_args": "Custom arguments to be passed onto the setvars.sh|bat script from Intel oneAPI",
"tools.system.package_manager:tool": "Default package manager tool: 'apt-get', 'yum', 'dnf', 'brew', 'pacman', 'choco', 'zypper', 'pkg' or 'pkgutil'",
"tools.system.package_manager:mode": "Mode for package_manager tools: 'check' or 'install'",
"tools.system.package_manager:sudo": "Use 'sudo' when invoking the package manager tools in Linux (False by default)",
"tools.system.package_manager:sudo_askpass": "Use the '-A' argument if using sudo in Linux to invoke the system package manager (False by default)",
"tools.apple.xcodebuild:verbosity": "Verbosity level for xcodebuild: 'verbose' or 'quiet",
"tools.apple:enable_bitcode": "(boolean) Enable/Disable Bitcode Apple Clang flags",
"tools.apple:enable_arc": "(boolean) Enable/Disable ARC Apple Clang flags",
"tools.apple:enable_visibility": "(boolean) Enable/Disable Visibility Apple Clang flags",
"tools.build:cxxflags": "List of extra CXX flags used by different toolchains like CMakeToolchain, AutotoolsToolchain and MesonToolchain",
"tools.build:cflags": "List of extra C flags used by different toolchains like CMakeToolchain, AutotoolsToolchain and MesonToolchain",
"tools.build:defines": "List of extra definition flags used by different toolchains like CMakeToolchain and AutotoolsToolchain",
"tools.build:sharedlinkflags": "List of extra flags used by CMakeToolchain for CMAKE_SHARED_LINKER_FLAGS_INIT variable",
"tools.build:exelinkflags": "List of extra flags used by CMakeToolchain for CMAKE_EXE_LINKER_FLAGS_INIT variable",
"tools.build:compiler_executables": "Defines a Python dict-like with the compilers path to be used. Allowed keys {'c', 'cpp', 'cuda', 'objc', 'objcpp', 'rc', 'fortran', 'asm', 'hip', 'ispc'}",
"tools.build:linker_scripts": "List of linker script files to pass to the linker used by different toolchains like CMakeToolchain, AutotoolsToolchain, and MesonToolchain",
"tools.microsoft.bash:subsystem": "Set subsystem to use for Windows. Possible values: 'msys2', 'msys', 'cygwin', 'wsl' and 'sfu'",
"tools.microsoft.bash:path": "Path to the shell executable. Default: 'bash'",
"tools.apple:sdk_path": "Path for the sdk location. This value will be passed as SDKROOT or -isysroot depending on the generator used",
"tools.cmake.cmaketoolchain:toolset_arch": "Will add the ',host=xxx' specifier in the 'CMAKE_GENERATOR_TOOLSET' variable of 'conan_toolchain.cmake' file",
"tools.gnu:pkg_config": "Define the 'pkg_config' executable name or full path",
"tools.env.virtualenv:powershell": "Opt-in to generate Powershell '.ps1' scripts instead of '.bat'",
"tools.meson.mesontoolchain:backend": "Set the Meson backend. Possible values: 'ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'vs2019', 'xcode'",
"tools.meson.mesontoolchain:extra_machine_files": "List of paths for any additional native/cross file references to be appended to the existing Conan ones",
"tools.files.download:download_cache": "Location for the download cache",
"tools.build.cross_building:can_run": "Set the return value for the 'conan.tools.build.can_run()' tool",
}
BUILT_IN_CONFS = {key: value for key, value in sorted(BUILT_IN_CONFS.items())}
def _is_profile_module(module_name):
# These are the modules that are propagated to profiles and user recipes
_user_modules = "tools.", "user."
return any(module_name.startswith(user_module) for user_module in _user_modules)
# FIXME: Refactor all the next classes because they are mostly the same as
# conan.tools.env.environment ones
class _ConfVarPlaceHolder:
pass
class _ConfValue(object):
def __init__(self, name, value, path=False):
self._name = name
self._value = value
self._value_type = type(value)
self._path = path
def __repr__(self):
return repr(self._value)
@property
def value(self):
if self._value_type is list and _ConfVarPlaceHolder in self._value:
v = self._value[:]
v.remove(_ConfVarPlaceHolder)
return v
return self._value
def copy(self):
return _ConfValue(self._name, self._value, self._path)
def dumps(self):
if self._value is None:
return "{}=!".format(self._name) # unset
elif self._value_type is list and _ConfVarPlaceHolder in self._value:
v = self._value[:]
v.remove(_ConfVarPlaceHolder)
return "{}={}".format(self._name, v)
else:
return "{}={}".format(self._name, self._value)
def update(self, value):
if self._value_type is dict:
self._value.update(value)
def remove(self, value):
if self._value_type is list:
self._value.remove(value)
elif self._value_type is dict:
self._value.pop(value, None)
def append(self, value):
if self._value_type is not list:
raise ConanException("Only list-like values can append other values.")
if isinstance(value, list):
self._value.extend(value)
else:
self._value.append(value)
def prepend(self, value):
if self._value_type is not list:
raise ConanException("Only list-like values can prepend other values.")
if isinstance(value, list):
self._value = value + self._value
else:
self._value.insert(0, value)
def compose_conf_value(self, other):
"""
self has precedence, the "other" will add/append if possible and not conflicting, but
self mandates what to do. If self has define(), without placeholder, that will remain.
:type other: _ConfValue
"""
v_type = self._value_type
o_type = other._value_type
if v_type is list and o_type is list:
try:
index = self._value.index(_ConfVarPlaceHolder)
except ValueError: # It doesn't have placeholder
pass
else:
new_value = self._value[:] # do a copy
new_value[index:index + 1] = other._value # replace the placeholder
self._value = new_value
elif self._value is None or other._value is None \
or (isinstance(self._value, six.string_types) and isinstance(self._value, six.string_types)): # TODO: Python2, remove in 2.0
# It means any of those values were an "unset" so doing nothing because we don't
# really know the original value type
pass
elif o_type != v_type:
raise ConanException("It's not possible to compose {} values "
"and {} ones.".format(v_type.__name__, o_type.__name__))
# TODO: In case of any other object types?
def set_relative_base_folder(self, folder):
if not self._path:
return
if isinstance(self._value, list):
self._value = [os.path.join(folder, v) if v != _ConfVarPlaceHolder else v
for v in self._value]
if isinstance(self._value, dict):
self._value = {k: os.path.join(folder, v) for k, v in self._value.items()}
elif isinstance(self._value, str):
self._value = os.path.join(folder, self._value)
class Conf:
# Putting some default expressions to check that any value could be false
boolean_false_expressions = ("0", '"0"', "false", '"false"', "off")
def __init__(self):
# It being ordered allows for Windows case-insensitive composition
self._values = OrderedDict() # {var_name: [] of values, including separators}
def __bool__(self):
return bool(self._values)
# TODO: Python2, remove in 2.0
__nonzero__ = __bool__
def __repr__(self):
return "Conf: " + repr(self._values)
def __eq__(self, other):
"""
:type other: Conf
"""
return other._values == self._values
# TODO: Python2, remove in 2.0
def __ne__(self, other):
return not self.__eq__(other)
def __getitem__(self, name):
"""
DEPRECATED: it's going to disappear in Conan 2.0. Use self.get() instead.
"""
# FIXME: Keeping backward compatibility
return self.get(name)
def __setitem__(self, name, value):
"""
DEPRECATED: it's going to disappear in Conan 2.0.
"""
# FIXME: Keeping backward compatibility
self.define(name, value) # it's like a new definition
def __delitem__(self, name):
"""
DEPRECATED: it's going to disappear in Conan 2.0.
"""
# FIXME: Keeping backward compatibility
del self._values[name]
def items(self):
# FIXME: Keeping backward compatibility
for k, v in self._values.items():
yield k, v.value
@property
def sha(self):
# FIXME: Keeping backward compatibility
return self.dumps()
@staticmethod
def _get_boolean_value(value):
if type(value) is bool:
return value
elif str(value).lower() in Conf.boolean_false_expressions:
return False
else:
return True
def get(self, conf_name, default=None, check_type=None):
"""
Get all the values belonging to the passed conf name.
:param conf_name: conf name
:param default: default value in case of conf does not have the conf_name key
:param check_type: check the conf type(value) is the same as the given by this param.
There are two default smart conversions for bool and str types.
"""
conf_value = self._values.get(conf_name)
if conf_value:
v = conf_value.value
# Some smart conversions
if check_type is bool and not isinstance(v, bool):
# Perhaps, user has introduced a "false", "0" or even "off"
return self._get_boolean_value(v)
elif check_type is str and not isinstance(v, str):
return str(v)
elif v is None: # value was unset
return default
elif check_type is not None and not isinstance(v, check_type):
raise ConanException("[conf] {name} must be a {type}-like object. "
"The value '{value}' introduced is a {vtype} "
"object".format(name=conf_name, type=check_type.__name__,
value=v, vtype=type(v).__name__))
return v
else:
return default
def pop(self, conf_name, default=None):
"""
Remove any key-value given the conf name
"""
value = self.get(conf_name, default=default)
self._values.pop(conf_name, None)
return value
@staticmethod
def _validate_lower_case(name):
if name != name.lower():
raise ConanException("Conf '{}' must be lowercase".format(name))
def copy(self):
c = Conf()
c._values = self._values.copy()
return c
def dumps(self):
""" returns a string with a profile-like original definition, not the full environment
values
"""
return "\n".join([v.dumps() for v in reversed(self._values.values())])
def define(self, name, value):
self._validate_lower_case(name)
self._values[name] = _ConfValue(name, value)
def define_path(self, name, value):
self._validate_lower_case(name)
self._values[name] = _ConfValue(name, value, path=True)
def unset(self, name):
"""
clears the variable, equivalent to a unset or set XXX=
"""
self._values[name] = _ConfValue(name, None)
def update(self, name, value):
self._validate_lower_case(name)
conf_value = _ConfValue(name, {})
self._values.setdefault(name, conf_value).update(value)
def update_path(self, name, value):
self._validate_lower_case(name)
conf_value = _ConfValue(name, {}, path=True)
self._values.setdefault(name, conf_value).update(value)
def append(self, name, value):
self._validate_lower_case(name)
conf_value = _ConfValue(name, [_ConfVarPlaceHolder])
self._values.setdefault(name, conf_value).append(value)
def append_path(self, name, value):
self._validate_lower_case(name)
conf_value = _ConfValue(name, [_ConfVarPlaceHolder], path=True)
self._values.setdefault(name, conf_value).append(value)
def prepend(self, name, value):
self._validate_lower_case(name)
conf_value = _ConfValue(name, [_ConfVarPlaceHolder])
self._values.setdefault(name, conf_value).prepend(value)
def prepend_path(self, name, value):
self._validate_lower_case(name)
conf_value = _ConfValue(name, [_ConfVarPlaceHolder], path=True)
self._values.setdefault(name, conf_value).prepend(value)
def remove(self, name, value):
conf_value = self._values.get(name)
if conf_value:
conf_value.remove(value)
else:
raise ConanException("Conf {} does not exist.".format(name))
def compose_conf(self, other):
"""
:param other: other has less priority than current one
:type other: Conf
"""
for k, v in other._values.items():
existing = self._values.get(k)
if existing is None:
self._values[k] = v.copy()
else:
existing.compose_conf_value(v)
return self
def filter_user_modules(self):
result = Conf()
for k, v in self._values.items():
if _is_profile_module(k):
result._values[k] = v
return result
def set_relative_base_folder(self, folder):
for v in self._values.values():
v.set_relative_base_folder(folder)
class ConfDefinition:
actions = (("+=", "append"), ("=+", "prepend"),
("=!", "unset"), ("=", "define"))
def __init__(self):
self._pattern_confs = OrderedDict()
def __repr__(self):
return "ConfDefinition: " + repr(self._pattern_confs)
def __bool__(self):
return bool(self._pattern_confs)
__nonzero__ = __bool__
def __getitem__(self, module_name):
"""
DEPRECATED: it's going to disappear in Conan 2.0. Use self.get() instead.
if a module name is requested for this, it goes to the None-Global config by default
"""
pattern, name = self._split_pattern_name(module_name)
return self._pattern_confs.get(pattern, Conf()).get(name)
def __delitem__(self, module_name):
"""
DEPRECATED: it's going to disappear in Conan 2.0. Use self.pop() instead.
if a module name is requested for this, it goes to the None-Global config by default
"""
pattern, name = self._split_pattern_name(module_name)
del self._pattern_confs.get(pattern, Conf())[name]
def get(self, conf_name, default=None, check_type=None):
"""
Get the value of the conf name requested and convert it to the [type]-like passed.
"""
pattern, name = self._split_pattern_name(conf_name)
return self._pattern_confs.get(pattern, Conf()).get(name, default=default,
check_type=check_type)
def pop(self, conf_name, default=None):
"""
Remove the conf name passed.
"""
pattern, name = self._split_pattern_name(conf_name)
return self._pattern_confs.get(pattern, Conf()).pop(name, default=default)
@staticmethod
def _split_pattern_name(pattern_name):
if pattern_name.count(":") >= 2:
pattern, name = pattern_name.split(":", 1)
else:
pattern, name = None, pattern_name
return pattern, name
def get_conanfile_conf(self, ref):
""" computes package-specific Conf
it is only called when conanfile.buildenv is called
the last one found in the profile file has top priority
"""
result = Conf()
for pattern, conf in self._pattern_confs.items():
if pattern is None or fnmatch.fnmatch(str(ref), pattern):
# Latest declared has priority, copy() necessary to not destroy data
result = conf.copy().compose_conf(result)
return result
def update_conf_definition(self, other):
"""
:type other: ConfDefinition
:param other: The argument profile has priority/precedence over the current one.
"""
for pattern, conf in other._pattern_confs.items():
self._update_conf_definition(pattern, conf)
def _update_conf_definition(self, pattern, conf):
existing = self._pattern_confs.get(pattern)
if existing:
self._pattern_confs[pattern] = conf.compose_conf(existing)
else:
self._pattern_confs[pattern] = conf
def rebase_conf_definition(self, other):
"""
for taking the new global.conf and composing with the profile [conf]
:type other: ConfDefinition
"""
for pattern, conf in other._pattern_confs.items():
new_conf = conf.filter_user_modules() # Creates a copy, filtered
existing = self._pattern_confs.get(pattern)
if existing:
existing.compose_conf(new_conf)
else:
self._pattern_confs[pattern] = new_conf
def update(self, key, value, profile=False, method="define"):
"""
Define/append/prepend/unset any Conf line
>> update("tools.microsoft.msbuild:verbosity", "Detailed")
"""
pattern, name = self._split_pattern_name(key)
if not _is_profile_module(name):
if profile:
raise ConanException("[conf] '{}' not allowed in profiles".format(key))
if pattern is not None:
raise ConanException("Conf '{}' cannot have a package pattern".format(key))
# strip whitespaces before/after =
# values are not strip() unless they are a path, to preserve potential whitespaces
name = name.strip()
# When loading from profile file, latest line has priority
conf = Conf()
if method == "unset":
conf.unset(name)
else:
getattr(conf, method)(name, value)
# Update
self._update_conf_definition(pattern, conf)
def as_list(self):
result = []
for pattern, conf in self._pattern_confs.items():
for name, value in sorted(conf.items()):
if pattern:
result.append(("{}:{}".format(pattern, name), value))
else:
result.append((name, value))
return result
def dumps(self):
result = []
for pattern, conf in self._pattern_confs.items():
if pattern is None:
result.append(conf.dumps())
else:
result.append("\n".join("{}:{}".format(pattern, line) if line else ""
for line in conf.dumps().splitlines()))
if result:
result.append("")
return "\n".join(result)
@staticmethod
def _get_evaluated_value(__v):
"""
Function to avoid eval() catching local variables
"""
try:
# Isolated eval
parsed_value = eval(__v)
if isinstance(parsed_value, str): # xxx:xxx = "my string"
# Let's respect the quotes introduced by any user
parsed_value = '"{}"'.format(parsed_value)
except:
# It means eval() failed because of a string without quotes
parsed_value = __v.strip()
return parsed_value
def loads(self, text, profile=False):
self._pattern_confs = {}
for line in text.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
for op, method in ConfDefinition.actions:
tokens = line.split(op, 1)
if len(tokens) != 2:
continue
pattern_name, value = tokens
parsed_value = ConfDefinition._get_evaluated_value(value)
self.update(pattern_name, parsed_value, profile=profile, method=method)
break
else:
raise ConanException("Bad conf definition: {}".format(line))