Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/IOSResolver/IOSResolver.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Google.JarResolver">
<HintPath>..\AndroidResolver\bin\Release\Google.JarResolver.dll</HintPath>
</Reference>
Expand All @@ -68,6 +69,7 @@
<ItemGroup>
<Compile Include="src\IOSResolver.cs" />
<Compile Include="src\IOSResolverSettingsDialog.cs" />
<Compile Include="src\SwiftPackageManager.cs" />
<Compile Include="src\VersionNumber.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
Expand Down
102 changes: 92 additions & 10 deletions source/IOSResolver/src/IOSResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,12 @@ protected override bool Read(string filename, Logger logger) {
private static SortedDictionary<string, Pod> pods =
new SortedDictionary<string, Pod>();

// List of pods to ignore because they are replaced by Swift packages.
private static HashSet<string> podsToIgnore = new HashSet<string>();
// Flag used to denoted if generating the Podfile was skipped because of no pods.
// Used by subsequent steps to know they should be skipped as well.
private static bool podfileGenerationSkipped = false;

// Order of post processing operations.
private const int BUILD_ORDER_REFRESH_DEPENDENCIES = 10;
private const int BUILD_ORDER_CHECK_COCOAPODS_INSTALL = 20;
Expand Down Expand Up @@ -510,6 +516,14 @@ protected override bool Read(string filename, Logger logger) {
private const string PREFERENCE_PODFILE_ALLOW_PODS_IN_MULTIPLE_TARGETS =
PREFERENCE_NAMESPACE + "PodfileAllowPodsInMultipleTargets";

// Whether to allow empty Podfile generation.
private const string PREFERENCE_ALLOW_EMPTY_PODFILE_GENERATION =
PREFERENCE_NAMESPACE + "AllowEmptyPodfileGeneration";

// Whether to use Swift Package Manager for dependency resolution.
private const string PREFERENCE_SWIFT_PACKAGE_MANAGER_ENABLED =
PREFERENCE_NAMESPACE + "SwiftPackageManagerEnabled";

// List of preference keys, used to restore default settings.
private static string[] PREFERENCE_KEYS = new [] {
PREFERENCE_COCOAPODS_INSTALL_ENABLED,
Expand All @@ -525,7 +539,9 @@ protected override bool Read(string filename, Logger logger) {
PREFERENCE_SWIFT_FRAMEWORK_SUPPORT_WORKAROUND,
PREFERENCE_SWIFT_LANGUAGE_VERSION,
PREFERENCE_PODFILE_ALWAYS_ADD_MAIN_TARGET,
PREFERENCE_PODFILE_ALLOW_PODS_IN_MULTIPLE_TARGETS
PREFERENCE_PODFILE_ALLOW_PODS_IN_MULTIPLE_TARGETS,
PREFERENCE_ALLOW_EMPTY_PODFILE_GENERATION,
PREFERENCE_SWIFT_PACKAGE_MANAGER_ENABLED
};

// Whether the xcode extension was successfully loaded.
Expand Down Expand Up @@ -605,6 +621,9 @@ protected override bool Read(string filename, Logger logger) {
// Parses dependencies from XML dependency files.
private static IOSXmlDependencies xmlDependencies = new IOSXmlDependencies();

// Parses SPM dependencies from XML dependency files.
private static SwiftPackageManager spmDependencies = new SwiftPackageManager();

// Project level settings for this module.
private static ProjectSettings settings = new ProjectSettings(PREFERENCE_NAMESPACE);

Expand Down Expand Up @@ -1158,6 +1177,31 @@ public static bool PodfileAllowPodsInMultipleTargets {
}
}

/// <summary>
/// Whether to allow empty Podfile generation. True by default.
/// If true, a Podfile will be generated even if there are no Pods to install.
/// </summary>
public static bool AllowEmptyPodfileGeneration {
get { return settings.GetBool(PREFERENCE_ALLOW_EMPTY_PODFILE_GENERATION,
defaultValue: true); }
set {
settings.SetBool(PREFERENCE_ALLOW_EMPTY_PODFILE_GENERATION, value);
}
}

/// <summary>
/// Whether to use Swift Package Manager for dependency resolution.
/// If enabled, the resolver will attempt to use SPM for packages that define SPM support,
/// falling back to Cocoapods if disabled or if the package does not support SPM.
/// </summary>
public static bool SwiftPackageManagerEnabled {
get { return settings.GetBool(PREFERENCE_SWIFT_PACKAGE_MANAGER_ENABLED,
defaultValue: true); }
set {
settings.SetBool(PREFERENCE_SWIFT_PACKAGE_MANAGER_ENABLED, value);
}
}


/// <summary>
/// Whether to use project level settings.
Expand Down Expand Up @@ -1288,7 +1332,7 @@ public static bool PodPresent(string pod) {
private static bool InjectDependencies() {
return (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS ||
EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS) &&
Enabled && pods.Count > 0;
Enabled && (pods.Count > 0 || spmDependencies.SwiftPackages.Count > 0 || AllowEmptyPodfileGeneration);
}

/// <summary>
Expand Down Expand Up @@ -2148,14 +2192,26 @@ internal static void AddDummySwiftFile(
File.WriteAllText(pbxprojPath, project.WriteToString());
}

[PostProcessBuildAttribute(35)]
public static void OnPostProcessResolveSwiftPackages(BuildTarget buildTarget,
string pathToBuiltProject) {
if (!InjectDependencies() || !SwiftPackageManagerEnabled) {
return;
}
var resolvedPackages = SwiftPackageManager.Resolve(spmDependencies.SwiftPackages, logger);
SwiftPackageManager.AddPackagesToProject(resolvedPackages, pathToBuiltProject, logger);
podsToIgnore = SwiftPackageManager.GetReplacedPods(resolvedPackages);
}

/// <summary>
/// Post-processing build step to generate the podfile for ios.
/// </summary>
[PostProcessBuildAttribute(BUILD_ORDER_GEN_PODFILE)]
public static void OnPostProcessGenPodfile(BuildTarget buildTarget,
string pathToBuiltProject) {
if (!InjectDependencies() || !PodfileGenerationEnabled) return;
GenPodfile(buildTarget, pathToBuiltProject);
if (pods.Count == 0 && !AllowEmptyPodfileGeneration) return;
GenPodfile(buildTarget, pathToBuiltProject, podsToIgnore);
}

/// <summary>
Expand Down Expand Up @@ -2299,7 +2355,8 @@ private static string GeneratePodfileSourcesSection() {
// Mono runtime from loading the Xcode API before calling the post
// processing step.
public static void GenPodfile(BuildTarget buildTarget,
string pathToBuiltProject) {
string pathToBuiltProject,
HashSet<string> podsToIgnore = null) {
analytics.Report("generatepodfile", "Generate Podfile");
string podfilePath = GetPodfilePath(pathToBuiltProject);

Expand All @@ -2316,6 +2373,21 @@ public static void GenPodfile(BuildTarget buildTarget,
}
}

var filteredPods = new List<Pod>();
foreach (var pod in pods.Values) {
if (podsToIgnore != null && podsToIgnore.Contains(pod.name)) {
Log(String.Format("Skipping pod {0} because it is replaced by a Swift Package.", pod.name), verbose: true);
continue;
}
filteredPods.Add(pod);
}
if (filteredPods.Count == 0 && !AllowEmptyPodfileGeneration) {
Log("Found no pods to add, skipping generation of the Podfile");
podfileGenerationSkipped = true;
return;
}
podfileGenerationSkipped = false;

Log(String.Format("Generating Podfile {0} with {1} integration.", podfilePath,
(CocoapodsWorkspaceIntegrationEnabled ? "Xcode workspace" :
(CocoapodsProjectIntegrationEnabled ? "Xcode project" : "no target"))),
Expand All @@ -2340,7 +2412,7 @@ public static void GenPodfile(BuildTarget buildTarget,

foreach (var target in XcodeTargetNames) {
file.WriteLine(String.Format("target '{0}' do", target));
foreach(var pod in pods.Values) {
foreach(var pod in filteredPods) {
file.WriteLine(String.Format(" {0}", pod.PodFilePodLine));
}
file.WriteLine("end");
Expand All @@ -2350,7 +2422,7 @@ public static void GenPodfile(BuildTarget buildTarget,
file.WriteLine(String.Format("target '{0}' do", XcodeMainTargetName));
bool allowPodsInMultipleTargets = PodfileAllowPodsInMultipleTargets;
int podAdded = 0;
foreach(var pod in pods.Values) {
foreach(var pod in filteredPods) {
if (pod.addToAllTargets) {
file.WriteLine(String.Format(" {0}{1}",
allowPodsInMultipleTargets ? "" : "# ",
Expand All @@ -2377,7 +2449,7 @@ public static void GenPodfile(BuildTarget buildTarget,
int maxProperties = 0;
int maxSources = Pod.Sources.Count;
int fromXmlFileCount = 0;
foreach (var pod in pods.Values) {
foreach (var pod in filteredPods) {
maxProperties = Math.Max(maxProperties, pod.propertiesByName.Count);
maxSources = Math.Max(maxSources, pod.sources.Count);
if (!String.IsNullOrEmpty(pod.version)) versionCount++;
Expand All @@ -2387,7 +2459,7 @@ public static void GenPodfile(BuildTarget buildTarget,
}
analytics.Report("generatepodfile/podinfo",
new KeyValuePair<string, string>[] {
new KeyValuePair<string, string>("numPods", pods.Count.ToString()),
new KeyValuePair<string, string>("numPods", filteredPods.Count.ToString()),
new KeyValuePair<string, string>("numPodsWithVersions",
versionCount.ToString()),
new KeyValuePair<string, string>("numLocalPods",
Expand Down Expand Up @@ -2736,6 +2808,11 @@ public static void OnPostProcessInstallPods(BuildTarget buildTarget,
string pathToBuiltProject) {
if (!InjectDependencies() || !PodfileGenerationEnabled) return;

// If the Podfile Generation was skipped because of no pods to install, skip this step.
if (podfileGenerationSkipped) {
return;
}

if(EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS) {
UpdateTargetIosSdkVersion(true);
}
Expand Down Expand Up @@ -2842,7 +2919,8 @@ public static void OnPostProcessUpdateProjectDeps(
BuildTarget buildTarget, string pathToBuiltProject) {
if (!InjectDependencies() || !PodfileGenerationEnabled ||
!CocoapodsProjectIntegrationEnabled || // Early out for Workspace level integration.
!cocoapodsToolsInstallPresent) {
!cocoapodsToolsInstallPresent ||
podfileGenerationSkipped) {
return;
}

Expand Down Expand Up @@ -2908,10 +2986,14 @@ private static void RefreshXmlDependencies() {
foreach (var podName in podsToRemove) {
pods.Remove(podName);
}
spmDependencies.SwiftPackages.Clear();
podsToIgnore.Clear();

// Clear all sources (only can be set via XML config).
Pod.Sources = new List<KeyValuePair<string, string>>();
// Read pod specifications from XML dependencies.
// Read pod and spm specifications from XML dependencies.
xmlDependencies.ReadAll(logger);
spmDependencies.ReadAll(logger);
}
}

Expand Down
29 changes: 29 additions & 0 deletions source/IOSResolver/src/IOSResolverSettingsDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ private class Settings {
internal string swiftLanguageVersion;
internal bool podfileAlwaysAddMainTarget;
internal bool podfileAllowPodsInMultipleTargets;
internal bool swiftPackageManagerEnabled;
internal bool allowEmptyPodfileGeneration;
internal bool useProjectSettings;
internal EditorMeasurement.Settings analyticsSettings;

Expand All @@ -64,6 +66,8 @@ internal Settings() {
swiftLanguageVersion = IOSResolver.SwiftLanguageVersion;
podfileAlwaysAddMainTarget = IOSResolver.PodfileAlwaysAddMainTarget;
podfileAllowPodsInMultipleTargets = IOSResolver.PodfileAllowPodsInMultipleTargets;
swiftPackageManagerEnabled = IOSResolver.SwiftPackageManagerEnabled;
allowEmptyPodfileGeneration = IOSResolver.AllowEmptyPodfileGeneration;
useProjectSettings = IOSResolver.UseProjectSettings;
analyticsSettings = new EditorMeasurement.Settings(IOSResolver.analytics);
}
Expand All @@ -86,6 +90,8 @@ internal void Save() {
IOSResolver.SwiftLanguageVersion = swiftLanguageVersion;
IOSResolver.PodfileAlwaysAddMainTarget = podfileAlwaysAddMainTarget;
IOSResolver.PodfileAllowPodsInMultipleTargets = podfileAllowPodsInMultipleTargets;
IOSResolver.SwiftPackageManagerEnabled = swiftPackageManagerEnabled;
IOSResolver.AllowEmptyPodfileGeneration = allowEmptyPodfileGeneration;
IOSResolver.UseProjectSettings = useProjectSettings;
analyticsSettings.Save();
}
Expand Down Expand Up @@ -149,6 +155,26 @@ public void OnGUI() {

scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);

GUILayout.BeginHorizontal();
GUILayout.Label("Swift Package Manager Integration", EditorStyles.boldLabel);
settings.swiftPackageManagerEnabled =
EditorGUILayout.Toggle(settings.swiftPackageManagerEnabled);
GUILayout.EndHorizontal();
GUILayout.Label("Use Swift Package Manager to resolve dependencies that support it. " +
"If this is enabled, the resolver will prioritize SPM packages and " +
"fall back to Cocoapods for any dependencies that do not have an " +
"SPM equivalent specified.");

GUILayout.BeginHorizontal();
GUILayout.Label("Allow empty Podfile generation", EditorStyles.boldLabel);
settings.allowEmptyPodfileGeneration =
EditorGUILayout.Toggle(settings.allowEmptyPodfileGeneration);
GUILayout.EndHorizontal();
GUILayout.Label("If enabled, a Podfile will be generated even if there are no Pods " +
"to install.");

GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));

GUILayout.BeginHorizontal();
GUILayout.Label("Podfile Generation", EditorStyles.boldLabel);
settings.podfileGenerationEnabled =
Expand Down Expand Up @@ -363,6 +389,9 @@ public void OnGUI() {
new KeyValuePair<string, string>(
"swiftLanguageVersion",
IOSResolver.SwiftLanguageVersion.ToString()),
new KeyValuePair<string, string>(
"allowEmptyPodfileGeneration",
IOSResolver.AllowEmptyPodfileGeneration.ToString()),
},
"Settings Save");
settings.Save();
Expand Down
Loading