# core.py # Index of this file: # [SECTION] version # [SECTION] imports # [SECTION] enums # [SECTION] public classes # [SECTION] private classes # [SECTION] global context # [SECTION] context managers # [SECTION] public api #----------------------------------------------------------------------------- # [SECTION] version #----------------------------------------------------------------------------- __version__ = "1.0.5" #----------------------------------------------------------------------------- # [SECTION] imports #----------------------------------------------------------------------------- from enum import Enum from contextlib import contextmanager #----------------------------------------------------------------------------- # [SECTION] enums #----------------------------------------------------------------------------- class TargetType(Enum): NONE = 0 STATIC_LIBRARY = 0 DYNAMIC_LIBRARY = 1 EXECUTABLE = 2 #----------------------------------------------------------------------------- # [SECTION] public classes #----------------------------------------------------------------------------- class CompilerSettings: def __init__(self, name: str): self.name = name self.output_directory = None self.output_binary = None self.output_binary_extension = None self.definitions = [] self.compiler_flags = [] self.linker_flags = [] self.include_directories = [] self.link_directories = [] self.source_files = [] self.static_link_libraries = [] self.dynamic_link_libraries = [] self.link_frameworks = [] self.target_type = TargetType.NONE self.pre_build_step = None self.post_build_step = None # inherited from platform self.platform_name = None # inherited from config self.config_name = None # inherited from target self.target_name = None self.target_type = None self.lock_file = None self.reloadable = None class ScriptData: def __init__(self): self.version = __version__ self.current_settings = [] self.project_name = None self.reload_target_name = None self.registered_configurations = [] class BuildContext: def __init__(self): self._script_data = ScriptData() # current profiles self._profiles = [] # current target information self._target_name = None self._target_type = None self._target_lock_file = None self._target_reloadable = False # current platform self._platform_name = None # current working settings self._working_settings = None # project scope self._project_output_directory = None self._project_definitions = [] self._project_include_directories = [] self._project_link_directories = [] self._project_static_link_libraries = [] self._project_dynamic_link_libraries = [] self._project_link_frameworks = [] self._project_source_files = [] # target scope self._target_output_binary = None self._target_output_directory = None self._target_definitions = [] self._target_include_directories = [] self._target_link_directories = [] self._target_static_link_libraries = [] self._target_dynamic_link_libraries = [] self._target_link_frameworks = [] self._target_source_files = [] # config scope self._config_name = None self._config_output_binary = None self._config_output_directory = None self._config_definitions = [] self._config_include_directories = [] self._config_link_directories = [] self._config_static_link_libraries = [] self._config_dynamic_link_libraries = [] self._config_link_frameworks = [] self._config_source_files = [] # platform scope self._platform_output_binary = None self._platform_output_directory = None self._platform_definitions = [] self._platform_include_directories = [] self._platform_link_directories = [] self._platform_static_link_libraries = [] self._platform_dynamic_link_libraries = [] self._platform_link_frameworks = [] self._platform_source_files = [] #----------------------------------------------------------------------------- # [SECTION] private classes #----------------------------------------------------------------------------- class _CompilerProfile: def __init__(self): self.compiler_filter = None self.platform_filter = None self.configuration_filter = None self.target_filter = None self.target_type_filter = None self.output_directory = None self.definitions = [] self.include_directories = [] self.link_directories = [] self.static_link_libraries = [] self.dynamic_link_libraries = [] self.link_frameworks = [] self.source_files = [] self.compiler_flags = [] self.linker_flags = [] self.output_binary_extension = None def is_active(self): if self.target_type_filter is not None: found = False for target in self.target_type_filter: if target == _context._target_type: found = True break if not found: return False if self.target_filter is not None: found = False for target in self.target_filter: if target == _context._target_name: found = True break if not found: return False if self.configuration_filter is not None: found = False for config_name in self.configuration_filter: if config_name == _context._config_name: found = True break if not found: return False if self.platform_filter is not None: found = False for platform_name in self.platform_filter: if platform_name == _context._platform_name: found = True break if not found: return False if self.compiler_filter is not None: found = False for name in self.compiler_filter: if name == _context._working_settings.name: found = True break if not found: return False return True #----------------------------------------------------------------------------- # [SECTION] global context #----------------------------------------------------------------------------- _context = BuildContext() #----------------------------------------------------------------------------- # [SECTION] context managers #----------------------------------------------------------------------------- @contextmanager def project(name: str): try: # persistent data _context._script_data = ScriptData() _context._script_data.project_name = name # current profiles _context._profiles = [] # current target _context._target_name = None _context._target_type = None _context._target_lock_file = None _context._target_reloadable = False # current config _context._config_name = None # current platform _context._platform_name = None # working settings _context._working_settings = None # project scope _context._project_output_directory = None _context._project_definitions = [] _context._project_include_directories = [] _context._project_link_directories = [] _context._project_static_link_libraries = [] _context._project_dynamic_link_libraries = [] _context._project_link_frameworks = [] _context._project_source_files = [] yield None finally: pass @contextmanager def target(name: str, target_type: TargetType = TargetType.EXECUTABLE, reloadable: bool = False): try: _context._target_name = name _context._target_type = target_type _context._target_lock_file = "lock.tmp" _context._target_reloadable = reloadable yield None finally: _context._target_name = None _context._target_type = None _context._target_lock_file = None _context._target_reloadable = False _context._target_output_directory = None _context._target_definitions = [] _context._target_include_directories = [] _context._target_link_directories = [] _context._target_static_link_libraries = [] _context._target_dynamic_link_libraries = [] _context._target_link_frameworks = [] _context._target_source_files = [] @contextmanager def configuration(name: str): try: _context._config_name = name # register configurations if not already exists = False for config in _context._script_data.registered_configurations: if config == name: exists = True break if not exists: _context._script_data.registered_configurations.append(name) yield None finally: _context._config_name = None _context._config_output_directory = None _context._config_definitions = [] _context._config_include_directories = [] _context._config_link_directories = [] _context._config_static_link_libraries = [] _context._config_dynamic_link_libraries = [] _context._config_link_frameworks = [] _context._config_source_files = [] @contextmanager def platform(name: str): try: _context._platform_name = name yield None finally: _context._platform_name = None _context._platform_output_directory = None _context._platform_definitions = [] _context._platform_include_directories = [] _context._platform_link_directories = [] _context._platform_static_link_libraries = [] _context._platform_dynamic_link_libraries = [] _context._platform_link_frameworks = [] _context._platform_source_files = [] @contextmanager def compiler(name: str): try: compiler = CompilerSettings(name) # inherited from platform compiler.platform_name = _context._platform_name # inherited from configuration compiler.config_name = _context._config_name # inherited from target compiler.target_name = _context._target_name compiler.target_type = _context._target_type compiler.lock_file = _context._target_lock_file compiler.reloadable = _context._target_reloadable # inherited from various scopes if _context._platform_output_directory is not None: compiler.output_directory = _context._platform_output_directory elif _context._config_output_directory is not None: compiler.output_directory = _context._config_output_directory elif _context._target_output_directory is not None: compiler.output_directory = _context._target_output_directory elif _context._project_output_directory is not None: compiler.output_directory = _context._project_output_directory if _context._platform_output_binary is not None: compiler.output_binary = _context._platform_output_binary elif _context._config_output_binary is not None: compiler.output_binary = _context._config_output_binary elif _context._target_output_binary is not None: compiler.output_binary = _context._target_output_binary compiler.link_directories = _context._project_link_directories + _context._target_link_directories + _context._config_link_directories + _context._platform_link_directories compiler.definitions = _context._project_definitions + _context._target_definitions + _context._config_definitions + _context._platform_definitions compiler.include_directories = _context._project_include_directories + _context._target_include_directories + _context._config_include_directories + _context._platform_include_directories compiler.static_link_libraries = _context._project_static_link_libraries + _context._target_static_link_libraries + _context._config_static_link_libraries + _context._platform_static_link_libraries compiler.dynamic_link_libraries = _context._project_dynamic_link_libraries + _context._target_dynamic_link_libraries + _context._config_dynamic_link_libraries + _context._platform_dynamic_link_libraries compiler.link_frameworks = _context._project_link_frameworks + _context._target_link_frameworks + _context._config_link_frameworks + _context._platform_link_frameworks compiler.source_files = _context._project_source_files + _context._target_source_files + _context._config_source_files + _context._platform_source_files # this is set here since profile.is_active() looks at it _context._working_settings = compiler # inherited from matching profiles for profile in _context._profiles: if profile.is_active(): _context._working_settings.definitions.extend(profile.definitions) _context._working_settings.compiler_flags.extend(profile.compiler_flags) _context._working_settings.include_directories.extend(profile.include_directories) _context._working_settings.link_directories.extend(profile.link_directories) _context._working_settings.static_link_libraries.extend(profile.static_link_libraries) _context._working_settings.dynamic_link_libraries.extend(profile.dynamic_link_libraries) _context._working_settings.link_frameworks.extend(profile.link_frameworks) _context._working_settings.source_files.extend(profile.source_files) _context._working_settings.linker_flags.extend(profile.linker_flags) if _context._working_settings.output_directory is None: _context._working_settings.output_directory = profile.output_directory if _context._working_settings.output_binary_extension is None: _context._working_settings.output_binary_extension = profile.output_binary_extension yield _context._working_settings finally: _context._script_data.current_settings.append(_context._working_settings) _context._working_settings = None #----------------------------------------------------------------------------- # [SECTION] public api #----------------------------------------------------------------------------- def set_hot_reload_target(target_name: str): _context._script_data.reload_target_name = target_name def add_source_files(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.source_files.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_source_files.append(arg) elif _context._config_name is not None: for arg in args: _context._config_source_files.append(arg) elif _context._target_name is not None: for arg in args: _context._target_source_files.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_source_files.append(arg) else: raise Exception("'add_source_files(...)' must be called within a scope") def add_static_link_libraries(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.static_link_libraries.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_static_link_libraries.append(arg) elif _context._config_name is not None: for arg in args: _context._config_static_link_libraries.append(arg) elif _context._target_name is not None: for arg in args: _context._target_static_link_libraries.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_static_link_libraries.append(arg) else: raise Exception("'add_static_link_libraries(...)' must be called within a scope") def add_dynamic_link_libraries(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.dynamic_link_libraries.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_dynamic_link_libraries.append(arg) elif _context._config_name is not None: for arg in args: _context._config_dynamic_link_libraries.append(arg) elif _context._target_name is not None: for arg in args: _context._target_dynamic_link_libraries.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_dynamic_link_libraries.append(arg) else: raise Exception("'add_dynamic_link_libraries(...)' must be called within a scope") def add_link_frameworks(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.link_frameworks.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_link_frameworks.append(arg) elif _context._config_name is not None: for arg in args: _context._config_link_frameworks.append(arg) elif _context._target_name is not None: for arg in args: _context._target_link_frameworks.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_link_frameworks.append(arg) else: raise Exception("'add_link_frameworks(...)' must be called within a scope") def add_definitions(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.definitions.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_definitions.append(arg) elif _context._config_name is not None: for arg in args: _context._config_definitions.append(arg) elif _context._target_name is not None: for arg in args: _context._target_definitions.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_definitions.append(arg) else: raise Exception("'add_definitions(...)' must be called within a scope") def add_compiler_flags(*args): for arg in args: _context._working_settings.compiler_flags.append(arg) def add_linker_flags(*args): for arg in args: _context._working_settings.linker_flags.append(arg) def add_include_directories(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.include_directories.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_include_directories.append(arg) elif _context._config_name is not None: for arg in args: _context._config_include_directories.append(arg) elif _context._target_name is not None: for arg in args: _context._target_include_directories.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_include_directories.append(arg) else: raise Exception("'add_include_directories(...)' must be called within a scope") def add_link_directories(*args): if _context._working_settings is not None: for arg in args: _context._working_settings.link_directories.append(arg) elif _context._platform_name is not None: for arg in args: _context._platform_link_directories.append(arg) elif _context._config_name is not None: for arg in args: _context._config_link_directories.append(arg) elif _context._target_name is not None: for arg in args: _context._target_link_directories.append(arg) elif _context._script_data.project_name is not None: for arg in args: _context._project_link_directories.append(arg) else: raise Exception("'add_link_directories(...)' must be called within a scope") def set_output_binary(binary: str): if _context._working_settings is not None: _context._working_settings.output_binary = binary elif _context._platform_name is not None: _context._platform_output_binary = binary elif _context._config_name is not None: _context._config_output_binary = binary elif _context._target_name is not None: _context._target_output_binary = binary else: raise Exception("'set_output_binary(...)' must be called within a correct scope") def set_output_binary_extension(extension: str): _context._working_settings.output_binary_extension = extension def set_output_directory(directory: str): if _context._working_settings is not None: _context._working_settings.output_directory = directory elif _context._platform_name is not None: _context._platform_output_directory = directory elif _context._config_name is not None: _context._config_output_directory = directory elif _context._target_name is not None: _context._target_output_directory = directory elif _context._script_data.project_name is not None: _context._project_output_directory = directory else: raise Exception("'set_output_directory(...)' must be called within a scope") def set_pre_target_build_step(code: str): _context._working_settings.pre_build_step = code def set_post_target_build_step(code: str): _context._working_settings.post_build_step = code def add_profile(*, compiler_filter=None, platform_filter=None, configuration_filter=None, target_filter=None, target_type_filter=None, definitions=[], include_directories=[], link_directories=[], static_link_libraries=[], dynamic_link_libraries=[], link_frameworks=[], source_files=[], compiler_flags=[], linker_flags=[], output_directory: str = None, output_binary_extension: str = None): profile = _CompilerProfile() profile.compiler_filter = compiler_filter profile.platform_filter = platform_filter profile.configuration_filter = configuration_filter profile.target_filter = target_filter profile.target_type_filter = target_type_filter profile.definitions = definitions profile.include_directories = include_directories profile.output_directory = output_directory profile.link_directories = link_directories profile.static_link_libraries = static_link_libraries profile.dynamic_link_libraries = dynamic_link_libraries profile.link_frameworks = link_frameworks profile.source_files = source_files profile.compiler_flags = compiler_flags profile.linker_flags = linker_flags profile.output_binary_extension = output_binary_extension _context._profiles.append(profile) def get_script_data() -> ScriptData: return _context._script_data