using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; using Unity.Plastic.Newtonsoft.Json; using UnityEditor; using UnityEngine; /// /// Provides utilities for resolving Android dependency versions from various sources. /// internal static class AndroidDependencyVersionResolver { /// /// Gets the version of a specific Android dependency from project configuration files. /// /// The package name to search for. /// The version string if found; otherwise, null. public static string? GetAndroidDependencyVersion(string dependencyPackage) { // If the dependency is added by EDM4U foreach (var path in SafeEnumerateFiles("Assets", "*Dependencies*.xml")) { try { var xdoc = XDocument.Load(path); foreach (var package in xdoc.Descendants("androidPackage")) { var attribute = (string)package.Attribute("spec"); if (string.IsNullOrEmpty(attribute)) { continue; } if (attribute.StartsWith($"{dependencyPackage}:", StringComparison.OrdinalIgnoreCase)) { var parts = attribute.Split(':'); if (parts.Length >= 3) { return parts[2].Trim(); } } } } catch { /* ignored */ } } // If the dependency is added by Asset/Manually var gradlePatterns = new[] { "*.gradle", "*.gradle.kts" }; foreach (var pattern in gradlePatterns) { foreach (var file in SafeEnumerateFiles("Assets", pattern)) { try { var text = File.ReadAllText(file); var rx = new Regex($@"['""]{Regex.Escape(dependencyPackage)}:([^'""\s)]+)['""]"); var m = rx.Match(text); if (m.Success) { return m.Groups[1].Value.Trim(); } } catch { } } } return null; } /// /// Extracts the major version number from various version specification formats. /// /// The version specification. /// The major version number if parseable; otherwise, null. public static int? ParseMajorFromVersionSpec(string version) { if (string.IsNullOrWhiteSpace(version)) { return null; } // ">=13.0.0" => 13 var ge = Regex.Match(version, @"^>=\s*(\d+)\."); if (ge.Success) { return int.Parse(ge.Groups[1].Value); } // "13.+" or "13.x" => 13 var dyn = Regex.Match(version, @"^(\d+)\s*\.\s*(\+|x)$"); if (dyn.Success) { return int.Parse(dyn.Groups[1].Value); } // Maven range "[12.0.0,12.10.0[" => 12 var range = Regex.Match(version, @"^[\[\(]\s*(\d+)\."); if (range.Success) { return int.Parse(range.Groups[1].Value); } // "13.2.1" or "13.2" => 13 var exact = Regex.Match(version, @"^(\d+)\."); if (exact.Success) { return int.Parse(exact.Groups[1].Value); } // "13" => 13 var majorOnly = Regex.Match(version, @"^(\d+)$"); if (majorOnly.Success) { return int.Parse(majorOnly.Groups[1].Value); } return null; } private static IEnumerable SafeEnumerateFiles(string root, string pattern) { try { return Directory.GetFiles(root, pattern, SearchOption.AllDirectories); } catch { return Enumerable.Empty(); } } /// /// Resolves the appropriate adapter version based on the dependency configuration and versioning rules. /// /// The name of the dependency adapter. /// The package identifier for the dependency. /// The default version to use if resolution fails. /// The resolved adapter version or the default version if resolution fails. public static string ResolveAdapterVersionFromConfig(string dependencyName, string dependencyPackage, string defaultVersion) { Debug.Log($"[JustTrack] Resolving adapter version for {dependencyName} ({dependencyPackage})"); string? dependencyVersion = GetAndroidDependencyVersion(dependencyPackage); if (dependencyVersion == null) { Debug.LogWarning($"[JustTrack] Unable to retrieve dependency version for {dependencyPackage}. Using default adapter version '{defaultVersion}'."); return defaultVersion; } const string adapterVersioningJsonPath = "Packages/io.justtrack.justtrack-unity-sdk/Editor/adapter_dynamic_versioning.json"; var adapterVersioningJsonAsset = AssetDatabase.LoadAssetAtPath(adapterVersioningJsonPath); if (!adapterVersioningJsonAsset) { Debug.LogWarning($"[JustTrack] Not found at {adapterVersioningJsonPath}. Check your package name in package.json and that the JSON is in Editor/."); return defaultVersion; } var adapterJson = adapterVersioningJsonAsset.text; try { var adapterVersionConfig = JsonConvert.DeserializeObject(adapterJson); if (adapterVersionConfig?.android == null) { return defaultVersion; } if (!adapterVersionConfig.android.TryGetValue(dependencyName, out var rules) || rules == null || rules.Count == 0) { return defaultVersion; } var actualMajorVersion = ParseMajorFromVersionSpec(dependencyVersion); foreach (var rule in rules) { var req = rule.ThirdpartyVersionRequirement; if (string.IsNullOrEmpty(req) || req == "*") { return rule.Version; } // Major-only dynamic: "13.+" or "13.x" if (Regex.IsMatch(req, @"^\s*\d+\s*\.\s*(\+|x)\s*$")) { var reqMajor = ParseMajorFromVersionSpec(req); if (actualMajorVersion.HasValue && reqMajor.HasValue && actualMajorVersion.Value == reqMajor.Value) { return rule.Version; } continue; } // Exact or partial version like "13.2.1" or "13.2": match by major equality if (Regex.IsMatch(req, @"^\s*\d+(\.\d+){0,2}\s*$")) { var reqMajor = ParseMajorFromVersionSpec(req); if (actualMajorVersion.HasValue && reqMajor.HasValue && actualMajorVersion.Value == reqMajor.Value) { return rule.Version; } continue; } } } catch (Exception ex) { Debug.LogWarning($"[JustTrack] Error ResolveAdapterVersionFromConfig: {ex}"); } // fallback to version if can not retreive version. return defaultVersion; } /// /// Represents a versioning rule for adapter dependencies. /// #pragma warning disable SA1401, SA1307 // Fields should be private; field names should begin with upper-case letter (required for JSON serialization) [Serializable] internal class AdapterRule { /// /// Gets or sets the third-party version requirement pattern. /// public string ThirdpartyVersionRequirement = string.Empty; /// /// Gets or sets the adapter version to use when the requirement is met. /// public string Version = string.Empty; } /// /// Represents the dynamic versioning configuration for adapters. /// [Serializable] internal class AdapterDynamicVersioning { /// /// Gets or sets the Android adapter versioning rules mapped by dependency name. /// public Dictionary> android = new Dictionary>(); } #pragma warning restore SA1401, SA1307 }