internal static void StripAssemblies(string managedAssemblyFolderPath, BaseUnityLinkerPlatformProvider unityLinkerPlatformProvider, IIl2CppPlatformProvider il2cppPlatformProvider, RuntimeClassRegistry rcr, ManagedStrippingLevel managedStrippingLevel) { var runInformation = new UnityLinkerRunInformation(managedAssemblyFolderPath, unityLinkerPlatformProvider, il2cppPlatformProvider.target, rcr, managedStrippingLevel, il2cppPlatformProvider); RunAssemblyStripper(runInformation); }
private static string WriteTypesInScenesBlacklist(UnityLinkerRunInformation runInformation, BuildFile[] buildFiles) { var items = runInformation.rcr.GetAllManagedTypesInScenes(); var sb = new StringBuilder(); sb.AppendLine("<linker>"); foreach (var assemblyTypePair in items.OrderBy(t => t.Key)) { // Some how stuff for assemblies that will not be in the build make it into UsedTypePerUserAssembly such as // ex: [UnityEditor.TestRunner.dll] UnityEditor.TestTools.TestRunner.TestListCacheData // // Filter anything out where the assembly doesn't exist so that UnityLinker can be strict about preservations in link xml files var filename = assemblyTypePair.Key.ToNPath().FileNameWithoutExtension; if (buildFiles.All(file => !BuildFileMatchesAssembly(file, filename))) { continue; } sb.AppendLine($"\t<assembly fullname=\"{filename}\">"); foreach (var type in assemblyTypePair.Value.OrderBy(s => s)) { sb.AppendLine($"\t\t<type fullname=\"{type}\" preserve=\"nothing\"/>"); } sb.AppendLine("\t</assembly>"); } sb.AppendLine("</linker>"); var path = Path.Combine(runInformation.managedAssemblyFolderPath, "TypesInScenes.xml"); File.WriteAllText(path, sb.ToString()); return(path); }
public static List <string> GetLinkXmlFiles(UnityLinkerRunInformation runInformation) { var managedAssemblyFolderPath = runInformation.managedAssemblyFolderPath; var linkXmlFiles = new List <string>(); linkXmlFiles.AddRange(Il2CppBlacklistPaths); if (runInformation.rcr != null) { var buildFiles = runInformation.BuildReport.GetFiles(); linkXmlFiles.Add(WriteMethodsToPreserveBlackList(runInformation)); linkXmlFiles.Add(WriteTypesInScenesBlacklist(runInformation, buildFiles)); linkXmlFiles.Add(WriteSerializedTypesBlacklist(runInformation, buildFiles)); } linkXmlFiles.AddRange(ProcessBuildPipelineGenerateAdditionalLinkXmlFiles(runInformation)); linkXmlFiles.AddRange(GetUserBlacklistFiles()); if (runInformation.isMonoBackend) { // The old Mono assembly stripper uses per-platform link.xml files if available. Apply these here. var buildToolsDirectory = BuildPipeline.GetBuildToolsDirectory(runInformation.target); if (!string.IsNullOrEmpty(buildToolsDirectory)) { var platformDescriptor = Path.Combine(buildToolsDirectory, "link.xml"); if (File.Exists(platformDescriptor)) { linkXmlFiles.Add(platformDescriptor); } } } return(SanitizeLinkXmlFilePaths(linkXmlFiles, runInformation).ToList()); }
private static string WriteMethodsToPreserveBlackList(UnityLinkerRunInformation runInformation) { var contents = GetMethodPreserveBlacklistContents(runInformation.rcr); if (contents == null) { return(null); } var methodPreserveBlackList = Path.Combine(runInformation.managedAssemblyFolderPath, "MethodsToPreserve.xml"); File.WriteAllText(methodPreserveBlackList, contents); return(methodPreserveBlackList); }
static void ProcessBuildPipelineOnAfterRun(UnityLinkerRunInformation runInformation) { var processors = BuildPipelineInterfaces.processors.unityLinkerProcessors; if (processors == null) { return; } foreach (var processor in processors) { processor.OnAfterRun(runInformation.BuildReport, runInformation.pipelineData); } }
private static void UpdateBuildReport(LinkerToEditorData dataFromLinker, UnityLinkerRunInformation runInformation) { var strippingInfo = runInformation.BuildReportData; if (strippingInfo == null) { return; } foreach (var moduleInfo in dataFromLinker.report.modules) { strippingInfo.AddModule(moduleInfo.name); foreach (var moduleDependency in moduleInfo.dependencies) { strippingInfo.RegisterDependency(StrippingInfo.ModuleName(moduleInfo.name), moduleDependency.name); if (!string.IsNullOrEmpty(moduleDependency.icon)) { strippingInfo.SetIcon(moduleDependency.name, moduleDependency.icon); } // Hacky way to match the existing behavior if (moduleDependency.name == "UnityConnectSettings") { strippingInfo.RegisterDependency(moduleDependency.name, "Required by UnityAnalytics"); } foreach (var scene in moduleDependency.scenes) { strippingInfo.RegisterDependency(moduleDependency.name, scene); var klass = UnityType.FindTypeByName(moduleDependency.name); if (klass != null && !klass.IsDerivedFrom(CodeStrippingUtils.GameManagerTypeInfo)) { if (scene.EndsWith(".unity")) { strippingInfo.SetIcon(scene, "class/SceneAsset"); } else { strippingInfo.SetIcon(scene, "class/AssetBundle"); } } } } } }
static List <string> ProcessBuildPipelineGenerateAdditionalLinkXmlFiles(UnityLinkerRunInformation runInformation) { var results = new List <string>(); var processors = BuildPipelineInterfaces.processors.unityLinkerProcessors; if (processors == null) { return(results); } foreach (var processor in processors) { results.Add(processor.GenerateAdditionalLinkXmlFile(runInformation.BuildReport, runInformation.pipelineData)); } return(results); }
private static void WriteEditorData(UnityLinkerRunInformation runInformation) { var items = GetTypesInScenesInformation(runInformation.managedAssemblyFolderPath, runInformation.rcr); List <string> forceIncludeModules; List <string> forceExcludeModules; CollectIncludedAndExcludedModules(out forceIncludeModules, out forceExcludeModules); var editorToLinkerData = new EditorToLinkerData { typesInScenes = items.ToArray(), allNativeTypes = CollectNativeTypeData().ToArray(), forceIncludeModules = forceIncludeModules.ToArray(), forceExcludeModules = forceExcludeModules.ToArray() }; File.WriteAllText(runInformation.EditorToLinkerDataPath, JsonUtility.ToJson(editorToLinkerData, true)); }
static List <string> ProcessBuildPipelineGenerateAdditionalLinkXmlFiles(UnityLinkerRunInformation runInformation) { var results = new List <string>(); var processors = BuildPipelineInterfaces.processors.unityLinkerProcessors; if (processors == null) { return(results); } foreach (var processor in processors) { results.Add(processor.GenerateAdditionalLinkXmlFile(runInformation.BuildReport, runInformation.pipelineData)); var processorType = processor.GetType(); // The OnBeforeRun and OnAfterRun methods are no longer supported. We warn if the project uses them. // But since these were interface methods, any project using GenerateAdditionalLinkXmlFile also had to // implement these. So we only want to warn if the methods are not empty. // // To detect if we should consider a method as "empty" we check if the method body has more than 2 bytes. // An empty method is 1 byte (ret), or 2 bytes in debug mode (nop, ret). The assumption is that there is // no viable void method with side effects having only 2 bytes. var onBeforeRun = processorType.GetMethod("OnBeforeRun"); if (onBeforeRun != null && onBeforeRun.GetMethodBody().GetILAsByteArray().Length > 2) { Debug.LogWarning($"{processorType} has a non-empty OnBeforeRun method, but IUnityLinkerProcessor.OnBeforeRun is no longer supported."); } var onAfterRun = processorType.GetMethod("OnAfterRun"); if (onAfterRun != null && onAfterRun.GetMethodBody().GetILAsByteArray().Length > 2) { Debug.LogWarning($"{processorType} has a non-empty OnAfterRun method, but IUnityLinkerProcessor.OnAfterRun is no longer supported."); } } return(results); }
private static string WriteSerializedTypesBlacklist(UnityLinkerRunInformation runInformation, BuildFile[] buildFiles) { var items = runInformation.rcr.GetAllSerializedClassesAsString(); var oneOrMoreItemsWritten = false; var sb = new StringBuilder(); sb.AppendLine("<linker>"); foreach (var assemblyTypePair in items.OrderBy(t => t.Key)) { // Filter anything out where the assembly doesn't exist so that UnityLinker can be strict about preservations in link xml files if (buildFiles.All(file => !BuildFileMatchesAssembly(file, assemblyTypePair.Key))) { continue; } sb.AppendLine($"\t<assembly fullname=\"{assemblyTypePair.Key}\">"); foreach (var type in assemblyTypePair.Value.OrderBy(s => s)) { oneOrMoreItemsWritten = true; sb.AppendLine($"\t\t<type fullname=\"{type}\" preserve=\"nothing\" serialized=\"true\"/>"); } sb.AppendLine("\t</assembly>"); } sb.AppendLine("</linker>"); // Avoid writing empty files if (!oneOrMoreItemsWritten) { return(null); } var path = Path.Combine(runInformation.managedAssemblyFolderPath, "SerializedTypes.xml"); File.WriteAllText(path, sb.ToString()); return(path); }
private static bool StripAssembliesTo(string outputFolder, out string output, out string error, IEnumerable <string> linkXmlFiles, UnityLinkerRunInformation runInformation) { if (!Directory.Exists(outputFolder)) { Directory.CreateDirectory(outputFolder); } var assemblies = runInformation.AssembliesToProcess(); var args = new List <string> { $"-out={CommandLineFormatter.PrepareFileName(outputFolder)}", }; if (!UseUnityLinkerEngineModuleStripping) { args.Add($"-x={CommandLineFormatter.PrepareFileName(GetModuleWhitelist("Core", runInformation.platformProvider.moduleStrippingInformationFolder))}"); } args.AddRange(linkXmlFiles.Select(path => $"-x={CommandLineFormatter.PrepareFileName(path)}")); args.AddRange(runInformation.SearchDirectories.Select(d => $"-d={CommandLineFormatter.PrepareFileName(d)}")); args.AddRange(assemblies.Select(assembly => $"--include-unity-root-assembly={CommandLineFormatter.PrepareFileName(Path.GetFullPath(assembly))}")); args.Add($"--dotnetruntime={runInformation.argumentProvider.Runtime}"); args.Add($"--dotnetprofile={runInformation.argumentProvider.Profile}"); args.Add("--use-editor-options"); args.Add($"--include-directory={CommandLineFormatter.PrepareFileName(runInformation.managedAssemblyFolderPath)}"); if (EditorUserBuildSettings.allowDebugging) { args.Add("--editor-settings-flag=AllowDebugging"); } if (EditorUserBuildSettings.development) { args.Add("--editor-settings-flag=Development"); } args.Add($"--rule-set={runInformation.argumentProvider.RuleSet}"); args.Add($"--editor-data-file={CommandLineFormatter.PrepareFileName(runInformation.EditorToLinkerDataPath)}"); if (runInformation.platformProvider.AllowOutputToBeMadePlatformDependent) { var platform = runInformation.platformProvider.Platform; if (string.IsNullOrEmpty(platform)) { throw new ArgumentException($"Platform is required if AllowOutputToBeMadePlatformDependent is true"); } args.Add($"--platform={platform}"); } if (runInformation.platformProvider.AllowOutputToBeMadeArchitectureDependent) { var architecture = runInformation.platformProvider.Architecture; if (string.IsNullOrEmpty(architecture)) { throw new ArgumentException($"Architecture is required if AllowOutputToBeMadeArchitectureDependent is true"); } args.Add($"--architecture={architecture}"); } if (!UseUnityLinkerEngineModuleStripping) { args.Add("--disable-engine-module-support"); } if (runInformation.performEngineStripping) { args.Add("--enable-engine-module-stripping"); if (UnityEngine.Connect.UnityConnectSettings.enabled) { args.Add("--engine-stripping-flag=EnableUnityConnect"); } if (UnityEngine.Analytics.PerformanceReporting.enabled) { args.Add("--engine-stripping-flag=EnablePerformanceReporting"); } if (UnityEngine.Analytics.Analytics.enabled) { args.Add("--engine-stripping-flag=EnableAnalytics"); } if (UnityEditor.CrashReporting.CrashReportingSettings.enabled) { args.Add("--engine-stripping-flag=EnableCrashReporting"); } if (UnityEditorInternal.VR.VRModule.ShouldInjectVRDependenciesForBuildTarget(runInformation.target)) { args.Add("--engine-stripping-flag=EnableVR"); } } var modulesAssetPath = runInformation.ModulesAssetFilePath; if (File.Exists(modulesAssetPath)) { args.Add($"--engine-modules-asset-file={CommandLineFormatter.PrepareFileName(modulesAssetPath)}"); } var additionalArgs = System.Environment.GetEnvironmentVariable("UNITYLINKER_ADDITIONAL_ARGS"); if (!string.IsNullOrEmpty(additionalArgs)) { args.Add(additionalArgs); } additionalArgs = Debug.GetDiagnosticSwitch("VMUnityLinkerAdditionalArgs") as string; if (!string.IsNullOrEmpty(additionalArgs)) { args.Add(additionalArgs.Trim('\'')); } return(RunAssemblyLinker(args, out output, out error, UnityLinkerPath, runInformation.managedAssemblyFolderPath)); }
static IEnumerable <string> SanitizeLinkXmlFilePaths(List <string> linkXmlFilePaths, UnityLinkerRunInformation runInformation) { foreach (var linkXmlFilePath in linkXmlFilePaths) { // Generated link xml files that would have been empty will be nulled out. Need to filter these out before running the linker if (string.IsNullOrEmpty(linkXmlFilePath)) { continue; } var absolutePath = linkXmlFilePath; if (!Path.IsPathRooted(linkXmlFilePath)) { absolutePath = Path.Combine(runInformation.managedAssemblyFolderPath, linkXmlFilePath); } if (File.Exists(absolutePath)) { yield return(absolutePath); } } }
private static void RunAssemblyStripper(UnityLinkerRunInformation runInformation) { string output; string error; var rcr = runInformation.rcr; var managedAssemblyFolderPath = runInformation.managedAssemblyFolderPath; var linkXmlFiles = new List <string>(); linkXmlFiles.AddRange(Il2CppBlacklistPaths); if (rcr != null) { linkXmlFiles.Add(WriteMethodsToPreserveBlackList(rcr, runInformation.target)); linkXmlFiles.Add(MonoAssemblyStripping.GenerateLinkXmlToPreserveDerivedTypes(managedAssemblyFolderPath, rcr)); linkXmlFiles.Add(WriteTypesInScenesBlacklist(managedAssemblyFolderPath, rcr)); } linkXmlFiles.AddRange(ProcessBuildPipelineGenerateAdditionalLinkXmlFiles(runInformation)); linkXmlFiles.AddRange(GetUserBlacklistFiles()); if (runInformation.isMonoBackend) { // The old Mono assembly stripper uses per-platform link.xml files if available. Apply these here. var buildToolsDirectory = BuildPipeline.GetBuildToolsDirectory(runInformation.target); if (!string.IsNullOrEmpty(buildToolsDirectory)) { var platformDescriptor = Path.Combine(buildToolsDirectory, "link.xml"); if (File.Exists(platformDescriptor)) { linkXmlFiles.Add(platformDescriptor); } } } WriteEditorData(runInformation); if (!runInformation.performEngineStripping && !UseUnityLinkerEngineModuleStripping) { // if we don't do stripping, add all modules blacklists. linkXmlFiles.AddRange(runInformation.GetModuleBlacklistFiles()); } var tempStripPath = Path.GetFullPath(Path.Combine(managedAssemblyFolderPath, "tempStrip")); ProcessBuildPipelineOnBeforeRun(runInformation); bool addedMoreBlacklists; do { addedMoreBlacklists = false; if (EditorUtility.DisplayCancelableProgressBar("Building Player", "Stripping assemblies", 0.0f)) { throw new OperationCanceledException(); } if (!StripAssembliesTo( tempStripPath, out output, out error, SanitizeLinkXmlFilePaths(linkXmlFiles, runInformation), runInformation)) { throw new Exception("Error in stripping assemblies: " + runInformation.AssembliesToProcess() + ", " + error); } if (runInformation.engineStrippingSupported) { var icallSummaryPath = Path.Combine(managedAssemblyFolderPath, "ICallSummary.txt"); GenerateInternalCallSummaryFile(icallSummaryPath, managedAssemblyFolderPath, tempStripPath); if (runInformation.performEngineStripping && !UseUnityLinkerEngineModuleStripping) { // Find which modules we must include in the build based on Assemblies HashSet <UnityType> nativeClasses; HashSet <string> nativeModules; CodeStrippingUtils.GenerateDependencies(tempStripPath, icallSummaryPath, rcr, runInformation.performEngineStripping, out nativeClasses, out nativeModules, runInformation.il2CppPlatformProvider); // Add module-specific blacklists. addedMoreBlacklists = AddWhiteListsForModules(nativeModules, linkXmlFiles, runInformation.platformProvider.moduleStrippingInformationFolder); } } if (runInformation.performEngineStripping && UseUnityLinkerEngineModuleStripping) { UpdateBuildReport(ReadLinkerToEditorData(tempStripPath), runInformation); } // If we had to add more whitelists, we need to run AssemblyStripper again with the added whitelists. }while (addedMoreBlacklists && !UseUnityLinkerEngineModuleStripping); // keep unstripped files for debugging purposes var tempUnstrippedPath = Path.GetFullPath(Path.Combine(managedAssemblyFolderPath, "tempUnstripped")); if (debugUnstripped) { Directory.CreateDirectory(tempUnstrippedPath); } foreach (var file in Directory.GetFiles(managedAssemblyFolderPath)) { var extension = Path.GetExtension(file); if (string.Equals(extension, ".dll", StringComparison.InvariantCultureIgnoreCase) || string.Equals(extension, ".winmd", StringComparison.InvariantCultureIgnoreCase) || string.Equals(extension, ".mdb", StringComparison.InvariantCultureIgnoreCase) || string.Equals(extension, ".pdb", StringComparison.InvariantCultureIgnoreCase)) { if (debugUnstripped) { File.Move(file, Path.Combine(tempUnstrippedPath, Path.GetFileName(file))); } else { File.Delete(file); } } } foreach (var file in Directory.GetFiles(tempStripPath)) { File.Move(file, Path.Combine(managedAssemblyFolderPath, Path.GetFileName(file))); } foreach (var dir in Directory.GetDirectories(tempStripPath)) { Directory.Move(dir, Path.Combine(managedAssemblyFolderPath, Path.GetFileName(dir))); } Directory.Delete(tempStripPath); ProcessBuildPipelineOnAfterRun(runInformation); }
public UnityLinkerArgumentValueProvider(UnityLinkerRunInformation runInformation) { this.m_RunInformation = runInformation; }
private static void RunAssemblyStripper(UnityLinkerRunInformation runInformation) { string output; string error; var managedAssemblyFolderPath = runInformation.managedAssemblyFolderPath; var linkXmlFiles = GetLinkXmlFiles(runInformation); WriteEditorData(runInformation); var tempStripPath = GetFullPath(Path.Combine(managedAssemblyFolderPath, "tempStrip")); if (EditorUtility.DisplayCancelableProgressBar("Building Player", "Stripping assemblies", 0.0f)) { throw new OperationCanceledException(); } if (!StripAssembliesTo( tempStripPath, out output, out error, linkXmlFiles, runInformation)) { throw new Exception("Error in stripping assemblies: " + runInformation.AssembliesToProcess() + ", " + error); } if (runInformation.engineStrippingSupported) { var icallSummaryPath = Path.Combine(managedAssemblyFolderPath, "ICallSummary.txt"); GenerateInternalCallSummaryFile(icallSummaryPath, managedAssemblyFolderPath, tempStripPath); } if (runInformation.performEngineStripping) { var strippingInfo = runInformation.BuildReportData; if (strippingInfo != null) { UpdateBuildReport(ReadLinkerToEditorData(tempStripPath), strippingInfo); } } // keep unstripped files for debugging purposes var tempUnstrippedPath = GetFullPath(Path.Combine(managedAssemblyFolderPath, "tempUnstripped")); if (debugUnstripped) { Directory.CreateDirectory(tempUnstrippedPath); } foreach (var file in Directory.GetFiles(managedAssemblyFolderPath)) { var extension = Path.GetExtension(file); if (string.Equals(extension, ".dll", StringComparison.InvariantCultureIgnoreCase) || string.Equals(extension, ".winmd", StringComparison.InvariantCultureIgnoreCase) || string.Equals(extension, ".mdb", StringComparison.InvariantCultureIgnoreCase) || string.Equals(extension, ".pdb", StringComparison.InvariantCultureIgnoreCase)) { if (debugUnstripped) { File.Move(file, Path.Combine(tempUnstrippedPath, Path.GetFileName(file))); } else { File.Delete(file); } } } foreach (var file in Directory.GetFiles(tempStripPath)) { File.Move(file, Path.Combine(managedAssemblyFolderPath, Path.GetFileName(file))); } foreach (var dir in Directory.GetDirectories(tempStripPath)) { Directory.Move(dir, Path.Combine(managedAssemblyFolderPath, Path.GetFileName(dir))); } Directory.Delete(tempStripPath); }