""" vxsdk.toml document parser """ import os import toml from core.logger import log from core.pkg.version import version_get __all__ = [ 'VxProjectMeta' ] class VxProjectMeta(): r""" Represente the project meta-information All meta-information is stored in a file named `vxsdk.toml` at the root of the project directory. This class exposes: ================================== ========================================= Property Description ================================== ========================================= name (str) Project name version (str) Project versions path (str) Project path description (str) Project description type (str) Project type target_support (list,str) List of all supported target ================================== ========================================= Methods Description ================================== ========================================= get_dependencies (list,dict) List of all dependencies information get_build_rules (dict) Building rules information ================================== ========================================= For the file to be valid, it should expose *at least* the 'project' section with the name of the project. Other information like the project type have default value: ================================== ======================================= Information Default value ================================== ======================================= name (str) Required. version (str) auto. (see core.pkg.version.version_get) type (str) 'bin' description (str) Project description target_support (list,str) [] (support all) ================================== ======================================= Also, this class will perform any vxSDK configuration needed described in the "special" section 'config' which can expose "key/value" which will be added (if not exist) in the vxSDK's configuration file (~/.config/vxsdk/config.toml). Example of a valid 'vxsdk.toml' file: ``` [project] name = 'project' type = 'addin' [dependencies] vxkernel = '^1.0.0' [vxkernel.extra] build = '--static --verbose' [config] 'superh.path.sysroot' = '{path.sysroot}/superh' 'superh.path.sysroot_test' = '{superh.path.sysroot}/test/yes' ``` """ def __init__(self, path, parent_path=None, extra_conf=None): """ Open and parse the vxsdk.toml file @args > path (str) : project path which we can find `vxsdk.toml` file > parent_path (str) : project parent path > extra_conf (dict) : extra flags information about particular steps @todo > proper handle exception """ # try to open the file self._path = os.path.abspath(path) with open(self._path + '/vxsdk.toml', encoding='utf-8') as file: self._toml = toml.loads(file.read()) # check mandatory information # @notes # - 'project' section # - project name information if 'project' not in self._toml or 'name' not in self._toml['project']: raise Exception(f"'{self._path}': missing project name information") # setup information and cache self._type = None self._target_support = None self._parent_path = parent_path self._extra_conf = extra_conf def __str__(self): """ Display project information """ content = f"project '{self.name}' - {self.version}\n" content += '\n' content += 'project meta\n' content += ' - path:'.ljust(16) + f'{self.path}\n' content += ' - parent:'.ljust(16) + f'{self.parent_path}\n' content += ' - type:'.ljust(16) + f'{self.type}\n' content += '\n' content += 'Build information\n' #if not self.build: # content += ' default building rules used\n' # content += '\n' #else: # rule_list = self.get_build_rules() # for rule in rule_list: # content += ' - ' # content += f'{rule}:'.ljust(16) # content += f'{rule_list[rule]}\n' # content += '\n' #content += 'Dependencies list\n' #if not self.dependencies: # content += ' No dependencies for this project\n' #else: # for dep in self.dependencies: # content += ' - ' + f'{dep.name}'.ljust(16) + f'({dep.version})\n' return content def __repr__(self): return f'{self.name}<{self.version}>({self.type})' #--- # Properties #--- @property def name(self): """ get project name""" return self._toml['project']['name'] @property def path(self): """ get project path""" return self._path @property def description(self): """ get project description""" if 'description' in self._toml['project']: return self._toml['project']['description'] return '' @property def parent_path(self): """ Return project parent path """ return self._parent_path if self._parent_path else self._path @property def is_original(self): """ Return if the project is the target or a dependency""" return not self._parent_path @property def extra_conf(self): """ Return extra pacakge information """ return self._extra_conf @property def type(self): """ get project build rules""" if self._type: return self._type if 'type' not in self._toml['project']: return 'bin' self._type = self._toml['project']['type'] if self._type not in ['app', 'bin', 'lib', 'addin']: log.warn( f"{self.path}: project type '{self._type}' is unknown !" f" Handled like a 'bin' type project" ) return self._type @property def target_support(self): """ get supported target list""" if not self._target_support: self._target_support = [] if 'target' in self._toml['project']: self._target_support = self._toml['project']['target'] return self._target_support @property def version(self): """ get current package version""" return version_get(self.path) #--- # Methods #--- def get_dependencies(self, target=None): """ Get project dependency list for a particular board target @args > target (str) - target name @return > Return a list of dependencies information : [ { 'name' : , 'version' : , 'target' : , 'extra_conf' : }, ... ] """ # check mandatory target requirement if self.target_support: if not target or target not in self.target_support: log.warn(f"[{self.name}] target '{target}' not supported") return [] # help particular section dump def section_dep_fetch(section, target): if 'dependencies' not in section: return [] dep_list = [] for dep_name in section['dependencies']: extra = None if 'extra' in section and dep_name in section['extra']: extra = section['extra'][dep_name] pkg_target = target pkg_version = section['dependencies'][dep_name].split('@') if len(pkg_version) >= 2: pkg_target = pkg_version[1] dep_list.append({ 'name' : dep_name, 'version' : pkg_version[0], 'target' : pkg_target, 'extra_conf' : extra }) return dep_list # fetch dependencies information in common and target-specific section dep_list = section_dep_fetch(self._toml, target) if target and target in self._toml: dep_list += section_dep_fetch(self._toml[target], target) return dep_list def get_build_rules(self, target=None): """ Get project building rules @args > target (str) - target name @return > a dictionary with all available building step : { 'configure' : , 'build' : , 'install' : , 'uninstall' : , } """ def section_rules_fetch(section): if 'build' not in section: log.warn(f"[{self.name}] no building rules") return {} rules = {} for rule in section['build']: if rule in ['configure', 'build', 'install', 'uninstall']: rules[rule] = section['build'][rule] continue log.warn( f"[{self.name}] building rule '{rule}' doesn't exists" ) return rules # check if we need to fetch common rules or target-specific one if target: if self.target_support and target not in self.target_support: log.error(f"[{self.name}] not supported target '{target}'") return {} if target in self._toml: return section_rules_fetch(self._toml[target]) return section_rules_fetch(self._toml)