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

390 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Topon_Adapter.Editor
{
[Serializable]
public sealed class ToponBuildSettings
{
public bool enableDebuggerUI = false;
public bool forceVerbtoUtilVersion = true;
public string verbtoUtilDependency = ToponBuildSettingsStore.DefaultVerbtoUtilDependency;
public bool stripResolvedDebuggerArtifacts = true;
}
internal static class ToponBuildSettingsStore
{
public const string DefaultVerbtoUtilDependency = "com.verbto.tools:util:1.1.3";
private const string ActiveBuildSessionKey = "Commercialization.Topon.ActiveBuildSettings";
private const string SettingsFileSuffix = "_topon_build_settings.json";
private const string BuildConfigsFolder = "BuildConfigs";
private const string DebuggerPackageMarker = "com.anythink.sdk:debugger-ui";
private const string VerbtoPackageMarker = "com.verbto.tools:util";
[Serializable]
private sealed class ActiveBuildSettings
{
public ToponBuildSettings settings;
public long utcTicks;
}
public static ToponBuildSettings CreateDefault()
{
return new ToponBuildSettings();
}
public static string GetVerbtoUtilDependency(ToponBuildSettings settings)
{
if (settings == null || string.IsNullOrWhiteSpace(settings.verbtoUtilDependency))
{
return DefaultVerbtoUtilDependency;
}
return settings.verbtoUtilDependency.Trim();
}
public static ToponBuildSettings LoadForProfileName(string profileName, string repositoryRoot)
{
var settings = CreateDefault();
var path = GetSettingsPath(profileName, repositoryRoot);
if (string.IsNullOrEmpty(path) || !File.Exists(path))
{
return settings;
}
try
{
JsonUtility.FromJsonOverwrite(File.ReadAllText(path), settings);
Normalize(settings);
}
catch (Exception exception)
{
Debug.LogWarning($"[TopOn Build] Failed to read build settings: {exception.Message}");
}
return settings;
}
public static void SaveForProfileName(string profileName, string repositoryRoot, ToponBuildSettings settings)
{
if (settings == null)
{
settings = CreateDefault();
}
else
{
Normalize(settings);
}
var path = GetSettingsPath(profileName, repositoryRoot);
if (string.IsNullOrEmpty(path))
{
return;
}
try
{
var directory = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(path, JsonUtility.ToJson(settings, true));
}
catch (Exception exception)
{
Debug.LogWarning($"[TopOn Build] Failed to save build settings: {exception.Message}");
}
}
public static void SetActiveForCurrentBuild(ToponBuildSettings settings)
{
var activeSettings = new ActiveBuildSettings
{
settings = Clone(settings),
utcTicks = DateTime.UtcNow.Ticks
};
SessionState.SetString(ActiveBuildSessionKey, JsonUtility.ToJson(activeSettings));
}
public static ToponBuildSettings GetActiveForCurrentBuild()
{
var json = SessionState.GetString(ActiveBuildSessionKey, string.Empty);
if (string.IsNullOrWhiteSpace(json))
{
return CreateDefault();
}
try
{
var activeSettings = JsonUtility.FromJson<ActiveBuildSettings>(json);
if (activeSettings == null || activeSettings.settings == null)
{
return CreateDefault();
}
Normalize(activeSettings.settings);
var activatedAt = new DateTime(activeSettings.utcTicks, DateTimeKind.Utc);
if (DateTime.UtcNow - activatedAt > TimeSpan.FromHours(6))
{
ClearActiveBuildSettings();
return CreateDefault();
}
return Clone(activeSettings.settings);
}
catch (Exception exception)
{
Debug.LogWarning($"[TopOn Build] Failed to read active build settings: {exception.Message}");
return CreateDefault();
}
}
public static void ClearActiveBuildSettings()
{
SessionState.EraseString(ActiveBuildSessionKey);
}
public static bool HasStaleDebuggerResolverOutput(string repositoryRoot)
{
foreach (var path in GetResolverOutputPaths(repositoryRoot))
{
if (!File.Exists(path))
{
continue;
}
var content = File.ReadAllText(path);
if (content.Contains(DebuggerPackageMarker))
{
return true;
}
}
return false;
}
public static bool HasUnforcedVerbtoUtilOutput(string repositoryRoot, string expectedDependency)
{
expectedDependency = NormalizeDependency(expectedDependency);
foreach (var path in GetResolverOutputPaths(repositoryRoot))
{
if (!File.Exists(path))
{
continue;
}
var content = File.ReadAllText(path);
if (content.Contains(VerbtoPackageMarker) && !content.Contains(expectedDependency))
{
return true;
}
}
foreach (var path in FindResolvedVerbtoUtilArtifacts(repositoryRoot))
{
if (!IsExpectedVerbtoUtilArtifactFileName(Path.GetFileName(path), expectedDependency))
{
return true;
}
}
return false;
}
public static IReadOnlyList<string> FindResolvedDebuggerArtifacts(string repositoryRoot)
{
var result = new List<string>();
var root = ResolveRepositoryRoot(repositoryRoot);
if (string.IsNullOrEmpty(root))
{
return result;
}
var androidPluginPath = Path.Combine(root, "Assets", "Plugins", "Android");
if (!Directory.Exists(androidPluginPath))
{
return result;
}
foreach (var path in Directory.GetFiles(androidPluginPath, "*", SearchOption.AllDirectories))
{
if (IsDebuggerArtifactFileName(Path.GetFileName(path)))
{
result.Add(path);
}
}
return result;
}
public static IReadOnlyList<string> FindResolvedVerbtoUtilArtifacts(string repositoryRoot)
{
var result = new List<string>();
var root = ResolveRepositoryRoot(repositoryRoot);
if (string.IsNullOrEmpty(root))
{
return result;
}
var androidPluginPath = Path.Combine(root, "Assets", "Plugins", "Android");
if (!Directory.Exists(androidPluginPath))
{
return result;
}
foreach (var path in Directory.GetFiles(androidPluginPath, "*", SearchOption.AllDirectories))
{
if (IsVerbtoUtilArtifactFileName(Path.GetFileName(path)))
{
result.Add(path);
}
}
return result;
}
internal static bool IsDebuggerArtifactFileName(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return false;
}
return fileName.StartsWith("com.anythink.sdk.debugger-ui-", StringComparison.OrdinalIgnoreCase);
}
internal static bool IsVerbtoUtilArtifactFileName(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return false;
}
return fileName.StartsWith("com.verbto.tools.util-", StringComparison.OrdinalIgnoreCase);
}
internal static bool IsExpectedVerbtoUtilArtifactFileName(string fileName, string expectedDependency)
{
if (!IsVerbtoUtilArtifactFileName(fileName))
{
return false;
}
var version = GetVersionFromDependency(expectedDependency);
if (string.IsNullOrEmpty(version))
{
return false;
}
return fileName.StartsWith($"com.verbto.tools.util-{version}", StringComparison.OrdinalIgnoreCase);
}
private static ToponBuildSettings Clone(ToponBuildSettings settings)
{
if (settings == null)
{
return CreateDefault();
}
return new ToponBuildSettings
{
enableDebuggerUI = settings.enableDebuggerUI,
forceVerbtoUtilVersion = settings.forceVerbtoUtilVersion,
verbtoUtilDependency = GetVerbtoUtilDependency(settings),
stripResolvedDebuggerArtifacts = settings.stripResolvedDebuggerArtifacts
};
}
private static void Normalize(ToponBuildSettings settings)
{
if (settings == null)
{
return;
}
settings.verbtoUtilDependency = NormalizeDependency(settings.verbtoUtilDependency);
}
private static string NormalizeDependency(string dependency)
{
if (string.IsNullOrWhiteSpace(dependency))
{
return DefaultVerbtoUtilDependency;
}
return dependency.Trim();
}
private static string GetVersionFromDependency(string dependency)
{
dependency = NormalizeDependency(dependency);
var parts = dependency.Split(':');
return parts.Length >= 3 ? parts[parts.Length - 1] : string.Empty;
}
private static IEnumerable<string> GetResolverOutputPaths(string repositoryRoot)
{
var root = ResolveRepositoryRoot(repositoryRoot);
if (string.IsNullOrEmpty(root))
{
yield break;
}
yield return Path.Combine(root, "ProjectSettings", "AndroidResolverDependencies.xml");
yield return Path.Combine(root, "Assets", "Plugins", "Android", "mainTemplate.gradle");
yield return Path.Combine(root, "Assets", "Plugins", "Android", "mainTemplate.gradle.backup");
yield return Path.Combine(root, "Assets", "Plugins", "Android", "settingsTemplate.gradle");
}
private static string GetSettingsPath(string profileName, string repositoryRoot)
{
var root = ResolveRepositoryRoot(repositoryRoot);
if (string.IsNullOrEmpty(root))
{
return string.Empty;
}
return Path.Combine(root, BuildConfigsFolder, $"{SanitizeProfileName(profileName)}{SettingsFileSuffix}");
}
private static string ResolveRepositoryRoot(string repositoryRoot)
{
if (!string.IsNullOrWhiteSpace(repositoryRoot))
{
return Path.GetFullPath(repositoryRoot);
}
var dataPath = Application.dataPath;
var parent = Directory.GetParent(dataPath);
return parent == null ? string.Empty : parent.FullName;
}
private static string SanitizeProfileName(string profileName)
{
if (string.IsNullOrWhiteSpace(profileName))
{
return "default";
}
var invalidChars = Path.GetInvalidFileNameChars();
var chars = profileName.ToCharArray();
for (var i = 0; i < chars.Length; i++)
{
if (Array.IndexOf(invalidChars, chars[i]) >= 0)
{
chars[i] = '_';
}
}
return new string(chars);
}
}
}