#!/usr/bin/python3 # # This script generates these config files: # # Saved/UnrealBuildTool/BuildConfiguration.xml # Integration.uproject # Integration.code-workspace # Makefile # Source/Integration/lpx-paths.hpp # # The BuildConfiguration.xml file specifies that this project will # use visual studio code (on linux) or visual studio (on windows). # It can't be checked into git because it differs by platform. # # Unreal needs "Integration.uproject". This config file tells # UnrealBuildTool what version of the unreal engine to link this game # with. It also specifies a couple of other game-specific configuration # settings. It can't easily be checked into git because it contains # a GUID which is specific to your local machine. We generate # Integration.uproject from scratch. Later, the unreal # editor will inject the engine GUID into the file. # # VSCode needs "Integration.code-workspace," it tells vscode how to # compile this game and how to run it under the debugger. It can't # be checked into git because it contains many hardwired # paths. # # The UnrealBuildTool which is included with the UnrealEngine can # generate a rough draft of Integration.code-workspace. However, the # file it generates is not well-configured for our luprex-related # needs. This python script uses UnrealBuildTool to generate the rough # draft, then it loads Integration.code-workspace into RAM, edits it # in RAM, and writes a new, improved version back out. # # We don't really need a Makefile, since Unreal games are build using # UnrealBuildTool, not make. But, having a one-liner makefile can at # least make it obvious what command you're supposed to type to build # things. UnrealBuildTool creates a Makefile, but we delete that, and # replace it with a simple one-liner. # # The file lpx-paths.hpp contains hardwired absolute paths of the # Luprex DLL and Luprex root path. Eventually, we're going to write # C++ code to calculate these dynamically, so that they don't need to # be hardwired in. But this will do for now. # import sys, os, json from pathlib import Path # # Some handy utility functions # def writefile(fn, str): with open(fn, "w") as f: f.write(str) # # These are some directory paths that we will need. # INTEGRATION = os.path.dirname(os.path.abspath(sys.argv[0])) UNREALENGINE = os.environ["HOME"] + "/UnrealEngine" UNREALBUILDTOOL = f"{UNREALENGINE}/Engine/Binaries/DotNET/UnrealBuildTool/UnrealBuildTool.dll" # # Change to the target directory. # Remove any existing project files. # os.chdir(INTEGRATION) Path("Saved/UnrealBuildTool").mkdir(parents=True, exist_ok=True) Path("Saved/UnrealBuildTool/BuildConfiguration.xml").unlink(missing_ok=True) Path("Integration.uproject").unlink(missing_ok=True) Path("Integration.code-workspace").unlink(missing_ok=True) Path("Makefile").unlink(missing_ok=True) Path("Source/Integration/lpx-paths.hpp").unlink(missing_ok=True) # # Write BuildConfiguration.xml # writefile("Saved/UnrealBuildTool/Integration.uproject", f""" VisualStudioCode """) # # Write lpx-paths.hpp. # writefile("Source/Integration/lpx-paths.hpp", f""" #define LUPREX_DLL_PATH "{INTEGRATION}/luprex/build/linux/luprexlib.so" #define LUPREX_ROOT_PATH "{INTEGRATION}/luprex" """) # # Write Integration.uproject. # writefile("Integration.uproject", """ { "FileVersion": 3, "EngineAssociation": "5.3", "Category": "", "Description": "", "Modules": [ { "Name": "Integration", "Type": "Runtime", "LoadingPhase": "Default", "AdditionalDependencies": [ "Engine", "CoreUObject" ] } ], "Plugins": [ { "Name": "ModelingToolsEditorMode", "Enabled": true, "TargetAllowList": [ "Editor" ] } ] } """) # # Use UnrealBuildTool to generate a rough draft of Integration.code-workspace. # BUILDPROJECTFILES = f'dotnet {UNREALBUILDTOOL} -projectfiles -project="{INTEGRATION}/Integration.uproject" -game' print(BUILDPROJECTFILES) os.system(BUILDPROJECTFILES) # # Load the rough Integration.code-workspace into RAM, then delete the rough draft. # with open("Integration.code-workspace") as original: WORKSPACE=json.load(original) os.remove("Integration.code-workspace") # # Configure the correct build task as the default task. # for task in WORKSPACE["tasks"]["tasks"]: if task["label"] == "IntegrationEditor Linux DebugGame Build": task["group"] = { "kind": "build", "isDefault": "true" } # # Delete all build tasks that aren't relevant. # def goodtask(task): return task["label"].startswith("IntegrationEditor Linux DebugGame") WORKSPACE["tasks"]["tasks"] = [x for x in WORKSPACE["tasks"]["tasks"] if goodtask(x)] # # Add a build task for Luprex # LUPREXBUILDTASK={} WORKSPACE["tasks"]["tasks"].append(LUPREXBUILDTASK) LUPREXBUILDTASK["label"] = "Build Luprex" LUPREXBUILDTASK["group"] = "build" LUPREXBUILDTASK["command"] = "make" LUPREXBUILDTASK["problemMatcher"] = "$msCompile" LUPREXBUILDTASK["type"] = "shell" LUPREXBUILDTASK["options"] = {} LUPREXBUILDTASK["options"]["cwd"] = f"{INTEGRATION}/luprex" # # Convert all launch configurations to lldb. # LLDBINIT=f"command script import {UNREALENGINE}/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py" for config in WORKSPACE["launch"]["configurations"]: config["type"] = "lldb" config["initCommands"] = [ LLDBINIT ] config.pop("visualizerFile", None) config.pop("showDisplayString", None) # # Delete all but the relevant launch configuration. # def goodconf(config): return config["name"] == "Launch IntegrationEditor (DebugGame)" WORKSPACE["launch"]["configurations"] = [x for x in WORKSPACE["launch"]["configurations"] if goodconf(x)] # # Write Integration.code-workspace. # with open("Integration.code-workspace", "w") as rewritten: json.dump(WORKSPACE, rewritten, indent=4) # # Write the Makefile # writefile("Makefile", f""" all: (cd luprex ; make) dotnet {UNREALBUILDTOOL} Integration Linux DebugGame -project="{INTEGRATION}/Integration.uproject" """)