refactor: use typer for CLI argument parsing
- Replaces `argparse` with `typer` for command-line argument parsing to improve CLI interface and maintainability. - Converts commands to subcommands using `typer.Typer()`. - Streamlines CLI structure and enhances user experience.
This commit is contained in:
@@ -19,6 +19,7 @@ pyyaml = "^6.0.2"
|
||||
pydantic = "^2.11.3"
|
||||
rich = "^14.0.0"
|
||||
gitpython = "^3.1.44"
|
||||
typer = "^0.16.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
twine = "^6.1.0"
|
||||
|
@@ -1,32 +1,32 @@
|
||||
import argparse
|
||||
import typer
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
from hdlbuild.commands import register_commands
|
||||
|
||||
def get_version():
|
||||
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
|
||||
from hdlbuild.commands.test import cli as test_cli
|
||||
from hdlbuild.commands.init import cli as init_cli
|
||||
|
||||
def get_version() -> str:
|
||||
try:
|
||||
return version("hdlbuild") # Paketname aus pyproject.toml
|
||||
return version("hdlbuild")
|
||||
except PackageNotFoundError:
|
||||
return "unknown"
|
||||
|
||||
app = typer.Typer(
|
||||
rich_help_panel="ℹ️ HDLBuild – FPGA‑Build‑Tool",
|
||||
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")
|
||||
|
||||
def main():
|
||||
version_str = get_version()
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"hdlbuild v{version_str} - Build management tool for FPGA projects",
|
||||
formatter_class=argparse.RawTextHelpFormatter
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
title="Commands",
|
||||
description="Available commands",
|
||||
dest="command",
|
||||
required=True
|
||||
)
|
||||
|
||||
# Register all commands
|
||||
register_commands(subparsers)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@@ -1 +0,0 @@
|
||||
from .commands import register_commands
|
@@ -1,30 +1,34 @@
|
||||
from hdlbuild.tools.xilinx_ise.main import xilinx_ise_all, xilinx_ise_synth
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.directory_manager import ensure_directories_exist
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
import typer
|
||||
|
||||
class BuildCommand:
|
||||
def __init__(self):
|
||||
self.console_utils = ConsoleUtils("hdlbuild")
|
||||
from hdlbuild.tools.xilinx_ise.main import xilinx_ise_all, xilinx_ise_synth
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.directory_manager import ensure_directories_exist
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
|
||||
def register(self, subparsers):
|
||||
parser = subparsers.add_parser("build", help="Start the build process")
|
||||
parser.add_argument(
|
||||
"target",
|
||||
nargs="?",
|
||||
choices=["synth"],
|
||||
help="Specify 'synth' to only synthesize the design (optional)"
|
||||
)
|
||||
parser.set_defaults(func=self.execute)
|
||||
cli = typer.Typer(rich_help_panel="🔨 Build Commands")
|
||||
|
||||
def execute(self, args):
|
||||
"""Starts the build process."""
|
||||
self.project = load_project_config()
|
||||
if args.target == "synth":
|
||||
self.console_utils.print("Starting synth process...")
|
||||
ensure_directories_exist(True)
|
||||
xilinx_ise_synth(self.project)
|
||||
else:
|
||||
self.console_utils.print("Starting build process...")
|
||||
ensure_directories_exist(True)
|
||||
xilinx_ise_all(self.project)
|
||||
@cli.callback(invoke_without_command=True)
|
||||
def build(
|
||||
target: str = typer.Argument(
|
||||
None,
|
||||
help="Optional: 'synth' to run synthesis only",
|
||||
show_default=False,
|
||||
rich_help_panel="🔨 Build Commands",
|
||||
)
|
||||
) -> None:
|
||||
"""
|
||||
Run the full build flow or synthesis only.
|
||||
|
||||
* `hdlbuild build` → full flow
|
||||
* `hdlbuild build synth` → synthesis only
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
project = load_project_config()
|
||||
|
||||
ensure_directories_exist(True)
|
||||
if target == "synth":
|
||||
console.print("Starting synthesis …")
|
||||
xilinx_ise_synth(project)
|
||||
else:
|
||||
console.print("Starting full build …")
|
||||
xilinx_ise_all(project)
|
||||
|
@@ -1,27 +1,35 @@
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.directory_manager import clear_build_directories, clear_directories
|
||||
import typer
|
||||
|
||||
class CleanCommand:
|
||||
def __init__(self):
|
||||
self.console_utils = ConsoleUtils("hdlbuild")
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.directory_manager import clear_build_directories, clear_directories
|
||||
|
||||
def register(self, subparsers):
|
||||
parser = subparsers.add_parser("clean", help="Clean build artifacts")
|
||||
parser.add_argument(
|
||||
"target",
|
||||
nargs="?",
|
||||
choices=["all"],
|
||||
help="Specify 'all' to clean everything (optional)"
|
||||
)
|
||||
parser.set_defaults(func=self.execute)
|
||||
cli = typer.Typer(rich_help_panel="🧹 Clean Commands")
|
||||
|
||||
def execute(self, args):
|
||||
"""Cleans the build artifacts."""
|
||||
if args.target == "all":
|
||||
self.console_utils.print("Starting clean all process...")
|
||||
clear_directories()
|
||||
self.console_utils.print("All cleaned.")
|
||||
else:
|
||||
self.console_utils.print("Clearing build artifacts...")
|
||||
clear_build_directories()
|
||||
self.console_utils.print("Build artifacts cleaned.")
|
||||
@cli.callback(invoke_without_command=True)
|
||||
def clean(
|
||||
target: str = typer.Argument(
|
||||
None,
|
||||
help="Optional: 'all' → wipe *all* artefacts, otherwise only the build directory",
|
||||
show_default=False,
|
||||
)
|
||||
) -> None:
|
||||
"""
|
||||
Remove build artefacts (`build/*`) or *everything* (`all`).
|
||||
|
||||
Examples
|
||||
--------
|
||||
```bash
|
||||
hdlbuild clean # build/* and temporary files only
|
||||
hdlbuild clean all # also caches, logs, etc.
|
||||
```
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
|
||||
if target == "all":
|
||||
console.print("Starting clean‑all …")
|
||||
clear_directories()
|
||||
console.print("All artefacts removed.")
|
||||
else:
|
||||
console.print("Removing build artefacts …")
|
||||
clear_build_directories()
|
||||
console.print("Build artefacts removed.")
|
||||
|
@@ -1,19 +0,0 @@
|
||||
from hdlbuild.commands.build import BuildCommand
|
||||
from hdlbuild.commands.clean import CleanCommand
|
||||
from hdlbuild.commands.dep import DepCommand
|
||||
from hdlbuild.commands.init import InitCommand
|
||||
from hdlbuild.commands.test import TestCommand
|
||||
|
||||
|
||||
def register_commands(subparsers):
|
||||
"""Registers all available commands."""
|
||||
commands = [
|
||||
CleanCommand(),
|
||||
BuildCommand(),
|
||||
DepCommand(),
|
||||
TestCommand(),
|
||||
InitCommand(),
|
||||
]
|
||||
|
||||
for command in commands:
|
||||
command.register(subparsers)
|
@@ -1,17 +1,23 @@
|
||||
from hdlbuild.dependencies.resolver import DependencyResolver
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
import typer
|
||||
|
||||
class DepCommand:
|
||||
def __init__(self):
|
||||
self.console_utils = ConsoleUtils("hdlbuild")
|
||||
from hdlbuild.dependencies.resolver import DependencyResolver
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
|
||||
def register(self, subparsers):
|
||||
parser = subparsers.add_parser("dep", help="Start the dependencies process")
|
||||
parser.set_defaults(func=self.execute)
|
||||
cli = typer.Typer(rich_help_panel="🔗 Dependency Commands")
|
||||
|
||||
def execute(self, args):
|
||||
"""Starts the dependencies process."""
|
||||
self.project = load_project_config()
|
||||
self.console_utils.print("Starting dependencies process...")
|
||||
DependencyResolver(self.project).resolve_all()
|
||||
@cli.callback(invoke_without_command=True)
|
||||
def dep() -> None:
|
||||
"""
|
||||
Resolve all project dependencies.
|
||||
|
||||
```bash
|
||||
hdlbuild dep
|
||||
```
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
project = load_project_config()
|
||||
|
||||
console.print("Resolving dependencies …")
|
||||
DependencyResolver(project).resolve_all()
|
||||
console.print("Dependencies resolved.")
|
||||
|
@@ -1,36 +1,35 @@
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from hdlbuild.dependencies.resolver import DependencyResolver
|
||||
from pathlib import Path
|
||||
import typer
|
||||
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
|
||||
class InitCommand:
|
||||
def __init__(self):
|
||||
self.console_utils = ConsoleUtils("hdlbuild")
|
||||
cli = typer.Typer(rich_help_panel="🆕 Init Commands")
|
||||
|
||||
def register(self, subparsers):
|
||||
parser = subparsers.add_parser("init", help="Initialize a new HDLBuild project")
|
||||
parser.set_defaults(func=self.execute)
|
||||
@cli.callback(invoke_without_command=True)
|
||||
def init() -> None:
|
||||
"""
|
||||
Initialise a new HDLBuild project in the current directory.
|
||||
|
||||
def execute(self, args):
|
||||
"""Initialize a new HDLBuild project."""
|
||||
project_dir = Path.cwd()
|
||||
Copies `.gitignore` and `project.yml` from the template folder.
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
project_dir = Path.cwd()
|
||||
|
||||
# Correctly resolve path to templates directory
|
||||
script_dir = Path(__file__).parent.resolve()
|
||||
template_dir = (script_dir / ".." / "templates").resolve()
|
||||
script_dir = Path(__file__).parent.resolve()
|
||||
template_dir = (script_dir / ".." / "templates").resolve()
|
||||
|
||||
# Files to copy
|
||||
files = [
|
||||
("gitignore.template", ".gitignore"),
|
||||
("project.yml.template", "project.yml"),
|
||||
]
|
||||
files = [
|
||||
("gitignore.template", ".gitignore"),
|
||||
("project.yml.template", "project.yml"),
|
||||
]
|
||||
|
||||
for template_name, target_name in files:
|
||||
template_path = template_dir / template_name
|
||||
target_path = project_dir / target_name
|
||||
for template_name, target_name in files:
|
||||
template_path = template_dir / template_name
|
||||
target_path = project_dir / target_name
|
||||
|
||||
if not target_path.exists():
|
||||
shutil.copy(template_path, target_path)
|
||||
self.console_utils.print(f"Created {target_name}")
|
||||
else:
|
||||
self.console_utils.print(f"{target_name} already exists, skipping.")
|
||||
if not target_path.exists():
|
||||
shutil.copy(template_path, target_path)
|
||||
console.print(f"Created {target_name}")
|
||||
else:
|
||||
console.print(f"{target_name} already exists – skipping.")
|
||||
|
@@ -1,23 +1,31 @@
|
||||
from hdlbuild.tools.xilinx_ise.isim import build_testbench, run_testbench
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
import typer
|
||||
|
||||
class TestCommand:
|
||||
def __init__(self):
|
||||
self.console_utils = ConsoleUtils("hdlbuild")
|
||||
from hdlbuild.tools.xilinx_ise.isim import build_testbench, run_testbench
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
|
||||
def register(self, subparsers):
|
||||
parser = subparsers.add_parser("test", help="Start the Tests process")
|
||||
parser.add_argument(
|
||||
"target",
|
||||
nargs="?",
|
||||
help="Select the target to test"
|
||||
)
|
||||
parser.set_defaults(func=self.execute)
|
||||
cli = typer.Typer(rich_help_panel="🧪 Test Commands")
|
||||
|
||||
def execute(self, args):
|
||||
"""Starts the test process."""
|
||||
self.project = load_project_config()
|
||||
self.console_utils.print("Starting test process...")
|
||||
build_testbench(self.project, args.target)
|
||||
run_testbench(self.project, args.target)
|
||||
@cli.callback(invoke_without_command=True)
|
||||
def test(
|
||||
target: str = typer.Argument(
|
||||
None,
|
||||
help="Name of the test target (leave empty to run all)",
|
||||
show_default=False,
|
||||
)
|
||||
) -> None:
|
||||
"""
|
||||
Build and run testbenches.
|
||||
|
||||
```bash
|
||||
hdlbuild test # run all TBs
|
||||
hdlbuild test alu # run TB 'alu' only
|
||||
```
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
project = load_project_config()
|
||||
|
||||
console.print("Starting test flow …")
|
||||
build_testbench(project, target)
|
||||
run_testbench(project, target)
|
||||
console.print("Tests finished.")
|
||||
|
Reference in New Issue
Block a user