shuriken: Refactor merged target configs
This commit is contained in:
parent
d5d480d854
commit
acd53e17d1
1 changed files with 47 additions and 17 deletions
|
|
@ -10,7 +10,6 @@
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import copy
|
|
||||||
import json
|
import json
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
@ -18,7 +17,7 @@ import sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from dataclasses import asdict, dataclass, field
|
from dataclasses import asdict, dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
@ -60,6 +59,38 @@ class TargetConfig:
|
||||||
return f"{self.linker_args or ''} {self.linker_args_extra or ''}".strip()
|
return f"{self.linker_args or ''} {self.linker_args_extra or ''}".strip()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(kw_only=True)
|
||||||
|
class MergedTargetConfig(TargetConfig):
|
||||||
|
cpp_compiler: str = ''
|
||||||
|
cpp_standard: str = ''
|
||||||
|
cpp_flags: str = ''
|
||||||
|
cpp_flags_extra: str = ''
|
||||||
|
|
||||||
|
linker_flags: str = ''
|
||||||
|
linker_flags_extra: str = ''
|
||||||
|
linker_args: str = ''
|
||||||
|
linker_args_extra: str = ''
|
||||||
|
|
||||||
|
output_file: str = ''
|
||||||
|
run_command: str = ''
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_from(cls, target_defaults: TargetConfig, target_config: TargetConfig) -> Self:
|
||||||
|
merged_config = cls()
|
||||||
|
merged_config.update(asdict(target_defaults))
|
||||||
|
merged_config.update(asdict(target_config))
|
||||||
|
return merged_config
|
||||||
|
|
||||||
|
def validate(self) -> None:
|
||||||
|
# Validate that all required fields are set
|
||||||
|
if not self.cpp_compiler:
|
||||||
|
raise ConfigValidationException('cpp_compiler must be set')
|
||||||
|
if not self.cpp_standard:
|
||||||
|
raise ConfigValidationException('cpp_standard must be set')
|
||||||
|
if not self.output_file:
|
||||||
|
raise ConfigValidationException('output_file must be set')
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class ShurikenConfig:
|
class ShurikenConfig:
|
||||||
__path_fields = ['source_dir', 'build_dir']
|
__path_fields = ['source_dir', 'build_dir']
|
||||||
|
|
@ -72,9 +103,6 @@ class ShurikenConfig:
|
||||||
defaults: TargetConfig = field(default_factory=lambda: TargetConfig(
|
defaults: TargetConfig = field(default_factory=lambda: TargetConfig(
|
||||||
cpp_compiler='clang++',
|
cpp_compiler='clang++',
|
||||||
cpp_standard='c++20',
|
cpp_standard='c++20',
|
||||||
cpp_flags='',
|
|
||||||
linker_flags='',
|
|
||||||
linker_args='',
|
|
||||||
))
|
))
|
||||||
|
|
||||||
enable_import_std: bool = False
|
enable_import_std: bool = False
|
||||||
|
|
@ -82,6 +110,9 @@ class ShurikenConfig:
|
||||||
default_target: str = ''
|
default_target: str = ''
|
||||||
targets: dict[str, TargetConfig] = field(default_factory=dict)
|
targets: dict[str, TargetConfig] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# Merged target configs, generated from defaults and targets
|
||||||
|
_merged_target_configs = {}
|
||||||
|
|
||||||
def update(self, data: dict) -> None:
|
def update(self, data: dict) -> None:
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
# Ignore unknown config keys
|
# Ignore unknown config keys
|
||||||
|
|
@ -116,6 +147,10 @@ class ShurikenConfig:
|
||||||
else:
|
else:
|
||||||
setattr(self, key, str(value))
|
setattr(self, key, str(value))
|
||||||
|
|
||||||
|
# Generate merged target configs (we generate all of them so that we can validate the full config)
|
||||||
|
for target_name, target_config in self.targets.items():
|
||||||
|
self._merged_target_configs[target_name] = MergedTargetConfig.create_from(self.defaults, target_config)
|
||||||
|
|
||||||
def update_from_yaml(self, file_path: Path) -> None:
|
def update_from_yaml(self, file_path: Path) -> None:
|
||||||
with file_path.open('r') as file:
|
with file_path.open('r') as file:
|
||||||
self.update(yaml.safe_load(file))
|
self.update(yaml.safe_load(file))
|
||||||
|
|
@ -137,23 +172,18 @@ class ShurikenConfig:
|
||||||
raise ConfigValidationException('default_target is not defined in targets')
|
raise ConfigValidationException('default_target is not defined in targets')
|
||||||
|
|
||||||
# Validate individual build targets
|
# Validate individual build targets
|
||||||
for target_name, target_config in self.targets.items():
|
for target_name, merged_target_config in self._merged_target_configs.items():
|
||||||
if not target_config.cpp_compiler and not self.defaults.cpp_compiler:
|
try:
|
||||||
raise ConfigValidationException(f'Target "{target_name}": cpp_compiler must be set')
|
merged_target_config.validate()
|
||||||
if not target_config.cpp_standard and not self.defaults.cpp_standard:
|
except ConfigValidationException as exc:
|
||||||
raise ConfigValidationException(f'Target "{target_name}": cpp_standard must be set')
|
raise ConfigValidationException(f'Target "{target_name}": {str(exc)}')
|
||||||
if not target_config.output_file and not self.defaults.output_file:
|
|
||||||
raise ConfigValidationException(f'Target "{target_name}": output_file must be set')
|
|
||||||
|
|
||||||
def get_ninja_file_path(self, target: str) -> Path:
|
def get_ninja_file_path(self, target: str) -> Path:
|
||||||
return self.build_dir / f'build.{target}.ninja'
|
return self.build_dir / f'build.{target}.ninja'
|
||||||
|
|
||||||
def get_merged_target_config(self, target: str) -> TargetConfig:
|
def get_merged_target_config(self, target: str) -> MergedTargetConfig:
|
||||||
assert target in self.targets, f'Target "{target}" not defined!'
|
assert target in self.targets, f'Target "{target}" not defined!'
|
||||||
|
return self._merged_target_configs[target]
|
||||||
merged_config = copy.deepcopy(self.defaults)
|
|
||||||
merged_config.update(asdict(self.targets[target]))
|
|
||||||
return merged_config
|
|
||||||
|
|
||||||
|
|
||||||
class ShurikenArgumentParser:
|
class ShurikenArgumentParser:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue