@@ -36,6 +36,9 @@ namespace Dirichlet.Mediation.Editor
private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION" ;
private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION" ;
private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion" ;
private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion" ;
private const string PrefKeyATTDescription = "Dirichlet.iOS.TrackingUsageDescription" ;
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" } ;
/// <summary>
/// <summary>
/// 解析出的 target 信息
/// 解析出的 target 信息
@@ -78,8 +81,8 @@ namespace Dirichlet.Mediation.Editor
// 4. Run pod install
// 4. Run pod install
RunPodInstall ( pathToBuiltProject ) ;
RunPodInstall ( pathToBuiltProject ) ;
// 5. E mbed GDT dynamic frameworks into app target (must run after pod install)
// 5. Fix ad SDK framework/header search paths and e mbed GDT dynamic frameworks (must run after pod install)
EmbedGDTDynamicFrameworks ( pathToBuiltProject , targetInfo ) ;
FixAdSDKIOSFrameworkConfiguration ( pathToBuiltProject , targetInfo ) ;
Debug . Log ( "[DirichletMediation] iOS post-process completed successfully." ) ;
Debug . Log ( "[DirichletMediation] iOS post-process completed successfully." ) ;
}
}
@@ -517,20 +520,16 @@ namespace Dirichlet.Mediation.Editor
}
}
/// <summary>
/// <summary>
/// GDTMobSDK 提供的是预编译动态库( GDTMobSDK.framework, Tquic.framework) ,
/// UnityFramework 链接阶段需要显式搜索广告 SDK xcframework 内部的真机 slice ,
/// 必须 embed 到 app bundle 中才能在运行时加载 。
/// 否则 Xcode 可能报 ld: framework 'GDTMobSDK' not found 或 XCFrameworkIntermediates 搜索路径缺失 。
/// 由于所有 pods 都在 Framework target 上, CocoaPods 不会自动 embed 到 app target ,
/// 同时 GDTMobSDK 提供的是预编译动态库( GDTMobSDK.framework, Tquic.framework) ,
/// 需要在 pod install 之后手动处理。
/// 必须 embed 到 app bundle 中才能在运行时加载。由于所有 pods 都在 Framework target 上,
/// CocoaPods 不会自动 embed 到 app target, 需要在 pod install 之后手动处理。
/// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。
/// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。
/// </summary>
/// </summary>
private static void EmbedGDTDynamicFrameworks ( string projectPath , TargetInfo targetInfo )
private static void FixAdSDKIOSFrameworkConfiguration ( string projectPath , TargetInfo targetInfo )
{
{
var enableGdt = EditorPrefs . GetBool ( "Dirichlet.iOS.EnableGDT" , true ) ;
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 xcodeProjectName = DetectXcodeProjectName ( projectPath ) ;
var projectFilePath = Path . Combine ( projectPath , $"{xcodeProjectName}.xcodeproj/project.pbxproj" ) ;
var projectFilePath = Path . Combine ( projectPath , $"{xcodeProjectName}.xcodeproj/project.pbxproj" ) ;
@@ -543,12 +542,19 @@ namespace Dirichlet.Mediation.Editor
mainTargetGuid = pbxProject . TargetGuidByName ( targetInfo . AppTargetName ) ;
mainTargetGuid = pbxProject . TargetGuidByName ( targetInfo . AppTargetName ) ;
}
}
// GDTMobSDK 的动态框架列表
var targetGuid = ResolveUnityFrameworkTargetGuid ( pbxProject , targetInfo ) ;
var dynamicFrameworkNames = new [ ] { "GDTMob SDK.f ramework" , "Tquic.framework" } ;
var frameworkSearchPaths = GetAd SDKF rameworkSearchPaths ( ) ;
var headerSearchPaths = GetAdSDKHeaderSearchPaths ( frameworkSearchPaths ) ;
var searchPathsAdded = AddAdSDKSearchPaths ( pbxProject , targetGuid , frameworkSearchPaths , headerSearchPaths ) ;
PatchPodsXCConfigSearchPaths ( projectPath , targetInfo , frameworkSearchPaths , headerSearchPaths ) ;
var podsDir = Path . Combine ( projectPath , "Pods" ) ;
var podsDir = Path . Combine ( projectPath , "Pods" ) ;
var embedded = 0 ;
var embedded = 0 ;
foreach ( var frameworkName in dynamicFrameworkNames )
if ( enableGdt )
{
foreach ( var frameworkName in GDTDynamicFrameworkNames )
{
{
var frameworkPath = FindDynamicFramework ( podsDir , frameworkName ) ;
var frameworkPath = FindDynamicFramework ( podsDir , frameworkName ) ;
if ( string . IsNullOrEmpty ( frameworkPath ) )
if ( string . IsNullOrEmpty ( frameworkPath ) )
@@ -566,12 +572,251 @@ namespace Dirichlet.Mediation.Editor
embedded + + ;
embedded + + ;
Debug . Log ( $"[DirichletMediation] Embedded dynamic framework: {frameworkName}" ) ;
Debug . Log ( $"[DirichletMediation] Embedded dynamic framework: {frameworkName}" ) ;
}
}
}
if ( embedded > 0 )
else
{
{
Debug . Log ( "[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding" ) ;
}
pbxProject . WriteToFile ( projectFilePath ) ;
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}" ) ;
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 )
{
continue ;
}
File . WriteAllLines ( xcconfigPath , lines ) ;
patched + + ;
}
Debug . Log ( $"[DirichletMediation] Patched {patched} CocoaPods xcconfig search path file(s) for {targetInfo.FrameworkTargetName}" ) ;
}
private static bool UpsertXCConfigBuildSetting ( System . Collections . Generic . List < string > lines , string propertyName , string [ ] searchPaths )
{
var insertIndex = - 1 ;
var originalLines = new System . Collections . Generic . List < string > ( ) ;
var tokens = new System . Collections . Generic . List < string > ( ) ;
for ( var i = lines . Count - 1 ; i > = 0 ; i - - )
{
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 < string > 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 < string > tokens , string value )
{
var normalizedValue = NormalizeXCConfigToken ( value ) ;
return tokens . Any ( token = > NormalizeXCConfigToken ( token ) = = normalizedValue ) ;
}
private static void AddXCConfigTokenIfMissing ( System . Collections . Generic . List < string > 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 < string >
{
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" ;
}
}
/// <summary>
/// <summary>