388 lines
14 KiB
Python
388 lines
14 KiB
Python
"""
|
|
core.build.meta - vxsdk.toml document parser
|
|
"""
|
|
import os
|
|
import toml
|
|
|
|
from core.logger import log
|
|
from core.pkg.version import version_get
|
|
from core.build.env import env_config_evaluate, env_pkg_header
|
|
|
|
__all__ = [
|
|
'VxProjectMetaException',
|
|
'VxProjectMeta'
|
|
]
|
|
|
|
#---
|
|
# Public
|
|
#---
|
|
|
|
class VxProjectMetaException(Exception):
|
|
""" Simple exception handling """
|
|
|
|
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
|
|
get_env_config (dict) List all env configuration var
|
|
================================== =======================================
|
|
|
|
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, build_prefix='', target='', extra_conf=None):
|
|
""" Open and parse the vxsdk.toml file
|
|
|
|
@args
|
|
> path (str) : project path which we can find `vxsdk.toml` file
|
|
> build_prefix (str) : project build prefix
|
|
> 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
|
|
if 'project' not in self._toml or 'name' not in self._toml['project']:
|
|
raise VxProjectMetaException(
|
|
f"'{self._path}': missing project name information"
|
|
)
|
|
|
|
# check if the requested target is valid
|
|
target_support = []
|
|
if 'target' in self._toml['project']:
|
|
target_support = self._toml['project']['target']
|
|
if target_support:
|
|
error = 0
|
|
if not target:
|
|
log.error(
|
|
f"[{self.name}] no target specified, "
|
|
" you should specify a target between:"
|
|
)
|
|
error = -1
|
|
if target not in target_support:
|
|
log.error(
|
|
f"[{self.name}] target specified not supported, "
|
|
" you should specify a target between:"
|
|
)
|
|
error = -2
|
|
if error:
|
|
for target_name in target_support:
|
|
log.error(f" - {target_name}")
|
|
raise VxProjectMetaException(
|
|
f"[{self.name}] target {target} not supported"
|
|
)
|
|
|
|
# setup information and cache
|
|
self._type = ''
|
|
self._target = target
|
|
self._extra_conf = extra_conf
|
|
self._asset_prefix_list = []
|
|
|
|
# handle build prefix
|
|
self._build_prefix = build_prefix
|
|
if not self._build_prefix:
|
|
self._build_prefix = f"{self._path}/.vxsdk/{self._target}"
|
|
|
|
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 += ' - build:'.ljust(16) + f'{self.build_prefix}\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:<16}({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 build_prefix(self):
|
|
"""<property> Return project build path """
|
|
return self._build_prefix
|
|
|
|
@property
|
|
def is_original(self):
|
|
"""<property> Return if the project is the target or a dependency"""
|
|
return self._build_prefix == f"{self._path}/.vxsdk"
|
|
|
|
@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 version(self):
|
|
"""<property> get current package version"""
|
|
return version_get(self.path)
|
|
|
|
@property
|
|
def target(self):
|
|
"""<property> Return build target """
|
|
return self._target
|
|
|
|
#---
|
|
# Methods
|
|
#---
|
|
|
|
def get_dependencies(self):
|
|
""" Get project dependency list for a particular board target
|
|
|
|
@return
|
|
> Return a list of dependencies information : [
|
|
{
|
|
'name' : <dep name>,
|
|
'version' : <dep target version>,
|
|
'target' : <dep build target>,
|
|
'extra_conf' : <dep extra configuration>
|
|
},
|
|
...
|
|
]
|
|
"""
|
|
# 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, self._target)
|
|
if self._target and self._target in self._toml:
|
|
dep_list += section_dep_fetch(
|
|
self._toml[self._target],
|
|
self._target
|
|
)
|
|
return dep_list
|
|
|
|
def get_build_rules(self):
|
|
""" 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):
|
|
rules = {}
|
|
for rule in section:
|
|
if rule in ['configure', 'build', 'install', 'uninstall']:
|
|
rules[rule] = section[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 self._target \
|
|
and self._target in self._toml \
|
|
and 'build' in self._toml[self._target]:
|
|
return section_rules_fetch(self._toml[self._target]['build'])
|
|
if 'build' in self._toml:
|
|
return section_rules_fetch(self._toml['build'])
|
|
return {}
|
|
|
|
def get_env_config(self):
|
|
""" Get environment configuration
|
|
|
|
@args
|
|
> target (str) - project target
|
|
|
|
@return
|
|
> a dictionary with public and private environment variables
|
|
"""
|
|
def _key_update(env, key, value):
|
|
if key in env:
|
|
env[key] += value
|
|
return
|
|
env[key] = value
|
|
|
|
def _dump_section_env_conf(env_config, section):
|
|
for item in section.items():
|
|
if item[0].find('VXSDK_COMMON') == 0:
|
|
key_name = f"VXSDK_{item[0][13:]}"
|
|
_key_update(env_config['public'], key_name, item[1])
|
|
_key_update(env_config['private'], key_name, item[1])
|
|
elif item[0].find('VXSDK_PUBLIC') == 0:
|
|
key_name = f"VXSDK_{item[0][13:]}"
|
|
_key_update(env_config['public'], key_name, item[1])
|
|
elif item[0].find('VXSDK_PRIVATE') == 0:
|
|
key_name = f"VXSDK_{item[0][14:]}"
|
|
_key_update(env_config['private'], key_name, item[1])
|
|
else:
|
|
log.warn(f"Unable to convert {item[0]}, skipped")
|
|
|
|
env_config = {
|
|
'public' : {},
|
|
'private' : {}
|
|
}
|
|
if self._target \
|
|
and self._target in self._toml \
|
|
and 'env' in self._toml[self._target]:
|
|
_dump_section_env_conf(env_config, self._toml[self._target]['env'])
|
|
if 'env' in self._toml:
|
|
_dump_section_env_conf(env_config, self._toml['env'])
|
|
log.debug(f"[{self.name}] get_env_config() pre-eval = {env_config}")
|
|
|
|
# evaluate env content
|
|
# (todo/8DC1) : isolate the PKG ENV information (shouldn't be edited)
|
|
pkg_env_head = env_pkg_header(self)
|
|
env_config_evaluate(env_config['public'], pkg_env_head)
|
|
env_config_evaluate(env_config['private'], pkg_env_head)
|
|
|
|
# return env information
|
|
log.debug(f"[{self.name}] get_env_config() post-eval = {env_config}")
|
|
return env_config
|
|
|
|
def get_assets_prefix_list(self):
|
|
""" Fetch all assets prefix
|
|
|
|
@return
|
|
> a list with all assets path
|
|
"""
|
|
if self._asset_prefix_list:
|
|
return self._asset_prefix_list
|
|
if 'converter' not in self._toml:
|
|
return [f"{self._path}/assets"]
|
|
if 'assets_prefix' not in self._toml['converter']:
|
|
return [f"{self._path}/assets"]
|
|
self._asset_prefix_list = []
|
|
for asset in self._toml['converter']['assets_prefix']:
|
|
asset_prefix = f"{self._path}/{asset}"
|
|
if not os.path.exists(asset_prefix):
|
|
log.warn(f"{self.name}: assets path '{asset}' doesn't exists")
|
|
continue
|
|
self._asset_prefix_list.append(asset_prefix)
|
|
return self._asset_prefix_list
|