Repository: The-OpenROAD-Project/OpenROAD
Pull request #7296: bazel: handle runfiles init with global constructor
Author: login: oharboe
Created: 2025-05-06T18:22:10Z | Merged: 2025-05-07T14:24:38Z

No need to modify OpenROAD or OpenSTA source code

@hzeller @QuantamHD Thoughts?


Diff:
diff --git a/BUILD.bazel b/BUILD.bazel
index 8a0348aefd3..df54813375b 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -117,6 +117,7 @@ cc_binary(
     srcs = OPENROAD_BINARY_SRCS_WITHOUT_MAIN + [
         "src/Main.cc",
         "src/OpenRoad.cc",
+        "//bazel:runfiles",
     ] + select({
         ":platform_cli": [],
     }),
@@ -912,6 +913,7 @@ cc_binary(
         "src/sta/app/Main.cc",
         ":StaApp",
         ":StaTclInitVar",
+        "//bazel:runfiles",
     ],
     copts = [
         "-fexceptions",
diff --git a/bazel/BUILD b/bazel/BUILD
index 87845bc0954..5df8db14d4a 100644
--- a/bazel/BUILD
+++ b/bazel/BUILD
@@ -6,3 +6,14 @@ compile_pip_requirements(
     python_version = "3.13",
     requirements_txt = "requirements_lock_3_13.txt",
 )
+
+cc_library(
+    name = "runfiles",
+    srcs = [
+        "InitRunFiles.cpp",
+    ],
+    deps = [
+        "@rules_cc//cc/runfiles",
+    ],
+    visibility = ["//visibility:public"],
+)
\ No newline at end of file
diff --git a/bazel/InitRunFiles.cpp b/bazel/InitRunFiles.cpp
new file mode 100644
index 00000000000..50498d0b7ad
--- /dev/null
+++ b/bazel/InitRunFiles.cpp
@@ -0,0 +1,63 @@
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "rules_cc/cc/runfiles/runfiles.h"
+
+// Avoid adding any dependencies like boost.filesystem
+// Returns path to running binary if possible, otherwise nullopt.
+static std::optional<std::string> getProgramLocation()
+{
+#if defined(_WIN32)
+  char result[MAX_PATH + 1] = {'\0'};
+  auto path_len = GetModuleFileNameA(NULL, result, MAX_PATH);
+#elif defined(__APPLE__)
+  char result[MAXPATHLEN + 1] = {'\0'};
+  uint32_t path_len = MAXPATHLEN;
+  if (_NSGetExecutablePath(result, &path_len) != 0) {
+    path_len = readlink("/proc/self/exe", result, MAXPATHLEN);
+  }
+#else
+  char result[PATH_MAX + 1] = {'\0'};
+  ssize_t path_len = readlink("/proc/self/exe", result, PATH_MAX);
+#endif
+  if (path_len > 0) {
+    return result;
+  }
+  return std::nullopt;
+}
+
+// Global constructor class to initialize Bazel runfiles and environment
+// variables
+class BazelInitializer
+{
+ public:
+  BazelInitializer()
+  {
+    using rules_cc::cc::runfiles::Runfiles;
+
+    std::string error;
+    std::unique_ptr<Runfiles> runfiles(Runfiles::Create(
+        getProgramLocation().value(), BAZEL_CURRENT_REPOSITORY, &error));
+    if (!runfiles) {
+      std::cerr << "Error initializing Bazel runfiles: " << error << std::endl;
+      std::exit(1);
+    }
+
+    // Set the TCL_LIBRARY environment variable
+    std::string path = runfiles->Rlocation("tk_tcl/library/");
+    if (!path.empty()) {
+      setenv("TCL_LIBRARY", path.c_str(), 0);
+    } else {
+      std::cerr << "Error: Could not locate 'tk_tcl/library/' in runfiles."
+                << std::endl;
+      std::exit(1);
+    }
+  }
+};
+
+// Instantiate the global constructor
+static BazelInitializer bazel_initializer;
diff --git a/src/Main.cc b/src/Main.cc
index c01f28beb76..66b3359db11 100644
--- a/src/Main.cc
+++ b/src/Main.cc
@@ -29,10 +29,6 @@
 #include <tclExtend.h>
 #endif
 
-#ifdef BAZEL_CURRENT_REPOSITORY
-#include "rules_cc/cc/runfiles/runfiles.h"
-#endif
-
 #include "gui/gui.h"
 #include "ord/Design.h"
 #include "ord/InitOpenRoad.hh"
@@ -198,33 +194,6 @@ static void handler(int sig)
   raise(sig);
 }
 
-#ifdef BAZEL_CURRENT_REPOSITORY
-
-// Avoid adding any dependencies like boost.filesystem
-//
-// Returns path to running binary if possible, otherwise nullopt.
-static std::optional<std::string> getProgramLocation()
-{
-#if defined(_WIN32)
-  char result[MAX_PATH + 1] = {'\0'};
-  auto path_len = GetModuleFileNameA(NULL, result, MAX_PATH);
-#elif defined(__APPLE__)
-  char result[MAXPATHLEN + 1] = {'\0'};
-  uint32_t path_len = MAXPATHLEN;
-  if (_NSGetExecutablePath(result, &path_len) != 0) {
-    path_len = readlink("/proc/self/exe", result, MAXPATHLEN);
-  }
-#else
-  char result[PATH_MAX + 1] = {'\0'};
-  ssize_t path_len = readlink("/proc/self/exe", result, PATH_MAX);
-#endif
-  if (path_len > 0) {
-    return result;
-  }
-  return std::nullopt;
-}
-#endif
-
 int main(int argc, char* argv[])
 {
   // This avoids problems with locale setting dependent
@@ -237,19 +206,6 @@ int main(int argc, char* argv[])
     }
   }
 
