暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BurstAotCompiler.cs 102KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239
  1. #if UNITY_EDITOR && ENABLE_BURST_AOT
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9. using UnityEditor;
  10. using UnityEditor.Android;
  11. using UnityEditor.Build;
  12. using UnityEditor.Build.Reporting;
  13. using UnityEditor.Compilation;
  14. using UnityEditor.Scripting;
  15. using UnityEditor.Scripting.ScriptCompilation;
  16. using UnityEditor.Scripting.Compilers;
  17. using UnityEditor.UnityLinker;
  18. using UnityEditor.Utils;
  19. using UnityEngine;
  20. using CompilerMessageType = UnityEditor.Scripting.Compilers.CompilerMessageType;
  21. using Debug = UnityEngine.Debug;
  22. using System.Runtime.InteropServices;
  23. #if UNITY_EDITOR_OSX
  24. using System.ComponentModel;
  25. using Unity.Burst.LowLevel;
  26. using UnityEditor.Callbacks;
  27. #endif
  28. namespace Unity.Burst.Editor
  29. {
  30. using static BurstCompilerOptions;
  31. internal class TargetCpus
  32. {
  33. public List<BurstTargetCpu> Cpus;
  34. public TargetCpus()
  35. {
  36. Cpus = new List<BurstTargetCpu>();
  37. }
  38. public TargetCpus(BurstTargetCpu single)
  39. {
  40. Cpus = new List<BurstTargetCpu>(1)
  41. {
  42. single
  43. };
  44. }
  45. public bool IsX86()
  46. {
  47. foreach (var cpu in Cpus)
  48. {
  49. switch (cpu)
  50. {
  51. case BurstTargetCpu.X86_SSE2:
  52. case BurstTargetCpu.X86_SSE4:
  53. return true;
  54. }
  55. }
  56. return false;
  57. }
  58. public override string ToString()
  59. {
  60. var result = "";
  61. var first = true;
  62. foreach (var cpu in Cpus)
  63. {
  64. if (first)
  65. {
  66. result += $"{cpu}";
  67. first = false;
  68. }
  69. else
  70. {
  71. result += $", {cpu}";
  72. }
  73. }
  74. return result;
  75. }
  76. public TargetCpus Clone()
  77. {
  78. var copy = new TargetCpus
  79. {
  80. Cpus = new List<BurstTargetCpu>(Cpus.Count)
  81. };
  82. foreach (var cpu in Cpus)
  83. {
  84. copy.Cpus.Add(cpu);
  85. }
  86. return copy;
  87. }
  88. }
  89. #if !ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  90. internal class LinkXMLGenerator : IUnityLinkerProcessor
  91. {
  92. public int callbackOrder => 1;
  93. public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data)
  94. {
  95. var linkXml = Path.GetFullPath(Path.Combine("Temp", BurstAotCompiler.BurstLinkXmlName));
  96. return linkXml;
  97. }
  98. public void OnBeforeRun(BuildReport report, UnityLinkerBuildPipelineData data)
  99. {
  100. }
  101. public void OnAfterRun(BuildReport report, UnityLinkerBuildPipelineData data)
  102. {
  103. }
  104. }
  105. #endif
  106. #if ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  107. internal class BurstAOTCompilerPostprocessor : IGenerateNativePluginsForAssemblies
  108. #else
  109. internal class BurstAOTCompilerPostprocessor : IPostBuildPlayerScriptDLLs
  110. #endif
  111. {
  112. public int callbackOrder => 0;
  113. private Assembly[] playerBuildAssemblies = null;
  114. #if ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  115. public IGenerateNativePluginsForAssemblies.PrepareResult PrepareOnMainThread(IGenerateNativePluginsForAssemblies.PrepareArgs args)
  116. {
  117. if (ForceDisableBurstCompilation)
  118. return new();
  119. DoSetup(args.report);
  120. var target = BurstPlatformAotSettings.ResolveTarget(settings.summary.platform);
  121. return new()
  122. {
  123. additionalInputFiles = new[]
  124. {
  125. // Any files in this list will be scanned for changes, and any changes in these files will trigger
  126. // a rerun of the Burst compiler on the player build (even if the script assemblies have not changed).
  127. //
  128. // We add the settings so that changing any Burst setting will trigger a rebuild.
  129. BurstPlatformAotSettings.GetPath(target),
  130. // Like above, but specifically for settings unique to player-builds (like SDK versions and like)
  131. // Those settings are extracted in `DoSetup`
  132. BurstAotCompiler.BurstAOTSettings.GetPath(target),
  133. // We don't want to scan every file in the Burst package (though every file could potentially change
  134. // behavior). When working on Burst code locally, you may need to select "Clean Build" in the Build
  135. // settings window to force a rebuild to pick up the changes.
  136. //
  137. // But we add the compiler executable to have at least on file in the package. This should be good
  138. // enough for users. Because any change in Burst will come with a change of the Burst package
  139. // version, which will change the pathname for this file (which will then trigger a rebuild, even
  140. // if the contents have not changed).
  141. BurstLoader.BclConfiguration.ExecutablePath,
  142. },
  143. displayName = "Running Burst Compiler"
  144. };
  145. }
  146. public IGenerateNativePluginsForAssemblies.GenerateResult GenerateNativePluginsForAssemblies(IGenerateNativePluginsForAssemblies.GenerateArgs args)
  147. {
  148. if (ForceDisableBurstCompilation)
  149. return new ();
  150. if (Directory.Exists(BurstAotCompiler.OutputBaseFolder))
  151. Directory.Delete(BurstAotCompiler.OutputBaseFolder, true);
  152. var assemblies = args.assemblyFiles.Select(path =>
  153. {
  154. var name = Path.GetFileNameWithoutExtension(path);
  155. return new Assembly(
  156. name,
  157. path,
  158. Array.Empty<string>(),
  159. // Try to look up any defines for this assembly from the player build report
  160. playerBuildAssemblies.FirstOrDefault(asm => asm.name == name)?.defines ?? Array.Empty<string>(),
  161. Array.Empty<Assembly>(),
  162. Array.Empty<string>(),
  163. UnityEditor.Compilation.AssemblyFlags.None);
  164. })
  165. // We don't run Burst on UnityEngine assemblies, so we skip them to save time
  166. .Where(a => !a.name.StartsWith("UnityEngine."))
  167. .ToArray();
  168. return new () { generatedPlugins = DoGenerate(assemblies).ToArray() };
  169. }
  170. #else
  171. public void OnPostBuildPlayerScriptDLLs(BuildReport report)
  172. {
  173. if (ForceDisableBurstCompilation)
  174. {
  175. return;
  176. }
  177. var step = report.BeginBuildStep("burst");
  178. try
  179. {
  180. DoSetup(report);
  181. DoGenerate(playerBuildAssemblies)
  182. .ToList(); // Force enumeration
  183. }
  184. finally
  185. {
  186. report.EndBuildStep(step);
  187. }
  188. }
  189. #endif
  190. private BurstAotCompiler.BurstAOTSettings settings;
  191. public void DoSetup(BuildReport report)
  192. {
  193. settings = new BurstAotCompiler.BurstAOTSettings()
  194. {
  195. summary = report.summary,
  196. productName = PlayerSettings.productName
  197. };
  198. settings.aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(settings.summary.platform);
  199. settings.isSupported = BurstAotCompiler.IsSupportedPlatform(settings.summary.platform, settings.aotSettingsForTarget);
  200. if (settings.isSupported)
  201. {
  202. settings.targetPlatform = BurstAotCompiler.GetTargetPlatformAndDefaultCpu(settings.summary.platform,
  203. out settings.targetCpus, settings.aotSettingsForTarget);
  204. settings.combinations =
  205. BurstAotCompiler.CollectCombinations(settings.targetPlatform, settings.targetCpus,
  206. settings.summary);
  207. settings.scriptingBackend =
  208. #if UNITY_2021_2_OR_NEWER
  209. PlayerSettings.GetScriptingBackend(NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(settings.summary.platform)));
  210. #else
  211. PlayerSettings.GetScriptingBackend(BuildPipeline.GetBuildTargetGroup(settings.summary.platform));
  212. #endif
  213. #if UNITY_IOS
  214. if (settings.targetPlatform == TargetPlatform.iOS)
  215. {
  216. settings.extraOptions = new List<string>();
  217. settings.extraOptions.Add(GetOption(OptionLinkerOptions, $"min-ios-version={PlayerSettings.iOS.targetOSVersionString}"));
  218. settings.extraOptions.Add(GetOption(OptionPlatformConfiguration, PlayerSettings.iOS.targetOSVersionString));
  219. }
  220. #endif
  221. #if UNITY_TVOS
  222. if (settings.targetPlatform == TargetPlatform.tvOS)
  223. {
  224. settings.extraOptions = new List<string>();
  225. settings.extraOptions.Add(GetOption(OptionLinkerOptions, $"min-tvos-version={PlayerSettings.tvOS.targetOSVersionString}"));
  226. settings.extraOptions.Add(GetOption(OptionPlatformConfiguration, PlayerSettings.tvOS.targetOSVersionString));
  227. }
  228. #endif
  229. #if UNITY_VISIONOS
  230. if (settings.targetPlatform == TargetPlatform.visionOS || settings.targetPlatform == TargetPlatform.visionSimulator)
  231. {
  232. settings.extraOptions = new List<string>();
  233. #if UNITY_2023_3_OR_NEWER
  234. var targetOSVersionString = PlayerSettings.VisionOS.targetOSVersionString;
  235. #else
  236. var playerSettings = typeof(PlayerSettings);
  237. var visionOs = playerSettings.GetNestedType("VisionOS");
  238. if (visionOs == null) throw new Exception("Editor does not appear to support visionOS");
  239. var targetOSVersionStringProperty = visionOs.GetProperty("targetOSVersionString", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
  240. if (targetOSVersionStringProperty == null) throw new Exception("Property `targetOSVersionString` not found");
  241. var targetOSVersionString = targetOSVersionStringProperty.GetValue(null, null);
  242. #endif
  243. settings.extraOptions.Add(GetOption(OptionLinkerOptions, $"min-visionos-version={targetOSVersionString}"));
  244. settings.extraOptions.Add(GetOption(OptionPlatformConfiguration, targetOSVersionString));
  245. }
  246. #endif
  247. #if UNITY_2022_2_OR_NEWER && UNITY_ANDROID
  248. if (settings.targetPlatform == TargetPlatform.Android)
  249. {
  250. // Enable Armv9 security features (PAC/BTI) if needed
  251. settings.aotSettingsForTarget.EnableArmv9SecurityFeatures = PlayerSettings.Android.enableArmv9SecurityFeatures;
  252. if (PlayerSettings.Android.enableArmv9SecurityFeatures)
  253. {
  254. settings.extraOptions ??= new List<string>();
  255. settings.extraOptions.Add(GetOption(OptionPlatformConfiguration, "armv9-sec"));
  256. }
  257. }
  258. #endif
  259. if (settings.targetPlatform == TargetPlatform.UWP)
  260. {
  261. settings.extraOptions = new List<string>();
  262. if (!string.IsNullOrEmpty(EditorUserBuildSettings.wsaUWPVisualStudioVersion))
  263. {
  264. settings.extraOptions.Add(GetOption(OptionLinkerOptions, $"vs-version={EditorUserBuildSettings.wsaUWPVisualStudioVersion}"));
  265. }
  266. if (!string.IsNullOrEmpty(EditorUserBuildSettings.wsaUWPSDK))
  267. {
  268. settings.extraOptions.Add(GetOption(OptionLinkerOptions, $"target-sdk-version={EditorUserBuildSettings.wsaUWPSDK}"));
  269. }
  270. settings.extraOptions.Add(GetOption(OptionPlatformConfiguration, $"{EditorUserBuildSettings.wsaUWPVisualStudioVersion}:{EditorUserBuildSettings.wsaUWPSDK}:{EditorUserBuildSettings.wsaMinUWPSDK}"));
  271. }
  272. #if PLATFORM_QNX
  273. if (settings.targetPlatform == TargetPlatform.QNX)
  274. {
  275. settings.extraOptions = new List<string>();
  276. settings.extraOptions.Add(GetOption(OptionPlatformConfiguration, GetQNXTargetOsVersion()));
  277. }
  278. #endif
  279. playerBuildAssemblies = BurstAotCompiler.GetPlayerAssemblies(report);
  280. Hash128 definesHash = default;
  281. foreach (var assembly in playerBuildAssemblies.OrderBy(x => x.name))
  282. {
  283. definesHash.Append(assembly.name);
  284. definesHash.Append(assembly.defines.Length);
  285. foreach (var symbol in assembly.defines.OrderBy(x => x))
  286. {
  287. definesHash.Append(symbol);
  288. }
  289. }
  290. settings.symbolDefinesHash = definesHash;
  291. settings.Save();
  292. }
  293. }
  294. private static string GetQNXTargetOsVersion()
  295. {
  296. var flags = System.Reflection.BindingFlags.Public |
  297. System.Reflection.BindingFlags.Static |
  298. System.Reflection.BindingFlags.FlattenHierarchy;
  299. var property = typeof(EditorUserBuildSettings).GetProperty("selectedQnxOsVersion", flags);
  300. if (null == property)
  301. {
  302. return "NOT_FOUND";
  303. }
  304. var value = (int)property.GetValue(null, null);
  305. switch (value)
  306. {
  307. case /*UnityEditor.QNXOsVersion.Neutrino70*/ 0: return "Neutrino70";
  308. case /*UnityEditor.QNXOsVersion.Neutrino71*/ 1: return "Neutrino71";
  309. default: return $"UNKNOWN_{value}";
  310. }
  311. }
  312. public IEnumerable<string> DoGenerate(Assembly[] assemblies)
  313. {
  314. if (!settings.isSupported)
  315. return Array.Empty<string>();
  316. return BurstAotCompiler.OnPostBuildPlayerScriptDLLsImpl(settings, assemblies);
  317. }
  318. }
  319. #if !ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  320. internal class BurstAndroidGradlePostprocessor : IPostGenerateGradleAndroidProject
  321. {
  322. int IOrderedCallback.callbackOrder => 1;
  323. void IPostGenerateGradleAndroidProject.OnPostGenerateGradleAndroidProject(string path)
  324. {
  325. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.Android);
  326. // Early exit if burst is not activated
  327. if (BurstCompilerOptions.ForceDisableBurstCompilation || !aotSettingsForTarget.EnableBurstCompilation)
  328. {
  329. return;
  330. }
  331. // Copy bursted .so's from tempburstlibs to the actual location in the gradle project
  332. var sourceLocation = Path.GetFullPath(Path.Combine("Temp", "StagingArea", "tempburstlibs"));
  333. var targetLocation = Path.GetFullPath(Path.Combine(path, "src", "main", "jniLibs"));
  334. FileUtil.CopyDirectoryRecursive(sourceLocation, targetLocation, true);
  335. }
  336. }
  337. // For static builds, there are two different approaches:
  338. // Postprocessing adds the libraries after Unity is done building,
  339. // for platforms that need to build a project file, etc.
  340. // Preprocessing simply adds the libraries to the Unity build,
  341. // for platforms where Unity can directly build an app.
  342. internal class StaticPreProcessor : IPreprocessBuildWithReport
  343. {
  344. private const string TempSourceLibrary = @"Temp/StagingArea/SourcePlugins";
  345. public int callbackOrder { get { return 0; } }
  346. public void OnPreprocessBuild(BuildReport report)
  347. {
  348. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform);
  349. // Early exit if burst is not activated
  350. if (BurstCompilerOptions.ForceDisableBurstCompilation || !aotSettingsForTarget.EnableBurstCompilation)
  351. {
  352. return;
  353. }
  354. if(report.summary.platform == BuildTarget.Switch)
  355. {
  356. if(!Directory.Exists(TempSourceLibrary))
  357. {
  358. Directory.CreateDirectory(TempSourceLibrary);
  359. Directory.CreateDirectory(TempSourceLibrary);
  360. }
  361. BurstAotCompiler.WriteStaticLinkCppFile(TempSourceLibrary);
  362. }
  363. }
  364. }
  365. #endif
  366. /// <summary>
  367. /// Integration of the burst AOT compiler into the Unity build player pipeline
  368. /// </summary>
  369. internal class BurstAotCompiler
  370. {
  371. #if ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  372. // When using the new player build API, don't write to Temp/StagingArea.
  373. // We still need code in Unity to support old versions of Burst not using the new API.
  374. // for that case, we will just pick up files written to the Temp/StagingArea.
  375. // So in order to not pick up files twice, use a different output location for the new
  376. // API.
  377. internal const string OutputBaseFolder = @"Temp/BurstOutput/";
  378. #else
  379. private const string OutputBaseFolder = @"Temp/StagingArea/";
  380. #endif
  381. private const string TempStagingManaged = OutputBaseFolder + @"Data/Managed/";
  382. private const string LibraryPlayerScriptAssemblies = "Library/PlayerScriptAssemblies";
  383. private const string TempManagedSymbols = @"Temp/ManagedSymbols/";
  384. internal const string BurstLinkXmlName = "burst.link.xml";
  385. internal struct BurstAOTSettings
  386. {
  387. public BuildSummary summary;
  388. public BurstPlatformAotSettings aotSettingsForTarget;
  389. public TargetPlatform targetPlatform;
  390. public TargetCpus targetCpus;
  391. public List<BurstAotCompiler.BurstOutputCombination> combinations;
  392. public ScriptingImplementation scriptingBackend;
  393. public string productName;
  394. public bool isSupported;
  395. public List<string> extraOptions;
  396. public Hash128 symbolDefinesHash;
  397. // Hash any fields that might have an effect on whether Bursted code needs to be recompiled
  398. // Note that the BurstPlatformAotSettings are saved and used separately, so they don't need to
  399. // be included in this hash.
  400. public Hash128 Hash()
  401. {
  402. var hc = new Hash128();
  403. hc.Append((int)(summary.options & (BuildOptions.InstallInBuildFolder | BuildOptions.Development)));
  404. hc.Append((int)targetPlatform);
  405. hc.Append(isSupported ? 1 : 0);
  406. if (targetCpus?.Cpus != null)
  407. {
  408. hc.Append(targetCpus.Cpus.Count);
  409. foreach (var cpu in targetCpus.Cpus)
  410. {
  411. hc.Append((int)cpu);
  412. }
  413. }
  414. if (combinations != null)
  415. {
  416. hc.Append(combinations.Count);
  417. foreach (var comb in combinations)
  418. {
  419. comb.HashInto(ref hc);
  420. }
  421. }
  422. if (extraOptions != null)
  423. {
  424. hc.Append(extraOptions.Count);
  425. foreach (var opt in extraOptions)
  426. {
  427. hc.Append(opt);
  428. }
  429. }
  430. hc.Append(symbolDefinesHash.u64_0);
  431. hc.Append(symbolDefinesHash.u64_1);
  432. hc.Append((int)scriptingBackend);
  433. hc.Append(productName);
  434. return hc;
  435. }
  436. public void Save()
  437. {
  438. var path = GetPath(BurstPlatformAotSettings.ResolveTarget(summary.platform));
  439. var hash = Hash();
  440. try
  441. {
  442. if (File.Exists(path))
  443. {
  444. // If the hashes match, don't touch the file so Bee will consider it unchanged
  445. var bytes = File.ReadAllBytes(path);
  446. var storedHash = new Hash128(BitConverter.ToUInt64(bytes, 0), BitConverter.ToUInt64(bytes, 8));
  447. if (storedHash == hash)
  448. {
  449. return;
  450. }
  451. }
  452. using (var f = new BufferedStream(File.OpenWrite(path)))
  453. {
  454. f.Write(BitConverter.GetBytes(hash.u64_0), 0, 8);
  455. f.Write(BitConverter.GetBytes(hash.u64_1), 0, 8);
  456. }
  457. }
  458. catch (Exception)
  459. {
  460. // If we for some reason fail to save the settings, delete the existing ones (if any) to be sure
  461. // we invalidate the cache and cause a recompilation
  462. try
  463. {
  464. if (File.Exists(path))
  465. {
  466. File.Delete(path);
  467. }
  468. }
  469. catch (Exception)
  470. {
  471. // Welp
  472. }
  473. }
  474. }
  475. public static string GetPath(BuildTarget? target)
  476. {
  477. var root = "Library/BurstCache";
  478. if (target.HasValue)
  479. {
  480. return $"{root}/AotSettings_{target.Value}.hash";
  481. }
  482. else
  483. {
  484. return $"{root}/AotSettings.hash";
  485. }
  486. }
  487. }
  488. static void CopyDirectory(string sourceDir, string destinationDir, bool recursive)
  489. {
  490. // Get information about the source directory
  491. var dir = new DirectoryInfo(sourceDir);
  492. // Check if the source directory exists
  493. if (!dir.Exists)
  494. throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
  495. // Cache directories before we start copying
  496. DirectoryInfo[] dirs = dir.GetDirectories();
  497. // Create the destination directory
  498. Directory.CreateDirectory(destinationDir);
  499. // Get the files in the source directory and copy to the destination directory
  500. foreach (FileInfo file in dir.GetFiles())
  501. {
  502. string targetFilePath = Path.Combine(destinationDir, file.Name);
  503. file.CopyTo(targetFilePath);
  504. }
  505. // If recursive and copying subdirectories, recursively call this method
  506. if (recursive)
  507. {
  508. foreach (DirectoryInfo subDir in dirs)
  509. {
  510. string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
  511. CopyDirectory(subDir.FullName, newDestinationDir, true);
  512. }
  513. }
  514. }
  515. internal static IEnumerable<string> OnPostBuildPlayerScriptDLLsImpl(BurstAOTSettings settings, Assembly[] playerAssemblies)
  516. {
  517. var buildTarget = settings.summary.platform;
  518. string burstMiscAlongsidePath = "";
  519. if ((settings.summary.options & BuildOptions.InstallInBuildFolder) == 0)
  520. {
  521. burstMiscAlongsidePath = BurstPlatformAotSettings.FetchOutputPath(settings.summary, settings.productName);
  522. }
  523. HashSet<string> assemblyDefines = new HashSet<string>();
  524. // Early exit if burst is not activated or the platform is not supported
  525. if (BurstCompilerOptions.ForceDisableBurstCompilation || !settings.aotSettingsForTarget.EnableBurstCompilation)
  526. {
  527. return Array.Empty<string>();
  528. }
  529. var isDevelopmentBuild = (settings.summary.options & BuildOptions.Development) != 0;
  530. var commonOptions = new List<string>();
  531. var stagingFolder = Path.GetFullPath(TempStagingManaged);
  532. // grab the location of the root of the player folder - for handling nda platforms that require keys
  533. var keyFolder = BuildPipeline.GetPlaybackEngineDirectory(buildTarget, BuildOptions.None);
  534. commonOptions.Add(GetOption(OptionAotKeyFolder, keyFolder));
  535. commonOptions.Add(GetOption(OptionAotDecodeFolder, Path.Combine(Environment.CurrentDirectory, "Library", "Burst")));
  536. // Extract the TargetPlatform and Cpus from the current build settings
  537. commonOptions.Add(GetOption(OptionPlatform, settings.targetPlatform));
  538. // --------------------------------------------------------------------------------------------------------
  539. // 1) Calculate AssemblyFolders
  540. // These are the folders to look for assembly resolution
  541. // --------------------------------------------------------------------------------------------------------
  542. var assemblyFolders = new List<string> { stagingFolder };
  543. foreach (var assembly in playerAssemblies)
  544. AddAssemblyFolder(assembly.outputPath, stagingFolder, buildTarget, assemblyFolders);
  545. if (buildTarget == BuildTarget.WSAPlayer || buildTarget == BuildTarget.GameCoreXboxOne || buildTarget == BuildTarget.GameCoreXboxSeries)
  546. {
  547. // On UWP, not all assemblies are copied to StagingArea, so we want to
  548. // find all directories that we can reference assemblies from
  549. // If we don't do this, we will crash with AssemblyResolutionException
  550. // when following type references.
  551. foreach (var assembly in playerAssemblies)
  552. {
  553. foreach (var assemblyRef in assembly.compiledAssemblyReferences)
  554. AddAssemblyFolder(assemblyRef, stagingFolder, buildTarget, assemblyFolders);
  555. }
  556. }
  557. if (settings.extraOptions != null)
  558. {
  559. commonOptions.AddRange(settings.extraOptions);
  560. }
  561. // Copy assembly used during staging to have a trace
  562. if (BurstLoader.IsDebugging)
  563. {
  564. try
  565. {
  566. var copyAssemblyFolder = Path.Combine(Environment.CurrentDirectory, "Logs", "StagingAssemblies");
  567. try
  568. {
  569. if (Directory.Exists(copyAssemblyFolder)) Directory.Delete(copyAssemblyFolder);
  570. }
  571. catch
  572. {
  573. }
  574. if (!Directory.Exists(copyAssemblyFolder)) Directory.CreateDirectory(copyAssemblyFolder);
  575. foreach (var file in Directory.EnumerateFiles(stagingFolder))
  576. {
  577. File.Copy(file, Path.Combine(copyAssemblyFolder, Path.GetFileName(file)));
  578. }
  579. }
  580. catch
  581. {
  582. }
  583. }
  584. // --------------------------------------------------------------------------------------------------------
  585. // 2) Calculate root assemblies
  586. // These are the assemblies that the compiler will look for methods to compile
  587. // This list doesn't typically include .NET runtime assemblies but only assemblies compiled as part
  588. // of the current Unity project
  589. // --------------------------------------------------------------------------------------------------------
  590. var rootAssemblies = new List<string>();
  591. foreach (var playerAssembly in playerAssemblies)
  592. {
  593. #if ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  594. var playerAssemblyPath = Path.GetFullPath(playerAssembly.outputPath);
  595. #else
  596. // the file at path `playerAssembly.outputPath` is actually not on the disk
  597. // while it is in the staging folder because OnPostBuildPlayerScriptDLLs is being called once the files are already
  598. // transferred to the staging folder, so we are going to work from it but we are reusing the file names that we got earlier
  599. var playerAssemblyPath = Path.Combine(stagingFolder, Path.GetFileName(playerAssembly.outputPath));
  600. #endif
  601. if (!File.Exists(playerAssemblyPath))
  602. {
  603. Debug.LogWarning($"Unable to find player assembly: {playerAssembly.outputPath}");
  604. }
  605. else
  606. {
  607. rootAssemblies.Add(playerAssemblyPath);
  608. commonOptions.Add(GetOption(OptionAssemblyDefines, $"{playerAssembly.name};{string.Join(";", playerAssembly.defines)}"));
  609. }
  610. }
  611. commonOptions.AddRange(rootAssemblies.Select(root => GetOption(OptionRootAssembly, root)));
  612. // --------------------------------------------------------------------------------------------------------
  613. // 4) Compile each combination
  614. //
  615. // Here bcl.exe is called for each target CPU combination
  616. // --------------------------------------------------------------------------------------------------------
  617. string debugLogFile = null;
  618. if (BurstLoader.IsDebugging)
  619. {
  620. // Reset log files
  621. try
  622. {
  623. var logDir = Path.Combine(Environment.CurrentDirectory, "Logs");
  624. debugLogFile = Path.Combine(logDir, "burst_bcl_editor.log");
  625. if (!Directory.Exists(logDir)) Directory.CreateDirectory(logDir);
  626. File.WriteAllText(debugLogFile, string.Empty);
  627. }
  628. catch
  629. {
  630. debugLogFile = null;
  631. }
  632. }
  633. if ((settings.summary.options & BuildOptions.InstallInBuildFolder) == 0)
  634. {
  635. CreateFolderForMiscFiles(burstMiscAlongsidePath);
  636. }
  637. // Log the targets generated by BurstReflection.FindExecuteMethods
  638. foreach (var combination in settings.combinations)
  639. {
  640. // Gets the output folder
  641. var stagingOutputFolder = Path.GetFullPath(Path.Combine(OutputBaseFolder, combination.OutputPath));
  642. var outputFilePrefix = Path.Combine(stagingOutputFolder, combination.LibraryName);
  643. var options = new List<string>(commonOptions)
  644. {
  645. GetOption(OptionAotOutputPath, outputFilePrefix),
  646. GetOption(OptionTempDirectory, Path.Combine(Environment.CurrentDirectory, "Temp", "Burst"))
  647. };
  648. foreach (var cpu in combination.TargetCpus.Cpus)
  649. {
  650. options.Add(GetOption(OptionTarget, cpu));
  651. }
  652. if (settings.targetPlatform == TargetPlatform.iOS || settings.targetPlatform == TargetPlatform.tvOS || settings.targetPlatform == TargetPlatform.Switch || settings.targetPlatform == TargetPlatform.visionOS)
  653. {
  654. options.Add(GetOption(OptionStaticLinkage));
  655. #if ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  656. WriteStaticLinkCppFile($"{OutputBaseFolder}/{combination.OutputPath}");
  657. #endif
  658. }
  659. if (settings.targetPlatform == TargetPlatform.Windows)
  660. {
  661. options.Add(GetOption(OptionLinkerOptions, $"PdbAltPath=\"{settings.productName}_{combination.OutputPath}/{Path.GetFileNameWithoutExtension(combination.LibraryName)}.pdb\""));
  662. }
  663. #if UNITY_2022_2_OR_NEWER && UNITY_ANDROID
  664. if (settings.targetPlatform == TargetPlatform.Android)
  665. {
  666. // Enable Armv9 security features (PAC/BTI) if needed
  667. if (settings.aotSettingsForTarget.EnableArmv9SecurityFeatures)
  668. options.Add(GetOption(OptionBranchProtection, "Standard"));
  669. }
  670. #endif
  671. options.AddRange(assemblyFolders.Select(assemblyFolder => GetOption(OptionAotAssemblyFolder, assemblyFolder)));
  672. // Set the flag to print a message on missing MonoPInvokeCallback attribute on IL2CPP only
  673. if (settings.scriptingBackend == ScriptingImplementation.IL2CPP)
  674. {
  675. options.Add(GetOption(OptionPrintLogOnMissingPInvokeCallbackAttribute));
  676. }
  677. // Log the targets generated by BurstReflection.FindExecuteMethods
  678. if (BurstLoader.IsDebugging && debugLogFile != null)
  679. {
  680. try
  681. {
  682. var writer = new StringWriter();
  683. writer.WriteLine("-----------------------------------------------------------");
  684. writer.WriteLine("Combination: " + combination);
  685. writer.WriteLine("-----------------------------------------------------------");
  686. foreach (var option in options)
  687. {
  688. writer.WriteLine(option);
  689. }
  690. writer.WriteLine("Assemblies in AssemblyFolders:");
  691. foreach (var assemblyFolder in assemblyFolders)
  692. {
  693. writer.WriteLine("|- Folder: " + assemblyFolder);
  694. foreach (var assemblyOrDll in Directory.EnumerateFiles(assemblyFolder, "*.dll"))
  695. {
  696. var fileInfo = new FileInfo(assemblyOrDll);
  697. writer.WriteLine(" |- " + assemblyOrDll + " Size: " + fileInfo.Length + " Date: " + fileInfo.LastWriteTime);
  698. }
  699. }
  700. File.AppendAllText(debugLogFile, writer.ToString());
  701. }
  702. catch
  703. {
  704. // ignored
  705. }
  706. }
  707. // Allow burst to find managed symbols in the backup location in case the symbols are stripped in the build location
  708. options.Add(GetOption(OptionAotPdbSearchPaths, TempManagedSymbols));
  709. if (isDevelopmentBuild && Environment.GetEnvironmentVariable("UNITY_BURST_ENABLE_SAFETY_CHECKS_IN_PLAYER_BUILD") != null)
  710. {
  711. options.Add("--global-safety-checks-setting=ForceOn");
  712. }
  713. options.Add(GetOption(OptionGenerateLinkXml, Path.Combine("Temp", BurstLinkXmlName)));
  714. if (!string.IsNullOrWhiteSpace(settings.aotSettingsForTarget.DisabledWarnings))
  715. {
  716. options.Add(GetOption(OptionDisableWarnings, settings.aotSettingsForTarget.DisabledWarnings));
  717. }
  718. if (isDevelopmentBuild || settings.aotSettingsForTarget.EnableDebugInAllBuilds)
  719. {
  720. if (!isDevelopmentBuild)
  721. {
  722. Debug.LogWarning(
  723. "Symbols are being generated for burst compiled code, please ensure you intended this - see Burst AOT settings.");
  724. }
  725. options.Add(GetOption(OptionDebug,
  726. (settings.aotSettingsForTarget.DebugDataKind == DebugDataKind.Full) && (!combination.WorkaroundFullDebugInfo) ? "Full" : "LineOnly"));
  727. }
  728. if (!settings.aotSettingsForTarget.EnableOptimisations)
  729. {
  730. options.Add(GetOption(OptionDisableOpt));
  731. }
  732. else
  733. {
  734. switch (settings.aotSettingsForTarget.OptimizeFor)
  735. {
  736. case OptimizeFor.Default:
  737. case OptimizeFor.Balanced:
  738. options.Add(GetOption(OptionOptLevel, 2));
  739. break;
  740. case OptimizeFor.Performance:
  741. options.Add(GetOption(OptionOptLevel, 3));
  742. break;
  743. case OptimizeFor.Size:
  744. options.Add(GetOption(OptionOptForSize));
  745. options.Add(GetOption(OptionOptLevel, 3));
  746. break;
  747. case OptimizeFor.FastCompilation:
  748. options.Add(GetOption(OptionOptLevel, 1));
  749. break;
  750. }
  751. }
  752. if (BurstLoader.IsDebugging)
  753. {
  754. options.Add(GetOption("debug-logging"));
  755. }
  756. // Add list of assemblies to ignore
  757. var disabledAssemblies = BurstAssemblyDisable.GetDisabledAssemblies(BurstAssemblyDisable.DisableType.Player, BurstPlatformAotSettings.ResolveTarget(buildTarget).ToString());
  758. foreach (var discard in disabledAssemblies)
  759. {
  760. options.Add(GetOption(OptionDiscardAssemblies, discard));
  761. }
  762. // Write current options to the response file
  763. var responseFile = Path.GetTempFileName();
  764. File.WriteAllLines(responseFile, options);
  765. if (BurstLoader.IsDebugging)
  766. {
  767. Debug.Log($"bcl.exe {OptionBurstcSwitch} @{responseFile}\n\nResponse File:\n" + string.Join("\n", options));
  768. }
  769. try
  770. {
  771. var burstcSwitch = OptionBurstcSwitch;
  772. if (!string.IsNullOrEmpty(
  773. Environment.GetEnvironmentVariable("UNITY_BURST_DISABLE_INCREMENTAL_PLAYER_BUILDS")))
  774. {
  775. burstcSwitch = "";
  776. }
  777. if (BurstLoader.BclConfiguration.IsExecutableNative)
  778. {
  779. BclRunner.RunNativeProgram(
  780. BurstLoader.BclConfiguration.ExecutablePath,
  781. $"{burstcSwitch} {BclRunner.EscapeForShell("@" + responseFile)}",
  782. new BclOutputErrorParser());
  783. }
  784. else
  785. {
  786. BclRunner.RunManagedProgram(
  787. BurstLoader.BclConfiguration.ExecutablePath,
  788. $"{burstcSwitch} {BclRunner.EscapeForShell("@" + responseFile)}",
  789. new BclOutputErrorParser(),
  790. combination.EnvironmentVariables);
  791. }
  792. // Additionally copy the pdb to the root of the player build so run in editor also locates the symbols
  793. var pdbPath = $"{Path.Combine(stagingOutputFolder, combination.LibraryName)}.pdb";
  794. if (File.Exists(pdbPath))
  795. {
  796. var dstPath = Path.Combine(OutputBaseFolder, $"{combination.LibraryName}.pdb");
  797. File.Copy(pdbPath, dstPath, overwrite: true);
  798. }
  799. }
  800. catch (BuildFailedException)
  801. {
  802. throw;
  803. }
  804. catch (Exception e)
  805. {
  806. throw new BuildFailedException(e);
  807. }
  808. }
  809. PostProcessCombinations(settings.targetPlatform, settings.combinations, settings.summary);
  810. var pdbsRemainInBuild = isDevelopmentBuild || settings.aotSettingsForTarget.EnableDebugInAllBuilds || settings.targetPlatform == TargetPlatform.UWP;
  811. // Finally move out any symbols/misc files from the final output
  812. if ((settings.summary.options & BuildOptions.InstallInBuildFolder) == 0)
  813. {
  814. return CollateMiscFiles(settings.combinations, burstMiscAlongsidePath, pdbsRemainInBuild);
  815. }
  816. return Array.Empty<string>();
  817. }
  818. private static void AddAssemblyFolder(string assemblyRef, string stagingFolder, BuildTarget buildTarget,
  819. List<string> assemblyFolders)
  820. {
  821. // Exclude folders with assemblies already compiled in the `folder`
  822. var assemblyName = Path.GetFileName(assemblyRef);
  823. if (assemblyName != null && File.Exists(Path.Combine(stagingFolder, assemblyName)))
  824. {
  825. return;
  826. }
  827. var directory = Path.GetDirectoryName(assemblyRef);
  828. if (directory != null)
  829. {
  830. var fullPath = Path.GetFullPath(directory);
  831. if (IsMonoReferenceAssemblyDirectory(fullPath) || IsDotNetStandardAssemblyDirectory(fullPath))
  832. {
  833. // Don't pass reference assemblies to burst because they contain methods without implementation
  834. // If burst accidentally resolves them, it will emit calls to burst_abort.
  835. fullPath = Path.Combine(EditorApplication.applicationContentsPath, "MonoBleedingEdge/lib/mono");
  836. #if UNITY_2021_2_OR_NEWER
  837. // In 2021.2 we got multiple mono distributions, per platform.
  838. fullPath = Path.Combine(fullPath, "unityaot-" + BuildTargetDiscovery.GetPlatformProfileSuffix(buildTarget));
  839. #else
  840. fullPath = Path.Combine(fullPath, "unityaot");
  841. #endif
  842. fullPath = Path.GetFullPath(fullPath); // GetFullPath will normalize path separators to OS native format
  843. if (!assemblyFolders.Contains(fullPath))
  844. assemblyFolders.Add(fullPath);
  845. fullPath = Path.Combine(fullPath, "Facades");
  846. if (!assemblyFolders.Contains(fullPath))
  847. assemblyFolders.Add(fullPath);
  848. }
  849. else if (!assemblyFolders.Contains(fullPath))
  850. {
  851. assemblyFolders.Add(fullPath);
  852. }
  853. }
  854. }
  855. private static void CreateFolderForMiscFiles(string finalFolder)
  856. {
  857. try
  858. {
  859. if (Directory.Exists(finalFolder)) Directory.Delete(finalFolder,true);
  860. }
  861. catch
  862. {
  863. }
  864. Directory.CreateDirectory(finalFolder);
  865. }
  866. private static IEnumerable<string> CollateMiscFiles(List<BurstOutputCombination> combinations, string finalFolder, bool retainPdbs)
  867. {
  868. foreach (var combination in combinations)
  869. {
  870. var inputPath = Path.GetFullPath(Path.Combine(OutputBaseFolder, combination.OutputPath));
  871. var outputPath = Path.Combine(finalFolder, combination.OutputPath);
  872. Directory.CreateDirectory(outputPath);
  873. if (!Directory.Exists(inputPath))
  874. continue;
  875. var files = Directory.GetFiles(inputPath);
  876. var directories = Directory.GetDirectories(inputPath);
  877. foreach (var fileName in files)
  878. {
  879. var lowerCase = fileName.ToLower();
  880. if ( (!retainPdbs && lowerCase.EndsWith(".pdb")) || lowerCase.EndsWith(".dsym") || lowerCase.EndsWith(".txt"))
  881. {
  882. // Move the file out of the staging area so its not included in the build
  883. File.Move(fileName, Path.Combine(outputPath, Path.GetFileName(fileName)));
  884. }
  885. else if (!combination.CollateDirectory)
  886. {
  887. yield return fileName;
  888. }
  889. }
  890. foreach (var fileName in directories)
  891. {
  892. var lowerCase = fileName.ToLower();
  893. if ( (!retainPdbs && lowerCase.EndsWith(".pdb")) || lowerCase.EndsWith(".dsym") || lowerCase.EndsWith(".txt"))
  894. {
  895. // Move the folder out of the staging area so its not included in the build
  896. Directory.Move(fileName, Path.Combine(outputPath, Path.GetFileName(fileName)));
  897. }
  898. else if (!combination.CollateDirectory)
  899. {
  900. yield return fileName;
  901. }
  902. }
  903. if (combination.CollateDirectory)
  904. yield return inputPath;
  905. }
  906. }
  907. private static bool AndroidHasX86(AndroidArchitecture architecture)
  908. {
  909. // Deal with rename that occured
  910. AndroidArchitecture val;
  911. if (AndroidArchitecture.TryParse("X86", out val))
  912. {
  913. return (architecture & val)!=0;
  914. }
  915. else if (AndroidArchitecture.TryParse("x86", out val))
  916. {
  917. return (architecture & val)!=0;
  918. }
  919. return false;
  920. }
  921. private static bool AndroidHasX86_64(AndroidArchitecture architecture)
  922. {
  923. // Deal with rename that occured
  924. AndroidArchitecture val;
  925. if (AndroidArchitecture.TryParse("X86_64", out val))
  926. {
  927. return (architecture & val)!=0;
  928. }
  929. else if (AndroidArchitecture.TryParse("x86_64", out val))
  930. {
  931. return (architecture & val)!=0;
  932. }
  933. return false;
  934. }
  935. private enum SimulatorPlatforms
  936. {
  937. iOS,
  938. tvOS
  939. }
  940. private static bool IsForSimulator(BuildTarget target)
  941. {
  942. switch (target)
  943. {
  944. case BuildTarget.iOS:
  945. return IsForSimulator(SimulatorPlatforms.iOS);
  946. case BuildTarget.tvOS:
  947. return IsForSimulator(SimulatorPlatforms.tvOS);
  948. default:
  949. return false;
  950. }
  951. }
  952. private static bool IsForSimulator(TargetPlatform targetPlatform)
  953. {
  954. switch (targetPlatform)
  955. {
  956. case TargetPlatform.iOS:
  957. return IsForSimulator(SimulatorPlatforms.iOS);
  958. case TargetPlatform.tvOS:
  959. return IsForSimulator(SimulatorPlatforms.tvOS);
  960. default:
  961. return false;
  962. }
  963. }
  964. private static bool IsForSimulator(SimulatorPlatforms simulatorPlatforms)
  965. {
  966. switch (simulatorPlatforms)
  967. {
  968. case SimulatorPlatforms.iOS:
  969. return UnityEditor.PlayerSettings.iOS.sdkVersion == iOSSdkVersion.SimulatorSDK;
  970. case SimulatorPlatforms.tvOS:
  971. return UnityEditor.PlayerSettings.tvOS.sdkVersion == tvOSSdkVersion.Simulator;
  972. }
  973. return false;
  974. }
  975. public static void WriteStaticLinkCppFile(string dir)
  976. {
  977. Directory.CreateDirectory(dir);
  978. string cppPath = Path.Combine(dir, "lib_burst_generated.cpp");
  979. // Additionally we need a small cpp file (weak symbols won't unfortunately override directly from the libs
  980. //presumably due to link order?
  981. File.WriteAllText(cppPath, @"
  982. extern ""C""
  983. {
  984. void Staticburst_initialize(void* );
  985. void* StaticBurstStaticMethodLookup(void* );
  986. int burst_enable_static_linkage = 1;
  987. void burst_initialize(void* i) { Staticburst_initialize(i); }
  988. void* BurstStaticMethodLookup(void* i) { return StaticBurstStaticMethodLookup(i); }
  989. }
  990. ");
  991. }
  992. /// <summary>
  993. /// Collect CPU combinations for the specified TargetPlatform and TargetCPU
  994. /// </summary>
  995. /// <param name="targetPlatform">The target platform (e.g Windows)</param>
  996. /// <param name="targetCpus">The target CPUs (e.g X64_SSE4)</param>
  997. /// <param name="report">Error reporting</param>
  998. /// <returns>The list of CPU combinations</returns>
  999. internal static List<BurstOutputCombination> CollectCombinations(TargetPlatform targetPlatform, TargetCpus targetCpus, BuildSummary summary)
  1000. {
  1001. var combinations = new List<BurstOutputCombination>();
  1002. if (targetPlatform == TargetPlatform.macOS)
  1003. {
  1004. // NOTE: OSX has a special folder for the plugin
  1005. // Declared in GetStagingAreaPluginsFolder
  1006. // PlatformDependent\OSXPlayer\Extensions\Managed\OSXDesktopStandalonePostProcessor.cs
  1007. var outputPath = Path.Combine(Path.GetFileName(summary.outputPath), "Contents", "Plugins");
  1008. // Based on : PlatformDependent/OSXPlayer/Extension/OSXStandaloneBuildWindowExtension.cs
  1009. var aotSettings = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.StandaloneOSX);
  1010. var buildTargetName = BuildPipeline.GetBuildTargetName(BuildTarget.StandaloneOSX);
  1011. var architecture = EditorUserBuildSettings.GetPlatformSettings(buildTargetName, "Architecture").ToLowerInvariant();
  1012. switch (architecture)
  1013. {
  1014. case "x64":
  1015. combinations.Add(new BurstOutputCombination(outputPath, aotSettings.GetDesktopCpu64Bit()));
  1016. break;
  1017. case "arm64":
  1018. // According to
  1019. // https://web.archive.org/web/20220504192056/https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/AArch64TargetParser.def#L240
  1020. // M1 is equivalent to Armv8.5-A, so it supports everything from HALFFP target
  1021. // (there's no direct confirmation on crypto because it's not mandatory)
  1022. combinations.Add(new BurstOutputCombination(outputPath, new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64_HALFFP)));
  1023. break;
  1024. default:
  1025. combinations.Add(new BurstOutputCombination(Path.Combine(outputPath, "x64"), aotSettings.GetDesktopCpu64Bit()));
  1026. combinations.Add(new BurstOutputCombination(Path.Combine(outputPath, "arm64"), new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64_HALFFP)));
  1027. break;
  1028. }
  1029. }
  1030. else if (targetPlatform == TargetPlatform.iOS || targetPlatform == TargetPlatform.tvOS || targetPlatform == TargetPlatform.visionOS || targetPlatform == TargetPlatform.visionSimulator)
  1031. {
  1032. if (IsForSimulator(targetPlatform))
  1033. {
  1034. Debug.LogWarning("Burst Does not currently support the simulator, burst is disabled for this build.");
  1035. }
  1036. else if (Application.platform != RuntimePlatform.OSXEditor)
  1037. {
  1038. Debug.LogWarning("Burst Cross Compilation to iOS/tvOS for standalone player, is only supported on OSX Editor at this time, burst is disabled for this build.");
  1039. }
  1040. else
  1041. {
  1042. // Looks like a way to detect iOS CPU capabilities in runtime (like getauxval()) is sysctlbyname()
  1043. // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
  1044. // TODO: add support for it when needed, for now using the lowest common denominator
  1045. // https://web.archive.org/web/20220504192056/https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/AArch64TargetParser.def#L240
  1046. // This LLVM code implies A11 is the first Armv8.2-A CPU
  1047. // However, it doesn't support dotprod, so we can't consider it equivalent to our HALFFP variant
  1048. // A13 (equivalent to Armv8.4-A) and M1 seem to be the first CPUs we can claim HALFFP compatible
  1049. // Since we need to support older CPUs, have to use the "basic" Armv8A here
  1050. combinations.Add(new BurstOutputCombination("StaticLibraries", new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64)));
  1051. }
  1052. }
  1053. else if (targetPlatform == TargetPlatform.Android)
  1054. {
  1055. // TODO: would be better to query AndroidNdkRoot (but thats not exposed from unity)
  1056. string ndkRoot = null;
  1057. var targetAPILevel = PlayerSettings.Android.GetMinTargetAPILevel();
  1058. var environment = new Dictionary<string, string>();
  1059. #if UNITY_ANDROID
  1060. ndkRoot = UnityEditor.Android.AndroidExternalToolsSettings.ndkRootPath;
  1061. #else
  1062. // 2019.1 now has an embedded ndk
  1063. if (EditorPrefs.HasKey("NdkUseEmbedded"))
  1064. {
  1065. if (EditorPrefs.GetBool("NdkUseEmbedded"))
  1066. {
  1067. ndkRoot = Path.Combine(BuildPipeline.GetPlaybackEngineDirectory(BuildTarget.Android, BuildOptions.None), "NDK");
  1068. }
  1069. else
  1070. {
  1071. ndkRoot = EditorPrefs.GetString("AndroidNdkRootR16b");
  1072. }
  1073. }
  1074. #endif
  1075. // If we still don't have a valid root, try the old key
  1076. if (string.IsNullOrEmpty(ndkRoot))
  1077. {
  1078. ndkRoot = EditorPrefs.GetString("AndroidNdkRoot");
  1079. }
  1080. // Verify the directory at least exists, if not we fall back to ANDROID_NDK_ROOT current setting
  1081. if (!string.IsNullOrEmpty(ndkRoot) && !Directory.Exists(ndkRoot))
  1082. {
  1083. ndkRoot = null;
  1084. }
  1085. // Always set the ANDROID_NDK_ROOT (if we got a valid result from above), so BCL knows where to find the Android toolchain and its the one the user expects
  1086. if (!string.IsNullOrEmpty(ndkRoot))
  1087. {
  1088. environment["ANDROID_NDK_ROOT"] = ndkRoot;
  1089. }
  1090. environment["BURST_ANDROID_MIN_API_LEVEL"] = $"{targetAPILevel}";
  1091. // Setting tempburstlibs/ as the interim target directory
  1092. // Don't target libs/ directly because incremental build pipeline doesn't expect the so's at that path
  1093. // Rather, so's are copied to the actual location in the gradle project in BurstAndroidGradlePostprocessor
  1094. var androidTargetArch = PlayerSettings.Android.targetArchitectures;
  1095. if ((androidTargetArch & AndroidArchitecture.ARMv7) != 0)
  1096. {
  1097. combinations.Add(new BurstOutputCombination("tempburstlibs/armeabi-v7a", new TargetCpus(BurstTargetCpu.ARMV7A_NEON32), collateDirectory: true, environment: environment));
  1098. }
  1099. if ((androidTargetArch & AndroidArchitecture.ARM64) != 0)
  1100. {
  1101. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(summary.platform);
  1102. combinations.Add(new BurstOutputCombination("tempburstlibs/arm64-v8a", aotSettingsForTarget.GetAndroidCpuArm64(), collateDirectory: true, environment: environment));
  1103. }
  1104. #if UNITY_2019_4_OR_NEWER
  1105. if (AndroidHasX86(androidTargetArch))
  1106. {
  1107. combinations.Add(new BurstOutputCombination("tempburstlibs/x86", new TargetCpus(BurstTargetCpu.X86_SSE4), collateDirectory: true, environment: environment));
  1108. }
  1109. if (AndroidHasX86_64(androidTargetArch))
  1110. {
  1111. combinations.Add(new BurstOutputCombination("tempburstlibs/x86_64", new TargetCpus(BurstTargetCpu.X64_SSE4), collateDirectory: true, environment: environment));
  1112. }
  1113. #endif
  1114. }
  1115. else if (targetPlatform == TargetPlatform.UWP)
  1116. {
  1117. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(summary.platform);
  1118. if (EditorUserBuildSettings.wsaUWPBuildType == WSAUWPBuildType.ExecutableOnly)
  1119. {
  1120. combinations.Add(new BurstOutputCombination($"Plugins/{GetUWPTargetArchitecture()}", targetCpus, collateDirectory: true));
  1121. }
  1122. else
  1123. {
  1124. combinations.Add(new BurstOutputCombination("Plugins/x64", aotSettingsForTarget.GetDesktopCpu64Bit(), collateDirectory: true));
  1125. combinations.Add(new BurstOutputCombination("Plugins/x86", aotSettingsForTarget.GetDesktopCpu32Bit(), collateDirectory: true));
  1126. combinations.Add(new BurstOutputCombination("Plugins/ARM", new TargetCpus(BurstTargetCpu.THUMB2_NEON32), collateDirectory: true));
  1127. combinations.Add(new BurstOutputCombination("Plugins/ARM64", new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64), collateDirectory: true));
  1128. }
  1129. }
  1130. #if !UNITY_2022_2_OR_NEWER
  1131. else if (targetPlatform == TargetPlatform.Lumin)
  1132. {
  1133. // Set the LUMINSDK_UNITY so bcl.exe will be able to find the SDK
  1134. if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("LUMINSDK_UNITY")))
  1135. {
  1136. var sdkRoot = EditorPrefs.GetString("LuminSDKRoot");
  1137. if (!string.IsNullOrEmpty(sdkRoot))
  1138. {
  1139. Environment.SetEnvironmentVariable("LUMINSDK_UNITY", sdkRoot);
  1140. }
  1141. }
  1142. combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpus));
  1143. }
  1144. #endif
  1145. else if (targetPlatform == TargetPlatform.Switch)
  1146. {
  1147. combinations.Add(new BurstOutputCombination("NativePlugins/", targetCpus));
  1148. }
  1149. else
  1150. {
  1151. if (targetPlatform == TargetPlatform.Windows)
  1152. {
  1153. // This is what is expected by PlatformDependent\Win\Plugins.cpp
  1154. if (targetCpus.IsX86())
  1155. {
  1156. combinations.Add(new BurstOutputCombination("Data/Plugins/x86", targetCpus, collateDirectory: true));
  1157. }
  1158. else
  1159. {
  1160. var windowsArchitecture = GetWindows64BitTargetArchitecture();
  1161. if (string.Equals(windowsArchitecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1162. {
  1163. combinations.Add(new BurstOutputCombination("Data/Plugins/ARM64", targetCpus, collateDirectory: true, workaroundBrokenDebug: true));
  1164. }
  1165. else
  1166. {
  1167. combinations.Add(new BurstOutputCombination("Data/Plugins/x86_64", targetCpus, collateDirectory: true));
  1168. }
  1169. }
  1170. }
  1171. else
  1172. {
  1173. // Safeguard
  1174. combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpus));
  1175. }
  1176. }
  1177. return combinations;
  1178. }
  1179. private static void PostProcessCombinations(TargetPlatform targetPlatform, List<BurstOutputCombination> combinations, BuildSummary summary)
  1180. {
  1181. if (targetPlatform == TargetPlatform.macOS && combinations.Count > 1)
  1182. {
  1183. // Figure out which files we need to lipo
  1184. string outputSymbolsDir = null;
  1185. var outputDir = Path.Combine(OutputBaseFolder, Path.GetFileName(summary.outputPath), "Contents", "Plugins");
  1186. var sliceCount = combinations.Count;
  1187. var binarySlices = new string[sliceCount];
  1188. var debugSymbolSlices = new string[sliceCount];
  1189. for (int i = 0; i < sliceCount; i++)
  1190. {
  1191. var slice = combinations[i];
  1192. var binaryFileName = slice.LibraryName + ".bundle";
  1193. var binaryPath = Path.Combine(OutputBaseFolder, slice.OutputPath, binaryFileName);
  1194. binarySlices[i] = binaryPath;
  1195. // Only attempt to lipo symbols if they actually exist
  1196. var dsymPath = binaryPath + ".dsym";
  1197. var debugSymbolsPath = Path.Combine(dsymPath, "Contents", "Resources", "DWARF", binaryFileName);
  1198. if (File.Exists(debugSymbolsPath))
  1199. {
  1200. if (string.IsNullOrWhiteSpace(outputSymbolsDir))
  1201. {
  1202. // Copy over the symbols from the first combination for metadata files which we aren't merging, like Info.plist
  1203. var outputDsymPath = Path.Combine(outputDir, binaryFileName + ".dsym");
  1204. CopyDirectory(dsymPath, outputDsymPath, true);
  1205. outputSymbolsDir = Path.Combine(outputDsymPath, "Contents", "Resources", "DWARF");
  1206. }
  1207. debugSymbolSlices[i] = debugSymbolsPath;
  1208. }
  1209. }
  1210. // lipo combinations together
  1211. var outBinaryFileName = combinations[0].LibraryName + ".bundle";
  1212. RunLipo(binarySlices, Path.Combine(outputDir, outBinaryFileName));
  1213. if (!string.IsNullOrWhiteSpace(outputSymbolsDir))
  1214. RunLipo(debugSymbolSlices, Path.Combine(outputSymbolsDir, outBinaryFileName));
  1215. // Remove single-slice binary so they don't end up in the build
  1216. for (int i = 0; i < sliceCount; i++)
  1217. Directory.Delete(Path.GetDirectoryName(binarySlices[i]), true);
  1218. // Since we have combined the files, we need to adjust combinations for the next step
  1219. var outFolder = Path.GetDirectoryName(combinations[0].OutputPath); // remove platform folder
  1220. combinations.Clear();
  1221. combinations.Add(new BurstOutputCombination(outFolder, new TargetCpus()));
  1222. }
  1223. }
  1224. private static void RunLipo(string[] inputFiles, string outputFile)
  1225. {
  1226. var outputDir = Path.GetDirectoryName(outputFile);
  1227. Directory.CreateDirectory(outputDir);
  1228. var cmdLine = new StringBuilder();
  1229. foreach (var input in inputFiles)
  1230. {
  1231. if (string.IsNullOrEmpty(input))
  1232. continue;
  1233. cmdLine.Append(BclRunner.EscapeForShell(input));
  1234. cmdLine.Append(' ');
  1235. }
  1236. cmdLine.Append("-create -output ");
  1237. cmdLine.Append(BclRunner.EscapeForShell(outputFile));
  1238. string lipoPath;
  1239. var currentEditorPlatform = Application.platform;
  1240. switch (currentEditorPlatform)
  1241. {
  1242. case RuntimePlatform.LinuxEditor:
  1243. lipoPath = Path.Combine(BurstLoader.BclConfiguration.FolderPath, "hostlin", "llvm-lipo");
  1244. break;
  1245. case RuntimePlatform.OSXEditor:
  1246. lipoPath = Path.Combine(BurstLoader.BclConfiguration.FolderPath, "hostmac", "llvm-lipo");
  1247. break;
  1248. case RuntimePlatform.WindowsEditor:
  1249. lipoPath = Path.Combine(BurstLoader.BclConfiguration.FolderPath, "hostwin", "llvm-lipo.exe");
  1250. break;
  1251. default:
  1252. throw new NotSupportedException("Unknown Unity editor platform: " + currentEditorPlatform);
  1253. }
  1254. BclRunner.RunNativeProgram(lipoPath, cmdLine.ToString(), null);
  1255. }
  1256. internal static Assembly[] GetPlayerAssemblies(BuildReport report)
  1257. {
  1258. // We need to build the list of root assemblies based from the "PlayerScriptAssemblies" folder.
  1259. // This is so we compile the versions of the library built for the individual platforms, not the editor version.
  1260. var oldOutputDir = EditorCompilationInterface.GetCompileScriptsOutputDirectory();
  1261. try
  1262. {
  1263. EditorCompilationInterface.SetCompileScriptsOutputDirectory(LibraryPlayerScriptAssemblies);
  1264. var shouldIncludeTestAssemblies = report.summary.options.HasFlag(BuildOptions.IncludeTestAssemblies);
  1265. #if UNITY_2021_1_OR_NEWER
  1266. // Workaround that with 'Server Build' ticked in the build options, since there is no 'AssembliesType.Server'
  1267. // enum, we need to manually add the BuildingForHeadlessPlayer compilation option.
  1268. #if UNITY_2022_1_OR_NEWER
  1269. var isHeadless = report.summary.subtarget == (int)StandaloneBuildSubtarget.Server;
  1270. #elif UNITY_2021_2_OR_NEWER
  1271. // A really really really gross hack - thanks Cristian Mazo! Querying the BuildOptions.EnableHeadlessMode is
  1272. // obselete, but accessing its integer value is not... Note: this is just the temporary workaround to unblock
  1273. // us (as of 1st June 2021, I say this with **much hope** that it is indeed temporary!).
  1274. var isHeadless = report.summary.options.HasFlag((BuildOptions)16384);
  1275. #else
  1276. var isHeadless = report.summary.options.HasFlag(BuildOptions.EnableHeadlessMode);
  1277. #endif
  1278. if (isHeadless)
  1279. {
  1280. var compilationOptions = EditorCompilationInterface.GetAdditionalEditorScriptCompilationOptions();
  1281. compilationOptions |= EditorScriptCompilationOptions.BuildingForHeadlessPlayer;
  1282. if (shouldIncludeTestAssemblies)
  1283. {
  1284. compilationOptions |= EditorScriptCompilationOptions.BuildingIncludingTestAssemblies;
  1285. }
  1286. return CompilationPipeline.ToAssemblies(CompilationPipeline.GetScriptAssemblies(EditorCompilationInterface.Instance, compilationOptions));
  1287. }
  1288. else
  1289. {
  1290. return CompilationPipeline.GetAssemblies(shouldIncludeTestAssemblies ? AssembliesType.Player : AssembliesType.PlayerWithoutTestAssemblies);
  1291. }
  1292. #else
  1293. // Workaround that with 'Server Build' ticked in the build options, since there is no 'AssembliesType.Server'
  1294. // enum, we need to manually add the 'UNITY_SERVER' define to the player assembly search list.
  1295. if (report.summary.options.HasFlag(BuildOptions.EnableHeadlessMode))
  1296. {
  1297. var compilationOptions = EditorCompilationInterface.GetAdditionalEditorScriptCompilationOptions();
  1298. if (shouldIncludeTestAssemblies)
  1299. {
  1300. compilationOptions |= EditorScriptCompilationOptions.BuildingIncludingTestAssemblies;
  1301. }
  1302. return CompilationPipeline.GetPlayerAssemblies(EditorCompilationInterface.Instance, compilationOptions, new string[] { "UNITY_SERVER" });
  1303. }
  1304. else
  1305. {
  1306. return CompilationPipeline.GetAssemblies(shouldIncludeTestAssemblies ? AssembliesType.Player : AssembliesType.PlayerWithoutTestAssemblies);
  1307. }
  1308. #endif
  1309. }
  1310. finally
  1311. {
  1312. EditorCompilationInterface.SetCompileScriptsOutputDirectory(oldOutputDir); // restore output directory back to original value
  1313. }
  1314. }
  1315. private static bool IsMonoReferenceAssemblyDirectory(string path)
  1316. {
  1317. var editorDir = Path.GetFullPath(EditorApplication.applicationContentsPath);
  1318. return path.IndexOf(editorDir, StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("MonoBleedingEdge", StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("-api", StringComparison.OrdinalIgnoreCase) != -1;
  1319. }
  1320. private static bool IsDotNetStandardAssemblyDirectory(string path)
  1321. {
  1322. var editorDir = Path.GetFullPath(EditorApplication.applicationContentsPath);
  1323. return path.IndexOf(editorDir, StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("netstandard", StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("shims", StringComparison.OrdinalIgnoreCase) != -1;
  1324. }
  1325. internal static TargetPlatform GetTargetPlatformAndDefaultCpu(BuildTarget target, out TargetCpus targetCpu, BurstPlatformAotSettings aotSettingsForTarget)
  1326. {
  1327. var platform = TryGetTargetPlatform(target, out targetCpu, aotSettingsForTarget);
  1328. if (!platform.HasValue)
  1329. {
  1330. throw new NotSupportedException("The target platform " + target + " is not supported by the burst compiler");
  1331. }
  1332. return platform.Value;
  1333. }
  1334. internal static bool IsSupportedPlatform(BuildTarget target, BurstPlatformAotSettings aotSettingsForTarget)
  1335. {
  1336. return TryGetTargetPlatform(target, out var _, aotSettingsForTarget).HasValue;
  1337. }
  1338. private static TargetPlatform? TryGetTargetPlatform(BuildTarget target, out TargetCpus targetCpus, BurstPlatformAotSettings aotSettingsForTarget)
  1339. {
  1340. switch (target)
  1341. {
  1342. case BuildTarget.StandaloneWindows:
  1343. targetCpus = aotSettingsForTarget.GetDesktopCpu32Bit();
  1344. return TargetPlatform.Windows;
  1345. case BuildTarget.StandaloneWindows64:
  1346. var windowsArchitecture = GetWindows64BitTargetArchitecture();
  1347. if (string.Equals(windowsArchitecture, "x64", StringComparison.OrdinalIgnoreCase))
  1348. {
  1349. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  1350. }
  1351. else if (string.Equals(windowsArchitecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1352. {
  1353. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1354. }
  1355. else
  1356. {
  1357. throw new InvalidOperationException("Unknown Windows 64 Bit CPU architecture: " + windowsArchitecture);
  1358. }
  1359. return TargetPlatform.Windows;
  1360. case BuildTarget.StandaloneOSX:
  1361. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  1362. return TargetPlatform.macOS;
  1363. case BuildTarget.StandaloneLinux64:
  1364. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  1365. return TargetPlatform.Linux;
  1366. case BuildTarget.WSAPlayer:
  1367. {
  1368. var uwpArchitecture = GetUWPTargetArchitecture();
  1369. if (string.Equals(uwpArchitecture, "x64", StringComparison.OrdinalIgnoreCase))
  1370. {
  1371. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  1372. }
  1373. else if (string.Equals(uwpArchitecture, "x86", StringComparison.OrdinalIgnoreCase))
  1374. {
  1375. targetCpus = aotSettingsForTarget.GetDesktopCpu32Bit();
  1376. }
  1377. else if (string.Equals(uwpArchitecture, "ARM", StringComparison.OrdinalIgnoreCase))
  1378. {
  1379. targetCpus = new TargetCpus(BurstTargetCpu.THUMB2_NEON32);
  1380. }
  1381. else if (string.Equals(uwpArchitecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1382. {
  1383. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1384. }
  1385. else
  1386. {
  1387. throw new InvalidOperationException("Unknown UWP CPU architecture: " + uwpArchitecture);
  1388. }
  1389. return TargetPlatform.UWP;
  1390. }
  1391. case BuildTarget.GameCoreXboxOne:
  1392. targetCpus = new TargetCpus(BurstTargetCpu.AVX);
  1393. return TargetPlatform.GameCoreXboxOne;
  1394. case BuildTarget.GameCoreXboxSeries:
  1395. targetCpus = new TargetCpus(BurstTargetCpu.AVX2);
  1396. return TargetPlatform.GameCoreXboxSeries;
  1397. case BuildTarget.PS4:
  1398. targetCpus = new TargetCpus(BurstTargetCpu.X64_SSE4);
  1399. return TargetPlatform.PS4;
  1400. case BuildTarget.Android:
  1401. targetCpus = new TargetCpus(BurstTargetCpu.ARMV7A_NEON32);
  1402. return TargetPlatform.Android;
  1403. case BuildTarget.iOS:
  1404. targetCpus = new TargetCpus(BurstTargetCpu.ARMV7A_NEON32);
  1405. return TargetPlatform.iOS;
  1406. case BuildTarget.tvOS:
  1407. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1408. return TargetPlatform.tvOS;
  1409. #if !UNITY_2022_2_OR_NEWER
  1410. case BuildTarget.Lumin:
  1411. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1412. return TargetPlatform.Lumin;
  1413. #endif
  1414. case BuildTarget.Switch:
  1415. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1416. return TargetPlatform.Switch;
  1417. case BuildTarget.PS5:
  1418. targetCpus = new TargetCpus(BurstTargetCpu.AVX2);
  1419. return TargetPlatform.PS5;
  1420. }
  1421. #if UNITY_2023_3_OR_NEWER
  1422. const int buildTargetVisionOS = (int)BuildTarget.VisionOS;
  1423. #else
  1424. const int buildTargetVisionOS = 47;
  1425. #endif
  1426. if ((int)target == buildTargetVisionOS)
  1427. {
  1428. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1429. // An SDK Version of "0" indicates that the visionOS simulator SDK is not being
  1430. // used, so assume this is the device SDK instead.
  1431. return GetVisionSdkVersion() == 0 ? TargetPlatform.visionOS : TargetPlatform.visionSimulator;
  1432. }
  1433. #if UNITY_2022_1_OR_NEWER
  1434. const int qnxTarget = (int)BuildTarget.QNX;
  1435. #else
  1436. const int qnxTarget = 46;
  1437. #endif
  1438. if (qnxTarget == (int)target)
  1439. {
  1440. // QNX is supported on 2019.4 (shadow branch), 2020.3 (shadow branch) and 2022.1+ (official).
  1441. var qnxArchitecture = GetQNXTargetArchitecture();
  1442. if ("Arm64" == qnxArchitecture)
  1443. {
  1444. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1445. }
  1446. else if ("X64" == qnxArchitecture)
  1447. {
  1448. targetCpus = new TargetCpus(BurstTargetCpu.X64_SSE4);
  1449. }
  1450. else if ("X86" == qnxArchitecture)
  1451. {
  1452. targetCpus = new TargetCpus(BurstTargetCpu.X86_SSE4);
  1453. }
  1454. else if ("Arm32" == qnxArchitecture)
  1455. {
  1456. targetCpus = new TargetCpus(BurstTargetCpu.ARMV7A_NEON32);
  1457. }
  1458. else
  1459. {
  1460. throw new InvalidOperationException("Unknown QNX CPU architecture: " + qnxArchitecture);
  1461. }
  1462. return TargetPlatform.QNX;
  1463. }
  1464. #if UNITY_2021_2_OR_NEWER
  1465. const int embeddedLinuxTarget = (int)BuildTarget.EmbeddedLinux;
  1466. #else
  1467. const int embeddedLinuxTarget = 45;
  1468. #endif
  1469. if (embeddedLinuxTarget == (int)target)
  1470. {
  1471. //EmbeddedLinux is supported on 2019.4 (shadow branch), 2020.3 (shadow branch) and 2021.2+ (official).
  1472. var embeddedLinuxArchitecture = GetEmbeddedLinuxTargetArchitecture();
  1473. if ("Arm64" == embeddedLinuxArchitecture)
  1474. {
  1475. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1476. }
  1477. else if ("X64" == embeddedLinuxArchitecture)
  1478. {
  1479. targetCpus = new TargetCpus(BurstTargetCpu.X64_SSE2); //lowest supported for now
  1480. }
  1481. else if (("X86" == embeddedLinuxArchitecture) || ("Arm32" == embeddedLinuxArchitecture))
  1482. {
  1483. //32bit platforms cannot be support with the current SDK/Toolchain combination.
  1484. //i686-embedded-linux-gnu/8.3.0\libgcc.a(_moddi3.o + _divdi3.o): contains a compressed section, but zlib is not available
  1485. //_moddi3.o + _divdi3.o are required by LLVM for 64bit operations on 32bit platforms.
  1486. throw new InvalidOperationException($"No EmbeddedLinux Burst Support on {embeddedLinuxArchitecture} architecture.");
  1487. }
  1488. else
  1489. {
  1490. throw new InvalidOperationException("Unknown EmbeddedLinux CPU architecture: " + embeddedLinuxArchitecture);
  1491. }
  1492. return TargetPlatform.EmbeddedLinux;
  1493. }
  1494. targetCpus = new TargetCpus(BurstTargetCpu.Auto);
  1495. return null;
  1496. }
  1497. private static string GetWindows64BitTargetArchitecture()
  1498. {
  1499. var buildTargetName = BuildPipeline.GetBuildTargetName(BuildTarget.StandaloneWindows64);
  1500. var architecture = EditorUserBuildSettings.GetPlatformSettings(buildTargetName, "Architecture").ToLowerInvariant();
  1501. if (string.Equals(architecture, "x64", StringComparison.OrdinalIgnoreCase) ||
  1502. string.Equals(architecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1503. {
  1504. return architecture;
  1505. }
  1506. // Default to x64 if editor user build setting is garbage
  1507. return "x64";
  1508. }
  1509. private static string GetUWPTargetArchitecture()
  1510. {
  1511. var architecture = EditorUserBuildSettings.wsaArchitecture;
  1512. if (string.Equals(architecture, "x64", StringComparison.OrdinalIgnoreCase) ||
  1513. string.Equals(architecture, "x86", StringComparison.OrdinalIgnoreCase) ||
  1514. string.Equals(architecture, "ARM", StringComparison.OrdinalIgnoreCase) ||
  1515. string.Equals(architecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1516. {
  1517. return architecture;
  1518. }
  1519. // Default to x64 if editor user build setting is garbage
  1520. return "x64";
  1521. }
  1522. private static string GetEmbeddedLinuxTargetArchitecture()
  1523. {
  1524. var flags = System.Reflection.BindingFlags.Public |
  1525. System.Reflection.BindingFlags.Static |
  1526. System.Reflection.BindingFlags.FlattenHierarchy;
  1527. var property = typeof(EditorUserBuildSettings).GetProperty("selectedEmbeddedLinuxArchitecture", flags);
  1528. if (null == property)
  1529. {
  1530. return "NOT_FOUND";
  1531. }
  1532. var value = (int)property.GetValue(null, null);
  1533. switch (value)
  1534. {
  1535. case /*UnityEditor.EmbeddedLinuxArchitecture.Arm64*/ 0: return "Arm64";
  1536. case /*UnityEditor.EmbeddedLinuxArchitecture.Arm32*/ 1: return "Arm32";
  1537. case /*UnityEditor.EmbeddedLinuxArchitecture.X64*/ 2: return "X64";
  1538. case /*UnityEditor.EmbeddedLinuxArchitecture.X86*/ 3: return "X86";
  1539. default: return $"UNKNOWN_{value}";
  1540. }
  1541. }
  1542. private static string GetQNXTargetArchitecture()
  1543. {
  1544. var flags = System.Reflection.BindingFlags.Public |
  1545. System.Reflection.BindingFlags.Static |
  1546. System.Reflection.BindingFlags.FlattenHierarchy;
  1547. var property = typeof(EditorUserBuildSettings).GetProperty("selectedQnxArchitecture", flags);
  1548. if (null == property)
  1549. {
  1550. return "NOT_FOUND";
  1551. }
  1552. var value = (int)property.GetValue(null, null);
  1553. switch (value)
  1554. {
  1555. case /*UnityEditor.QNXArchitecture.Arm64*/ 0: return "Arm64";
  1556. case /*UnityEditor.QNXArchitecture.Arm32*/ 1: return "Arm32";
  1557. case /*UnityEditor.QNXArchitecture.X64*/ 2: return "X64";
  1558. case /*UnityEditor.QNXArchitecture.X86*/ 3: return "X86";
  1559. default: return $"UNKNOWN_{value}";
  1560. }
  1561. }
  1562. private static int GetVisionSdkVersion()
  1563. {
  1564. var playerSettings = typeof(PlayerSettings);
  1565. var visionOs = playerSettings.GetNestedType("VisionOS");
  1566. if (visionOs == null) throw new Exception("Editor does not appear to support visionOS");
  1567. var property = visionOs.GetProperty("sdkVersion", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
  1568. if (property == null) throw new Exception("Property `sdkVersion` not found");
  1569. var sdkVersion = property.GetValue(null, null);
  1570. return (int)sdkVersion;
  1571. }
  1572. /// <summary>
  1573. /// Defines an output path (for the generated code) and the target CPU
  1574. /// </summary>
  1575. internal struct BurstOutputCombination
  1576. {
  1577. public readonly TargetCpus TargetCpus;
  1578. public readonly string OutputPath;
  1579. public readonly string LibraryName;
  1580. public readonly bool CollateDirectory;
  1581. public readonly bool WorkaroundFullDebugInfo;
  1582. public readonly Dictionary<string, string> EnvironmentVariables;
  1583. public BurstOutputCombination(string outputPath, TargetCpus targetCpus, string libraryName = DefaultLibraryName, bool collateDirectory = false, bool workaroundBrokenDebug=false, Dictionary<string, string> environment = null)
  1584. {
  1585. TargetCpus = targetCpus.Clone();
  1586. OutputPath = outputPath;
  1587. LibraryName = libraryName;
  1588. CollateDirectory = collateDirectory;
  1589. WorkaroundFullDebugInfo = workaroundBrokenDebug;
  1590. EnvironmentVariables = environment;
  1591. }
  1592. public void HashInto(ref Hash128 hc)
  1593. {
  1594. hc.Append(TargetCpus.Cpus.Count);
  1595. foreach (var cpu in TargetCpus.Cpus)
  1596. {
  1597. hc.Append((int)cpu);
  1598. }
  1599. hc.Append(OutputPath);
  1600. hc.Append(LibraryName);
  1601. hc.Append(CollateDirectory ? 1 : 0);
  1602. hc.Append(WorkaroundFullDebugInfo ? 1 : 0);
  1603. }
  1604. public override string ToString()
  1605. {
  1606. return $"{nameof(TargetCpus)}: {TargetCpus}, {nameof(OutputPath)}: {OutputPath}, {nameof(LibraryName)}: {LibraryName}";
  1607. }
  1608. }
  1609. private class BclRunner
  1610. {
  1611. private static readonly Regex MatchVersion = new Regex(@"com.unity.burst@(\d+.*?)[\\/]");
  1612. public static void RunManagedProgram(string exe, string args, CompilerOutputParserBase parser, IReadOnlyDictionary<string, string> environment = null)
  1613. {
  1614. RunManagedProgram(exe, args, Application.dataPath + "/..", parser, environment);
  1615. }
  1616. private static void RunManagedProgram(
  1617. string exe,
  1618. string args,
  1619. string workingDirectory,
  1620. CompilerOutputParserBase parser,
  1621. IReadOnlyDictionary<string, string> environment = null)
  1622. {
  1623. Program p;
  1624. bool unreliableExitCode = false;
  1625. if (Application.platform == RuntimePlatform.WindowsEditor)
  1626. {
  1627. if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
  1628. {
  1629. // For Windows on Arm 64, we need to be explicit and specify the architecture we want our
  1630. //managed process to run with - unfortunately, doing this means the exit code of the process
  1631. //no longer reflects if compilation was successful, so unreliableExitCode works around this
  1632. args = "/c start /machine arm64 /b /WAIT " + exe + " " + args;
  1633. exe = "cmd";
  1634. unreliableExitCode = true;
  1635. }
  1636. ProcessStartInfo si = new ProcessStartInfo()
  1637. {
  1638. Arguments = args,
  1639. CreateNoWindow = true,
  1640. FileName = exe,
  1641. };
  1642. SetEnvironmentVariables(si);
  1643. p = new Program(si);
  1644. }
  1645. else
  1646. {
  1647. p = (Program) new ManagedProgram(MonoInstallationFinder.GetMonoInstallation("MonoBleedingEdge"), (string) null, exe, args, false, SetEnvironmentVariables);
  1648. }
  1649. RunProgram(p, exe, args, workingDirectory, parser, unreliableExitCode);
  1650. return;
  1651. void SetEnvironmentVariables(ProcessStartInfo psi)
  1652. {
  1653. if (environment == null) return;
  1654. foreach (var kvp in environment)
  1655. {
  1656. psi.Environment[kvp.Key] = kvp.Value;
  1657. }
  1658. }
  1659. }
  1660. public static void RunNativeProgram(string exe, string args, CompilerOutputParserBase parser)
  1661. {
  1662. RunNativeProgram(exe, args, Application.dataPath + "/..", parser);
  1663. }
  1664. private static void RunNativeProgram(string exePath, string arguments, string workingDirectory, CompilerOutputParserBase parser)
  1665. {
  1666. // On non Windows platform, make sure that the command is executable
  1667. // This is a workaround - occasionally the execute bits are lost from our package
  1668. if (Application.platform != RuntimePlatform.WindowsEditor && Path.IsPathRooted(exePath))
  1669. {
  1670. var escapedExePath = EscapeForShell(exePath, singleQuoteWrapped: true);
  1671. var shArgs = $"-c '[ ! -x {escapedExePath} ] && chmod 755 {escapedExePath}'";
  1672. var p = new Program(new ProcessStartInfo("sh", shArgs) { CreateNoWindow = true});
  1673. p.GetProcessStartInfo().WorkingDirectory = workingDirectory;
  1674. p.Start();
  1675. p.WaitForExit();
  1676. }
  1677. var startInfo = new ProcessStartInfo(exePath, arguments);
  1678. startInfo.CreateNoWindow = true;
  1679. RunProgram(new Program(startInfo), exePath, arguments, workingDirectory, parser, false);
  1680. }
  1681. public static string EscapeForShell(string s, bool singleQuoteWrapped = false)
  1682. {
  1683. // On Windows it's enough to enclose the path in double quotes (double quotes are not allowed in paths)
  1684. if (Application.platform == RuntimePlatform.WindowsEditor) return $"\"{s}\"";
  1685. // On non-windows platforms we enclose in single-quotes and escape any existing single quotes with: '\'':
  1686. // John's Folder => 'John'\''s Folder'
  1687. var sb = new StringBuilder();
  1688. var escaped = s.Replace("'", "'\\''");
  1689. sb.Append('\'');
  1690. sb.Append(escaped);
  1691. sb.Append('\'');
  1692. // If the outer-context is already wrapped in single-quotes, we need to double escape things:
  1693. // John's Folder => 'John'\''s Folder'
  1694. // => '\''John'\''\'\'''\''s Folder'\''
  1695. if (singleQuoteWrapped)
  1696. {
  1697. // Pain
  1698. return sb.ToString().Replace("'", "'\\''");
  1699. }
  1700. return sb.ToString();
  1701. }
  1702. public static void RunProgram(
  1703. Program p,
  1704. string exe,
  1705. string args,
  1706. string workingDirectory,
  1707. CompilerOutputParserBase parser,
  1708. bool unreliableExitCode)
  1709. {
  1710. Stopwatch stopwatch = new Stopwatch();
  1711. stopwatch.Start();
  1712. using (p)
  1713. {
  1714. p.GetProcessStartInfo().WorkingDirectory = workingDirectory;
  1715. p.Start();
  1716. p.WaitForExit();
  1717. stopwatch.Stop();
  1718. Console.WriteLine("{0} exited after {1} ms.", (object)exe, (object)stopwatch.ElapsedMilliseconds);
  1719. IEnumerable<UnityEditor.Scripting.Compilers.CompilerMessage> compilerMessages = null;
  1720. string[] errorOutput = p.GetErrorOutput();
  1721. string[] standardOutput = p.GetStandardOutput();
  1722. if (parser != null)
  1723. {
  1724. compilerMessages = parser.Parse(errorOutput, standardOutput, true, "n/a (burst)");
  1725. }
  1726. var errorMessageBuilder = new StringBuilder();
  1727. if (compilerMessages != null)
  1728. {
  1729. foreach (UnityEditor.Scripting.Compilers.CompilerMessage compilerMessage in compilerMessages)
  1730. {
  1731. switch (compilerMessage.type)
  1732. {
  1733. case CompilerMessageType.Warning:
  1734. Debug.LogWarning(compilerMessage.message, compilerMessage.file, compilerMessage.line, compilerMessage.column);
  1735. break;
  1736. case CompilerMessageType.Error:
  1737. Debug.LogPlayerBuildError(compilerMessage.message, compilerMessage.file, compilerMessage.line, compilerMessage.column);
  1738. break;
  1739. }
  1740. }
  1741. }
  1742. var exitFailed = p.ExitCode != 0;
  1743. if (unreliableExitCode)
  1744. {
  1745. // Bcl +burstc will emit a Done as the final stdout if everything was ok.
  1746. exitFailed = (standardOutput.Length==0) || (standardOutput[standardOutput.Length - 1].Trim() != "Done");
  1747. }
  1748. if (exitFailed)
  1749. {
  1750. // We try to output the version in the heading error if we can
  1751. var matchVersion = MatchVersion.Match(exe);
  1752. errorMessageBuilder.Append(matchVersion.Success ?
  1753. "Burst compiler (" + matchVersion.Groups[1].Value + ") failed running" :
  1754. "Burst compiler failed running");
  1755. errorMessageBuilder.AppendLine();
  1756. errorMessageBuilder.AppendLine();
  1757. // Don't output the path if we are not burst-debugging or the exe exist
  1758. if (BurstLoader.IsDebugging || !File.Exists(exe))
  1759. {
  1760. errorMessageBuilder.Append(exe).Append(" ").Append(args);
  1761. errorMessageBuilder.AppendLine();
  1762. errorMessageBuilder.AppendLine();
  1763. }
  1764. errorMessageBuilder.AppendLine("stdout:");
  1765. foreach (string str in standardOutput)
  1766. errorMessageBuilder.AppendLine(str);
  1767. errorMessageBuilder.AppendLine("stderr:");
  1768. foreach (string str in errorOutput)
  1769. errorMessageBuilder.AppendLine(str);
  1770. throw new BuildFailedException(errorMessageBuilder.ToString());
  1771. }
  1772. Console.WriteLine(p.GetAllOutput());
  1773. }
  1774. }
  1775. }
  1776. /// <summary>
  1777. /// Internal class used to parse bcl output errors
  1778. /// </summary>
  1779. private class BclOutputErrorParser : CompilerOutputParserBase
  1780. {
  1781. // Format of an error message:
  1782. //
  1783. //C:\work\burst\src\Burst.Compiler.IL.Tests\Program.cs(17,9): error: Loading a managed string literal is not supported by burst
  1784. // at Buggy.NiceBug() (at C:\work\burst\src\Burst.Compiler.IL.Tests\Program.cs:17)
  1785. //
  1786. //
  1787. // [1] [2] [3] [4] [5]
  1788. // path line col type message
  1789. private static readonly Regex MatchLocation = new Regex(@"^(.*?)\((\d+)\s*,\s*(\d+)\):\s*([\w\s]+)\s*:\s*(.*)");
  1790. // Matches " at "
  1791. private static readonly Regex MatchAt = new Regex(@"^\s+at\s+");
  1792. public override IEnumerable<UnityEditor.Scripting.Compilers.CompilerMessage> Parse(
  1793. string[] errorOutput,
  1794. string[] standardOutput,
  1795. bool compilationHadFailure,
  1796. string assemblyName)
  1797. {
  1798. var messages = new List<UnityEditor.Scripting.Compilers.CompilerMessage>();
  1799. var textBuilder = new StringBuilder();
  1800. for (var i = 0; i < errorOutput.Length; i++)
  1801. {
  1802. string line = errorOutput[i];
  1803. var message = new UnityEditor.Scripting.Compilers.CompilerMessage {assemblyName = assemblyName};
  1804. // If we are able to match a location, we can decode it including the following attached " at " lines
  1805. textBuilder.Clear();
  1806. var match = MatchLocation.Match(line);
  1807. if (match.Success)
  1808. {
  1809. var path = match.Groups[1].Value;
  1810. int.TryParse(match.Groups[2].Value, out message.line);
  1811. int.TryParse(match.Groups[3].Value, out message.column);
  1812. if (match.Groups[4].Value.Contains("error"))
  1813. {
  1814. message.type = CompilerMessageType.Error;
  1815. }
  1816. else
  1817. {
  1818. message.type = CompilerMessageType.Warning;
  1819. }
  1820. message.file = !string.IsNullOrEmpty(path) ? path : "unknown";
  1821. // Replace '\' with '/' to let the editor open the file
  1822. message.file = message.file.Replace('\\', '/');
  1823. // Make path relative to project path path
  1824. var projectPath = Path.GetDirectoryName(Application.dataPath)?.Replace('\\', '/');
  1825. if (projectPath != null && message.file.StartsWith(projectPath))
  1826. {
  1827. message.file = message.file.Substring(projectPath.EndsWith("/") ? projectPath.Length : projectPath.Length + 1);
  1828. }
  1829. // debug
  1830. // textBuilder.AppendLine("line: " + message.line + " column: " + message.column + " error: " + message.type + " file: " + message.file);
  1831. textBuilder.Append(match.Groups[5].Value);
  1832. }
  1833. else
  1834. {
  1835. // Don't output any blank line
  1836. if (string.IsNullOrWhiteSpace(line))
  1837. {
  1838. continue;
  1839. }
  1840. // Otherwise we output an error, but without source location information
  1841. // so that at least the user can see it directly in the log errors
  1842. message.type = CompilerMessageType.Error;
  1843. message.line = 0;
  1844. message.column = 0;
  1845. message.file = "unknown";
  1846. textBuilder.Append(line);
  1847. }
  1848. // Collect attached location call context information ("at ...")
  1849. // we do it for both case (as if we have an exception in bcl we want to print this in a single line)
  1850. bool isFirstAt = true;
  1851. for (int j = i + 1; j < errorOutput.Length; j++)
  1852. {
  1853. var nextLine = errorOutput[j];
  1854. // Empty lines are ignored by the stack trace parser.
  1855. if (string.IsNullOrWhiteSpace(nextLine))
  1856. {
  1857. i++;
  1858. continue;
  1859. }
  1860. if (MatchAt.Match(nextLine).Success)
  1861. {
  1862. i++;
  1863. if (isFirstAt)
  1864. {
  1865. textBuilder.AppendLine();
  1866. isFirstAt = false;
  1867. }
  1868. textBuilder.AppendLine(nextLine);
  1869. }
  1870. else
  1871. {
  1872. break;
  1873. }
  1874. }
  1875. message.message = textBuilder.ToString();
  1876. messages.Add(message);
  1877. }
  1878. return messages;
  1879. }
  1880. protected override string GetErrorIdentifier()
  1881. {
  1882. throw new NotImplementedException(); // as we overriding the method Parse()
  1883. }
  1884. protected override Regex GetOutputRegex()
  1885. {
  1886. throw new NotImplementedException(); // as we overriding the method Parse()
  1887. }
  1888. }
  1889. #if UNITY_EDITOR_OSX && !ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API
  1890. private class StaticLibraryPostProcessor
  1891. {
  1892. private const string TempSourceLibrary = @"Temp/StagingArea/StaticLibraries";
  1893. [PostProcessBuildAttribute(1)]
  1894. public static void OnPostProcessBuild(BuildTarget target, string path)
  1895. {
  1896. // Early out if we are building for the simulator, as we don't
  1897. //currently generate burst libraries that will work for that.
  1898. if (IsForSimulator(target))
  1899. {
  1900. return;
  1901. }
  1902. // We only support AOT compilation for ios from a macos host (we require xcrun and the apple tool chains)
  1903. //for other hosts, we simply act as if burst is not being used (an error will be generated by the build aot step)
  1904. //this keeps the behaviour consistent with how it was before static linkage was introduced
  1905. if (target == BuildTarget.iOS)
  1906. {
  1907. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.iOS);
  1908. // Early exit if burst is not activated
  1909. if (!aotSettingsForTarget.EnableBurstCompilation)
  1910. {
  1911. return;
  1912. }
  1913. PostAddStaticLibraries(path);
  1914. }
  1915. if (target == BuildTarget.tvOS)
  1916. {
  1917. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.tvOS);
  1918. // Early exit if burst is not activated
  1919. if (!aotSettingsForTarget.EnableBurstCompilation)
  1920. {
  1921. return;
  1922. }
  1923. PostAddStaticLibraries(path);
  1924. }
  1925. #if UNITY_2023_3_OR_NEWER
  1926. if (target == BuildTarget.visionOS)
  1927. {
  1928. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.visionOS);
  1929. // Early exit if burst is not activated
  1930. if (!aotSettingsForTarget.EnableBurstCompilation)
  1931. {
  1932. return;
  1933. }
  1934. PostAddStaticLibraries(path);
  1935. }
  1936. #endif
  1937. }
  1938. private static void PostAddStaticLibraries(string path)
  1939. {
  1940. var assm = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(assembly =>
  1941. assembly.GetName().Name == "UnityEditor.iOS.Extensions.Xcode");
  1942. Type PBXType = assm?.GetType("UnityEditor.iOS.Xcode.PBXProject");
  1943. Type PBXSourceTree = assm?.GetType("UnityEditor.iOS.Xcode.PBXSourceTree");
  1944. if (PBXType != null && PBXSourceTree != null)
  1945. {
  1946. var project = Activator.CreateInstance(PBXType, null);
  1947. var _sGetPBXProjectPath = PBXType.GetMethod("GetPBXProjectPath");
  1948. var _ReadFromFile = PBXType.GetMethod("ReadFromFile");
  1949. var _sGetUnityTargetName = PBXType.GetMethod("GetUnityTargetName");
  1950. var _AddFileToBuild = PBXType.GetMethod("AddFileToBuild");
  1951. var _AddFile = PBXType.GetMethod("AddFile");
  1952. var _WriteToString = PBXType.GetMethod("WriteToString");
  1953. var sourcetree = new EnumConverter(PBXSourceTree).ConvertFromString("Source");
  1954. string sPath = (string)_sGetPBXProjectPath?.Invoke(null, new object[] { path });
  1955. _ReadFromFile?.Invoke(project, new object[] { sPath });
  1956. var _TargetGuidByName = PBXType.GetMethod("GetUnityFrameworkTargetGuid");
  1957. string g = (string) _TargetGuidByName?.Invoke(project, null);
  1958. var srcPath = TempSourceLibrary;
  1959. var dstPath = "Libraries";
  1960. var dstCopyPath = Path.Combine(path, dstPath);
  1961. var burstCppLinkFile = "lib_burst_generated.cpp";
  1962. var libName = $"{DefaultLibraryName}.a";
  1963. var libSrcPath = Path.Combine(srcPath, libName);
  1964. var libExists = File.Exists(libSrcPath);
  1965. if (!libExists)
  1966. {
  1967. return; // No libs, so don't write the cpp either
  1968. }
  1969. File.Copy(libSrcPath, Path.Combine(dstCopyPath, libName));
  1970. AddLibToProject(project, _AddFileToBuild, _AddFile, sourcetree, g, dstPath, libName);
  1971. // Additionally we need a small cpp file (weak symbols won't unfortunately override directly from the libs
  1972. //presumably due to link order?
  1973. WriteStaticLinkCppFile(dstCopyPath);
  1974. string cppPath = Path.Combine(dstPath, burstCppLinkFile);
  1975. string fileg = (string)_AddFile?.Invoke(project, new object[] { cppPath, cppPath, sourcetree });
  1976. _AddFileToBuild?.Invoke(project, new object[] { g, fileg });
  1977. string pstring = (string)_WriteToString?.Invoke(project, null);
  1978. File.WriteAllText(sPath, pstring);
  1979. }
  1980. }
  1981. private static void AddLibToProject(object project, System.Reflection.MethodInfo _AddFileToBuild, System.Reflection.MethodInfo _AddFile, object sourcetree, string g, string dstPath, string lib32Name)
  1982. {
  1983. string fg = (string)_AddFile?.Invoke(project,
  1984. new object[] { Path.Combine(dstPath, lib32Name), Path.Combine(dstPath, lib32Name), sourcetree });
  1985. _AddFileToBuild?.Invoke(project, new object[] { g, fg });
  1986. }
  1987. }
  1988. #endif
  1989. }
  1990. }
  1991. #endif