using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; using MobileDependencyResolver.Utils.Editor; using UnityEditor; using UnityEngine; namespace Unity.Services.Mediation.Adapters.Editor { class AdaptersDependencyGenerator { const string k_FileName = "MediationAdapterDependencies.xml"; const string k_DefaultFolder = "Assets/Editor"; const string k_DefaultPath = k_DefaultFolder + "/" + k_FileName; const string k_IdentifierKey = "mediation-identifier"; const string k_VersionKey = "mediation-version"; #if UNITY_MEDIATION_STAGING const string k_AndroidArtifactoryURL = @"https://unity3ddist.jfrog.io/artifactory/unity-mediation-mvn-stg-local/"; const string k_IOSArtifactoryURL = @"'git@github.com:Unity-Technologies/mz-liveops-cocoapods.git'"; #else // Prod const string k_AndroidArtifactoryURL = @"https://unity3ddist.jfrog.io/artifactory/unity-mediation-mvn-prod-local/"; const string k_IOSArtifactoryURL = @"'https://github.com/Unity-Technologies/unity-mediation-cocoapods-prod.git'"; #endif string m_DependenciesPath; Regex m_MavenRegex; public virtual event Action AdaptersChanged; string GetDependenciesPath(bool createPath = true) { if (m_DependenciesPath != null) { if (File.Exists(m_DependenciesPath)) { m_DependenciesPath = EnsureDependenciesFileInEditorFolder(m_DependenciesPath); return m_DependenciesPath; } } if (File.Exists(k_DefaultPath)) { m_DependenciesPath = k_DefaultPath; return m_DependenciesPath; } var files = Directory.GetFiles("Assets", k_FileName, SearchOption.AllDirectories); if (files.Length > 0) { var file = files[0]; file = EnsureDependenciesFileInEditorFolder(file); m_DependenciesPath = file; return m_DependenciesPath; } if (createPath) { m_DependenciesPath = CreateDependencyPath(); } return m_DependenciesPath; } string CreateDependencyPath() { Directory.CreateDirectory(k_DefaultFolder); AdapterInfo unityAdsAdapterInfo = MediationSdkInfo.GetAllAdapters().Find((x) => x.Identifier == "unityads-adapter"); if (unityAdsAdapterInfo.InstalledVersion?.Identifier == null) { unityAdsAdapterInfo.InstalledVersion = unityAdsAdapterInfo.Versions.First(); } InstallOverrideAdapters(k_DefaultPath, new List() {unityAdsAdapterInfo}); return m_DependenciesPath; } internal string GenerateXmlContentWithAllDependencies(List adapters) { var sdkInfo = MediationSdkInfo.GetSdkInfo(); var repoList = new List(); foreach (var adapterInfo in adapters) { foreach (var repository in adapterInfo.Repositories) { repoList.Add(repository); } } repoList = repoList.Distinct().ToList(); return (new XComment(" auto-generated. do not modify by hand. ") + Environment.NewLine + new XElement("dependencies", new XElement("androidPackages", new XElement("repositories", new XElement("repository", k_AndroidArtifactoryURL), repoList.Select(repo => new XElement("repository", repo)) ), new XComment(" Mediation Android SDK "), new XElement("androidPackage", new XAttribute("spec", $"{sdkInfo.AndroidArtifact}:{VersionInfo.OptimisticVersion(SemanticVersioningType.Maven,sdkInfo.SdkVersion)}"), new XAttribute(k_IdentifierKey, sdkInfo.Identifier), new XAttribute(k_VersionKey, sdkInfo.SdkVersion) ), new XComment(" Mediation SDK Android Adapters "), adapters.OrderBy(info => info.Identifier) .Select(info => new XElement("androidPackage", new XAttribute("spec", $"{info.AndroidArtifact}:{VersionInfo.OptimisticVersion(SemanticVersioningType.Maven,sdkInfo.SdkVersion)}"), new XAttribute(k_IdentifierKey, info.Identifier), new XAttribute(k_VersionKey, info.InstalledVersion.Identifier) )) ), new XElement("iosPods", new XComment(" Mediation iOS SDK "), new XElement("iosPod", new XAttribute("name", sdkInfo.IosPod), new XAttribute("version", VersionInfo.OptimisticVersion(SemanticVersioningType.CocoaPods, sdkInfo.SdkVersion)), new XAttribute("source", k_IOSArtifactoryURL), new XAttribute(k_IdentifierKey, sdkInfo.Identifier), new XAttribute(k_VersionKey, sdkInfo.SdkVersion) ), new XComment(" Mediation Dependency Required For resolution "), new XElement("iosPod", new XAttribute("name", "Protobuf") ), new XComment(" Mediation SDK iOS Adapters "), adapters.OrderBy(info => info.Identifier) .Select(info => new XElement("iosPod", new XAttribute("name", $"{info.IosPod}"), new XAttribute("version", info.InstalledVersion.Version(SemanticVersioningType.CocoaPods)), new XAttribute("source", k_IOSArtifactoryURL), new XAttribute(k_IdentifierKey, info.Identifier), new XAttribute(k_VersionKey, info.InstalledVersion.Identifier) )) ) ) ).Replace(">", ">"); } string FindLocalMavenRepo() { var searchFolders = new[] { "Packages/com.unity.services.mediation", "Packages/com.unity.accelerate", "Assets" }; string repo = null; foreach (var folder in searchFolders) { try { repo = Directory.GetDirectories(folder, "*", SearchOption.AllDirectories) .FirstOrDefault(IsPathLikeLocalMavenPath); if (repo != null) { break; } } catch (Exception) { // ignored } } if (repo == null) { throw new FileNotFoundException($"Can't find local maven repository for Unity Mediation"); } for (int i = 0; i < 4; i++) { repo = Path.GetDirectoryName(repo); } return repo; } internal bool IsPathLikeLocalMavenPath(string path) { if (m_MavenRegex == null) { m_MavenRegex = new Regex("Editor[\\\\/]Maven~[\\\\/]com[\\\\/]unity3d[\\\\/]mediation[\\\\/]mediation-sdk$"); } return m_MavenRegex.IsMatch(path); } string FindPodSpecPath(string podName) { var searchFolders = new[] { "Packages/com.unity.services.mediation", "Packages/com.unity.accelerate", "Assets" }; string podspec = null; foreach (var folder in searchFolders) { try { podspec = Directory.GetFiles(folder, $"{podName}.podspec", SearchOption.AllDirectories).FirstOrDefault(); if (podspec != null) { break; } } catch (Exception) { // ignored } } if (podspec == null) { throw new FileNotFoundException($"Can't find {podName}.podspec"); } return Path.GetDirectoryName(podspec)?.Replace("\\", "/"); } /// /// Get a list of adapters currently in use with Mediation SDK /// /// Returns a List of AdapterInfo public virtual List GetInstalledAdapters(bool generateXml = true) { var xmlPath = GetDependenciesPath(); return GetInstalledAdapters(xmlPath, generateXml); } internal List GetInstalledAdapters(string xmlPath, bool generateXml = true) { var needRegeneration = false; var sdkInfo = MediationSdkInfo.GetSdkInfo(); var allAdapters = MediationSdkInfo.GetAllAdapters(); var installedAdapters = new List(); var xmlDoc = XElement.Load(xmlPath); var xmlDeps = xmlDoc.Element("androidPackages")?.Elements("androidPackage"); if (xmlDeps == null) return new List(); foreach (var element in xmlDeps) { var identifier = element.Attribute(k_IdentifierKey)?.Value; if (string.IsNullOrWhiteSpace(identifier)) { needRegeneration = true; continue; } if (identifier.StartsWith(sdkInfo.Identifier)) { var sdkVersion = element.Attribute(k_VersionKey)?.Value; if (sdkVersion != sdkInfo.SdkVersion) { needRegeneration = true; } //SDK component, not an adapter. Skip continue; } var adapter = allAdapters.FirstOrDefault(info => info.Identifier == identifier); if (adapter == null) { needRegeneration = true; continue; } var versionIdentifier = element.Attribute(k_VersionKey)?.Value; if (!string.IsNullOrWhiteSpace(versionIdentifier) && Array.Exists(adapter.Versions, (x) => x.Identifier == versionIdentifier)) { adapter.InstalledVersion = adapter.Versions.First(versionInfo => versionInfo.Identifier == versionIdentifier); } else { adapter.InstalledVersion = adapter.Versions.Last(); needRegeneration = true; } installedAdapters.Add(adapter); } if (needRegeneration && generateXml) { InstallOverrideAdapters(xmlPath, installedAdapters); } return installedAdapters; } /// /// Installs or updates an adapter to specified version. /// If the adapter is already installed under a different version, the version will be updated. /// If version is not specified, latest available version will be installed. /// /// The identifier of the adapter /// Version of the adapter /// Thrown if combination of identifier/version is not valid public virtual void InstallAdapter(string identifier, VersionInfo versionInfo = null) { var xmlPath = GetDependenciesPath(); InstallAdapter(identifier, versionInfo, xmlPath); } internal void InstallAdapter(string identifier, VersionInfo versionInfo, string xmlPath) { var adapter = MediationSdkInfo.GetAllAdapters().FirstOrDefault(info => info.Identifier == identifier); if (adapter == null) { throw new InvalidOperationException($"Can't install adapter with identifier \'{identifier}\'"); } if (versionInfo != null && !Array.Exists(adapter.Versions, (x) => x == versionInfo)) { throw new InvalidOperationException( $"Can't install adapter with identifier \'{identifier}\': version \'{versionInfo.Identifier}\' not available"); } if (versionInfo == null) { versionInfo = adapter.Versions.First(); } var installedAdapters = GetInstalledAdapters(xmlPath); var installedAdapter = installedAdapters.FirstOrDefault(info => info.Identifier == identifier); if (installedAdapter != null) { if (installedAdapter.InstalledVersion == versionInfo) return; installedAdapter.InstalledVersion = versionInfo; } else { adapter.InstalledVersion = versionInfo; installedAdapters.Add(adapter); } InstallOverrideAdapters(xmlPath, installedAdapters); } /// /// Remove an existing adapter from Mediation. /// If adapter is already uninstalled, no action will take place. /// /// The identifier of the adapter /// Thrown if identifier is not valid public virtual void UninstallAdapter(string identifier) { var xmlPath = GetDependenciesPath(); UninstallAdapter(identifier, xmlPath); } internal void UninstallAdapter(string identifier, string xmlPath) { var installedAdapters = GetInstalledAdapters(xmlPath); var adapter = installedAdapters.FirstOrDefault(info => info.Identifier == identifier); if (adapter != null) { installedAdapters.Remove(adapter); InstallOverrideAdapters(xmlPath, installedAdapters); } } void InstallOverrideAdapters(string xmlPath, List adapters) { File.WriteAllText(xmlPath, GenerateXmlContentWithAllDependencies(adapters)); AdaptersChanged?.Invoke(); } public void Apply(bool resolve = false) { AssetDatabase.ImportAsset(GetDependenciesPath()); #if UNITY_ANDROID if (resolve) { MobileDependencyResolverUtils.ResolveIfNeeded(); } #endif } string EnsureDependenciesFileInEditorFolder(string file) { if (Array.IndexOf(file.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), "Editor") == -1) { var editorFolder = Path.Combine(Path.GetDirectoryName(file), "Editor"); var newPath = Path.Combine(editorFolder, k_FileName); Debug.LogWarningFormat($"{k_FileName} is not inside an Editor folder. Moving to: '{newPath}'"); Directory.CreateDirectory(editorFolder); File.Move(file, newPath); var metaFile = file + ".meta"; if (File.Exists(metaFile)) { var newMetaPath = Path.Combine(editorFolder, k_FileName) + ".meta"; File.Move(metaFile, newMetaPath); } AssetDatabase.Refresh(); file = newPath; } return file; } } }