From 894178a4c78d1ded6fab3adb4201cf5c14ee8dd9 Mon Sep 17 00:00:00 2001 From: "CORE-FOLDCC\\Core" <1813547935@qq.com> Date: Sat, 13 Jun 2026 06:28:03 +0800 Subject: [PATCH] fix: repair iOS ad SDK framework search paths --- Assets/CHANGELOG.md | 8 + .../DirichletMediationIOSPostProcessor.cs | 303 ++++++++++++++++-- Assets/package.json | 2 +- README.md | 2 +- 4 files changed, 284 insertions(+), 31 deletions(-) diff --git a/Assets/CHANGELOG.md b/Assets/CHANGELOG.md index b248a6e..2c9bed4 100644 --- a/Assets/CHANGELOG.md +++ b/Assets/CHANGELOG.md @@ -1,3 +1,11 @@ +# [1.0.3] + +### 修复 + +* iOS 导出后自动修复 `UnityFramework` target 的广告 SDK `FRAMEWORK_SEARCH_PATHS` / `HEADER_SEARCH_PATHS`,覆盖 Dirichlet、CSJ、GDT、DRA、GDTMobSDK、Tquic、BUAdSDK 的真实 xcframework slice。 +* iOS 导出后清理 CocoaPods `Pods-UnityFramework*.xcconfig` 中的 `XCFrameworkIntermediates` 搜索路径,避免 Xcode 真机 Play 时出现 framework not found 或 search path not found。 +* 保持签名配置由业务工程负责,广告模块不写入 Team、provisioning profile 或 code signing 设置。 + # [1.0.1] ### 调整 diff --git a/Assets/DirichletMediation/Editor/DirichletMediationIOSPostProcessor.cs b/Assets/DirichletMediation/Editor/DirichletMediationIOSPostProcessor.cs index 7fbd79e..b36f6c6 100644 --- a/Assets/DirichletMediation/Editor/DirichletMediationIOSPostProcessor.cs +++ b/Assets/DirichletMediation/Editor/DirichletMediationIOSPostProcessor.cs @@ -36,6 +36,9 @@ namespace Dirichlet.Mediation.Editor private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION"; private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion"; private const string PrefKeyATTDescription = "Dirichlet.iOS.TrackingUsageDescription"; + private const string IOSDeviceSlice = "ios-arm64"; + private const string TquicIOSDeviceSlice = "ios-arm64_armv7"; + private static readonly string[] GDTDynamicFrameworkNames = { "GDTMobSDK.framework", "Tquic.framework" }; /// /// 解析出的 target 信息 @@ -78,8 +81,8 @@ namespace Dirichlet.Mediation.Editor // 4. Run pod install RunPodInstall(pathToBuiltProject); - // 5. Embed GDT dynamic frameworks into app target (must run after pod install) - EmbedGDTDynamicFrameworks(pathToBuiltProject, targetInfo); + // 5. Fix ad SDK framework/header search paths and embed GDT dynamic frameworks (must run after pod install) + FixAdSDKIOSFrameworkConfiguration(pathToBuiltProject, targetInfo); Debug.Log("[DirichletMediation] iOS post-process completed successfully."); } @@ -517,20 +520,16 @@ namespace Dirichlet.Mediation.Editor } /// - /// GDTMobSDK 提供的是预编译动态库(GDTMobSDK.framework, Tquic.framework), - /// 必须 embed 到 app bundle 中才能在运行时加载。 - /// 由于所有 pods 都在 Framework target 上,CocoaPods 不会自动 embed 到 app target, - /// 需要在 pod install 之后手动处理。 + /// UnityFramework 链接阶段需要显式搜索广告 SDK xcframework 内部的真机 slice, + /// 否则 Xcode 可能报 ld: framework 'GDTMobSDK' not found 或 XCFrameworkIntermediates 搜索路径缺失。 + /// 同时 GDTMobSDK 提供的是预编译动态库(GDTMobSDK.framework, Tquic.framework), + /// 必须 embed 到 app bundle 中才能在运行时加载。由于所有 pods 都在 Framework target 上, + /// CocoaPods 不会自动 embed 到 app target,需要在 pod install 之后手动处理。 /// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。 /// - private static void EmbedGDTDynamicFrameworks(string projectPath, TargetInfo targetInfo) + private static void FixAdSDKIOSFrameworkConfiguration(string projectPath, TargetInfo targetInfo) { var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true); - if (!enableGdt) - { - Debug.Log("[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding"); - return; - } var xcodeProjectName = DetectXcodeProjectName(projectPath); var projectFilePath = Path.Combine(projectPath, $"{xcodeProjectName}.xcodeproj/project.pbxproj"); @@ -543,35 +542,281 @@ namespace Dirichlet.Mediation.Editor mainTargetGuid = pbxProject.TargetGuidByName(targetInfo.AppTargetName); } - // GDTMobSDK 的动态框架列表 - var dynamicFrameworkNames = new[] { "GDTMobSDK.framework", "Tquic.framework" }; + var targetGuid = ResolveUnityFrameworkTargetGuid(pbxProject, targetInfo); + var frameworkSearchPaths = GetAdSDKFrameworkSearchPaths(); + var headerSearchPaths = GetAdSDKHeaderSearchPaths(frameworkSearchPaths); + + var searchPathsAdded = AddAdSDKSearchPaths(pbxProject, targetGuid, frameworkSearchPaths, headerSearchPaths); + PatchPodsXCConfigSearchPaths(projectPath, targetInfo, frameworkSearchPaths, headerSearchPaths); + var podsDir = Path.Combine(projectPath, "Pods"); var embedded = 0; - foreach (var frameworkName in dynamicFrameworkNames) + if (enableGdt) { - var frameworkPath = FindDynamicFramework(podsDir, frameworkName); - if (string.IsNullOrEmpty(frameworkPath)) + foreach (var frameworkName in GDTDynamicFrameworkNames) + { + var frameworkPath = FindDynamicFramework(podsDir, frameworkName); + if (string.IsNullOrEmpty(frameworkPath)) + { + Debug.LogWarning($"[DirichletMediation] Dynamic framework not found: {frameworkName}"); + continue; + } + + var relativePath = frameworkPath.StartsWith(projectPath) + ? frameworkPath.Substring(projectPath.Length + 1) + : frameworkPath; + + var fileGuid = pbxProject.AddFile(relativePath, "Frameworks/" + frameworkName); + PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid); + embedded++; + Debug.Log($"[DirichletMediation] Embedded dynamic framework: {frameworkName}"); + } + } + else + { + Debug.Log("[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding"); + } + + pbxProject.WriteToFile(projectFilePath); + if (searchPathsAdded) + { + Debug.Log($"[DirichletMediation] Added ad SDK framework/header search paths to {targetInfo.FrameworkTargetName}"); + } + Debug.Log($"[DirichletMediation] Embedded {embedded} GDT dynamic frameworks into {targetInfo.AppTargetName}"); + } + + private static string ResolveUnityFrameworkTargetGuid(PBXProject pbxProject, TargetInfo targetInfo) + { +#if UNITY_2019_3_OR_NEWER + var targetGuid = pbxProject.GetUnityFrameworkTargetGuid(); +#else + var targetGuid = targetInfo.FrameworkTargetGuid; +#endif + if (string.IsNullOrEmpty(targetGuid)) + { + targetGuid = targetInfo.FrameworkTargetGuid; + } + if (string.IsNullOrEmpty(targetGuid)) + { + targetGuid = pbxProject.TargetGuidByName(targetInfo.FrameworkTargetName); + } + + return targetGuid; + } + + private static bool AddAdSDKSearchPaths(PBXProject pbxProject, string targetGuid, string[] frameworkSearchPaths, string[] headerSearchPaths) + { + if (string.IsNullOrEmpty(targetGuid)) + { + Debug.LogWarning("[DirichletMediation] UnityFramework target GUID is empty, skipping ad SDK search paths"); + return false; + } + + pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(inherited)"); + foreach (var searchPath in frameworkSearchPaths) + { + pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", searchPath); + } + + pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(inherited)"); + foreach (var searchPath in headerSearchPaths) + { + pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", searchPath); + } + + return true; + } + + private static void PatchPodsXCConfigSearchPaths( + string projectPath, + TargetInfo targetInfo, + string[] frameworkSearchPaths, + string[] headerSearchPaths) + { + var targetSupportRoot = Path.Combine(projectPath, "Pods", "Target Support Files"); + if (!Directory.Exists(targetSupportRoot)) + { + Debug.LogWarning("[DirichletMediation] CocoaPods target support files not found, skipping xcconfig search path patch"); + return; + } + + var targetPrefix = "Pods-" + targetInfo.FrameworkTargetName; + var xcconfigFiles = Directory.GetFiles(targetSupportRoot, "*.xcconfig", SearchOption.AllDirectories) + .Where(path => Path.GetFileNameWithoutExtension(path).StartsWith(targetPrefix, System.StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + if (xcconfigFiles.Length == 0) + { + Debug.LogWarning($"[DirichletMediation] No CocoaPods xcconfig found for {targetInfo.FrameworkTargetName}, skipping search path patch"); + return; + } + + var patched = 0; + foreach (var xcconfigPath in xcconfigFiles) + { + var lines = File.ReadAllLines(xcconfigPath).ToList(); + var changed = UpsertXCConfigBuildSetting(lines, "FRAMEWORK_SEARCH_PATHS", frameworkSearchPaths); + changed |= UpsertXCConfigBuildSetting(lines, "HEADER_SEARCH_PATHS", headerSearchPaths); + + if (!changed) { - Debug.LogWarning($"[DirichletMediation] Dynamic framework not found: {frameworkName}"); continue; } - var relativePath = frameworkPath.StartsWith(projectPath) - ? frameworkPath.Substring(projectPath.Length + 1) - : frameworkPath; - - var fileGuid = pbxProject.AddFile(relativePath, "Frameworks/" + frameworkName); - PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid); - embedded++; - Debug.Log($"[DirichletMediation] Embedded dynamic framework: {frameworkName}"); + File.WriteAllLines(xcconfigPath, lines); + patched++; } - if (embedded > 0) + Debug.Log($"[DirichletMediation] Patched {patched} CocoaPods xcconfig search path file(s) for {targetInfo.FrameworkTargetName}"); + } + + private static bool UpsertXCConfigBuildSetting(System.Collections.Generic.List lines, string propertyName, string[] searchPaths) + { + var insertIndex = -1; + var originalLines = new System.Collections.Generic.List(); + var tokens = new System.Collections.Generic.List(); + + for (var i = lines.Count - 1; i >= 0; i--) { - pbxProject.WriteToFile(projectFilePath); - Debug.Log($"[DirichletMediation] Embedded {embedded} GDT dynamic frameworks into {targetInfo.AppTargetName}"); + if (!IsXCConfigBuildSettingLine(lines[i], propertyName)) + { + continue; + } + + insertIndex = i; + originalLines.Add(lines[i]); + CollectXCConfigBuildSettingTokens(lines[i], tokens); + lines.RemoveAt(i); } + + if (!ContainsXCConfigToken(tokens, "$(inherited)")) + { + tokens.Insert(0, "$(inherited)"); + } + + foreach (var searchPath in searchPaths) + { + AddXCConfigTokenIfMissing(tokens, searchPath); + } + + var newLine = $"{propertyName} = {string.Join(" ", tokens)}"; + + if (insertIndex < 0) + { + lines.Add(newLine); + return true; + } + + lines.Insert(insertIndex, newLine); + return originalLines.Count != 1 || originalLines[0] != newLine; + } + + private static void CollectXCConfigBuildSettingTokens(string line, System.Collections.Generic.List tokens) + { + var separatorIndex = line.IndexOf('='); + if (separatorIndex < 0 || separatorIndex >= line.Length - 1) + { + return; + } + + var value = line.Substring(separatorIndex + 1).Trim(); + var lineTokens = value.Split(new[] { ' ', '\t' }, System.StringSplitOptions.RemoveEmptyEntries); + foreach (var token in lineTokens) + { + if (token.IndexOf("XCFrameworkIntermediates", System.StringComparison.OrdinalIgnoreCase) >= 0) + { + continue; + } + + AddXCConfigTokenIfMissing(tokens, token); + } + } + + private static bool ContainsXCConfigToken(System.Collections.Generic.List tokens, string value) + { + var normalizedValue = NormalizeXCConfigToken(value); + return tokens.Any(token => NormalizeXCConfigToken(token) == normalizedValue); + } + + private static void AddXCConfigTokenIfMissing(System.Collections.Generic.List tokens, string value) + { + if (!ContainsXCConfigToken(tokens, value)) + { + tokens.Add(value); + } + } + + private static string NormalizeXCConfigToken(string token) + { + return (token ?? string.Empty).Trim().Trim('"'); + } + + private static bool IsXCConfigBuildSettingLine(string line, string propertyName) + { + if (string.IsNullOrWhiteSpace(line)) + { + return false; + } + + var trimmed = line.TrimStart(); + return trimmed.StartsWith(propertyName + " =", System.StringComparison.Ordinal) + || trimmed.StartsWith(propertyName + "=", System.StringComparison.Ordinal); + } + + private static string[] GetAdSDKFrameworkSearchPaths() + { + var sdkVersion = ResolveIOSSDKVersion(); + var enableCsj = EditorPrefs.GetBool("Dirichlet.iOS.EnableCSJ", true); + var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true); + var paths = new System.Collections.Generic.List + { + VersionedDirichletFrameworkSearchPath("DirichletAdSDK", sdkVersion), + VersionedDirichletFrameworkSearchPath("DirichletCoreSDK", sdkVersion), + VersionedDirichletFrameworkSearchPath("DirichletMediationSDK", sdkVersion), + VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterDRA", sdkVersion) + }; + + if (enableCsj) + { + paths.Add(VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterCSJ", sdkVersion)); + paths.Add("$(PODS_ROOT)/Ads-CN/SDK/BUAdSDK.xcframework/" + IOSDeviceSlice); + } + + if (enableGdt) + { + paths.Add(VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterGDT", sdkVersion)); + paths.Add("$(PODS_ROOT)/GDTMobSDK/GDTFramework/GDTMobSDK.xcframework/" + IOSDeviceSlice); + paths.Add("$(PODS_ROOT)/GDTMobSDK/GDTFramework/Tquic.xcframework/" + TquicIOSDeviceSlice); + } + + return paths.ToArray(); + } + + private static string[] GetAdSDKHeaderSearchPaths(string[] frameworkSearchPaths) + { + return frameworkSearchPaths + .Select(ToFrameworkHeaderSearchPath) + .Where(path => !string.IsNullOrEmpty(path)) + .ToArray(); + } + + private static string VersionedDirichletFrameworkSearchPath(string frameworkName, string sdkVersion) + { + return $"$(PODS_ROOT)/{frameworkName}/{frameworkName}-{sdkVersion}/{frameworkName}.xcframework/{IOSDeviceSlice}"; + } + + private static string ToFrameworkHeaderSearchPath(string frameworkSearchPath) + { + var parts = frameworkSearchPath.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries); + var xcframeworkPart = parts.LastOrDefault(part => part.EndsWith(".xcframework", System.StringComparison.OrdinalIgnoreCase)); + if (string.IsNullOrEmpty(xcframeworkPart)) + { + return null; + } + + var frameworkName = xcframeworkPart.Substring(0, xcframeworkPart.Length - ".xcframework".Length); + return $"{frameworkSearchPath}/{frameworkName}.framework/Headers"; } /// diff --git a/Assets/package.json b/Assets/package.json index 792adf4..cbcf410 100644 --- a/Assets/package.json +++ b/Assets/package.json @@ -2,7 +2,7 @@ "name": "com.commercialization.tapadn", "displayName": "Commercialization.tapadn", "description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.", - "version": "1.0.2", + "version": "1.0.3", "unity": "2022.3", "license": "MIT", "repository": { diff --git a/README.md b/README.md index 20c9290..19e45e8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ```json { "com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15", - "com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git#1.0.1" + "com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git#1.0.3" } ```