1
0

refac: profile system [pypi]
All checks were successful
Deploy Tools / build-package (push) Successful in 34s

This commit is contained in:
Jonathan Hoffstadt 2024-09-05 15:16:45 -05:00
parent 0e49ee908b
commit 9d5da19a22
7 changed files with 1550 additions and 1375 deletions

View File

@ -1,6 +1,37 @@
## Pilot Light Build
Under construction.
## Information
### Background
The **pl-build** project is a child of the larger [Pilot Light](https://github.com/PilotLightTech/pilotlight) project. In this larger project, we do not have a "build system" per se. Instead we prefer to write batch/bash scripts that directly call the compiler. If the project was an end user product, this would be the end of it. However, this is not the case. It is meant to be easily be extended through adding additional extensions and being used as a "pilot light" to start new projects. With this comes a couple issues. Extensions are meant to be cross platform so users need the ability to easily add new binaries for all target platforms with minimal duplication. Users shouldn't need to be bash or batch scripting experts to build new targets for all platforms and shouldn't need to test the build scripts continuously on each platform.
### Requirements
* minimize duplicated information
* fine-grained control over compilation settings
* support unity builds
* support hot reloading
* generate scripts that can be used separately
* easily extended
* easy to add new platforms and compilers
* extremely light weight
* easy to customize and extended
* no preference on editor/IDE
* doesn't pretend different platforms don't exist
* entire build system can be understood in an hour
Another way of putting it, is we want to focus on what matters to build binaries. Ultimately this is just compiler & linker settings. We don't want to think about the differences in bash/batch syntax.
### Benefits Over Raw Scripts
Prior to this system, when we were writing scripts directly, we did what every developer does and began overengineering the build scripts to improve things like information duplication, configuration options, etc. However, this led to more compilicated scripts that were harder to debug and heavily deviated between platforms since batch/command are very different in features and syntax. It became hard for newcomers to work on and encouraged differences to creep in between the way individual build scripts worked.
Using pl-build allows the clever/overengineered solutions to be accomplished at a higher level by the user in python. In the end, build scripts are output that are very straight forward and much easier to debug. It also makes it easier to implement new backends to target new compilers, platforms, scripting languages, etc.
### Alternatives
Keep in mind, **we don't want a build system**. We don't want to hide away details or abstract away control. We ultimately just need a tool to assist in generating the scripts for each platform.
What about other CMake (and others)? They are bloated over complicated messes. They are also overkill for what we need (see Requirements section above).
## Documentation
Under construction.

View File

@ -1,5 +1,4 @@
import pl_build as pl
from pl_build import _context, __version__
import pl_build.core as pl
from pathlib import PurePath
class plLinuxHelper:
@ -51,7 +50,26 @@ class plLinuxHelper:
def delete_file(self, file):
self.buffer += ' ' * self.indent + 'rm -f "' + file + '"\n'
def generate_build(name, platform, compiler, user_options):
def generate_build(name, user_options = None):
platform = "Linux"
compiler = "gcc"
data = pl.get_script_data()
# retrieve settings supported by this platform/compiler & add
# default extensions if the user did not
platform_settings = []
for settings in data.current_settings:
if settings.platform_name == platform and settings.name == compiler:
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)
helper = plLinuxHelper()
@ -64,10 +82,10 @@ def generate_build(name, platform, compiler, user_options):
helper.add_line("#!/bin/bash")
helper.add_spacing()
helper.add_comment("Auto Generated by:")
helper.add_comment('"pl_build.py" version: ' + __version__)
helper.add_comment('"pl_build.py" version: ' + data.version)
helper.add_spacing()
helper.add_comment("Project: " + _context.project_name)
helper.add_comment("Project: " + data.project_name)
helper.add_spacing()
helper.add_title("Development Setup")
@ -101,9 +119,9 @@ def generate_build(name, platform, compiler, user_options):
helper.add_spacing()
# set default config
if _context.registered_configurations:
if data.registered_configurations:
helper.add_comment("default configuration")
helper.add_line("PL_CONFIG=" + _context.registered_configurations[0])
helper.add_line("PL_CONFIG=" + data.registered_configurations[0])
helper.add_spacing()
# check command line args for config
@ -123,20 +141,7 @@ def generate_build(name, platform, compiler, user_options):
# 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:
for register_config in data.registered_configurations:
# filter this config only settings
config_only_settings = []
@ -148,7 +153,7 @@ def generate_build(name, platform, compiler, user_options):
continue
# find hot reload target
if _context.reload_target_name is not None:
if data.reload_target_name is not None:
hot_reload = True
helper.add_title("configuration | " + register_config)
@ -181,7 +186,7 @@ def generate_build(name, platform, compiler, user_options):
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.add_line('if pidof -x "' + PurePath(data.reload_target_name).stem + '" -o $$ >/dev/null;then')
helper.set_indent(4)
helper.add_line('PL_HOT_RELOAD_STATUS=1')
helper.print_space()
@ -216,124 +221,122 @@ def generate_build(name, platform, compiler, user_options):
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 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()
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_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('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_raw('PL_INCLUDE_DIRECTORIES="')
for include in settings.include_directories:
helper.add_raw('-I' + include + ' ')
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_raw('PL_LINK_DIRECTORIES="')
for link in settings.link_directories:
helper.add_raw('-L' + link + ' ')
helper.add_raw('"\n')
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()
helper.add_raw('PL_COMPILER_FLAGS="')
for flag in settings.compiler_flags:
helper.add_raw(flag + ' ')
helper.add_raw('"\n')
# 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()
helper.add_raw('PL_LINKER_FLAGS="')
for flag in settings.linker_flags:
helper.add_raw(flag + ' ')
helper.add_raw('"\n')
# print results
helper.add_comment("print results")
helper.print_line("${CYAN}Results: ${NC} ${PL_RESULT}")
helper.print_line("${CYAN}~~~~~~~~~~~~~~~~~~~~~~${NC}")
helper.add_spacing()
helper.add_raw('PL_STATIC_LINK_LIBRARIES="')
for link in settings.static_link_libraries:
helper.add_raw('-l:' + link + '.a ')
helper.add_raw('"\n')
if settings.post_build_step is not None:
helper.add_line(settings.post_build_step)
helper.add_spacing()
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}")
if not settings.reloadable and hot_reload:
helper.add_comment("hot reload skip")
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)
@ -352,4 +355,4 @@ def generate_build(name, platform, compiler, user_options):
helper.add_comment('return CWD to previous CWD')
helper.add_line('popd >/dev/null')
helper.write_file(_context.working_directory + "/" + name)
helper.write_file(name)

