Update wrapped_clang to make -add_ast_path options absolute

This should allow us to debug Swift binaries without using dSYMs.

See
https://forums.swift.org/t/improving-swift-lldb-support-for-path-remappings/22694

PiperOrigin-RevId: 244896679
diff --git a/tools/osx/crosstool/wrapped_clang.cc b/tools/osx/crosstool/wrapped_clang.cc
index a087881..2115b8d 100644
--- a/tools/osx/crosstool/wrapped_clang.cc
+++ b/tools/osx/crosstool/wrapped_clang.cc
@@ -57,6 +57,8 @@
 
 namespace {
 
+constexpr char kAddASTPathPrefix[] = "-Wl,-add_ast_path,";
+
 // Returns the base name of the given filepath. For example, given
 // /foo/bar/baz.txt, returns 'baz.txt'.
 const char *Basename(const char *filepath) {
@@ -151,6 +153,21 @@
   return env_value;
 }
 
+// Returns true if `str` starts with the specified `prefix`.
+bool StartsWith(const std::string &str, const std::string &prefix) {
+  return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// If *`str` begins `prefix`, strip it out and return true.
+// Otherwise leave *`str` unchanged and return false.
+bool StripPrefixStringIfPresent(std::string *str, const std::string &prefix) {
+  if (StartsWith(*str, prefix)) {
+    *str = str->substr(prefix.size());
+    return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 int main(int argc, char *argv[]) {
@@ -203,9 +220,31 @@
     }
     FindAndReplace("__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir, &arg);
     FindAndReplace("__BAZEL_XCODE_SDKROOT__", sdk_root, &arg);
+
+    // Make the `add_ast_path` options used to embed Swift module references
+    // absolute to enable Swift debugging without dSYMs: see
+    // https://forums.swift.org/t/improving-swift-lldb-support-for-path-remappings/22694
+    if (StripPrefixStringIfPresent(&arg, kAddASTPathPrefix)) {
+      // Only modify relative paths.
+      if (!StartsWith(arg, "/")) {
+        arg = std::string(kAddASTPathPrefix) +
+              std::string(cwd.get()) + "/" + arg;
+      } else {
+        arg = std::string(kAddASTPathPrefix) + arg;
+      }
+    }
+
     processed_args.push_back(arg);
   }
 
+  // Special mode that only prints the command. Used for testing.
+  if (getenv("__WRAPPED_CLANG_LOG_ONLY")) {
+    for (const std::string &arg : processed_args)
+        std::cout << arg << ' ';
+    std::cout << "\n";
+    return 0;
+  }
+
   // Check to see if we should postprocess with dsymutil.
   bool postprocess = false;
   if ((!linked_binary.empty()) || (!dsym_path.empty())) {