vxSDK/vxsdk/core/build/dependency.py

174 lines
4.8 KiB
Python

"""
core.build.dependency - Dependencies resolver
"""
import os
import sys
import core.pkg
from core.logger import log
from core.build.meta import VxProjectMeta
from core.build.env import env_config_merge, env_pkg_configure
__all__ = [
'dependency_project_clone'
]
#---
# Internals
#---
## DAG struct handling
def _dep_graph_update(dep_graph, dep_parent_id, pkg_info):
"""
Update dependency graph
"""
# inject new pkg information and gues its ID
dep_id = len(dep_graph)
dep_graph.append({
'info' : pkg_info,
'completed' : False,
'dependencies' : []
})
# update internal parent ID
if dep_parent_id >= 0:
dep_graph[dep_parent_id]['dependencies'].append(dep_id)
return dep_id
def _recurs_clone(build_prefix, dep_info, pkg_info, prefix):
"""Clone all dependency and generate dependency graph
@args
> build_prefix (str) - build prefix information
> dep_info (list) - contains dependencies list, stack and parent ID
> pkg_info (dict) - package information
> prefix (str) - prefix for package cloning
@return
> The current public env of the package
"""
# fetch info
dep_graph, dep_parent_id, dep_stack = dep_info
# check circular dependency error and update the stack
for dep in dep_stack:
if dep != pkg_info:
continue
log.error(f"circular dependency with '{pkg_info['name']}' detected")
sys.exit(84)
dep_stack.append(pkg_info)
# try to clone the package
pkg_path = core.pkg.clone(
pkg_info['name'],
pkg_info['version'],
f"{prefix}/"
)
if not pkg_path:
log.error(
'unable to clone the dependency '
f"{pkg_info['name']}@{pkg_info['version']}"
)
sys.exit(84)
log.debug(f"pkg path = {pkg_path}")
# check pacakge validity
pkg_meta = VxProjectMeta(
pkg_path,
build_prefix,
pkg_info['target'],
pkg_info['extra_conf']
)
# generate dependency information
pkg_env_config = pkg_meta.get_env_config()
pkg_depinfo = {
'meta' : pkg_meta,
'target' : pkg_info['target'],
'env' : None
}
pkg_dep_id = _dep_graph_update(dep_graph, dep_parent_id, pkg_depinfo)
# Handle dependencies and update the ENV information
dep_env_commit = {}
for dep in pkg_meta.get_dependencies():
dep_env_commit[dep['name']] = _recurs_clone(
build_prefix,
(dep_graph, pkg_dep_id, dep_stack.copy()),
dep,
prefix
)
# Generate env configuration and return the "current" package env
# configuration
log.debug(f"[{pkg_meta.name}] dep pre-env = {pkg_env_config['public']}")
pkg_depinfo['env'] = env_pkg_configure(
pkg_meta,
pkg_env_config['private'],
dep_env_commit
)
env_config_merge(pkg_env_config['public'], dep_env_commit)
log.debug(f"[{pkg_meta.name}] dep post-env = {pkg_env_config['public']}")
return pkg_env_config['public']
#---
# Public
#---
def dependency_project_clone(pkg):
r""" Clone dependencies of package and generate a DAG graph
This function will clone all dependency of a package optimised for a
particular target a `Direct Acyclic Graph` (DAG) graph will be generated to
facilitate project compilation later.
@args
> pkg (dict) - first pacakge information
@return
> A list with package and dependency information : [
{
'meta' : <:obj:VxProjectMeta>
'extra_conf' : <extra configuration information>
'target' : build target
},
...
]
"""
# create the package "storage" folder
# @note
# All dependencies will be cloned in "global" path, only symbolic link will
# be generated here
prefix = f"{pkg.build_prefix}/dependencies/"
if not os.path.exists(prefix):
os.makedirs(prefix)
# generate the root DAG node
dep_graph = []
pkg_env_config = pkg.get_env_config()
pkg_depinfo = {'meta' : pkg, 'target' : pkg.target, 'env' : None}
dep_origin_id = _dep_graph_update(dep_graph, -1, pkg_depinfo)
# clone all dependencies and update the DAG
# @note
# we need to manualy bootstrap the current project as a dependency to
# facilitate graph generation with a unified data structure
dep_env_commit = {}
for dep in pkg.get_dependencies():
dep_env_commit[dep['name']] = _recurs_clone(
pkg.build_prefix,
(dep_graph, dep_origin_id, []),
dep,
prefix
)
# generate package env configuration
env = env_pkg_configure(pkg, pkg_env_config['private'], dep_env_commit)
log.debug(f"[{pkg.name}] generated env = {env}")
pkg_depinfo['env'] = env
# return the DAG
return dep_graph