__version__ = "1.0.0" ############################################################################### # Info # ############################################################################### # very poorly written & dirty system, to be cleaned up later ############################################################################### # Modules # ############################################################################### from enum import Enum from contextlib import contextmanager ############################################################################### # Enums # ############################################################################### class TargetType(Enum): NONE = 0 STATIC_LIBRARY = 0 DYNAMIC_LIBRARY = 1 EXECUTABLE = 2 ############################################################################### # 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 # inherited from project self.project_name = None self.reload_target_name = None self.working_directory = None self.registered_configurations = [] class CompilerProfile: def __init__(self, compilers = None, platforms = None, configurations = None, targets = None): self.compilers = compilers self.platforms = platforms self.configurations = configurations self.targets = targets 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.targets is not None: found = False for target in self.targets: if target == _context.target_name: found = True break if not found: return False if self.configurations is not None: found = False for config_name in self.configurations: if config_name == _context.config_name: found = True break if not found: return False if self.platforms is not None: found = False for platform_name in self.platforms: if platform_name == _context.platform_name: found = True break if not found: return False if self.compilers is not None: found = False for name in self.compilers: if name == _context.working_settings.name: found = True break if not found: return False return True class BuildContext: def __init__(self): # persistent data self.current_settings = [] # current project self.project_name = None self.reload_target_name = None self.working_directory = None self.registered_configurations = [] self.profiles = [] # current target self.target_name = None self.target_type = None self.target_lock_file = None self.target_reloadable = False # current config self.config_name = None # current platform self.platform_name = None # 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_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 = [] ############################################################################### # Global Context # ############################################################################### _context = BuildContext() ############################################################################### # Project # ############################################################################### @contextmanager def project(name: str): try: # persistent data _context.current_settings = [] # current project _context.project_name = name _context.reload_target_name = None _context.working_directory = "./" _context.registered_configurations = [] _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 def add_configuration(name: str): _context.registered_configurations.append(name) def set_working_directory(directory: str): _context.working_directory = directory def set_hot_reload_target(target_name: str): _context.reload_target_name = target_name ############################################################################### # Target # ############################################################################### @contextmanager def target(name: str, target_type: TargetType, 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 = [] ############################################################################### # Configuration # ############################################################################### @contextmanager def configuration(name: str): try: _context.config_name = 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 = [] ############################################################################### # Platform # ############################################################################### @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 = [] ############################################################################### # Compiler # ############################################################################### @contextmanager def compiler(name: str): try: compiler = CompilerSettings(name) # inherited from platform compiler.platform_name = _context.platform_name # inherited from config 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 project compiler.project_name = _context.project_name compiler.reload_target_name = _context.reload_target_name compiler.working_directory = _context.working_directory compiler.registered_configurations = _context.registered_configurations # inherited 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 _context.working_settings = compiler # check 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.current_settings.append(_context.working_settings) _context.working_settings = None 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.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.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.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.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.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.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.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.project_name is not None: _context._project_output_directory = directory else: raise Exception("'set_output_directory(...)' must be called within a scope") def add_compiler_flags_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.compiler_flags = args _context.profiles.append(profile) def add_include_directories_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.include_directories = args _context.profiles.append(profile) def add_definitions_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.definitions = args _context.profiles.append(profile) def add_link_directories_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.link_directories = args _context.profiles.append(profile) def add_static_link_libraries_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.static_link_libraries = args _context.profiles.append(profile) def add_dynamic_link_libraries_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.dynamic_link_libraries = args _context.profiles.append(profile) def add_link_frameworks_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.link_frameworks = args _context.profiles.append(profile) def add_source_files_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.source_files = args _context.profiles.append(profile) def add_linker_flags_profile(targets, configurations, platforms, compilers, *args): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.linker_flags = args _context.profiles.append(profile) def set_output_binary_extension_profile(targets, configurations, platforms, compilers, extension): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.output_binary_extension = extension _context.profiles.append(profile) def set_output_directory_profile(targets, configurations, platforms, compilers, irectory): profile = CompilerProfile(compilers, platforms, configurations, targets) profile.output_directory = irectory _context.profiles.append(profile) 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 ############################################################################### # Query # ############################################################################### def get_platform() -> str: return _context.platform_name def get_target() -> str: return _context.target_name def get_target_type() -> str: return _context.target_type def get_configuration() -> str: return _context.config_name def get_compiler() -> str: return _context.working_settings.name