View File

@ -1,5 +1,4 @@
import pl_build as pl
from pl_build import _context, __version__
import pl_build.core as pl
from pathlib import PurePath
class plAppleHelper:
@ -51,10 +50,28 @@ class plAppleHelper:
def delete_file(self, file):
self.buffer += ' ' * self.indent + 'rm -f "' + file + '"\n'
def generate_build(name, platform, compiler, user_options):
def generate_build(name, user_options = None):
platform = "Darwin"
compiler = "clang"
data = pl.get_script_data()
helper = plAppleHelper()
# retrieve settings supported by this platform/compiler & add
# default extensions if the user did not
platform_settings = []
for settings in data.current_settings:
if settings.platform_name == platform and settings.name == compiler:
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)
hot_reload = False
###############################################################################
@ -63,10 +80,10 @@ def generate_build(name, platform, compiler, user_options):
helper.add_spacing()
helper.add_comment("Auto Generated by:")
helper.add_comment('"pl_build.py" version: ' + __version__)
helper.add_comment('"pl_build.py" version: ' + data.version)
helper.add_spacing()
helper.add_comment("Project: " + _context.project_name)
helper.add_comment("Project: " + data.project_name)
helper.add_spacing()
helper.add_title("Development Setup")
@ -104,9 +121,9 @@ def generate_build(name, platform, compiler, user_options):
helper.add_spacing()
# set default config
if _context.registered_configurations:
if data.registered_configurations:
helper.add_comment("default configuration")
helper.add_line("PL_CONFIG=" + _context.registered_configurations[0])
helper.add_line("PL_CONFIG=" + data.registered_configurations[0])
helper.add_spacing()
# check command line args for config
@ -126,20 +143,7 @@ def generate_build(name, platform, compiler, user_options):
# 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:
for register_config in data.registered_configurations:
# filter this config only settings
config_only_settings = []
@ -151,7 +155,7 @@ def generate_build(name, platform, compiler, user_options):
continue
# check if hot reload
if _context.reload_target_name is not None:
if data.reload_target_name is not None:
hot_reload = True
helper.add_title("configuration | " + register_config)
@ -184,7 +188,7 @@ def generate_build(name, platform, compiler, user_options):
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('running_count=$(ps aux | grep -v grep | grep -ci "' + PurePath(data.reload_target_name).stem + '")')
helper.add_line('if [ $running_count -gt 0 ]')
helper.add_line('then')
helper.set_indent(4)
@ -220,131 +224,129 @@ def generate_build(name, platform, compiler, user_options):
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 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()
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_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_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_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_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_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_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_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_SOURCES="')
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:
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')
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('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_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()
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.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()
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()
# 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}")
# 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()
# 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)
@ -363,4 +365,4 @@ def generate_build(name, platform, compiler, user_options):
helper.add_comment('return CWD to previous CWD')
helper.add_line('popd >/dev/null')
helper.write_file(_context.working_directory + "/" + name)
helper.write_file(name)

454
pl_build/backend_win32.py Normal file
View File

@ -0,0 +1,454 @@
import pl_build.core as pl
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, user_options = None):
platform = "Windows"
compiler = "msvc"
data = pl.get_script_data()
# retrieve settings supported by this platform/compiler & add
# default extensions if the user did not
platform_settings = []
for settings in data.current_settings:
if settings.platform_name == platform and settings.name == compiler:
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)
helper = plWin32Helper()
hot_reload = False
###############################################################################
# Intro #
###############################################################################
helper.add_comment("Project: " + data.project_name)
helper.add_comment("Auto Generated by:")
helper.add_comment('"pl_build.py" version: ' + data.version)
helper.add_spacing()
helper.add_comment("Project: " + data.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 data.registered_configurations:
helper.add_comment("default configuration")
helper.add_line("@set PL_CONFIG=" + data.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 data.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()
for register_config in data.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 data.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 " + data.reload_target_name + " exe is running")
helper.add_line("@echo off")
helper.add_line('2>nul (>>"' + data.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:
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 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(name)

800
pl_build/core.py Normal file
View File

@ -0,0 +1,800 @@
# 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.1"
#-----------------------------------------------------------------------------
# [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.compilers = None
self.platforms = None
self.configurations = None
self.targets = 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.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
#-----------------------------------------------------------------------------
# [SECTION] global context
#-----------------------------------------------------------------------------
_context = BuildContext()
#-----------------------------------------------------------------------------
# [SECTION] context managers
#-----------------------------------------------------------------------------
@contextmanager
def project(name: str):
try:
# persistent data
_context._script_data = ScriptData()
_context._script_data.project_name = name
# current profiles
_context._profiles = []
# current target
_context._target_name = None
_context._target_type = None
_context._target_lock_file = None
_context._target_reloadable = False
# current config
_context._config_name = None
# current platform
_context._platform_name = None
# working settings
_context._working_settings = None
# project scope
_context._project_output_directory = None
_context._project_definitions = []
_context._project_include_directories = []
_context._project_link_directories = []
_context._project_static_link_libraries = []
_context._project_dynamic_link_libraries = []
_context._project_link_frameworks = []
_context._project_source_files = []
yield None
finally:
pass
@contextmanager
def target(name: str, target_type: TargetType = TargetType.EXECUTABLE, reloadable: bool = False):
try:
_context._target_name = name
_context._target_type = target_type
_context._target_lock_file = "lock.tmp"
_context._target_reloadable = reloadable
yield None
finally:
_context._target_name = None
_context._target_type = None
_context._target_lock_file = None
_context._target_reloadable = False
_context._target_output_directory = None
_context._target_definitions = []
_context._target_include_directories = []
_context._target_link_directories = []
_context._target_static_link_libraries = []
_context._target_dynamic_link_libraries = []
_context._target_link_frameworks = []
_context._target_source_files = []
@contextmanager
def configuration(name: str):
try:
_context._config_name = name
# register configurations if not already
exists = False
for config in _context._script_data.registered_configurations:
if config == name:
exists = True
break
if not exists:
_context._script_data.registered_configurations.append(name)
yield None
finally:
_context._config_name = None
_context._config_output_directory = None
_context._config_definitions = []
_context._config_include_directories = []
_context._config_link_directories = []
_context._config_static_link_libraries = []
_context._config_dynamic_link_libraries = []
_context._config_link_frameworks = []
_context._config_source_files = []
@contextmanager
def platform(name: str):
try:
_context._platform_name = name
yield None
finally:
_context._platform_name = None
_context._platform_output_directory = None
_context._platform_definitions = []
_context._platform_include_directories = []
_context._platform_link_directories = []
_context._platform_static_link_libraries = []
_context._platform_dynamic_link_libraries = []
_context._platform_link_frameworks = []
_context._platform_source_files = []
@contextmanager
def compiler(name: str):
try:
compiler = CompilerSettings(name)
# inherited from platform
compiler.platform_name = _context._platform_name
# inherited from configuration
compiler.config_name = _context._config_name
# inherited from target
compiler.target_name = _context._target_name
compiler.target_type = _context._target_type
compiler.lock_file = _context._target_lock_file
compiler.reloadable = _context._target_reloadable
# inherited from various scopes
if _context._platform_output_directory is not None:
compiler.output_directory = _context._platform_output_directory
elif _context._config_output_directory is not None:
compiler.output_directory = _context._config_output_directory
elif _context._target_output_directory is not None:
compiler.output_directory = _context._target_output_directory
elif _context._project_output_directory is not None:
compiler.output_directory = _context._project_output_directory
if _context._platform_output_binary is not None:
compiler.output_binary = _context._platform_output_binary
elif _context._config_output_binary is not None:
compiler.output_binary = _context._config_output_binary
elif _context._target_output_binary is not None:
compiler.output_binary = _context._target_output_binary
compiler.link_directories = _context._project_link_directories + _context._target_link_directories + _context._config_link_directories + _context._platform_link_directories
compiler.definitions = _context._project_definitions + _context._target_definitions + _context._config_definitions + _context._platform_definitions
compiler.include_directories = _context._project_include_directories + _context._target_include_directories + _context._config_include_directories + _context._platform_include_directories
compiler.static_link_libraries = _context._project_static_link_libraries + _context._target_static_link_libraries + _context._config_static_link_libraries + _context._platform_static_link_libraries
compiler.dynamic_link_libraries = _context._project_dynamic_link_libraries + _context._target_dynamic_link_libraries + _context._config_dynamic_link_libraries + _context._platform_dynamic_link_libraries
compiler.link_frameworks = _context._project_link_frameworks + _context._target_link_frameworks + _context._config_link_frameworks + _context._platform_link_frameworks
compiler.source_files = _context._project_source_files + _context._target_source_files + _context._config_source_files + _context._platform_source_files
# this is set here since profile.is_active() looks at it
_context._working_settings = compiler
# inherited from matching profiles
for profile in _context._profiles:
if profile.is_active():
_context._working_settings.definitions.extend(profile.definitions)
_context._working_settings.compiler_flags.extend(profile.compiler_flags)
_context._working_settings.include_directories.extend(profile.include_directories)
_context._working_settings.link_directories.extend(profile.link_directories)
_context._working_settings.static_link_libraries.extend(profile.static_link_libraries)
_context._working_settings.dynamic_link_libraries.extend(profile.dynamic_link_libraries)
_context._working_settings.link_frameworks.extend(profile.link_frameworks)
_context._working_settings.source_files.extend(profile.source_files)
_context._working_settings.linker_flags.extend(profile.linker_flags)
if _context._working_settings.output_directory is None:
_context._working_settings.output_directory = profile.output_directory
if _context._working_settings.output_binary_extension is None:
_context._working_settings.output_binary_extension = profile.output_binary_extension
yield _context._working_settings
finally:
_context._script_data.current_settings.append(_context._working_settings)
_context._working_settings = None
#-----------------------------------------------------------------------------
# [SECTION] public api
#-----------------------------------------------------------------------------
def set_hot_reload_target(target_name: str):
_context._script_data.reload_target_name = target_name
def add_source_files(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.source_files.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_source_files.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_source_files.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_source_files.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_source_files.append(arg)
else:
raise Exception("'add_source_files(...)' must be called within a scope")
def add_static_link_libraries(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.static_link_libraries.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_static_link_libraries.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_static_link_libraries.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_static_link_libraries.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_static_link_libraries.append(arg)
else:
raise Exception("'add_static_link_libraries(...)' must be called within a scope")
def add_dynamic_link_libraries(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.dynamic_link_libraries.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_dynamic_link_libraries.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_dynamic_link_libraries.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_dynamic_link_libraries.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_dynamic_link_libraries.append(arg)
else:
raise Exception("'add_dynamic_link_libraries(...)' must be called within a scope")
def add_link_frameworks(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.link_frameworks.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_link_frameworks.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_link_frameworks.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_link_frameworks.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_link_frameworks.append(arg)
else:
raise Exception("'add_link_frameworks(...)' must be called within a scope")
def add_definitions(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.definitions.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_definitions.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_definitions.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_definitions.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_definitions.append(arg)
else:
raise Exception("'add_definitions(...)' must be called within a scope")
def add_compiler_flags(*args):
for arg in args:
_context._working_settings.compiler_flags.append(arg)
def add_linker_flags(*args):
for arg in args:
_context._working_settings.linker_flags.append(arg)
def add_include_directories(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.include_directories.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_include_directories.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_include_directories.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_include_directories.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_include_directories.append(arg)
else:
raise Exception("'add_include_directories(...)' must be called within a scope")
def add_link_directories(*args):
if _context._working_settings is not None:
for arg in args:
_context._working_settings.link_directories.append(arg)
elif _context._platform_name is not None:
for arg in args:
_context._platform_link_directories.append(arg)
elif _context._config_name is not None:
for arg in args:
_context._config_link_directories.append(arg)
elif _context._target_name is not None:
for arg in args:
_context._target_link_directories.append(arg)
elif _context._script_data.project_name is not None:
for arg in args:
_context._project_link_directories.append(arg)
else:
raise Exception("'add_link_directories(...)' must be called within a scope")
def set_output_binary(binary: str):
if _context._working_settings is not None:
_context._working_settings.output_binary = binary
elif _context._platform_name is not None:
_context._platform_output_binary = binary
elif _context._config_name is not None:
_context._config_output_binary = binary
elif _context._target_name is not None:
_context._target_output_binary = binary
else:
raise Exception("'set_output_binary(...)' must be called within a correct scope")
def set_output_binary_extension(extension: str):
_context._working_settings.output_binary_extension = extension
def set_output_directory(directory: str):
if _context._working_settings is not None:
_context._working_settings.output_directory = directory
elif _context._platform_name is not None:
_context._platform_output_directory = directory
elif _context._config_name is not None:
_context._config_output_directory = directory
elif _context._target_name is not None:
_context._target_output_directory = directory
elif _context._script_data.project_name is not None:
_context._project_output_directory = directory
else:
raise Exception("'set_output_directory(...)' must be called within a scope")
def set_pre_target_build_step(code: str):
_context._working_settings.pre_build_step = code
def set_post_target_build_step(code: str):
_context._working_settings.post_build_step = code
def add_compiler_flags_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.compiler_flags = args
_context._profiles.append(profile)
def add_include_directories_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.include_directories = args
_context._profiles.append(profile)
def add_definitions_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.definitions = args
_context._profiles.append(profile)
def add_link_directories_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.link_directories = args
_context._profiles.append(profile)
def add_static_link_libraries_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.static_link_libraries = args
_context._profiles.append(profile)
def add_dynamic_link_libraries_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.dynamic_link_libraries = args
_context._profiles.append(profile)
def add_link_frameworks_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.link_frameworks = args
_context._profiles.append(profile)
def add_source_files_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.source_files = args
_context._profiles.append(profile)
def add_linker_flags_profile(*args, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.linker_flags = args
_context._profiles.append(profile)
def set_output_binary_extension_profile(extension: str, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.output_binary_extension = extension
_context._profiles.append(profile)
def set_output_directory_profile(directory: str, **kwargs):
profile = _CompilerProfile()
if "targets" in kwargs:
profile.targets = kwargs["targets"]
if "configurations" in kwargs:
profile.configurations = kwargs["configurations"]
if "compilers" in kwargs:
profile.compilers = kwargs["compilers"]
if "platforms" in kwargs:
profile.platforms = kwargs["platforms"]
profile.output_directory = directory
_context._profiles.append(profile)
def get_script_data() -> ScriptData:
return _context._script_data

View File

@ -1,666 +0,0 @@
__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

View File

@ -1,449 +0,0 @@
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)