Add a warning for iOS bundle targets with different min OS versions
Users should use a single minimum_os_version when possible to reduce
the costs of Bazel analysis time and Xcode indexing time.
Changes:
- New warning for different minimum iOS versions
- Removed old top-level target warning as we still support it
- RuleEntryMap insertion should be faster for larger projects.
PiperOrigin-RevId: 263366741
diff --git a/src/TulsiGenerator/DeploymentTarget.swift b/src/TulsiGenerator/DeploymentTarget.swift
index 13e9df6..68d91ba 100644
--- a/src/TulsiGenerator/DeploymentTarget.swift
+++ b/src/TulsiGenerator/DeploymentTarget.swift
@@ -146,6 +146,15 @@
}
}
+ var userString: String {
+ switch self {
+ case .ios: return "iOS"
+ case .macos: return "macOS"
+ case .tvos: return "tvOS"
+ case .watchos: return "watchOS"
+ }
+ }
+
/// Path of where the test host is expected to be built for each available platform.
func testHostPath(hostTargetPath: String, hostTargetProductName: String) -> String? {
switch self {
diff --git a/src/TulsiGenerator/RuleEntryMap.swift b/src/TulsiGenerator/RuleEntryMap.swift
index 5020536..c91e01d 100644
--- a/src/TulsiGenerator/RuleEntryMap.swift
+++ b/src/TulsiGenerator/RuleEntryMap.swift
@@ -42,16 +42,7 @@
public func insert(ruleEntry: RuleEntry) {
allEntries.append(ruleEntry)
-
- let label = ruleEntry.label
- guard var entries = labelToEntries[label] else {
- labelToEntries[label] = [ruleEntry]
- return
- }
- // Don't warn about duplicate entries with the same DeploymentTarget as they should
- // only be caused by a root-level library which we already warn about.
- entries.append(ruleEntry)
- labelToEntries[label] = entries
+ labelToEntries[ruleEntry.label, default: []].append(ruleEntry)
}
public func hasAnyRuleEntry(withBuildLabel buildLabel: BuildLabel) -> Bool {
diff --git a/src/TulsiGenerator/XcodeProjectGenerator.swift b/src/TulsiGenerator/XcodeProjectGenerator.swift
index 8e56234..0de351f 100644
--- a/src/TulsiGenerator/XcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/XcodeProjectGenerator.swift
@@ -82,10 +82,6 @@
private static let DefaultSwiftVersion = "4"
private static let SupportScriptsPath = "Library/Application Support/Tulsi/Scripts"
- /// Rules which should not be generated at the top level.
- private static let LibraryRulesForTopLevelWarning =
- Set(["objc_library", "swift_library", "cc_library"])
-
private let workspaceRootURL: URL
private let config: TulsiGeneratorConfig
private let localizedMessageLogger: LocalizedMessageLogger
@@ -333,7 +329,7 @@
}
/// Validates that the aspect output contains all targets listed in the config file and that
- /// there are no ambiguous top-level targets.
+ /// any bundled iOS targets use the same minimum OS version.
private func validateConfigReferences(_ ruleEntryMap: RuleEntryMap) throws {
let unresolvedLabels = config.buildTargetLabels.filter {
!ruleEntryMap.hasAnyRuleEntry(withBuildLabel: $0)
@@ -341,13 +337,43 @@
if !unresolvedLabels.isEmpty {
throw ProjectGeneratorError.labelResolutionFailed(Set<BuildLabel>(unresolvedLabels))
}
- for label in config.buildTargetLabels {
- if let entry = ruleEntryMap.anyRuleEntry(withBuildLabel: label),
- XcodeProjectGenerator.LibraryRulesForTopLevelWarning.contains(entry.type) {
- localizedMessageLogger.warning("TopLevelLibraryTarget",
- comment: "Warning when a library target is used as a top level buildTarget. Target in %1$@, target type in %2$@.",
- values: entry.label.description, entry.type)
+
+ var bundleEntriesByMinIos = [String: [RuleEntry]]()
+
+ // Phase 1: Collect all bundled iOS targets into a dictionary by their min iOS version.
+ // - Explicitly ignore the ios_default_host target which may be outside of a user's project.
+ for entry in ruleEntryMap.allRuleEntries {
+ // Only inspect bundled targets - they will all have a product type.
+ guard entry.productType != nil else { continue }
+
+ // Ignore the `ios_default_host` target as it's outside of the user's project.
+ guard entry.label.targetName != "ios_default_host" else { continue }
+
+ // For now we only care about iOS targets. In the future we could expand this to handle
+ // others platforms as well.
+ guard let deploymentTarget = entry.deploymentTarget,
+ deploymentTarget.platform == .ios else { continue }
+
+ bundleEntriesByMinIos[deploymentTarget.osVersion, default: []].append(entry)
+ }
+
+ // Phase 2: Warning if they have multiple min iOS versions.
+ if bundleEntriesByMinIos.count > 1 {
+ let platform = PlatformType.ios.userString
+
+ // Sort the entries so the most popular min iOS version is first.
+ let sortedEntries = bundleEntriesByMinIos.enumerated().sorted { (a, b) -> Bool in
+ return a.element.value.count > b.element.value.count
}
+ let debugString = sortedEntries.map { (offset, element) in
+ let targets = element.value.map { $0.label.value }.joined(separator: ", ")
+ return "\(platform) \(element.key) minimum_os_version: target(s) \(targets)"
+ }.joined(separator: "\n")
+
+ localizedMessageLogger.warning("MultiMinOSVersions",
+ comment: "Warning when multiple bundled targets have different minimum OS versions. Platform type in %1$@, context in %2$@.",
+ context: config.projectName,
+ values: platform, debugString)
}
}
diff --git a/src/TulsiGenerator/en.lproj/Localizable.strings b/src/TulsiGenerator/en.lproj/Localizable.strings
index cb85d0b..67e2d22 100644
--- a/src/TulsiGenerator/en.lproj/Localizable.strings
+++ b/src/TulsiGenerator/en.lproj/Localizable.strings
@@ -86,12 +86,12 @@
/* Warning to show when a user has selected an XCTest but not its host application. */
"MissingTestHost" = "Failed to link test target '%1$@' to its host, '%2$@'. This test will not be runnable in the generated Xcode project.";
+/* Warning when multiple bundled targets have different minimum OS versions. Platform type in %1$@, context in %2$@. */
+"MultiMinOSVersions" = "[ACTION REQUIRED] You have multiple top-level %1$@ bundle targets (e.g. applications or tests) with different minimum %1$@ versions.\nWe advise you to use a single minimum OS version for these targets in order to save Bazel analysis time and Xcode indexing time. You can fix this by setting a single `minimum_os_version` attribute on all of the targets mentioned below:\n------------------------\n%2$@\n------------------------";
+
/* Warning to show when a RuleEntry does not have a DeploymentTarget to be used by the indexer. */
"NoDeploymentTarget" = "Target %1$@ has no DeploymentTarget set. Defaulting to iOS 9. This is an unexpected problem and should be reported as a Tulsi bug.";
-/* Warning when a library target is used as a top level buildTarget. Target in %1$@, target type in %2$@. */
-"TopLevelLibraryTarget" = "You've included a %2$@ target (%1$@) in your project as a top-level build target. This is not supported and may trigger other warnings and errors. We advise you to no longer reference any %2$@ targets at the top level. Instead, use a top-level application or test target with %1$@ as a dependency.";
-
/* Warning shown when none of the tests of a test suite %1$@ were able to be resolved. */
"TestSuiteHasNoValidTests" = "Failed to resolve any tests for test_suite '%1$@', no scheme will be generated.";