-#ifdef BAZEL_CURRENT_REPOSITORY
-  using rules_cc::cc::runfiles::Runfiles;
-  std::string error;
-  std::unique_ptr<Runfiles> runfiles(Runfiles::Create(
-      getProgramLocation().value(), BAZEL_CURRENT_REPOSITORY, &error));
-  if (!runfiles) {
-    std::cerr << error << std::endl;
-    return 1;
-  }
-  std::string path = runfiles->Rlocation("tk_tcl/library/");
-  setenv("TCL_LIBRARY", path.c_str(), 0);
-#endif
-
   // Generate a stacktrace on crash
   signal(SIGABRT, handler);
   signal(SIGBUS, handler);


Reviews:
user: login: github-actions[bot]
body: clang-tidy made some suggestions
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T18:25:21Z
user: login: oharboe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T18:40:32Z
user: login: oharboe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T18:57:33Z
user: login: maliberty
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-07T14:24:30Z

Review comments:
diff_hunk: @@ -0,0 +1,63 @@
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "rules_cc/cc/runfiles/runfiles.h"
path: bazel/InitRunFiles.cpp
user: login: github-actions[bot]
body: warning: 'rules_cc/cc/runfiles/runfiles.h' file not found [clang-diagnostic-error]
```cpp
#include "rules_cc/cc/runfiles/runfiles.h"
         ^
```

created_at: 2025-05-06T18:25:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,63 @@
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "rules_cc/cc/runfiles/runfiles.h"
path: bazel/InitRunFiles.cpp
user: login: oharboe
body: ??
created_at: 2025-05-06T18:40:32Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "rules_cc/cc/runfiles/runfiles.h"
path: bazel/InitRunFiles.cpp
user: login: oharboe
body: @maliberty Any idea what clang tidy wants here? 

Merge and forget all about it?
created_at: 2025-05-06T18:57:33Z
author_association: COLLABORATOR

PR comments:
user: login: maliberty
created_at: 2025-05-06T18:25:20Z
body: Fine with me
author_association: MEMBER
user: login: oharboe
created_at: 2025-05-06T19:11:30Z
body: @maliberty This approach could perhaps fix these too?

```
INFO: From Action StaTclInitVar.cc:
application-specific initialization failed: unknown encoding "ascii"
```

author_association: COLLABORATOR
user: login: QuantamHD
created_at: 2025-05-06T20:33:26Z
body: Fine with me as well
author_association: COLLABORATOR
user: login: oharboe
created_at: 2025-05-07T04:33:52Z
body: @maliberty Merge?
author_association: COLLABORATOR