Нема описа
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 78KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716
  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. #if UNITY_EDITOR_OSX
  23. using System.ComponentModel;
  24. using Unity.Burst.LowLevel;
  25. using UnityEditor.Callbacks;
  26. #endif
  27. namespace Unity.Burst.Editor
  28. {
  29. using static BurstCompilerOptions;
  30. internal class TargetCpus
  31. {
  32. public List<BurstTargetCpu> Cpus;
  33. public TargetCpus()
  34. {
  35. Cpus = new List<BurstTargetCpu>();
  36. }
  37. public TargetCpus(BurstTargetCpu single)
  38. {
  39. Cpus = new List<BurstTargetCpu>(1)
  40. {
  41. single
  42. };
  43. }
  44. public bool IsX86()
  45. {
  46. foreach (var cpu in Cpus)
  47. {
  48. switch (cpu)
  49. {
  50. case BurstTargetCpu.X86_SSE2:
  51. case BurstTargetCpu.X86_SSE4:
  52. return true;
  53. }
  54. }
  55. return false;
  56. }
  57. public override string ToString()
  58. {
  59. var result = "";
  60. var first = true;
  61. foreach (var cpu in Cpus)
  62. {
  63. if (first)
  64. {
  65. result += $"{cpu}";
  66. first = false;
  67. }
  68. else
  69. {
  70. result += $", {cpu}";
  71. }
  72. }
  73. return result;
  74. }
  75. public TargetCpus Clone()
  76. {
  77. var copy = new TargetCpus
  78. {
  79. Cpus = new List<BurstTargetCpu>(Cpus.Count)
  80. };
  81. foreach (var cpu in Cpus)
  82. {
  83. copy.Cpus.Add(cpu);
  84. }
  85. return copy;
  86. }
  87. }
  88. internal class LinkXMLGenerator : IUnityLinkerProcessor
  89. {
  90. public int callbackOrder => 1;
  91. public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data)
  92. {
  93. var linkXml = Path.GetFullPath(Path.Combine("Temp", BurstAotCompiler.BurstLinkXmlName));
  94. return linkXml;
  95. }
  96. public void OnBeforeRun(BuildReport report, UnityLinkerBuildPipelineData data)
  97. {
  98. }
  99. public void OnAfterRun(BuildReport report, UnityLinkerBuildPipelineData data)
  100. {
  101. }
  102. }
  103. internal class BurstAndroidGradlePostprocessor : IPostGenerateGradleAndroidProject
  104. {
  105. int IOrderedCallback.callbackOrder => 1;
  106. void IPostGenerateGradleAndroidProject.OnPostGenerateGradleAndroidProject(string path)
  107. {
  108. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.Android);
  109. // Early exit if burst is not activated
  110. if (!aotSettingsForTarget.EnableBurstCompilation)
  111. {
  112. return;
  113. }
  114. // Copy bursted .so's from tempburstlibs to the actual location in the gradle project
  115. var sourceLocation = Path.GetFullPath(Path.Combine("Temp", "StagingArea", "tempburstlibs"));
  116. var targetLocation = Path.GetFullPath(Path.Combine(path, "src", "main", "jniLibs"));
  117. FileUtil.CopyDirectoryRecursive(sourceLocation, targetLocation, true);
  118. }
  119. }
  120. // For static builds, there are two different approaches:
  121. // Postprocessing adds the libraries after Unity is done building,
  122. // for platforms that need to build a project file, etc.
  123. // Preprocessing simply adds the libraries to the Unity build,
  124. // for platforms where Unity can directly build an app.
  125. internal class StaticPreProcessor : IPreprocessBuildWithReport
  126. {
  127. private const string TempSourceLibrary = @"Temp/StagingArea/SourcePlugins";
  128. private const string TempStaticLibrary = @"Temp/StagingArea/NativePlugins";
  129. public int callbackOrder { get { return 0; } }
  130. public void OnPreprocessBuild(BuildReport report)
  131. {
  132. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform);
  133. // Early exit if burst is not activated
  134. if (!aotSettingsForTarget.EnableBurstCompilation)
  135. {
  136. return;
  137. }
  138. if(report.summary.platform == BuildTarget.Switch)
  139. {
  140. // add the static lib, and the c++ shim
  141. string burstCppLinkFile = "lib_burst_generated.cpp";
  142. string burstStaticLibFile = "lib_burst_generated.a";
  143. string cppPath = Path.Combine(TempSourceLibrary, burstCppLinkFile);
  144. string libPath = Path.Combine(TempStaticLibrary, burstStaticLibFile);
  145. if(!Directory.Exists(TempSourceLibrary))
  146. {
  147. Directory.CreateDirectory(TempSourceLibrary);
  148. Directory.CreateDirectory(TempSourceLibrary);
  149. }
  150. File.WriteAllText(cppPath, @"
  151. extern ""C""
  152. {
  153. void Staticburst_initialize(void* );
  154. void* StaticBurstStaticMethodLookup(void* );
  155. int burst_enable_static_linkage = 1;
  156. void burst_initialize(void* i) { Staticburst_initialize(i); }
  157. void* BurstStaticMethodLookup(void* i) { return StaticBurstStaticMethodLookup(i); }
  158. }
  159. ");
  160. }
  161. }
  162. }
  163. /// <summary>
  164. /// Integration of the burst AOT compiler into the Unity build player pipeline
  165. /// </summary>
  166. internal class BurstAotCompiler : IPostBuildPlayerScriptDLLs
  167. {
  168. private const string BurstAotCompilerExecutable = "bcl.exe";
  169. private const string TempStaging = @"Temp/StagingArea/";
  170. private const string TempStagingManaged = TempStaging + @"Data/Managed/";
  171. private const string LibraryPlayerScriptAssemblies = "Library/PlayerScriptAssemblies";
  172. private const string TempManagedSymbols = @"Temp/ManagedSymbols/";
  173. internal const string BurstLinkXmlName = "burst.link.xml";
  174. int IOrderedCallback.callbackOrder => 0;
  175. public void OnPostBuildPlayerScriptDLLs(BuildReport report)
  176. {
  177. var step = report.BeginBuildStep("burst");
  178. try
  179. {
  180. OnPostBuildPlayerScriptDLLsImpl(report);
  181. }
  182. finally
  183. {
  184. report.EndBuildStep(step);
  185. }
  186. }
  187. private void OnPostBuildPlayerScriptDLLsImpl(BuildReport report)
  188. {
  189. var buildTarget = report.summary.platform;
  190. string burstMiscAlongsidePath = "";
  191. if ((report.summary.options & BuildOptions.InstallInBuildFolder) == 0)
  192. {
  193. burstMiscAlongsidePath = BurstPlatformAotSettings.FetchOutputPath(report);
  194. }
  195. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(buildTarget);
  196. HashSet<string> assemblyDefines = new HashSet<string>();
  197. // Early exit if burst is not activated or the platform is not supported
  198. if (BurstCompilerOptions.ForceDisableBurstCompilation || !aotSettingsForTarget.EnableBurstCompilation || !IsSupportedPlatform(buildTarget))
  199. {
  200. return;
  201. }
  202. var isDevelopmentBuild = (report.summary.options & BuildOptions.Development) != 0;
  203. var commonOptions = new List<string>();
  204. var stagingFolder = Path.GetFullPath(TempStagingManaged);
  205. var playerAssemblies = GetPlayerAssemblies(report);
  206. // grab the location of the root of the player folder - for handling nda platforms that require keys
  207. var keyFolder = BuildPipeline.GetPlaybackEngineDirectory(buildTarget, BuildOptions.None);
  208. commonOptions.Add(GetOption(OptionAotKeyFolder, keyFolder));
  209. commonOptions.Add(GetOption(OptionAotDecodeFolder, Path.Combine(Environment.CurrentDirectory, "Library", "Burst")));
  210. // Extract the TargetPlatform and Cpus from the current build settings
  211. var targetPlatform = GetTargetPlatformAndDefaultCpu(buildTarget, out var targetCpus);
  212. commonOptions.Add(GetOption(OptionPlatform, targetPlatform));
  213. // --------------------------------------------------------------------------------------------------------
  214. // 1) Calculate AssemblyFolders
  215. // These are the folders to look for assembly resolution
  216. // --------------------------------------------------------------------------------------------------------
  217. var assemblyFolders = new List<string> { stagingFolder };
  218. if (buildTarget == BuildTarget.WSAPlayer
  219. || buildTarget == BuildTarget.XboxOne
  220. #if UNITY_2019_4_OR_NEWER
  221. || buildTarget == BuildTarget.GameCoreXboxOne || buildTarget == BuildTarget.GameCoreXboxSeries
  222. #endif
  223. )
  224. {
  225. // On UWP, not all assemblies are copied to StagingArea, so we want to
  226. // find all directories that we can reference assemblies from
  227. // If we don't do this, we will crash with AssemblyResolutionException
  228. // when following type references.
  229. foreach (var assembly in playerAssemblies)
  230. {
  231. foreach (var assemblyRef in assembly.compiledAssemblyReferences)
  232. {
  233. // Exclude folders with assemblies already compiled in the `folder`
  234. var assemblyName = Path.GetFileName(assemblyRef);
  235. if (assemblyName != null && File.Exists(Path.Combine(stagingFolder, assemblyName)))
  236. {
  237. continue;
  238. }
  239. var directory = Path.GetDirectoryName(assemblyRef);
  240. if (directory != null)
  241. {
  242. var fullPath = Path.GetFullPath(directory);
  243. if (IsMonoReferenceAssemblyDirectory(fullPath) || IsDotNetStandardAssemblyDirectory(fullPath))
  244. {
  245. // Don't pass reference assemblies to burst because they contain methods without implementation
  246. // If burst accidentally resolves them, it will emit calls to burst_abort.
  247. fullPath = Path.Combine(EditorApplication.applicationContentsPath, "MonoBleedingEdge/lib/mono");
  248. #if UNITY_2021_2_OR_NEWER
  249. // In 2021.2 we got multiple mono distributions, per platform.
  250. fullPath = Path.Combine(fullPath, "unityaot-" + BuildTargetDiscovery.GetPlatformProfileSuffix(buildTarget));
  251. #else
  252. fullPath = Path.Combine(fullPath, "unityaot");
  253. #endif
  254. fullPath = Path.GetFullPath(fullPath); // GetFullPath will normalize path separators to OS native format
  255. if (!assemblyFolders.Contains(fullPath))
  256. assemblyFolders.Add(fullPath);
  257. fullPath = Path.Combine(fullPath, "Facades");
  258. if (!assemblyFolders.Contains(fullPath))
  259. assemblyFolders.Add(fullPath);
  260. }
  261. else if (!assemblyFolders.Contains(fullPath))
  262. {
  263. assemblyFolders.Add(fullPath);
  264. }
  265. }
  266. }
  267. }
  268. }
  269. // Copy assembly used during staging to have a trace
  270. if (BurstLoader.IsDebugging)
  271. {
  272. try
  273. {
  274. var copyAssemblyFolder = Path.Combine(Environment.CurrentDirectory, "Logs", "StagingAssemblies");
  275. try
  276. {
  277. if (Directory.Exists(copyAssemblyFolder)) Directory.Delete(copyAssemblyFolder);
  278. }
  279. catch
  280. {
  281. }
  282. if (!Directory.Exists(copyAssemblyFolder)) Directory.CreateDirectory(copyAssemblyFolder);
  283. foreach (var file in Directory.EnumerateFiles(stagingFolder))
  284. {
  285. File.Copy(file, Path.Combine(copyAssemblyFolder, Path.GetFileName(file)));
  286. }
  287. }
  288. catch
  289. {
  290. }
  291. }
  292. // --------------------------------------------------------------------------------------------------------
  293. // 2) Calculate root assemblies
  294. // These are the assemblies that the compiler will look for methods to compile
  295. // This list doesn't typically include .NET runtime assemblies but only assemblies compiled as part
  296. // of the current Unity project
  297. // --------------------------------------------------------------------------------------------------------
  298. var rootAssemblies = new List<string>();
  299. foreach (var playerAssembly in playerAssemblies)
  300. {
  301. // the file at path `playerAssembly.outputPath` is actually not on the disk
  302. // while it is in the staging folder because OnPostBuildPlayerScriptDLLs is being called once the files are already
  303. // transferred to the staging folder, so we are going to work from it but we are reusing the file names that we got earlier
  304. var playerAssemblyPathToStaging = Path.Combine(stagingFolder, Path.GetFileName(playerAssembly.outputPath));
  305. if (!File.Exists(playerAssemblyPathToStaging))
  306. {
  307. Debug.LogWarning($"Unable to find player assembly: {playerAssemblyPathToStaging}");
  308. }
  309. else
  310. {
  311. rootAssemblies.Add(playerAssemblyPathToStaging);
  312. assemblyDefines.UnionWith(playerAssembly.defines);
  313. }
  314. }
  315. commonOptions.AddRange(assemblyFolders.Select(folder => GetOption(OptionAotAssemblyFolder, folder)));
  316. commonOptions.AddRange(assemblyDefines.Select(define => GetOption(OptionCompilationDefines, define)));
  317. // --------------------------------------------------------------------------------------------------------
  318. // 3) Calculate the different target CPU combinations for the specified OS
  319. //
  320. // Typically, on some platforms like iOS we can be asked to compile a ARM32 and ARM64 CPU version
  321. // --------------------------------------------------------------------------------------------------------
  322. var combinations = CollectCombinations(targetPlatform, targetCpus, report);
  323. // --------------------------------------------------------------------------------------------------------
  324. // 4) Compile each combination
  325. //
  326. // Here bcl.exe is called for each target CPU combination
  327. // --------------------------------------------------------------------------------------------------------
  328. string debugLogFile = null;
  329. if (BurstLoader.IsDebugging)
  330. {
  331. // Reset log files
  332. try
  333. {
  334. var logDir = Path.Combine(Environment.CurrentDirectory, "Logs");
  335. debugLogFile = Path.Combine(logDir, "burst_bcl_editor.log");
  336. if (!Directory.Exists(logDir)) Directory.CreateDirectory(logDir);
  337. File.WriteAllText(debugLogFile, string.Empty);
  338. }
  339. catch
  340. {
  341. debugLogFile = null;
  342. }
  343. }
  344. if ((report.summary.options & BuildOptions.InstallInBuildFolder) == 0)
  345. {
  346. CreateFolderForMiscFiles(burstMiscAlongsidePath);
  347. }
  348. // Log the targets generated by BurstReflection.FindExecuteMethods
  349. foreach (var combination in combinations)
  350. {
  351. // Gets the output folder
  352. var stagingOutputFolder = Path.GetFullPath(Path.Combine(TempStaging, combination.OutputPath));
  353. var outputFilePrefix = Path.Combine(stagingOutputFolder, combination.LibraryName);
  354. var options = new List<string>(commonOptions)
  355. {
  356. GetOption(OptionAotOutputPath, outputFilePrefix),
  357. GetOption(OptionTempDirectory, Path.Combine(Environment.CurrentDirectory, "Temp", "Burst"))
  358. };
  359. foreach (var cpu in combination.TargetCpus.Cpus)
  360. {
  361. options.Add(GetOption(OptionTarget, cpu));
  362. }
  363. if (targetPlatform == TargetPlatform.iOS || targetPlatform == TargetPlatform.tvOS || targetPlatform == TargetPlatform.Switch)
  364. {
  365. options.Add(GetOption(OptionStaticLinkage));
  366. }
  367. if (targetPlatform == TargetPlatform.Windows)
  368. {
  369. options.Add(GetOption(OptionLinkerOptions, $"PdbAltPath=\"{PlayerSettings.productName}_{combination.OutputPath}\""));
  370. }
  371. // finally add method group options
  372. options.AddRange(rootAssemblies.Select(path => GetOption(OptionRootAssembly, path)));
  373. // Set the flag to print a message on missing MonoPInvokeCallback attribute on IL2CPP only
  374. if (PlayerSettings.GetScriptingBackend(BuildPipeline.GetBuildTargetGroup(buildTarget)) == ScriptingImplementation.IL2CPP)
  375. {
  376. options.Add(GetOption(OptionPrintLogOnMissingPInvokeCallbackAttribute));
  377. }
  378. // Log the targets generated by BurstReflection.FindExecuteMethods
  379. if (BurstLoader.IsDebugging && debugLogFile != null)
  380. {
  381. try
  382. {
  383. var writer = new StringWriter();
  384. writer.WriteLine("-----------------------------------------------------------");
  385. writer.WriteLine("Combination: " + combination);
  386. writer.WriteLine("-----------------------------------------------------------");
  387. foreach (var option in options)
  388. {
  389. writer.WriteLine(option);
  390. }
  391. writer.WriteLine("Assemblies in AssemblyFolders:");
  392. foreach (var assemblyFolder in assemblyFolders)
  393. {
  394. writer.WriteLine("|- Folder: " + assemblyFolder);
  395. foreach (var assemblyOrDll in Directory.EnumerateFiles(assemblyFolder, "*.dll"))
  396. {
  397. var fileInfo = new FileInfo(assemblyOrDll);
  398. writer.WriteLine(" |- " + assemblyOrDll + " Size: " + fileInfo.Length + " Date: " + fileInfo.LastWriteTime);
  399. }
  400. }
  401. File.AppendAllText(debugLogFile, writer.ToString());
  402. }
  403. catch
  404. {
  405. // ignored
  406. }
  407. }
  408. // Allow burst to find managed symbols in the backup location in case the symbols are stripped in the build location
  409. options.Add(GetOption(OptionAotPdbSearchPaths, TempManagedSymbols));
  410. if (isDevelopmentBuild && Environment.GetEnvironmentVariable("UNITY_BURST_ENABLE_SAFETY_CHECKS_IN_PLAYER_BUILD") != null)
  411. {
  412. options.Add("--global-safety-checks-setting=ForceOn");
  413. }
  414. options.Add(GetOption(OptionGenerateLinkXml, Path.Combine("Temp", BurstLinkXmlName)));
  415. // Write current options to the response file
  416. var responseFile = Path.GetTempFileName();
  417. File.WriteAllLines(responseFile, options);
  418. if (BurstLoader.IsDebugging)
  419. {
  420. Debug.Log($"bcl @{responseFile}\n\nResponse File:\n" + string.Join("\n", options));
  421. }
  422. try
  423. {
  424. string extraGlobalOptions = "";
  425. if (!string.IsNullOrWhiteSpace(aotSettingsForTarget.DisabledWarnings))
  426. {
  427. extraGlobalOptions += GetOption(OptionDisableWarnings, aotSettingsForTarget.DisabledWarnings) + " ";
  428. }
  429. if (isDevelopmentBuild || aotSettingsForTarget.EnableDebugInAllBuilds)
  430. {
  431. if (!isDevelopmentBuild)
  432. {
  433. Debug.LogWarning("Symbols are being generated for burst compiled code, please ensure you intended this - see Burst AOT settings.");
  434. }
  435. extraGlobalOptions += GetOption(OptionDebug, "Full") + " ";
  436. }
  437. if (!aotSettingsForTarget.EnableOptimisations)
  438. {
  439. extraGlobalOptions += GetOption(OptionDisableOpt) + " ";
  440. }
  441. if (aotSettingsForTarget.UsePlatformSDKLinker)
  442. {
  443. extraGlobalOptions += GetOption(OptionAotUsePlatformSDKLinkers) + " ";
  444. }
  445. switch (aotSettingsForTarget.OptimizeFor)
  446. {
  447. case OptimizeFor.Default:
  448. case OptimizeFor.Balanced:
  449. extraGlobalOptions += GetOption(OptionOptLevel, 2) + " ";
  450. break;
  451. case OptimizeFor.Performance:
  452. extraGlobalOptions += GetOption(OptionOptLevel, 3) + " ";
  453. break;
  454. case OptimizeFor.Size:
  455. extraGlobalOptions += GetOption(OptionOptForSize) + " ";
  456. extraGlobalOptions += GetOption(OptionOptLevel, 3) + " ";
  457. break;
  458. case OptimizeFor.FastCompilation:
  459. extraGlobalOptions += GetOption(OptionOptLevel, 1) + " ";
  460. break;
  461. }
  462. BclRunner.RunManagedProgram(Path.Combine(BurstLoader.RuntimePath, BurstAotCompilerExecutable),
  463. $"{extraGlobalOptions} {BclRunner.EscapeForShell("@" + responseFile)}",
  464. new BclOutputErrorParser());
  465. // Additionally copy the pdb to the root of the player build so run in editor also locates the symbols
  466. var pdbPath = $"{Path.Combine(stagingOutputFolder, combination.LibraryName)}.pdb";
  467. if (File.Exists(pdbPath))
  468. {
  469. var dstPath = Path.Combine(TempStaging, $"{combination.LibraryName}.pdb");
  470. File.Copy(pdbPath, dstPath, overwrite: true);
  471. }
  472. }
  473. catch (BuildFailedException)
  474. {
  475. throw;
  476. }
  477. catch (Exception e)
  478. {
  479. throw new BuildFailedException(e);
  480. }
  481. }
  482. PostProcessCombinations(targetPlatform, combinations, report);
  483. // Finally move out any symbols/misc files from the final output
  484. if ((report.summary.options & BuildOptions.InstallInBuildFolder) == 0)
  485. {
  486. CollateMiscFiles(combinations, burstMiscAlongsidePath, isDevelopmentBuild);
  487. }
  488. }
  489. private static void CreateFolderForMiscFiles(string finalFolder)
  490. {
  491. try
  492. {
  493. if (Directory.Exists(finalFolder)) Directory.Delete(finalFolder,true);
  494. }
  495. catch
  496. {
  497. }
  498. Directory.CreateDirectory(finalFolder);
  499. }
  500. private static void CollateMiscFiles(List<BurstOutputCombination> combinations, string finalFolder, bool retainPdbs)
  501. {
  502. foreach (var combination in combinations)
  503. {
  504. var inputPath = Path.GetFullPath(Path.Combine(TempStaging, combination.OutputPath));
  505. var outputPath = Path.Combine(finalFolder, combination.OutputPath);
  506. Directory.CreateDirectory(outputPath);
  507. var files = Directory.GetFiles(inputPath);
  508. var directories = Directory.GetDirectories(inputPath);
  509. foreach (var fileName in files)
  510. {
  511. var lowerCase = fileName.ToLower();
  512. if ( (!retainPdbs && lowerCase.EndsWith(".pdb")) || lowerCase.EndsWith(".dsym") || lowerCase.EndsWith(".txt"))
  513. {
  514. // Move the file out of the staging area so its not included in the build
  515. File.Move(fileName, Path.Combine(outputPath, Path.GetFileName(fileName)));
  516. }
  517. }
  518. foreach (var fileName in directories)
  519. {
  520. var lowerCase = fileName.ToLower();
  521. if ( (!retainPdbs && lowerCase.EndsWith(".pdb")) || lowerCase.EndsWith(".dsym") || lowerCase.EndsWith(".txt"))
  522. {
  523. // Move the folder out of the staging area so its not included in the build
  524. Directory.Move(fileName, Path.Combine(outputPath, Path.GetFileName(fileName)));
  525. }
  526. }
  527. }
  528. }
  529. private static bool AndroidHasX86(AndroidArchitecture architecture)
  530. {
  531. // Deal with rename that occured
  532. AndroidArchitecture val;
  533. if (AndroidArchitecture.TryParse("X86", out val))
  534. {
  535. return (architecture & val)!=0;
  536. }
  537. else if (AndroidArchitecture.TryParse("x86", out val))
  538. {
  539. return (architecture & val)!=0;
  540. }
  541. return false;
  542. }
  543. private static bool AndroidHasX86_64(AndroidArchitecture architecture)
  544. {
  545. // Deal with rename that occured
  546. AndroidArchitecture val;
  547. if (AndroidArchitecture.TryParse("X86_64", out val))
  548. {
  549. return (architecture & val)!=0;
  550. }
  551. else if (AndroidArchitecture.TryParse("x86_64", out val))
  552. {
  553. return (architecture & val)!=0;
  554. }
  555. return false;
  556. }
  557. private enum SimulatorPlatforms
  558. {
  559. iOS,
  560. tvOS
  561. }
  562. private static bool IsForSimulator(BuildTarget target)
  563. {
  564. switch (target)
  565. {
  566. case BuildTarget.iOS:
  567. return IsForSimulator(SimulatorPlatforms.iOS);
  568. case BuildTarget.tvOS:
  569. return IsForSimulator(SimulatorPlatforms.tvOS);
  570. default:
  571. return false;
  572. }
  573. }
  574. private static bool IsForSimulator(TargetPlatform targetPlatform)
  575. {
  576. switch (targetPlatform)
  577. {
  578. case TargetPlatform.iOS:
  579. return IsForSimulator(SimulatorPlatforms.iOS);
  580. case TargetPlatform.tvOS:
  581. return IsForSimulator(SimulatorPlatforms.tvOS);
  582. default:
  583. return false;
  584. }
  585. }
  586. private static bool IsForSimulator(SimulatorPlatforms simulatorPlatforms)
  587. {
  588. switch (simulatorPlatforms)
  589. {
  590. case SimulatorPlatforms.iOS:
  591. return UnityEditor.PlayerSettings.iOS.sdkVersion == iOSSdkVersion.SimulatorSDK;
  592. case SimulatorPlatforms.tvOS:
  593. return UnityEditor.PlayerSettings.tvOS.sdkVersion == tvOSSdkVersion.Simulator;
  594. }
  595. return false;
  596. }
  597. /// <summary>
  598. /// Collect CPU combinations for the specified TargetPlatform and TargetCPU
  599. /// </summary>
  600. /// <param name="targetPlatform">The target platform (e.g Windows)</param>
  601. /// <param name="targetCpus">The target CPUs (e.g X64_SSE4)</param>
  602. /// <param name="report">Error reporting</param>
  603. /// <returns>The list of CPU combinations</returns>
  604. private static List<BurstOutputCombination> CollectCombinations(TargetPlatform targetPlatform, TargetCpus targetCpus, BuildReport report)
  605. {
  606. var combinations = new List<BurstOutputCombination>();
  607. if (targetPlatform == TargetPlatform.macOS)
  608. {
  609. // NOTE: OSX has a special folder for the plugin
  610. // Declared in GetStagingAreaPluginsFolder
  611. // PlatformDependent\OSXPlayer\Extensions\Managed\OSXDesktopStandalonePostProcessor.cs
  612. #if UNITY_2019_3_OR_NEWER
  613. var outputPath = Path.Combine(Path.GetFileName(report.summary.outputPath), "Contents", "Plugins");
  614. #else
  615. var outputPath = "UnityPlayer.app/Contents/Plugins";
  616. #endif
  617. #if UNITY_2020_2_OR_NEWER
  618. // Based on : PlatformDependent/OSXPlayer/Extension/OSXStandaloneBuildWindowExtension.cs
  619. var aotSettings = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.StandaloneOSX);
  620. var buildTargetName = BuildPipeline.GetBuildTargetName(BuildTarget.StandaloneOSX);
  621. var architecture = EditorUserBuildSettings.GetPlatformSettings(buildTargetName, "Architecture").ToLowerInvariant();
  622. switch (architecture)
  623. {
  624. case "x64":
  625. combinations.Add(new BurstOutputCombination(outputPath, aotSettings.GetDesktopCpu64Bit()));
  626. break;
  627. case "arm64":
  628. combinations.Add(new BurstOutputCombination(outputPath, new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64)));
  629. break;
  630. default:
  631. combinations.Add(new BurstOutputCombination(Path.Combine(outputPath, "x64"), aotSettings.GetDesktopCpu64Bit()));
  632. combinations.Add(new BurstOutputCombination(Path.Combine(outputPath, "arm64"), new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64)));
  633. break;
  634. }
  635. #else
  636. combinations.Add(new BurstOutputCombination(outputPath, targetCpus));
  637. #endif
  638. }
  639. else if (targetPlatform == TargetPlatform.iOS || targetPlatform == TargetPlatform.tvOS)
  640. {
  641. if (IsForSimulator(targetPlatform))
  642. {
  643. Debug.LogWarning("Burst Does not currently support the simulator, burst is disabled for this build.");
  644. }
  645. else if (Application.platform != RuntimePlatform.OSXEditor)
  646. {
  647. 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.");
  648. }
  649. else
  650. {
  651. var targetArchitecture = (IOSArchitecture) UnityEditor.PlayerSettings.GetArchitecture(report.summary.platformGroup);
  652. if (targetArchitecture == IOSArchitecture.ARMv7 || targetArchitecture == IOSArchitecture.Universal)
  653. {
  654. // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs
  655. combinations.Add(new BurstOutputCombination("StaticLibraries", new TargetCpus(BurstTargetCpu.ARMV7A_NEON32), DefaultLibraryName + "32"));
  656. }
  657. if (targetArchitecture == IOSArchitecture.ARM64 || targetArchitecture == IOSArchitecture.Universal)
  658. {
  659. // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs
  660. combinations.Add(new BurstOutputCombination("StaticLibraries", new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64), DefaultLibraryName + "64"));
  661. }
  662. }
  663. }
  664. else if (targetPlatform == TargetPlatform.Android)
  665. {
  666. // TODO: would be better to query AndroidNdkRoot (but thats not exposed from unity)
  667. string ndkRoot = null;
  668. var targetAPILevel = PlayerSettings.Android.GetMinTargetAPILevel();
  669. #if UNITY_2019_3_OR_NEWER && UNITY_ANDROID
  670. ndkRoot = UnityEditor.Android.AndroidExternalToolsSettings.ndkRootPath;
  671. #elif UNITY_2019_1_OR_NEWER
  672. // 2019.1 now has an embedded ndk
  673. if (EditorPrefs.HasKey("NdkUseEmbedded"))
  674. {
  675. if (EditorPrefs.GetBool("NdkUseEmbedded"))
  676. {
  677. ndkRoot = Path.Combine(BuildPipeline.GetPlaybackEngineDirectory(BuildTarget.Android, BuildOptions.None), "NDK");
  678. }
  679. else
  680. {
  681. ndkRoot = EditorPrefs.GetString("AndroidNdkRootR16b");
  682. }
  683. }
  684. #elif UNITY_2018_3_OR_NEWER
  685. // Unity 2018.3 is using NDK r16b
  686. ndkRoot = EditorPrefs.GetString("AndroidNdkRootR16b");
  687. #endif
  688. // If we still don't have a valid root, try the old key
  689. if (string.IsNullOrEmpty(ndkRoot))
  690. {
  691. ndkRoot = EditorPrefs.GetString("AndroidNdkRoot");
  692. }
  693. // Verify the directory at least exists, if not we fall back to ANDROID_NDK_ROOT current setting
  694. if (!string.IsNullOrEmpty(ndkRoot) && !Directory.Exists(ndkRoot))
  695. {
  696. ndkRoot = null;
  697. }
  698. // 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
  699. if (!string.IsNullOrEmpty(ndkRoot))
  700. {
  701. Environment.SetEnvironmentVariable("ANDROID_NDK_ROOT", ndkRoot);
  702. }
  703. Environment.SetEnvironmentVariable("BURST_ANDROID_MIN_API_LEVEL", $"{targetAPILevel}");
  704. // Setting tempburstlibs/ as the interim target directory
  705. // Don't target libs/ directly because incremental build pipeline doesn't expect the so's at that path
  706. // Rather, so's are copied to the actual location in the gradle project in BurstAndroidGradlePostprocessor
  707. var androidTargetArch = PlayerSettings.Android.targetArchitectures;
  708. if ((androidTargetArch & AndroidArchitecture.ARMv7) != 0)
  709. {
  710. combinations.Add(new BurstOutputCombination("tempburstlibs/armeabi-v7a", new TargetCpus(BurstTargetCpu.ARMV7A_NEON32)));
  711. }
  712. if ((androidTargetArch & AndroidArchitecture.ARM64) != 0)
  713. {
  714. combinations.Add(new BurstOutputCombination("tempburstlibs/arm64-v8a", new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64)));
  715. }
  716. #if !UNITY_2019_2_OR_NEWER
  717. if (AndroidHasX86(androidTargetArch))
  718. {
  719. combinations.Add(new BurstOutputCombination("tempburstlibs/x86", new TargetCpus(BurstTargetCpu.X86_SSE2)));
  720. }
  721. #endif
  722. #if UNITY_2021_2_OR_NEWER
  723. if (AndroidHasX86(androidTargetArch))
  724. {
  725. combinations.Add(new BurstOutputCombination("tempburstlibs/x86", new TargetCpus(BurstTargetCpu.X86_SSE4)));
  726. }
  727. if (AndroidHasX86_64(androidTargetArch))
  728. {
  729. combinations.Add(new BurstOutputCombination("tempburstlibs/x86_64", new TargetCpus(BurstTargetCpu.X64_SSE4)));
  730. }
  731. #endif
  732. }
  733. else if (targetPlatform == TargetPlatform.UWP)
  734. {
  735. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform);
  736. #if UNITY_2019_1_OR_NEWER
  737. if (EditorUserBuildSettings.wsaUWPBuildType == WSAUWPBuildType.ExecutableOnly)
  738. {
  739. combinations.Add(new BurstOutputCombination($"Plugins/{GetUWPTargetArchitecture()}", targetCpus));
  740. }
  741. else
  742. #endif
  743. {
  744. combinations.Add(new BurstOutputCombination("Plugins/x64", aotSettingsForTarget.GetDesktopCpu64Bit()));
  745. combinations.Add(new BurstOutputCombination("Plugins/x86", aotSettingsForTarget.GetDesktopCpu32Bit()));
  746. combinations.Add(new BurstOutputCombination("Plugins/ARM", new TargetCpus(BurstTargetCpu.THUMB2_NEON32)));
  747. combinations.Add(new BurstOutputCombination("Plugins/ARM64", new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64)));
  748. }
  749. }
  750. else if (targetPlatform == TargetPlatform.Lumin)
  751. {
  752. // Set the LUMINSDK_UNITY so bcl.exe will be able to find the SDK
  753. if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("LUMINSDK_UNITY")))
  754. {
  755. var sdkRoot = EditorPrefs.GetString("LuminSDKRoot");
  756. if (!string.IsNullOrEmpty(sdkRoot))
  757. {
  758. Environment.SetEnvironmentVariable("LUMINSDK_UNITY", sdkRoot);
  759. }
  760. }
  761. combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpus));
  762. }
  763. else if (targetPlatform == TargetPlatform.Switch)
  764. {
  765. combinations.Add(new BurstOutputCombination("NativePlugins/", targetCpus));
  766. }
  767. #if UNITY_2019_3_OR_NEWER
  768. else if (targetPlatform == TargetPlatform.Stadia)
  769. {
  770. combinations.Add(new BurstOutputCombination("NativePlugins", targetCpus));
  771. }
  772. #endif
  773. else
  774. {
  775. #if UNITY_2019_3_OR_NEWER
  776. if (targetPlatform == TargetPlatform.Windows)
  777. {
  778. // This is what is expected by PlatformDependent\Win\Plugins.cpp
  779. if (targetCpus.IsX86())
  780. {
  781. combinations.Add(new BurstOutputCombination("Data/Plugins/x86", targetCpus));
  782. }
  783. else
  784. {
  785. combinations.Add(new BurstOutputCombination("Data/Plugins/x86_64", targetCpus));
  786. }
  787. }
  788. else
  789. #endif
  790. {
  791. // Safeguard
  792. combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpus));
  793. }
  794. }
  795. return combinations;
  796. }
  797. private void PostProcessCombinations(TargetPlatform targetPlatform, List<BurstOutputCombination> combinations, BuildReport report)
  798. {
  799. #if UNITY_2020_2_OR_NEWER
  800. if (targetPlatform == TargetPlatform.macOS && combinations.Count > 1)
  801. {
  802. // Figure out which files we need to lipo
  803. string outputSymbolsDir = null;
  804. var outputDir = Path.Combine(TempStaging, Path.GetFileName(report.summary.outputPath), "Contents", "Plugins");
  805. var sliceCount = combinations.Count;
  806. var binarySlices = new string[sliceCount];
  807. var debugSymbolSlices = new string[sliceCount];
  808. for (int i = 0; i < sliceCount; i++)
  809. {
  810. var slice = combinations[i];
  811. var binaryFileName = slice.LibraryName + ".bundle";
  812. var binaryPath = Path.Combine(TempStaging, slice.OutputPath, binaryFileName);
  813. binarySlices[i] = binaryPath;
  814. // Only attempt to lipo symbols if they actually exist
  815. var dsymPath = binaryPath + ".dsym";
  816. var debugSymbolsPath = Path.Combine(dsymPath, "Contents", "Resources", "DWARF", binaryFileName);
  817. if (File.Exists(debugSymbolsPath))
  818. {
  819. if (string.IsNullOrWhiteSpace(outputSymbolsDir))
  820. {
  821. // Copy over the symbols from the first combination for metadata files which we aren't merging, like Info.plist
  822. var outputDsymPath = Path.Combine(outputDir, binaryFileName + ".dsym");
  823. FileUtil.CopyFileOrDirectory(dsymPath, outputDsymPath);
  824. outputSymbolsDir = Path.Combine(outputDsymPath, "Contents", "Resources", "DWARF");
  825. }
  826. debugSymbolSlices[i] = debugSymbolsPath;
  827. }
  828. }
  829. // lipo combinations together
  830. var outBinaryFileName = combinations[0].LibraryName + ".bundle";
  831. RunLipo(binarySlices, Path.Combine(outputDir, outBinaryFileName));
  832. if (!string.IsNullOrWhiteSpace(outputSymbolsDir))
  833. RunLipo(debugSymbolSlices, Path.Combine(outputSymbolsDir, outBinaryFileName));
  834. // Remove single-slice binary so they don't end up in the build
  835. for (int i = 0; i < sliceCount; i++)
  836. FileUtil.DeleteFileOrDirectory(Path.GetDirectoryName(binarySlices[i]));
  837. // Since we have combined the files, we need to adjust combinations for the next step
  838. var outFolder = Path.GetDirectoryName(combinations[0].OutputPath); // remove platform folder
  839. combinations.Clear();
  840. combinations.Add(new BurstOutputCombination(outFolder, new TargetCpus()));
  841. }
  842. #endif
  843. }
  844. private static void RunLipo(string[] inputFiles, string outputFile)
  845. {
  846. var outputDir = Path.GetDirectoryName(outputFile);
  847. Directory.CreateDirectory(outputDir);
  848. var cmdLine = new StringBuilder();
  849. foreach (var input in inputFiles)
  850. {
  851. if (string.IsNullOrEmpty(input))
  852. continue;
  853. cmdLine.Append(BclRunner.EscapeForShell(input));
  854. cmdLine.Append(' ');
  855. }
  856. cmdLine.Append("-create -output ");
  857. cmdLine.Append(BclRunner.EscapeForShell(outputFile));
  858. string lipoPath;
  859. var currentEditorPlatform = Application.platform;
  860. switch (currentEditorPlatform)
  861. {
  862. case RuntimePlatform.LinuxEditor:
  863. lipoPath = Path.Combine(BurstLoader.RuntimePath, "hostlin", "llvm-lipo");
  864. break;
  865. case RuntimePlatform.OSXEditor:
  866. lipoPath = Path.Combine(BurstLoader.RuntimePath, "hostmac", "llvm-lipo");
  867. break;
  868. case RuntimePlatform.WindowsEditor:
  869. lipoPath = Path.Combine(BurstLoader.RuntimePath, "hostwin", "llvm-lipo.exe");
  870. break;
  871. default:
  872. throw new NotSupportedException("Unknown Unity editor platform: " + currentEditorPlatform);
  873. }
  874. BclRunner.RunNativeProgram(lipoPath, cmdLine.ToString(), null);
  875. }
  876. private static Assembly[] GetPlayerAssemblies(BuildReport report)
  877. {
  878. // We need to build the list of root assemblies based from the "PlayerScriptAssemblies" folder.
  879. // This is so we compile the versions of the library built for the individual platforms, not the editor version.
  880. var oldOutputDir = EditorCompilationInterface.GetCompileScriptsOutputDirectory();
  881. try
  882. {
  883. EditorCompilationInterface.SetCompileScriptsOutputDirectory(LibraryPlayerScriptAssemblies);
  884. var shouldIncludeTestAssemblies = report.summary.options.HasFlag(BuildOptions.IncludeTestAssemblies);
  885. #if UNITY_2021_1_OR_NEWER
  886. // Workaround that with 'Server Build' ticked in the build options, since there is no 'AssembliesType.Server'
  887. // enum, we need to manually add the BuildingForHeadlessPlayer compilation option.
  888. #if UNITY_2021_2_OR_NEWER
  889. // A really really really gross hack - thanks Cristian Mazo! Querying the BuildOptions.EnableHeadlessMode is
  890. // obselete, but accessing its integer value is not... Note: this is just the temporary workaround to unblock
  891. // us (as of 1st June 2021, I say this with **much hope** that it is indeed temporary!).
  892. var flag = (BuildOptions)16384;
  893. #else
  894. var flag = BuildOptions.EnableHeadlessMode;
  895. #endif
  896. if (report.summary.options.HasFlag(flag))
  897. {
  898. var compilationOptions = EditorCompilationInterface.GetAdditionalEditorScriptCompilationOptions();
  899. compilationOptions |= EditorScriptCompilationOptions.BuildingForHeadlessPlayer;
  900. if (shouldIncludeTestAssemblies)
  901. {
  902. compilationOptions |= EditorScriptCompilationOptions.BuildingIncludingTestAssemblies;
  903. }
  904. return CompilationPipeline.ToAssemblies(CompilationPipeline.GetScriptAssemblies(EditorCompilationInterface.Instance, compilationOptions));
  905. }
  906. else
  907. {
  908. return CompilationPipeline.GetAssemblies(shouldIncludeTestAssemblies ? AssembliesType.Player : AssembliesType.PlayerWithoutTestAssemblies);
  909. }
  910. #elif UNITY_2019_3_OR_NEWER
  911. // Workaround that with 'Server Build' ticked in the build options, since there is no 'AssembliesType.Server'
  912. // enum, we need to manually add the 'UNITY_SERVER' define to the player assembly search list.
  913. if (report.summary.options.HasFlag(BuildOptions.EnableHeadlessMode))
  914. {
  915. var compilationOptions = EditorCompilationInterface.GetAdditionalEditorScriptCompilationOptions();
  916. if (shouldIncludeTestAssemblies)
  917. {
  918. compilationOptions |= EditorScriptCompilationOptions.BuildingIncludingTestAssemblies;
  919. }
  920. return CompilationPipeline.GetPlayerAssemblies(EditorCompilationInterface.Instance, compilationOptions, new string[] { "UNITY_SERVER" });
  921. }
  922. else
  923. {
  924. return CompilationPipeline.GetAssemblies(shouldIncludeTestAssemblies ? AssembliesType.Player : AssembliesType.PlayerWithoutTestAssemblies);
  925. }
  926. #else
  927. var compilationOptions = EditorCompilationInterface.GetAdditionalEditorScriptCompilationOptions();
  928. if (shouldIncludeTestAssemblies)
  929. {
  930. compilationOptions |= EditorScriptCompilationOptions.BuildingIncludingTestAssemblies;
  931. }
  932. #if UNITY_2019_2_OR_NEWER
  933. return CompilationPipeline.GetPlayerAssemblies(EditorCompilationInterface.Instance, compilationOptions, null);
  934. #else
  935. return CompilationPipeline.GetPlayerAssemblies(EditorCompilationInterface.Instance, compilationOptions);
  936. #endif
  937. #endif
  938. }
  939. finally
  940. {
  941. EditorCompilationInterface.SetCompileScriptsOutputDirectory(oldOutputDir); // restore output directory back to original value
  942. }
  943. }
  944. private static bool IsMonoReferenceAssemblyDirectory(string path)
  945. {
  946. var editorDir = Path.GetFullPath(EditorApplication.applicationContentsPath);
  947. return path.IndexOf(editorDir, StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("MonoBleedingEdge", StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("-api", StringComparison.OrdinalIgnoreCase) != -1;
  948. }
  949. private static bool IsDotNetStandardAssemblyDirectory(string path)
  950. {
  951. var editorDir = Path.GetFullPath(EditorApplication.applicationContentsPath);
  952. return path.IndexOf(editorDir, StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("netstandard", StringComparison.OrdinalIgnoreCase) != -1 && path.IndexOf("shims", StringComparison.OrdinalIgnoreCase) != -1;
  953. }
  954. private static TargetPlatform GetTargetPlatformAndDefaultCpu(BuildTarget target, out TargetCpus targetCpu)
  955. {
  956. var platform = TryGetTargetPlatform(target, out targetCpu);
  957. if (!platform.HasValue)
  958. {
  959. throw new NotSupportedException("The target platform " + target + " is not supported by the burst compiler");
  960. }
  961. return platform.Value;
  962. }
  963. private static bool IsSupportedPlatform(BuildTarget target)
  964. {
  965. return TryGetTargetPlatform(target, out var _).HasValue;
  966. }
  967. private static TargetPlatform? TryGetTargetPlatform(BuildTarget target, out TargetCpus targetCpus)
  968. {
  969. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(target);
  970. switch (target)
  971. {
  972. case BuildTarget.StandaloneWindows:
  973. targetCpus = aotSettingsForTarget.GetDesktopCpu32Bit();
  974. return TargetPlatform.Windows;
  975. case BuildTarget.StandaloneWindows64:
  976. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  977. return TargetPlatform.Windows;
  978. case BuildTarget.StandaloneOSX:
  979. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  980. return TargetPlatform.macOS;
  981. #if !UNITY_2019_2_OR_NEWER
  982. // 32 bit linux support was deprecated
  983. case BuildTarget.StandaloneLinux:
  984. targetCpus = aotSettingsForTarget.GetDesktopCpu32Bit();
  985. return TargetPlatform.Linux;
  986. #endif
  987. case BuildTarget.StandaloneLinux64:
  988. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  989. return TargetPlatform.Linux;
  990. case BuildTarget.WSAPlayer:
  991. {
  992. var uwpArchitecture = GetUWPTargetArchitecture();
  993. if (string.Equals(uwpArchitecture, "x64", StringComparison.OrdinalIgnoreCase))
  994. {
  995. targetCpus = aotSettingsForTarget.GetDesktopCpu64Bit();
  996. }
  997. else if (string.Equals(uwpArchitecture, "x86", StringComparison.OrdinalIgnoreCase))
  998. {
  999. targetCpus = aotSettingsForTarget.GetDesktopCpu32Bit();
  1000. }
  1001. else if (string.Equals(uwpArchitecture, "ARM", StringComparison.OrdinalIgnoreCase))
  1002. {
  1003. targetCpus = new TargetCpus(BurstTargetCpu.THUMB2_NEON32);
  1004. }
  1005. else if (string.Equals(uwpArchitecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1006. {
  1007. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1008. }
  1009. else
  1010. {
  1011. throw new InvalidOperationException("Unknown UWP CPU architecture: " + uwpArchitecture);
  1012. }
  1013. return TargetPlatform.UWP;
  1014. }
  1015. case BuildTarget.XboxOne:
  1016. targetCpus = new TargetCpus(BurstTargetCpu.X64_SSE4);
  1017. return TargetPlatform.XboxOne;
  1018. #if UNITY_2019_4_OR_NEWER
  1019. case BuildTarget.GameCoreXboxOne:
  1020. targetCpus = new TargetCpus(BurstTargetCpu.AVX);
  1021. return TargetPlatform.GameCoreXboxOne;
  1022. case BuildTarget.GameCoreXboxSeries:
  1023. targetCpus = new TargetCpus(BurstTargetCpu.AVX2);
  1024. return TargetPlatform.GameCoreXboxSeries;
  1025. #endif
  1026. case BuildTarget.PS4:
  1027. targetCpus = new TargetCpus(BurstTargetCpu.X64_SSE4);
  1028. return TargetPlatform.PS4;
  1029. case BuildTarget.Android:
  1030. targetCpus = new TargetCpus(BurstTargetCpu.ARMV7A_NEON32);
  1031. return TargetPlatform.Android;
  1032. case BuildTarget.iOS:
  1033. targetCpus = new TargetCpus(BurstTargetCpu.ARMV7A_NEON32);
  1034. return TargetPlatform.iOS;
  1035. case BuildTarget.tvOS:
  1036. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1037. return TargetPlatform.tvOS;
  1038. case BuildTarget.Lumin:
  1039. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1040. return TargetPlatform.Lumin;
  1041. case BuildTarget.Switch:
  1042. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1043. return TargetPlatform.Switch;
  1044. #if UNITY_2019_3_OR_NEWER
  1045. case BuildTarget.Stadia:
  1046. targetCpus = new TargetCpus(BurstTargetCpu.AVX2);
  1047. return TargetPlatform.Stadia;
  1048. case BuildTarget.PS5:
  1049. targetCpus = new TargetCpus(BurstTargetCpu.AVX2);
  1050. return TargetPlatform.PS5;
  1051. #endif
  1052. }
  1053. if (/*BuildTarget.EmbeddedLinux*/ 45 == (int)target)
  1054. {
  1055. //EmbeddedLinux is supported on 2019.4 (shadow branch), 2020.3 (shadow branch) and 2021.2+ (official).
  1056. var embeddedLinuxArchitecture = GetEmbeddedLinuxTargetArchitecture();
  1057. if ("Arm64" == embeddedLinuxArchitecture)
  1058. {
  1059. targetCpus = new TargetCpus(BurstTargetCpu.ARMV8A_AARCH64);
  1060. }
  1061. else if ("X64" == embeddedLinuxArchitecture)
  1062. {
  1063. targetCpus = new TargetCpus(BurstTargetCpu.X64_SSE2); //lowest supported for now
  1064. }
  1065. else if (("X86" == embeddedLinuxArchitecture) || ("Arm32" == embeddedLinuxArchitecture))
  1066. {
  1067. //32bit platforms cannot be support with the current SDK/Toolchain combination.
  1068. //i686-embedded-linux-gnu/8.3.0\libgcc.a(_moddi3.o + _divdi3.o): contains a compressed section, but zlib is not available
  1069. //_moddi3.o + _divdi3.o are required by LLVM for 64bit operations on 32bit platforms.
  1070. throw new InvalidOperationException($"No EmbeddedLinux Burst Support on {embeddedLinuxArchitecture} architecture.");
  1071. }
  1072. else
  1073. {
  1074. throw new InvalidOperationException("Unknown EmbeddedLinux CPU architecture: " + embeddedLinuxArchitecture);
  1075. }
  1076. return TargetPlatform.EmbeddedLinux;
  1077. }
  1078. targetCpus = new TargetCpus(BurstTargetCpu.Auto);
  1079. return null;
  1080. }
  1081. /// <summary>
  1082. /// Not exposed by Unity Editor today.
  1083. /// This is a copy of the Architecture enum from `PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs`
  1084. /// </summary>
  1085. private enum IOSArchitecture
  1086. {
  1087. ARMv7,
  1088. ARM64,
  1089. Universal
  1090. }
  1091. private static string GetUWPTargetArchitecture()
  1092. {
  1093. var architecture = EditorUserBuildSettings.wsaArchitecture;
  1094. if (string.Equals(architecture, "x64", StringComparison.OrdinalIgnoreCase) ||
  1095. string.Equals(architecture, "x86", StringComparison.OrdinalIgnoreCase) ||
  1096. string.Equals(architecture, "ARM", StringComparison.OrdinalIgnoreCase) ||
  1097. string.Equals(architecture, "ARM64", StringComparison.OrdinalIgnoreCase))
  1098. {
  1099. return architecture;
  1100. }
  1101. // Default to x64 if editor user build setting is garbage
  1102. return "x64";
  1103. }
  1104. private static string GetEmbeddedLinuxTargetArchitecture()
  1105. {
  1106. var flags = System.Reflection.BindingFlags.Public |
  1107. System.Reflection.BindingFlags.Static |
  1108. System.Reflection.BindingFlags.FlattenHierarchy;
  1109. var property = typeof(EditorUserBuildSettings).GetProperty("selectedEmbeddedLinuxArchitecture", flags);
  1110. if (null == property)
  1111. {
  1112. return "NOT_FOUND";
  1113. }
  1114. var value = (int)property.GetValue(null, null);
  1115. switch (value)
  1116. {
  1117. case /*UnityEditor.EmbeddedLinuxArchitecture.Arm64*/ 0: return "Arm64";
  1118. case /*UnityEditor.EmbeddedLinuxArchitecture.Arm32*/ 1: return "Arm32";
  1119. case /*UnityEditor.EmbeddedLinuxArchitecture.X64*/ 2: return "X64";
  1120. case /*UnityEditor.EmbeddedLinuxArchitecture.X86*/ 3: return "X86";
  1121. default: return $"UNKNOWN_{value}";
  1122. }
  1123. }
  1124. /// <summary>
  1125. /// Defines an output path (for the generated code) and the target CPU
  1126. /// </summary>
  1127. private struct BurstOutputCombination
  1128. {
  1129. public readonly TargetCpus TargetCpus;
  1130. public readonly string OutputPath;
  1131. public readonly string LibraryName;
  1132. public BurstOutputCombination(string outputPath, TargetCpus targetCpus, string libraryName = DefaultLibraryName)
  1133. {
  1134. TargetCpus = targetCpus.Clone();
  1135. OutputPath = outputPath;
  1136. LibraryName = libraryName;
  1137. }
  1138. public override string ToString()
  1139. {
  1140. return $"{nameof(TargetCpus)}: {TargetCpus}, {nameof(OutputPath)}: {OutputPath}, {nameof(LibraryName)}: {LibraryName}";
  1141. }
  1142. }
  1143. private class BclRunner
  1144. {
  1145. private static readonly Regex MatchVersion = new Regex(@"com.unity.burst@(\d+.*?)[\\/]");
  1146. public static void RunManagedProgram(string exe, string args, CompilerOutputParserBase parser)
  1147. {
  1148. RunManagedProgram(exe, args, Application.dataPath + "/..", parser);
  1149. }
  1150. private static void RunManagedProgram(
  1151. string exe,
  1152. string args,
  1153. string workingDirectory,
  1154. CompilerOutputParserBase parser)
  1155. {
  1156. Program p;
  1157. if (Application.platform == RuntimePlatform.WindowsEditor)
  1158. {
  1159. ProcessStartInfo si = new ProcessStartInfo()
  1160. {
  1161. Arguments = args,
  1162. CreateNoWindow = true,
  1163. FileName = exe
  1164. };
  1165. p = new Program(si);
  1166. }
  1167. else
  1168. {
  1169. p = (Program) new ManagedProgram(MonoInstallationFinder.GetMonoInstallation("MonoBleedingEdge"), (string) null, exe, args, false, null);
  1170. }
  1171. RunProgram(p, exe, args, workingDirectory, parser);
  1172. }
  1173. public static void RunNativeProgram(string exe, string args, CompilerOutputParserBase parser)
  1174. {
  1175. RunNativeProgram(exe, args, Application.dataPath + "/..", parser);
  1176. }
  1177. private static void RunNativeProgram(string exePath, string arguments, string workingDirectory, CompilerOutputParserBase parser)
  1178. {
  1179. // On non Windows platform, make sure that the command is executable
  1180. // This is a workaround - occasionally the execute bits are lost from our package
  1181. if (Application.platform != RuntimePlatform.WindowsEditor && Path.IsPathRooted(exePath))
  1182. {
  1183. var escapedExePath = EscapeForShell(exePath, singleQuoteWrapped: true);
  1184. var shArgs = $"-c '[ ! -x {escapedExePath} ] && chmod 755 {escapedExePath}'";
  1185. var p = new Program(new ProcessStartInfo("sh", shArgs) { CreateNoWindow = true});
  1186. p.GetProcessStartInfo().WorkingDirectory = workingDirectory;
  1187. p.Start();
  1188. p.WaitForExit();
  1189. }
  1190. var startInfo = new ProcessStartInfo(exePath, arguments);
  1191. startInfo.CreateNoWindow = true;
  1192. RunProgram(new Program(startInfo), exePath, arguments, workingDirectory, parser);
  1193. }
  1194. public static string EscapeForShell(string s, bool singleQuoteWrapped = false)
  1195. {
  1196. // On Windows it's enough to enclose the path in double quotes (double quotes are not allowed in paths)
  1197. if (Application.platform == RuntimePlatform.WindowsEditor) return $"\"{s}\"";
  1198. // On non-windows platforms we enclose in single-quotes and escape any existing single quotes with: '\'':
  1199. // John's Folder => 'John'\''s Folder'
  1200. var sb = new StringBuilder();
  1201. var escaped = s.Replace("'", "'\\''");
  1202. sb.Append('\'');
  1203. sb.Append(escaped);
  1204. sb.Append('\'');
  1205. // If the outer-context is already wrapped in single-quotes, we need to double escape things:
  1206. // John's Folder => 'John'\''s Folder'
  1207. // => '\''John'\''\'\'''\''s Folder'\''
  1208. if (singleQuoteWrapped)
  1209. {
  1210. // Pain
  1211. return sb.ToString().Replace("'", "'\\''");
  1212. }
  1213. return sb.ToString();
  1214. }
  1215. public static void RunProgram(
  1216. Program p,
  1217. string exe,
  1218. string args,
  1219. string workingDirectory,
  1220. CompilerOutputParserBase parser)
  1221. {
  1222. Stopwatch stopwatch = new Stopwatch();
  1223. stopwatch.Start();
  1224. using (p)
  1225. {
  1226. p.GetProcessStartInfo().WorkingDirectory = workingDirectory;
  1227. p.Start();
  1228. p.WaitForExit();
  1229. stopwatch.Stop();
  1230. Console.WriteLine("{0} exited after {1} ms.", (object)exe, (object)stopwatch.ElapsedMilliseconds);
  1231. IEnumerable<UnityEditor.Scripting.Compilers.CompilerMessage> compilerMessages = null;
  1232. string[] errorOutput = p.GetErrorOutput();
  1233. string[] standardOutput = p.GetStandardOutput();
  1234. if (parser != null)
  1235. {
  1236. compilerMessages = parser.Parse(errorOutput, standardOutput, true, "n/a (burst)");
  1237. }
  1238. var errorMessageBuilder = new StringBuilder();
  1239. if (compilerMessages != null)
  1240. {
  1241. foreach (UnityEditor.Scripting.Compilers.CompilerMessage compilerMessage in compilerMessages)
  1242. {
  1243. switch (compilerMessage.type)
  1244. {
  1245. case CompilerMessageType.Warning:
  1246. #if UNITY_2020_2_OR_NEWER
  1247. Debug.LogWarning(compilerMessage.message, compilerMessage.file, compilerMessage.line, compilerMessage.column);
  1248. #else
  1249. if (compilerMessage.file != "unknown")
  1250. {
  1251. Debug.LogWarning($"{compilerMessage.file}({compilerMessage.line},{compilerMessage.column}): {compilerMessage.message}");
  1252. }
  1253. else
  1254. {
  1255. Debug.LogWarning($"{compilerMessage.message}");
  1256. }
  1257. #endif
  1258. break;
  1259. case CompilerMessageType.Error:
  1260. Debug.LogPlayerBuildError(compilerMessage.message, compilerMessage.file, compilerMessage.line, compilerMessage.column);
  1261. break;
  1262. }
  1263. }
  1264. }
  1265. if (p.ExitCode != 0)
  1266. {
  1267. // We try to output the version in the heading error if we can
  1268. var matchVersion = MatchVersion.Match(exe);
  1269. errorMessageBuilder.Append(matchVersion.Success ?
  1270. "Burst compiler (" + matchVersion.Groups[1].Value + ") failed running" :
  1271. "Burst compiler failed running");
  1272. errorMessageBuilder.AppendLine();
  1273. errorMessageBuilder.AppendLine();
  1274. // Don't output the path if we are not burst-debugging or the exe exist
  1275. if (BurstLoader.IsDebugging || !File.Exists(exe))
  1276. {
  1277. errorMessageBuilder.Append(exe).Append(" ").Append(args);
  1278. errorMessageBuilder.AppendLine();
  1279. errorMessageBuilder.AppendLine();
  1280. }
  1281. errorMessageBuilder.AppendLine("stdout:");
  1282. foreach (string str in standardOutput)
  1283. errorMessageBuilder.AppendLine(str);
  1284. errorMessageBuilder.AppendLine("stderr:");
  1285. foreach (string str in errorOutput)
  1286. errorMessageBuilder.AppendLine(str);
  1287. throw new BuildFailedException(errorMessageBuilder.ToString());
  1288. }
  1289. Console.WriteLine(p.GetAllOutput());
  1290. }
  1291. }
  1292. }
  1293. /// <summary>
  1294. /// Internal class used to parse bcl output errors
  1295. /// </summary>
  1296. private class BclOutputErrorParser : CompilerOutputParserBase
  1297. {
  1298. // Format of an error message:
  1299. //
  1300. //C:\work\burst\src\Burst.Compiler.IL.Tests\Program.cs(17,9): error: Loading a managed string literal is not supported by burst
  1301. // at Buggy.NiceBug() (at C:\work\burst\src\Burst.Compiler.IL.Tests\Program.cs:17)
  1302. //
  1303. //
  1304. // [1] [2] [3] [4] [5]
  1305. // path line col type message
  1306. private static readonly Regex MatchLocation = new Regex(@"^(.*?)\((\d+)\s*,\s*(\d+)\):\s*([\w\s]+)\s*:\s*(.*)");
  1307. // Matches " at "
  1308. private static readonly Regex MatchAt = new Regex(@"^\s+at\s+");
  1309. public override IEnumerable<UnityEditor.Scripting.Compilers.CompilerMessage> Parse(
  1310. string[] errorOutput,
  1311. string[] standardOutput,
  1312. bool compilationHadFailure,
  1313. string assemblyName)
  1314. {
  1315. var messages = new List<UnityEditor.Scripting.Compilers.CompilerMessage>();
  1316. var textBuilder = new StringBuilder();
  1317. for (var i = 0; i < errorOutput.Length; i++)
  1318. {
  1319. string line = errorOutput[i];
  1320. var message = new UnityEditor.Scripting.Compilers.CompilerMessage {assemblyName = assemblyName};
  1321. // If we are able to match a location, we can decode it including the following attached " at " lines
  1322. textBuilder.Clear();
  1323. var match = MatchLocation.Match(line);
  1324. if (match.Success)
  1325. {
  1326. var path = match.Groups[1].Value;
  1327. int.TryParse(match.Groups[2].Value, out message.line);
  1328. int.TryParse(match.Groups[3].Value, out message.column);
  1329. if (match.Groups[4].Value.Contains("error"))
  1330. {
  1331. message.type = CompilerMessageType.Error;
  1332. }
  1333. else
  1334. {
  1335. message.type = CompilerMessageType.Warning;
  1336. }
  1337. message.file = !string.IsNullOrEmpty(path) ? path : "unknown";
  1338. // Replace '\' with '/' to let the editor open the file
  1339. message.file = message.file.Replace('\\', '/');
  1340. // Make path relative to project path path
  1341. var projectPath = Path.GetDirectoryName(Application.dataPath)?.Replace('\\', '/');
  1342. if (projectPath != null && message.file.StartsWith(projectPath))
  1343. {
  1344. message.file = message.file.Substring(projectPath.EndsWith("/") ? projectPath.Length : projectPath.Length + 1);
  1345. }
  1346. // debug
  1347. // textBuilder.AppendLine("line: " + message.line + " column: " + message.column + " error: " + message.type + " file: " + message.file);
  1348. textBuilder.Append(match.Groups[5].Value);
  1349. }
  1350. else
  1351. {
  1352. // Don't output any blank line
  1353. if (string.IsNullOrWhiteSpace(line))
  1354. {
  1355. continue;
  1356. }
  1357. // Otherwise we output an error, but without source location information
  1358. // so that at least the user can see it directly in the log errors
  1359. message.type = CompilerMessageType.Error;
  1360. message.line = 0;
  1361. message.column = 0;
  1362. message.file = "unknown";
  1363. textBuilder.Append(line);
  1364. }
  1365. // Collect attached location call context information ("at ...")
  1366. // we do it for both case (as if we have an exception in bcl we want to print this in a single line)
  1367. bool isFirstAt = true;
  1368. for (int j = i + 1; j < errorOutput.Length; j++)
  1369. {
  1370. var nextLine = errorOutput[j];
  1371. // Empty lines are ignored by the stack trace parser.
  1372. if (string.IsNullOrWhiteSpace(nextLine))
  1373. {
  1374. i++;
  1375. continue;
  1376. }
  1377. if (MatchAt.Match(nextLine).Success)
  1378. {
  1379. i++;
  1380. if (isFirstAt)
  1381. {
  1382. textBuilder.AppendLine();
  1383. isFirstAt = false;
  1384. }
  1385. textBuilder.AppendLine(nextLine);
  1386. }
  1387. else
  1388. {
  1389. break;
  1390. }
  1391. }
  1392. message.message = textBuilder.ToString();
  1393. messages.Add(message);
  1394. }
  1395. return messages;
  1396. }
  1397. protected override string GetErrorIdentifier()
  1398. {
  1399. throw new NotImplementedException(); // as we overriding the method Parse()
  1400. }
  1401. protected override Regex GetOutputRegex()
  1402. {
  1403. throw new NotImplementedException(); // as we overriding the method Parse()
  1404. }
  1405. }
  1406. #if UNITY_EDITOR_OSX
  1407. private class StaticLibraryPostProcessor
  1408. {
  1409. private const string TempSourceLibrary = @"Temp/StagingArea/StaticLibraries";
  1410. [PostProcessBuildAttribute(1)]
  1411. public static void OnPostProcessBuild(BuildTarget target, string path)
  1412. {
  1413. // Early out if we are building for the simulator, as we don't
  1414. //currently generate burst libraries that will work for that.
  1415. if (IsForSimulator(target))
  1416. {
  1417. return;
  1418. }
  1419. // We only support AOT compilation for ios from a macos host (we require xcrun and the apple tool chains)
  1420. //for other hosts, we simply act as if burst is not being used (an error will be generated by the build aot step)
  1421. //this keeps the behaviour consistent with how it was before static linkage was introduced
  1422. if (target == BuildTarget.iOS)
  1423. {
  1424. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.iOS);
  1425. // Early exit if burst is not activated
  1426. if (!aotSettingsForTarget.EnableBurstCompilation)
  1427. {
  1428. return;
  1429. }
  1430. PostAddStaticLibraries(path);
  1431. }
  1432. if (target == BuildTarget.tvOS)
  1433. {
  1434. var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.tvOS);
  1435. // Early exit if burst is not activated
  1436. if (!aotSettingsForTarget.EnableBurstCompilation)
  1437. {
  1438. return;
  1439. }
  1440. PostAddStaticLibraries(path);
  1441. }
  1442. }
  1443. private static void PostAddStaticLibraries(string path)
  1444. {
  1445. var assm = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(assembly =>
  1446. assembly.GetName().Name == "UnityEditor.iOS.Extensions.Xcode");
  1447. Type PBXType = assm?.GetType("UnityEditor.iOS.Xcode.PBXProject");
  1448. Type PBXSourceTree = assm?.GetType("UnityEditor.iOS.Xcode.PBXSourceTree");
  1449. if (PBXType != null && PBXSourceTree != null)
  1450. {
  1451. var project = Activator.CreateInstance(PBXType, null);
  1452. var _sGetPBXProjectPath = PBXType.GetMethod("GetPBXProjectPath");
  1453. var _ReadFromFile = PBXType.GetMethod("ReadFromFile");
  1454. var _sGetUnityTargetName = PBXType.GetMethod("GetUnityTargetName");
  1455. var _AddFileToBuild = PBXType.GetMethod("AddFileToBuild");
  1456. var _AddFile = PBXType.GetMethod("AddFile");
  1457. var _WriteToString = PBXType.GetMethod("WriteToString");
  1458. var sourcetree = new EnumConverter(PBXSourceTree).ConvertFromString("Source");
  1459. string sPath = (string)_sGetPBXProjectPath?.Invoke(null, new object[] { path });
  1460. _ReadFromFile?.Invoke(project, new object[] { sPath });
  1461. #if UNITY_2019_3_OR_NEWER
  1462. var _TargetGuidByName = PBXType.GetMethod("GetUnityFrameworkTargetGuid");
  1463. string g = (string) _TargetGuidByName?.Invoke(project, null);
  1464. #else
  1465. var _TargetGuidByName = PBXType.GetMethod("TargetGuidByName");
  1466. string tn = (string) _sGetUnityTargetName?.Invoke(null, null);
  1467. string g = (string) _TargetGuidByName?.Invoke(project, new object[] {tn});
  1468. #endif
  1469. var srcPath = TempSourceLibrary;
  1470. var dstPath = "Libraries";
  1471. var dstCopyPath = Path.Combine(path, dstPath);
  1472. var burstCppLinkFile = "lib_burst_generated.cpp";
  1473. var lib32Name = $"{DefaultLibraryName}32.a";
  1474. var lib64Name = $"{DefaultLibraryName}64.a";
  1475. var lib32SrcPath = Path.Combine(srcPath, lib32Name);
  1476. var lib64SrcPath = Path.Combine(srcPath, lib64Name);
  1477. var lib32Exists = File.Exists(lib32SrcPath);
  1478. var lib64Exists = File.Exists(lib64SrcPath);
  1479. var numLibs = (lib32Exists?1:0)+(lib64Exists?1:0);
  1480. if (numLibs==0)
  1481. {
  1482. return; // No libs, so don't write the cpp either
  1483. }
  1484. var libsCombine=new string [numLibs];
  1485. var libsIdx=0;
  1486. if (lib32Exists) libsCombine[libsIdx++] = lib32SrcPath;
  1487. if (lib64Exists) libsCombine[libsIdx++] = lib64SrcPath;
  1488. // Combine the static libraries into a single file to support newer xcode build systems
  1489. var libName = $"{DefaultLibraryName}.a";
  1490. RunLipo(libsCombine, Path.Combine(dstCopyPath, libName));
  1491. AddLibToProject(project, _AddFileToBuild, _AddFile, sourcetree, g, dstPath, libName);
  1492. // Additionally we need a small cpp file (weak symbols won't unfortunately override directly from the libs
  1493. //presumably due to link order?
  1494. string cppPath = Path.Combine(dstCopyPath, burstCppLinkFile);
  1495. File.WriteAllText(cppPath, @"
  1496. extern ""C""
  1497. {
  1498. void Staticburst_initialize(void* );
  1499. void* StaticBurstStaticMethodLookup(void* );
  1500. int burst_enable_static_linkage = 1;
  1501. void burst_initialize(void* i) { Staticburst_initialize(i); }
  1502. void* BurstStaticMethodLookup(void* i) { return StaticBurstStaticMethodLookup(i); }
  1503. }
  1504. ");
  1505. cppPath = Path.Combine(dstPath, burstCppLinkFile);
  1506. string fileg = (string)_AddFile?.Invoke(project, new object[] { cppPath, cppPath, sourcetree });
  1507. _AddFileToBuild?.Invoke(project, new object[] { g, fileg });
  1508. string pstring = (string)_WriteToString?.Invoke(project, null);
  1509. File.WriteAllText(sPath, pstring);
  1510. }
  1511. }
  1512. private static void AddLibToProject(object project, System.Reflection.MethodInfo _AddFileToBuild, System.Reflection.MethodInfo _AddFile, object sourcetree, string g, string dstPath, string lib32Name)
  1513. {
  1514. string fg = (string)_AddFile?.Invoke(project,
  1515. new object[] { Path.Combine(dstPath, lib32Name), Path.Combine(dstPath, lib32Name), sourcetree });
  1516. _AddFileToBuild?.Invoke(project, new object[] { g, fg });
  1517. }
  1518. }
  1519. #endif
  1520. }
  1521. }
  1522. #endif