Automated rollback of commit f0bab83186b9e474ce2c7fda0e93be838086b3c0.

*** Reason for rollback ***

Rollforward with fixes to include a default swift version fetched via xcrun

*** Original change description ***

Automated rollback of commit 59704429b54110ed10a7490704cbbdee2232b0c9.

*** Reason for rollback ***

Breaks Swift test generation as we no longer have a default swift version; we will need to use xcrun to come up with a default.

*** Original change description ***

Use swift provider to fetch swift version

PiperOrigin-RevId: 185891775
diff --git a/src/TulsiGenerator/Base.lproj/Localizable.strings b/src/TulsiGenerator/Base.lproj/Localizable.strings
index 2260b3f..f1f18d3 100644
--- a/src/TulsiGenerator/Base.lproj/Localizable.strings
+++ b/src/TulsiGenerator/Base.lproj/Localizable.strings
@@ -65,6 +65,9 @@
 /* Message to show when the execution root was not able to be extracted from the workspace. */
 "ExecutionRootNotFound" = "Was not able to extract the execution root from the workspace. This is a Tulsi or Bazel bug, please report.";
 
+/* Message to show when unable to extract the default swift version from xcrun. */
+"ExtractingDefaultSwiftVersionFailed" = "Unable to extract default swift version from xcrun. Defaulting to swift version %1$@. Additional error context: %2$@.";
+
 /* Failed to readString from FileLineReader when handling the BEP file. */
 "FileLineReaderReadStringFailed" = "Failed to readString from the BEP JSON file. This is a Tulsi or Bazel bug, please report.";
 
diff --git a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
index 1c30818..47a5a6c 100644
--- a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
+++ b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
@@ -466,14 +466,6 @@
   return str(min_os)
 
 
-def _extract_swift_language_version(ctx):
-  """Returns the Swift version of a swift_library rule."""
-
-  if ctx.rule.kind != 'swift_library':
-    return None
-  return _get_label_attr(ctx, 'rule.attr.swift_version') or "3.0"
-
-
 def _collect_swift_modules(target):
   """Returns a depset of Swift modules found on the given target."""
   swift_modules = depset()
@@ -633,9 +625,9 @@
     infoplist = None
 
   # Build up any local transitive attributes and apply them.
-  swift_language_version = _extract_swift_language_version(ctx)
-  if swift_language_version:
-    transitive_attributes['swift_language_version'] = swift_language_version
+  swift = _get_opt_attr(target, 'swift')
+  if swift:
+    transitive_attributes['swift_language_version'] = getattr(swift, 'swift_version', None)
     transitive_attributes['has_swift_dependency'] = True
 
   all_attributes = attributes + inheritable_attributes + transitive_attributes
diff --git a/src/TulsiGenerator/XcodeProjectGenerator.swift b/src/TulsiGenerator/XcodeProjectGenerator.swift
index af3e332..319465c 100644
--- a/src/TulsiGenerator/XcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/XcodeProjectGenerator.swift
@@ -77,6 +77,7 @@
   private static let StubWatchOS2InfoPlistFilename = "StubWatchOS2InfoPlist.plist"
   private static let StubWatchOS2AppExInfoPlistFilename = "StubWatchOS2AppExInfoPlist.plist"
   private static let CachedExecutionRootFilename = "execroot_path.py"
+  private static let DefaultSwiftVersion = "4"
 
   /// Rules which should not be generated at the top level.
   private static let LibraryRulesForTopLevelWarning =
@@ -113,6 +114,8 @@
   /// generated by Bazel.
   var suppressGeneratedArtifactFolderCreation = false
 
