1
0
pl-build/pl_build/core.py
Jonathan Hoffstadt 0fff761253
All checks were successful
Deploy Tools / build-package (push) Successful in 29s
chore: version 1.0.12
2024-11-18 19:58:25 -06:00

629 lines
24 KiB
Python

# 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.12"
#-----------------------------------------------------------------------------
# [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_output_binary = 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_output_binary = 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_output_binary = 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