""" 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' : '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