feat: add --format switch for runtime artifact family filtering

This commit is contained in:
yyc12345 committed 2026-10-15 17:43:33 +08:00
1 parent 9e41900596
commit d0a3d48c17
12 files changed
+103 -8

No files matched your search

+1
View File
@@ -41,6 +41,7 @@ The library ships a default CLI front-end that is meant to be a convenient start
- Output directory. - Output directory.
- SVG renderer selection. - SVG renderer selection.
- **`--only`** (short `-l`): restrict the build to the jobs whose names are given. It accepts one or more names and may be repeated. Jobs that the selected ones depend on are still built as needed, so it is a resource-saving way to rebuild or verify only a subset of an entire icon set. - **`--only`** (short `-l`): restrict the build to the jobs whose names are given. It accepts one or more names and may be repeated. Jobs that the selected ones depend on are still built as needed, so it is a resource-saving way to rebuild or verify only a subset of an entire icon set.
- **`--format`** (short `-f`): restrict the build to the given artifact formats (`freedesktop`, `ico`, `msix`, `icns`). It accepts one or more formats and may be repeated. Sinks and sources of non-selected formats skip themselves at run time, while intermediate jobs (like temporary artifact producers) are unaffected and always run. Defaults to all formats.
Because Assemblicon is a library, this CLI is not fixed. You can write your own entry point and extend (or replace) the standard argument parser with project-specific options. Because Assemblicon is a library, this CLI is not fixed. You can write your own entry point and extend (or replace) the standard argument parser with project-specific options.
+37
View File
@@ -4,6 +4,7 @@ from pathlib import Path
from argparse import ArgumentParser, Namespace from argparse import ArgumentParser, Namespace
from dataclasses import dataclass from dataclasses import dataclass
from .lowlevel.artrdr import SvgRenderKind from .lowlevel.artrdr import SvgRenderKind
from .highlevel.address import Family
class CliSvgRenderKind(enum.StrEnum): class CliSvgRenderKind(enum.StrEnum):
@@ -20,6 +21,26 @@ class CliSvgRenderKind(enum.StrEnum):
raise ValueError(f"Unknown CLI-specific SVG render kind: {self}") raise ValueError(f"Unknown CLI-specific SVG render kind: {self}")
class CliArtifactFormat(enum.StrEnum):
Freedesktop = "freedesktop"
Ico = "ico"
Msix = "msix"
Icns = "icns"
def to_assemblicon_type(self) -> Family:
match self:
case CliArtifactFormat.Freedesktop:
return Family.Freedesktop
case CliArtifactFormat.Ico:
return Family.Ico
case CliArtifactFormat.Msix:
return Family.Msix
case CliArtifactFormat.Icns:
return Family.Icns
case _:
raise ValueError(f"Unknown CLI-specific artifact format: {self}")
@dataclass(frozen=True) @dataclass(frozen=True)
class StdOpts: class StdOpts:
"""Assemblicon standard command line options""" """Assemblicon standard command line options"""
@@ -32,6 +53,11 @@ class StdOpts:
"""The list holding all name of jobs for building.""" """The list holding all name of jobs for building."""
svg_render: CliSvgRenderKind svg_render: CliSvgRenderKind
"""The render kind for SVG file.""" """The render kind for SVG file."""
format_selection: Optional[list[CliArtifactFormat]]
"""
The list holding all formats of artifacts for building.
``None`` for building artifacts of all formats.
"""
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -98,6 +124,16 @@ def create_argparser(setup: StdOptsSetup) -> ArgumentParser:
help="The render used for rendering SVG into PNG.", help="The render used for rendering SVG into PNG.",
metavar="RENDER", metavar="RENDER",
) )
parser.add_argument(
"-f",
"--format",
dest="format_selection",
action="extend",
nargs="+",
type=CliArtifactFormat,
help="Only build artifacts of given formats. Repeatable. Default all formats.",
metavar="FORMAT",
)
return parser return parser
@@ -107,4 +143,5 @@ def parse_standard_opts(cfg: StdOptsSetup, ns: Namespace) -> StdOpts:
output_directory=ns.output if cfg.require_output else None, output_directory=ns.output if cfg.require_output else None,
selection=ns.selection, selection=ns.selection,
svg_render=ns.svg_render, svg_render=ns.svg_render,
format_selection=ns.format_selection,
) )
+13
View File
@@ -1,6 +1,19 @@
import enum import enum
class Family(enum.StrEnum):
"""The family of an output artifact, used by runtime family filtering."""
Freedesktop = "freedesktop"
"""FreeDesktop compatible PNG icons."""
Ico = "ico"
"""Windows ICO icons."""
Msix = "msix"
"""Windows MSIX package icons."""
Icns = "icns"
"""macOS ICNS icons."""
class AddressDomain(enum.IntEnum): class AddressDomain(enum.IntEnum):
Input = enum.auto() Input = enum.auto()
Temporary = enum.auto() Temporary = enum.auto()
+20
View File
@@ -7,6 +7,7 @@ from pathlib import Path
from typing import TypeVar, Generic, Callable from typing import TypeVar, Generic, Callable
from ..lowlevel.environment import Prologue as LowPrologue, Environment as LowEnv from ..lowlevel.environment import Prologue as LowPrologue, Environment as LowEnv
from ..lowlevel.artrdr import SvgRenderKind from ..lowlevel.artrdr import SvgRenderKind
from .address import Family
from .utils import ( from .utils import (
FdContext, FdContext,
IcoCategory, IcoCategory,
@@ -53,6 +54,16 @@ class Prologue(LowPrologue):
The only argument in function is the path to user temporary directory. The only argument in function is the path to user temporary directory.
``None`` for no extra initializer. ``None`` for no extra initializer.
""" """
allowed_families: set[Family] | None = None
"""
The set of artifact families allowed to be generated.
Output-family sinks and sources check this at run time and silently
skip themselves when their family is not listed, which provides a
resource-saving way to only produce a subset of output families.
Intermediate artifacts (like temporary ones) have no family and are
always produced. ``None`` for no restriction, and an empty set means
no output family is allowed.
"""
def __post_init__(self) -> None: def __post_init__(self) -> None:
super().__post_init__() super().__post_init__()
@@ -70,6 +81,15 @@ class Prologue(LowPrologue):
for targetsize in self.msix_ext_targetsizes: for targetsize in self.msix_ext_targetsizes:
check_msix_targetsize(targetsize) check_msix_targetsize(targetsize)
def allow_family(self, family: Family) -> bool:
"""
Check whether given artifact family is allowed to be generated.
:param family: The family to be checked.
:returns: ``True`` if this family is allowed to be generated.
"""
return self.allowed_families is None or family in self.allowed_families
_TEMP_USER_DIR: str = "user" _TEMP_USER_DIR: str = "user"
_TEMP_LIB_DIR: str = "assemblicon" _TEMP_LIB_DIR: str = "assemblicon"
+4 -1
View File
@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from .common import Sink from .common import Sink
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..utils import Artifact, FdContext, check_vart from ..utils import Artifact, FdContext, check_vart
from ...lowlevel import artio, artrdr from ...lowlevel import artio, artrdr
@@ -26,6 +26,9 @@ class FdArtSink(Sink):
return Address(AddressDomain.PosixFdOutput, str(self.__kind), self.__name) return Address(AddressDomain.PosixFdOutput, str(self.__kind), self.__name)
def push(self, env: Environment, art: Artifact) -> None: def push(self, env: Environment, art: Artifact) -> None:
if not env.prologue.allow_family(Family.Freedesktop):
return
LOGGER.info( LOGGER.info(
'Saving FreeDesktop artifact with kind "%s" and name "%s".', 'Saving FreeDesktop artifact with kind "%s" and name "%s".',
self.__kind, self.__kind,
@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from .common import Sink from .common import Sink
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..utils import Artifact, IcnsCategory, check_vart from ..utils import Artifact, IcnsCategory, check_vart
from ...lowlevel import artrdr from ...lowlevel import artrdr
@@ -26,6 +26,9 @@ class IcnsArtSink(Sink):
return Address(AddressDomain.MacIcnsOutput, str(self.__kind), self.__name) return Address(AddressDomain.MacIcnsOutput, str(self.__kind), self.__name)
def push(self, env: Environment, art: Artifact) -> None: def push(self, env: Environment, art: Artifact) -> None:
if not env.prologue.allow_family(Family.Icns):
return
LOGGER.info( LOGGER.info(
'Saving macOS ICNS artifact with kind "%s" and name "%s".', 'Saving macOS ICNS artifact with kind "%s" and name "%s".',
self.__kind, self.__kind,
@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from .common import Sink from .common import Sink
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..utils import Artifact, IcoCategory, check_vart from ..utils import Artifact, IcoCategory, check_vart
from ...lowlevel import artrdr from ...lowlevel import artrdr
@@ -26,6 +26,9 @@ class IcoArtSink(Sink):
return Address(AddressDomain.WinIcoOutput, str(self.__kind), self.__name) return Address(AddressDomain.WinIcoOutput, str(self.__kind), self.__name)
def push(self, env: Environment, art: Artifact) -> None: def push(self, env: Environment, art: Artifact) -> None:
if not env.prologue.allow_family(Family.Ico):
return
LOGGER.info( LOGGER.info(
'Saving Windows ICO artifact with kind "%s" and name "%s".', 'Saving Windows ICO artifact with kind "%s" and name "%s".',
self.__kind, self.__kind,
@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from .common import Sink from .common import Sink
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..utils import ( from ..utils import (
Artifact, Artifact,
@@ -33,6 +33,9 @@ class MsixArtSink(Sink):
return Address(AddressDomain.WinMsixOutput, str(self.__kind), self.__name) return Address(AddressDomain.WinMsixOutput, str(self.__kind), self.__name)
def push(self, env: Environment, art: Artifact) -> None: def push(self, env: Environment, art: Artifact) -> None:
if not env.prologue.allow_family(Family.Msix):
return
LOGGER.info( LOGGER.info(
'Saving Windows MSIX artifact with kind "%s" and name "%s".', 'Saving Windows MSIX artifact with kind "%s" and name "%s".',
self.__kind, self.__kind,
@@ -1,5 +1,5 @@
from .common import Source from .common import Source
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..sink.fdart_sink import FdArtSink from ..sink.fdart_sink import FdArtSink
from ..utils import FdContext from ..utils import FdContext
@@ -20,6 +20,9 @@ class FdArtSource(Source):
return Address(AddressDomain.PosixFdOutput, str(self.__kind), self.__name) return Address(AddressDomain.PosixFdOutput, str(self.__kind), self.__name)
def link_to(self, env: Environment, fd_sink: FdArtSink) -> None: def link_to(self, env: Environment, fd_sink: FdArtSink) -> None:
if not env.prologue.allow_family(Family.Freedesktop):
return
sink_hint = fd_sink.push_hint() sink_hint = fd_sink.push_hint()
LOGGER.info( LOGGER.info(
'Duplicating FreeDesktop artifact from kind "%s" name "%s" to kind "%s" name "%s".', 'Duplicating FreeDesktop artifact from kind "%s" name "%s" to kind "%s" name "%s".',
@@ -1,5 +1,5 @@
from .common import Source from .common import Source
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..sink.icnsart_sink import IcnsArtSink from ..sink.icnsart_sink import IcnsArtSink
from ..utils import IcnsCategory from ..utils import IcnsCategory
@@ -20,6 +20,9 @@ class IcnsArtSource(Source):
return Address(AddressDomain.MacIcnsOutput, str(self.__kind), self.__name) return Address(AddressDomain.MacIcnsOutput, str(self.__kind), self.__name)
def link_to(self, env: Environment, icns_sink: IcnsArtSink) -> None: def link_to(self, env: Environment, icns_sink: IcnsArtSink) -> None:
if not env.prologue.allow_family(Family.Icns):
return
sink_hint = icns_sink.push_hint() sink_hint = icns_sink.push_hint()
LOGGER.info( LOGGER.info(
'Duplicating macOS ICNS artifact from kind "%s" name "%s" to kind "%s" name "%s".', 'Duplicating macOS ICNS artifact from kind "%s" name "%s" to kind "%s" name "%s".',
@@ -1,5 +1,5 @@
from .common import Source from .common import Source
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..sink.icoart_sink import IcoArtSink from ..sink.icoart_sink import IcoArtSink
from ..utils import IcoCategory from ..utils import IcoCategory
@@ -20,6 +20,9 @@ class IcoArtSource(Source):
return Address(AddressDomain.WinIcoOutput, str(self.__kind), self.__name) return Address(AddressDomain.WinIcoOutput, str(self.__kind), self.__name)
def link_to(self, env: Environment, ico_sink: IcoArtSink) -> None: def link_to(self, env: Environment, ico_sink: IcoArtSink) -> None:
if not env.prologue.allow_family(Family.Ico):
return
sink_hint = ico_sink.push_hint() sink_hint = ico_sink.push_hint()
LOGGER.info( LOGGER.info(
'Duplicating Windows ICO artifact from kind "%s" name "%s" to kind "%s" name "%s".', 'Duplicating Windows ICO artifact from kind "%s" name "%s" to kind "%s" name "%s".',
@@ -1,5 +1,5 @@
from .common import Source from .common import Source
from ..address import Address, AddressDomain from ..address import Address, AddressDomain, Family
from ..environment import Environment from ..environment import Environment
from ..sink.msixart_sink import MsixArtSink, MsixArtSinkHint from ..sink.msixart_sink import MsixArtSink, MsixArtSinkHint
from ..utils import MsixCategory, MsixAppLogoKind, MsixAppPlatedKind from ..utils import MsixCategory, MsixAppLogoKind, MsixAppPlatedKind
@@ -20,6 +20,9 @@ class MsixArtSource(Source):
return Address(AddressDomain.WinMsixOutput, str(self.__kind), self.__name) return Address(AddressDomain.WinMsixOutput, str(self.__kind), self.__name)
def link_to(self, env: Environment, msix_sink: MsixArtSink) -> None: def link_to(self, env: Environment, msix_sink: MsixArtSink) -> None:
if not env.prologue.allow_family(Family.Msix):
return
sink_hint = msix_sink.push_hint() sink_hint = msix_sink.push_hint()
LOGGER.info( LOGGER.info(
'Duplicating Windows MSIX artifact from kind "%s" name "%s" to kind "%s" name "%s".', 'Duplicating Windows MSIX artifact from kind "%s" name "%s" to kind "%s" name "%s".',