Skip to content
Snippets Groups Projects
Commit 2b4b01d7 authored by Frederik Hennig's avatar Frederik Hennig
Browse files

Added configuration module

parent 43daedc4
Branches
Tags
No related merge requests found
from typing import List, Sequence
from enum import Enum, auto
from dataclasses import dataclass, replace
from argparse import ArgumentParser
from jinja2.filters import do_indent
from .exceptions import SfgException
HEADER_FILE_EXTENSIONS = {'h', 'hpp'}
SOURCE_FILE_EXTENSIONS = {'c', 'cpp'}
@dataclass
class SfgCodeStyle:
indent_width: int = 2
def indent(self, s: str):
return do_indent(s, self.indent_width, first=True)
@dataclass
class SfgConfiguration:
header_extension: str = None
"""File extension for generated header files."""
source_extension: str = None
"""File extension for generated source files."""
header_only: bool = None
"""If set to `True`, generate only a header file without accompaning source file."""
base_namespace: str = None
"""The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier
(like `a::b::c`) or `None` if no outer namespace should be generated."""
codestyle: SfgCodeStyle = None
"""Code style that should be used by the code generator."""
output_directory: str = None
"""Directory to which the generated files should be written."""
def __post_init__(self):
if self.header_only:
raise SfgException(
"Header-only code generation is not implemented yet.")
if self.header_extension[0] == '.':
self.header_extension = self.header_extension[1:]
if self.source_extension[0] == '.':
self.source_extension = self.source_extension[1:]
DEFAULT_CONFIG = SfgConfiguration(
header_extension='h',
source_extension='cpp',
header_only=False,
base_namespace=None,
codestyle=SfgCodeStyle(),
output_directory=""
)
def get_file_extensions(self, extensions: Sequence[str]):
h_ext = None
src_ext = None
extensions = ((ext[1:] if ext[0] == '.' else ext) for ext in extensions)
for ext in extensions:
if ext in HEADER_FILE_EXTENSIONS:
if h_ext is not None:
raise ValueError("Multiple header file extensions found.")
h_ext = ext
elif ext in SOURCE_FILE_EXTENSIONS:
if src_ext is not None:
raise ValueError("Multiple source file extensions found.")
src_ext = ext
else:
raise ValueError(f"Don't know how to interpret extension '.{ext}'")
return h_ext, src_ext
def run_configurator(configurator_script: str):
raise NotImplementedError()
def config_from_commandline(self, argv: List[str]):
parser = ArgumentParser("pystencilssfg",
description="pystencils Source File Generator",
allow_abbrev=False)
parser.add_argument("-sfg-d", "--sfg-output-dir",
type=str, default='.', dest='output_directory')
parser.add_argument("-sfg-e", "--sfg-file-extensions",
type=str, default=None, nargs='*', dest='file_extensions')
parser.add_argument("--sfg-header-only",
type=str, default=None, nargs='*', dest='header_only')
parser.add_argument("--sfg-configurator", type=str,
default=None, nargs='*', dest='configurator_script')
args, script_args = parser.parse_known_args(argv)
if args.configurator_script is not None:
project_config = run_configurator(args.configurator_script)
else:
project_config = None
if args.file_extensions is not None:
h_ext, src_ext = get_file_extensions(args.file_extensions)
else:
h_ext, src_ext = None, None
cmdline_config = SfgConfiguration(
header_extension=h_ext,
source_extension=src_ext,
header_only=args.header_only,
output_directory=args.output_directory
)
return project_config, cmdline_config, script_args
def merge_configurations(project_config: SfgConfiguration,
cmdline_config: SfgConfiguration,
script_config: SfgConfiguration):
# Project config completely overrides default config
config = DEFAULT_CONFIG
if project_config is not None:
config = replace(DEFAULT_CONFIG, **(project_config.asdict()))
if cmdline_config is not None:
cmdline_dict = cmdline_config.asdict()
# Commandline config completely overrides project and default config
config = replace(config, **cmdline_dict)
else:
cmdline_dict = {}
if script_config is not None:
# User config may only set values not specified on the command line
script_dict = script_config.asdict()
for key, cmdline_value in cmdline_dict.items():
if cmdline_value is not None and script_dict[key] is not None:
raise SfgException(
f"Conflicting configuration: Parameter {key} was specified both in the script and on the command line.")
config = replace(config, **script_dict)
return config
......@@ -5,13 +5,11 @@ import sys
import os
from os import path
from argparse import ArgumentParser
from jinja2.filters import do_indent
from pystencils import Field
from pystencils.astnodes import KernelFunction
from .configuration import SfgConfiguration, config_from_commandline, merge_configurations, SfgCodeStyle
from .kernel_namespace import SfgKernelNamespace, SfgKernelHandle
from .tree import SfgCallTreeNode, SfgSequence, SfgKernelCallNode, SfgStatements
from .tree.deferred_nodes import SfgDeferredFieldMapping
......@@ -21,37 +19,22 @@ from .source_concepts import SrcField, TypedSymbolOrObject
from .source_components import SfgFunction, SfgHeaderInclude
@dataclass
class SfgCodeStyle:
indent_width: int = 2
def indent(self, s: str):
return do_indent(s, self.indent_width, first=True)
class SourceFileGenerator:
def __init__(self,
namespace: str = "pystencils",
codestyle: SfgCodeStyle = SfgCodeStyle()):
parser = ArgumentParser(
"pystencilssfg",
description="pystencils Source File Generator",
allow_abbrev=False)
parser.add_argument("-d", "--sfg-output-dir", type=str, default='.', dest='output_directory')
generator_args, script_args = parser.parse_known_args(sys.argv)
def __init__(self, sfg_config: SfgConfiguration):
import __main__
scriptpath = __main__.__file__
scriptname = path.split(scriptpath)[1]
basename = path.splitext(scriptname)[0]
basename = path.splitext(scriptname)[0]
project_config, cmdline_config, script_args = config_from_commandline(sys.argv)
config = merge_configurations(project_config, cmdline_config, sfg_config)
self._context = SfgContext(script_args, namespace, codestyle)
self._context = SfgContext(script_args, config)
from .emitters.cpu.basic_cpu import BasicCpuEmitter
self._emitter = BasicCpuEmitter(self._context, basename, generator_args.output_directory)
self._emitter = BasicCpuEmitter(self._context, basename, config.output_directory)
def clean_files(self):
for file in self._emitter.output_files:
......@@ -68,10 +51,9 @@ class SourceFileGenerator:
class SfgContext:
def __init__(self, argv, root_namespace: str, codestyle: SfgCodeStyle):
def __init__(self, argv, config: SfgConfiguration):
self._argv = argv
self._root_namespace = root_namespace
self._codestyle = codestyle
self._config = config
self._default_kernel_namespace = SfgKernelNamespace(self, "kernels")
# Source Components
......@@ -85,11 +67,11 @@ class SfgContext:
@property
def root_namespace(self) -> str:
return self._root_namespace
return self._config.base_namespace
@property
def codestyle(self) -> SfgCodeStyle:
return self._codestyle
return self._config.codestyle
#----------------------------------------------------------------------------------------------
# Source Component Getters
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment