Allow building the Bazel C++ code on OpenBSD.

This change, split out of the larger PR https://github.com/bazelbuild/bazel/pull/10274, is part of the OpenBSD port in https://github.com/bazelbuild/bazel/issues/10250.

Closes #10316.

PiperOrigin-RevId: 284198941
diff --git a/src/conditions/BUILD b/src/conditions/BUILD
index faa41a4..4992880 100644
--- a/src/conditions/BUILD
+++ b/src/conditions/BUILD
@@ -47,6 +47,12 @@
 )
 
 config_setting(
+    name = "openbsd",
+    values = {"cpu": "openbsd"},
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
     name = "windows",
     values = {"cpu": "x64_windows"},
     visibility = ["//visibility:public"],
diff --git a/src/conditions/BUILD.tools b/src/conditions/BUILD.tools
index c9160fd..51a154a 100644
--- a/src/conditions/BUILD.tools
+++ b/src/conditions/BUILD.tools
@@ -5,6 +5,12 @@
 )
 
 config_setting(
+    name = "openbsd",
+    values = {"cpu": "openbsd"},
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
     name = "darwin",
     values = {"cpu": "darwin"},
     visibility = ["//visibility:public"],
diff --git a/src/main/cpp/BUILD b/src/main/cpp/BUILD
index 7de595d..5f8f49f 100644
--- a/src/main/cpp/BUILD
+++ b/src/main/cpp/BUILD
@@ -1,11 +1,11 @@
+load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
+
 # Description:
 #   The Bazel launcher.
 package(
     default_visibility = ["//visibility:public"],
 )
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
-
 WIN_LINK_OPTS = [
     "-DEFAULTLIB:advapi32.lib",  # GetUserNameW
     "-DEFAULTLIB:ole32.lib",  # CoTaskMemFree
@@ -29,7 +29,11 @@
             "blaze_util_posix.cc",
         ],
         "//src/conditions:freebsd": [
-            "blaze_util_freebsd.cc",
+            "blaze_util_bsd.cc",
+            "blaze_util_posix.cc",
+        ],
+        "//src/conditions:openbsd": [
+            "blaze_util_bsd.cc",
             "blaze_util_posix.cc",
         ],
         "//src/conditions:windows": [
@@ -53,6 +57,8 @@
         ],
         "//src/conditions:freebsd": [
         ],
+        "//src/conditions:openbsd": [
+        ],
         "//src/conditions:windows": WIN_LINK_OPTS,
         "//conditions:default": [
             "-lrt",
@@ -114,6 +120,8 @@
             "-lprocstat",
             "-lm",
         ],
+        "//src/conditions:openbsd": [
+        ],
         "//src/conditions:windows": [
         ],
         "//conditions:default": [
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index b9a623c..78edeeb 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -1519,21 +1519,35 @@
 
   const blaze_util::Path jvm_path = startup_options.GetJvm();
   const string server_jar_path = GetServerJarPath(archive_contents);
-  const vector<string> server_exe_args = GetServerExeArgs(
-      jvm_path,
-      server_jar_path,
-      archive_contents,
-      install_md5,
-      workspace_layout,
-      workspace,
-      startup_options);
-
-  KillRunningServerIfDifferentStartupOptions(
-      startup_options, server_exe_args, logging_info, blaze_server);
 
   const blaze_util::Path server_exe =
       startup_options.GetExe(jvm_path, server_jar_path);
 
+  vector<string> server_exe_args =
+      GetServerExeArgs(jvm_path, server_jar_path, archive_contents, install_md5,
+                       workspace_layout, workspace, startup_options);
+#if defined(__OpenBSD__)
+  // When spawning the server's JVM process, we normally set argv[0] to
+  // "bazel(workspace)". On OpenBSD, doing so causes the JVM process to fail
+  // during startup; ld.so fails to find a shared library that exists in
+  // /usr/local/jdk-1.8.0/jre/lib/amd64. Setting LD_LIBRARY_PATH does not help,
+  // but setting argv[0] to the JVM binary's path
+  // (/usr/local/jdk-1.8.0/bin/java) allows the JVM process to run. The JVM
+  // process apparently tries to compute a path to where the shared libraries
+  // should be, via a relative path from the JVM executable's path -- but
+  // OpenBSD does not provide a way for a process to determine a path to its
+  // own executable, and so the JVM falls back to searching the PATH for
+  // argv[0], which of course fails when argv[0] looks like "bazel(workspace)".
+  //
+  // TODO(aldersondrive): This hack is unnecessary on FreeBSD, but the relevant
+  // OpenJDK code doesn't seem to include anything FreeBSD-specific.
+  // Investigate why and possibly remove this.
+  server_exe_args[0] = server_exe.AsNativePath();
+#endif
+
+  KillRunningServerIfDifferentStartupOptions(
+      startup_options, server_exe_args, logging_info, blaze_server);
+
   const blaze_util::Path server_dir =
       blaze_util::Path(startup_options.output_base).GetRelative("server");
   if (IsServerMode(option_processor.GetCommand())) {
@@ -1558,7 +1572,7 @@
       new blaze_util::BazelLogHandler());
   blaze_util::SetLogHandler(std::move(default_handler));
 
-  const string self_path = GetSelfPath();
+  const string self_path = GetSelfPath(argv[0]);
 
   if (argc == 2 && strcmp(argv[1], "--version") == 0) {
     PrintVersionInfo(self_path, option_processor->GetLowercaseProductName());
diff --git a/src/main/cpp/blaze_util_freebsd.cc b/src/main/cpp/blaze_util_bsd.cc
similarity index 73%
rename from src/main/cpp/blaze_util_freebsd.cc
rename to src/main/cpp/blaze_util_bsd.cc
index 72a211c..7232102 100644
--- a/src/main/cpp/blaze_util_freebsd.cc
+++ b/src/main/cpp/blaze_util_bsd.cc
@@ -12,11 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#if defined(__FreeBSD__)
+# define HAVE_PROCSTAT
+# define STANDARD_JAVABASE "/usr/local/openjdk8"
+#elif defined(__OpenBSD__)
+# define STANDARD_JAVABASE "/usr/local/jdk-1.8.0"
+#else
+# error This BSD is not supported
+#endif
+
+#if !defined(DEFAULT_SYSTEM_JAVABASE)
+# define DEFAULT_SYSTEM_JAVABASE STANDARD_JAVABASE
+#endif
+
 #include <errno.h>  // errno, ENAMETOOLONG
 #include <limits.h>
 #include <pwd.h>
 #include <signal.h>
 #include <spawn.h>
+#include <stdlib.h>
 #include <string.h>  // strerror
 #include <sys/mount.h>
 #include <sys/param.h>
@@ -26,13 +40,16 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
-#include <libprocstat.h>  // must be included after <sys/...> headers
+#if defined(HAVE_PROCSTAT)
+# include <libprocstat.h>  // must be included after <sys/...> headers
+#endif
 
 #include "src/main/cpp/blaze_util.h"
 #include "src/main/cpp/blaze_util_platform.h"
 #include "src/main/cpp/util/errors.h"
 #include "src/main/cpp/util/exit_code.h"
 #include "src/main/cpp/util/file.h"
+#include "src/main/cpp/util/file_platform.h"
 #include "src/main/cpp/util/logging.h"
 #include "src/main/cpp/util/path.h"
 #include "src/main/cpp/util/port.h"
@@ -72,7 +89,8 @@
   }
 }
 
-string GetSelfPath() {
+string GetSelfPath(const char* argv0) {
+#if defined(__FreeBSD__)
   char buffer[PATH_MAX] = {};
   auto pid = getpid();
   if (kill(pid, 0) < 0) return "";
@@ -94,6 +112,36 @@
   }
   procstat_close(procstat);
   return string(buffer);
+#elif defined(__OpenBSD__)
+  // OpenBSD does not provide a way for a running process to find a path to its
+  // own executable, so we try to figure out a path by inspecting argv[0]. In
+  // theory this is inadequate, since the parent process can set argv[0] to
+  // anything, but in practice this is good enough.
+
+  const std::string argv0str(argv0);
+
+  // If argv[0] starts with a slash, it's an absolute path. Use it.
+  if (argv0str.length() > 0 && argv0str[0] == '/') {
+    return argv0str;
+  }
+
+  // Otherwise, if argv[0] contains a slash, then it's a relative path. Prepend
+  // the current directory to form an absolute path.
+  if (argv0str.length() > 0 && argv0str.find('/') != std::string::npos) {
+    return GetCwd() + "/" + argv0str;
+  }
+
+  // TODO(aldersondrive): Try to find the executable by inspecting the PATH.
+
+  // None of the above worked. Give up.
+  BAZEL_DIE(blaze_exit_code::BAD_ARGV)
+      << "Unable to determine the location of this Bazel executable. "
+         "Currently, argv[0] must be an absolute or relative path to the "
+         "executable.";
+  return "";  // Never executed. Needed so compiler does not complain.
+#else
+# error This BSD is not supported
+#endif
 }
 
 uint64_t GetMillisecondsMonotonic() {
@@ -103,10 +151,11 @@
 }
 
 void SetScheduling(bool batch_cpu_scheduling, int io_nice_level) {
-  // Stubbed out so we can compile for FreeBSD.
+  // Stubbed out so we can compile.
 }
 
 blaze_util::Path GetProcessCWD(int pid) {
+#if defined(HAVE_PROCSTAT)
   if (kill(pid, 0) < 0) {
     return blaze_util::Path();
   }
@@ -136,6 +185,9 @@
   }
   procstat_close(procstat);
   return blaze_util::Path(cwd);
+#else
+  return blaze_util::Path("");
+#endif
 }
 
 bool IsSharedLibrary(const string &filename) {
@@ -143,7 +195,7 @@
 }
 
 string GetSystemJavabase() {
-  // if JAVA_HOME is defined, then use it as default.
+  // If JAVA_HOME is defined, then use it as default.
   string javahome = GetPathEnv("JAVA_HOME");
 
   if (!javahome.empty()) {
@@ -155,7 +207,7 @@
         << "Ignoring JAVA_HOME, because it must point to a JDK, not a JRE.";
   }
 
-  return "/usr/local/openjdk8";
+  return DEFAULT_SYSTEM_JAVABASE;
 }
 
 int ConfigureDaemonProcess(posix_spawnattr_t *attrp,
diff --git a/src/main/cpp/blaze_util_darwin.cc b/src/main/cpp/blaze_util_darwin.cc
index 62f08c8..a484f00 100644
--- a/src/main/cpp/blaze_util_darwin.cc
+++ b/src/main/cpp/blaze_util_darwin.cc
@@ -124,7 +124,7 @@
   }
 }
 
