Skip to content

Commit 4758e58

Browse files
committed
Remove direct dependency on Unity's iOS dll
1 parent 8cf54b6 commit 4758e58

File tree

2 files changed

+206
-54
lines changed

2 files changed

+206
-54
lines changed

source/IOSResolver/IOSResolver.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@
4343
<Reference Include="UnityEngine">
4444
<HintPath>$(UnityHintPath)/UnityEngine.dll</HintPath>
4545
</Reference>
46-
<Reference Include="UnityEditor.iOS.Extensions.Xcode">
47-
<HintPath Condition="Exists('$(UnityIosPath)/UnityEditor.iOS.Extensions.Xcode.dll')">$(UnityIosPath)/UnityEditor.iOS.Extensions.Xcode.dll</HintPath>
48-
<HintPath Condition="Exists('$(UnityIosPath)/Unity.iOS.Extensions.Xcode.dll')">$(UnityIosPath)/Unity.iOS.Extensions.Xcode.dll</HintPath>
49-
</Reference>
5046
<Reference Include="System" />
5147
<Reference Include="System.Core" />
5248
<Reference Include="System.Xml" />

source/IOSResolver/src/IOSResolver.cs

Lines changed: 206 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17-
#if UNITY_IOS
1817
using Google;
1918
using GooglePlayServices;
2019
using Google.JarResolver;
@@ -719,6 +718,123 @@ private static Assembly ResolveUnityEditoriOSXcodeExtension(
719718
return assembly;
720719
}
721720

