5 Commits
v0.6.1 ... main

Author SHA1 Message Date
6c2df38dd4 chore(changelog): update unreleased changelog 2025-07-18 18:29:37 +00:00
3d52b6e13f docs(readme): add pip installation instructions and refine license section
All checks were successful
Auto Changelog & (Release) / release (push) Successful in 13s
Build and Publish nightly package / build-and-publish (push) Successful in 1m30s
2025-07-18 20:29:21 +02:00
ef8dda20d8 chore(changelog): update changelog for v0.7.0
All checks were successful
Build and Publish nightly package / build-and-publish (release) Successful in 1m21s
2025-07-17 08:57:14 +00:00
8adaa916ff feat(version): bump version to 0.7.0
All checks were successful
Auto Changelog & (Release) / release (push) Successful in 13s
Build and Publish nightly package / build-and-publish (push) Successful in 1m29s
2025-07-17 10:56:58 +02:00
af0477f8e7 feat(cli): add template generation commands
- Introduces `gen` subcommand for HDL template generation
- Adds Jinja2 dependency for template rendering
- Updates project model to support template configurations
- Implements template listing and rendering functionality
2025-07-17 10:56:58 +02:00
9 changed files with 218 additions and 14 deletions

View File

@@ -2,6 +2,19 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [unreleased]
### 📚 Documentation
- *(readme)* Add pip installation instructions and refine license section - ([3d52b6e](https://git.0xmax42.io/maxp/hdlbuild/commit/3d52b6e13fd14ff1df61cb6e27d4f31445c82270))
## [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 ## [0.6.1] - 2025-07-16
### 🚀 Features ### 🚀 Features

View File

@@ -41,6 +41,15 @@ HDLBuild is a flexible build management tool for FPGA projects. It simplifies th
--- ---
## Installation per pip
To install HDLBuild via pip, run:
```bash
pip install --index-url https://git.0xmax42.io/api/packages/maxp/pypi/simple/ --extra-index-url https://pypi.org/ hdlbuild
```
---
## Usage ## Usage
### CLI Commands ### CLI Commands
@@ -155,14 +164,4 @@ The project includes GitHub workflows for building and deploying the package:
## License ## License
This project is licensed under the MIT License. This project is licensed under the [MIT License](LICENSE). See the LICENSE file for details.
---
## Contributing
Contributions are welcome! Please follow these steps:
1. Fork the repository.
2. Create a new branch for your feature or bugfix.
3. Submit a pull request.

View File

@@ -1 +1 @@
0.6.1 0.7.0

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "hdlbuild" name = "hdlbuild"
version = "0.6.1" version = "0.7.0"
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,6 +18,7 @@ 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"

View File

@@ -1,6 +1,7 @@
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
@@ -18,12 +19,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()

View File

@@ -0,0 +1,53 @@
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)

View File

@@ -0,0 +1,120 @@
"""
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}")

View File

@@ -1,6 +1,8 @@
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'
@@ -43,6 +45,7 @@ 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()

View File

@@ -0,0 +1,14 @@
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“