commit 0e49ee908be6b26344f97e95cbd7f5b52516f516 Author: Jonathan Hoffstadt Date: Thu Sep 5 11:48:31 2024 -0500 initial commit diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..f85b72a --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,42 @@ +name: Deploy Tools + +on: + + push: + + branches: + - master + +jobs: + + build-package: + + runs-on: ubuntu-latest + + steps: + + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip setuptools wheel build + + - name: Build package + run: | + cd $GITHUB_WORKSPACE + echo [build-system] >> pyproject.toml + echo 'requires = ["setuptools", "wheel"]' >> pyproject.toml + echo 'build-backend = "setuptools.build_meta"' >> pyproject.toml + python3 -m build + + - name: PyPi Deployment + if: ${{contains(github.event.head_commit.message, '[pypi]')}} + run: | + python3 -m pip install twine + python3 -m twine upload dist/* -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} --skip-existing + + - name: Test PyPi Deployment + if: ${{contains(github.event.head_commit.message, '[testpypi]')}} + run: | + python3 -m pip install twine + python3 -m twine upload --repository testpypi dist/* -u __token__ -p ${{ secrets.TEST_PYPI_API_TOKEN }} --skip-existing \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1e8f53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +pl_build/__pycache__/ +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f592ce3 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +## Pilot Light Build +Under construction. + +## Information +Under construction. + diff --git a/pl_build/__init__.py b/pl_build/__init__.py new file mode 100644 index 0000000..882e540 --- /dev/null +++ b/pl_build/__init__.py @@ -0,0 +1 @@ +pass diff --git a/pl_build/pl_build.py b/pl_build/pl_build.py new file mode 100644 index 0000000..2029f63 --- /dev/null +++ b/pl_build/pl_build.py @@ -0,0 +1,666 @@ +__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 diff --git a/pl_build/pl_build_linux.py b/pl_build/pl_build_linux.py new file mode 100644 index 0000000..399a7b9 --- /dev/null +++ b/pl_build/pl_build_linux.py @@ -0,0 +1,355 @@ +import pl_build as pl +from pl_build import _context, __version__ +from pathlib import PurePath + +class plLinuxHelper: + + def __init__(self): + self.buffer = '' + self.indent = 0 + + def set_indent(self, indent): + self.indent = indent + + def write_file(self, file_path): + with open(file_path, "w") as file: + file.write(self.buffer) + + def add_line(self, line): + self.buffer += ' ' * self.indent + line + '\n' + + def add_raw(self, text): + self.buffer += text + + def add_spacing(self, count = 1): + self.buffer += '\n' * count + + def add_title(self, title): + line_length = 80 + padding = (line_length - 2 - len(title)) / 2 + self.buffer += "# " + "#" * line_length + "\n" + self.buffer += "# #" + " " * int(padding) + title + " " * int(padding + 0.5) + "#" + "\n" + self.buffer += ("# " + "#" * line_length) + "\n" + + def add_sub_title(self, title): + line_length = 80 + padding = (line_length - 2 - len(title)) / 2 + self.buffer += "#" + "~" * int(padding) + " " + title + " " + "~" * int(padding + 0.5) + "\n" + + def add_comment(self, comment): + self.buffer += ' ' * self.indent + '# ' + comment + '\n' + + def print_line(self, text): + self.buffer += ' ' * self.indent + 'echo ' + text + '\n' + + def print_space(self): + self.buffer += ' ' * self.indent + 'echo\n' + + def create_directory(self, directory): + self.buffer += ' ' * self.indent + 'mkdir -p "' + directory + '"\n' + + def delete_file(self, file): + self.buffer += ' ' * self.indent + 'rm -f "' + file + '"\n' + +def generate_build(name, platform, compiler, user_options): + + helper = plLinuxHelper() + + hot_reload = False + + ############################################################################### + # Intro # + ############################################################################### + + helper.add_line("#!/bin/bash") + helper.add_spacing() + helper.add_comment("Auto Generated by:") + helper.add_comment('"pl_build.py" version: ' + __version__) + + helper.add_spacing() + helper.add_comment("Project: " + _context.project_name) + helper.add_spacing() + + helper.add_title("Development Setup") + helper.add_spacing() + + helper.add_comment("colors") + helper.add_line("BOLD=$'\\e[0;1m'") + helper.add_line("RED=$'\\e[0;31m'") + helper.add_line("RED_BG=$'\\e[0;41m'") + helper.add_line("GREEN=$'\\e[0;32m'") + helper.add_line("GREEN_BG=$'\\e[0;42m'") + helper.add_line("CYAN=$'\\e[0;36m'") + helper.add_line("MAGENTA=$'\\e[0;35m'") + helper.add_line("YELLOW=$'\\e[0;33m'") + helper.add_line("WHITE=$'\\e[0;97m'") + helper.add_line("NC=$'\\e[0m'") + helper.add_spacing() + + helper.add_comment('find directory of this script') + helper.add_line("SOURCE=${BASH_SOURCE[0]}") + helper.add_line('while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink') + helper.add_line(' DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )') + helper.add_line(' SOURCE=$(readlink "$SOURCE")') + helper.add_line(' [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located') + helper.add_line('done') + helper.add_line('DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )') + helper.add_spacing() + + helper.add_comment('make script directory CWD') + helper.add_line('pushd $DIR >/dev/null') + helper.add_spacing() + + # set default config + if _context.registered_configurations: + helper.add_comment("default configuration") + helper.add_line("PL_CONFIG=" + _context.registered_configurations[0]) + helper.add_spacing() + + # check command line args for config + helper.add_comment('check command line args for configuration') + helper.add_line('while getopts ":c:" option; do') + helper.add_line(' case $option in') + helper.add_line(' c) # set conf') + helper.add_line(' PL_CONFIG=$OPTARG;;') + helper.add_line(' \\?) # Invalid option') + helper.add_line(' echo "Error: Invalid option"') + helper.add_line(' exit;;') + helper.add_line(' esac') + helper.add_line('done') + helper.add_spacing() + + # if _context.pre_build_step is not None: + # helper.add_line(_context.pre_build_step) + # helper.add_spacing() + + # filter linux only settings + platform_settings = [] + for settings in _context.current_settings: + if settings.platform_name == platform: + if settings.output_binary_extension is None: + if settings.target_type == pl.TargetType.EXECUTABLE: + settings.output_binary_extension = "" + elif settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + settings.output_binary_extension = ".so" + elif settings.target_type == pl.TargetType.STATIC_LIBRARY: + settings.output_binary_extension = ".a" + platform_settings.append(settings) + + for register_config in _context.registered_configurations: + + # filter this config only settings + config_only_settings = [] + for settings in platform_settings: + if settings.config_name == register_config: + config_only_settings.append(settings) + + if len(config_only_settings) == 0: + continue + + # find hot reload target + if _context.reload_target_name is not None: + hot_reload = True + + helper.add_title("configuration | " + register_config) + helper.add_spacing() + + helper.add_line('if [[ "$PL_CONFIG" == "' + register_config + '" ]]; then') + helper.add_spacing() + + output_dirs = set() + for settings in config_only_settings: + output_dirs.add(settings.output_directory) + + # create output directories + helper.add_comment("create output directory(s)") + for dir in output_dirs: + helper.create_directory(dir) + helper.add_spacing() + + lock_files = set() + for settings in config_only_settings: + lock_files.add(settings.lock_file) + helper.add_comment("create lock file(s)") + for lock_file in lock_files: + helper.add_line('echo LOCKING > "' + settings.output_directory + '/' + lock_file + '"') + helper.add_spacing() + + if hot_reload: + helper.add_comment('check if this is a reload') + helper.add_line('PL_HOT_RELOAD_STATUS=0') + helper.add_spacing() + + helper.add_comment("# let user know if hot reloading") + helper.add_line('if pidof -x "' + PurePath(_context.reload_target_name).stem + '" -o $$ >/dev/null;then') + helper.set_indent(4) + helper.add_line('PL_HOT_RELOAD_STATUS=1') + helper.print_space() + helper.print_line('echo ${BOLD}${WHITE}${RED_BG}--------${GREEN_BG} HOT RELOADING ${RED_BG}--------${NC}') + helper.print_line('echo') + helper.set_indent(0) + helper.add_line('else') + helper.set_indent(4) + helper.add_comment('cleanup binaries if not hot reloading') + helper.print_line('PL_HOT_RELOAD_STATUS=0') + + # delete old binaries & files + for settings in config_only_settings: + if settings.source_files: + if settings.name == compiler: + if settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + helper.delete_file(settings.output_directory + '/' + settings.output_binary + '_*' + settings.output_binary_extension) + elif settings.target_type == pl.TargetType.EXECUTABLE: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + elif settings.target_type == pl.TargetType.STATIC_LIBRARY: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + + helper.add_spacing() + helper.set_indent(0) + if hot_reload: + helper.add_spacing() + helper.add_line("fi") + + # other targets + for settings in config_only_settings: + helper.add_sub_title(settings.target_name + " | " + register_config) + helper.add_spacing() + + if settings.name == compiler: + + if not settings.reloadable and hot_reload: + helper.add_comment('skip during hot reload') + helper.add_line('if [ $PL_HOT_RELOAD_STATUS -ne 1 ]; then') + helper.add_spacing() + + if settings.pre_build_step is not None: + helper.add_line(settings.pre_build_step) + helper.add_spacing() + + helper.add_line("PL_RESULT=${BOLD}${GREEN}Successful.${NC}") + helper.add_raw('PL_DEFINES="') + for define in settings.definitions: + helper.add_raw('-D' + define + " ") + helper.add_raw('"\n') + + helper.add_raw('PL_INCLUDE_DIRECTORIES="') + for include in settings.include_directories: + helper.add_raw('-I' + include + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_LINK_DIRECTORIES="') + for link in settings.link_directories: + helper.add_raw('-L' + link + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_COMPILER_FLAGS="') + for flag in settings.compiler_flags: + helper.add_raw(flag + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_LINKER_FLAGS="') + for flag in settings.linker_flags: + helper.add_raw(flag + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_STATIC_LINK_LIBRARIES="') + for link in settings.static_link_libraries: + helper.add_raw('-l:' + link + '.a ') + helper.add_raw('"\n') + + helper.add_raw('PL_DYNAMIC_LINK_LIBRARIES="') + for link in settings.dynamic_link_libraries: + helper.add_raw('-l' + link + ' ') + helper.add_raw('"\n') + + if settings.target_type == pl.TargetType.STATIC_LIBRARY: + helper.add_comment('# run compiler only') + helper.print_space() + helper.print_line('${YELLOW}Step: ' + settings.target_name +'${NC}') + helper.print_line('${YELLOW}~~~~~~~~~~~~~~~~~~~${NC}') + helper.print_line('${CYAN}Compiling...${NC}') + helper.add_spacing() + + helper.add_comment('each file must be compiled separately') + for source in settings.source_files: + source_as_path = PurePath(source) + helper.add_line('gcc -c $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS ' + source + ' -o "./' + settings.output_directory + '/' + source_as_path.stem + '.o"') + helper.add_spacing() + helper.add_comment('combine object files into a static lib') + helper.add_line('ar rcs ./' + settings.output_directory + '/' + settings.output_binary + '.a ./' + settings.output_directory + '/*.o') + helper.add_line('rm ./' + settings.output_directory + '/*.o') + helper.add_spacing() + + elif settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + helper.add_raw('PL_SOURCES="') + for source in settings.source_files: + helper.add_raw(source + ' ') + helper.add_raw('"\n') + helper.add_spacing() + + helper.add_comment('run compiler (and linker)') + helper.print_space() + helper.print_line('${YELLOW}Step: ' + settings.target_name +'${NC}') + helper.print_line('${YELLOW}~~~~~~~~~~~~~~~~~~~${NC}') + helper.print_line('${CYAN}Compiling and Linking...${NC}') + helper.add_line('gcc -shared $PL_SOURCES $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS $PL_INCLUDE_DIRECTORIES $PL_LINK_DIRECTORIES $PL_LINKER_FLAGS $PL_STATIC_LINK_LIBRARIES $PL_DYNAMIC_LINK_LIBRARIES -o "./' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension +'"') + helper.add_spacing() + + elif settings.target_type == pl.TargetType.EXECUTABLE: + helper.add_raw('PL_SOURCES="') + for source in settings.source_files: + helper.add_raw(source + ' ') + helper.add_raw('"\n') + helper.add_spacing() + + helper.add_comment('run compiler (and linker)') + helper.print_space() + helper.print_line('${YELLOW}Step: ' + settings.target_name +'${NC}') + helper.print_line('${YELLOW}~~~~~~~~~~~~~~~~~~~${NC}') + helper.print_line('${CYAN}Compiling and Linking...${NC}') + helper.add_line('gcc $PL_SOURCES $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS $PL_INCLUDE_DIRECTORIES $PL_LINK_DIRECTORIES $PL_LINKER_FLAGS $PL_STATIC_LINK_LIBRARIES $PL_DYNAMIC_LINK_LIBRARIES -o "./' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension +'"') + helper.add_spacing() + + # check build status + helper.add_comment("check build status") + helper.add_line("if [ $? -ne 0 ]") + helper.add_line("then") + helper.add_line(" PL_RESULT=${BOLD}${RED}Failed.${NC}") + helper.add_line("fi") + helper.add_spacing() + + # print results + helper.add_comment("print results") + helper.print_line("${CYAN}Results: ${NC} ${PL_RESULT}") + helper.print_line("${CYAN}~~~~~~~~~~~~~~~~~~~~~~${NC}") + helper.add_spacing() + + if settings.post_build_step is not None: + helper.add_line(settings.post_build_step) + helper.add_spacing() + + if not settings.reloadable and hot_reload: + helper.add_comment("hot reload skip") + helper.add_line("fi") + helper.add_spacing() + + helper.add_comment("delete lock file(s)") + for lock_file in lock_files: + helper.delete_file(settings.output_directory + '/' + lock_file) + helper.add_spacing() + + # end of config + helper.add_comment('~' * 40) + helper.add_comment('end of ' + register_config) + helper.add_line('fi') + helper.add_spacing() + + helper.add_spacing() + # if _context.post_build_step is not None: + # helper.add_line(_context.post_build_step) + # helper.add_spacing() + helper.add_comment('return CWD to previous CWD') + helper.add_line('popd >/dev/null') + + helper.write_file(_context.working_directory + "/" + name) \ No newline at end of file diff --git a/pl_build/pl_build_macos.py b/pl_build/pl_build_macos.py new file mode 100644 index 0000000..55b7c60 --- /dev/null +++ b/pl_build/pl_build_macos.py @@ -0,0 +1,366 @@ +import pl_build as pl +from pl_build import _context, __version__ +from pathlib import PurePath + +class plAppleHelper: + + def __init__(self): + self.buffer = '' + self.indent = 0 + + def set_indent(self, indent): + self.indent = indent + + def write_file(self, file_path): + with open(file_path, "w") as file: + file.write(self.buffer) + + def add_line(self, line): + self.buffer += ' ' * self.indent + line + '\n' + + def add_raw(self, text): + self.buffer += text + + def add_spacing(self, count = 1): + self.buffer += '\n' * count + + def add_title(self, title): + line_length = 80 + padding = (line_length - 2 - len(title)) / 2 + self.buffer += "# " + "#" * line_length + "\n" + self.buffer += "# #" + " " * int(padding) + title + " " * int(padding + 0.5) + "#" + "\n" + self.buffer += ("# " + "#" * line_length) + "\n" + + def add_sub_title(self, title): + line_length = 80 + padding = (line_length - 2 - len(title)) / 2 + self.buffer += "#" + "~" * int(padding) + " " + title + " " + "~" * int(padding + 0.5) + "\n" + + def add_comment(self, comment): + self.buffer += ' ' * self.indent + '# ' + comment + '\n' + + def print_line(self, text): + self.buffer += ' ' * self.indent + 'echo ' + text + '\n' + + def print_space(self): + self.buffer += ' ' * self.indent + 'echo\n' + + def create_directory(self, directory): + self.buffer += ' ' * self.indent + 'mkdir -p "' + directory + '"\n' + + def delete_file(self, file): + self.buffer += ' ' * self.indent + 'rm -f "' + file + '"\n' + +def generate_build(name, platform, compiler, user_options): + + helper = plAppleHelper() + + hot_reload = False + + ############################################################################### + # Intro # + ############################################################################### + + helper.add_spacing() + helper.add_comment("Auto Generated by:") + helper.add_comment('"pl_build.py" version: ' + __version__) + helper.add_spacing() + + helper.add_comment("Project: " + _context.project_name) + helper.add_spacing() + + helper.add_title("Development Setup") + helper.add_spacing() + + helper.add_comment("colors") + helper.add_line("BOLD=$'\\e[0;1m'") + helper.add_line("RED=$'\\e[0;31m'") + helper.add_line("RED_BG=$'\\e[0;41m'") + helper.add_line("GREEN=$'\\e[0;32m'") + helper.add_line("GREEN_BG=$'\\e[0;42m'") + helper.add_line("CYAN=$'\\e[0;36m'") + helper.add_line("MAGENTA=$'\\e[0;35m'") + helper.add_line("YELLOW=$'\\e[0;33m'") + helper.add_line("WHITE=$'\\e[0;97m'") + helper.add_line("NC=$'\\e[0m'") + helper.add_spacing() + + helper.add_comment('find directory of this script') + helper.add_line("SOURCE=${BASH_SOURCE[0]}") + helper.add_line('while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink') + helper.add_line(' DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )') + helper.add_line(' SOURCE=$(readlink "$SOURCE")') + helper.add_line(' [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located') + helper.add_line('done') + helper.add_line('DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )') + helper.add_spacing() + + helper.add_comment('get architecture (intel or apple silicon)') + helper.add_line('ARCH="$(uname -m)"') + helper.add_spacing() + + helper.add_comment('make script directory CWD') + helper.add_line('pushd $DIR >/dev/null') + helper.add_spacing() + + # set default config + if _context.registered_configurations: + helper.add_comment("default configuration") + helper.add_line("PL_CONFIG=" + _context.registered_configurations[0]) + helper.add_spacing() + + # check command line args for config + helper.add_comment('check command line args for configuration') + helper.add_line('while getopts ":c:" option; do') + helper.add_line(' case $option in') + helper.add_line(' c) # set conf') + helper.add_line(' PL_CONFIG=$OPTARG;;') + helper.add_line(' \\?) # Invalid option') + helper.add_line(' echo "Error: Invalid option"') + helper.add_line(' exit;;') + helper.add_line(' esac') + helper.add_line('done') + helper.add_spacing() + + # if _context.pre_build_step is not None: + # helper.add_line(_context.pre_build_step) + # helper.add_spacing() + + # filter macos only settings + platform_settings = [] + for settings in _context.current_settings: + if settings.platform_name == platform: + if settings.output_binary_extension is None: + if settings.target_type == pl.TargetType.EXECUTABLE: + settings.output_binary_extension = "" + elif settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + settings.output_binary_extension = ".dylib" + elif settings.target_type == pl.TargetType.STATIC_LIBRARY: + settings.output_binary_extension = ".a" + platform_settings.append(settings) + + for register_config in _context.registered_configurations: + + # filter this config only settings + config_only_settings = [] + for settings in platform_settings: + if settings.config_name == register_config: + config_only_settings.append(settings) + + if len(config_only_settings) == 0: + continue + + # check if hot reload + if _context.reload_target_name is not None: + hot_reload = True + + helper.add_title("configuration | " + register_config) + helper.add_spacing() + + helper.add_line('if [[ "$PL_CONFIG" == "' + register_config + '" ]]; then') + helper.add_spacing() + + output_dirs = set() + for settings in config_only_settings: + output_dirs.add(settings.output_directory) + + # create output directories + helper.add_comment("create output directory(s)") + for dir in output_dirs: + helper.create_directory(dir) + helper.add_spacing() + + lock_files = set() + for settings in config_only_settings: + lock_files.add(settings.lock_file) + helper.add_comment("create lock file(s)") + for lock_file in lock_files: + helper.add_line('echo LOCKING > "' + settings.output_directory + '/' + lock_file + '"') + helper.add_spacing() + + if hot_reload: + helper.add_comment('check if this is a reload') + helper.add_line('PL_HOT_RELOAD_STATUS=0') + helper.add_spacing() + + helper.add_comment("# let user know if hot reloading") + helper.add_line('running_count=$(ps aux | grep -v grep | grep -ci "' + PurePath(_context.reload_target_name).stem + '")') + helper.add_line('if [ $running_count -gt 0 ]') + helper.add_line('then') + helper.set_indent(4) + helper.add_line('PL_HOT_RELOAD_STATUS=1') + helper.print_space() + helper.print_line('echo ${BOLD}${WHITE}${RED_BG}--------${GREEN_BG} HOT RELOADING ${RED_BG}--------${NC}') + helper.print_line('echo') + helper.set_indent(0) + helper.add_line('else') + helper.set_indent(4) + helper.add_comment('cleanup binaries if not hot reloading') + helper.print_line('PL_HOT_RELOAD_STATUS=0') + + # delete old binaries & files + for settings in config_only_settings: + if settings.source_files: + if settings.name == compiler: + if settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + helper.delete_file(settings.output_directory + '/' + settings.output_binary + '_*' + settings.output_binary_extension) + elif settings.target_type == pl.TargetType.EXECUTABLE: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + elif settings.target_type == pl.TargetType.STATIC_LIBRARY: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + + helper.set_indent(0) + if hot_reload: + helper.add_spacing() + helper.add_line("fi") + + # other targets + for settings in config_only_settings: + helper.add_sub_title(settings.target_name + " | " + register_config) + helper.add_spacing() + + if settings.name == compiler: + + if not settings.reloadable and hot_reload: + helper.add_comment('skip during hot reload') + helper.add_line('if [ $PL_HOT_RELOAD_STATUS -ne 1 ]; then') + helper.add_spacing() + + if settings.pre_build_step is not None: + helper.add_line(settings.pre_build_step) + helper.add_spacing() + + helper.add_line("PL_RESULT=${BOLD}${GREEN}Successful.${NC}") + helper.add_raw('PL_DEFINES="') + for define in settings.definitions: + helper.add_raw('-D' + define + " ") + helper.add_raw('"\n') + + helper.add_raw('PL_INCLUDE_DIRECTORIES="') + for include in settings.include_directories: + helper.add_raw('-I' + include + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_LINK_DIRECTORIES="') + for link in settings.link_directories: + helper.add_raw('-L' + link + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_COMPILER_FLAGS="') + for flag in settings.compiler_flags: + helper.add_raw(flag + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_LINKER_FLAGS="') + for flag in settings.linker_flags: + helper.add_raw(flag + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_STATIC_LINK_LIBRARIES="') + for link in settings.static_link_libraries: + helper.add_raw('-l' + link + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_DYNAMIC_LINK_LIBRARIES="') + for link in settings.dynamic_link_libraries: + helper.add_raw('-l' + link + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_SOURCES="') + for source in settings.source_files: + helper.add_raw(source + ' ') + helper.add_raw('"\n') + + helper.add_raw('PL_LINK_FRAMEWORKS="') + for link in settings.link_frameworks: + helper.add_raw('-framework ' + link + ' ') + helper.add_raw('"\n') + + helper.add_spacing() + helper.add_comment('add flags for specific hardware') + helper.add_line('if [[ "$ARCH" == "arm64" ]]; then') + helper.add_line(' PL_COMPILER_FLAGS+="-arch arm64 "') + helper.add_line('else') + helper.add_line(' PL_COMPILER_FLAGS+="-arch x86_64 "') + helper.add_line('fi') + helper.add_spacing() + + if settings.target_type == pl.TargetType.STATIC_LIBRARY: + helper.add_comment('# run compiler only') + helper.print_space() + helper.print_line('${YELLOW}Step: ' + settings.target_name +'${NC}') + helper.print_line('${YELLOW}~~~~~~~~~~~~~~~~~~~${NC}') + helper.print_line('${CYAN}Compiling...${NC}') + helper.add_spacing() + + helper.add_comment('each file must be compiled separately') + for source in settings.source_files: + source_as_path = PurePath(source) + helper.add_line( 'clang -c $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS ' + source + ' -o "./' + settings.output_directory + '/' + source_as_path.stem + '.o"') + helper.add_spacing() + helper.add_comment('combine object files into a static lib') + helper.add_line('ar rcs ./' + settings.output_directory + '/lib' + settings.output_binary + '.a ./' + settings.output_directory + '/*.o') + helper.add_line('rm ./' + settings.output_directory + '/*.o') + helper.add_spacing() + + elif settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + helper.add_comment('run compiler (and linker)') + helper.print_space() + helper.print_line('${YELLOW}Step: ' + settings.target_name +'${NC}') + helper.print_line('${YELLOW}~~~~~~~~~~~~~~~~~~~${NC}') + helper.print_line('${CYAN}Compiling and Linking...${NC}') + helper.add_line('clang -shared $PL_SOURCES $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS $PL_INCLUDE_DIRECTORIES $PL_LINK_DIRECTORIES $PL_LINKER_FLAGS $PL_STATIC_LINK_LIBRARIES $PL_DYNAMIC_LINK_LIBRARIES $PL_LINK_FRAMEWORKS -o "./' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension +'"') + helper.add_spacing() + + elif settings.target_type == pl.TargetType.EXECUTABLE: + helper.add_comment('run compiler (and linker)') + helper.print_space() + helper.print_line('${YELLOW}Step: ' + settings.target_name +'${NC}') + helper.print_line('${YELLOW}~~~~~~~~~~~~~~~~~~~${NC}') + helper.print_line('${CYAN}Compiling and Linking...${NC}') + helper.add_line('clang $PL_SOURCES $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS $PL_INCLUDE_DIRECTORIES $PL_LINK_DIRECTORIES $PL_LINKER_FLAGS $PL_STATIC_LINK_LIBRARIES $PL_DYNAMIC_LINK_LIBRARIES -o "./' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension +'"') + helper.add_spacing() + + # check build status + helper.add_comment("check build status") + helper.add_line("if [ $? -ne 0 ]") + helper.add_line("then") + helper.add_line(" PL_RESULT=${BOLD}${RED}Failed.${NC}") + helper.add_line("fi") + helper.add_spacing() + + # print results + helper.add_comment("print results") + helper.print_line("${CYAN}Results: ${NC} ${PL_RESULT}") + helper.print_line("${CYAN}~~~~~~~~~~~~~~~~~~~~~~${NC}") + helper.add_spacing() + + if settings.post_build_step is not None: + helper.add_line(settings.post_build_step) + helper.add_spacing() + + if not settings.reloadable and hot_reload: + helper.add_comment("hot reload skip") + helper.add_line("fi") + helper.add_spacing() + + helper.add_comment("delete lock file(s)") + for lock_file in lock_files: + helper.delete_file(settings.output_directory + '/' + lock_file) + helper.add_spacing() + + # end of config + helper.add_comment('~' * 40) + helper.add_comment('end of ' + register_config) + helper.add_line('fi') + helper.add_spacing() + + helper.add_spacing() + # if _context.post_build_step is not None: + # helper.add_line(_context.post_build_step) + # helper.add_spacing() + helper.add_comment('return CWD to previous CWD') + helper.add_line('popd >/dev/null') + + helper.write_file(_context.working_directory + "/" + name) \ No newline at end of file diff --git a/pl_build/pl_build_win32.py b/pl_build/pl_build_win32.py new file mode 100644 index 0000000..65be0d9 --- /dev/null +++ b/pl_build/pl_build_win32.py @@ -0,0 +1,449 @@ +import pl_build as pl +from pl_build import _context, __version__ + +class plWin32Helper: + + def __init__(self): + self.buffer = '\n' + self.indent = 0 + + def set_indent(self, indent): + self.indent = indent + + def write_file(self, file_path): + with open(file_path, "w") as file: + file.write(self.buffer) + + def add_line(self, line): + self.buffer += ' ' * self.indent + line + '\n' + + def add_raw(self, text): + self.buffer += text + + def add_label(self, label): + self.buffer += ':' + label + '\n' + + def add_spacing(self, count = 1): + self.buffer += '\n' * count + + def add_title(self, title): + line_length = 80 + padding = (line_length - 2 - len(title)) / 2 + self.buffer += ":: " + "#" * line_length + "\n" + self.buffer += ":: #" + " " * int(padding) + title + " " * int(padding + 0.5) + "#" + "\n" + self.buffer += (":: " + "#" * line_length) + "\n" + + def add_sub_title(self, title): + line_length = 80 + padding = (line_length - 2 - len(title)) / 2 + self.buffer += "::" + "~" * int(padding) + " " + title + " " + "~" * int(padding + 0.5) + "\n" + + def add_comment(self, comment): + self.buffer += ' ' * self.indent + ':: ' + comment + '\n' + + def print_line(self, text): + self.buffer += ' ' * self.indent + '@echo ' + text + '\n' + + def print_space(self): + self.buffer += '@echo.\n' + + def create_directory(self, directory): + self.buffer += ' ' * self.indent + '@if not exist "' + directory + '" @mkdir "' + directory + '"\n\n' + + def delete_file(self, file): + self.buffer += ' ' * self.indent + '@if exist "' + file + '"' + self.buffer += ' del "' + file + '"\n' + +def generate_build(name, platform, compiler, user_options): + + helper = plWin32Helper() + + hot_reload = False + + ############################################################################### + # Intro # + ############################################################################### + + helper.add_comment("Project: " + _context.project_name) + helper.add_comment("Auto Generated by:") + helper.add_comment('"pl_build.py" version: ' + __version__) + helper.add_spacing() + helper.add_comment("Project: " + _context.project_name) + helper.add_spacing() + + helper.add_title("Development Setup") + helper.add_spacing() + + helper.add_comment('keep environment variables modifications local') + helper.add_line("@setlocal") + helper.add_spacing() + + helper.add_comment("make script directory CWD") + helper.add_line('@pushd %~dp0') + helper.add_line('@set dir=%~dp0') + helper.add_spacing() + + # try to setup dev environment + if isinstance(user_options, dict): + if "dev env setup" in user_options: + if user_options["dev env setup"] == True: + helper.add_comment("modify PATH to find vcvarsall.bat") + helper.add_line('@set PATH=C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build;%PATH%') + helper.add_line('@set PATH=C:\\Program Files\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build;%PATH%') + helper.add_line('@set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build;%PATH%') + helper.add_line('@set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build;%PATH%') + helper.add_line('@set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise/VC\\Auxiliary\\Build;%PATH%') + helper.add_spacing() + + helper.add_comment("setup environment for MSVC dev tools") + helper.add_line('@call vcvarsall.bat amd64 > nul') + helper.add_spacing() + + helper.add_comment("default compilation result") + helper.add_line('@set PL_RESULT=Successful.') + helper.add_spacing() + + # set default config + if _context.registered_configurations: + helper.add_comment("default configuration") + helper.add_line("@set PL_CONFIG=" + _context.registered_configurations[0]) + helper.add_spacing() + + # check command line args for config + helper.add_comment("check command line args for configuration") + helper.add_label("CheckConfiguration") + helper.add_line('@if "%~1"=="-c" (@set PL_CONFIG=%2) & @shift & @shift & @goto CheckConfiguration') + for register_config in _context.registered_configurations: + helper.add_line('@if "%PL_CONFIG%" equ "' + register_config + '" ( goto ' + register_config + ' )') + helper.add_spacing() + + # if _context.pre_build_step is not None: + # helper.add_line(_context.pre_build_step) + # helper.add_spacing() + + # filter win32 only settings & apply defaults + platform_settings = [] + for settings in _context.current_settings: + if settings.platform_name == platform: + if settings.output_binary_extension is None: + if settings.target_type == pl.TargetType.EXECUTABLE: + settings.output_binary_extension = ".exe" + elif settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + settings.output_binary_extension = ".dll" + elif settings.target_type == pl.TargetType.STATIC_LIBRARY: + settings.output_binary_extension = ".lib" + platform_settings.append(settings) + + for register_config in _context.registered_configurations: + + # filter this config only settings + config_only_settings = [] + for settings in platform_settings: + if settings.config_name == register_config: + config_only_settings.append(settings) + + if len(config_only_settings) == 0: + continue + + # find hot reload target + if _context.reload_target_name is not None: + hot_reload = True + + helper.add_title("configuration | " + register_config) + helper.add_spacing() + helper.add_label(register_config) + helper.add_spacing() + + output_dirs = set() + for settings in config_only_settings: + output_dirs.add(settings.output_directory) + + # create output directories + helper.add_comment("create output directories") + for dir in output_dirs: + helper.create_directory(dir) + + lock_files = set() + for settings in config_only_settings: + lock_files.add(settings.lock_file) + helper.add_comment("create lock file(s)") + for lock_file in lock_files: + helper.add_line('@echo LOCKING > "' + settings.output_directory + '/' + lock_file + '"') + + helper.add_spacing() + + if hot_reload: + + helper.add_comment("check if this is a hot reload") + helper.add_line("@set PL_HOT_RELOAD_STATUS=0") + + helper.add_spacing() + helper.add_comment("hack to see if " + _context.reload_target_name + " exe is running") + helper.add_line("@echo off") + helper.add_line('2>nul (>>"' + _context.reload_target_name + '.exe" echo off) && (@set PL_HOT_RELOAD_STATUS=0) || (@set PL_HOT_RELOAD_STATUS=1)') + + helper.add_spacing() + helper.add_comment("let user know if hot reloading") + helper.add_line("@if %PL_HOT_RELOAD_STATUS% equ 1 (") + helper.set_indent(4) + helper.print_line("-------- HOT RELOADING --------") + helper.set_indent(0) + helper.add_line(")") + helper.set_indent(0) + helper.add_spacing() + helper.add_comment("cleanup binaries if not hot reloading") + helper.add_line("@if %PL_HOT_RELOAD_STATUS% equ 0 (\n") + helper.set_indent(4) + + # delete old binaries & files + for settings in config_only_settings: + if settings.source_files: + if settings.name == compiler: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension) + if settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + '_*' + settings.output_binary_extension) + helper.delete_file(settings.output_directory + '/' + settings.output_binary + '_*.pdb') + elif settings.target_type == pl.TargetType.EXECUTABLE: + helper.delete_file(settings.output_directory + '/' + settings.output_binary + '_*.pdb') + + helper.set_indent(0) + if hot_reload: + helper.add_spacing() + helper.add_line(")") + helper.add_spacing() + + # other targets + for settings in config_only_settings: + helper.add_sub_title(settings.target_name + " | " + register_config) + helper.add_spacing() + + if settings.name == compiler: + + if not settings.reloadable and hot_reload: + helper.add_comment("skip during hot reload") + helper.add_line('@if %PL_HOT_RELOAD_STATUS% equ 1 goto ' + "Exit_" + settings.target_name) + helper.add_spacing() + + if settings.pre_build_step is not None: + helper.add_line(settings.pre_build_step) + helper.add_spacing() + + if settings.definitions: + helper.add_raw('@set PL_DEFINES=') + for define in settings.definitions: + helper.add_raw("-D" + define + " ") + helper.add_spacing() + + if settings.include_directories: + helper.add_raw('@set PL_INCLUDE_DIRECTORIES=') + for include in settings.include_directories: + helper.add_raw('-I"' + include + '" ') + helper.add_spacing() + + if settings.link_directories: + helper.add_raw('@set PL_LINK_DIRECTORIES=') + for link in settings.link_directories: + helper.add_raw('-LIBPATH:"' + link + '" ') + helper.add_spacing() + + if settings.compiler_flags: + helper.add_raw('@set PL_COMPILER_FLAGS=') + for flag in settings.compiler_flags: + helper.add_raw(flag + " ") + helper.add_spacing() + + settings.linker_flags.extend(["-incremental:no"]) + if settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + settings.linker_flags.extend(["-noimplib", "-noexp"]) + + if settings.linker_flags: + helper.add_raw('@set PL_LINKER_FLAGS=') + for flag in settings.linker_flags: + helper.add_raw(flag + " ") + helper.add_spacing() + + if settings.static_link_libraries: + helper.add_raw('@set PL_STATIC_LINK_LIBRARIES=') + for link in settings.static_link_libraries: + helper.add_raw(link + ".lib ") + helper.add_spacing() + + if settings.dynamic_link_libraries: + helper.add_raw('@set PL_DYNAMIC_LINK_LIBRARIES=') + for link in settings.dynamic_link_libraries: + helper.add_raw(link + ".lib ") + helper.add_spacing() + + if settings.target_type == pl.TargetType.STATIC_LIBRARY: + + helper.add_spacing() + helper.add_comment("run compiler only") + helper.print_space() + helper.print_line("Step: " + settings.target_name +"") + helper.print_line("~~~~~~~~~~~~~~~~~~~~~~") + helper.print_line("Compiling...") + + helper.add_spacing() + helper.add_comment('each file must be compiled separately') + for source in settings.source_files: + sub_buffer = "" + if settings.include_directories: + sub_buffer += " %PL_INCLUDE_DIRECTORIES%" + if settings.definitions: + sub_buffer += " %PL_DEFINES%" + if settings.compiler_flags: + sub_buffer += " %PL_COMPILER_FLAGS%" + helper.add_line('cl -c' + sub_buffer + " " + source + ' -Fo"' + settings.output_directory + '/"') + helper.add_spacing() + + helper.add_spacing() + helper.add_comment("check build status") + helper.add_line("@set PL_BUILD_STATUS=%ERRORLEVEL%") + + helper.add_spacing() + helper.add_comment("if failed, skip linking") + helper.add_raw("@if %PL_BUILD_STATUS% NEQ 0 (\n") + helper.add_raw(" @echo Compilation Failed with error code: %PL_BUILD_STATUS%\n") + helper.add_raw(" @set PL_RESULT=Failed.\n") + helper.add_raw(" goto " + 'Cleanup' + register_config) + helper.add_raw("\n)\n") + + helper.add_spacing() + helper.add_comment('link object files into a shared lib') + helper.add_raw("@echo Linking...\n") + helper.add_raw('lib -nologo -OUT:"' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension + '" "' + settings.output_directory + '/*.obj"\n') + + elif settings.target_type == pl.TargetType.DYNAMIC_LIBRARY: + + helper.add_raw('@set PL_SOURCES=') + for source in settings.source_files: + helper.add_raw('"' + source + '" ') + helper.add_spacing() + + sub_buffer0 = "" + sub_buffer1 = "" + sub_buffer2 = "" + if settings.include_directories: + sub_buffer0 += " %PL_INCLUDE_DIRECTORIES%" + if settings.definitions: + sub_buffer0 += " %PL_DEFINES%" + if settings.compiler_flags: + sub_buffer0 += " %PL_COMPILER_FLAGS%" + if settings.linker_flags: + sub_buffer1 = " %PL_LINKER_FLAGS%" + if settings.link_directories: + sub_buffer2 += " %PL_LINK_DIRECTORIES%" + if settings.static_link_libraries: + sub_buffer2 += " %PL_STATIC_LINK_LIBRARIES%" + if settings.dynamic_link_libraries: + sub_buffer2 += " %PL_DYNAMIC_LINK_LIBRARIES%" + + helper.add_spacing() + helper.add_comment("run compiler (and linker)") + helper.print_space() + helper.add_raw('@echo Step: ' + settings.target_name +'\n') + helper.add_raw('@echo ~~~~~~~~~~~~~~~~~~~~~~\n') + helper.add_raw('@echo Compiling and Linking...\n') + helper.add_raw('cl' + sub_buffer0 + ' %PL_SOURCES% -Fe"' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension + '" -Fo"' + settings.output_directory + '/" -LD -link' + sub_buffer1 + ' -PDB:"' + settings.output_directory + '/' + settings.output_binary + '_%random%.pdb"' + sub_buffer2 + "\n\n") + + helper.add_comment("check build status") + helper.add_raw("@set PL_BUILD_STATUS=%ERRORLEVEL%\n") + + helper.add_raw("\n:: failed\n") + helper.add_raw("@if %PL_BUILD_STATUS% NEQ 0 (\n") + helper.add_raw(" @echo Compilation Failed with error code: %PL_BUILD_STATUS%\n") + helper.add_raw(" @set PL_RESULT=Failed.\n") + helper.add_raw(" goto " + 'Cleanup' + register_config) + helper.add_raw("\n)\n") + + elif settings.target_type == pl.TargetType.EXECUTABLE: + + helper.add_raw('@set PL_SOURCES=') + for source in settings.source_files: + helper.add_raw('"' + source + '" ') + helper.add_spacing(2) + + sub_buffer0 = "" + sub_buffer1 = "" + sub_buffer2 = "" + if settings.include_directories: + sub_buffer0 += " %PL_INCLUDE_DIRECTORIES%" + if settings.definitions: + sub_buffer0 += " %PL_DEFINES%" + if settings.compiler_flags: + sub_buffer0 += " %PL_COMPILER_FLAGS%" + if settings.linker_flags: + sub_buffer1 = " %PL_LINKER_FLAGS%" + if settings.link_directories: + sub_buffer2 += " %PL_LINK_DIRECTORIES%" + if settings.static_link_libraries: + sub_buffer2 += " %PL_STATIC_LINK_LIBRARIES%" + if settings.dynamic_link_libraries: + sub_buffer2 += " %PL_DYNAMIC_LINK_LIBRARIES%" + + helper.add_comment("run compiler (and linker)") + helper.print_space() + helper.print_line('Step: ' + settings.target_name +'') + helper.print_line('~~~~~~~~~~~~~~~~~~~~~~') + helper.print_line('Compiling and Linking...') + + + if hot_reload: + helper.add_raw('\n:: skip actual compilation if hot reloading\n') + helper.add_raw('@if %PL_HOT_RELOAD_STATUS% equ 1 ( goto ' + 'Cleanup' + settings.target_name + ' )\n') + + helper.add_raw('\n:: call compiler\n') + helper.add_raw('cl' + sub_buffer0 + ' %PL_SOURCES% -Fe"' + settings.output_directory + '/' + settings.output_binary + settings.output_binary_extension + '" -Fo"' + settings.output_directory + '/" -link' + sub_buffer1 + ' -PDB:"' + settings.output_directory + '/' + settings.output_binary + '_%random%.pdb"' + sub_buffer2 + "\n\n") + + helper.add_comment("check build status") + helper.add_raw("@set PL_BUILD_STATUS=%ERRORLEVEL%\n") + helper.add_raw("\n:: failed\n") + helper.add_raw("@if %PL_BUILD_STATUS% NEQ 0 (\n") + helper.add_raw(" @echo Compilation Failed with error code: %PL_BUILD_STATUS%\n") + helper.add_raw(" @set PL_RESULT=Failed.\n") + helper.add_raw(" goto " + 'Cleanup' + register_config) + helper.add_raw("\n)\n") + + helper.add_spacing() + + helper.add_comment("print results") + helper.print_line("Result:  %PL_RESULT%") + helper.add_raw("@echo ~~~~~~~~~~~~~~~~~~~~~~\n") + helper.add_spacing() + + if settings.post_build_step is not None: + helper.add_line(settings.post_build_step) + helper.add_spacing() + + if not settings.reloadable: + helper.add_label("Exit_" + settings.target_name) + helper.add_spacing() + + helper.add_line(':Cleanup' + register_config) + helper.add_spacing() + helper.print_line('Cleaning...') + + helper.add_spacing() + helper.add_comment("delete obj files(s)") + for dir in output_dirs: + helper.add_line('@del "' + dir + '/*.obj" > nul 2> nul') + + helper.add_spacing() + helper.add_comment("delete lock file(s)") + for lock_file in lock_files: + helper.delete_file(settings.output_directory + '/' + lock_file) + + helper.add_spacing() + helper.add_comment('~' * 40) + helper.add_comment('end of ' + settings.config_name + ' configuration') + helper.add_line("goto ExitLabel") + helper.add_spacing() + + helper.add_label("ExitLabel") + helper.add_spacing() + # if _context.post_build_step is not None: + # helper.add_line(_context.post_build_step) + # helper.add_spacing() + helper.add_comment("return CWD to previous CWD") + helper.add_line('@popd') + helper.write_file(_context.working_directory + "/" + name) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0194a39 --- /dev/null +++ b/setup.py @@ -0,0 +1,42 @@ +import setuptools +from codecs import open +import os + +wip_version = "1.0.1" + +def readme(): + try: + with open('README.md', encoding='utf-8') as f: + return f.read() + except IOError: + return 'Not Found' + +setuptools.setup( + name="pl_build", + version=wip_version, + license='MIT', + python_requires='>=3.6', + author="Jonathan Hoffstadt", + author_email="jonathanhoffstadt@yahoo.com", + description='Pilot Light Build', + long_description=readme(), + long_description_content_type="text/markdown", + url='https://github.com/PilotLightTech/pilotlight', # Optional + packages=['pl_build'], + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows :: Windows 10', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + package_data={ # Optional + 'pl_build': ['README.md'] + } +)