#if UNITY_EDITOR_WIN using System; using System.Diagnostics; using System.IO; using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEditor.UnityLinker; using UnityEngine; using PlayerSettings = UnityEditor.PlayerSettings; using WW1.PlayStation; internal class BuildPackageLibs : IPreprocessBuildWithReport, IMovePRXCallback { public int callbackOrder => 0; static string PackageName => "com.ww1gameseries.pssr"; static string SourcePath => $"Packages/{PackageName}/Source~"; const int k_PRXBuildTimeOutS = 600; //10 minutes is the max time allowed to build the PRX #region CompileLibs readonly string[] m_CompileAgainstLibs = new string[] { "SceSysmodule_stub_weak", "ScePsml", "ScePsml_stub_weak", "SceAgcGnmp", "SceAgc", "SceAgcCore", "SceAgcGpuAddress", "SceVideoOut_stub_weak", "SceAgcDriver_stub_weak", "SceAgc_stub_weak", }; #endregion public void OnPreprocessBuild(BuildReport report) { var buildTarget = report.summary.platform; if (!IsBuildTargetValid(buildTarget)) { return; } string prxPath = GetPRXBuildLocation(buildTarget); string prxDir = Path.GetDirectoryName(prxPath); if (IsPRXRebuildRequired(prxPath)) { if (!Directory.Exists(prxDir)) { if (string.IsNullOrWhiteSpace(prxDir)) { throw new BuildFailedException("No PRX Directory"); } Directory.CreateDirectory(prxDir); } BuildPackagePRX(buildTarget, prxPath, m_CompileAgainstLibs); } } public void MovePRXToBuildLocation(BuildTarget target, string buildOutputLocation, bool developmentBuild) { if (!IsBuildTargetValid(target)) { return; } string projectDir = Application.dataPath.Replace("/Assets", ""); string stagingArea = BuildUtils.GetStagingAreaLocationFor(target, projectDir, buildOutputLocation); CopyLibraryPRXToStagingArea(GetPRXBuildLocation(target), stagingArea); } static bool IsPRXRebuildRequired(string prxPath) { if (!File.Exists(prxPath)) { return true; } DateTime prxLastWrite = File.GetLastWriteTimeUtc(prxPath); if (BuildUtils.HaveSourceFilesBeenModifiedSince(SourcePath, prxLastWrite)) { return true; } return false; } static void BuildPackagePRX(BuildTarget target, string outputPath, string[] compileAgainstLibs) { string sdkLoc = Environment.GetEnvironmentVariable(SDKUtils.GetSdkEnvVarFor(target)); string clangLoc = $"{sdkLoc}/host_tools/bin/{SDKUtils.GetClangFor(target)}"; string allSourcePaths = string.Empty; string[] allDirs = Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories); //Include the base folder also, if it has cpp files in it if (Directory.GetFiles(SourcePath, "*.cpp").Length > 0) { allSourcePaths += $"\"{Path.GetFullPath(Path.Combine(SourcePath, "*.cpp"))}\" "; } foreach (var dir in allDirs) { if (Directory.GetFiles(dir, "*.cpp").Length == 0) { continue; } allSourcePaths += $"\"{Path.GetFullPath(Path.Combine(dir, "*.cpp"))}\" "; } string allLinkLibs = string.Empty; foreach (var lib in compileAgainstLibs) { allLinkLibs += $"-l {lib} "; } //Additionally include the common includes string command = $"-D\"NDEBUG\" -O2 -Wall -o\"{outputPath}\" {allSourcePaths} --for-linker=--oformat=prx {allLinkLibs}"; ProcessStartInfo startInfo = new ProcessStartInfo(clangLoc, command) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardError = true, }; Console.WriteLine($"Building PRX with {clangLoc} {command}"); Process proc = Process.Start(startInfo); string errorOutput = proc.StandardError.ReadToEnd(); proc.WaitForExit(k_PRXBuildTimeOutS); if (!proc.HasExited) { proc.Kill(); throw new BuildFailedException($"PRX Build Timed Out\n{errorOutput}"); } if (proc.ExitCode != 0) { throw new BuildFailedException(errorOutput); } } static void CopyLibraryPRXToStagingArea(string prxLocation, string stagingArea) { string copyPath = Path.Combine(stagingArea, LibraryInfo.LibraryNameWithExtension); if (!Directory.Exists(stagingArea)) { Directory.CreateDirectory(stagingArea); } File.Copy(prxLocation, copyPath, true); } static string GetPRXBuildLocation(BuildTarget target) { //Example Path: Library/SourceGeneratedPlugins/PS5/7000045/CommonDialog.prx string relativePath = Path.Combine("Library", "SourceGeneratedPlugins", target.ToString(), SDKUtils.GetSDKVersionString(target), $"{LibraryInfo.LibraryNameWithExtension}"); return Path.GetFullPath(relativePath); } static bool IsBuildTargetValid(BuildTarget target) { return target is BuildTarget.PS5; } } internal interface IMovePRXCallback : IUnityLinkerProcessor { string IUnityLinkerProcessor.GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data) { //In 2021.3 report can return null for non-playstation platforms if (report == null) { return string.Empty; } MovePRXToBuildLocation(report.summary.platform, report.summary.outputPath, report.summary.options.HasFlag(BuildOptions.Development)); return string.Empty; } public void MovePRXToBuildLocation(BuildTarget target, string buildOutputLocation, bool developmentBuild); } #endif