Files
Commercialization.topon/Topon_Adapter/Editor/ToponAndroidDebuggerDependencyPostProcessor.cs
2026-06-10 21:52:43 +08:00

307 lines
11 KiB
C#

#if UNITY_ANDROID
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor.Android;
using UnityEngine;
namespace Topon_Adapter.Editor
{
public sealed class ToponAndroidDebuggerDependencyPostProcessor : IPostGenerateGradleAndroidProject
{
private const string Tag = "[TopOn Build]";
private const string DebuggerDependency = "com.anythink.sdk:debugger-ui:+";
private const string DebuggerDependencyMarker = "com.anythink.sdk:debugger-ui";
private const string VerbtoDependencyMarker = "com.verbto.tools:util";
private const string DebuggerRepositoryUrl = "https://jfrog.anythinktech.com/artifactory/debugger";
private const string DepsStart = "// TopOn Debugger UI Dependencies Start";
private const string DepsEnd = "// TopOn Debugger UI Dependencies End";
private const string VerbtoDepsStart = "// TopOn Verbto Util Dependency Start";
private const string VerbtoDepsEnd = "// TopOn Verbto Util Dependency End";
private const string ReposStart = "// TopOn Debugger UI Repository Start";
private const string ReposEnd = "// TopOn Debugger UI Repository End";
public int callbackOrder => int.MaxValue;
public void OnPostGenerateGradleAndroidProject(string path)
{
var settings = ToponBuildSettingsStore.GetActiveForCurrentBuild();
try
{
ProcessGeneratedProject(path, settings);
}
finally
{
ToponBuildSettingsStore.ClearActiveBuildSettings();
}
}
public static void ProcessGeneratedProject(string path, ToponBuildSettings settings)
{
if (settings == null)
{
settings = ToponBuildSettingsStore.CreateDefault();
}
var gradleRoot = GetGradleRoot(path);
var gradleFiles = new[]
{
Path.Combine(path, "build.gradle"),
Path.Combine(gradleRoot, "launcher", "build.gradle"),
Path.Combine(gradleRoot, "build.gradle")
};
var forceVerbtoUtilVersion = settings.forceVerbtoUtilVersion;
var verbtoDependency = ToponBuildSettingsStore.GetVerbtoUtilDependency(settings);
foreach (var gradleFile in gradleFiles)
{
StripManagedDependenciesFromGradleFile(gradleFile, forceVerbtoUtilVersion);
}
StripManagedDependenciesFromGradleFile(Path.Combine(gradleRoot, "settings.gradle"), forceVerbtoUtilVersion);
if (settings.enableDebuggerUI || forceVerbtoUtilVersion)
{
InjectRepository(Path.Combine(gradleRoot, "settings.gradle"));
InjectRepository(Path.Combine(gradleRoot, "build.gradle"));
}
if (settings.enableDebuggerUI)
{
InjectDebuggerDependency(Path.Combine(path, "build.gradle"));
Debug.Log($"{Tag} DebugUI dependency enabled for this build.");
}
else
{
if (settings.stripResolvedDebuggerArtifacts)
{
RemoveGeneratedDebuggerArtifacts(gradleRoot);
}
Debug.Log($"{Tag} DebugUI dependency disabled for this build.");
}
if (forceVerbtoUtilVersion)
{
InjectVerbtoUtilDependency(Path.Combine(path, "build.gradle"), verbtoDependency);
RemoveGeneratedStaleVerbtoUtilArtifacts(gradleRoot, verbtoDependency);
Debug.Log($"{Tag} Verbto util dependency forced to {verbtoDependency}.");
}
else
{
Debug.Log($"{Tag} Verbto util dependency is not modified by TopOn build settings.");
}
}
private static string GetGradleRoot(string unityLibraryPath)
{
#if UNITY_2019_3_OR_NEWER
return Path.GetFullPath(Path.Combine(unityLibraryPath, ".."));
#else
return Path.GetFullPath(unityLibraryPath);
#endif
}
private static void StripManagedDependenciesFromGradleFile(string filePath, bool forceVerbtoUtilVersion)
{
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
{
return;
}
var content = File.ReadAllText(filePath);
var original = content;
content = RemoveMarkedBlock(content, DepsStart, DepsEnd);
content = RemoveMarkedBlock(content, VerbtoDepsStart, VerbtoDepsEnd);
content = RemoveMarkedBlock(content, ReposStart, ReposEnd);
content = RemoveLinesContaining(content, DebuggerDependencyMarker);
if (forceVerbtoUtilVersion)
{
content = RemoveLinesContaining(content, VerbtoDependencyMarker);
}
if (!string.Equals(original, content, StringComparison.Ordinal))
{
File.WriteAllText(filePath, content);
}
}
private static void InjectDebuggerDependency(string buildGradlePath)
{
InjectDependencyBlock(buildGradlePath, DepsStart, DepsEnd, DebuggerDependency);
}
private static void InjectVerbtoUtilDependency(string buildGradlePath, string dependency)
{
InjectDependencyBlock(buildGradlePath, VerbtoDepsStart, VerbtoDepsEnd, dependency);
}
private static void InjectDependencyBlock(string buildGradlePath, string startMarker, string endMarker, string dependency)
{
if (!File.Exists(buildGradlePath))
{
Debug.LogWarning($"{Tag} build.gradle not found: {buildGradlePath}");
return;
}
if (string.IsNullOrWhiteSpace(dependency))
{
Debug.LogWarning($"{Tag} dependency is empty, skip injecting {startMarker}.");
return;
}
var content = File.ReadAllText(buildGradlePath);
var block = new StringBuilder();
block.AppendLine(startMarker);
block.AppendLine($" implementation '{dependency.Trim()}'");
block.AppendLine($" {endMarker}");
var pattern = new Regex(@"(dependencies\s*\{)");
if (!pattern.IsMatch(content))
{
Debug.LogWarning($"{Tag} dependencies block not found: {buildGradlePath}");
return;
}
content = pattern.Replace(content, match => match.Groups[1].Value + "\n " + block, 1);
File.WriteAllText(buildGradlePath, content);
}
private static void InjectRepository(string gradlePath)
{
if (!File.Exists(gradlePath))
{
return;
}
var content = File.ReadAllText(gradlePath);
if (content.Contains(DebuggerRepositoryUrl))
{
return;
}
var block = $"{ReposStart}\n maven {{ url '{DebuggerRepositoryUrl}' }}\n {ReposEnd}";
Regex pattern;
if (Path.GetFileName(gradlePath).Equals("settings.gradle", StringComparison.OrdinalIgnoreCase))
{
pattern = new Regex(@"(dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{)");
}
else
{
block = $"{ReposStart}\n maven {{ url '{DebuggerRepositoryUrl}' }}\n {ReposEnd}";
pattern = new Regex(@"(repositories\s*\{)");
}
if (!pattern.IsMatch(content))
{
return;
}
content = pattern.Replace(content, match => match.Groups[1].Value + "\n " + block, 1);
File.WriteAllText(gradlePath, content);
}
private static void RemoveGeneratedDebuggerArtifacts(string gradleRoot)
{
if (string.IsNullOrWhiteSpace(gradleRoot) || !Directory.Exists(gradleRoot))
{
return;
}
var root = Path.GetFullPath(gradleRoot).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
foreach (var path in Directory.GetFiles(root, "*", SearchOption.AllDirectories))
{
if (!ToponBuildSettingsStore.IsDebuggerArtifactFileName(Path.GetFileName(path)))
{
continue;
}
var fullPath = Path.GetFullPath(path);
if (!fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase))
{
continue;
}
try
{
File.Delete(fullPath);
Debug.Log($"{Tag} Removed generated debugger artifact: {fullPath}");
}
catch (Exception exception)
{
Debug.LogWarning($"{Tag} Failed to remove generated debugger artifact: {exception.Message}");
}
}
}
private static void RemoveGeneratedStaleVerbtoUtilArtifacts(string gradleRoot, string expectedDependency)
{
if (string.IsNullOrWhiteSpace(gradleRoot) || !Directory.Exists(gradleRoot))
{
return;
}
var root = Path.GetFullPath(gradleRoot).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
foreach (var path in Directory.GetFiles(root, "*", SearchOption.AllDirectories))
{
var fileName = Path.GetFileName(path);
if (!ToponBuildSettingsStore.IsVerbtoUtilArtifactFileName(fileName) ||
ToponBuildSettingsStore.IsExpectedVerbtoUtilArtifactFileName(fileName, expectedDependency))
{
continue;
}
var fullPath = Path.GetFullPath(path);
if (!fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase))
{
continue;
}
try
{
File.Delete(fullPath);
Debug.Log($"{Tag} Removed stale verbto util artifact: {fullPath}");
}
catch (Exception exception)
{
Debug.LogWarning($"{Tag} Failed to remove stale verbto util artifact: {exception.Message}");
}
}
}
private static string RemoveMarkedBlock(string content, string startMarker, string endMarker)
{
var pattern = new Regex($@"\s*{Regex.Escape(startMarker)}[\s\S]*?{Regex.Escape(endMarker)}\s*");
return pattern.Replace(content, "\n");
}
private static string RemoveLinesContaining(string content, params string[] markers)
{
var lines = content.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n');
var kept = new List<string>();
foreach (var line in lines)
{
var remove = false;
foreach (var marker in markers)
{
if (line.Contains(marker))
{
remove = true;
break;
}
}
if (!remove)
{
kept.Add(line);
}
}
return string.Join("\n", kept);
}
}
}
#endif