2025-06-11 18:46:10 -04:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
#
|
2025-06-13 17:02:53 -04:00
|
|
|
# This is the one-and-only build script for luprex!
|
2025-06-11 21:00:55 -04:00
|
|
|
#
|
2025-06-13 17:02:53 -04:00
|
|
|
# Do not follow any of the build instructions on the Unreal Engine
|
|
|
|
|
# websites! Instead, use this script. The instructions for this
|
|
|
|
|
# script are in README.md
|
2025-06-11 20:38:54 -04:00
|
|
|
#
|
2025-06-13 17:02:53 -04:00
|
|
|
|
2025-06-11 21:00:55 -04:00
|
|
|
|
2025-06-19 18:01:05 -04:00
|
|
|
import sys, os, json, shutil, subprocess, re, time, tarfile
|
|
|
|
|
import itertools, hashlib, zipfile, fnmatch
|
2025-06-11 18:46:10 -04:00
|
|
|
from pathlib import Path
|
|
|
|
|
from types import SimpleNamespace
|
|
|
|
|
|
2025-06-27 19:14:29 -04:00
|
|
|
#
|
|
|
|
|
# Suppress an error in some of Unreal's scripts:
|
|
|
|
|
#
|
|
|
|
|
os.environ["GIT_DIR"] = ".git"
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Utility subroutines
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
def shell(dir, cmd):
|
|
|
|
|
"Run a shell command in a directory"
|
2025-06-27 17:23:43 -04:00
|
|
|
print("Running:", cmd)
|
2025-06-11 18:46:10 -04:00
|
|
|
subprocess.run(cmd, shell=True, check=True, cwd=dir)
|
|
|
|
|
|
|
|
|
|
def create_tarfile(directory, glob_pattern, outputfile):
|
|
|
|
|
"Create a tarfile from a source directory and a glob pattern"
|
|
|
|
|
directory = Path(directory)
|
|
|
|
|
with tarfile.open(outputfile, mode='w:gz') as tar:
|
|
|
|
|
for path in directory.rglob(glob_pattern):
|
|
|
|
|
if path.is_file():
|
|
|
|
|
tar.add(path, arcname=path.relative_to(directory))
|
|
|
|
|
|
|
|
|
|
def find_cpp(dir):
|
|
|
|
|
"Find all the C++ and C files in a given directory"
|
|
|
|
|
list1 = list(Path(dir).rglob("*.[ch]pp"))
|
|
|
|
|
list2 = list(Path(dir).rglob("*.[ch]"))
|
|
|
|
|
return sorted([str(x) for x in list1 + list2])
|
|
|
|
|
|
|
|
|
|
def hash_json(data):
|
|
|
|
|
"Calculate a sha256 hexdigest of any valid json data structure"
|
|
|
|
|
serialized = json.dumps(data, sort_keys=True).encode("utf-8")
|
|
|
|
|
return hashlib.sha256(serialized).hexdigest()
|
|
|
|
|
|
|
|
|
|
def read_if_exists(fn):
|
|
|
|
|
"Read a file if it exists, otherwise, return null string"
|
|
|
|
|
try: return Path(fn).read_text()
|
|
|
|
|
except: return ""
|
|
|
|
|
|
2025-09-08 16:42:16 -04:00
|
|
|
#
|
|
|
|
|
# Fix a chmod bug in the Zipfile module.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
class JZipFile(zipfile.ZipFile):
|
|
|
|
|
def _extract_member(self, member, targetpath, pwd):
|
|
|
|
|
if not isinstance(member, zipfile.ZipInfo):
|
|
|
|
|
member = self.getinfo(member)
|
|
|
|
|
targetpath = super()._extract_member(member, targetpath, pwd)
|
|
|
|
|
attr = member.external_attr >> 16
|
|
|
|
|
if attr != 0:
|
|
|
|
|
os.chmod(targetpath, attr)
|
|
|
|
|
return targetpath
|
|
|
|
|
|
2025-06-11 18:46:10 -04:00
|
|
|
#
|
|
|
|
|
# A JSON preprocessor.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
JSON_VAR_REGEX = re.compile(r'\[([A-Z0-9_]+)\]')
|
|
|
|
|
|
|
|
|
|
def expand_json(data, vars):
|
2025-06-11 20:38:54 -04:00
|
|
|
"""
|
|
|
|
|
A JSON preprocessor that can expand for-each loops and substitute variables.
|
|
|
|
|
The for-loop thing is mainly used by the code-workspace template, to avoid
|
|
|
|
|
extreme code duplication in the launch configurations and build configurations.
|
|
|
|
|
Variable substitution is used to plug hardwired paths into the file, and also
|
|
|
|
|
to implement the for-loop thing.
|
|
|
|
|
"""
|
2025-06-11 18:46:10 -04:00
|
|
|
if isinstance(data, dict):
|
|
|
|
|
if "for-each" in data and "body" in data:
|
|
|
|
|
body = data["body"]
|
|
|
|
|
foreach = data["for-each"]
|
|
|
|
|
return [ expand_json(body, vars | lvars) for lvars in foreach ]
|
2025-06-27 15:11:54 -04:00
|
|
|
if "append1" in data and "append2" in data:
|
|
|
|
|
result = []
|
|
|
|
|
for i in range(1, 1000):
|
|
|
|
|
key = f"append{i}"
|
|
|
|
|
if key not in data: break
|
|
|
|
|
result = result + expand_json(data[key], vars)
|
|
|
|
|
return result
|
2025-06-11 18:46:10 -04:00
|
|
|
else:
|
2025-07-02 17:18:31 -04:00
|
|
|
return { expand_json(key, vars): expand_json(value, vars) for key, value in data.items() }
|
2025-06-11 18:46:10 -04:00
|
|
|
elif isinstance(data, list):
|
|
|
|
|
return [ expand_json(item, vars) for item in data ]
|
|
|
|
|
elif isinstance(data, str):
|
2025-06-11 19:10:14 -04:00
|
|
|
full = JSON_VAR_REGEX.fullmatch(data)
|
|
|
|
|
if full: return vars.get(full.group(1), data)
|
|
|
|
|
else: return JSON_VAR_REGEX.sub(lambda m: str(vars.get(m.group(1), m.group(0))), data)
|
2025-06-11 18:46:10 -04:00
|
|
|
else:
|
|
|
|
|
return data
|
|
|
|
|
|
2025-06-11 20:38:54 -04:00
|
|
|
|
2025-06-11 18:46:10 -04:00
|
|
|
def expand_json_file(sourcefile, outputfile, config):
|
2025-06-11 20:38:54 -04:00
|
|
|
"""
|
|
|
|
|
Apply the json preprocessor to a file on disk
|
|
|
|
|
"""
|
2025-06-11 18:46:10 -04:00
|
|
|
Path(outputfile).unlink(missing_ok=True)
|
|
|
|
|
data = json.loads(Path(sourcefile).read_text())
|
|
|
|
|
expanded = expand_json(data, vars(config))
|
|
|
|
|
Path(outputfile).write_text(json.dumps(expanded, indent=4))
|
|
|
|
|
|
2025-06-23 17:46:56 -04:00
|
|
|
#
|
|
|
|
|
# Extracting a generated module file.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
def cpp_files_included_by(module):
|
|
|
|
|
"""
|
|
|
|
|
Given the name of a Module.XYZ.cpp file generated by unreal
|
|
|
|
|
build tool, scan the module for include directives that include
|
|
|
|
|
another C++ file. Produce a list of these C++ files.
|
|
|
|
|
"""
|
|
|
|
|
result = []
|
|
|
|
|
for line in Path(module).read_text().splitlines():
|
|
|
|
|
if line.startswith("#include "):
|
|
|
|
|
file = line[9:].strip().strip('"')
|
|
|
|
|
if file.endswith(".cpp"):
|
|
|
|
|
result.append(file)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
2025-06-11 18:46:10 -04:00
|
|
|
#
|
|
|
|
|
# Determining the build configuration.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
def get_build_mode_from_command_line():
|
|
|
|
|
"""
|
|
|
|
|
Build.py accepts a single argument, which should
|
|
|
|
|
be one of the following modes: all, c++, clean.
|
|
|
|
|
If nothing is specified, the mode is all.
|
|
|
|
|
We understand cpp and cxx as synonyms for c++.
|
|
|
|
|
"""
|
|
|
|
|
mode = sys.argv[1].lower() if len(sys.argv) > 1 else 'all'
|
|
|
|
|
if mode in ["cpp", "cxx"]: mode = "c++"
|
2025-07-02 17:01:49 -04:00
|
|
|
if not mode in ["all", "c++", "clean", "ccjson"]:
|
2025-06-11 18:46:10 -04:00
|
|
|
sys.exit(f"Invalid build mode: {mode}")
|
|
|
|
|
return mode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def autodetect_system_config():
|
|
|
|
|
"""
|
|
|
|
|
This autodetects where integration and unrealengine are installed,
|
|
|
|
|
and it also autodetects your operating system. Based on these,
|
|
|
|
|
it returns a config object containing a variety of useful
|
2025-06-11 20:38:54 -04:00
|
|
|
configuration settings. We haven't tested the Windows version.
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
|
|
|
|
config = SimpleNamespace()
|
2025-06-19 18:01:05 -04:00
|
|
|
# Make sure we're actually inside an integration repository.
|
|
|
|
|
config.INTEGRATION = os.path.dirname(os.path.abspath(sys.argv[0]))
|
|
|
|
|
if not Path(f"{config.INTEGRATION}/Source/Integration").is_dir():
|
|
|
|
|
sys.exit(f"Integration repository is not valid: {config.INTEGRATION}")
|
2025-06-19 20:54:21 -04:00
|
|
|
config.UNREALENGINE = config.INTEGRATION + ".UE"
|
2025-06-19 18:01:05 -04:00
|
|
|
# Configure other parameters.
|
2025-06-11 18:46:10 -04:00
|
|
|
if sys.platform == "windows":
|
|
|
|
|
config.OS = "Windows"
|
|
|
|
|
config.DLL = "dll"
|
|
|
|
|
config.BAT = "bat"
|
|
|
|
|
config.DOT_EXE = ".exe"
|
|
|
|
|
config.USER = "Unknown"
|
|
|
|
|
config.BUILD_BAT = "Build.bat"
|
2025-07-02 16:01:18 -04:00
|
|
|
config.DEBUG = "DebugGame"
|
2025-06-11 18:46:10 -04:00
|
|
|
else:
|
|
|
|
|
config.OS = "Linux"
|
|
|
|
|
config.DLL = "so"
|
|
|
|
|
config.BAT = "sh"
|
|
|
|
|
config.DOT_EXE = ""
|
|
|
|
|
config.USER = os.environ["USER"]
|
|
|
|
|
config.BUILD_BAT = "Linux/Build.sh"
|
2025-07-02 16:01:18 -04:00
|
|
|
config.DEBUG = "DebugGame"
|
2025-06-11 18:46:10 -04:00
|
|
|
return config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def store_system_config_in_globals(config):
|
|
|
|
|
"""
|
|
|
|
|
Copy all the config data from the config object into global variables.
|
|
|
|
|
"""
|
2025-07-02 16:01:18 -04:00
|
|
|
global OS,DLL,BAT,DOT_EXE,USER,BUILD_BAT,DEBUG,INTEGRATION,UNREALENGINE
|
2025-06-11 18:46:10 -04:00
|
|
|
OS = config.OS
|
|
|
|
|
DLL = config.DLL
|
|
|
|
|
BAT = config.BAT
|
|
|
|
|
DOT_EXE = config.DOT_EXE
|
|
|
|
|
USER = config.USER
|
|
|
|
|
BUILD_BAT = config.BUILD_BAT
|
2025-07-02 16:01:18 -04:00
|
|
|
DEBUG = config.DEBUG
|
2025-06-11 18:46:10 -04:00
|
|
|
INTEGRATION = config.INTEGRATION
|
|
|
|
|
UNREALENGINE = config.UNREALENGINE
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# The actual build steps.
|
|
|
|
|
#
|
|
|
|
|
|
2025-06-19 18:01:05 -04:00
|
|
|
def unzip_unreal_engine_and_apply_patch():
|
2025-06-11 21:00:55 -04:00
|
|
|
"""
|
2025-06-19 18:01:05 -04:00
|
|
|
Unzip the unreal engine source, then apply a patch.
|
2025-06-11 21:00:55 -04:00
|
|
|
"""
|
2025-09-08 16:42:16 -04:00
|
|
|
zips = list(Path(INTEGRATION).glob("UnrealEngine-*-release.zip"))
|
|
|
|
|
if len(zips) == 0:
|
|
|
|
|
sys.exit("Cannot find UnrealEngine-*-release.zip")
|
|
|
|
|
if len(zips) > 1:
|
|
|
|
|
sys.exit("Found multiple files matching UnrealEngine-*-release.zip")
|
|
|
|
|
print("Unreal version: ", zips[0].stem)
|
|
|
|
|
zipfn = os.path.join(INTEGRATION, zips[0].name)
|
|
|
|
|
extracted = os.path.join(INTEGRATION, zips[0].stem)
|
|
|
|
|
touchfile = os.path.join(UNREALENGINE, zips[0].stem)
|
|
|
|
|
if not Path(touchfile).is_file():
|
|
|
|
|
print("Removing old version of unreal engine source...")
|
2025-06-19 20:54:21 -04:00
|
|
|
shutil.rmtree(UNREALENGINE, ignore_errors=True)
|
2025-09-08 16:42:16 -04:00
|
|
|
shutil.rmtree(extracted, ignore_errors=True)
|
|
|
|
|
print(f"Unzipping {zipfn}...")
|
|
|
|
|
with JZipFile(zipfn) as zf:
|
|
|
|
|
zf.extractall(INTEGRATION)
|
2025-06-23 15:20:44 -04:00
|
|
|
patchfile = f"{INTEGRATION}/EnginePatches/EnginePatch"
|
2025-09-08 16:42:16 -04:00
|
|
|
shell(extracted, f"patch -p1 < {patchfile}")
|
|
|
|
|
Path(extracted).rename(UNREALENGINE)
|
|
|
|
|
Path(touchfile).touch()
|
2025-06-11 21:00:55 -04:00
|
|
|
|
2025-06-27 17:23:43 -04:00
|
|
|
|
2025-06-11 18:46:10 -04:00
|
|
|
def generate_buildconfiguration_xml():
|
|
|
|
|
"""
|
|
|
|
|
Generates BuildConfiguration.xml. We actually have two versions of this
|
|
|
|
|
file in git, one for windows, one for linux. This just copies the appropriate
|
|
|
|
|
version to the relevant target directories.
|
|
|
|
|
"""
|
|
|
|
|
dir1 = Path(f"{INTEGRATION}/Saved/UnrealBuildTool")
|
|
|
|
|
dir2 = Path(f"{UNREALENGINE}/Engine/Saved/UnrealBuildTool")
|
2025-06-27 17:23:43 -04:00
|
|
|
target1 = Path(f"{dir1}/BuildConfiguration.xml")
|
|
|
|
|
target2 = Path(f"{dir2}/BuildConfiguration.xml")
|
2025-06-11 18:46:10 -04:00
|
|
|
source = Path(f"{INTEGRATION}/EnginePatches/BuildConfiguration{OS}.xml")
|
|
|
|
|
template = source.read_text();
|
|
|
|
|
dir1.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
dir2.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
target1.unlink(missing_ok=True)
|
|
|
|
|
target2.unlink(missing_ok=True)
|
|
|
|
|
target1.write_text(template)
|
|
|
|
|
target2.write_text(template)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_lpx_paths():
|
|
|
|
|
"""
|
|
|
|
|
Unreal needs to be able to find the Luprex DLL, and it also needs to find the
|
|
|
|
|
Lua source code. For now, we just compile some hardwired paths into the
|
|
|
|
|
binary. Someday we'll do something more sophisticated.
|
|
|
|
|
"""
|
|
|
|
|
target = Path(f"{INTEGRATION}/Source/Integration/lpx-paths.hpp")
|
|
|
|
|
line1 = f'#define LUPREX_DLL_PATH "{INTEGRATION}/luprex/build/{OS}/luprexlib.{DLL}"'
|
|
|
|
|
line2 = f'#define LUPREX_ROOT_PATH "{INTEGRATION}/luprex"'
|
|
|
|
|
code = line1 + "\n" + line2 + "\n"
|
|
|
|
|
target.unlink(missing_ok=True)
|
|
|
|
|
target.write_text(code)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_integration_uproject():
|
|
|
|
|
"""
|
|
|
|
|
Generate integration.uproject
|
|
|
|
|
The uproject file is used by UnrealBuildTool to guide the build process.
|
|
|
|
|
"""
|
|
|
|
|
template = f"{INTEGRATION}/Integration.uproject.tpl.json"
|
|
|
|
|
target = f"{INTEGRATION}/Integration.uproject"
|
|
|
|
|
expand_json_file(template, target, CONFIG)
|
|
|
|
|
|
|
|
|
|
|
2025-06-27 17:23:43 -04:00
|
|
|
def run_unrealengine_setup_bat_replacement():
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
2025-06-27 17:23:43 -04:00
|
|
|
The Setup.bat in UnrealEngine does a lot of unnecessary stuff,
|
|
|
|
|
generates a lot of error messages, and pops up an interactive
|
|
|
|
|
prompt in the middle of a build. Yuk. So we've written
|
|
|
|
|
our own script that replaces Setup.bat/Setup.sh.
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
2025-06-27 19:14:29 -04:00
|
|
|
touch = Path(f"{UNREALENGINE}/Engine/Build/HaveGitDependencies")
|
|
|
|
|
if not touch.is_file():
|
2025-06-27 17:23:43 -04:00
|
|
|
if sys.platform == "windows":
|
|
|
|
|
shell(UNREALENGINE, "Engine/Binaries/DotNET/GitDependencies/win-x64/GitDependencies.exe")
|
|
|
|
|
shell(UNREALENGINE, "Engine/Extras/Redist/en-us/UEPrereqSetup_x64.exe /quiet /norestart")
|
|
|
|
|
else:
|
|
|
|
|
shell(UNREALENGINE, "Engine/Build/BatchFiles/Linux/GitDependencies.sh")
|
|
|
|
|
shell(f"{UNREALENGINE}/Engine/Build/BatchFiles/Linux", "./Setup.sh")
|
2025-06-27 19:14:29 -04:00
|
|
|
touch.write_text("Downloaded")
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
|
2025-06-11 19:30:44 -04:00
|
|
|
def build_unrealbuildtool():
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
2025-06-11 19:30:44 -04:00
|
|
|
Build the unreal build tool itself, also including shader compile worker.
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
2025-06-11 19:30:44 -04:00
|
|
|
shell(UNREALENGINE, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -buildubt ShaderCompileWorker {OS} Development")
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_luprex_and_integration():
|
|
|
|
|
"""
|
2025-06-11 20:38:54 -04:00
|
|
|
This builds our C++ code, and also UnrealEngine's C++ code.
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
2026-01-14 18:44:16 -05:00
|
|
|
shell(f"{INTEGRATION}/luprex", "make all 2>&1 | sed 's|^cpp/|luprex/cpp/|; s|^ext/|luprex/ext/|'")
|
2025-07-02 16:01:18 -04:00
|
|
|
shell(INTEGRATION, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -waitmutex IntegrationEditor {OS} {DEBUG} {INTEGRATION}/Integration.uproject")
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
|
2025-06-18 18:39:25 -04:00
|
|
|
#
|
|
|
|
|
# Make a compile_commands.json file.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
def build_intellisense_database_for_clangd():
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
|
|
|
|
This builds compile_commands.json, which tells the intellisense system
|
2025-06-18 18:39:25 -04:00
|
|
|
based on clangd how to compile each source file.
|
|
|
|
|
This also installs a .clangd file in the UnrealEngine directory.
|
2025-06-11 18:46:10 -04:00
|
|
|
"""
|
2025-07-02 17:01:49 -04:00
|
|
|
# Find clang compiler.
|
2025-06-18 18:39:25 -04:00
|
|
|
clangs = list(Path(f"{UNREALENGINE}/Engine/Extras/ThirdPartyNotUE/SDKs").rglob("*-linux-gnu/bin/clang++"))
|
|
|
|
|
if len(clangs) != 1: sys.exit("Couldn't identify correct clang++ compiler in UnrealEngine thirdparty directory")
|
|
|
|
|
clang = str(clangs[0])
|
2025-07-02 17:01:49 -04:00
|
|
|
# Build the table of source files and RSP files.
|
|
|
|
|
mods1 = Path(f"{INTEGRATION}/Intermediate/Build/{OS}").rglob(f"UnrealEditor/{DEBUG}/**/Module.*.o.rsp")
|
|
|
|
|
mods2 = Path(f"{UNREALENGINE}/Engine/Intermediate/Build/{OS}").rglob("UnrealEditor/Development/**/Module.*.o.rsp")
|
|
|
|
|
cpp_to_rsp = {}
|
|
|
|
|
for mod in itertools.chain(mods1, mods2):
|
|
|
|
|
rsp = str(mod)
|
|
|
|
|
cpp = os.path.abspath(rsp.removesuffix(".o.rsp"))
|
|
|
|
|
cpp_to_rsp[cpp] = rsp
|
|
|
|
|
for subfile in cpp_files_included_by(cpp):
|
|
|
|
|
abs = os.path.abspath(os.path.join(f"{UNREALENGINE}/Engine/Source", subfile))
|
|
|
|
|
cpp_to_rsp[abs] = rsp
|
|
|
|
|
# Read the luprex compile commands database.
|
2025-06-18 18:39:25 -04:00
|
|
|
ccjson = json.loads(Path(f"{INTEGRATION}/luprex/build/{OS}/compile_commands.json").read_text())
|
2025-07-02 17:01:49 -04:00
|
|
|
# Generate the new commands
|
2025-06-18 18:39:25 -04:00
|
|
|
ccdir = f"{UNREALENGINE}/Engine/Source";
|
2025-07-02 17:01:49 -04:00
|
|
|
for cpp in sorted(cpp_to_rsp.keys()):
|
|
|
|
|
rsp = cpp_to_rsp[cpp]
|
2025-06-18 18:39:25 -04:00
|
|
|
args = [clang, "@"+rsp]
|
|
|
|
|
ccjson.append({ "file" : cpp, "arguments":args, "directory":ccdir })
|
|
|
|
|
Path(f"{INTEGRATION}/.vscode/compile_commands.json").write_text(json.dumps(ccjson, indent=2))
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
|
2026-02-11 13:21:42 -05:00
|
|
|
def run_generateprojectfiles_in_sandbox():
|
|
|
|
|
"""
|
|
|
|
|
Unreal's GenerateProjectFiles does an absolutely terrible
|
|
|
|
|
job of generating project files for vscode. It's so bad
|
|
|
|
|
that we've decided to just not use it at all. But we
|
|
|
|
|
still sometimes want to look at the output, to see what
|
|
|
|
|
it would have generated. We run GenerateProjectFiles
|
|
|
|
|
in a sandbox so it can't modify the real project. Then,
|
|
|
|
|
we leave the output in the sandbox for inspection. The
|
|
|
|
|
results don't affect our build system at all.
|
|
|
|
|
"""
|
|
|
|
|
sandbox = Path(f"{INTEGRATION}/GPF-output")
|
|
|
|
|
if sandbox.exists():
|
|
|
|
|
shutil.rmtree(sandbox)
|
|
|
|
|
sandbox.mkdir()
|
|
|
|
|
(sandbox / "Integration.uproject").write_bytes(Path(f"{INTEGRATION}/Integration.uproject").read_bytes())
|
|
|
|
|
for name in ["Source", "Config", "Content"]:
|
|
|
|
|
(sandbox / name).symlink_to(f"../{name}")
|
|
|
|
|
shell(str(sandbox), f'{UNREALENGINE}/GenerateProjectFiles.{BAT} -projectfiles -project="{sandbox}/Integration.uproject" -game')
|
|
|
|
|
# Remove the symlinks and uproject copy, leaving only generated files
|
|
|
|
|
for name in ["Source", "Config", "Content", "Integration.uproject"]:
|
|
|
|
|
(sandbox / name).unlink()
|
|
|
|
|
|
|
|
|
|
|
2025-06-11 18:46:10 -04:00
|
|
|
def generate_integration_code_workspace():
|
2025-06-11 20:38:54 -04:00
|
|
|
"""
|
|
|
|
|
We build Integration.code-workspace from a template that we
|
|
|
|
|
wrote ourselves, Integration.code-workspace.tpl.json.
|
|
|
|
|
"""
|
|
|
|
|
workspace = f"{INTEGRATION}/Integration.code-workspace"
|
2025-06-11 18:46:10 -04:00
|
|
|
template = f"{INTEGRATION}/Integration.code-workspace.tpl.json"
|
2025-06-11 20:38:54 -04:00
|
|
|
Path(workspace).unlink(missing_ok=True)
|
|
|
|
|
expand_json_file(template, workspace, CONFIG)
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_clean():
|
2025-06-11 20:38:54 -04:00
|
|
|
"""
|
|
|
|
|
This code is underdeveloped.
|
|
|
|
|
"""
|
2025-06-11 18:46:10 -04:00
|
|
|
shell(f"{INTEGRATION}/luprex", "make clean")
|
2025-07-02 16:01:18 -04:00
|
|
|
shell(INTEGRATION, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -waitmutex IntegrationEditor {OS} {DEBUG} {INTEGRATION}/Integration.uproject -clean")
|
2025-06-11 18:46:10 -04:00
|
|
|
Path(f"{INTEGRATION}/.vscode/compile_commands.json").unlink(missing_ok = True)
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# MAIN PROGRAM
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
MODE = get_build_mode_from_command_line()
|
|
|
|
|
CONFIG = autodetect_system_config()
|
|
|
|
|
store_system_config_in_globals(CONFIG)
|
|
|
|
|
os.chdir(f"{INTEGRATION}/EnginePatches")
|
|
|
|
|
|
2025-07-02 17:01:49 -04:00
|
|
|
if MODE == "ccjson":
|
|
|
|
|
build_intellisense_database_for_clangd()
|
|
|
|
|
|
2025-06-11 18:46:10 -04:00
|
|
|
if MODE == "all":
|
2025-06-19 18:01:05 -04:00
|
|
|
unzip_unreal_engine_and_apply_patch()
|
2025-06-11 18:46:10 -04:00
|
|
|
generate_buildconfiguration_xml()
|
|
|
|
|
generate_lpx_paths()
|
|
|
|
|
generate_integration_uproject()
|
2025-06-27 17:23:43 -04:00
|
|
|
run_unrealengine_setup_bat_replacement()
|
2026-02-11 13:21:42 -05:00
|
|
|
run_generateprojectfiles_in_sandbox()
|
2025-06-11 19:30:44 -04:00
|
|
|
build_unrealbuildtool()
|
2025-06-11 20:38:54 -04:00
|
|
|
generate_integration_code_workspace()
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
if MODE in ["all", "c++"]:
|
|
|
|
|
build_luprex_and_integration()
|
2025-06-18 18:39:25 -04:00
|
|
|
build_intellisense_database_for_clangd()
|
2025-06-11 18:46:10 -04:00
|
|
|
|
|
|
|
|
if MODE == "clean":
|
|
|
|
|
build_clean()
|
|
|
|
|
|