-string GetSelfPath() {
+string GetSelfPath(const char* argv0) {
   char pathbuf[PROC_PIDPATHINFO_MAXSIZE] = {};
   int len = proc_pidpath(getpid(), pathbuf, sizeof(pathbuf));
   if (len == 0) {
diff --git a/src/main/cpp/blaze_util_linux.cc b/src/main/cpp/blaze_util_linux.cc
index 76344fd..3cfea54 100644
--- a/src/main/cpp/blaze_util_linux.cc
+++ b/src/main/cpp/blaze_util_linux.cc
@@ -82,7 +82,7 @@
   }
 }
 
-string GetSelfPath() {
+string GetSelfPath(const char* argv0) {
   // The file to which this symlink points could change contents or go missing
   // concurrent with execution of the Bazel client, so we don't eagerly resolve
   // it.
diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h
index 8545dfa..0056163 100644
--- a/src/main/cpp/blaze_util_platform.h
+++ b/src/main/cpp/blaze_util_platform.h
@@ -106,9 +106,9 @@
 
 std::string GetProcessIdAsString();
 
-// Get an absolute path to the binary being executed that is guaranteed to be
+// Gets an absolute path to the binary being executed that is guaranteed to be
 // readable.
-std::string GetSelfPath();
+std::string GetSelfPath(const char* argv0);
 
 // Returns the directory Bazel can use to store output.
 std::string GetOutputRoot();
@@ -129,7 +129,12 @@
 // on Linux, so it should only be called when necessary.
 void SetScheduling(bool batch_cpu_scheduling, int io_nice_level);
 
-// Returns the cwd for a process.
+// Returns the cwd of the specified process, or an empty string if the
+// directory is unknown.
+//
+// TODO(aldersondrive): Change the return type to
+// std::unique_ptr<blaze_util::Path>, so that we can return nullptr instead of
+// relying on callers to recognize the empty string as special.
 blaze_util::Path GetProcessCWD(int pid);
 
 bool IsSharedLibrary(const std::string& filename);
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 401f9db..cd702db 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -383,7 +383,7 @@
   return ToString(GetCurrentProcessId());
 }
 
-string GetSelfPath() {
+string GetSelfPath(const char* argv0) {
   WCHAR buffer[kWindowsPathBufferSize] = {0};
   if (!GetModuleFileNameW(0, buffer, kWindowsPathBufferSize)) {
     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
diff --git a/src/main/native/BUILD b/src/main/native/BUILD
index 7813537..329e9c0 100644
--- a/src/main/native/BUILD
+++ b/src/main/native/BUILD
@@ -6,6 +6,7 @@
         "//src/conditions:darwin": ["//tools/jdk:jni_md_header-darwin"],
         "//src/conditions:darwin_x86_64": ["//tools/jdk:jni_md_header-darwin"],
         "//src/conditions:freebsd": ["//tools/jdk:jni_md_header-freebsd"],
+        "//src/conditions:openbsd": ["//tools/jdk:jni_md_header-openbsd"],
         "//src/conditions:windows": ["//tools/jdk:jni_md_header-windows"],
         "//conditions:default": ["//tools/jdk:jni_md_header-linux"],
     }),
@@ -33,7 +34,8 @@
             "unix_jni_darwin.cc",
             "fsevents.cc",
         ],
-        "//src/conditions:freebsd": ["unix_jni_freebsd.cc"],
+        "//src/conditions:freebsd": ["unix_jni_bsd.cc"],
+        "//src/conditions:openbsd": ["unix_jni_bsd.cc"],
         "//conditions:default": ["unix_jni_linux.cc"],
     }),
 )