721+
private static Type pbxProjectType = null;
722+
private static bool loadedPbxProjectType = false;
723+
private static MethodInfo pbxProjectReadFromString = null;
724+
private static MethodInfo pbxProjectAddFileToBuild = null;
725+
private static MethodInfo pbxProjectAddFile = null;
726+
private static MethodInfo pbxProjectAddBuildProperty = null;
727+
private static MethodInfo pbxProjectSetBuildProperty = null;
728+
private static MethodInfo pbxProjectWriteToFile = null;
729+
private static MethodInfo pbxProjectWriteToString = null;
730+
731+
private static MethodInfo LoadPbxMethodViaReflection(string name, Type[] parameters) {
732+
var methodInfo = pbxProjectType.GetMethod(name,
733+
BindingFlags.Public | BindingFlags.Instance,
734+
null, parameters, null);
735+
if (methodInfo == null) {
736+
throw new Exception($"Unable to load method: {name}");
737+
}
738+
return methodInfo;
739+
}
740+
741+
private static Type GetPbxProjectType() {
742+
if (loadedPbxProjectType) {
743+
return pbxProjectType;
744+
}
745+
746+
// The type name including its namespace
747+
string fullTypeName = "UnityEditor.iOS.Xcode.PBXProject";
748+
// The name of the assembly (without the .dll extension)
749+
string assemblyName = "UnityEditor.iOS.Extensions.Xcode";
750+
751+
try {
752+
// First, try to get the type assuming the assembly is already loaded.
753+
// This is often the case if the iOS module is installed and active.
754+
pbxProjectType = Type.GetType(fullTypeName + ", " + assemblyName);
755+
756+
/*
757+
if (pbxProjectType == null) {
758+
// If not found, iterate through all loaded assemblies.
759+
// This is a more robust fallback if the assembly name guess
760+
// or Type.GetType's search path isn't sufficient.
761+
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
762+
// Check if this assembly is the one we're looking for.
763+
// The actual assembly name might sometimes vary slightly or
764+
// not be exactly what's expected, so checking the type directly
765+
// can be more reliable.
766+
if (assembly.GetName().Name == assemblyName) {
767+
pbxProjectType = assembly.GetType(fullTypeName);
768+
if (pbxProjectType != null) {
769+
// Found it, exit out.
770+
break;
771+
}
772+
}
773+
}
774+
}*/
775+
776+
// Get the Enum type loaded in first, since one of the methods needs it.
777+
GetPbxSourceTreeEnumSource();
778+
779+
// Load the various methods, throwing if failing to find any.
780+
pbxProjectReadFromString = LoadPbxMethodViaReflection("ReadFromString",
781+
new Type[] { typeof(string) });
782+
pbxProjectAddFileToBuild = LoadPbxMethodViaReflection("AddFileToBuild",
783+
new Type[] { typeof(string), typeof(string) });
784+
pbxProjectAddFile = LoadPbxMethodViaReflection("AddFile",
785+
new Type[] { typeof(string), typeof(string), pbxSourceTreeEnumType });
786+
pbxProjectAddBuildProperty = LoadPbxMethodViaReflection("AddBuildProperty",
787+
new Type[] { typeof(string), typeof(string), typeof(string) });
788+
pbxProjectSetBuildProperty = LoadPbxMethodViaReflection("SetBuildProperty",
789+
new Type[] { typeof(string), typeof(string), typeof(string) });
790+
pbxProjectWriteToFile = LoadPbxMethodViaReflection("WriteToFile",
791+
new Type[] { typeof(string) });
792+
pbxProjectWriteToString = LoadPbxMethodViaReflection("WriteToString",
793+
Type.EmptyTypes);
794+
} catch (Exception e) {
795+
Debug.LogWarning($"Exception while trying to find PBXProject type: {e}");
796+
pbxProjectType = null;
797+
}
798+
loadedPbxProjectType = true;
799+
return pbxProjectType;
800+
}
801+
802+
private static Type pbxSourceTreeEnumType = null;
803+
private static object pbxSourceTreeEnum_Source = null;
804+
private static bool loadedPbxSourceTreeEnum = false;
805+
806+
private static object GetPbxSourceTreeEnumSource() {
807+
if (loadedPbxSourceTreeEnum) {
808+
return pbxSourceTreeEnum_Source;
809+
}
810+
811+
// The type name including its namespace
812+
string fullTypeName = "UnityEditor.iOS.Xcode.PBXSourceTree";
813+
// The name of the assembly (without the .dll extension)
814+
string assemblyName = "UnityEditor.iOS.Extensions.Xcode";
815+
816+
try {
817+
// First, try to get the type assuming the assembly is already loaded.
818+
// This is often the case if the iOS module is installed and active.
819+
pbxSourceTreeEnumType = Type.GetType(fullTypeName + ", " + assemblyName);
820+
821+
if (pbxSourceTreeEnumType == null) {
822+
pbxSourceTreeEnum_Source = null;
823+
loadedPbxSourceTreeEnum = true;
824+
return null;
825+
}
826+
827+
// Get and cache the specific PBXSourceTree.Source value
828+
pbxSourceTreeEnum_Source = Enum.Parse(pbxSourceTreeEnumType, "Source");
829+
} catch (Exception e) {
830+
Debug.LogWarning($"Exception while trying to find PBXSourceTree enum: {e}");
831+
pbxSourceTreeEnumType = null;
832+
pbxSourceTreeEnum_Source = null;
833+
}
834+
loadedPbxSourceTreeEnum = true;
835+
return pbxSourceTreeEnum_Source;
836+
}
837+
722838
/// <summary>
723839
/// Initialize the module.
724840
/// </summary>
@@ -843,7 +959,9 @@ public static void SettingsDialog() {
843959
/// </summary>
844960
internal static bool MultipleXcodeTargetsSupported {
845961
get {
846-
return typeof(UnityEditor.iOS.Xcode.PBXProject).GetMethod(
962+
Type pbxProject = GetPbxProjectType();
963+
if (pbxProject == null) return false;
964+
return pbxProject.GetMethod(
847965
"GetUnityMainTargetGuid", Type.EmptyTypes) != null;
848966
}
849967
}
@@ -856,8 +974,9 @@ public static string XcodeMainTargetName {
856974
// NOTE: Unity-iPhone is hard coded in UnityEditor.iOS.Xcode.PBXProject and will no
857975
// longer be exposed via GetUnityTargetName(). It hasn't changed in many years though
858976
// so we'll use this constant as a relatively safe default.
859-
return MultipleXcodeTargetsSupported ? "Unity-iPhone" :
860-
(string)VersionHandler.InvokeStaticMethod(typeof(UnityEditor.iOS.Xcode.PBXProject),
977+
Type pbxProject = GetPbxProjectType();
978+
return (pbxProject == null || MultipleXcodeTargetsSupported) ? "Unity-iPhone" :
979+
(string)VersionHandler.InvokeStaticMethod(GetPbxProjectType(),
861980
"GetUnityTargetName", null);
862981
}
863982
}
@@ -2035,7 +2154,6 @@ public static IEnumerable<string> GetXcodeTargetGuids(object xcodeProject) {
20352154
/// <returns>List of target GUIDs.</returns>
20362155
public static IEnumerable<string> GetXcodeTargetGuids(object xcodeProject,
20372156
bool includeAllTargets) {
2038-
var project = (UnityEditor.iOS.Xcode.PBXProject)xcodeProject;
20392157
var targets = new List<string>();
20402158
if (MultipleXcodeTargetsSupported) {
20412159
// In Unity 2019.3+ TargetGuidByName will throw an exception if the Unity-iPhone target
@@ -2047,7 +2165,7 @@ public static IEnumerable<string> GetXcodeTargetGuids(object xcodeProject,
20472165
guidMethods.Add("GetUnityMainTargetGuid");
20482166
}
20492167
foreach (var guidMethod in guidMethods) {
2050-
targets.Add((string)VersionHandler.InvokeInstanceMethod(project, guidMethod,
2168+
targets.Add((string)VersionHandler.InvokeInstanceMethod(xcodeProject, guidMethod,
20512169
null));
20522170
}
20532171
} catch (Exception exception) {
@@ -2062,7 +2180,7 @@ public static IEnumerable<string> GetXcodeTargetGuids(object xcodeProject,
20622180
// TargetGuidByName in Unity < 2019.3 can be used to lookup any target GUID.
20632181
foreach (var targetName in XcodeTargetNames) {
20642182
targets.Add((string)VersionHandler.InvokeInstanceMethod(
2065-
project, "TargetGuidByName", new [] { (object)targetName }));
2183+
xcodeProject, "TargetGuidByName", new [] { (object)targetName }));
20662184
}
20672185
}
20682186
return targets;
@@ -2083,53 +2201,86 @@ internal static void PatchProject(
20832201
}
20842202
// Configure project settings for CocoaPods.
20852203
string pbxprojPath = GetProjectPath(pathToBuiltProject);
2086-
var project = new UnityEditor.iOS.Xcode.PBXProject();
2087-
project.ReadFromString(File.ReadAllText(pbxprojPath));
2204+
var pbxProjectType = GetPbxProjectType();
2205+
if (pbxProjectType == null) {
2206+
return;
2207+
}
2208+
var project = Activator.CreateInstance(pbxProjectType);
2209+
pbxProjectReadFromString.Invoke(project, new object[] { File.ReadAllText(pbxprojPath) });
20882210
foreach (var target in GetXcodeTargetGuids(project)) {
2089-
project.SetBuildProperty(target, "CLANG_ENABLE_MODULES", "YES");
2090-
project.AddBuildProperty(target, "OTHER_LDFLAGS", "-ObjC");
2211+
pbxProjectSetBuildProperty.Invoke(project, new object[] {
2212+
target, "CLANG_ENABLE_MODULES", "YES"
2213+
});
2214+
pbxProjectAddBuildProperty.Invoke(target, new object[] {
2215+
target, "OTHER_LDFLAGS", "-ObjC"
2216+
});
20912217
// GTMSessionFetcher requires Obj-C exceptions.
2092-
project.SetBuildProperty(target, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
2218+
pbxProjectSetBuildProperty.Invoke(project, new object[] {
2219+
target, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES"
2220+
});
20932221
if (bitcodeDisabled) {
2094-
project.AddBuildProperty(target, "ENABLE_BITCODE", "NO");
2222+
pbxProjectAddBuildProperty.Invoke(project, new object[] {
2223+
target, "ENABLE_BITCODE", "NO"
2224+
});
20952225
}
20962226
}
2097-
File.WriteAllText(pbxprojPath, project.WriteToString());
2227+
var contents = pbxProjectWriteToString.Invoke(project, null) as string;
2228+
File.WriteAllText(pbxprojPath, contents);
20982229
}
20992230

21002231
internal static void AddDummySwiftFile(
21012232
BuildTarget buildTarget, string pathToBuiltProject) {
2102-
string pbxprojPath = GetProjectPath(pathToBuiltProject);
2103-
var project = new UnityEditor.iOS.Xcode.PBXProject();
2104-
project.ReadFromString(File.ReadAllText(pbxprojPath));
2105-
2106-
string DUMMY_SWIFT_FILE_NAME = "Dummy.swift";
2107-
string DUMMY_SWIFT_FILE_CONTENT =
2108-
"// Generated by External Dependency Manager for Unity\n" +
2109-
"import Foundation";
2110-
string dummySwiftPath = Path.Combine(pathToBuiltProject, DUMMY_SWIFT_FILE_NAME);
2111-
if (!File.Exists(dummySwiftPath)) {
2112-
File.WriteAllText(dummySwiftPath, DUMMY_SWIFT_FILE_CONTENT);
2113-
}
2114-
2115-
foreach (var target in GetXcodeTargetGuids(project, includeAllTargets: false)) {
2116-
project.AddFileToBuild(
2117-
target,
2118-
project.AddFile(DUMMY_SWIFT_FILE_NAME,
2119-
DUMMY_SWIFT_FILE_NAME,
2120-
UnityEditor.iOS.Xcode.PBXSourceTree.Source));
2121-
if(!string.IsNullOrEmpty(SwiftLanguageVersion)) {
2122-
project.SetBuildProperty(target, "SWIFT_VERSION", SwiftLanguageVersion);
2233+
try {
2234+
string pbxprojPath = GetProjectPath(pathToBuiltProject);
2235+
Type pbxProjectType = GetPbxProjectType();
2236+
if (pbxProjectType == null) {
2237+
return;
2238+
}
2239+
var project = Activator.CreateInstance(pbxProjectType);
2240+
pbxProjectReadFromString.Invoke(project, new object[] { File.ReadAllText(pbxprojPath) });
2241+
2242+
string DUMMY_SWIFT_FILE_NAME = "Dummy.swift";
2243+
string DUMMY_SWIFT_FILE_CONTENT =
2244+
"// Generated by External Dependency Manager for Unity\n" +
2245+
"import Foundation";
2246+
string dummySwiftPath = Path.Combine(pathToBuiltProject, DUMMY_SWIFT_FILE_NAME);
2247+
if (!File.Exists(dummySwiftPath)) {
2248+
File.WriteAllText(dummySwiftPath, DUMMY_SWIFT_FILE_CONTENT);
21232249
}
21242250

2125-
// These build properties are only required for multi-target Xcode project, which is
2126-
// generated from 2019.3+.
2127-
if(MultipleXcodeTargetsSupported) {
2128-
project.SetBuildProperty(target, "CLANG_ENABLE_MODULES", "YES");
2251+
foreach (var target in GetXcodeTargetGuids(project, includeAllTargets: false)) {
2252+
pbxProjectAddFileToBuild.Invoke(project, new object[] {
2253+
target,
2254+
pbxProjectAddFile.Invoke(project, new object[] {
2255+
DUMMY_SWIFT_FILE_NAME,
2256+
DUMMY_SWIFT_FILE_NAME,
2257+
GetPbxSourceTreeEnumSource()
2258+
}) as string
2259+
});
2260+
if(!string.IsNullOrEmpty(SwiftLanguageVersion)) {
2261+
pbxProjectSetBuildProperty.Invoke(project, new object[] {
2262+
target,
2263+
"SWIFT_VERSION",
2264+
SwiftLanguageVersion
2265+
});
2266+
}
2267+
2268+
// These build properties are only required for multi-target Xcode project, which is
2269+
// generated from 2019.3+.
2270+
if(MultipleXcodeTargetsSupported) {
2271+
pbxProjectSetBuildProperty.Invoke(project, new object[] {
2272+
target,
2273+
"CLANG_ENABLE_MODULES",
2274+
"YES"
2275+
});
2276+
}
21292277
}
2130-
}
21312278

2132-
File.WriteAllText(pbxprojPath, project.WriteToString());
2279+
var contents = pbxProjectWriteToString.Invoke(project, null) as string;
2280+
File.WriteAllText(pbxprojPath, contents);
2281+
} catch (Exception ex) {
2282+
Debug.LogWarning($"Failed to add Dummy Swift file to Xcode project: {ex}");
2283+
}
21332284
}
21342285

21352286
/// <summary>
@@ -2319,7 +2470,6 @@ public static void GenPodfile(BuildTarget buildTarget,
23192470
default:
23202471
throw new Exception("IOSResolver.GenPodfile() invoked for a " +
23212472
"build target other than iOS or tvOS.");
2322-
break;
23232473
}
23242474

23252475
foreach (var target in XcodeTargetNames) {
@@ -2866,16 +3016,24 @@ public static void UpdateProjectDeps(
28663016
}
28673017

28683018
var pbxprojPath = GetProjectPath(pathToBuiltProject);
2869-
var project = new UnityEditor.iOS.Xcode.PBXProject();
2870-
project.ReadFromString(File.ReadAllText(pbxprojPath));
3019+
var pbxProjectType = GetPbxProjectType();
3020+
if (pbxProjectType == null) {
3021+
return;
3022+
}
3023+
var project = Activator.CreateInstance(pbxProjectType);
3024+
pbxProjectReadFromString.Invoke(project, new object[] { File.ReadAllText(pbxprojPath) });
28713025
foreach (var target in GetXcodeTargetGuids(project)) {
2872-
var guid = project.AddFile(
3026+
var guid = pbxProjectAddFile.Invoke(project, new object[] {
28733027
Path.Combine(podsDir, PODS_PROJECT_NAME + ".xcodeproj"),
28743028
"Pods.xcodeproj",
2875-
UnityEditor.iOS.Xcode.PBXSourceTree.Source);
2876-
project.AddFileToBuild(target, guid);
3029+
GetPbxSourceTreeEnumSource()
3030+
}) as string;
3031+
3032+
pbxProjectAddFileToBuild.Invoke(project, new object[] {
3033+
target, guid
3034+
});
28773035
}
2878-
project.WriteToFile(pbxprojPath);
3036+
pbxProjectWriteToFile.Invoke(project, new object[] { pbxprojPath });
28793037
}
28803038

28813039
/// <summary>
@@ -2900,5 +3058,3 @@ private static void RefreshXmlDependencies() {
29003058
}
29013059

29023060
} // namespace Google
2903-
2904-
#endif // UNITY_IOS

0 commit comments

Comments
 (0)