Compare commits
5 Commits
5d8d995a04
...
main
Author | SHA1 | Date | |
---|---|---|---|
6c2df38dd4 | |||
3d52b6e13f
|
|||
ef8dda20d8 | |||
8adaa916ff
|
|||
af0477f8e7
|
13
CHANGELOG.md
13
CHANGELOG.md
@@ -2,6 +2,19 @@
|
||||
|
||||
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
|
||||
|
||||
### 🚀 Features
|
||||
|
21
README.md
21
README.md
@@ -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
|
||||
|
||||
### CLI Commands
|
||||
@@ -155,14 +164,4 @@ The project includes GitHub workflows for building and deploying the package:
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
This project is licensed under the [MIT License](LICENSE). See the LICENSE file for details.
|
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "hdlbuild"
|
||||
version = "0.6.1"
|
||||
version = "0.7.0"
|
||||
description = "Flexible FPGA Build System"
|
||||
authors = ["0xMax42 <Mail@0xMax42.io>"]
|
||||
license = "MIT"
|
||||
@@ -18,6 +18,7 @@ pydantic = "^2.11.3"
|
||||
rich = "^14.0.0"
|
||||
gitpython = "^3.1.44"
|
||||
typer = "^0.16.0"
|
||||
jinja2 = "^3.1.6"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
twine = "^6.1.0"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import typer
|
||||
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.clean import cli as clean_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"
|
||||
)
|
||||
|
||||
# Unter‑Kommandos registrieren (entspricht add_subparsers)
|
||||
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(dep_cli, name="dep", help="Resolve dependencies")
|
||||
app.add_typer(test_cli, name="test", help="Run simulations/testbenches")
|
||||
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():
|
||||
app()
|
||||
|
53
src/hdlbuild/commands/gen.py
Normal file
53
src/hdlbuild/commands/gen.py
Normal 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)
|
120
src/hdlbuild/generate/template_generator.py
Normal file
120
src/hdlbuild/generate/template_generator.py
Normal 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}")
|
@@ -1,6 +1,8 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional
|
||||
|
||||
from hdlbuild.models.templates import ProjectTemplates
|
||||
|
||||
class SourceFile(BaseModel):
|
||||
path: str
|
||||
library: str = "work" # Default auf 'work'
|
||||
@@ -43,6 +45,7 @@ class ProjectConfig(BaseModel):
|
||||
sources: Sources
|
||||
testbenches: Optional[Testbenches] = None
|
||||
constraints: Optional[str] = None
|
||||
templates: Optional[ProjectTemplates] = None
|
||||
build: Optional[BuildOptions] = None
|
||||
dependencies: Optional[List[Dependency]] = Field(default_factory=list)
|
||||
tool_options: Optional[ToolOptions] = ToolOptions()
|
||||
|
14
src/hdlbuild/models/templates.py
Normal file
14
src/hdlbuild/models/templates.py
Normal 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“
|
Reference in New Issue
Block a user