diff --git a/src/main/native/unix_jni.cc b/src/main/native/unix_jni.cc
index cd3e753..7110c48 100644
--- a/src/main/native/unix_jni.cc
+++ b/src/main/native/unix_jni.cc
@@ -85,7 +85,9 @@
     case ENAMETOOLONG:  // File name too long
     case ENODATA:    // No data available
     case EINVAL:     // Invalid argument
+#if defined(EMULTIHOP)
     case EMULTIHOP:  // Multihop attempted
+#endif
     case ENOLINK:    // Link has been severed
     case EIO:        // I/O error
     case EAGAIN:     // Try again
diff --git a/src/main/native/unix_jni.h b/src/main/native/unix_jni.h
index 7a6c307..1127698 100644
--- a/src/main/native/unix_jni.h
+++ b/src/main/native/unix_jni.h
@@ -17,6 +17,7 @@
 #ifndef BAZEL_SRC_MAIN_NATIVE_UNIX_JNI_H__
 #define BAZEL_SRC_MAIN_NATIVE_UNIX_JNI_H__
 
+#include <errno.h>
 #include <jni.h>
 #include <sys/stat.h>
 
@@ -33,7 +34,7 @@
       } \
     } while (0)
 
-#if defined(__APPLE__) || defined(__FreeBSD__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
 // stat64 is deprecated on OS X/BSD.
 typedef struct stat portable_stat_struct;
 #define portable_stat ::stat
