feat: support public/interface dependencies in packer
This commit is contained in:
@@ -13,7 +13,7 @@ from re import Pattern, compile
|
||||
from .cli import Cli
|
||||
from .utils import Triple
|
||||
from .metadata import MetadataExtractor, Metadata
|
||||
from .renders.cmake import CMakeProperties, CMakeRender
|
||||
from .renders.cmake import CMakeDependency, CMakeProperties, CMakeRender
|
||||
from .renders.pkgconfig import PkgConfigProperties, PkgConfigRender
|
||||
|
||||
|
||||
@@ -241,6 +241,14 @@ def resolve_cmake_copy(ctx: ArtifactContext) -> Iterator[TextCopyInfo]:
|
||||
if metadata.cmake.target_name is not None:
|
||||
cmake_target_name = metadata.cmake.target_name
|
||||
|
||||
# gather declared dependencies (with metadata -> render conversion)
|
||||
cmake_dependencies: tuple[CMakeDependency, ...] = tuple()
|
||||
if metadata.cmake is not None and metadata.cmake.dependencies is not None:
|
||||
cmake_dependencies = tuple(
|
||||
CMakeDependency(dep.package, dep.target)
|
||||
for dep in metadata.cmake.dependencies
|
||||
)
|
||||
|
||||
# build cmake properties and render
|
||||
properties = CMakeProperties(
|
||||
cmake_namespace_name,
|
||||
@@ -248,6 +256,7 @@ def resolve_cmake_copy(ctx: ArtifactContext) -> Iterator[TextCopyInfo]:
|
||||
_generate_dll_artifact_name(target_name, target_triple),
|
||||
_generate_lib_artifact_name(target_name, target_triple),
|
||||
extractor.get_version(),
|
||||
cmake_dependencies,
|
||||
)
|
||||
render = CMakeRender(properties)
|
||||
# return infos
|
||||
@@ -291,8 +300,16 @@ def resolve_pkgconfig_copy(ctx: ArtifactContext) -> Iterator[TextCopyInfo]:
|
||||
if pkgconfig_description is None:
|
||||
pkgconfig_description = ""
|
||||
|
||||
pkgconfig_requires: tuple[str, ...] = tuple()
|
||||
if metadata.pkgconfig is not None and metadata.pkgconfig.requires is not None:
|
||||
pkgconfig_requires = metadata.pkgconfig.requires
|
||||
|
||||
properties = PkgConfigProperties(
|
||||
pkgconfig_name, pkgconfig_description, target_name, extractor.get_version()
|
||||
pkgconfig_name,
|
||||
pkgconfig_description,
|
||||
target_name,
|
||||
extractor.get_version(),
|
||||
pkgconfig_requires,
|
||||
)
|
||||
render = PkgConfigRender(properties)
|
||||
yield TextCopyInfo(render.render(), Path("lib", "pkgconfig", f"{pkgconfig_id}.pc"))
|
||||
|
||||
@@ -48,6 +48,35 @@ class MetadataHeader:
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MetadataCMakeDependency:
|
||||
"""One CMake dependency declared in the OMRF metadata.
|
||||
|
||||
Carries the ``find_dependency`` package name and the link target to wire
|
||||
into the imported target's ``INTERFACE_LINK_LIBRARIES``.
|
||||
"""
|
||||
|
||||
package: str
|
||||
"""Package name passed to ``find_dependency`` (e.g. ``ZLIB``)."""
|
||||
target: str
|
||||
"""Link target wired into ``INTERFACE_LINK_LIBRARIES`` (e.g. ``ZLIB::ZLIB``)."""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate that ``package`` and ``target`` are non-empty strings."""
|
||||
if self.package == "":
|
||||
raise ValueError("bad cmake dependency package")
|
||||
if self.target == "":
|
||||
raise ValueError("bad cmake dependency target")
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict[str, Any]) -> "MetadataCMakeDependency":
|
||||
"""Build a :class:`CMakeDependency` from its raw TOML table."""
|
||||
return MetadataCMakeDependency(
|
||||
package=dict_typed_get_required(d, "package", str),
|
||||
target=dict_typed_get_required(d, "target", str),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MetadataCMake:
|
||||
"""Optional ``[package.metadata.omrf.cmake]`` section."""
|
||||
@@ -56,6 +85,8 @@ class MetadataCMake:
|
||||
"""CMake target namespace, or ``None`` to fall back to the project name."""
|
||||
target_name: str | None
|
||||
"""CMake target name, or ``None`` to fall back to the project name."""
|
||||
dependencies: tuple[MetadataCMakeDependency, ...] | None
|
||||
"""Declared CMake dependencies, or ``None`` to no dependencies specified."""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate the namespace and target names when specified.
|
||||
@@ -72,9 +103,21 @@ class MetadataCMake:
|
||||
@staticmethod
|
||||
def from_dict(d: dict[str, Any]) -> "MetadataCMake":
|
||||
"""Build a :class:`MetadataCMake` from its raw TOML table."""
|
||||
raw_dependencies = dict_typed_get(d, "dependencies", list)
|
||||
if raw_dependencies is not None:
|
||||
dependencies_list: list[MetadataCMakeDependency] = []
|
||||
for i, item in enumerate(raw_dependencies):
|
||||
if not isinstance(item, dict):
|
||||
raise TypeError(f"dependencies[{i}] must be a table")
|
||||
dependencies_list.append(MetadataCMakeDependency.from_dict(item))
|
||||
dependencies = tuple(dependencies_list)
|
||||
else:
|
||||
dependencies = None
|
||||
|
||||
return MetadataCMake(
|
||||
namespace_name=dict_typed_get(d, "namespace_name", str),
|
||||
target_name=dict_typed_get(d, "target_name", str),
|
||||
dependencies=dependencies,
|
||||
)
|
||||
|
||||
|
||||
@@ -88,6 +131,8 @@ class MetadataPkgConfig:
|
||||
"""Human-readable package name, or ``None`` to fall back to the project name."""
|
||||
description: str | None
|
||||
"""Brief package description, or ``None`` to fall back to the project description."""
|
||||
requires: tuple[str, ...] | None
|
||||
"""Declared public pkg-config dependencies, or ``None`` to no dependencies specified."""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate the id, name and description when specified.
|
||||
@@ -103,14 +148,30 @@ class MetadataPkgConfig:
|
||||
self.description
|
||||
):
|
||||
raise ValueError("bad pkg-config description (has EOL chars)")
|
||||
if self.requires is not None:
|
||||
for req in self.requires:
|
||||
if req == "":
|
||||
raise ValueError("bad pkg-config require spec")
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict[str, Any]) -> "MetadataPkgConfig":
|
||||
"""Build a :class:`MetadataPkgConfig` from its raw TOML table."""
|
||||
raw_requires = dict_typed_get(d, "requires", list)
|
||||
if raw_requires is not None:
|
||||
requires_list: list[str] = []
|
||||
for i, item in enumerate(raw_requires):
|
||||
if not isinstance(item, str):
|
||||
raise TypeError(f"requires[{i}] must be a string")
|
||||
requires_list.append(item)
|
||||
requires = tuple(requires_list)
|
||||
else:
|
||||
requires = None
|
||||
|
||||
return MetadataPkgConfig(
|
||||
id=dict_typed_get(d, "id", str),
|
||||
name=dict_typed_get(d, "name", str),
|
||||
description=dict_typed_get(d, "description", str),
|
||||
requires=requires,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,27 @@ from . import common
|
||||
from .. import utils
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CMakeDependency:
|
||||
"""One CMake dependency consumed by the renderer.
|
||||
|
||||
Carries the ``find_dependency`` package name and the link target wired into
|
||||
the imported target's ``INTERFACE_LINK_LIBRARIES``.
|
||||
"""
|
||||
|
||||
package: str
|
||||
"""Package name passed to ``find_dependency`` (e.g. ``ZLIB``)."""
|
||||
target: str
|
||||
"""Link target wired into ``INTERFACE_LINK_LIBRARIES`` (e.g. ``ZLIB::ZLIB``)."""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate that ``package`` and ``target`` are non-empty strings."""
|
||||
if self.package == "":
|
||||
raise ValueError("bad cmake dependency package")
|
||||
if self.target == "":
|
||||
raise ValueError("bad cmake dependency target")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CMakeProperties:
|
||||
"""Inputs required to render the CMake package-configuration files.
|
||||
@@ -44,13 +65,16 @@ class CMakeProperties:
|
||||
|
||||
version: Version
|
||||
"""Version of the library. Must not carry a prerelease or build component."""
|
||||
dependencies: tuple[CMakeDependency, ...]
|
||||
"""CMake dependencies to ``find_dependency`` and link into the target
|
||||
(empty when none are declared)."""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate the fields after construction.
|
||||
|
||||
:raises ValueError: if the namespace or target name contains characters
|
||||
outside the CMake name pattern, an artifact file name is empty, or
|
||||
the version carries a prerelease or build component (unsupported by
|
||||
outside the CMake name pattern, an artifact file name is empty, or the
|
||||
version carries a prerelease or build component (unsupported by
|
||||
CMake config-version files).
|
||||
"""
|
||||
if not utils.is_good_name(self.namespace_name):
|
||||
@@ -99,6 +123,7 @@ class CMakeRender:
|
||||
"target": self.__properties.target_name,
|
||||
"artifact_dll": self.__properties.artifact_dll,
|
||||
"artifact_lib": self.__properties.artifact_lib,
|
||||
"dependencies": self.__properties.dependencies,
|
||||
}
|
||||
return self.__render.render("XXXConfig.cmake.jinja", payload)
|
||||
|
||||
|
||||
@@ -42,13 +42,14 @@ class PkgConfigProperties:
|
||||
|
||||
version: Version
|
||||
"""Version of the library. Must not carry a prerelease or build component."""
|
||||
requires: tuple[str, ...]
|
||||
"""Public pkg-config dependencies (empty when none are declared)."""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate the fields after construction.
|
||||
|
||||
:raises ValueError: if the name or description is blank, or the
|
||||
version carries a prerelease or build component (unsupported in
|
||||
pkg-config).
|
||||
:raises ValueError: if the name or description is blank, or the version
|
||||
carries a prerelease or build component (unsupported in pkg-config).
|
||||
"""
|
||||
if self.name == "" or not utils.is_good_sentence(self.name):
|
||||
raise ValueError("bad name (blank or has EOL chars) of pkg-config")
|
||||
@@ -89,5 +90,6 @@ class PkgConfigRender:
|
||||
"name": self.__properties.name,
|
||||
"description": self.__properties.description,
|
||||
"artifact": self.__properties.artifact,
|
||||
"requires": self.__properties.requires,
|
||||
}
|
||||
return self.__render.render("XXX.pc.jinja", payload)
|
||||
|
||||
@@ -10,5 +10,8 @@ includedir=${prefix}/include
|
||||
Name: {{ name }}
|
||||
Description: {{ description }}
|
||||
Version: {{ version }}
|
||||
{% if len(requires) != 0 -%}
|
||||
Requires: {{ requires | join(", ") }}
|
||||
{%- endif %}
|
||||
Libs: -L${libdir} -l{{ artifact }}
|
||||
Cflags: -I${includedir}
|
||||
|
||||
@@ -32,5 +32,16 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
{% if len(dependencies) != 0 -%}
|
||||
{# Pull in declared dependencies and wire them into the imported target -#}
|
||||
include(CMakeFindDependencyMacro)
|
||||
{% for dep in dependencies -%}
|
||||
find_dependency({{ dep.package }})
|
||||
{% endfor -%}
|
||||
set_target_properties({{ namespace }}::{{ target }} PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "{{ dependencies | map(attribute='target') | join(';') }}"
|
||||
)
|
||||
{%- endif %}
|
||||
|
||||
{# Cleanup temporary variables -#}
|
||||
set(_IMPORT_PREFIX)
|
||||
|
||||
Reference in New Issue
Block a user