Files
Bachelor-Arbeit-Adrian-Haefner/Library/PackageCache/com.unity.xr.arkit@58a677f717be/Editor/ARKitBuildProcessor.cs
adriadri6972 d3d9c5f833 upload project
2025-07-31 15:21:08 +02:00

392 lines
17 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Rendering;
using UnityEditor.XR.ARSubsystems;
using UnityEditor.XR.Management;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.XR.ARKit;
using Unity.EditorCoroutines.Editor;
#if UNITY_IOS
using System.IO;
using UnityEditor.iOS;
using UnityEditor.iOS.Xcode;
#endif
using OSVersion = UnityEngine.XR.ARKit.OSVersion;
namespace UnityEditor.XR.ARKit
{
static class ARKitBuildProcessor
{
public static bool loaderEnabled;
public static bool faceTrackingEnabled;
class Preprocessor : IPreprocessBuildWithReport, IPreprocessShaders
{
// Magic value according to
// https://docs.unity3d.com/ScriptReference/PlayerSettings.GetArchitecture.html
// "0 - None, 1 - ARM64, 2 - Universal."
const int k_TargetArchitectureArm64 = 1;
const int k_TargetArchitectureUniversal = 2;
// The minimum target Xcode version for the plug-in
const int k_TargetMinimumMajorXcodeVersion = 11;
const int k_TargetMinimumMinorXcodeVersion = 0;
const int k_TargetMinimumPatchXcodeVersion = 0;
readonly string[] runtimePluginNames =
{
"libUnityARKit.a",
"UnityARKit.m",
"libUnityARKitFaceTracking.a",
};
public int callbackOrder => 0;
void IPreprocessShaders.OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
#if UNITY_IOS && UNITY_XR_ARKIT_LOADER_ENABLED
ProcessShader(shader, snippet, data);
#endif
}
static void ProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
// Remove shader variants for the camera background shader that will fail compilation because of package dependencies.
foreach (var backgroundShaderName in ARKitCameraSubsystem.backgroundShaderNames)
{
if (backgroundShaderName.Equals(shader.name))
{
foreach (var backgroundShaderKeywordToNotCompile in ARKitCameraSubsystem.backgroundShaderKeywordsToNotCompile)
{
ShaderKeyword shaderKeywordToNotCompile = new ShaderKeyword(shader, backgroundShaderKeywordToNotCompile);
for (int i = (data.Count - 1); i >= 0; --i)
{
if (data[i].shaderKeywordSet.IsEnabled(shaderKeywordToNotCompile))
{
data.RemoveAt(i);
}
}
}
}
}
}
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
{
// Sets delegate to return whether or not a plugin should be included in the build
SetRuntimePluginCopyDelegate();
#if UNITY_IOS && UNITY_XR_ARKIT_LOADER_ENABLED
PreprocessBuild(report);
#elif UNITY_XR_ARKIT_LOADER_ENABLED
if (report.summary.platform == BuildTarget.iOS)
{
Debug.LogWarning(
"Apple ARKit XR Plug-in requires the UNITY_IOS preprocessor directive to build a correctly " +
"configured app for XR. You are building for the iOS platform with the Apple ARKit XR Plug-in " +
"enabled, but Unity's active build target is not set to iOS. This may result in runtime " +
"errors in your build.\n To ensure that your build is correctly configured, set Unity's " +
"active build target to iOS and reload the domain before building, or use the " +
"'-buildTarget iOS' command line argument when building in batch mode.");
}
#endif
}
// ReSharper disable once UnusedMember.Local
void SetRuntimePluginCopyDelegate()
{
var allPlugins = PluginImporter.GetAllImporters();
foreach (var plugin in allPlugins)
{
if (plugin.isNativePlugin)
{
foreach (var pluginName in runtimePluginNames)
{
if (plugin.assetPath.Contains(pluginName))
{
plugin.SetIncludeInBuildDelegate(ShouldIncludeRuntimePluginsInBuild);
break;
}
}
}
}
}
// ReSharper disable once UnusedMember.Local
static void PreprocessBuild(BuildReport report)
{
if (Application.isBatchMode)
{
// by the time we reach the preprocessor in batch mode the AssetDatabase is ready to load our iOS XR
// settings, so we manually update the arkit defines here.
LoaderEnabledCheck.UpdateARKitDefines();
}
if (report.summary.platform != BuildTarget.iOS || !loaderEnabled)
return;
if (string.IsNullOrEmpty(PlayerSettings.iOS.cameraUsageDescription))
throw new BuildFailedException(
"ARKit requires a Camera Usage Description (Player Settings > iOS > Other Settings > Camera Usage Description)");
EnsureMinimumXcodeVersion();
EnsureMetalIsFirstApi();
if (ARKitSettings.GetOrCreateSettings().requirement == ARKitSettings.Requirement.Required)
{
EnsureMinimumBuildTarget();
EnsureTargetArchitecturesAreSupported(report.summary.platformGroup);
}
foreach (var backgroundShaderName in ARKitCameraSubsystem.backgroundShaderNames)
{
BuildHelper.AddBackgroundShaderToProject(backgroundShaderName);
}
}
static void EnsureMinimumBuildTarget()
{
var userSetTargetVersion = OSVersion.Parse(PlayerSettings.iOS.targetOSVersionString);
if (userSetTargetVersion < new OSVersion(11))
{
throw new BuildFailedException(
$"You have selected a minimum target iOS version of {userSetTargetVersion} and have " +
" the Apple ARKit XR Plug-in package installed. ARKit requires at least iOS version 11.0. " +
"See Player Settings > Other Settings > Target minimum iOS Version.");
}
}
static void EnsureMinimumXcodeVersion()
{
#if UNITY_IOS && UNITY_EDITOR_OSX
var xcodeIndex = Math.Max(0, XcodeApplications.GetPreferedXcodeIndex());
var xcodeVersion = OSVersion.Parse(XcodeApplications.GetXcodeApplicationPublicName(xcodeIndex));
if (xcodeVersion == new OSVersion(0))
throw new BuildFailedException(
$"Could not determine which version of Xcode was selected in the Build Settings. Xcode app was computed as \"{XcodeApplications.GetXcodeApplicationPublicName(xcodeIndex)}\".");
if (xcodeVersion < new OSVersion(
k_TargetMinimumMajorXcodeVersion,
k_TargetMinimumMinorXcodeVersion,
k_TargetMinimumPatchXcodeVersion))
throw new BuildFailedException(
$"The selected Xcode version: {xcodeVersion} is below the minimum Xcode required Xcode version for the Apple ARKit XR Plug-in. Please target at least Xcode version {k_TargetMinimumMajorXcodeVersion}.{k_TargetMinimumMinorXcodeVersion}.{k_TargetMinimumPatchXcodeVersion}.");
#endif
}
static void EnsureTargetArchitecturesAreSupported(BuildTargetGroup buildTargetGroup)
{
var buildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
if (PlayerSettings.GetArchitecture(buildTarget) != k_TargetArchitectureArm64)
throw new BuildFailedException(
"Apple ARKit XR Plug-in only supports the ARM64 architecture. " +
"See Player Settings > Other Settings > Architecture.");
}
static void EnsureMetalIsFirstApi()
{
var graphicsApis = PlayerSettings.GetGraphicsAPIs(BuildTarget.iOS);
if (graphicsApis.Length > 0 && graphicsApis[0] != GraphicsDeviceType.Metal)
{
throw new BuildFailedException($"You currently have {graphicsApis[0]} at the top of the list of Graphics APis. However, Metal needs to be first in the list. (See Player Settings > Other Settings > Graphics APIs)");
}
}
static bool ShouldIncludeRuntimePluginsInBuild(string path)
{
if (!loaderEnabled)
return false;
if (path.Contains("libUnityARKitFaceTracking.a"))
return faceTrackingEnabled;
return true;
}
}
// Our PostProcessor depends on UnityEditor.iOS.PlistDocument, a class in Unity's iOS module.
// To ensure that this package successfully compiles in projects that don't have the iOS module installed, we must
// wrap the PostProcessor with a UNITY_IOS preprocessor directive.
#if UNITY_IOS
class PostProcessor : IPostprocessBuildWithReport
{
// Needs to be > 0 to make sure we remove the shader since the Input System overwrites the preloaded assets array
public int callbackOrder => 1;
void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report)
{
#if UNITY_XR_ARKIT_LOADER_ENABLED
PostprocessBuild(report);
#endif
}
static void PostprocessBuild(BuildReport report)
{
if (report.summary.platform != BuildTarget.iOS)
{
return;
}
foreach (var shaderName in ARKitCameraSubsystem.backgroundShaderNames)
BuildHelper.RemoveShaderFromProject(shaderName);
HandleARKitRequiredFlag(report.summary.outputPath);
}
static void HandleARKitRequiredFlag(string pathToBuiltProject)
{
var arkitSettings = ARKitSettings.GetOrCreateSettings();
string plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath));
PlistElementDict rootDict = plist.root;
// Get or create array to manage device capabilities
const string capsKey = "UIRequiredDeviceCapabilities";
PlistElementArray capsArray;
if (rootDict.values.TryGetValue(capsKey, out PlistElement pel))
{
capsArray = pel.AsArray();
}
else
{
capsArray = rootDict.CreateArray(capsKey);
}
// Remove any existing "arkit" plist entries
const string arkitStr = "arkit";
capsArray.values.RemoveAll(x => arkitStr.Equals(x.AsString()));
if (arkitSettings.requirement == ARKitSettings.Requirement.Required)
{
// Add "arkit" plist entry
capsArray.AddString(arkitStr);
}
File.WriteAllText(plistPath, plist.WriteToString());
// update for Unity-iOS.xcodeproj's Build Setting
var projectPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
var project = new PBXProject();
project.ReadFromFile(projectPath);
// target : unity-iphone
string targetGUID = project.GetUnityMainTargetGuid();
project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-ld64");
project.SetBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
project.AddBuildProperty(targetGUID, "LD_RUNPATH_SEARCH_PATHS", "/usr/lib/swift");
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)");
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)");
// target : unityframework
targetGUID = project.GetUnityFrameworkTargetGuid();
project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-ld64");
project.SetBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
project.AddBuildProperty(targetGUID, "LD_RUNPATH_SEARCH_PATHS", "/usr/lib/swift");
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "/usr/lib/swift");
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)");
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)");
// After we're done editing the build settings we save it
project.WriteToFile(projectPath);
}
}
#endif // UNITY_IOS
}
[InitializeOnLoad]
class LoaderEnabledCheck
{
static readonly NamedBuildTarget s_iOSTarget =
NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(BuildTarget.iOS));
static ARKitSettings s_ARKitSettings;
static LoaderEnabledCheck()
{
s_ARKitSettings = ARKitSettings.GetOrCreateSettings();
ARKitBuildProcessor.loaderEnabled = false;
// in batch mode, the AssetDatabase may not yet be properly initialized and trying to load the xr settings
// and check the loader won't work. This can leave us in a state where the build processor can't execute
// properly down the line even though the loader is enabled in the settings
if (Application.isBatchMode)
return;
UpdateARKitDefines();
EditorCoroutineUtility.StartCoroutineOwnerless(UpdateARKitDefinesCoroutine());
}
static IEnumerator UpdateARKitDefinesCoroutine()
{
var waitObj = new EditorWaitForSeconds(.25f);
while (true)
{
UpdateARKitDefines();
yield return waitObj;
}
}
internal static void UpdateARKitDefines()
{
var iOSXRSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(
BuildPipeline.GetBuildTargetGroup(BuildTarget.iOS));
if (iOSXRSettings == null)
return;
var arkitLoaderShouldBeEnabled = iOSXRSettings.Manager.activeLoaders.OfType<ARKitLoader>().Any();
if (arkitLoaderShouldBeEnabled && !ARKitBuildProcessor.loaderEnabled)
{
AddDefine("UNITY_XR_ARKIT_LOADER_ENABLED");
}
else if (!arkitLoaderShouldBeEnabled && ARKitBuildProcessor.loaderEnabled)
{
RemoveDefine("UNITY_XR_ARKIT_LOADER_ENABLED");
}
if (arkitLoaderShouldBeEnabled && s_ARKitSettings.faceTracking && !ARKitBuildProcessor.faceTrackingEnabled)
{
AddDefine("UNITY_XR_ARKIT_FACE_TRACKING_ENABLED");
}
else if (!s_ARKitSettings.faceTracking && ARKitBuildProcessor.faceTrackingEnabled)
{
RemoveDefine("UNITY_XR_ARKIT_FACE_TRACKING_ENABLED");
}
ARKitBuildProcessor.loaderEnabled = arkitLoaderShouldBeEnabled;
ARKitBuildProcessor.faceTrackingEnabled = arkitLoaderShouldBeEnabled && s_ARKitSettings.faceTracking;
}
static void AddDefine(string define)
{
var definesString = PlayerSettings.GetScriptingDefineSymbols(s_iOSTarget);
var allDefines = new HashSet<string>(definesString.Split(';'));
if (allDefines.Contains(define))
return;
allDefines.Add(define);
PlayerSettings.SetScriptingDefineSymbols(s_iOSTarget, string.Join(";", allDefines));
}
static void RemoveDefine(string define)
{
var definesString = PlayerSettings.GetScriptingDefineSymbols(s_iOSTarget);
var allDefines = new HashSet<string>(definesString.Split(';'));
allDefines.Remove(define);
PlayerSettings.SetScriptingDefineSymbols(s_iOSTarget, string.Join(";", allDefines));
}
}
}