The target names the Unity player, its executable and the runtime AetherDb is published for. Without the argument it builds win64 as before. Built 0.2.0 for both targets on Starfire (Linux cross-built with Mono); both stop at the aetherdb step, because Aetheria compiles CultMath from two CultLib pins and dotnet publish refuses the duplicated shader files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
5.4 KiB
C#
116 lines
5.4 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEditor;
|
|
using UnityEditor.AddressableAssets.Settings;
|
|
using UnityEditor.Build.Reporting;
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
// The one way to make a player directory, for Windows or Linux. Owns what the directory contains: the player and its data,
|
|
// GameData/Aetheria.cc, GameData/Narrative/**, GameData/Mods/** and ModTools/AetherDb. Manual builds, smokes and
|
|
// release cuts all call this and stage nothing themselves.
|
|
//
|
|
// Unity -batchmode -quit -projectPath <repo> -executeMethod AetheriaBuild.Build
|
|
// -aetheriaVersion <semver> -aetheriaOut <dir> [-aetheriaTarget win64|linux64] -logFile <log>
|
|
//
|
|
// AetherDb is published from tools/AetherDb, so the headless build's CULTLIB_ROOT / CULTMATH_ROOT (Directory.Build.props)
|
|
// must be set for dotnet when the defaults do not hold the pinned revisions.
|
|
// Any failure logs "AetheriaBuild FAILED at <step>" and exits 1.
|
|
public static class AetheriaBuild
|
|
{
|
|
private static readonly Regex SemVer = new Regex(@"^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$");
|
|
|
|
// A target: the player Unity builds, its executable, and the runtime AetherDb is published for.
|
|
private static (BuildTarget target, string player, string runtime, string aetherDb) Target(string name) => name switch
|
|
{
|
|
"win64" => (BuildTarget.StandaloneWindows64, "Aetheria.exe", "win-x64", "AetherDb.exe"),
|
|
"linux64" => (BuildTarget.StandaloneLinux64, "Aetheria.x86_64", "linux-x64", "AetherDb"),
|
|
_ => throw new InvalidOperationException($"-aetheriaTarget {name}: expected win64 or linux64"),
|
|
};
|
|
|
|
public static void Build()
|
|
{
|
|
var step = "arguments";
|
|
try
|
|
{
|
|
var version = Argument("-aetheriaVersion");
|
|
if (!SemVer.IsMatch(version)) throw new InvalidOperationException("-aetheriaVersion is not a semver");
|
|
var output = Path.GetFullPath(Argument("-aetheriaOut"));
|
|
var target = Target(Argument("-aetheriaTarget", "win64"));
|
|
var repoRoot = Directory.GetParent(UnityEngine.Application.dataPath).FullName;
|
|
|
|
step = "version";
|
|
UnityEditor.PlayerSettings.bundleVersion = version; // the game has its own global PlayerSettings
|
|
|
|
step = "addressables";
|
|
AddressableAssetSettings.BuildPlayerContent(out var content);
|
|
if (!string.IsNullOrEmpty(content.Error)) throw new InvalidOperationException("Addressables build reported an error: " + content.Error);
|
|
|
|
step = "player";
|
|
Directory.CreateDirectory(output);
|
|
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(scene => scene.path).ToArray();
|
|
if (scenes.Length == 0) throw new InvalidOperationException("EditorBuildSettings has no enabled scene");
|
|
var report = BuildPipeline.BuildPlayer(new BuildPlayerOptions
|
|
{
|
|
scenes = scenes,
|
|
locationPathName = Path.Combine(output, target.player),
|
|
target = target.target,
|
|
options = BuildOptions.None,
|
|
});
|
|
if (report.summary.result != BuildResult.Succeeded || report.summary.totalErrors != 0)
|
|
throw new InvalidOperationException($"BuildPlayer ended {report.summary.result} with {report.summary.totalErrors} errors");
|
|
|
|
step = "gamedata";
|
|
PlayerStaging.Stage(repoRoot, output);
|
|
|
|
step = "aetherdb";
|
|
Publish(repoRoot, Path.Combine(output, "ModTools"), target.runtime, target.aetherDb);
|
|
|
|
step = "burst";
|
|
var burst = Path.Combine(output, "Aetheria_BurstDebugInformation_DoNotShip");
|
|
if (Directory.Exists(burst)) Directory.Delete(burst, true);
|
|
|
|
Debug.Log($"AetheriaBuild OK {version} {output}");
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
Debug.LogError($"AetheriaBuild FAILED at {step}: {error.GetType().Name}: {error.Message}");
|
|
EditorApplication.Exit(1);
|
|
return;
|
|
}
|
|
EditorApplication.Exit(0);
|
|
}
|
|
|
|
private static void Publish(string repoRoot, string modTools, string runtime, string aetherDb)
|
|
{
|
|
var info = new ProcessStartInfo("dotnet")
|
|
{
|
|
Arguments = $"publish tools/AetherDb -c Release -r {runtime} --self-contained -p:PublishSingleFile=true -o \"{modTools}\"",
|
|
WorkingDirectory = repoRoot,
|
|
UseShellExecute = false,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
};
|
|
using (var process = Process.Start(info))
|
|
{
|
|
var errors = process.StandardError.ReadToEndAsync();
|
|
var text = process.StandardOutput.ReadToEnd();
|
|
process.WaitForExit();
|
|
if (process.ExitCode != 0)
|
|
throw new InvalidOperationException($"dotnet publish exited {process.ExitCode}:\n{text}\n{errors.Result}");
|
|
}
|
|
if (!File.Exists(Path.Combine(modTools, aetherDb)))
|
|
throw new InvalidOperationException($"dotnet publish produced no ModTools/{aetherDb}");
|
|
}
|
|
|
|
private static string Argument(string name, string fallback = null)
|
|
{
|
|
var args = Environment.GetCommandLineArgs();
|
|
var at = Array.IndexOf(args, name);
|
|
if (at < 0 || at + 1 >= args.Length)
|
|
return fallback ?? throw new InvalidOperationException(name + " is required");
|
|
return args[at + 1];
|
|
}
|
|
}
|