1
0
pl-build/pl_build/backend_win32.py
Jonathan Hoffstadt 8d6c6f3af1
All checks were successful
Deploy Tools / build-package (push) Successful in 21s
fix: windows backend escape code issues
2024-10-16 12:17:12 -05:00

459 lines
21 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pl_build.core as pl
import pathlib
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):
p = pathlib.PureWindowsPath(file)
self.buffer += ' ' * self.indent + '@if exist "' + file + '"'
self.buffer += ' del "' + str(p) + '"\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
helper.add_comment("modify PATH to find vcvarsall.bat")
helper.add_line('@if exist "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build" @set PATH=C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build" @set PATH=C:\\Program Files\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Auxiliary/Build" @set PATH=C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files/Microsoft Visual Studio/2019/Professional/VC/Auxiliary/Build" @set PATH=C:\\Program Files\\Microsoft Visual Studio\\2019\\Professional\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build" @set PATH=C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build" @set PATH=C:\\Program Files\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files (x86)/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build" @set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build" @set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files (x86)/Microsoft Visual Studio/2022/Professional/VC/Auxiliary/Build" @set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\Professional\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/VC/Auxiliary/Build" @set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files (x86)/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build" @set PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build;%PATH%')
helper.add_line('@if exist "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build" @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()
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:
p = pathlib.PureWindowsPath(dir + '/*.obj')
helper.add_line('@del "' + str(p) + '" > 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)