@@ -44,8 +45,12 @@
 #define portable_lstat ::lstat64
 #endif
 
-#if defined(__FreeBSD__)
-#define ENODATA ENOATTR
+#if !defined(ENODATA)
+# if defined(ENOATTR)
+#  define ENODATA ENOATTR
+# else
+#  error Don't know how to handle missing ENODATA
+# endif
 #endif
 
 // Posts a JNI exception to the current thread with the specified
diff --git a/src/main/native/unix_jni_freebsd.cc b/src/main/native/unix_jni_bsd.cc
similarity index 86%
rename from src/main/native/unix_jni_freebsd.cc
rename to src/main/native/unix_jni_bsd.cc
index 8fd7ae2..05c744f 100644
--- a/src/main/native/unix_jni_freebsd.cc
+++ b/src/main/native/unix_jni_bsd.cc
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#if defined(__FreeBSD__)
+# define HAVE_EXTATTR
+# define HAVE_SYSCTLBYNAME
+#elif defined(__OpenBSD__)
+// No sys/extattr.h or sysctlbyname on this platform.
+#else
+# error This BSD is not supported
+#endif
+
 #include "src/main/native/unix_jni.h"
 
 #include <assert.h>
@@ -19,7 +28,9 @@
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/extattr.h>
+#if defined(HAVE_EXTATTR)
+# include <sys/extattr.h>
+#endif
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
@@ -74,22 +85,37 @@
 
 ssize_t portable_getxattr(const char *path, const char *name, void *value,
                           size_t size, bool *attr_not_found) {
+#if (HAVE_EXTATTR)
   ssize_t result =
       extattr_get_file(path, EXTATTR_NAMESPACE_SYSTEM, name, value, size);
   *attr_not_found = (errno == ENOATTR);
   return result;
+#else
+  *attr_not_found = true;
+  return -1;
+#endif
 }
 
 ssize_t portable_lgetxattr(const char *path, const char *name, void *value,
                            size_t size, bool *attr_not_found) {
+#if (HAVE_EXTATTR)
   ssize_t result =
       extattr_get_link(path, EXTATTR_NAMESPACE_SYSTEM, name, value, size);
   *attr_not_found = (errno == ENOATTR);
   return result;
+#else
+  *attr_not_found = true;
+  return -1;
+#endif
 }
 
 int portable_sysctlbyname(const char *name_chars, long *mibp, size_t *sizep) {
+#if (HAVE_SYSCTLBYNAME)
   return sysctlbyname(name_chars, mibp, sizep, NULL, 0);
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
 }
 
 int portable_push_disable_sleep() {