-
Notifications
You must be signed in to change notification settings - Fork 948
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New "conf" configuration mechanism #8266
Changes from 21 commits
f1b3e68
fee01dc
3c6c059
a641daa
f69fef1
71418ed
5908f92
df76450
bbd8644
a32ec10
8fc1ac4
9e1dee8
4242f1d
6483c49
614d4d3
b575e18
8e8596c
6dbc141
43dcbab
3774403
054da0e
888ec72
a70ed07
62a5311
e2167f0
0e2664a
ae63225
d2c39f8
8d497a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
from conans.client.profile_loader import read_profile | ||
from conans.client.store.localdb import LocalDB | ||
from conans.errors import ConanException | ||
from conans.model.conf import ConfDefinition | ||
from conans.model.profile import Profile | ||
from conans.model.ref import ConanFileReference | ||
from conans.model.settings import Settings | ||
|
@@ -77,6 +78,7 @@ def __init__(self, cache_folder, output): | |
# Caching | ||
self._no_lock = None | ||
self._config = None | ||
self._new_config = None | ||
self.editable_packages = EditablePackages(self.cache_folder) | ||
# paths | ||
self._store_folder = self.config.storage_path or os.path.join(self.cache_folder, "data") | ||
|
@@ -155,6 +157,22 @@ def config(self): | |
self._config = ConanClientConfigParser(self.conan_conf_path) | ||
return self._config | ||
|
||
@property | ||
def new_config_path(self): | ||
return os.path.join(self.cache_folder, "conan_conf.txt") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer this to be decoupled from the cache folder. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not new, the current config (and absolutely everything from the cache) is already loaded this way, because assuming it is in the cache folder, you need the cache folder to locate it. It cannot be decoupled, but maybe I don't understand the suggestion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say it's not always a valid argument "it was always like that" when designing a brand new thing, especially if the approach is already known to be problematic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not an argument of "it was always like that". It is maybe a slightly incorrect naming. Lets rename the If we agree with that, I am very fine with renaming the Conan codebase from "cache" => "home" when it applies. But the logic implemented in this |
||
|
||
@property | ||
def new_config(self): | ||
""" this is the new conan_conf.txt to replace the old conan.conf that contains | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer .conf extension. .txt is generic, conf can be interpreted as .ini, even for code editors it can used for highlight. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a temporary name, it might be possible that everything become python files. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, temporary name, to be changed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd also prefer a different extension to indicate it's a config file, not a generic text file. something like |
||
configuration defined with the new syntax as in profiles, this config will be composed | ||
to the profile ones and passed to the conanfiles.conf, which can be passed to collaborators | ||
""" | ||
if self._new_config is None: | ||
self._new_config = ConfDefinition() | ||
if os.path.exists(self.new_config_path): | ||
self._new_config.loads(load(self.new_config_path)) | ||
return self._new_config | ||
|
||
@property | ||
def localdb(self): | ||
localdb_filename = os.path.join(self.cache_folder, LOCALDB) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import fnmatch | ||
from collections import OrderedDict | ||
|
||
from conans.errors import ConanException | ||
|
||
|
||
class _ConfModule(object): | ||
memsharded marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def __init__(self): | ||
self._confs = {} # Component => dict {config-name: value} | ||
|
||
def __getattr__(self, item): | ||
return self._confs.get(item) | ||
|
||
def update(self, other): | ||
""" | ||
:type other: _ConfModule | ||
""" | ||
self._confs.update(other._confs) | ||
|
||
def set_value(self, k, v): | ||
self._confs[k] = v | ||
|
||
def __repr__(self): | ||
return "_ConfModule: " + repr(self._confs) | ||
|
||
def items(self): | ||
return self._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) | ||
|
||
|
||
class Conf(object): | ||
|
||
def __init__(self): | ||
self._conf_modules = {} # module_name => _ConfModule | ||
|
||
def __getitem__(self, module_name): | ||
return self._conf_modules.get(module_name, _ConfModule()) | ||
|
||
def __repr__(self): | ||
return "Conf: " + repr(self._conf_modules) | ||
|
||
def items(self): | ||
return self._conf_modules.items() | ||
|
||
def filter_user_modules(self): | ||
result = Conf() | ||
for k, v in self._conf_modules.items(): | ||
if _is_profile_module(k): | ||
result._conf_modules[k] = v | ||
return result | ||
|
||
def update(self, other): | ||
""" | ||
:type other: Conf | ||
""" | ||
for module_name, module_conf in other._conf_modules.items(): | ||
existing = self._conf_modules.get(module_name) | ||
if existing: | ||
existing.update(module_conf) | ||
else: | ||
self._conf_modules[module_name] = module_conf | ||
|
||
def set_value(self, module_name, k, v): | ||
self._conf_modules.setdefault(module_name, _ConfModule()).set_value(k, v) | ||
|
||
|
||
class ConfDefinition(object): | ||
def __init__(self): | ||
self._pattern_confs = OrderedDict() # pattern (including None) => Conf | ||
|
||
def __bool__(self): | ||
return bool(self._pattern_confs) | ||
|
||
def __repr__(self): | ||
return "ConfDefinition: " + repr(self._pattern_confs) | ||
|
||
__nonzero__ = __bool__ | ||
|
||
def __getitem__(self, module_name): | ||
""" if a module name is requested for this, always goes to the None-Global config | ||
""" | ||
return self._pattern_confs.get(None, Conf())[module_name] | ||
|
||
def get_conanfile_conf(self, name): | ||
result = Conf() | ||
for pattern, conf in self._pattern_confs.items(): | ||
# TODO: standardize this package-pattern matching | ||
if pattern is None or fnmatch.fnmatch(name, pattern): | ||
result.update(conf) | ||
return result | ||
|
||
def update_conf_definition(self, other): | ||
""" | ||
This is used for composition of profiles [conf] section | ||
:type other: ConfDefinition | ||
""" | ||
for k, v in other._pattern_confs.items(): | ||
existing = self._pattern_confs.get(k) | ||
if existing: | ||
existing.update(v) | ||
else: | ||
self._pattern_confs[k] = v | ||
|
||
def rebase_conf_definition(self, other): | ||
""" | ||
for taking the new conan_conf.txt and composing with the profile [conf] | ||
:type other: ConfDefinition | ||
""" | ||
for k, v in other._pattern_confs.items(): | ||
new_v = v.filter_user_modules() # Creates a copy, filtered | ||
existing = self._pattern_confs.get(k) | ||
if existing: | ||
new_v.update(existing) | ||
self._pattern_confs[k] = new_v | ||
|
||
def dumps(self): | ||
result = [] | ||
for pattern, conf in self._pattern_confs.items(): | ||
for name, values in conf.items(): | ||
for k, v in values.items(): | ||
if pattern: | ||
result.append("{}:{}:{}={}".format(pattern, name, k, v)) | ||
else: | ||
result.append("{}:{}={}".format(name, k, v)) | ||
return "\n".join(result) | ||
|
||
def loads(self, text, profile=False): | ||
self._pattern_confs = {} | ||
for line in text.splitlines(): | ||
line = line.strip() | ||
if not line or line.startswith("#"): | ||
continue | ||
left, value = line.split("=", 1) | ||
tokens = left.split(":", 2) | ||
if len(tokens) == 3: | ||
pattern, conf_module, name = tokens | ||
else: | ||
assert len(tokens) == 2 | ||
conf_module, name = tokens | ||
pattern = None | ||
if not _is_profile_module(conf_module): | ||
if profile: | ||
raise ConanException("[conf] '{}' not allowed in profiles".format(line)) | ||
if pattern is not None: | ||
raise ConanException("Conf '{}' cannot have a package pattern".format(line)) | ||
conf = self._pattern_confs.setdefault(pattern, Conf()) | ||
conf.set_value(conf_module, name, value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, in the "toolchain" build_helper version we drop that
msbuild_verbosity
parameter, right? (just double checking)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally, both in the CMake and MSBuild helpers, that parameter is dropped. I don't see any value (actually a bit of noise) of specifying that in recipes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why dropping?
I would assume the configuration file provides a new default.
The search order of a value should be (from higher priority to lower priority):
[conf]
section of profile[conf]
section of globalconan_conf.txt
Arguments like these are often useful for debugging purposes.
It would be illogical having to modify a global configuration/profile to add debugging info to some recipe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it very annoying, not to say impossible to edit the recipe (when building dependencies) to change something like the verbosity? So what is really wanted is the possibility to define in command line, for example, like
-c tools.microsoft:msbuild_verbosity=Diagnostic
, but not editing the recipe.Plus, defining it as an argument in the recipe, doesn't have that strong priority for all users. Many of them will still want to be able to change that from the downstream consumer, even if defined in code (so this would be in fact the lowest priority). So this is what we are priorizing now, the case of the real
conf
definition, and we plan to add it as argument when there is a stronger evidence of the use case and after discussing and agreeing on the priority.