[Build] Convert build system to raw cmake and remove premake layer
This commit is contained in:
292
xenia-build.py
292
xenia-build.py
@@ -413,7 +413,7 @@ def generate_version_h():
|
||||
commit_short = ":("
|
||||
|
||||
# header
|
||||
contents_new = f"""// Autogenerated by `xb premake`.
|
||||
contents_new = f"""// Autogenerated by xenia-build.py.
|
||||
#ifndef GENERATED_VERSION_H_
|
||||
#define GENERATED_VERSION_H_
|
||||
#define XE_BUILD_BRANCH "{branch_name}"
|
||||
@@ -626,58 +626,31 @@ def get_clang_format_binary():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_premake_target_os(target_os_override=None):
|
||||
"""Gets the target --os to pass to premake, either for the current platform
|
||||
or for the user-specified cross-compilation target.
|
||||
def run_cmake_configure(build_type="Release", cc=None):
|
||||
"""Runs cmake configure on the project.
|
||||
|
||||
Args:
|
||||
target_os_override: override specified by the user for cross-compilation,
|
||||
or None to target the host platform.
|
||||
build_type: Build configuration (Debug, Release, Checked).
|
||||
cc: C compiler to use (e.g. 'clang', 'gcc').
|
||||
|
||||
Returns:
|
||||
Target --os to pass to premake. If a return value of this function valid
|
||||
for the current configuration is passed to it again, the same value will
|
||||
be returned.
|
||||
"""
|
||||
if sys.platform == "darwin":
|
||||
target_os = "macosx"
|
||||
elif sys.platform == "win32":
|
||||
target_os = "windows"
|
||||
elif host_linux_platform_is_android:
|
||||
target_os = "android"
|
||||
else:
|
||||
target_os = "linux"
|
||||
if target_os_override and target_os_override != target_os:
|
||||
if target_os_override == "android":
|
||||
target_os = target_os_override
|
||||
else:
|
||||
print_error(
|
||||
"cross-compilation is only supported for Android target")
|
||||
sys.exit(1)
|
||||
return target_os
|
||||
|
||||
|
||||
def run_premake(target_os, action, cc=None):
|
||||
"""Runs premake on the main project with the given format.
|
||||
|
||||
Args:
|
||||
target_os: target --os to pass to premake.
|
||||
action: action to perform.
|
||||
Return code from cmake.
|
||||
"""
|
||||
args = [
|
||||
sys.executable,
|
||||
os.path.join("tools", "build", "premake.py"),
|
||||
"--file=premake5.lua",
|
||||
f"--os={target_os}",
|
||||
"--test-suite-mode=combined",
|
||||
"--verbose",
|
||||
action,
|
||||
"cmake",
|
||||
"-S", ".",
|
||||
"-B", "build",
|
||||
"-G", "Ninja Multi-Config",
|
||||
]
|
||||
if not cc:
|
||||
cc = get_cc(cc=cc)
|
||||
|
||||
if cc:
|
||||
args.insert(4, f"--cc={cc}")
|
||||
if sys.platform != "win32":
|
||||
if not cc:
|
||||
cc = get_cc(cc=cc)
|
||||
c_compiler = cc or os.environ.get("CC", "clang")
|
||||
cxx_compiler = (cc + "++") if cc else os.environ.get("CXX", "clang++")
|
||||
args += [
|
||||
f"-DCMAKE_C_COMPILER={c_compiler}",
|
||||
f"-DCMAKE_CXX_COMPILER={cxx_compiler}",
|
||||
]
|
||||
|
||||
ret = subprocess.call(args)
|
||||
|
||||
@@ -687,29 +660,6 @@ def run_premake(target_os, action, cc=None):
|
||||
return ret
|
||||
|
||||
|
||||
def run_platform_premake(target_os_override=None, cc=None, devenv=None):
|
||||
"""Runs all gyp configurations.
|
||||
"""
|
||||
target_os = get_premake_target_os(target_os_override)
|
||||
if not devenv:
|
||||
if target_os == "macosx":
|
||||
devenv = "xcode4"
|
||||
elif target_os == "windows":
|
||||
vs_version = os.getenv("VSVERSION", VSVERSION_MINIMUM)
|
||||
# VS 2026 preview reports as vs18, map to vs2022 for premake
|
||||
# as it doesn't yet have a vs2026 target
|
||||
if vs_version == "18":
|
||||
vs_version = "2022"
|
||||
devenv = f"vs{vs_version}"
|
||||
elif target_os == "android":
|
||||
devenv = "androidndk"
|
||||
else:
|
||||
devenv = "cmake"
|
||||
if not cc:
|
||||
cc = get_cc(cc=cc)
|
||||
return run_premake(target_os=target_os, action=devenv, cc=cc)
|
||||
|
||||
|
||||
def get_build_bin_path(args):
|
||||
"""Returns the path of the bin/ path with build results based on the
|
||||
configuration specified in the parsed arguments.
|
||||
@@ -720,13 +670,10 @@ def get_build_bin_path(args):
|
||||
Returns:
|
||||
A full path for the bin folder.
|
||||
"""
|
||||
if sys.platform == "darwin":
|
||||
platform = "macosx"
|
||||
elif sys.platform == "win32":
|
||||
platform = "windows"
|
||||
else:
|
||||
platform = "linux"
|
||||
return os.path.join(self_path, "build", "bin", platform.capitalize(), args["config"].capitalize())
|
||||
config = args["config"].title()
|
||||
platform = "Windows" if sys.platform == "win32" else "Linux"
|
||||
# Multi-config: build/bin/<Platform>/<Config>
|
||||
return os.path.join(self_path, "build", "bin", platform, config)
|
||||
|
||||
|
||||
def create_clion_workspace():
|
||||
@@ -743,7 +690,7 @@ def create_clion_workspace():
|
||||
with open(os.path.join(".idea", "misc.xml"), "w") as f:
|
||||
f.write("""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$/build">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$">
|
||||
<contentRoot DIR="$PROJECT_DIR$" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -845,9 +792,6 @@ class SetupCommand(Command):
|
||||
name="setup",
|
||||
help_short="Setup the build environment.",
|
||||
*args, **kwargs)
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
print("Setting up the build environment...\n")
|
||||
@@ -859,8 +803,8 @@ class SetupCommand(Command):
|
||||
else:
|
||||
print_warning("Git not available or not a repository. Dependencies may be missing.")
|
||||
|
||||
print("\n- running premake...")
|
||||
ret = run_platform_premake(target_os_override=args["target_os"])
|
||||
print("\n- running cmake configure...")
|
||||
ret = run_cmake_configure()
|
||||
print_status(ResultStatus.SUCCESS if not ret else ResultStatus.FAILURE)
|
||||
return ret
|
||||
|
||||
@@ -878,9 +822,6 @@ class PullCommand(Command):
|
||||
self.parser.add_argument(
|
||||
"--merge", action="store_true",
|
||||
help=f"Merges on {default_branch} instead of rebasing.")
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
print("Pulling...\n")
|
||||
@@ -910,36 +851,29 @@ class PullCommand(Command):
|
||||
git_submodule_update()
|
||||
print("")
|
||||
|
||||
print("- running premake...")
|
||||
if run_platform_premake(target_os_override=args["target_os"]) == 0:
|
||||
print("- running cmake configure...")
|
||||
if run_cmake_configure() == 0:
|
||||
print_status(ResultStatus.SUCCESS)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class PremakeCommand(Command):
|
||||
"""'premake' command.
|
||||
"""'premake' command (now runs cmake configure).
|
||||
"""
|
||||
|
||||
def __init__(self, subparsers, *args, **kwargs):
|
||||
super(PremakeCommand, self).__init__(
|
||||
subparsers,
|
||||
name="premake",
|
||||
help_short="Runs premake to update all projects.",
|
||||
help_short="Runs cmake configure to update all projects.",
|
||||
*args, **kwargs)
|
||||
self.parser.add_argument(
|
||||
"--cc", choices=["clang", "gcc", "msc"], default=None, help="Compiler toolchain passed to premake")
|
||||
self.parser.add_argument(
|
||||
"--devenv", default=None, help="Development environment")
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
"--cc", choices=["clang", "gcc", "msc"], default=None, help="Compiler toolchain")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
# Update premake. If no binary found, it will be built from source.
|
||||
print("Running premake...\n")
|
||||
ret = run_platform_premake(target_os_override=args["target_os"],
|
||||
cc=args["cc"], devenv=args["devenv"])
|
||||
print("Running cmake configure...\n")
|
||||
ret = run_cmake_configure(cc=args["cc"])
|
||||
print_status(ResultStatus.SUCCESS if not ret else ResultStatus.FAILURE)
|
||||
|
||||
return ret
|
||||
@@ -954,7 +888,7 @@ class BaseBuildCommand(Command):
|
||||
subparsers,
|
||||
*args, **kwargs)
|
||||
self.parser.add_argument(
|
||||
"--cc", choices=["clang", "gcc", "msc"], default=None, help="Compiler toolchain passed to premake")
|
||||
"--cc", choices=["clang", "gcc", "msc"], default=None, help="Compiler toolchain")
|
||||
self.parser.add_argument(
|
||||
"--config", choices=["checked", "debug", "release"], default="debug",
|
||||
type=str.lower, help="Chooses the build configuration.")
|
||||
@@ -966,69 +900,32 @@ class BaseBuildCommand(Command):
|
||||
help="Forces a full rebuild.")
|
||||
self.parser.add_argument(
|
||||
"--no_premake", action="store_true",
|
||||
help="Skips running premake before building.")
|
||||
help="Skips running cmake configure before building.")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
config = args["config"].title()
|
||||
|
||||
if not args["no_premake"]:
|
||||
print("- running premake...")
|
||||
run_platform_premake(cc=args["cc"])
|
||||
print("- running cmake configure...")
|
||||
run_cmake_configure(build_type=config, cc=args["cc"])
|
||||
print("")
|
||||
|
||||
print("- building (%s):%s..." % (
|
||||
"all" if not len(args["target"]) else ", ".join(args["target"]),
|
||||
args["config"]))
|
||||
if sys.platform == "win32":
|
||||
if not vs_version:
|
||||
print_error("Visual Studio is not installed.")
|
||||
result = 1
|
||||
else:
|
||||
targets = None
|
||||
if args["target"]:
|
||||
targets = "/t:" + ";".join(
|
||||
target + (":Rebuild" if args["force"] else "")
|
||||
for target in args["target"])
|
||||
else:
|
||||
targets = "/t:Rebuild" if args["force"] else None
|
||||
|
||||
result = subprocess.call([
|
||||
"msbuild",
|
||||
"build/xenia.sln",
|
||||
"/nologo",
|
||||
"/m",
|
||||
"/v:m",
|
||||
f"/p:Configuration={args['config']}",
|
||||
] + ([targets] if targets else []) + pass_args)
|
||||
elif sys.platform == "darwin":
|
||||
schemes = args["target"] or ["xenia-app"]
|
||||
nested_args = [["-scheme", scheme] for scheme in schemes]
|
||||
scheme_args = [arg for pair in nested_args for arg in pair]
|
||||
result = subprocess.call([
|
||||
"xcodebuild",
|
||||
"-workspace",
|
||||
"build/xenia.xcworkspace",
|
||||
"-configuration",
|
||||
args["config"]
|
||||
] + scheme_args + pass_args, env=dict(os.environ))
|
||||
else:
|
||||
result = subprocess.call([
|
||||
"cmake",
|
||||
"-Sbuild",
|
||||
f"-Bbuild/build_{args['config']}",
|
||||
f"-DCMAKE_BUILD_TYPE={args['config'].title()}",
|
||||
f"-DCMAKE_C_COMPILER={os.environ.get('CC', 'clang')}",
|
||||
f"-DCMAKE_CXX_COMPILER={os.environ.get('CXX', 'clang++')}",
|
||||
"-GNinja"
|
||||
] + pass_args, env=dict(os.environ))
|
||||
print("")
|
||||
if result != 0:
|
||||
print_error("cmake failed with one or more errors.")
|
||||
return result
|
||||
result = subprocess.call([
|
||||
"ninja",
|
||||
f"-Cbuild/build_{args['config']}",
|
||||
] + pass_args, env=dict(os.environ))
|
||||
if result != 0:
|
||||
print_error("ninja failed with one or more errors.")
|
||||
build_args = [
|
||||
"cmake",
|
||||
"--build", "build",
|
||||
"--config", config,
|
||||
]
|
||||
if args["target"]:
|
||||
for target in args["target"]:
|
||||
build_args += ["--target", target]
|
||||
if args["force"]:
|
||||
build_args += ["--clean-first"]
|
||||
result = subprocess.call(build_args + pass_args)
|
||||
if result != 0:
|
||||
print_error("Build failed with one or more errors.")
|
||||
return result
|
||||
|
||||
|
||||
@@ -1653,14 +1550,13 @@ class CleanCommand(Command):
|
||||
name="clean",
|
||||
help_short="Removes intermediate files and build outputs.",
|
||||
*args, **kwargs)
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
print("Cleaning build artifacts...\n"
|
||||
"- premake clean...")
|
||||
run_premake(get_premake_target_os(args["target_os"]), "clean")
|
||||
print("Cleaning build artifacts...")
|
||||
# Clean all build directories
|
||||
if os.path.isdir("build"):
|
||||
print("- cleaning build...")
|
||||
subprocess.call(["cmake", "--build", "build", "--target", "clean"])
|
||||
|
||||
# Also clean generated files
|
||||
clean_generated_files()
|
||||
@@ -1698,9 +1594,6 @@ class NukeCommand(Command):
|
||||
name="nuke",
|
||||
help_short="Removes all build/ output.",
|
||||
*args, **kwargs)
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
print("Cleaning build artifacts...\n"
|
||||
@@ -1719,8 +1612,8 @@ class NukeCommand(Command):
|
||||
default_branch,
|
||||
])
|
||||
|
||||
print("\n- running premake...")
|
||||
run_platform_premake(target_os_override=args["target_os"])
|
||||
print("\n- running cmake configure...")
|
||||
run_cmake_configure()
|
||||
|
||||
print_status(ResultStatus.SUCCESS)
|
||||
return 0
|
||||
@@ -1963,15 +1856,11 @@ class TidyCommand(Command):
|
||||
self.parser.add_argument(
|
||||
"--fix", action="store_true",
|
||||
help="Applies suggested fixes, where possible.")
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
# Run premake to generate our compile_commands.json file for clang to use.
|
||||
# TODO(benvanik): only do linux? whatever clang-tidy is ok with.
|
||||
run_premake(get_premake_target_os(args["target_os"]),
|
||||
"export-compile-commands")
|
||||
# Run cmake configure to generate compile_commands.json for clang-tidy.
|
||||
# Use Ninja generator which produces compile_commands.json by default.
|
||||
run_cmake_configure()
|
||||
|
||||
if sys.platform == "darwin":
|
||||
platform_name = "darwin"
|
||||
@@ -2030,9 +1919,6 @@ class StubCommand(Command):
|
||||
self.parser.add_argument(
|
||||
"--class", default=None,
|
||||
help="Generate a class pair (.cc/.h) at the provided location in the source tree")
|
||||
self.parser.add_argument(
|
||||
"--target_os", default=None,
|
||||
help="Target OS passed to premake, for cross-compilation")
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
root = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -2064,7 +1950,8 @@ class StubCommand(Command):
|
||||
print_error("Please specify a file/class to generate")
|
||||
return 1
|
||||
|
||||
run_platform_premake(target_os_override=args["target_os"])
|
||||
# Reconfigure to pick up the new source file.
|
||||
run_cmake_configure()
|
||||
return 0
|
||||
|
||||
class DevenvCommand(Command):
|
||||
@@ -2079,23 +1966,15 @@ class DevenvCommand(Command):
|
||||
*args, **kwargs)
|
||||
|
||||
def execute(self, args, pass_args, cwd):
|
||||
devenv = None
|
||||
show_reload_prompt = False
|
||||
if sys.platform == "win32":
|
||||
if not vs_version:
|
||||
print_error("Visual Studio is not installed.");
|
||||
return 1
|
||||
print("Launching Visual Studio...")
|
||||
elif sys.platform == "darwin":
|
||||
print("Launching Xcode...")
|
||||
devenv = "xcode4"
|
||||
elif has_bin("clion") or has_bin("clion.sh"):
|
||||
print("Launching CLion...")
|
||||
show_reload_prompt = create_clion_workspace()
|
||||
devenv = "cmake"
|
||||
else:
|
||||
print("Launching CodeLite...")
|
||||
devenv = "codelite"
|
||||
print("IDE not detected. CMakeLists.txt is in the project root.")
|
||||
|
||||
print("\n- generating shaders...")
|
||||
shader_result = build_shaders()
|
||||
@@ -2103,37 +1982,30 @@ class DevenvCommand(Command):
|
||||
print_error("Shader generation failed")
|
||||
return shader_result
|
||||
|
||||
print("\n- running premake...")
|
||||
run_platform_premake(devenv=devenv)
|
||||
print("\n- running cmake configure...")
|
||||
run_cmake_configure()
|
||||
|
||||
print("\n- launching devenv...")
|
||||
if show_reload_prompt:
|
||||
print_box("Please run \"File ⇒ ↺ Reload CMake Project\" from inside the IDE!")
|
||||
if sys.platform == "win32":
|
||||
shell_call([
|
||||
"devenv",
|
||||
"build\\xenia.sln",
|
||||
])
|
||||
elif sys.platform == "darwin":
|
||||
shell_call([
|
||||
"xed",
|
||||
"build/xenia.xcworkspace",
|
||||
# Generate a VS .sln for IDE use (normal builds still use Ninja)
|
||||
vs_build_dir = os.path.join("build", "vs")
|
||||
subprocess.call([
|
||||
"cmake",
|
||||
"-S", ".",
|
||||
"-B", vs_build_dir,
|
||||
"-G", "Visual Studio 17 2022",
|
||||
"-A", "x64",
|
||||
])
|
||||
sln_path = os.path.join(vs_build_dir, "xenia.sln")
|
||||
print(f"Opening {sln_path} in Visual Studio...")
|
||||
shell_call(["devenv", sln_path])
|
||||
elif has_bin("clion"):
|
||||
shell_call([
|
||||
"clion",
|
||||
".",
|
||||
])
|
||||
shell_call(["clion", "."])
|
||||
elif has_bin("clion.sh"):
|
||||
shell_call([
|
||||
"clion.sh",
|
||||
".",
|
||||
])
|
||||
shell_call(["clion.sh", "."])
|
||||
else:
|
||||
shell_call([
|
||||
"codelite",
|
||||
"build/xenia.workspace",
|
||||
])
|
||||
print("No supported IDE found. Open the project root in your IDE.")
|
||||
print("CMakeLists.txt and CMakePresets.json are in the project root.")
|
||||
print("")
|
||||
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user