diff options
Diffstat (limited to 'py/config/base.py')
-rw-r--r-- | py/config/base.py | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/py/config/base.py b/py/config/base.py new file mode 100644 index 0000000000..dc43340386 --- /dev/null +++ b/py/config/base.py @@ -0,0 +1,410 @@ +try: + from configparser import ConfigParser, NoOptionError +except ImportError: + from ConfigParser import ConfigParser, NoOptionError + +from os.path import exists +from sys import version_info + +Default = None # Default value. +Disable = "-DISABLED-" # Disable this option +features_list = [] # Global features list. + + +def fatal(str): + print("FATAL: %s" % str) + exit(1) + + +class Value(object): + """Holds an option value internally. This acts in a similar fashion to a dictionary.""" + + def __init__(self, rc): + self.__dict__["options"] = {} + self.__dict__["rc"] = rc + + def __setattr__(self, key, value): + """Set a value, this either fetches the option or sets it if it already exists.""" + self._set_or_get(key, value) + + def __getitem__(self, key): + """Get an option from the internal table.""" + return self.options[key] + + def __getattr__(self, key): + """Allow an option to be fetched as an attribute.""" + return self.options[key] + + def _set_or_get(self, option, value): + """ + Set or get an option. + + :param option: Option name (string) + :param value: Value to set option + """ + + # Disabling an option removes it from the header completely irrespective of its type. + if type(value) == str and value == "-DISABLED-": + del self.options[option] + return + + if option not in self.options: + if option not in self.rc.default: + fatal("Missing default option: %s" % option) + opt = self.rc.default[option] # Get option class + i = opt(value) # Create option with value + self.options[i.name] = i # Set in dictionary + else: + i = self.options[option] + i.set(value) # Set value + + def __str__(self): + """String representation of a value object.""" + return "Value object at %s: %s" % (hex(id(self)), self.value) + + def __iter__(self): + """Allow iteration.""" + return iter(self.options) + + + +class Config(object): + feature = () #: Feature list (XXX: Why is this required?) + feature_default = ("gcc", "debug") #: Default features. + + def __init__(self, rc): + self.rc = rc + self.base = False #: Whether this is a base config or not. + self.option_header = Value(rc) #: Internal header options + self.option_build = Value(rc) #: Internal build options + + if hasattr(self, "bsp") and not hasattr(self, "descr"): + raise Exception("BSP.descr is required for BSPs") + + # Iterate over base classes in reverse order so the 'latest' + # defined option is taken. + for i in type(self).__mro__[::-1]: + if hasattr(i, "header"): + i.header(self, self.option_header) + if hasattr(i, "build"): + i.build(self, self.option_build) + + # Make sure features don't conflict with each other. + processed = [] + feature_obj = [x for x in features_list if x.name in self.feature] + for feature in feature_obj: + conflicts = set(processed) & set(feature.conflicts) + processed.append(feature.name) + if conflicts: + raise Exception("Feature %s conflicts with %s" % (feature.name, ", ".join(conflicts))) + feature(self) + + self.feature += self.feature_default # Features in this config + + + def header(self, c): + """ + Header config options. + + :param c: `self.option_header` + """ + pass + + + def build(self, c): + """ + Build config options. + + :param c: `self.build_header` + """ + pass + + # XXX: needs to merge both dictionaries and sort them. + def config_get(self): + """Get the config.cfg (.ini) format for this config.""" + str = "[%s]\n" % self.name + def add(d, str): + for o in sorted(d): + opt = d[o] + str += "%s" % opt.config_get() + str += "\n\n" + return str + + str = add(self.option_header, str) + str = add(self.option_build, str) + return str + + + + +class Feature(Config): + """Build feature base class""" + name = None #: Name of feature + description = None #: Description + exclude = None #: BSPs to exclude + include = None #: BSPs to include + conflicts = None #: Other features this one conflicts with + is_feature = True #: Whether this is a feature or not + + def __init__(self, parent): + self.build(parent.option_build) + self.header(parent.option_header) + + def __str__(self): + return "%s (%s)" % (self, self.name) + + + +class cfg_general(Config): + """[general] block for `config.cfg.`""" + name = "general" + def build(self, c): + c.BSP = Default + c.PREFIX = Default + c.PATH_TOOLS = Default + c.ENABLE_DEBUG = Default + c.CFLAGS = Default + c.LIBS = Default + c.LDFLAGS = Default + + +class cfg_host(Config): + """[host] block for `config.cfg.`""" + name = "host" + def build(self, c): + c.CFLAGS = Default + c.LIBS = Default + c.LDFLAGS = Default + + +class cfg_bsp(Config): + """[bsp] block for `config.cfg` to hold BSP-specific settings""" + name = "bsp" + def build(self, c): + c.ENABLE_DEBUG = Default + c.ENABLE_MP = Default + c.ENABLE_MULTILIB = Default + c.ENABLE_NETWORKING = Default + c.ENABLE_NEWLIB = Default + c.ENABLE_POSIX = Default + c.ENABLE_PTHREADS = Default + c.ENABLE_SERDBG = Default + c.ENABLE_SHELL = Default + c.ENABLE_SMP = Default + c.LINK_START = Default + c.LINK_END = Default + c.LINK_LINK = Default + c.ENABLE_SYSTEM_DEP = Default + + + + +class BuildConfig(object): + """ + This class handles creating and loading `config.cfg`. + """ + file_config = "config.cfg" #: Default config file name. + + def __init__(self, rc, list_bsp=[]): + self.cfg_default = [cfg_general(rc), cfg_host(rc), cfg_bsp(rc)] #: Default BSP configuration. + self.cfg = list(self.cfg_default) #: BSP config. + self.list_bsp = [] + self.rc = rc + + if list_bsp: + self.list_bsp = sorted(list_bsp) + elif not exists(self.file_config): + fatal("Missing config.cfg") + else: + # Load on-disk config. + self.cfg_user = ConfigParser() + self.cfg_user.read(self.file_config) + + # Set BSP list. + # XXX: Far too complicated due to chicken-and-egg issue. + # This will be refactored in the future. + tmp = cfg_general(self.rc) + opt = tmp.option_build["BSP"] + o = self.cfg_user.get("general", "BSP") + if version_info < (3,) and type(o) is unicode: #2to3 + o = str(o) + opt.set(o) + self.list_bsp = opt.value + + # Parse BSPs + self._parse_bsp(self.list_bsp) + + # Load user configuration + if not list_bsp: + self._cfg_user_load() + + # Make sure BSP= is always set. + self.option_set("general", "BSP", " " .join(self.list_bsp)) + + # XXX: this is not needed with the new system + def _parse_bsp(self, list_bsp): + """ + Parse BSP config. + + :param: list_bsp: List of BSPs in this config. + """ + + # Make this simplier + bsp_map = {} + + for b in self.list_bsp: + bsp = [x for x in self.rc.config if x.name == b][0] + + bsp_arch = [x for x in self.rc.config if x.arch == bsp.arch][0] + bsp_map.setdefault((bsp_arch.name, bsp_arch), []).append(bsp) + + # Save for usage in config_set + self.bsp_map = bsp_map + + for bsp_name, bsp_arch in sorted(bsp_map): +# self.cfg.append(bsp_arch(self.rc)) # XXX: [sparc] used to be its own section not anymore for now. + for bsp in bsp_map[(bsp_name, bsp_arch)]: + self.cfg.append(bsp(self.rc)) + + + def save(self): + """Save config to disk.""" + with open(self.file_config, "w") as fp: + fp.write(self._cfg_get()) + fp.write("\n\n") # Handy. + + + def config_set(self, ctx, cfg_name, arch_name=None): + """ + Apply config internally to waf. + + :param ctx: waf Context. + :param cfg_name: BSP config name (arch/bsp format). + :param arch_name: Architecture name. + """ + + cfg = None + if arch_name: +# self.config_set(ctx, arch_name) + cfg_name = "%s/%s" % (arch_name, cfg_name) + + for c in self.cfg: + if c.name == cfg_name: + cfg = c + break + + if not cfg: + fatal("BuildConfig:config_set(): Invalid config: %s" % cfg_name) + + for option in cfg.option_build: + opt = cfg.option_build[option] +# self._set_cfg_user(cfg_name, opt) + opt.set_config_build(ctx) + + for option in cfg.option_header: + opt = cfg.option_header[option] +# self._set_cfg_user(cfg_name, opt) + opt.set_config_header(ctx) + + + + def bsp_get_detail(self, arch, bsp): + cfg = None + + for c in self.rc.config: + if c.name == "%s/%s" % (arch, bsp): + cfg = c + break + + + if cfg is None: + raise Exception("MISSING BSP!") + + return cfg.descr + + + def option_set(self, cfg, option, value): + """ + Set an option within a config + + :param cfg: Config to set. + :param option: Option name. + :param value: Value to set. + """ + + for config in self.cfg_default: + if config.name == cfg: + # Only allow build options to be set for now. + for o in config.option_build: + opt = config.option_build[o] + if opt.name == option: + opt.set(value) + + def _cfg_get(self): + """Get config text.""" + cfg = "" + for bsp in self.cfg: + cfg += "\n\n" + cfg += bsp.config_get() + return cfg.strip() + + + #XXX: unused + def _cfg_add(self, str): + self.cfg += "\n" + self.cfg += str + + + def _cfg_user_load(self): + """Load user config from disk.""" + for cfg_bsp in self.cfg: + section = cfg_bsp.name + + if not self.cfg_user.has_section(section): + fatal("Missing section: [%s]" % section) + + for option in cfg_bsp.option_build: + opt = cfg_bsp.option_build[option] + + if not self.cfg_user.has_option(section, opt.name): + fatal("Missing Option in config: %s" % opt.name) + + o = self.cfg_user.get(section, opt.name) + + # configpaser does not convert values anymore. + if o in ["True", "False"]: + o = self.cfg_user.getboolean(section, opt.name) + + # We do not support unicode internally + if version_info < (3,) and type(o) is unicode: #2to3 + o = str(o) + + self._set_cfg_user(section, opt) + +# opt.set(o) + + for option in cfg_bsp.option_header: + opt = cfg_bsp.option_header[option] + self._set_cfg_user(section, opt) +# o = self.cfg_user.get(section, opt.name) +# opt.set(o) + + + def _set_cfg_user(self, section, opt): + if not self.cfg_user.has_section(section): + fatal("Missing section: [%s]" % section) + + o = self.cfg_user.get(section, opt.name) + + # configpaser does not convert values anymore. + if o in ["True", "False"]: + o = self.cfg_user.getboolean(section, opt.name) + + # We do not support unicode internally + if version_info < (3,) and type(o) is unicode: #2to3 + o = str(o) + + opt.set(o) + + + |