vxSDK/vxsdk/core/build/meta.py

289 lines
10 KiB
Python

"""
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) [<empty>] (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):
"""<property> get project name"""
return self._toml['project']['name']
@property
def path(self):
"""<property> get project path"""
return self._path
@property
def description(self):
"""<property> get project description"""
if 'description' in self._toml['project']:
return self._toml['project']['description']
return ''
@property
def parent_path(self):
"""<property> Return project parent path """
return self._parent_path if self._parent_path else self._path
@property
def is_original(self):
"""<property> Return if the project is the target or a dependency"""
return not self._parent_path
@property
def extra_conf(self):
"""<property> Return extra pacakge information """
return self._extra_conf
@property
def type(self):
"""<property> 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):
"""<property> 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):
"""<property> 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' : <dep name>,
'version' : <dep target version>,
'target' : <dep build target>,
'extra_conf' : <dep extra configuration>
},
...
]
"""
# 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' : <configure rule string>,
'build' : <build rule string>,
'install' : <install rule string>,
'uninstall' : <uninstall rule string>,
}
"""
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)