Compare commits
1 Commits
v0.7.0
...
ad718b6548
Author | SHA1 | Date | |
---|---|---|---|
ad718b6548
|
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,38 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
## [0.7.0](https://git.0xmax42.io/maxp/hdlbuild/compare/v0.6.1..v0.7.0) - 2025-07-17
|
|
||||||
|
|
||||||
### 🚀 Features
|
|
||||||
|
|
||||||
- *(version)* Bump version to 0.7.0 - ([8adaa91](https://git.0xmax42.io/maxp/hdlbuild/commit/8adaa916ff4e736e9da707c232d6f57b788e57e8))
|
|
||||||
- *(cli)* Add template generation commands - ([af0477f](https://git.0xmax42.io/maxp/hdlbuild/commit/af0477f8e74a9471c3e7d36877069592e41f651c))
|
|
||||||
|
|
||||||
## [0.6.1] - 2025-07-16
|
|
||||||
|
|
||||||
### 🚀 Features
|
|
||||||
|
|
||||||
- *(ci)* Add workflows for nightly builds and releases - ([da387f2](https://git.0xmax42.io/maxp/hdlbuild/commit/da387f2ee602390d616c79bf4057ccf941e21462))
|
|
||||||
|
|
||||||
### 🚜 Refactor
|
|
||||||
|
|
||||||
- Use typer for CLI argument parsing - ([6ca389d](https://git.0xmax42.io/maxp/hdlbuild/commit/6ca389d5cbbeff53faab9d61376a8c77ed097b6c))
|
|
||||||
- Improves project configuration - ([175bf48](https://git.0xmax42.io/maxp/hdlbuild/commit/175bf4882a8f172ee536d726b31136690572be36))
|
|
||||||
|
|
||||||
### 📚 Documentation
|
|
||||||
|
|
||||||
- *(readme)* Expand README with detailed usage and setup - ([fa7e738](https://git.0xmax42.io/maxp/hdlbuild/commit/fa7e738b7eade5a627218741a6fb4bd1617f7801))
|
|
||||||
|
|
||||||
### 🎨 Styling
|
|
||||||
|
|
||||||
- *(pyproject)* Simplify include array formatting - ([08679c2](https://git.0xmax42.io/maxp/hdlbuild/commit/08679c2680b49119e0414688a80e8dc2659236b4))
|
|
||||||
- Updates VS Code editor color scheme - ([1d7bc19](https://git.0xmax42.io/maxp/hdlbuild/commit/1d7bc1996522ab54970348b5118ad319849a6a1f))
|
|
||||||
|
|
||||||
### ⚙️ Miscellaneous Tasks
|
|
||||||
|
|
||||||
- *(config)* Add git-cliff configuration file - ([dde2363](https://git.0xmax42.io/maxp/hdlbuild/commit/dde2363ad7dd2fd2d48c6154e3b88c1c4a6867fd))
|
|
||||||
- Updates project version to 0.6.0 - ([7b6f9ef](https://git.0xmax42.io/maxp/hdlbuild/commit/7b6f9ef2240864b103903e79c895a76db59c14fa))
|
|
||||||
- Remove build-deb.yml workflow file - ([0d26c42](https://git.0xmax42.io/maxp/hdlbuild/commit/0d26c42f8ae419d509aa47d3f7a23bfdd08cf79b))
|
|
||||||
|
|
||||||
|
|
104
cliff.toml
104
cliff.toml
@@ -1,104 +0,0 @@
|
|||||||
# CLIFF_VERSION=2.8.0
|
|
||||||
# git-cliff ~ default configuration file
|
|
||||||
# https://git-cliff.org/docs/configuration
|
|
||||||
#
|
|
||||||
# Lines starting with "#" are comments.
|
|
||||||
# Configuration options are organized into tables and keys.
|
|
||||||
# See documentation for more information on available options.
|
|
||||||
[remote.gitea]
|
|
||||||
owner = "maxp"
|
|
||||||
repo = "hdlbuild"
|
|
||||||
|
|
||||||
[changelog]
|
|
||||||
# postprocessors
|
|
||||||
postprocessors = [
|
|
||||||
{ pattern = '<GITEA_URL>', replace = "https://git.0xmax42.io" }, # replace gitea url
|
|
||||||
]
|
|
||||||
|
|
||||||
# template for the changelog header
|
|
||||||
header = """
|
|
||||||
# Changelog\n
|
|
||||||
All notable changes to this project will be documented in this file.\n
|
|
||||||
"""
|
|
||||||
# template for the changelog body
|
|
||||||
# https://keats.github.io/tera/docs/#introduction
|
|
||||||
body = """
|
|
||||||
{%- macro remote_url() -%}
|
|
||||||
<GITEA_URL>/{{ remote.gitea.owner }}/{{ remote.gitea.repo }}
|
|
||||||
{%- endmacro -%}
|
|
||||||
|
|
||||||
{% if version %}\
|
|
||||||
{% if previous.version %}\
|
|
||||||
## [{{ version | trim_start_matches(pat="v") }}]\
|
|
||||||
({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
|
|
||||||
{% else %}\
|
|
||||||
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
|
||||||
{% endif %}\
|
|
||||||
{% else %}\
|
|
||||||
## [unreleased]
|
|
||||||
{% endif %}\
|
|
||||||
{% for group, commits in commits | group_by(attribute="group") %}
|
|
||||||
### {{ group | striptags | trim | upper_first }}
|
|
||||||
{% for commit in commits %}
|
|
||||||
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
|
|
||||||
{% if commit.breaking %}[**breaking**] {% endif %}\
|
|
||||||
{{ commit.message | upper_first }} - \
|
|
||||||
([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}\n
|
|
||||||
"""
|
|
||||||
# template for the changelog footer
|
|
||||||
footer = """
|
|
||||||
|
|
||||||
"""
|
|
||||||
# remove the leading and trailing s
|
|
||||||
trim = true
|
|
||||||
|
|
||||||
# render body even when there are no releases to process
|
|
||||||
# render_always = true
|
|
||||||
# output file path
|
|
||||||
# output = "test.md"
|
|
||||||
|
|
||||||
[git]
|
|
||||||
# parse the commits based on https://www.conventionalcommits.org
|
|
||||||
conventional_commits = true
|
|
||||||
# filter out the commits that are not conventional
|
|
||||||
filter_unconventional = true
|
|
||||||
# process each line of a commit as an individual commit
|
|
||||||
split_commits = false
|
|
||||||
# regex for preprocessing the commit messages
|
|
||||||
commit_preprocessors = [
|
|
||||||
# Replace issue numbers
|
|
||||||
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
|
|
||||||
# Check spelling of the commit with https://github.com/crate-ci/typos
|
|
||||||
# If the spelling is incorrect, it will be automatically fixed.
|
|
||||||
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
|
|
||||||
]
|
|
||||||
# regex for parsing and grouping commits
|
|
||||||
commit_parsers = [
|
|
||||||
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
|
|
||||||
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
|
|
||||||
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
|
|
||||||
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
|
|
||||||
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
|
|
||||||
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
|
|
||||||
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
|
|
||||||
{ message = "^chore\\(changelog\\)", skip = true },
|
|
||||||
{ message = "^chore\\(version\\)", skip = true },
|
|
||||||
{ message = "^chore\\(release\\): prepare for", skip = true },
|
|
||||||
{ message = "^chore\\(deps.*\\)", skip = true },
|
|
||||||
{ message = "^chore\\(pr\\)", skip = true },
|
|
||||||
{ message = "^chore\\(pull\\)", skip = true },
|
|
||||||
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
|
|
||||||
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
|
|
||||||
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
|
|
||||||
{ message = ".*", group = "<!-- 10 -->💼 Other" },
|
|
||||||
]
|
|
||||||
# Regex to select git tags that represent releases.
|
|
||||||
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
|
|
||||||
# filter out the commits that are not matched by commit parsers
|
|
||||||
filter_commits = false
|
|
||||||
# sort the tags topologically
|
|
||||||
topo_order = false
|
|
||||||
# sort the commits inside sections by oldest/newest order
|
|
||||||
sort_commits = "newest"
|
|
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "hdlbuild"
|
name = "hdlbuild"
|
||||||
version = "0.7.0"
|
version = "0.6.1"
|
||||||
description = "Flexible FPGA Build System"
|
description = "Flexible FPGA Build System"
|
||||||
authors = ["0xMax42 <Mail@0xMax42.io>"]
|
authors = ["0xMax42 <Mail@0xMax42.io>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -18,7 +18,6 @@ pydantic = "^2.11.3"
|
|||||||
rich = "^14.0.0"
|
rich = "^14.0.0"
|
||||||
gitpython = "^3.1.44"
|
gitpython = "^3.1.44"
|
||||||
typer = "^0.16.0"
|
typer = "^0.16.0"
|
||||||
jinja2 = "^3.1.6"
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
twine = "^6.1.0"
|
twine = "^6.1.0"
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import typer
|
import typer
|
||||||
from importlib.metadata import version, PackageNotFoundError
|
from importlib.metadata import version, PackageNotFoundError
|
||||||
|
|
||||||
from hdlbuild.commands.gen import cli as gen_cli
|
|
||||||
from hdlbuild.commands.build import cli as build_cli
|
from hdlbuild.commands.build import cli as build_cli
|
||||||
from hdlbuild.commands.clean import cli as clean_cli
|
from hdlbuild.commands.clean import cli as clean_cli
|
||||||
from hdlbuild.commands.dep import cli as dep_cli
|
from hdlbuild.commands.dep import cli as dep_cli
|
||||||
@@ -19,12 +18,12 @@ app = typer.Typer(
|
|||||||
help=f"hdlbuild v{get_version()} – Build‑Management for FPGA projects"
|
help=f"hdlbuild v{get_version()} – Build‑Management for FPGA projects"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Unter‑Kommandos registrieren (entspricht add_subparsers)
|
||||||
app.add_typer(build_cli, name="build", help="Build the project")
|
app.add_typer(build_cli, name="build", help="Build the project")
|
||||||
app.add_typer(clean_cli, name="clean", help="Clean build artifacts")
|
app.add_typer(clean_cli, name="clean", help="Clean build artifacts")
|
||||||
app.add_typer(dep_cli, name="dep", help="Resolve dependencies")
|
app.add_typer(dep_cli, name="dep", help="Resolve dependencies")
|
||||||
app.add_typer(test_cli, name="test", help="Run simulations/testbenches")
|
app.add_typer(test_cli, name="test", help="Run simulations/testbenches")
|
||||||
app.add_typer(init_cli, name="init", help="Initialize project")
|
app.add_typer(init_cli, name="init", help="Initialize project")
|
||||||
app.add_typer(gen_cli, name="gen", help="Generate HDL files from templates")
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app()
|
app()
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import typer
|
|
||||||
|
|
||||||
from hdlbuild.generate.template_generator import TemplateGenerator
|
|
||||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
|
||||||
from hdlbuild.utils.project_loader import load_project_config
|
|
||||||
|
|
||||||
cli = typer.Typer(rich_help_panel="🧬 Template Commands")
|
|
||||||
|
|
||||||
@cli.command("list")
|
|
||||||
def list_templates() -> None:
|
|
||||||
"""
|
|
||||||
List all available template names from *project.yml*.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hdlbuild gen list
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
console = ConsoleUtils("hdlbuild")
|
|
||||||
project = load_project_config()
|
|
||||||
TemplateGenerator.list_templates(project, console)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.callback(invoke_without_command=True)
|
|
||||||
def gen(
|
|
||||||
ctx: typer.Context,
|
|
||||||
name: str = typer.Option(
|
|
||||||
None,
|
|
||||||
"--name",
|
|
||||||
"-n",
|
|
||||||
help="Name of the template to generate (from project.yml)",
|
|
||||||
show_default=False,
|
|
||||||
),
|
|
||||||
dry_run: bool = typer.Option(
|
|
||||||
False,
|
|
||||||
"--dry-run",
|
|
||||||
help="Only show the output without writing file",
|
|
||||||
),
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Render HDL files from Jinja2 templates.
|
|
||||||
|
|
||||||
* `hdlbuild gen` → render all templates
|
|
||||||
* `hdlbuild gen <name>` → render a specific template
|
|
||||||
* `hdlbuild gen <name> --dry-run` → only show output without saving
|
|
||||||
"""
|
|
||||||
console = ConsoleUtils("hdlbuild")
|
|
||||||
project = load_project_config()
|
|
||||||
|
|
||||||
# Only executed when no subcommand (e.g., "list") is active.
|
|
||||||
if ctx.invoked_subcommand is None:
|
|
||||||
TemplateGenerator.generate(project, name, dry_run, console)
|
|
@@ -1,120 +0,0 @@
|
|||||||
"""
|
|
||||||
hdlbuild.generate.template_generator
|
|
||||||
====================================
|
|
||||||
|
|
||||||
Enthält die Klasse :class:`TemplateGenerator`, die das Auflisten und Rendern
|
|
||||||
von in *project.yml* definierten Jinja2-Templates kapselt.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
|
|
||||||
from hdlbuild.models.templates import TemplateInstance
|
|
||||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateGenerator:
|
|
||||||
"""
|
|
||||||
Hilfsklasse zum Auflisten und Rendern der im Projekt konfigurierten
|
|
||||||
Jinja2-Templates.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
# Öffentliche API
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def list_templates(project, console: ConsoleUtils) -> None:
|
|
||||||
"""
|
|
||||||
Alle in *project.yml* definierten Templates auflisten.
|
|
||||||
"""
|
|
||||||
if not project.templates:
|
|
||||||
console.print("[yellow]No templates defined in project.yml")
|
|
||||||
return
|
|
||||||
|
|
||||||
console.print("[bold underline]Available Templates:")
|
|
||||||
for name in project.templates.root.keys():
|
|
||||||
console.print(f"• {name}")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate(
|
|
||||||
cls,
|
|
||||||
project,
|
|
||||||
name: Optional[str],
|
|
||||||
dry_run: bool,
|
|
||||||
console: ConsoleUtils,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Templates erzeugen.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
project
|
|
||||||
Geladenes Projekt-Model.
|
|
||||||
name
|
|
||||||
Name eines einzelnen Templates oder *None*, um alle Templates
|
|
||||||
zu erzeugen.
|
|
||||||
dry_run
|
|
||||||
Wenn *True*, wird das gerenderte Ergebnis nur ausgegeben,
|
|
||||||
jedoch nicht auf die Festplatte geschrieben.
|
|
||||||
console
|
|
||||||
Farbige Konsolen-Ausgaben.
|
|
||||||
"""
|
|
||||||
if not project.templates:
|
|
||||||
console.print("[red]No templates defined in project.yml")
|
|
||||||
return
|
|
||||||
|
|
||||||
templates = project.templates.root
|
|
||||||
|
|
||||||
if name:
|
|
||||||
# Ein bestimmtes Template
|
|
||||||
if name not in templates:
|
|
||||||
console.print(f"[red]Template '{name}' not found.")
|
|
||||||
return
|
|
||||||
cls._render_template(name, templates[name], dry_run, console)
|
|
||||||
else:
|
|
||||||
# Alle Templates durchlaufen
|
|
||||||
for tname, template in templates.items():
|
|
||||||
cls._render_template(tname, template, dry_run, console)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
# Interne Helfer
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _render_template(
|
|
||||||
name: str,
|
|
||||||
template: TemplateInstance,
|
|
||||||
dry_run: bool,
|
|
||||||
console: ConsoleUtils,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Einzelnes Template rendern und wahlweise speichern.
|
|
||||||
"""
|
|
||||||
template_path = template.template
|
|
||||||
output_path = template.output
|
|
||||||
variables = template.variables
|
|
||||||
|
|
||||||
env = Environment(
|
|
||||||
loader=FileSystemLoader(os.path.dirname(template_path)),
|
|
||||||
trim_blocks=True,
|
|
||||||
lstrip_blocks=True,
|
|
||||||
)
|
|
||||||
j2 = env.get_template(os.path.basename(template_path))
|
|
||||||
result = j2.render(**variables)
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
console.print(f"[green]--- Template: {name} (dry-run) ---")
|
|
||||||
console.print(result)
|
|
||||||
console.print(f"[green]--- End of {name} ---")
|
|
||||||
return
|
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
||||||
with open(output_path, "w") as f:
|
|
||||||
f.write(result)
|
|
||||||
|
|
||||||
console.print(f"[cyan]✔ Rendered template '{name}' → {output_path}")
|
|
@@ -1,8 +1,6 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from hdlbuild.models.templates import ProjectTemplates
|
|
||||||
|
|
||||||
class SourceFile(BaseModel):
|
class SourceFile(BaseModel):
|
||||||
path: str
|
path: str
|
||||||
library: str = "work" # Default auf 'work'
|
library: str = "work" # Default auf 'work'
|
||||||
@@ -45,7 +43,6 @@ class ProjectConfig(BaseModel):
|
|||||||
sources: Sources
|
sources: Sources
|
||||||
testbenches: Optional[Testbenches] = None
|
testbenches: Optional[Testbenches] = None
|
||||||
constraints: Optional[str] = None
|
constraints: Optional[str] = None
|
||||||
templates: Optional[ProjectTemplates] = None
|
|
||||||
build: Optional[BuildOptions] = None
|
build: Optional[BuildOptions] = None
|
||||||
dependencies: Optional[List[Dependency]] = Field(default_factory=list)
|
dependencies: Optional[List[Dependency]] = Field(default_factory=list)
|
||||||
tool_options: Optional[ToolOptions] = ToolOptions()
|
tool_options: Optional[ToolOptions] = ToolOptions()
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
from pydantic import BaseModel, Field, RootModel
|
|
||||||
from typing import Dict, Any
|
|
||||||
|
|
||||||
class TemplateInstance(BaseModel):
|
|
||||||
template: str # Pfad zur Jinja2-Vorlage
|
|
||||||
output: str # Zielpfad
|
|
||||||
variables: Dict[str, Any] = Field(default_factory=dict) # Variablen für Rendering
|
|
||||||
|
|
||||||
class ProjectTemplates(RootModel):
|
|
||||||
"""
|
|
||||||
Pydantic-RootModel, das die Mapping-Struktur *name → TemplateInstance*
|
|
||||||
kapselt. In Pydantic v2 ersetzt `RootModel` die frühere `__root__`-Syntax.
|
|
||||||
"""
|
|
||||||
root: Dict[str, TemplateInstance] # key = Name wie „alu“, „control_unit“
|
|
Reference in New Issue
Block a user