Add support for a cc_import() of a DLL with no interface library on Windows.

Depending on this is very nearly a no-op; the linker cannot reference DLLs with no interface library. However, it's necessary for the copy_dynamic_libraries_to_binary feature to work in cases where a DLL is referenced without use of its interface library. (e.g., LoadLibrary, or one DLL a target depends on that itself depends on another DLL).

RELNOTES: Allow cc_import() of a DLL with no interface library on Windows, used to document runtime dependencies.
PiperOrigin-RevId: 248716186
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java
index b93932a..152768a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java
@@ -78,13 +78,12 @@
         CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
     FeatureConfiguration featureConfiguration =
         CcCommon.configureFeaturesOrReportRuleError(ruleContext, ccToolchain);
-    boolean targetWindows = featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS);
 
     Artifact staticLibrary = ruleContext.getPrerequisiteArtifact("static_library", Mode.TARGET);
     Artifact sharedLibrary = ruleContext.getPrerequisiteArtifact("shared_library", Mode.TARGET);
     Artifact interfaceLibrary =
         ruleContext.getPrerequisiteArtifact("interface_library", Mode.TARGET);
-    performErrorChecks(ruleContext, systemProvided, sharedLibrary, interfaceLibrary, targetWindows);
+    performErrorChecks(ruleContext, systemProvided, sharedLibrary, interfaceLibrary);
 
     Artifact resolvedSymlinkDynamicLibrary = null;
     Artifact resolvedSymlinkInterfaceLibrary = null;
@@ -192,8 +191,7 @@
       RuleContext ruleContext,
       boolean systemProvided,
       Artifact sharedLibrary,
-      Artifact interfaceLibrary,
-      boolean targetsWindows) {
+      Artifact interfaceLibrary) {
     // If the shared library will be provided by system during runtime, users are not supposed to
     // specify shared_library.
     if (systemProvided && sharedLibrary != null) {
@@ -206,11 +204,5 @@
       ruleContext.ruleError(
           "'shared_library' should be specified when 'system_provided' is false");
     }
-
-    if (targetsWindows && sharedLibrary != null && interfaceLibrary == null) {
-      ruleContext.ruleError(
-          "'interface library' must be specified when using cc_import for shared library on"
-              + " Windows");
-    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java
index 4ae243e..c8c8dbd 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java
@@ -286,15 +286,18 @@
         !Link.useStartEndLib(
             input,
             CppHelper.getArchiveType(cppConfiguration, ccToolchainProvider, featureConfiguration)));
+
+    expandedLinkerInputsBuilder.add(input);
     if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)
         && ccToolchainProvider.supportsInterfaceSharedLibraries(featureConfiguration)) {
       // On Windows, dynamic library (dll) cannot be linked directly when using toolchains that
-      // support interface library (eg. MSVC).
-      Preconditions.checkState(
-          !CppFileTypes.SHARED_LIBRARY.matches(input.getArtifact().getFilename()));
+      // support interface library (eg. MSVC). If the user is doing so, it is only to be referenced
+      // in other places (such as copy_dynamic_libraries_to_binary); skip adding it.
+      if (CppFileTypes.SHARED_LIBRARY.matches(input.getArtifact().getFilename())) {
+        return;
+      }
     }
 
-    expandedLinkerInputsBuilder.add(input);
     Artifact inputArtifact = input.getArtifact();
     PathFragment libDir = inputArtifact.getExecPath().getParentDirectory();
     if (!libDir.equals(solibDir)
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcImportBaseConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcImportBaseConfiguredTargetTest.java
index 2fe24fb..782e8cc 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcImportBaseConfiguredTargetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcImportBaseConfiguredTargetTest.java
@@ -114,7 +114,7 @@
   }
 
   @Test
-  public void testWrongCcImportDefinitionsOnWindows() throws Exception {
+  public void testRuntimeOnlyCcImportDefinitionsOnWindows() throws Exception {
     AnalysisMock.get()
         .ccSupport()
         .setupCcToolchainConfig(
@@ -124,15 +124,22 @@
                     CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY,
                     CppRuleClasses.TARGETS_WINDOWS));
     useConfiguration();
-    checkError(
-        "a",
-        "foo",
-        "'interface library' must be specified when using cc_import for shared library on Windows",
-        skylarkImplementationLoadStatement,
-        "cc_import(",
-        "  name = 'foo',",
-        "  shared_library = 'libfoo.dll',",
-        ")");
+    ConfiguredTarget target =
+        scratchConfiguredTarget(
+            "a",
+            "foo",
+            skylarkImplementationLoadStatement,
+            "cc_import(name = 'foo', shared_library = 'libfoo.dll')");
+    Artifact dynamicLibrary =
+        Iterables.getOnlyElement(target.get(CcInfo.PROVIDER).getCcLinkingContext().getLibraries())
+            .getResolvedSymlinkDynamicLibrary();
+    Iterable<Artifact> dynamicLibrariesForRuntime =
+        target
+            .get(CcInfo.PROVIDER)
+            .getCcLinkingContext()
+            .getDynamicLibrariesForRuntime(/* linkingStatically= */ false);
+    assertThat(dynamicLibrary).isEqualTo(null);
+    assertThat(artifactsToStrings(dynamicLibrariesForRuntime)).containsExactly("src a/libfoo.dll");
   }
 
   @Test