157 lines
4.7 KiB
Python
157 lines
4.7 KiB
Python
import yaml
|
|
|
|
from pathlib import Path
|
|
|
|
from ..models import Access
|
|
from ..models import Accessor
|
|
from ..models import Dependency
|
|
from ..models import Destination
|
|
from ..models import Origination
|
|
from ..models import Output
|
|
from ..models import Project
|
|
from ..models import Target
|
|
from ..shell import execute
|
|
from .._config import Config
|
|
from ._except import NoProjectFoundError
|
|
from ._shared import projects_by_name_mapping
|
|
from ._shared import targets_by_name_mapping
|
|
from ._shared import tests_by_name_mapping
|
|
from ._shared import project_by_targets_mapping
|
|
|
|
|
|
def load(path: Path) -> Project:
|
|
if path in projects_by_name_mapping:
|
|
return projects_by_name_mapping[path]
|
|
|
|
fullpath = path / "chinookfile"
|
|
if not Path.exists(fullpath):
|
|
raise NoProjectFoundError(fullpath)
|
|
|
|
try:
|
|
with open(fullpath, 'r') as file:
|
|
yamlobj = yaml.load(file.read(), Loader=Project.yaml_loader)
|
|
project = Project.from_dict(yamlobj)
|
|
project.origination = Origination(path)
|
|
project.destination = Destination(path)
|
|
_collect_profiles(project)
|
|
_collect_required(project)
|
|
_collect_targets(project)
|
|
_collect_tests(project)
|
|
_flatten_targets(project)
|
|
_flatten_tests(project)
|
|
return project
|
|
except Exception as rethrowme:
|
|
raise rethrowme
|
|
|
|
def _collect_profiles(project: Project) -> None:
|
|
# TODO: Determine what profile is supposed to be active and load the targets
|
|
# so that they will be compiled.
|
|
for profile in project.profiles:
|
|
...
|
|
|
|
def _collect_required(project: Project) -> None:
|
|
for required in project.requires:
|
|
_clone_then_load_required(required)
|
|
|
|
def _collect_targets(project: Project) -> None:
|
|
for target in project.targets:
|
|
if target.name not in targets_by_name_mapping:
|
|
targets_by_name_mapping[target.name] = target
|
|
project_by_targets_mapping[target.name] = project
|
|
|
|
def _collect_tests(project: Project) -> None:
|
|
for test in project.tests:
|
|
if test.name not in tests_by_name_mapping:
|
|
tests_by_name_mapping[test.name] = test
|
|
|
|
def _flatten_targets(project: Project) -> None:
|
|
for target in project.targets:
|
|
_flatten_target(target)
|
|
target.incs.includes.add(Accessor(
|
|
level = Access.PUBLIC \
|
|
if target.name in project.exports \
|
|
else Access.PRIVATE,
|
|
value = project.origination.src("include")
|
|
))
|
|
target.incs.libraries.add(Accessor(
|
|
level = Access.PUBLIC,
|
|
value = project.destination.libpath
|
|
))
|
|
|
|
def _flatten_tests(project: Project) -> None:
|
|
for test in project.tests:
|
|
_flatten_target(test)
|
|
test.incs.includes.add(Accessor(
|
|
level = Access.PRIVATE,
|
|
value = project.origination.src("include")
|
|
))
|
|
test.incs.libraries.add(Accessor(
|
|
level = Access.PRIVATE,
|
|
value = project.destination.libpath
|
|
))
|
|
|
|
def _flatten_target(target: Target) -> None:
|
|
for dependency in target.deps:
|
|
if (parent:=targets_by_name_mapping.get(dependency.value)) == None:
|
|
raise ValueError(f"No such dependable target: {dependency}")
|
|
_inherit_target(target, parent)
|
|
|
|
def _inherit_target(target: Target, parent: Target) -> None:
|
|
if parent.name in tests_by_name_mapping:
|
|
raise ValueError("Cannot inherit tests")
|
|
|
|
_flatten_target(parent)
|
|
target.incs.includes.update([v for v in parent.incs.includes if v.level != Access.PRIVATE])
|
|
target.incs.libraries.update([v for v in parent.incs.libraries if v.level != Access.PRIVATE])
|
|
|
|
parent_project = project_by_targets_mapping.get(parent.name)
|
|
assert parent_project is not None, "Parent project not mapped!"
|
|
target.incs.includes.add(Accessor(
|
|
level = Access.PUBLIC,
|
|
value = parent_project.origination.src("include")
|
|
))
|
|
target.incs.libraries.add(Accessor(
|
|
level = Access.PUBLIC,
|
|
value = parent_project.destination.libpath
|
|
))
|
|
|
|
target.opts.update([v for v in parent.opts if v.level != Access.PRIVATE])
|
|
target.defs.update([v for v in parent.defs if v.level != Access.PRIVATE])
|
|
target.deps.update([v for v in parent.deps if v.level != Access.PRIVATE])
|
|
target.libs.update([v for v in parent.libs if v.level != Access.PRIVATE])
|
|
target.libs.add(Accessor(
|
|
level = Access.PUBLIC,
|
|
value = parent.name
|
|
))
|
|
|
|
if parent.type == Output.INT:
|
|
target.srcs.update([v for v in parent.srcs if v.level != Access.PRIVATE])
|
|
|
|
def _clone_then_load_required(required: Dependency) -> None:
|
|
reqpath = Config.config_directory / required.cid
|
|
if not Path.exists(reqpath):
|
|
_clone_required(required, reqpath)
|
|
load(reqpath)
|
|
|
|
def _clone_required(
|
|
required: Dependency,
|
|
reqpath: Path
|
|
) -> None:
|
|
# TODO: Clean this up.
|
|
def handle_success(retcode: int, cmdstr: str, stdout: str) -> None:
|
|
return
|
|
|
|
errstr = None
|
|
def handle_error(retcode: int, cmdstr: str, stderr: str) -> None:
|
|
global errstr
|
|
errstr = stderr
|
|
|
|
execute(
|
|
f"git clone -b {required.branch} --depth 1 {required.remote} {reqpath}",
|
|
handle_success,
|
|
handle_error
|
|
)
|
|
|
|
if errstr != None:
|
|
raise Exception(errstr)
|