mirror of https://github.com/ARMmbed/mbed-os.git
134 lines
4.7 KiB
Python
134 lines
4.7 KiB
Python
#
|
|
# Copyright (c) 2020-2021 Arm Limited and Contributors. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
"""Objects for library reference handling."""
|
|
import logging
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Generator, List
|
|
|
|
from mbed_tools.project._internal import git_utils
|
|
from mbed_tools.project.exceptions import VersionControlError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass(frozen=True, order=True)
|
|
class MbedLibReference:
|
|
"""Metadata associated with an Mbed library.
|
|
|
|
An Mbed library is an external dependency of an MbedProgram. The MbedProgram is made aware of the library
|
|
dependency by the presence of a .lib file in the project tree, which we refer to as a library reference file. The
|
|
library reference file contains a URI where the dependency's source code can be fetched.
|
|
|
|
Attributes:
|
|
reference_file: Path to the .lib reference file for this library.
|
|
source_code_path: Path to the source code if it exists in the local project.
|
|
"""
|
|
|
|
reference_file: Path
|
|
source_code_path: Path
|
|
|
|
def is_resolved(self) -> bool:
|
|
"""Determines if the source code for this library is present in the source tree."""
|
|
return self.source_code_path.exists() and self.source_code_path.is_dir()
|
|
|
|
def get_git_reference(self) -> git_utils.GitReference:
|
|
"""Get the source code location from the library reference file.
|
|
|
|
Returns:
|
|
Data structure containing the contents of the library reference file.
|
|
"""
|
|
raw_ref = self.reference_file.read_text().strip()
|
|
url, sep, ref = raw_ref.partition("#")
|
|
|
|
if url.endswith("/"):
|
|
url = url[:-1]
|
|
|
|
return git_utils.GitReference(repo_url=url, ref=ref)
|
|
|
|
|
|
@dataclass
|
|
class LibraryReferences:
|
|
"""Manages library references in an MbedProgram."""
|
|
|
|
root: Path
|
|
ignore_paths: List[str]
|
|
|
|
def fetch(self) -> None:
|
|
"""Recursively clone all dependencies defined in .lib files."""
|
|
for lib in self.iter_unresolved():
|
|
git_ref = lib.get_git_reference()
|
|
logger.info(f"Resolving library reference {git_ref.repo_url}.")
|
|
_clone_at_ref(git_ref.repo_url, lib.source_code_path, git_ref.ref)
|
|
|
|
# Check if we find any new references after cloning dependencies.
|
|
if list(self.iter_unresolved()):
|
|
self.fetch()
|
|
|
|
def checkout(self, force: bool) -> None:
|
|
"""Check out all resolved libs to revision specified in .lib files."""
|
|
for lib in self.iter_resolved():
|
|
repo = git_utils.get_repo(lib.source_code_path)
|
|
git_ref = lib.get_git_reference()
|
|
|
|
if not git_ref.ref:
|
|
git_ref.ref = git_utils.get_default_branch(repo)
|
|
|
|
git_utils.fetch(repo, git_ref.ref)
|
|
git_utils.checkout(repo, "FETCH_HEAD", force=force)
|
|
|
|
def iter_all(self) -> Generator[MbedLibReference, None, None]:
|
|
"""Iterate all library references in the tree.
|
|
|
|
Yields:
|
|
Iterator to library reference.
|
|
"""
|
|
for lib in self.root.rglob("*.lib"):
|
|
if not self._in_ignore_path(lib):
|
|
yield MbedLibReference(lib, lib.with_suffix(""))
|
|
|
|
def iter_unresolved(self) -> Generator[MbedLibReference, None, None]:
|
|
"""Iterate all unresolved library references in the tree.
|
|
|
|
Yields:
|
|
Iterator to library reference.
|
|
"""
|
|
for lib in self.iter_all():
|
|
if not lib.is_resolved():
|
|
yield lib
|
|
|
|
def iter_resolved(self) -> Generator[MbedLibReference, None, None]:
|
|
"""Iterate all resolved library references in the tree.
|
|
|
|
Yields:
|
|
Iterator to library reference.
|
|
"""
|
|
for lib in self.iter_all():
|
|
if lib.is_resolved():
|
|
yield lib
|
|
|
|
def _in_ignore_path(self, lib_reference_path: Path) -> bool:
|
|
"""Check if a library reference is in a path we want to ignore."""
|
|
return any(p in lib_reference_path.parts for p in self.ignore_paths)
|
|
|
|
|
|
def _clone_at_ref(url: str, path: Path, ref: str) -> None:
|
|
if ref:
|
|
logger.info(f"Checking out revision {ref} for library {url}.")
|
|
try:
|
|
git_utils.clone(url, path, ref)
|
|
except VersionControlError:
|
|
# We weren't able to clone. Try again without the ref.
|
|
repo = git_utils.clone(url, path)
|
|
# We couldn't clone the ref and had to fall back to cloning
|
|
# just the default branch. Fetch the ref before checkout, so
|
|
# that we have it available locally.
|
|
logger.warning(f"No tag or branch with name {ref}. Fetching full repository.")
|
|
git_utils.fetch(repo, ref)
|
|
git_utils.checkout(repo, "FETCH_HEAD")
|
|
else:
|
|
git_utils.clone(url, path)
|