Compare commits
No commits in common. "7935eb6e25afb6994a09bbfed21181393c36a550" and "e37ee1af6c762f8cf9ddca6907c5458bb665d6ad" have entirely different histories.
7935eb6e25
...
e37ee1af6c
@ -1,9 +1,5 @@
|
|||||||
from .cli import CLI
|
from .cli import CLI
|
||||||
|
|
||||||
from .config import ConfigError, load_config
|
|
||||||
|
|
||||||
from .commands.setup import is_nut_workspace, find_template_conflicts, copy_template_files
|
from .commands.setup import is_nut_workspace, find_template_conflicts, copy_template_files
|
||||||
from .commands.compiler import compile as compile_project
|
|
||||||
|
|
||||||
def help_text():
|
def help_text():
|
||||||
return [
|
return [
|
||||||
@ -31,57 +27,8 @@ def main() -> int:
|
|||||||
help="Directory to initialize (default: current directory)",
|
help="Directory to initialize (default: current directory)",
|
||||||
).handle(cmd_init)
|
).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)
|
|
||||||
|
|
||||||
return cli.run()
|
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_nut_workspace():
|
|
||||||
print("Error: No Nutfile found. Please run 'nut 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:
|
def cmd_init(args, flags) -> int:
|
||||||
dest_dir = args[0]
|
dest_dir = args[0]
|
||||||
force = flags["force"]
|
force = flags["force"]
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
from nut.config import Config
|
|
||||||
from nut.paths import get_templates_path
|
|
||||||
from nut.utils import zip_dir
|
|
||||||
|
|
||||||
def _format_to_filename(name: str) -> str:
|
|
||||||
formatted = name.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():
|
|
||||||
shutil.rmtree(child)
|
|
||||||
else:
|
|
||||||
child.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
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}")
|
|
||||||
|
|
||||||
build_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
_clear_directory(build_dir)
|
|
||||||
|
|
||||||
builtins_path = get_templates_path() / "builtins"
|
|
||||||
if not builtins_path.is_dir():
|
|
||||||
raise RuntimeError(
|
|
||||||
"Builtins path does not exist: "
|
|
||||||
f"{builtins_path}. Installation may be corrupted."
|
|
||||||
)
|
|
||||||
|
|
||||||
shutil.copytree(
|
|
||||||
builtins_path,
|
|
||||||
build_dir / "builtins",
|
|
||||||
dirs_exist_ok=True,
|
|
||||||
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return build_dir
|
|
||||||
|
|
||||||
def generate_nutsi_file(build_dir: Path, file_name: str) -> None:
|
|
||||||
output_path = build_dir / file_name
|
|
||||||
|
|
||||||
zip_dir(build_dir, output_path, ignore_patterns=[
|
|
||||||
"__pycache__",
|
|
||||||
"*.pyc",
|
|
||||||
"*.nutsi",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def compile(config: Config, build_folder: Optional[str] = None, file_name: Optional[str] = None) -> None:
|
|
||||||
print("Compiling the project...")
|
|
||||||
|
|
||||||
selected_name = _format_to_filename(file_name or config.build.name)
|
|
||||||
selected_folder = build_folder or config.build.build_folder
|
|
||||||
prepared_dir = prepare_build_directory(Path(selected_folder))
|
|
||||||
|
|
||||||
generate_nutsi_file(prepared_dir, selected_name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -43,5 +43,5 @@ def copy_template_files(dest_dir: str, force: bool = False, checked: bool = Fals
|
|||||||
dirs_exist_ok=True
|
dirs_exist_ok=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_nut_workspace(path: str = ".") -> bool:
|
def is_nut_workspace(path: str) -> bool:
|
||||||
return os.path.isfile(os.path.join(path, "Nutfile"))
|
return os.path.isfile(os.path.join(path, "Nutfile"))
|
||||||
|
|||||||
@ -1,64 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, Optional
|
|
||||||
|
|
||||||
from .utils import read_yaml
|
|
||||||
|
|
||||||
class ConfigError(ValueError):
|
|
||||||
"""Raised when Nutfile contents are invalid."""
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class BuildConfig:
|
|
||||||
name: str
|
|
||||||
build_folder: str
|
|
||||||
entry: str
|
|
||||||
|
|
||||||
@property
|
|
||||||
def buildFolder(self) -> str:
|
|
||||||
# Compatibility alias for existing camelCase references.
|
|
||||||
return self.build_folder
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Config:
|
|
||||||
path: Path
|
|
||||||
raw: Dict[str, Any]
|
|
||||||
build: BuildConfig
|
|
||||||
|
|
||||||
@property
|
|
||||||
def buildFolder(self) -> str:
|
|
||||||
# Compatibility alias for older call-sites.
|
|
||||||
return self.build.build_folder
|
|
||||||
|
|
||||||
def _ensure_dict(value: Any, context: str) -> Dict[str, Any]:
|
|
||||||
if not isinstance(value, dict):
|
|
||||||
raise ConfigError(f"{context} must be a mapping")
|
|
||||||
return value
|
|
||||||
|
|
||||||
def _read_string(mapping: Dict[str, Any], key: str, default: str) -> str:
|
|
||||||
value = mapping.get(key, default)
|
|
||||||
if not isinstance(value, str) or not value.strip():
|
|
||||||
raise ConfigError(f"build.{key} must be a non-empty string")
|
|
||||||
return value
|
|
||||||
|
|
||||||
def load_config(path: str = "Nutfile") -> Config:
|
|
||||||
config_path = Path(path)
|
|
||||||
raw_data: Optional[Dict[str, Any]] = read_yaml(str(config_path))
|
|
||||||
|
|
||||||
if raw_data is None:
|
|
||||||
raw_data = {}
|
|
||||||
raw_dict = _ensure_dict(raw_data, "Nutfile root")
|
|
||||||
|
|
||||||
build_section = raw_dict.get("build", {})
|
|
||||||
build_dict = _ensure_dict(build_section, "build section")
|
|
||||||
|
|
||||||
build = BuildConfig(
|
|
||||||
name=_read_string(build_dict, "name", "Example Build"),
|
|
||||||
build_folder=_read_string(build_dict, "buildFolder", "build"),
|
|
||||||
entry=_read_string(build_dict, "entry", "src/main.nut"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return Config(
|
|
||||||
path=config_path,
|
|
||||||
raw=raw_dict,
|
|
||||||
build=build,
|
|
||||||
)
|
|
||||||
Loading…
x
Reference in New Issue
Block a user