+  var cachedDefaultSwiftVersion: String?
+
   init(workspaceRootURL: URL,
        config: TulsiGeneratorConfig,
        localizedMessageLogger: LocalizedMessageLogger,
@@ -231,6 +234,74 @@
 
   // MARK: - Private methods
 
+  /// Extracts the default swift version to use for targets without an explicit default by running
+  /// 'xcrun swift --version' if not already fetched.
+  private func fetchDefaultSwitchVersion() -> String {
+    // Used the already computed default version if it is available.
+    if let defaultSwiftVersion = cachedDefaultSwiftVersion {
+      return defaultSwiftVersion
+    }
+
+    let semaphore = DispatchSemaphore(value: 0)
+    var completionInfo: ProcessRunner.CompletionInfo?
+    let process = TulsiProcessRunner.createProcess("/usr/bin/xcrun",
+                                                   arguments: ["swift", "--version"],
+                                                   messageLogger: self.localizedMessageLogger,
+                                                   loggingIdentifier: "extract_default_swift_version") {
+                                                    processCompletionInfo in
+      defer { semaphore.signal() }
+
+      completionInfo = processCompletionInfo
+    }
+    process.launch()
+    _ = semaphore.wait(timeout: DispatchTime.distantFuture)
+
+    guard let info = completionInfo else {
+      self.localizedMessageLogger.warning("ExtractingDefaultSwiftVersionFailed",
+                                          comment: "Default version in %1$@, additional error context in %2$@.",
+                                          values: XcodeProjectGenerator.DefaultSwiftVersion,
+                                          "Internal error, unable to find process information")
+      cachedDefaultSwiftVersion = XcodeProjectGenerator.DefaultSwiftVersion
+      return XcodeProjectGenerator.DefaultSwiftVersion
+    }
+
+    guard info.terminationStatus == 0,
+      let stdout = NSString(data: info.stdout, encoding: String.Encoding.utf8.rawValue) else {
+        let stderr = NSString(data: info.stderr, encoding: String.Encoding.utf8.rawValue) ?? "<no stderr>"
+        self.localizedMessageLogger.warning("ExtractingDefaultSwiftVersionFailed",
+                                            comment: "Default version in %1$@, additional error context in %2$@.",
+                                            values: XcodeProjectGenerator.DefaultSwiftVersion,
+                                            "xcrun swift --version returned exitcode \(info.terminationStatus) with stderr: \(stderr)")
+        cachedDefaultSwiftVersion = XcodeProjectGenerator.DefaultSwiftVersion
+        return XcodeProjectGenerator.DefaultSwiftVersion
+    }
+    // Example output format:
+    // Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
+    // Target: x86_64-apple-macosx10.9
+    //
+    // Note that we only care about the major and minor version number (e.g. 4.0, not 4.0.3).
+    let pattern = "^Apple\\sSwift\\sversion\\s([0-9]+\\.?[0-9]?)"
+    guard let regExpr = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) else {
+      self.localizedMessageLogger.warning("ExtractingDefaultSwiftVersionFailed",
+                                          comment: "Default version in %1$@, additional error context in %2$@.",
+                                          values: XcodeProjectGenerator.DefaultSwiftVersion,
+                                          "Internal error, unable to create regular expression")
+      cachedDefaultSwiftVersion = XcodeProjectGenerator.DefaultSwiftVersion
+      return XcodeProjectGenerator.DefaultSwiftVersion
+    }
+    guard let match = regExpr.firstMatch(in: stdout as String,
+                                         range: NSMakeRange(0, stdout.length)) else {
+      self.localizedMessageLogger.warning("ExtractingDefaultSwiftVersionFailed",
+                                          comment: "Default version in %1$@, additional error context in %2$@.",
+                                          values: XcodeProjectGenerator.DefaultSwiftVersion,
+                                          "Unable to parse version from xcrun output. Output: \(stdout)")
+      cachedDefaultSwiftVersion = XcodeProjectGenerator.DefaultSwiftVersion
+      return XcodeProjectGenerator.DefaultSwiftVersion
+    }
+    cachedDefaultSwiftVersion = stdout.substring(with: match.rangeAt(1))
+    return stdout.substring(with: match.rangeAt(1))
+  }
+
   /// Encapsulates information about the results of a buildXcodeProjectWithMainGroup invocation.
   private struct GeneratedProjectInfo {
     /// The newly created PBXProject instance.
@@ -419,6 +490,8 @@
       for entry in targetRules {
         if let swiftVersion = entry.attributes[.swift_language_version] as? String {
           buildSettings["SWIFT_VERSION"] = swiftVersion
+        } else if entry.attributes[.has_swift_dependency] as? Bool ?? false {
+          buildSettings["SWIFT_VERSION"] = fetchDefaultSwitchVersion()
         }
         if let swiftToolchain = entry.attributes[.swift_toolchain] as? String {
           buildSettings["TOOLCHAINS"] = swiftToolchain