def __init__(self, filename): self.id = os.path.splitext(os.path.basename(filename))[0] self.log = pb_logging.logger.getChild("Recipe[{}]".format(self.id)) self.inherit = 'empty' self._static = False # Load original recipe: self.log.obnoxious("Loading recipe file: {}".format(filename)) self._data = self._load_recipe_from_file(filename) # Recursively do the inheritance: while self._data.get('inherit', 'empty'): inherit_from = self._data.get('inherit', 'empty') try: filename = recipe_manager.recipe_manager.get_template_filename(inherit_from) self.log.obnoxious("Loading template file: {}".format(filename)) except PBException as e: self.log.warn("Recipe attempting to inherit from unknown template {}".format( inherit_from )) break self.log.obnoxious("Inheriting from file {}".format(filename)) parent_data = self._load_recipe_from_file(filename) self._data['depends'] = self._data['depends'] + parent_data['depends'] self._data = dict_merge(parent_data, self._data) self._data['inherit'] = parent_data.get('inherit') if self._data.get('target') == 'package': self._data = self.get_local_package_data() else: self._data = self._normalize_package_data(self._data) # Map all recipe info onto self: for k, v in iteritems(self._data): if not hasattr(self, k): setattr(self, k, v) self.log.obnoxious("Loaded recipe - {}".format(self.id))
def update_cfg_file(self, new_data, cfg_file=None): """ Write new data to a config file. If no config file is specified, the local config file is used (e.g., on Linux, the one in ~/.pybombs/). """ if cfg_file is None: cfg_file = self.local_cfg if not os.path.isfile(cfg_file): self.log.info("Creating new config file {0}".format(cfg_file)) cfg_data = new_data path = os.path.split(cfg_file)[0] if not os.path.isdir(path): os.path.mkdir(path) else: self.log.obnoxious( "Updating file {0} with new data: {1}".format(cfg_file, new_data) ) try: old_cfg_data = yaml.safe_load(open(cfg_file).read()) or {} except IOError: self.log.debug("Error opening config file {0}.".format(cfg_file)) old_cfg_data = {} cfg_data = dict_merge(old_cfg_data, new_data) open(cfg_file, 'wb').write(yaml.dump(cfg_data, default_flow_style=False))
def update_cfg_file(self, new_data, cfg_file=None): """ Write new data to a config file. If no config file is specified, the local config file is used (e.g., on Linux, the one in ~/.pybombs/). """ if cfg_file is None: cfg_file = self.local_cfg if not os.path.isfile(cfg_file): self.log.info("Creating new config file {0}".format(cfg_file)) cfg_data = new_data path = os.path.split(cfg_file)[0] if not os.path.isdir(path): os.path.mkdir(path) else: self.log.obnoxious("Updating file {0} with new data: {1}".format( cfg_file, new_data)) try: old_cfg_data = yaml.safe_load(open(cfg_file).read()) or {} except IOError: self.log.debug( "Error opening config file {0}.".format(cfg_file)) old_cfg_data = {} cfg_data = dict_merge(old_cfg_data, new_data) open(cfg_file, 'wb').write(yaml.dump(cfg_data, default_flow_style=False))
def __init__(self, filename): self.id = os.path.splitext(os.path.basename(filename))[0] self.log = pb_logging.logger.getChild("Recipe[{0}]".format(self.id)) self.inherit = 'empty' self._static = False # Load original recipe: self.log.trace("Loading recipe file: {0}".format(filename)) self._data = self._load_recipe_from_file(filename) # Recursively do the inheritance: while self._data.get('inherit', 'empty'): inherit_from = self._data.get('inherit', 'empty') try: filename = recipe_manager.recipe_manager.get_template_filename( inherit_from) self.log.trace("Loading template file: {0}".format(filename)) except PBException as e: self.log.warn( "Recipe attempting to inherit from unknown template {0}". format(inherit_from)) break self.log.trace("Inheriting from file {0}".format(filename)) parent_data = self._load_recipe_from_file(filename) self._data[ 'depends'] = self._data['depends'] + parent_data['depends'] self._data = dict_merge(parent_data, self._data) self._data['inherit'] = parent_data.get('inherit') if self._data.get('target') == 'package': self._data = self.get_local_package_data() else: self._data = self._normalize_package_data(self._data) # Map all recipe info onto self: for k, v in iteritems(self._data): if not hasattr(self, k): setattr(self, k, v) self.log.trace("Loaded recipe - {0}".format(self.id))
def _install_sdk_to_prefix(self, sdkname): """ Read recipe for sdkname, and install the SDK to the prefix. """ from pybombs import recipe src_dir = self.prefix.src_dir cfg_file = self.prefix.cfg_file ### Get the recipe r = recipe.get_recipe(sdkname, target='sdk') try: self.log.trace("Switching CWD to {0}".format(src_dir)) if not op.isdir(src_dir): os.mkdir(src_dir) os.chdir(src_dir) except: self.log.error("Source dir required to install SDK.") return False ### Install the actual SDK file self.log.debug("Fetching SDK `{sdk}'".format(sdk=sdkname)) fetcher.Fetcher().fetch(r) self.log.info("Installing SDK `{sdk}'".format(sdk=sdkname)) # Install command cmd = r.var_replace_all(r.get_command('install')) if subproc.monitor_process(cmd, shell=True, env=os.environ) == 0: self.log.debug("Installation successful") else: self.log.error("Error installing SDK. Aborting.") return False # Clean up files_to_delete = [op.normpath(op.join(src_dir, r.var_replace_all(x))) for x in r.clean] if len(files_to_delete): self.log.info("Cleaning up files...") for ftd in files_to_delete: if op.commonprefix((src_dir, ftd)) != src_dir: self.log.warn("Not removing {ftd} -- outside source dir!".format(ftd=ftd)) continue self.log.debug("Removing {ftd}...".format(ftd=ftd)) if op.isdir(ftd): shutil.rmtree(ftd) elif op.isfile(ftd): os.remove(ftd) else: self.log.error("Not sure what this is: {ftd}".format(ftd=ftd)) return False ### Update the prefix-local config file self.log.debug("Updating config file with SDK recipe info.") try: old_cfg_data = PBConfigFile(cfg_file).get() except IOError: self.log.debug("There doesn't seem to be a config file yet for this prefix.") old_cfg_data = {} # Filter out keys we don't care about: sdk_recipe_keys_for_config = ('config', 'packages', 'categories', 'env') sdk_cfg_data = {k: v for k, v in iteritems(r.get_dict()) if k in sdk_recipe_keys_for_config} self.log.trace("New data: {new}".format(new=sdk_cfg_data)) cfg_data = dict_merge(old_cfg_data, sdk_cfg_data) self.log.debug("Writing updated prefix config to `{0}'".format(cfg_file)) PBConfigFile(cfg_file).save(cfg_data) return True
def __init__(self, filename): self.id = os.path.splitext(os.path.basename(filename))[0] self.log = pb_logging.logger.getChild("Recipe[{}]".format(self.id)) self.inherit = 'empty' self._static = False # Load original recipe: self.log.obnoxious("Loading recipe file: {}".format(filename)) self._data = self._load_recipe_from_yaml(filename) # Recursively do the inheritance: while self._data.get('inherit', 'empty'): inherit_from = self._data.get('inherit', 'empty') try: filename = recipe_manager.recipe_manager.get_template_filename( inherit_from) self.log.obnoxious( "Loading template file: {}".format(filename)) except PBException as e: self.log.warn( "Recipe attempting to inherit from unknown template {}". format(inherit_from)) break self.log.obnoxious("Inheriting from file {}".format(filename)) parent_data = self._load_recipe_from_yaml(filename) self._data[ 'depends'] = self._data['depends'] + parent_data['depends'] self._data = dict_merge(parent_data, self._data) self._data['inherit'] = parent_data.get('inherit') # Post-process some fields: if self._data.has_key('source') and not isinstance( self._data['source'], list): self._data['source'] = [ self._data['source'], ] # Package flags override vars: self._data['vars'] = dict_merge( self._data.get('vars', {}), config_manager.config_manager.get_package_flags( self.id, self._data.get('category')).get('vars', {})) # Map all recipe info onto self: for k, v in self._data.iteritems(): if not hasattr(self, k): setattr(self, k, v)
def _merge_config_info_from_file(self, cfg_file, cfg_data): """ Load a config file, load its contents, and merge it into cfg_info. Return the result. """ try: self.log.debug('Inspecting config file: {}'.format(cfg_file)) cfg_data_new = yaml.safe_load(open(cfg_file, 'r').read()) or {} except Exception as e: self.log.debug('Well, looks like that failed.') return cfg_data return dict_merge(cfg_data, cfg_data_new)
def _merge_config_info_from_file(self, cfg_file, cfg_data): """ Load a config file, load its contents, and merge it into cfg_info. Return the result. """ try: self.log.debug('Inspecting config file: {0}'.format(cfg_file)) cfg_data_new = PBConfigFile(cfg_file).get() except Exception: self.log.debug('Well, looks like that failed.') return cfg_data return dict_merge(cfg_data, cfg_data_new)
def get_local_package_data(self): """ Merges the recipe data with local config settings. Local settings always supersede recipe settings. This allows users to override anything in a recipe with whatever's stored in the `package:` and `category:` sections of their local config files. """ return dict_merge( self.get_dict(), config_manager.config_manager.get_package_flags( self.id, self.get_dict().get('category')))
def __init__(self, filename): self.id = os.path.splitext(os.path.basename(filename))[0] self.log = pb_logging.logger.getChild("Recipe[{}]".format(self.id)) self.inherit = 'empty' self._static = False # Load original recipe: self.log.obnoxious("Loading recipe file: {}".format(filename)) self._data = self._load_recipe_from_yaml(filename) # Recursively do the inheritance: while self._data.get('inherit', 'empty'): inherit_from = self._data.get('inherit', 'empty') try: filename = recipe_manager.recipe_manager.get_template_filename(inherit_from) self.log.obnoxious("Loading template file: {}".format(filename)) except PBException as e: self.log.warn("Recipe attempting to inherit from unknown template {}".format( inherit_from )) break self.log.obnoxious("Inheriting from file {}".format(filename)) parent_data = self._load_recipe_from_yaml(filename) self._data['depends'] = self._data['depends'] + parent_data['depends'] self._data = dict_merge(parent_data, self._data) self._data['inherit'] = parent_data.get('inherit') # Post-process some fields: if self._data.has_key('source') and not isinstance(self._data['source'], list): self._data['source'] = [self._data['source'],] # Package flags override vars: self._data['vars'] = dict_merge( self._data.get('vars', {}), config_manager.config_manager.get_package_flags( self.id, self._data.get('category') ).get('vars', {}) ) # Map all recipe info onto self: for k, v in self._data.iteritems(): if not hasattr(self, k): setattr(self, k, v) self.log.obnoxious("Loaded recipe - {}".format(self))
def get_local_package_data(self): """ Merges the recipe data with local config settings. Local settings always supersede recipe settings. This allows users to override anything in a recipe with whatever's stored in the `package:` and `category:` sections of their local config files. """ return self._normalize_package_data(dict_merge( self.get_dict(), config_manager.config_manager.get_package_flags( self.id, self.get_dict().get('category') ) ))
def get_package_flags(self, pkgname, categoryname=None): """ Return all the package flags of pkgname as a dictionary. If pkgname doesn't have any package flags, return an empty dict. Note: Only returns settings from the local settings files! If you want the full set of current package settings, prefer recipe.get_local_package_data(). If categoryname is provided, it will load the category flags first and then merge the package flags on top of it. """ return dict_merge( getattr(self._prefix_info, 'categories', {}).get(categoryname, {}), getattr(self._prefix_info, 'packages', {}).get(pkgname, {}))
def get_package_flags(self, pkgname, categoryname=None): """ Return all the package flags of pkgname as a dictionary. If pkgname doesn't have any package flags, return an empty dict. Note: Only returns settings from the local settings files! If you want the full set of current package settings, prefer recipe.get_local_package_data(). If categoryname is provided, it will load the category flags first and then merge the package flags on top of it. """ return dict_merge( getattr(self._prefix_info, 'categories', {}).get(categoryname, {}), getattr(self._prefix_info, 'packages', {}).get(pkgname, {}) )
def update(self, newdata): " Overwrite the data with newdata recursively. Updates the file. " self.data = dict_merge(self.data, newdata) self.save() return self.data
def _run_init(self): """ pybombs prefix init """ def register_alias(alias): if alias is not None: if self.prefix is not None and \ self.prefix.prefix_aliases.get(alias) is not None \ and not confirm("Alias `{0}' already exists, overwrite?".format(alias)): self.log.warn('Aborting.') raise PBException("Could not create alias.") self.cfg.update_cfg_file({'prefix_aliases': {self.args.alias: path}}) # Go, go, go! try: prefix_recipe = get_prefix_recipe(self.args.recipe) except PBException as ex: self.log.error(str(ex)) return -1 if prefix_recipe is None: self.log.error("Could not find recipe for `{0}'".format(self.args.recipe)) return -1 # Make sure the directory is writable path = op.abspath(op.normpath(self.args.path)) if not sysutils.mkdir_writable(path, self.log): self.log.error("Cannot write to prefix path `{0}'.".format(path)) return -1 # Make sure that a pybombs directory doesn't already exist from pybombs import config_manager if op.exists(op.join(path, config_manager.PrefixInfo.prefix_conf_dir)): self.log.error("Ignoring. A prefix already exists in `{0}'".format(path)) return -1 # Add subdirs sysutils.require_subdirs(path, [k for k, v in prefix_recipe.dirs.items() if v]) self.cfg.load(select_prefix=path) self.prefix = self.cfg.get_active_prefix() # Create files for fname, content in prefix_recipe.files.items(): sysutils.write_file_in_subdir(path, fname, prefix_recipe.var_replace_all(content)) # Register alias if self.args.alias is not None: register_alias(self.args.alias) # If there is no default prefix, make this the default if len(self.cfg.get('default_prefix')) == 0: if self.args.alias is not None: new_default_prefix = self.args.alias else: new_default_prefix = path self.cfg.update_cfg_file({'config': {'default_prefix': new_default_prefix}}) # Create virtualenv if so desired if self.args.virtualenv: self.log.info("Creating Python virtualenv in prefix...") venv_args = ['virtualenv', '--no-site-packages'] venv_args.append(path) subproc.monitor_process(args=venv_args) # Install SDK if so desired sdk = self.args.sdkname or prefix_recipe.sdk if sdk is not None: self.log.info("Installing SDK recipe {0}.".format(sdk)) self.log.info("Reloading configuration...") self.cfg.load(select_prefix=path) self.prefix = self.cfg.get_active_prefix() self.inventory = self.prefix.inventory self._install_sdk_to_prefix(sdk) # Update config section if len(prefix_recipe.config): if self.args.virtualenv: prefix_recipe.config = dict_merge( {'virtualenv': True}, prefix_recipe.config) self.cfg.update_cfg_file(prefix_recipe.config, self.prefix.cfg_file) else: prefix_recipe.config = dict_merge( {'virtualenv': False}, prefix_recipe.config) self.cfg.update_cfg_file(prefix_recipe.config, self.prefix.cfg_file) self.cfg.load(select_prefix=path) self.prefix = self.cfg.get_active_prefix() # Install dependencies if len(prefix_recipe.depends): self.log.info("Installing default packages for prefix...") self.log.info("".join(["\n - {0}".format(x) for x in prefix_recipe.depends])) from pybombs import install_manager install_manager.InstallManager().install( prefix_recipe.depends, 'install', # install / update fail_if_not_exists=False, update_if_exists=False, print_tree=True, )