Funnel all JNI load requests through JniLoader.

To avoid symbol collisions in native dependencies, we can safely only load
one shared library to provide our JNI bindings.  Unfortunately, we have
multiple places that attempt to load JNI code, which gives the illusion that
loading more than one library is OK.  To clarify this, centralize all JNI
loads into a single JniLoader entry point.  This change should be a no-op and
does not fix any problems with duplicate symbols yet.

As part of this change, remove the io.bazel.EnableJni JVM system property
that was used to tell Bazel whether to load the JNI code or not... sometimes.
We had many call sites that attempted to load JNI and some checked for this
property and others didn't.  Things worked mostly by chance and were very
fragile, and we can do better by being slightly smarter.

Note that UnixJniLoader and WindowsJniLoader remain after this change but
they are now hidden behind JniLoader.  An upcoming change will further
merge the code of these three, but I'll do it separately for simplicity
reasons.

PiperOrigin-RevId: 335040383
diff --git a/scripts/bootstrap/compile.sh b/scripts/bootstrap/compile.sh
index 0f9d8e2..9e128df 100755
--- a/scripts/bootstrap/compile.sh
+++ b/scripts/bootstrap/compile.sh
@@ -397,10 +397,11 @@
     cp "$tmp_output" "$output"
     chmod 0555 "$output"
 
-    JNI_FLAGS="-Dio.bazel.EnableJni=1 -Djava.library.path=${output_dir}"
+    JNI_FLAGS="-Djava.library.path=${output_dir}"
   else
-    # We don't need JNI on other platforms.
-    JNI_FLAGS="-Dio.bazel.EnableJni=0"
+    # We don't need JNI on other platforms. The Java NIO file system fallback is
+    # sufficient.
+    true
   fi
 }
 
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index b8e9b0a..93376b1 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -55,6 +55,7 @@
         "//src/main/java/com/google/devtools/build/lib/events:srcs",
         "//src/main/java/com/google/devtools/build/lib/exec:srcs",
         "//src/main/java/com/google/devtools/build/lib/graph:srcs",
+        "//src/main/java/com/google/devtools/build/lib/jni:srcs",
         "//src/main/java/com/google/devtools/build/lib/metrics:srcs",
         "//src/main/java/com/google/devtools/build/lib/network:srcs",
         "//src/main/java/com/google/devtools/build/lib/packages:srcs",
@@ -320,6 +321,7 @@
         "//src/main/java/com/google/devtools/build/lib/exec:symlink_tree_strategy",
         "//src/main/java/com/google/devtools/build/lib/exec:test_log_helper",
         "//src/main/java/com/google/devtools/build/lib/exec/local:options",
+        "//src/main/java/com/google/devtools/build/lib/jni",
         "//src/main/java/com/google/devtools/build/lib/metrics:event",
         "//src/main/java/com/google/devtools/build/lib/packages",
         "//src/main/java/com/google/devtools/build/lib/packages/semantics",
@@ -502,7 +504,7 @@
         # TODO(#11179): Remove when the stub template fix is released.
         "export JARBIN=$(JAVABASE)/bin/jar && " +
         "$(location //src/main/java/com/google/devtools/build/lib/bazel:BazelServer) " +
