Refactor CLI setup and command structure; streamline project initialization and compilation processes
This commit is contained in:
parent
d51b16ce8e
commit
2300c37fde
@ -0,0 +1 @@
|
||||
__version__ = "0.0.1"
|
||||
@ -1,9 +1,5 @@
|
||||
from .cli import CLI
|
||||
|
||||
from .config import ConfigError, load_config
|
||||
|
||||
from .commands.setup import is_chipnut_workspace, find_template_conflicts, copy_template_files
|
||||
from .commands.compiler import compile as compile_project
|
||||
from .commands import setup
|
||||
|
||||
def help_text():
|
||||
return [
|
||||
@ -12,96 +8,8 @@ def help_text():
|
||||
|
||||
def main() -> int:
|
||||
cli = CLI(help_callback=help_text)
|
||||
|
||||
cli.command(
|
||||
"init",
|
||||
help="Create an empty chipnut workspace or reinitialize with --force",
|
||||
description=(
|
||||
"Initialize a chipnut development workspace by creating a Nutfile and default project files. "
|
||||
"If a Nutfile already exists, initialization should fail unless --force is used."
|
||||
),
|
||||
).flag(
|
||||
"force",
|
||||
"f",
|
||||
help="Reinitialize even when a Nutfile already exists",
|
||||
).arg(
|
||||
"path",
|
||||
nargs="?",
|
||||
default=".",
|
||||
help="Directory to initialize (default: current directory)",
|
||||
).handle(cmd_init)
|
||||
|
||||
cli.command(
|
||||
"build",
|
||||
help="Compile the project",
|
||||
description=(
|
||||
"Compile the project according to the configuration in the Nutfile. "
|
||||
),
|
||||
).option(
|
||||
"config",
|
||||
"c",
|
||||
default="Nutfile",
|
||||
help="Path to Nutfile (default: ./Nutfile)",
|
||||
).option(
|
||||
"build-folder",
|
||||
"o",
|
||||
default=None,
|
||||
help="Override output build folder from config",
|
||||
).option(
|
||||
"name",
|
||||
"n",
|
||||
default=None,
|
||||
help="Override output file name from config",
|
||||
).handle(cmd_build)
|
||||
|
||||
setup(cli)
|
||||
return cli.run()
|
||||
|
||||
def cmd_build(args, flags) -> int:
|
||||
config_path = flags["config"]
|
||||
build_folder_override = flags["build_folder"]
|
||||
file_name_override = flags["name"]
|
||||
|
||||
if config_path == "Nutfile" and not is_chipnut_workspace():
|
||||
print("Error: No Nutfile found. Please run 'chipnut init' to create a workspace.")
|
||||
return 1
|
||||
|
||||
try:
|
||||
config = load_config(config_path)
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Config file not found: {config_path}")
|
||||
return 1
|
||||
except ConfigError as error:
|
||||
print(f"Error: Invalid Nutfile: {error}")
|
||||
return 1
|
||||
|
||||
try:
|
||||
compile_project(
|
||||
config,
|
||||
build_folder=build_folder_override,
|
||||
file_name=file_name_override,
|
||||
)
|
||||
except (RuntimeError, OSError) as error:
|
||||
print(f"Error: Build failed: {error}")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
def cmd_init(args, flags) -> int:
|
||||
dest_dir = args[0]
|
||||
force = flags["force"]
|
||||
|
||||
conflicts = find_template_conflicts(dest_dir)
|
||||
if conflicts and not force:
|
||||
print("Some files already exist that would be overwritten:")
|
||||
for file in conflicts:
|
||||
print(f" {file}")
|
||||
|
||||
print("Use --force to overwrite existing files.")
|
||||
return 1
|
||||
|
||||
copy_template_files(dest_dir, force=force, checked=True)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
from ..cli import CLI
|
||||
|
||||
from .setup import setup as cmd_setup
|
||||
from .compiler import setup as cmd_build
|
||||
|
||||
def setup(cli: CLI):
|
||||
cmd_setup(cli)
|
||||
cmd_build(cli)
|
||||
@ -2,12 +2,14 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from ..config import Config
|
||||
from ..cli import CLI
|
||||
from ..config import Config, ConfigError, load_config
|
||||
from ..paths import get_templates_path
|
||||
from ..utils import zip_dir
|
||||
from ..logger import logger
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CompileContext:
|
||||
@ -18,28 +20,27 @@ class CompileContext:
|
||||
output_name: str
|
||||
|
||||
|
||||
_ACTIVE_CONTEXT: Optional[CompileContext] = None
|
||||
|
||||
class Compiler:
|
||||
def __init__(self, context: CompileContext):
|
||||
self.context = context
|
||||
|
||||
def run(self) -> None:
|
||||
return None
|
||||
|
||||
def get_compile_context() -> CompileContext:
|
||||
if _ACTIVE_CONTEXT is None:
|
||||
raise RuntimeError("No active compile context. Call only during compile().")
|
||||
return _ACTIVE_CONTEXT
|
||||
|
||||
def _resolve_project_path(project_root: Path, value: Path) -> Path:
|
||||
if value.is_absolute():
|
||||
return value
|
||||
return (project_root / value).resolve()
|
||||
|
||||
|
||||
def _format_to_filename(name: str) -> str:
|
||||
formatted = name.strip().lower().replace(" ", "_")
|
||||
if not formatted.endswith(".nutsi"):
|
||||
formatted += ".nutsi"
|
||||
return formatted
|
||||
|
||||
|
||||
def _clear_directory(directory: Path) -> None:
|
||||
for child in directory.iterdir():
|
||||
if child.is_dir():
|
||||
@ -47,6 +48,11 @@ def _clear_directory(directory: Path) -> None:
|
||||
else:
|
||||
child.unlink()
|
||||
|
||||
|
||||
def _is_chipnut_workspace(path: str = ".") -> bool:
|
||||
return os.path.isfile(os.path.join(path, "Nutfile"))
|
||||
|
||||
|
||||
def prepare_build_directory(build_dir: Path) -> Path:
|
||||
if build_dir.exists() and not build_dir.is_dir():
|
||||
raise RuntimeError(f"Build path exists but is not a directory: {build_dir}")
|
||||
@ -70,6 +76,7 @@ def prepare_build_directory(build_dir: Path) -> Path:
|
||||
|
||||
return build_dir
|
||||
|
||||
|
||||
def stage_project_sources(project_root: Path, entry_path: Path, build_dir: Path) -> None:
|
||||
if entry_path.is_absolute():
|
||||
raise RuntimeError(
|
||||
@ -99,9 +106,9 @@ def stage_project_sources(project_root: Path, entry_path: Path, build_dir: Path)
|
||||
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.nutsi"),
|
||||
)
|
||||
|
||||
|
||||
def generate_nutsi_file(build_dir: Path, file_name: str) -> Path:
|
||||
output_path = build_dir / file_name
|
||||
|
||||
zip_dir(
|
||||
str(build_dir),
|
||||
str(output_path),
|
||||
@ -113,6 +120,7 @@ def generate_nutsi_file(build_dir: Path, file_name: str) -> Path:
|
||||
)
|
||||
return output_path
|
||||
|
||||
|
||||
def _create_context(config: Config, build_folder: Optional[str], file_name: Optional[str]) -> CompileContext:
|
||||
project_root = config.path.parent.resolve()
|
||||
entry_path = Path(config.build.entry)
|
||||
@ -134,18 +142,66 @@ def _create_context(config: Config, build_folder: Optional[str], file_name: Opti
|
||||
output_name=output_name,
|
||||
)
|
||||
|
||||
def compile(config: Config, build_folder: Optional[str] = None, file_name: Optional[str] = None) -> Path:
|
||||
global _ACTIVE_CONTEXT
|
||||
|
||||
def compile(config: Config, build_folder: Optional[str] = None, file_name: Optional[str] = None) -> Path:
|
||||
context = _create_context(config, build_folder=build_folder, file_name=file_name)
|
||||
prepared_dir = prepare_build_directory(context.build_dir)
|
||||
stage_project_sources(context.project_root, context.entry_path, prepared_dir)
|
||||
Compiler(context).run()
|
||||
return generate_nutsi_file(prepared_dir, context.output_name)
|
||||
|
||||
|
||||
def main(args, flags) -> int:
|
||||
config_path = flags["config"]
|
||||
build_folder_override = flags["build_folder"]
|
||||
file_name_override = flags["name"]
|
||||
|
||||
if config_path == "Nutfile" and not _is_chipnut_workspace():
|
||||
print("Error: No Nutfile found. Please run 'chipnut init' to create a workspace.")
|
||||
return 1
|
||||
|
||||
_ACTIVE_CONTEXT = context
|
||||
try:
|
||||
Compiler().run()
|
||||
finally:
|
||||
_ACTIVE_CONTEXT = None
|
||||
config = load_config(config_path)
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Config file not found: {config_path}")
|
||||
return 1
|
||||
except ConfigError as error:
|
||||
print(f"Error: Invalid Nutfile: {error}")
|
||||
return 1
|
||||
|
||||
archive_path = generate_nutsi_file(prepared_dir, context.output_name)
|
||||
return archive_path
|
||||
try:
|
||||
compile(
|
||||
config,
|
||||
build_folder=build_folder_override,
|
||||
file_name=file_name_override,
|
||||
)
|
||||
except (RuntimeError, OSError) as error:
|
||||
print(f"Error: Build failed: {error}")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def setup(cli: CLI):
|
||||
cli.command(
|
||||
"build",
|
||||
help="Compile the project",
|
||||
description=(
|
||||
"Compile the project according to the configuration in the Nutfile. "
|
||||
),
|
||||
).option(
|
||||
"config",
|
||||
"c",
|
||||
default="Nutfile",
|
||||
help="Path to Nutfile (default: ./Nutfile)",
|
||||
).option(
|
||||
"build-folder",
|
||||
"o",
|
||||
default=None,
|
||||
help="Override output build folder from config",
|
||||
).option(
|
||||
"name",
|
||||
"n",
|
||||
default=None,
|
||||
help="Override output file name from config",
|
||||
).handle(main)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from ..paths import get_templates_path
|
||||
from ..cli import CLI
|
||||
import shutil
|
||||
import os
|
||||
|
||||
@ -43,5 +44,38 @@ def copy_template_files(dest_dir: str, force: bool = False, checked: bool = Fals
|
||||
dirs_exist_ok=True
|
||||
)
|
||||
|
||||
def is_chipnut_workspace(path: str = ".") -> bool:
|
||||
return os.path.isfile(os.path.join(path, "Nutfile"))
|
||||
def main(args, flags) -> int:
|
||||
dest_dir = args[0]
|
||||
force = flags["force"]
|
||||
|
||||
conflicts = find_template_conflicts(dest_dir)
|
||||
if conflicts and not force:
|
||||
print("Some files already exist that would be overwritten:")
|
||||
for file in conflicts:
|
||||
print(f" {file}")
|
||||
|
||||
print("Use --force to overwrite existing files.")
|
||||
return 1
|
||||
|
||||
copy_template_files(dest_dir, force=force, checked=True)
|
||||
|
||||
return 0
|
||||
|
||||
def setup(cli: CLI):
|
||||
cli.command(
|
||||
"init",
|
||||
help="Create an empty chipnut workspace or reinitialize with --force",
|
||||
description=(
|
||||
"Initialize a chipnut development workspace by creating a Nutfile and default project files. "
|
||||
"If a Nutfile already exists, initialization should fail unless --force is used."
|
||||
),
|
||||
).flag(
|
||||
"force",
|
||||
"f",
|
||||
help="Reinitialize even when a Nutfile already exists",
|
||||
).arg(
|
||||
"path",
|
||||
nargs="?",
|
||||
default=".",
|
||||
help="Directory to initialize (default: current directory)",
|
||||
).handle(main)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user