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)