-        "--jvm_flag=-Dio.bazel.EnableJni=0 --batch " +
+        "--batch " +
         "--install_base=$${TMP} --output_base=$${TMP}/output/ --output_user_root=$${TMP} " +
         "--failure_detail_out=$${TMP}/output/failure_detail.rawproto " +
         "help everything-as-html >> $@ 2>/dev/null && " +
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index 4470dea..2b04039 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -253,6 +253,7 @@
     ],
     deps = [
         "//src/main/java/com/google/devtools/build/lib/concurrent",
+        "//src/main/java/com/google/devtools/build/lib/jni",
         "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
         "//src/main/java/com/google/devtools/build/lib/unix",
         "//src/main/java/com/google/devtools/build/lib/util:os",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/LocalHostResourceManagerDarwin.java b/src/main/java/com/google/devtools/build/lib/actions/LocalHostResourceManagerDarwin.java
index 4f06c28..12e178a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/LocalHostResourceManagerDarwin.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/LocalHostResourceManagerDarwin.java
@@ -14,16 +14,14 @@
 
 package com.google.devtools.build.lib.actions;
 
+import com.google.devtools.build.lib.jni.JniLoader;
 import com.google.devtools.build.lib.unix.NativePosixSystem;
-
 import java.io.IOException;
 
 /**
  * This class estimates the local host's resource capacity for Darwin.
  */
 public class LocalHostResourceManagerDarwin {
-  private static final Boolean JNI_UNAVAILABLE =
-      "0".equals(System.getProperty("io.bazel.EnableJni"));
 
   private static int getLogicalCpuCount() throws IOException {
     return (int) NativePosixSystem.sysctlbynameGetLong("hw.logicalcpu");
@@ -34,9 +32,10 @@
   }
 
   public static ResourceSet getLocalHostResources() {
-    if (JNI_UNAVAILABLE) {
+    if (!JniLoader.isJniAvailable()) {
       return null;
     }
+
     try {
       int logicalCpuCount = getLogicalCpuCount();
       double ramMb = getMemoryInMb();
diff --git a/src/main/java/com/google/devtools/build/lib/jni/BUILD b/src/main/java/com/google/devtools/build/lib/jni/BUILD
new file mode 100644
index 0000000..00e0bc6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/jni/BUILD
@@ -0,0 +1,24 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+    default_visibility = ["//src:__subpackages__"],
+)
+
+licenses(["notice"])
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["//src:__subpackages__"],
+)
+
+java_library(
+    name = "jni",
+    srcs = ["JniLoader.java"],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib/unix/jni",
+        "//src/main/java/com/google/devtools/build/lib/util:os",
+        "//src/main/java/com/google/devtools/build/lib/windows/jni:jni-loader",
+        "//third_party:flogger",
+    ],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/jni/JniLoader.java b/src/main/java/com/google/devtools/build/lib/jni/JniLoader.java
new file mode 100644
index 0000000..dfc8f38
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/jni/JniLoader.java
@@ -0,0 +1,72 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.jni;
+
+import com.google.common.flogger.GoogleLogger;
+import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
+import com.google.devtools.build.lib.util.OS;
+import com.google.devtools.build.lib.windows.jni.WindowsJniLoader;
+
+/** Generic code to interact with the platform-specific JNI code bundle. */
+public final class JniLoader {
+
+  private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
+
+  private static final boolean JNI_AVAILABLE;
+
+  static {
+    boolean jniAvailable;
+    try {
+      switch (OS.getCurrent()) {
+        case LINUX:
+        case FREEBSD:
+        case OPENBSD:
+        case UNKNOWN:
+        case DARWIN:
+          UnixJniLoader.loadJni();
+          break;
+        case WINDOWS:
+          WindowsJniLoader.loadJni();
+          break;
+        default:
+          throw new AssertionError("switch statement out of sync with OS values");
+      }
+      jniAvailable = true;
+    } catch (UnsatisfiedLinkError e) {
+      logger.atWarning().withCause(e).log("Failed to load JNI library");
+      jniAvailable = false;
+    }
+    JNI_AVAILABLE = jniAvailable;
+  }
+
+  protected JniLoader() {}
+
+  /**
+   * Triggers the load of the JNI bundle in a platform-independent basis.
+   *
+   * <p>This does <b>not</b> fail if the JNI bundle cannot be loaded because there are scenarios in
+   * which we want to run Bazel without JNI (e.g. during bootstrapping). We rely on the fact that
+   * any calls to native code will fail anyway and with a more descriptive error message if we
+   * failed to load the JNI bundle.
+   *
+   * <p>Callers can check if the JNI bundle load succeeded by calling {@link #isJniAvailable()}.
+   */
+  public static void loadJni() {}
+
+  /** Checks whether the JNI bundle was successfully loaded or not. */
+  public static boolean isJniAvailable() {
+    return JNI_AVAILABLE;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/platform/BUILD b/src/main/java/com/google/devtools/build/lib/platform/BUILD
index 888fa41..0445f44 100644
--- a/src/main/java/com/google/devtools/build/lib/platform/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/platform/BUILD
@@ -14,9 +14,8 @@
     name = "sleep_prevention_module",
     srcs = ["SleepPreventionModule.java"],
     deps = [
-        ":jni_loader",
         "//src/main/java/com/google/devtools/build/lib:runtime",
-        "//src/main/java/com/google/devtools/build/lib/util",
+        "//src/main/java/com/google/devtools/build/lib/jni",
         "//third_party:guava",
     ],
 )
@@ -27,7 +26,7 @@
         "SuspendCounter.java",
     ],
     deps = [
-        ":jni_loader",
+        "//src/main/java/com/google/devtools/build/lib/jni",
     ],
 )
 
@@ -37,17 +36,6 @@
         "MemoryPressureCounter.java",
     ],
     deps = [
-        ":jni_loader",
-    ],
-)
-
-java_library(
-    name = "jni_loader",
-    srcs = ["JniLoader.java"],
-    deps = [
-        "//src/main/java/com/google/devtools/build/lib/unix",
-        "//src/main/java/com/google/devtools/build/lib/unix/jni",
-        "//src/main/java/com/google/devtools/build/lib/util:os",
-        "//src/main/java/com/google/devtools/build/lib/windows/jni:jni-loader",
+        "//src/main/java/com/google/devtools/build/lib/jni",
     ],
 )
