vxSDK/vxsdk/core/build/dependency.py

151 lines
3.8 KiB
Python

"""
Dependencies resolver
"""
import os
from core.logger import log
from core.build.meta import VxProjectMeta
import core.pkg
__all__ = [
'project_dependency_clone'
]
#---
# Internals
#---
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(parent_path, dep_info, pkg_info, prefix, dep_stack):
"""Clone all dependency and generate dependency graph
@args
> dep_graph (list) - list dependencies
> dep_parent_id (int) - parent index in `dep_graph`
> pkg_info (dict) - package information
> prefix (str) - prefix for package cloning
@return
> 0 if success, negative value otherwise
"""
# fetch info
dep_graph = dep_info[0]
dep_parent_id = dep_info[1]
# check circular dependency and update the stack
for dep in dep_stack:
if dep != pkg_info:
continue
log.error(f"circular dependency with '{pkg_info['name']}' detected")
return -1
dep_stack.append(pkg_info)
# try to clone the package
pkg_path = core.pkg.clone(
pkg_info['name'],
pkg_info['version'],
prefix
)
# check pacakge validity
# @todo
# Find a way to not be dependent of VxProjectMeta to avoid spaghetti code
target = pkg_info['target']
pkg_meta = VxProjectMeta(pkg_path, parent_path, pkg_info['extra_conf'])
if target not in pkg_meta.target_support:
log.error(f"[{pkg_meta.name}] target '{target}' not supported")
return -2
# generate dependency information
pkg_dep_id = __dep_graph_update(
dep_graph,
dep_parent_id,
{
'meta' : pkg_meta,
'target' : pkg_info['target']
}
)
for dep in pkg_meta.get_dependencies(target):
__recurs_clone(
parent_path,
(dep_graph, pkg_dep_id),
dep,
prefix,
dep_stack.copy()
)
return 0
#---
# Public
#---
def project_dependency_clone(pkg, target):
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
> target (str) - build target
@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.parent_path}/.vxsdk/dependencies"
if not os.path.exists(prefix):
os.makedirs(prefix)
# clone all dependencies and generate a DAG graph
# @note
# we need to manualy bootstrap the current project as a dependency to
# facilitate graph generation with a unified data structure
dep_graph = []
dep_origin_id = __dep_graph_update(
dep_graph,
-1,
{
'meta' : pkg,
'target' : target,
}
)
for dep in pkg.get_dependencies(target):
__recurs_clone(
pkg.parent_path,
(dep_graph, dep_origin_id),
dep,
prefix,
[]
)
# return the DAG
return dep_graph