2023-07-07 16:25:49 +00:00
|
|
|
import argparse
|
2023-07-08 14:22:29 +00:00
|
|
|
import glob
|
2023-07-07 16:25:49 +00:00
|
|
|
import os
|
|
|
|
import platform
|
2023-07-08 14:22:29 +00:00
|
|
|
import urllib.request
|
|
|
|
import shutil
|
2023-07-07 16:25:49 +00:00
|
|
|
import subprocess
|
2023-07-08 14:22:29 +00:00
|
|
|
from zipfile import ZipFile
|
2023-07-07 16:25:49 +00:00
|
|
|
|
2023-07-07 23:22:13 +00:00
|
|
|
import checksum
|
|
|
|
from log import *
|
|
|
|
from utils import pushd, removeall
|
|
|
|
|
|
|
|
|
|
|
|
ANGLE_VERSION = "2023-07-05"
|
2023-07-07 16:25:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
def attach_build_runtime(subparsers):
|
|
|
|
build = subparsers.add_parser("build-runtime", help="TODO")
|
|
|
|
build.add_argument("--release", action="store_true", help="compile Orca in release mode (default is debug)")
|
|
|
|
build.set_defaults(func=shellish(build_runtime))
|
|
|
|
|
|
|
|
|
|
|
|
def build_runtime(args):
|
2023-07-07 23:22:13 +00:00
|
|
|
ensure_programs()
|
|
|
|
ensure_angle()
|
2023-07-07 16:25:49 +00:00
|
|
|
|
|
|
|
build_milepost("lib", args.release)
|
|
|
|
|
2023-07-07 23:22:13 +00:00
|
|
|
|
2023-07-07 16:25:49 +00:00
|
|
|
def build_milepost(target, release):
|
|
|
|
print("Building milepost...")
|
|
|
|
with pushd("milepost"):
|
|
|
|
os.makedirs("bin", exist_ok=True)
|
|
|
|
os.makedirs("lib", exist_ok=True)
|
|
|
|
os.makedirs("resources", exist_ok=True)
|
|
|
|
|
|
|
|
if target == "lib":
|
|
|
|
if platform.system() == "Darwin":
|
|
|
|
build_milepost_lib_mac(release)
|
|
|
|
else:
|
2023-07-07 23:22:13 +00:00
|
|
|
log_error(f"can't build milepost for unknown platform '{platform.system()}'")
|
|
|
|
exit(1)
|
2023-07-07 16:25:49 +00:00
|
|
|
elif target == "test":
|
|
|
|
with pushd("examples/test_app"):
|
|
|
|
# TODO?
|
|
|
|
subprocess.run(["./build.sh"])
|
|
|
|
elif target == "clean":
|
|
|
|
removeall("bin")
|
|
|
|
else:
|
2023-07-07 23:22:13 +00:00
|
|
|
log_error(f"unrecognized milepost target '{target}'")
|
2023-07-07 16:25:49 +00:00
|
|
|
exit(1)
|
|
|
|
|
2023-07-07 23:22:13 +00:00
|
|
|
|
2023-07-07 16:25:49 +00:00
|
|
|
def build_milepost_lib_mac(release):
|
|
|
|
sdk_dir = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
|
|
|
|
|
|
|
|
flags = ["-mmacos-version-min=10.15.4", "-maes"]
|
|
|
|
cflags = ["-std=c11"]
|
|
|
|
debug_flags = ["-O3"] if release else ["-g", "-DDEBUG", "-DLOG_COMPILE_DEBUG"]
|
|
|
|
ldflags = [f"-L{sdk_dir}/usr/lib", f"-F{sdk_dir}/System/Library/Frameworks/"]
|
|
|
|
includes = ["-Isrc", "-Isrc/util", "-Isrc/platform", "-Iext", "-Iext/angle_headers"]
|
|
|
|
|
|
|
|
# compile metal shader
|
|
|
|
subprocess.run([
|
|
|
|
"xcrun", "-sdk", "macosx", "metal",
|
|
|
|
# TODO: shaderFlagParam
|
|
|
|
"-fno-fast-math", "-c",
|
|
|
|
"-o", "lib/mtl_renderer.air",
|
|
|
|
"src/mtl_renderer.metal",
|
|
|
|
], check=True)
|
|
|
|
subprocess.run([
|
|
|
|
"xcrun", "-sdk", "macosx", "metallib",
|
|
|
|
"-o", "lib/mtl_renderer.metallib",
|
|
|
|
"lib/mtl_renderer.air",
|
|
|
|
], check=True)
|
|
|
|
|
|
|
|
# compile milepost. We use one compilation unit for all C code, and one
|
|
|
|
# compilation unit for all Objective-C code
|
|
|
|
subprocess.run([
|
|
|
|
"clang",
|
|
|
|
*debug_flags, "-c",
|
|
|
|
"-o", "bin/milepost_c.o",
|
|
|
|
*cflags, *flags, *includes,
|
|
|
|
"src/milepost.c"
|
|
|
|
], check=True)
|
|
|
|
subprocess.run([
|
|
|
|
"clang",
|
|
|
|
*debug_flags, "-c",
|
|
|
|
"-o", "bin/milepost_objc.o",
|
|
|
|
*flags, *includes,
|
|
|
|
"src/milepost.m"
|
|
|
|
], check=True)
|
|
|
|
|
|
|
|
# build dynamic library
|
|
|
|
subprocess.run([
|
|
|
|
"ld",
|
|
|
|
*ldflags, "-dylib",
|
|
|
|
"-o", "bin/libmilepost.dylib",
|
|
|
|
"bin/milepost_c.o", "bin/milepost_objc.o",
|
|
|
|
"-Llib", "-lc",
|
|
|
|
"-framework", "Carbon", "-framework", "Cocoa", "-framework", "Metal", "-framework", "QuartzCore",
|
|
|
|
"-weak-lEGL", "-weak-lGLESv2",
|
|
|
|
], check=True)
|
|
|
|
|
|
|
|
# change dependent libs path to @rpath
|
|
|
|
subprocess.run([
|
|
|
|
"install_name_tool",
|
|
|
|
"-change", "./libEGL.dylib", "@rpath/libEGL.dylib",
|
|
|
|
"bin/libmilepost.dylib",
|
|
|
|
], check=True)
|
|
|
|
subprocess.run([
|
|
|
|
"install_name_tool",
|
|
|
|
"-change", "./libGLESv2.dylib", "@rpath/libGLESv2.dylib",
|
|
|
|
"bin/libmilepost.dylib",
|
|
|
|
], check=True)
|
|
|
|
|
|
|
|
# add executable path to rpath. Client executable can still add its own
|
|
|
|
# rpaths if needed, e.g. @executable_path/libs/ etc.
|
|
|
|
subprocess.run([
|
|
|
|
"install_name_tool",
|
|
|
|
"-id", "@rpath/libmilepost.dylib",
|
|
|
|
"bin/libmilepost.dylib",
|
|
|
|
], check=True)
|
2023-07-07 23:22:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
def ensure_programs():
|
|
|
|
try:
|
|
|
|
subprocess.run(["clang", "-v"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
except FileNotFoundError:
|
|
|
|
msg = log_error("clang was not found on your system.")
|
|
|
|
if platform.system() == "Windows":
|
|
|
|
msg.more("We recommend installing clang via the Visual Studio installer.")
|
|
|
|
# TODO(ben): Link to the Visual Studio download page (I have no internet right now)
|
|
|
|
elif platform.system() == "Darwin":
|
|
|
|
msg.more("Run the following to install it:")
|
|
|
|
msg.more()
|
|
|
|
msg.more(" brew install llvm")
|
|
|
|
msg.more()
|
|
|
|
exit(1)
|
|
|
|
# TODO(ben): Check for xcode command line tools
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_angle():
|
|
|
|
checkfiles = None
|
|
|
|
if platform.system() == "Windows":
|
|
|
|
checkfiles = [
|
|
|
|
["libEGL-win", "milepost/lib/libEGL.dll"],
|
|
|
|
["libGLESv2-win", "milepost/lib/libGLESv2.dll"],
|
|
|
|
]
|
|
|
|
elif platform.system() == "Darwin":
|
|
|
|
# TODO(ben): Do we need specific builds of ANGLE for each macOS version?
|
|
|
|
if platform.machine() == "arm64":
|
|
|
|
checkfiles = [
|
|
|
|
["libEGL-mac-arm64", "milepost/lib/libEGL.dylib"],
|
|
|
|
["libGLESv2-mac-arm64", "milepost/lib/libGLESv2.dylib"],
|
|
|
|
]
|
|
|
|
else:
|
|
|
|
checkfiles = [
|
|
|
|
["libEGL-mac-x64", "milepost/lib/libEGL.dylib"],
|
|
|
|
["libGLESv2-mac-x64", "milepost/lib/libGLESv2.dylib"],
|
|
|
|
]
|
|
|
|
|
|
|
|
if checkfiles is None:
|
|
|
|
log_warning("could not verify if the correct version of ANGLE is present; the build will probably fail.")
|
|
|
|
return
|
|
|
|
|
|
|
|
angle_exists = True
|
|
|
|
for file in checkfiles:
|
|
|
|
key = file[0]
|
|
|
|
filepath = file[1]
|
|
|
|
if not os.path.isfile(filepath):
|
|
|
|
angle_exists = False
|
|
|
|
break
|
|
|
|
if not checksum.checkfile(key, filepath):
|
|
|
|
log_error("wrong version of ANGLE libraries installed")
|
|
|
|
exit(1)
|
|
|
|
|
|
|
|
if not angle_exists:
|
2023-07-08 14:22:29 +00:00
|
|
|
download_angle()
|
|
|
|
|
|
|
|
|
|
|
|
def download_angle():
|
|
|
|
print("Downloading ANGLE...")
|
|
|
|
if platform.system() == "Windows":
|
|
|
|
build = "win"
|
|
|
|
checksumkey = "angle.zip-win"
|
|
|
|
extension = "dll"
|
|
|
|
elif platform.system() == "Darwin":
|
|
|
|
extension = "dylib"
|
|
|
|
build = "macos-12"
|
|
|
|
checksumkey = "angle.zip-mac"
|
|
|
|
|
|
|
|
# TODO(ben): make universal dylibs
|
|
|
|
if platform.machine() == "arm64":
|
|
|
|
log_error(f"automated ANGLE builds are not yet available for Apple silicon")
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
log_error(f"could not automatically download ANGLE for unknown platform {platform.system()}")
|
|
|
|
return
|
|
|
|
|
|
|
|
url = f"https://github.com/HandmadeNetwork/build-angle/releases/download/{ANGLE_VERSION}/angle-{build}-{ANGLE_VERSION}.zip"
|
|
|
|
with urllib.request.urlopen(url) as response:
|
|
|
|
os.makedirs("scripts/files", exist_ok=True)
|
|
|
|
with open("scripts/files/angle.zip", "wb") as out:
|
|
|
|
shutil.copyfileobj(response, out)
|
|
|
|
|
|
|
|
if not checksum.checkfile(checksumkey, "scripts/files/angle.zip"):
|
|
|
|
log_error(f"ANGLE download did not match checksum")
|
|
|
|
exit(1)
|
|
|
|
|
|
|
|
with ZipFile("scripts/files/angle.zip", "r") as anglezip:
|
|
|
|
anglezip.extractall(path="scripts/files")
|
|
|
|
|
|
|
|
for filepath in glob.glob(f"scripts/files/angle/bin/*.{extension}"):
|
|
|
|
shutil.copy(filepath, "milepost/lib")
|