diff --git a/src/main/java/com/google/devtools/build/lib/platform/JniLoader.java b/src/main/java/com/google/devtools/build/lib/platform/JniLoader.java
deleted file mode 100644
index a61bea0..0000000
--- a/src/main/java/com/google/devtools/build/lib/platform/JniLoader.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.platform;
-
-import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
-import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.windows.jni.WindowsJniLoader;
-
-/** All classes that extend this class depend on being able to load jni. */
-class JniLoader {
-  private static final boolean JNI_ENABLED;
-
-  static {
-    JNI_ENABLED = !"0".equals(System.getProperty("io.bazel.EnableJni"));
-    if (JNI_ENABLED) {
-      switch (OS.getCurrent()) {
-        case LINUX:
-        case FREEBSD:
-        case OPENBSD:
-        case UNKNOWN:
-        case DARWIN:
-          UnixJniLoader.loadJni();
-          break;
-        case WINDOWS:
-          WindowsJniLoader.loadJni();
-          break;
-        default:
-          throw new AssertionError("switch statement out of sync with OS values");
-      }
-    }
-  }
-
-  protected JniLoader() {}
-
-  public static boolean jniEnabled() {
-    return JNI_ENABLED;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/platform/MemoryPressureCounter.java b/src/main/java/com/google/devtools/build/lib/platform/MemoryPressureCounter.java
index 0cdcb60..5025a9a 100644
--- a/src/main/java/com/google/devtools/build/lib/platform/MemoryPressureCounter.java
+++ b/src/main/java/com/google/devtools/build/lib/platform/MemoryPressureCounter.java
@@ -14,19 +14,28 @@
 
 package com.google.devtools.build.lib.platform;
 
+import com.google.devtools.build.lib.jni.JniLoader;
+
 /** Native methods for dealing with memory pressure events. */
-public final class MemoryPressureCounter extends JniLoader {
+public final class MemoryPressureCounter {
+
+  static {
+    JniLoader.loadJni();
+  }
+
+  private MemoryPressureCounter() {}
+
   static native int warningCountJNI();
 
   static native int criticalCountJNI();
 
   /** The number of times that a memory pressure warning notification has been seen. */
   public static int warningCount() {
-    return JniLoader.jniEnabled() ? warningCountJNI() : 0;
+    return JniLoader.isJniAvailable() ? warningCountJNI() : 0;
   }
 
   /** The number of times that a memory pressure critical notification has been seen. */
   public static int criticalCount() {
-    return JniLoader.jniEnabled() ? criticalCountJNI() : 0;
+    return JniLoader.isJniAvailable() ? criticalCountJNI() : 0;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/platform/SleepPreventionModule.java b/src/main/java/com/google/devtools/build/lib/platform/SleepPreventionModule.java
index 9a85c9c..0ab4510 100644
--- a/src/main/java/com/google/devtools/build/lib/platform/SleepPreventionModule.java
+++ b/src/main/java/com/google/devtools/build/lib/platform/SleepPreventionModule.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.platform;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.devtools.build.lib.jni.JniLoader;
 import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
 
@@ -23,7 +24,14 @@
 
   /** Methods for dealing with sleep prevention on local hardware. */
   @VisibleForTesting
-  public static final class SleepPrevention extends JniLoader {
+  public static final class SleepPrevention {
+
+    static {
+      JniLoader.loadJni();
+    }
+
+    private SleepPrevention() {}
+
     /**
      * Push a request to disable automatic sleep for hardware. Useful for making sure computers
      * don't go to sleep during long builds. Must be matched with a {@link #popDisableSleep} call.
@@ -44,14 +52,14 @@
 
   @Override
   public void beforeCommand(CommandEnvironment env) {
-    if (JniLoader.jniEnabled()) {
+    if (JniLoader.isJniAvailable()) {
       SleepPrevention.pushDisableSleep();
     }
   }
 
   @Override
   public void afterCommand() {
-    if (JniLoader.jniEnabled()) {
+    if (JniLoader.isJniAvailable()) {
       SleepPrevention.popDisableSleep();
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/platform/SuspendCounter.java b/src/main/java/com/google/devtools/build/lib/platform/SuspendCounter.java
index 99bb9b8..8ef12a4 100644
--- a/src/main/java/com/google/devtools/build/lib/platform/SuspendCounter.java
+++ b/src/main/java/com/google/devtools/build/lib/platform/SuspendCounter.java
@@ -14,8 +14,17 @@
 
 package com.google.devtools.build.lib.platform;
 
+import com.google.devtools.build.lib.jni.JniLoader;
+
 /** Native methods for dealing with suspension events. */
-public final class SuspendCounter extends JniLoader {
+public final class SuspendCounter {
+
+  static {
+    JniLoader.loadJni();
+  }
+
+  private SuspendCounter() {}
+
   static native int suspendCountJNI();
 
   /**
@@ -23,6 +32,6 @@
    * platform equivalents to a SIGSTOP/SIGTSTP.
    */
   public static int suspendCount() {
-    return JniLoader.jniEnabled() ? suspendCountJNI() : 0;
+    return JniLoader.isJniAvailable() ? suspendCountJNI() : 0;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.java
index 8616ed5..a01ea95 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.java
@@ -16,6 +16,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.base.Strings;
+import com.google.devtools.build.lib.jni.JniLoader;
 import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.server.FailureDetails.Filesystem;
 import com.google.devtools.build.lib.server.FailureDetails.Filesystem.Code;
@@ -26,6 +27,7 @@
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.DigestHashFunction;
 import com.google.devtools.build.lib.vfs.DigestHashFunction.DigestFunctionConverter;
+import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.windows.WindowsFileSystem;
@@ -65,18 +67,17 @@
             e);
       }
     }
-    boolean enableSymLinks = options.enableWindowsSymlinks;
-    if ("0".equals(System.getProperty("io.bazel.EnableJni"))) {
-      // Ignore UnixFileSystem, to be used for bootstrapping.
-      return ModuleFileSystem.create(
-          OS.getCurrent() == OS.WINDOWS
-              ? new WindowsFileSystem(digestHashFunction, enableSymLinks)
-              : new JavaIoFileSystem(digestHashFunction));
+
+    FileSystem fs;
+    if (OS.getCurrent() == OS.WINDOWS) {
+      fs = new WindowsFileSystem(digestHashFunction, options.enableWindowsSymlinks);
+    } else {
+      if (JniLoader.isJniAvailable()) {
+        fs = new UnixFileSystem(digestHashFunction, options.unixDigestHashAttributeName);
+      } else {
+        fs = new JavaIoFileSystem(digestHashFunction);
+      }
     }
-    // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
-    return ModuleFileSystem.create(
-        OS.getCurrent() == OS.WINDOWS
-            ? new WindowsFileSystem(digestHashFunction, enableSymLinks)
-            : new UnixFileSystem(digestHashFunction, options.unixDigestHashAttributeName));
+    return ModuleFileSystem.create(fs);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index 487d368..ced92e7 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -50,6 +50,7 @@
 import com.google.devtools.build.lib.events.ExtendedEventHandler;
 import com.google.devtools.build.lib.events.OutputFilter;
 import com.google.devtools.build.lib.exec.BinTools;
+import com.google.devtools.build.lib.jni.JniLoader;
 import com.google.devtools.build.lib.packages.Package.Builder.DefaultPackageSettings;
 import com.google.devtools.build.lib.packages.Package.Builder.PackageSettings;
 import com.google.devtools.build.lib.packages.PackageFactory;
@@ -1074,7 +1075,7 @@
   }
 
   private static SubprocessFactory subprocessFactoryImplementation() {
-    if (!"0".equals(System.getProperty("io.bazel.EnableJni")) && OS.getCurrent() == OS.WINDOWS) {
+    if (JniLoader.isJniAvailable() && OS.getCurrent() == OS.WINDOWS) {
       return WindowsSubprocessFactory.INSTANCE;
     } else {
       return JavaSubprocessFactory.INSTANCE;
@@ -1315,11 +1316,7 @@
   /** Loads JNI libraries, if necessary under the current platform. */
   @Nullable
   private static Integer maybeForceJNIByGettingPid(@Nullable PathFragment installBase) {
-    return jniLibsAvailable() ? getPidUsingJNI(installBase) : null;
-  }
-
-  private static boolean jniLibsAvailable() {
-    return !"0".equals(System.getProperty("io.bazel.EnableJni"));
+    return JniLoader.isJniAvailable() ? getPidUsingJNI(installBase) : null;
   }
 
   // Force JNI linking at a moment when we have 'installBase' handy, and print
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index 8062c16..f769caf 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -1616,7 +1616,7 @@
         ":broken_diff_awareness_exception",
         ":diff_awareness",
         ":incompatible_view_exception",
-        "//src/main/java/com/google/devtools/build/lib/unix/jni",
+        "//src/main/java/com/google/devtools/build/lib/jni",
         "//src/main/java/com/google/devtools/build/lib/util:os",
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/MacOSXFsEventsDiffAwareness.java b/src/main/java/com/google/devtools/build/lib/skyframe/MacOSXFsEventsDiffAwareness.java
index c056383..d8c5d17 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/MacOSXFsEventsDiffAwareness.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/MacOSXFsEventsDiffAwareness.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
-import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
+import com.google.devtools.build.lib.jni.JniLoader;
 import com.google.devtools.common.options.OptionsProvider;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -118,7 +118,7 @@
   static {
     boolean loadJniWorked = false;
     try {
-      UnixJniLoader.loadJni();
+      JniLoader.loadJni();
       loadJniWorked = true;
     } catch (UnsatisfiedLinkError ignored) {
       // Unfortunately, we compile this class into the Bazel bootstrap binary, which doesn't have
diff --git a/src/main/java/com/google/devtools/build/lib/unix/BUILD b/src/main/java/com/google/devtools/build/lib/unix/BUILD
index 76b8d76..7b1aac6 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/unix/BUILD
@@ -17,9 +17,8 @@
     srcs = glob(["*.java"]),
     deps = [
         "//src/main/java/com/google/devtools/build/lib/concurrent",
+        "//src/main/java/com/google/devtools/build/lib/jni",
         "//src/main/java/com/google/devtools/build/lib/profiler",
-        "//src/main/java/com/google/devtools/build/lib/shell",
-        "//src/main/java/com/google/devtools/build/lib/unix/jni",
         "//src/main/java/com/google/devtools/build/lib/util:os",
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
diff --git a/src/main/java/com/google/devtools/build/lib/unix/ErrnoFileStatus.java b/src/main/java/com/google/devtools/build/lib/unix/ErrnoFileStatus.java
index 296f02a..936d94f 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/ErrnoFileStatus.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/ErrnoFileStatus.java
@@ -14,7 +14,7 @@
 
 package com.google.devtools.build.lib.unix;
 
-import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
+import com.google.devtools.build.lib.jni.JniLoader;
 import com.google.devtools.build.lib.util.OS;
 
 /**
@@ -88,7 +88,7 @@
     }
 
     static {
-      UnixJniLoader.loadJni();
+      JniLoader.loadJni();
     }
 
     private native void initErrnoConstants();
diff --git a/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java b/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java
index 8f0e4f4..b53633a 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java
@@ -16,7 +16,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.flogger.GoogleLogger;
-import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
+import com.google.devtools.build.lib.jni.JniLoader;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.logging.LogManager;
@@ -49,7 +49,7 @@
         }
       }.start();
     }
-    UnixJniLoader.loadJni();
+    JniLoader.loadJni();
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/unix/NativePosixSystem.java b/src/main/java/com/google/devtools/build/lib/unix/NativePosixSystem.java
index e336c2a..11f19b6 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/NativePosixSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/NativePosixSystem.java
@@ -14,7 +14,7 @@
 
 package com.google.devtools.build.lib.unix;
 
-import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
+import com.google.devtools.build.lib.jni.JniLoader;
 import java.io.IOException;
 
 /**
@@ -24,14 +24,12 @@
  */
 public class NativePosixSystem {
 
-  private NativePosixSystem() {}
-
   static {
-    if (!"0".equals(System.getProperty("io.bazel.EnableJni"))) {
-      UnixJniLoader.loadJni();
-    }
+    JniLoader.loadJni();
   }
 
+  private NativePosixSystem() {}
+
   /**
    * Native wrapper around POSIX sysctlbyname(3) syscall.
    *
diff --git a/src/main/java/com/google/devtools/build/lib/unix/ProcessUtils.java b/src/main/java/com/google/devtools/build/lib/unix/ProcessUtils.java
index bfe1f79..7819d3e 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/ProcessUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/ProcessUtils.java
@@ -13,21 +13,19 @@
 // limitations under the License.
 package com.google.devtools.build.lib.unix;
 
-import com.google.devtools.build.lib.unix.jni.UnixJniLoader;
+import com.google.devtools.build.lib.jni.JniLoader;
 
 /**
  * Various utilities related to UNIX processes.
  */
 public final class ProcessUtils {
 
-  private ProcessUtils() {}
-
   static {
-    if (!"0".equals(System.getProperty("io.bazel.EnableJni"))) {
-      UnixJniLoader.loadJni();
-    }
+    JniLoader.loadJni();
   }
 
+  private ProcessUtils() {}
+
   /**
    * Native wrapper around POSIX getgid(2).
    *
diff --git a/src/main/java/com/google/devtools/build/lib/windows/jni/BUILD b/src/main/java/com/google/devtools/build/lib/windows/jni/BUILD
index 95b5224..584ac85 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/jni/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/windows/jni/BUILD
@@ -45,14 +45,14 @@
     name = "file",
     srcs = ["WindowsFileOperations.java"],
     visibility = [":android-junctions-prod"],
-    deps = [":jni-loader"],
+    deps = ["//src/main/java/com/google/devtools/build/lib/jni"],
 )
 
 java_library(
     name = "processes",
     srcs = ["WindowsProcesses.java"],
     visibility = [":bazel-prod"],
-    deps = [":jni-loader"],
+    deps = ["//src/main/java/com/google/devtools/build/lib/jni"],
 )
 
 # Java interfaces to various native Windows system services.
diff --git a/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java b/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java
index 9d7e937..163d20b 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.windows.jni;
 
+import com.google.devtools.build.lib.jni.JniLoader;
 import java.io.IOException;
 
 /** File operations on Windows. */
@@ -122,7 +123,7 @@
 
   /** Determines whether `path` is a junction point or directory symlink. */
   public static boolean isSymlinkOrJunction(String path) throws IOException {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     boolean[] result = new boolean[] {false};
     String[] error = new String[] {null};
     switch (nativeIsSymlinkOrJunction(asLongPath(path), result, error)) {
@@ -152,7 +153,7 @@
    * @throws IOException if the `path` is not found or some other I/O error occurs
    */
   public static String getLongPath(String path) throws IOException {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     String[] result = new String[] {null};
     String[] error = new String[] {null};
     if (nativeGetLongPath(asLongPath(path), result, error)) {
@@ -190,7 +191,7 @@
    * @throws IOException if some error occurs
    */
   public static void createJunction(String name, String target) throws IOException {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     String[] error = new String[] {null};
     switch (nativeCreateJunction(asLongPath(name), asLongPath(target), error)) {
       case CREATE_JUNCTION_SUCCESS:
@@ -219,7 +220,7 @@
   }
 
   public static void createSymlink(String name, String target) throws IOException {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     String[] error = new String[] {null};
     switch (nativeCreateSymlink(asLongPath(name), asLongPath(target), error)) {
       case CREATE_SYMLINK_SUCCESS:
@@ -236,7 +237,7 @@
   }
 
   public static ReadSymlinkOrJunctionResult readSymlinkOrJunction(String name) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     String[] target = new String[] {null};
     String[] error = new String[] {null};
     switch (nativeReadSymlinkOrJunction(asLongPath(name), target, error)) {
@@ -266,7 +267,7 @@
   }
 
   public static boolean deletePath(String path) throws IOException {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     String[] error = new String[] {null};
     int result = nativeDeletePath(asLongPath(path), error);
     switch (result) {
diff --git a/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsProcesses.java b/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsProcesses.java
index 63d72c5..90bb2d8 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsProcesses.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsProcesses.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.windows.jni;
 
+import com.google.devtools.build.lib.jni.JniLoader;
 
 /** Process management on Windows. */
 public class WindowsProcesses {
@@ -50,14 +51,14 @@
       String stdoutFile,
       String stderrFile,
       boolean redirectErrorStream) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeCreateProcess(
         argv0, argvRest, env, cwd, stdoutFile, stderrFile, redirectErrorStream);
   }
 
   public static long createProcess(
       String argv0, String argvRest, byte[] env, String cwd, String stdoutFile, String stderrFile) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeCreateProcess(argv0, argvRest, env, cwd, stdoutFile, stderrFile, false);
   }
 
@@ -78,7 +79,7 @@
    * @return the number of bytes written
    */
   public static int writeStdin(long process, byte[] bytes, int offset, int length) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeWriteStdin(process, bytes, offset, length);
   }
 
@@ -86,7 +87,7 @@
 
   /** Returns an opaque identifier of stdout stream for the process. */
   public static long getStdout(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeGetStdout(process);
   }
 
@@ -94,7 +95,7 @@
 
   /** Returns an opaque identifier of stderr stream for the process. */
   public static long getStderr(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeGetStderr(process);
   }
 
@@ -109,7 +110,7 @@
    * @return the number of bytes read, 0 on EOF, or -1 if there was an error.
    */
   public static int readStream(long stream, byte[] bytes, int offset, int length) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeReadStream(stream, bytes, offset, length);
   }
 
@@ -125,7 +126,7 @@
    * <li>2: Something went wrong
    */
   public static int waitFor(long process, long timeout) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeWaitFor(process, timeout);
   }
 
@@ -136,7 +137,7 @@
    * wrong.
    */
   public static int getExitCode(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeGetExitCode(process);
   }
 
@@ -144,7 +145,7 @@
 
   /** Returns the process ID of the given process or -1 if there was an error. */
   public static int getProcessPid(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeGetProcessPid(process);
   }
 
@@ -152,7 +153,7 @@
 
   /** Terminates the given process. Returns true if the termination was successful. */
   public static boolean terminate(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeTerminate(process);
   }
 
@@ -165,7 +166,7 @@
    * or worse.
    */
   public static void deleteProcess(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     nativeDeleteProcess(process);
   }
 
@@ -178,7 +179,7 @@
    *     #nativeGetStderr(long)}.
    */
   public static void closeStream(long stream) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     nativeCloseStream(stream);
   }
 
@@ -194,14 +195,14 @@
    * failed operation in between.
    */
   public static String processGetLastError(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeProcessGetLastError(process);
   }
 
   private static native String nativeProcessGetLastError(long process);
 
   public static String streamGetLastError(long process) {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeStreamGetLastError(process);
   }
 
@@ -209,7 +210,7 @@
 
   /** returns the PID of the current process. */
   public static int getpid() {
-    WindowsJniLoader.loadJni();
+    JniLoader.loadJni();
     return nativeGetpid();
   }