C++: Bazel compile/link API

Introduces the C++ Starlark API to Bazel after discussion in https://docs.google.com/document/d/1cRRdHOPTTUXBbq9Cj9hk_WLnPqsGtAoQynYd7TKBQI8/edit.

This includes an integration test and will be followed by more integration tests for Objective-C as well as unit tests.

RELNOTES:Bazel C++ compile/link Starlark API. Can be used with experimental flag --experimental_cc_skylark_api_enabled_packages=<package_path>,<package_path2>.
PiperOrigin-RevId: 242825783
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index a125fe2..d4a86c7 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -747,6 +747,7 @@
         ":packages-internal",
         ":proto-rules",
         ":python-rules",
+        ":skylarkinterface_internal",
         ":testing-support-rules",
         ":util",
         "//src/main/java/com/google/devtools/build/lib/actions",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcModule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcModule.java
index 5201065..06bdaca 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcModule.java
@@ -14,23 +14,43 @@
 
 package com.google.devtools.build.lib.bazel.rules.cpp;
 
+import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
 import com.google.devtools.build.lib.analysis.skylark.SkylarkActionFactory;
 import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import com.google.devtools.build.lib.rules.cpp.CcCommon;
 import com.google.devtools.build.lib.rules.cpp.CcCompilationContext;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
 import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper;
 import com.google.devtools.build.lib.rules.cpp.CcLinkingOutputs;
 import com.google.devtools.build.lib.rules.cpp.CcModule;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainConfigInfo;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CppRuleClasses;
+import com.google.devtools.build.lib.rules.cpp.FdoContext;
 import com.google.devtools.build.lib.rules.cpp.FeatureConfigurationForStarlark;
 import com.google.devtools.build.lib.rules.cpp.LibraryToLink;
 import com.google.devtools.build.lib.rules.cpp.LibraryToLink.CcLinkingContext;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkingMode;
 import com.google.devtools.build.lib.skylarkbuildapi.cpp.BazelCcModuleApi;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
+import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.List;
+import java.util.stream.Stream;
 
 /**
  * A module that contains Skylark utilities for C++ support.
@@ -48,11 +68,30 @@
         CcCompilationContext,
         CcCompilationOutputs,
         CcLinkingOutputs,
-        CcLinkingContext,
         LibraryToLink,
+        CcLinkingContext,
         CcToolchainVariables,
         CcToolchainConfigInfo> {
 
+  private static final List<String> SUPPORTED_OUTPUT_TYPES =
+      ImmutableList.of("executable", "dynamic_library");
+
+  private enum Language {
+    CPP("c++"),
+    OBJC("objc"),
+    OBJCPP("objc++");
+
+    private String representation;
+
+    Language(String representation) {
+      this.representation = representation;
+    }
+
+    private String getRepresentation() {
+      return representation;
+    }
+  }
+
   @Override
   public Tuple<Object> compile(
       SkylarkActionFactory skylarkActionFactoryApi,
@@ -64,46 +103,261 @@
       SkylarkList<String> includes,
       SkylarkList<String> quoteIncludes,
       SkylarkList<String> systemIncludes,
+      SkylarkList<String> defines,
       SkylarkList<String> userCompileFlags,
       SkylarkList<CcCompilationContext> ccCompilationContexts,
       String name,
       boolean disallowPicOutputs,
-      boolean disallowNopicOutputs)
-      throws EvalException, InterruptedException {
-    return null;
+      boolean disallowNopicOutputs,
+      Location location,
+      Environment environment)
+      throws EvalException {
+    CcCommon.checkLocationWhitelisted(
+        environment.getSemantics(),
+        location,
+        environment.getGlobals().getLabel().getPackageIdentifier().toString());
+    SkylarkActionFactory actions = skylarkActionFactoryApi;
+    CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null);
+    FeatureConfigurationForStarlark featureConfiguration =
+        convertFromNoneable(skylarkFeatureConfiguration, null);
+    Label label = getCallerLabel(location, environment, name);
+    FdoContext fdoContext = ccToolchainProvider.getFdoContext();
+    CcCompilationHelper helper =
+        new CcCompilationHelper(
+                actions.asActionRegistry(location, actions),
+                actions.getActionConstructionContext(),
+                label,
+                /* grepIncludes= */ null,
+                BazelCppSemantics.INSTANCE,
+                featureConfiguration.getFeatureConfiguration(),
+                ccToolchainProvider,
+                fdoContext)
+            .addPublicHeaders(publicHeaders)
+            .addPrivateHeaders(privateHeaders)
+            .addSources(sources)
+            .addCcCompilationContexts(ccCompilationContexts)
+            .addIncludeDirs(
+                includes.stream()
+                    .map(PathFragment::create)
+                    .collect(ImmutableList.toImmutableList()))
+            .addQuoteIncludeDirs(
+                quoteIncludes.stream()
+                    .map(PathFragment::create)
+                    .collect(ImmutableList.toImmutableList()))
+            .addSystemIncludeDirs(
+                systemIncludes.stream()
+                    .map(PathFragment::create)
+                    .collect(ImmutableList.toImmutableList()))
+            .addDefines(defines)
+            .setCopts(userCompileFlags);
+    if (disallowNopicOutputs) {
+      helper.setGenerateNoPicAction(false);
+    }
+    if (disallowPicOutputs) {
+      helper.setGeneratePicAction(false);
+    }
+    try {
+      CompilationInfo compilationInfo = helper.compile();
+      return Tuple.of(
+          compilationInfo.getCcCompilationContext(), compilationInfo.getCcCompilationOutputs());
+    } catch (RuleErrorException e) {
+      throw new EvalException(location, e);
+    }
   }
 
   @Override
-  public CcLinkingContext createLinkingContextFromCompilationOutputs(
+  public Tuple<Object> createLinkingContextFromCompilationOutputs(
       SkylarkActionFactory skylarkActionFactoryApi,
       FeatureConfigurationForStarlark skylarkFeatureConfiguration,
       CcToolchainProvider skylarkCcToolchainProvider,
       CcCompilationOutputs compilationOutputs,
-      SkylarkList userLinkFlags,
+      SkylarkList<String> userLinkFlags,
+      SkylarkList<CcLinkingContext> linkingContexts,
       String name,
       String language,
       boolean alwayslink,
-      SkylarkList nonCodeInputs,
+      SkylarkList<Artifact> additionalInputs,
       boolean disallowStaticLibraries,
-      boolean disallowDynamicLibraries)
+      boolean disallowDynamicLibraries,
+      Location location,
+      Environment environment,
+      StarlarkContext starlarkContext)
       throws InterruptedException, EvalException {
-    return null;
+    CcCommon.checkLocationWhitelisted(
+        environment.getSemantics(),
+        location,
+        environment.getGlobals().getLabel().getPackageIdentifier().toString());
+    validateLanguage(location, language);
+    SkylarkActionFactory actions = skylarkActionFactoryApi;
+    CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null);
+    FeatureConfigurationForStarlark featureConfiguration =
+        convertFromNoneable(skylarkFeatureConfiguration, null);
+    Label label = getCallerLabel(location, environment, name);
+    FdoContext fdoContext = ccToolchainProvider.getFdoContext();
+    LinkTargetType staticLinkTargetType = null;
+    if (language.equals(Language.CPP.getRepresentation())) {
+      staticLinkTargetType = LinkTargetType.STATIC_LIBRARY;
+    } else if (language.equals(Language.OBJC.getRepresentation())
+        || language.equals(Language.OBJCPP.getRepresentation())) {
+      staticLinkTargetType = LinkTargetType.OBJC_ARCHIVE;
+    } else {
+      throw new IllegalStateException("Language is not valid.");
+    }
+    CcLinkingHelper helper =
+        new CcLinkingHelper(
+                actions.getActionConstructionContext().getRuleErrorConsumer(),
+                label,
+                actions.asActionRegistry(location, actions),
+                actions.getActionConstructionContext(),
+                BazelCppSemantics.INSTANCE,
+                featureConfiguration.getFeatureConfiguration(),
+                ccToolchainProvider,
+                fdoContext,
+                actions.getActionConstructionContext().getConfiguration(),
+                actions
+                    .getActionConstructionContext()
+                    .getConfiguration()
+                    .getFragment(CppConfiguration.class),
+                ((BazelStarlarkContext) starlarkContext).getSymbolGenerator())
+            .addNonCodeLinkerInputs(additionalInputs)
+            .setShouldCreateStaticLibraries(!disallowStaticLibraries)
+            .setShouldCreateDynamicLibrary(
+                !disallowDynamicLibraries
+                    && !featureConfiguration
+                        .getFeatureConfiguration()
+                        .isEnabled(CppRuleClasses.TARGETS_WINDOWS))
+            .setStaticLinkType(staticLinkTargetType)
+            .setDynamicLinkType(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)
+            .addLinkopts(userLinkFlags);
+    try {
+      CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY;
+      ImmutableList<LibraryToLink> libraryToLink = ImmutableList.of();
+      if (!compilationOutputs.isEmpty()) {
+        ccLinkingOutputs = helper.link(compilationOutputs);
+        if (!ccLinkingOutputs.isEmpty()) {
+          libraryToLink =
+              ImmutableList.of(
+                  ccLinkingOutputs.getLibraryToLink().toBuilder()
+                      .setAlwayslink(alwayslink)
+                      .build());
+        }
+      }
+      CcLinkingContext linkingContext =
+          helper.buildCcLinkingContextFromLibrariesToLink(
+              libraryToLink, CcCompilationContext.EMPTY);
+      return Tuple.of(
+          CcLinkingContext.merge(
+              ImmutableList.<CcLinkingContext>builder()
+                  .add(linkingContext)
+                  .addAll(linkingContexts)
+                  .build()),
+          ccLinkingOutputs);
+    } catch (RuleErrorException e) {
+      throw new EvalException(location, e);
+    }
   }
 
   @Override
   public CcLinkingOutputs link(
-      SkylarkActionFactory skylarkActionFactoryApi,
+      SkylarkActionFactory actions,
       FeatureConfigurationForStarlark skylarkFeatureConfiguration,
       CcToolchainProvider skylarkCcToolchainProvider,
-      Object compilationOutputs,
-      SkylarkList userLinkFlags,
-      SkylarkList linkingContexts,
+      CcCompilationOutputs compilationOutputs,
+      SkylarkList<String> userLinkFlags,
+      SkylarkList<CcLinkingContext> linkingContexts,
       String name,
       String language,
       String outputType,
       boolean linkDepsStatically,
-      SkylarkList nonCodeInputs)
+      SkylarkList<Artifact> additionalInputs,
+      Location location,
+      Environment environment,
+      StarlarkContext starlarkContext)
       throws InterruptedException, EvalException {
-    return null;
+    CcCommon.checkLocationWhitelisted(
+        environment.getSemantics(),
+        location,
+        environment.getGlobals().getLabel().getPackageIdentifier().toString());
+    validateLanguage(location, language);
+    validateOutputType(location, outputType);
+    CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null);
+    FeatureConfigurationForStarlark featureConfiguration =
+        convertFromNoneable(skylarkFeatureConfiguration, null);
+    Label label = getCallerLabel(location, environment, name);
+    FdoContext fdoContext = ccToolchainProvider.getFdoContext();
+    LinkTargetType dynamicLinkTargetType = null;
+    if (language.equals(Language.CPP.getRepresentation())) {
+      if (outputType.equals("executable")) {
+        dynamicLinkTargetType = LinkTargetType.EXECUTABLE;
+      } else if (outputType.equals("dynamic_library")) {
+        dynamicLinkTargetType = LinkTargetType.DYNAMIC_LIBRARY;
+      }
+    } else if (language.equals(Language.OBJC.getRepresentation())
+        && outputType.equals("executable")) {
+      dynamicLinkTargetType = LinkTargetType.OBJC_EXECUTABLE;
+    } else if (language.equals(Language.OBJCPP.getRepresentation())
+        && outputType.equals("executable")) {
+      dynamicLinkTargetType = LinkTargetType.OBJCPP_EXECUTABLE;
+    } else {
+      throw new EvalException(
+          location, "Language '" + language + "' does not support " + outputType);
+    }
+
+    CcLinkingHelper helper =
+        new CcLinkingHelper(
+                actions.getActionConstructionContext().getRuleErrorConsumer(),
+                label,
+                actions.asActionRegistry(location, actions),
+                actions.getActionConstructionContext(),
+                BazelCppSemantics.INSTANCE,
+                featureConfiguration.getFeatureConfiguration(),
+                ccToolchainProvider,
+                fdoContext,
+                actions.getActionConstructionContext().getConfiguration(),
+                actions
+                    .getActionConstructionContext()
+                    .getConfiguration()
+                    .getFragment(CppConfiguration.class),
+                ((BazelStarlarkContext) starlarkContext).getSymbolGenerator())
+            .setLinkingMode(linkDepsStatically ? LinkingMode.STATIC : LinkingMode.DYNAMIC)
+            .addNonCodeLinkerInputs(additionalInputs)
+            .setDynamicLinkType(dynamicLinkTargetType)
+            .addCcLinkingContexts(linkingContexts)
+            .addLinkopts(userLinkFlags);
+    try {
+      CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY;
+      if (!compilationOutputs.isEmpty()) {
+        ccLinkingOutputs = helper.link(compilationOutputs);
+      }
+      return ccLinkingOutputs;
+    } catch (RuleErrorException e) {
+      throw new EvalException(location, e);
+    }
+  }
+
+  private void validateLanguage(Location location, String language) throws EvalException {
+    if (!Stream.of(Language.values())
+        .map(Language::getRepresentation)
+        .collect(ImmutableList.toImmutableList())
+        .contains(language)) {
+      throw new EvalException(location, "Language '" + language + "' is not supported");
+    }
+  }
+
+  private void validateOutputType(Location location, String outputType) throws EvalException {
+    if (!SUPPORTED_OUTPUT_TYPES.contains(outputType)) {
+      throw new EvalException(location, "Output type '" + outputType + "' is not supported");
+    }
+  }
+
+  private Label getCallerLabel(Location location, Environment environment, String name)
+      throws EvalException {
+    Label label;
+    try {
+      label = Label.create(environment.getCallerLabel().getPackageName(), name);
+    } catch (LabelSyntaxException e) {
+      throw new EvalException(location, e);
+    }
+    return label;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
index 81e7d5c..e65f09b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
@@ -268,7 +268,6 @@
   private String stripIncludePrefix = null;
   private String includePrefix = null;
 
-  // TODO(plf): Pull out of class.
   private CcCompilationContext ccCompilationContext;
 
   private final RuleErrorConsumer ruleErrorConsumer;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
index cdd9e58..a7ae6b7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
@@ -20,7 +20,10 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcCompilationOutputsApi;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import java.util.LinkedHashSet;
 import java.util.Set;
@@ -99,10 +102,35 @@
   }
 
   @Override
-  public SkylarkList<Artifact> getSkylarkObjectFiles(boolean usePic) {
+  public SkylarkList<Artifact> getSkylarkObjectFiles(
+      boolean usePic, Location location, Environment environment) throws EvalException {
+    CcCommon.checkLocationWhitelisted(
+        environment.getSemantics(),
+        location,
+        environment.getGlobals().getLabel().getPackageIdentifier().toString());
     return SkylarkList.createImmutable(getObjectFiles(usePic));
   }
 
+  @Override
+  public SkylarkList<Artifact> getSkylarkObjects(Location location, Environment environment)
+      throws EvalException {
+    CcCommon.checkLocationWhitelisted(
+        environment.getSemantics(),
+        location,
+        environment.getGlobals().getLabel().getPackageIdentifier().toString());
+    return SkylarkList.createImmutable(getObjectFiles(/* usePic= */ false));
+  }
+
+  @Override
+  public SkylarkList<Artifact> getSkylarkPicObjects(Location location, Environment environment)
+      throws EvalException {
+    CcCommon.checkLocationWhitelisted(
+        environment.getSemantics(),
+        location,
+        environment.getGlobals().getLabel().getPackageIdentifier().toString());
+    return SkylarkList.createImmutable(getObjectFiles(/* usePic= */ true));
+  }
+
   /** Returns information about bitcode object files resulting from compilation. */
   public LtoCompilationContext getLtoCompilationContext() {
     return ltoCompilationContext;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
index 43596eb..4bbaf1f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
@@ -262,7 +262,15 @@
    * <p>This only sets the link type (see {@link #setStaticLinkType}), either to a static library or
    * to an alwayslink static library (blaze uses a different file extension to signal alwayslink to
    * downstream code).
+   *
+   * @deprecated This is only set here for naming always to link static libraries with the *.lo
+   *     extension. This is no longer needed because {@link
+   *     com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink}s already carry
+   *     information about whether a library should always be linked or not. This method will be
+   *     removed when we no longer use *.lo for always to link static libraries in native
+   *     cc_library.
    */
+  @Deprecated
   public CcLinkingHelper setAlwayslink(boolean alwayslink) {
     staticLinkType =
         alwayslink ? LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY : LinkTargetType.STATIC_LIBRARY;
@@ -274,7 +282,6 @@
    * anything other than a static link causes this class to skip the link action creation. This
    * exists only for Objective-C.
    */
-  @Deprecated
   public CcLinkingHelper setStaticLinkType(LinkTargetType linkType) {
     Preconditions.checkNotNull(linkType);
     Preconditions.checkState(linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER);
@@ -442,12 +449,8 @@
           (!dynamicLinkType.isExecutable() && usePicForDynamicLibs)
               || (dynamicLinkType.isExecutable() && usePicForBinaries);
       hasBuiltDynamicLibrary =
-          createDynamicLibrary(
-              ccLinkingOutputsBuilder,
-              libraryToLinkBuilder,
-              usePic,
-              libraryIdentifier,
-              ccOutputs);
+          createDynamicLinkAction(
+              ccLinkingOutputsBuilder, libraryToLinkBuilder, usePic, libraryIdentifier, ccOutputs);
     }
 
     if (hasBuiltDynamicLibrary || shouldCreateStaticLibraries) {
@@ -621,7 +624,7 @@
     return action;
   }
 
-  private boolean createDynamicLibrary(
+  private boolean createDynamicLinkAction(
       CcLinkingOutputs.Builder ccLinkingOutputs,
       LibraryToLink.Builder libraryToLinkBuilder,
       boolean usePic,
@@ -630,21 +633,21 @@
       throws RuleErrorException, InterruptedException {
     boolean hasBuiltDynamicLibrary = false;
     // Create dynamic library.
-    Artifact soImpl;
+    Artifact linkerOutput;
     String mainLibraryIdentifier;
     if (linkerOutputArtifact == null) {
       // If the crosstool is configured to select an output artifact, we use that selection.
       // Otherwise, we use linux defaults.
-      soImpl = getLinkedArtifact(LinkTargetType.NODEPS_DYNAMIC_LIBRARY);
+      linkerOutput = getLinkedArtifact(dynamicLinkType);
       mainLibraryIdentifier = libraryIdentifier;
     } else {
       // This branch is only used for vestigial Google-internal rules where the name of the output
       // file is explicitly specified in the BUILD file and as such, is platform-dependent. Thus,
       // we just hardcode some reasonable logic to compute the library identifier and hope that this
       // will eventually go away.
-      soImpl = linkerOutputArtifact;
+      linkerOutput = linkerOutputArtifact;
       mainLibraryIdentifier =
-          FileSystemUtils.removeExtension(soImpl.getRootRelativePath().getPathString());
+          FileSystemUtils.removeExtension(linkerOutput.getRootRelativePath().getPathString());
     }
 
     List<String> sonameLinkopts = ImmutableList.of();
@@ -658,12 +661,12 @@
             ImmutableList.of(
                 "-Wl,-soname="
                     + SolibSymlinkAction.getDynamicLibrarySoname(
-                        soImpl.getRootRelativePath(), /* preserveName= */ false));
+                        linkerOutput.getRootRelativePath(), /* preserveName= */ false));
       }
     }
 
     CppLinkActionBuilder dynamicLinkActionBuilder =
-        newLinkActionBuilder(soImpl)
+        newLinkActionBuilder(linkerOutput)
             .setWholeArchive(wholeArchive)
             .setNativeDeps(nativeDeps)
             .setAdditionalLinkstampDefines(additionalLinkstampDefines.build())
@@ -746,6 +749,9 @@
       ccLinkingOutputs.addAllLtoArtifacts(dynamicLinkActionBuilder.getAllLtoBackendArtifacts());
     }
     CppLinkAction dynamicLinkAction = dynamicLinkActionBuilder.build();
+    if (dynamicLinkType.isExecutable()) {
+      ccLinkingOutputs.setExecutable(linkerOutput);
+    }
     ccLinkingOutputs.addLinkActionInputs(dynamicLinkAction.getInputs());
     actionConstructionContext.registerAction(dynamicLinkAction);
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java
index 430aa7b..4629a0f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java
@@ -28,15 +28,18 @@
   public static final CcLinkingOutputs EMPTY = builder().build();
 
   @Nullable private final LibraryToLink libraryToLink;
+  @Nullable private final Artifact executable;
 
   private final ImmutableList<LtoBackendArtifacts> allLtoArtifacts;
   private final ImmutableList<Artifact> linkActionInputs;
 
   private CcLinkingOutputs(
       LibraryToLink libraryToLink,
+      Artifact executable,
       ImmutableList<LtoBackendArtifacts> allLtoArtifacts,
       ImmutableList<Artifact> linkActionInputs) {
     this.libraryToLink = libraryToLink;
+    this.executable = executable;
     this.allLtoArtifacts = allLtoArtifacts;
     this.linkActionInputs = linkActionInputs;
   }
@@ -47,6 +50,12 @@
     return libraryToLink;
   }
 
+  @Override
+  @Nullable
+  public Artifact getExecutable() {
+    return executable;
+  }
+
   public ImmutableList<LtoBackendArtifacts> getAllLtoArtifacts() {
     return allLtoArtifacts;
   }
@@ -95,9 +104,10 @@
     return new Builder();
   }
 
-  /** Builder for {@link CcLinkingOutputs. */
+  /** Builder for {@link CcLinkingOutputs} */
   public static final class Builder {
     private LibraryToLink libraryToLink;
+    private Artifact executable;
 
     private Builder() {
       // private to avoid class initialization deadlock between this class and its outer class
@@ -110,7 +120,8 @@
     private final ImmutableList.Builder<Artifact> linkActionInputs = ImmutableList.builder();
 
     public CcLinkingOutputs build() {
-      return new CcLinkingOutputs(libraryToLink, allLtoArtifacts.build(), linkActionInputs.build());
+      return new CcLinkingOutputs(
+          libraryToLink, executable, allLtoArtifacts.build(), linkActionInputs.build());
     }
 
     public Builder setLibraryToLink(LibraryToLink libraryToLink) {
@@ -118,6 +129,11 @@
       return this;
     }
 
+    public Builder setExecutable(Artifact executable) {
+      this.executable = executable;
+      return this;
+    }
+
     public Builder addAllLtoArtifacts(Iterable<LtoBackendArtifacts> allLtoArtifacts) {
       this.allLtoArtifacts.addAll(allLtoArtifacts);
       return this;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LibraryToLink.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LibraryToLink.java
index f0cac21..73315f1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LibraryToLink.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LibraryToLink.java
@@ -61,7 +61,7 @@
   }
 
   /** Structure of CcLinkingContext. */
-  public static class CcLinkingContext implements CcLinkingContextApi {
+  public static class CcLinkingContext implements CcLinkingContextApi<Artifact, LibraryToLink> {
     public static final CcLinkingContext EMPTY = builder().build();
 
     /** A list of link options contributed by a single configured target/aspect. */
@@ -289,7 +289,7 @@
     }
 
     @Override
-    public SkylarkList<LibraryToLinkApi> getSkylarkLibrariesToLink() {
+    public SkylarkList<LibraryToLink> getSkylarkLibrariesToLink() {
       return SkylarkList.createImmutable(libraries.toList());
     }
 
@@ -443,6 +443,7 @@
   public abstract Artifact getDynamicLibrary();
 
   @Nullable
+  @Override
   public abstract Artifact getResolvedSymlinkDynamicLibrary();
 
   @Nullable
@@ -450,6 +451,7 @@
   public abstract Artifact getInterfaceLibrary();
 
   @Nullable
+  @Override
   public abstract Artifact getResolvedSymlinkInterfaceLibrary();
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/BazelCcModuleApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/BazelCcModuleApi.java
index b01da22..fbc05dc 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/BazelCcModuleApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/BazelCcModuleApi.java
@@ -14,18 +14,18 @@
 
 package com.google.devtools.build.lib.skylarkbuildapi.cpp;
 
+import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkActionFactoryApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleContextApi;
 import com.google.devtools.build.lib.skylarkinterface.Param;
-import com.google.devtools.build.lib.skylarkinterface.ParamType;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
+import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.Runtime.NoneType;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
-import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
 
 /** Utilites related to C++ support. */
 @SkylarkModule(
@@ -40,8 +40,8 @@
         CompilationContextT extends CcCompilationContextApi,
         CompilationOutputsT extends CcCompilationOutputsApi<FileT>,
         LinkingOutputsT extends CcLinkingOutputsApi<FileT>,
-        LinkingContextT extends CcLinkingContextApi,
-        LibraryToLinkT extends LibraryToLinkApi,
+        LibraryToLinkT extends LibraryToLinkApi<FileT>,
+        LinkingContextT extends CcLinkingContextApi<FileT, LibraryToLinkT>,
         CcToolchainVariablesT extends CcToolchainVariablesApi,
         CcToolchainConfigInfoT extends CcToolchainConfigInfoApi>
     extends CcModuleApi<
@@ -57,7 +57,9 @@
 
   @SkylarkCallable(
       name = "compile",
-      documented = false,
+      doc = "Should be used for C++ compilation.",
+      useEnvironment = true,
+      useLocation = true,
       parameters = {
         @Param(
             name = "actions",
@@ -137,6 +139,13 @@
             defaultValue = "[]",
             type = SkylarkList.class),
         @Param(
+            name = "defines",
+            doc = "Set of defines needed to compile this target. Each define is a string.",
+            positional = false,
+            named = true,
+            defaultValue = "[]",
+            type = SkylarkList.class),
+        @Param(
             name = "user_compile_flags",
             doc = "Additional list of compilation options.",
             positional = false,
@@ -184,16 +193,22 @@
       SkylarkList<String> includes,
       SkylarkList<String> quoteIncludes,
       SkylarkList<String> systemIncludes,
+      SkylarkList<String> defines,
       SkylarkList<String> userCompileFlags,
       SkylarkList<CompilationContextT> ccCompilationContexts,
       String name,
       boolean disallowPicOutputs,
-      boolean disallowNopicOutputs)
+      boolean disallowNopicOutputs,
+      Location location,
+      Environment environment)
       throws EvalException, InterruptedException;
 
   @SkylarkCallable(
       name = "link",
-      documented = false,
+      doc = "Should be used for C++ transitive linking.",
+      useEnvironment = true,
+      useLocation = true,
+      useContext = true,
       parameters = {
         @Param(
             name = "actions",
@@ -220,11 +235,7 @@
             named = true,
             defaultValue = "None",
             noneable = true,
-            allowedTypes = {
-              @ParamType(type = NoneType.class),
-              @ParamType(type = SkylarkList.class),
-              @ParamType(type = SkylarkNestedSet.class)
-            }),
+            type = CcCompilationOutputsApi.class),
         @Param(
             name = "user_link_flags",
             doc = "Additional list of linker options.",
@@ -236,8 +247,8 @@
         @Param(
             name = "linking_contexts",
             doc =
-                "Libraries from dependencies. These libraries will be linked into the output "
-                    + "artifact of the link() call, be it a binary or a library.",
+                "Linking contexts from dependencies to be linked into the linking context "
+                    + "generated by this rule.",
             positional = false,
             named = true,
             noneable = true,
@@ -253,15 +264,15 @@
             type = String.class),
         @Param(
             name = "language",
-            doc = "Can be one of C++, objc or objc++.",
+            doc = "Can be one of c++, objc or objc++.",
             positional = false,
             named = true,
             noneable = true,
-            defaultValue = "'C++'",
+            defaultValue = "'c++'",
             type = String.class),
         @Param(
             name = "output_type",
-            doc = "Can be either 'executable' or 'shared_library'.",
+            doc = "Can be either 'executable' or 'dynamic_library'.",
             positional = false,
             named = true,
             noneable = true,
@@ -276,7 +287,7 @@
             defaultValue = "True",
             type = Boolean.class),
         @Param(
-            name = "non_code_inputs",
+            name = "additional_inputs",
             doc = "For additional inputs to the linking action, e.g.: linking scripts.",
             positional = false,
             named = true,
@@ -287,19 +298,28 @@
       SkylarkActionFactoryT skylarkActionFactoryApi,
       FeatureConfigurationT skylarkFeatureConfiguration,
       CcToolchainProviderT skylarkCcToolchainProvider,
-      Object compilationOutputs,
+      CompilationOutputsT compilationOutputs,
       SkylarkList<String> userLinkFlags,
       SkylarkList<LinkingContextT> linkingContexts,
       String name,
       String language,
       String outputType,
       boolean linkDepsStatically,
-      SkylarkList<FileT> nonCodeInputs)
+      SkylarkList<FileT> additionalInputs,
+      Location location,
+      Environment environment,
+      StarlarkContext starlarkContext)
       throws InterruptedException, EvalException;
 
   @SkylarkCallable(
       name = "create_linking_context_from_compilation_outputs",
-      documented = false,
+      doc =
+          "Should be used for creating library rules that can propagate information downstream in"
+              + " order to be linked later by a top level rule that does transitive linking to"
+              + " create an executable or dynamic library.",
+      useLocation = true,
+      useEnvironment = true,
+      useContext = true,
       parameters = {
         @Param(
             name = "actions",
@@ -334,6 +354,16 @@
             noneable = true,
             type = SkylarkList.class),
         @Param(
+            name = "linking_contexts",
+            doc =
+                "Libraries from dependencies. These libraries will be linked into the output "
+                    + "artifact of the link() call, be it a binary or a library.",
+            positional = false,
+            named = true,
+            noneable = true,
+            defaultValue = "[]",
+            type = SkylarkList.class),
+        @Param(
             name = "name",
             doc =
                 "This is used for naming the output artifacts of actions created by this "
@@ -343,11 +373,11 @@
             type = String.class),
         @Param(
             name = "language",
-            doc = "Can be one of C++, objc or objc++.",
+            doc = "Can be one of c++, objc or objc++.",
             positional = false,
             named = true,
             noneable = true,
-            defaultValue = "'C++'",
+            defaultValue = "'c++'",
             type = String.class),
         @Param(
             name = "alwayslink",
@@ -358,7 +388,7 @@
             defaultValue = "False",
             type = Boolean.class),
         @Param(
-            name = "non_code_inputs",
+            name = "additional_inputs",
             doc = "For additional inputs to the linking action, e.g.: linking scripts.",
             positional = false,
             named = true,
@@ -372,24 +402,28 @@
             defaultValue = "False",
             type = Boolean.class),
         @Param(
-            name = "disallow_dynamic_libraries",
-            doc = "Whether dynamic libraries should be created.",
+            name = "disallow_dynamic_library",
+            doc = "Whether a dynamic library should be created.",
             positional = false,
             named = true,
             defaultValue = "False",
             type = Boolean.class)
       })
-  LinkingContextT createLinkingContextFromCompilationOutputs(
+  Tuple<Object> createLinkingContextFromCompilationOutputs(
       SkylarkActionFactoryT skylarkActionFactoryApi,
       FeatureConfigurationT skylarkFeatureConfiguration,
       CcToolchainProviderT skylarkCcToolchainProvider,
       CompilationOutputsT compilationOutputs,
       SkylarkList<String> userLinkFlags,
+      SkylarkList<LinkingContextT> linkingContexts,
       String name,
       String language,
       boolean alwayslink,
-      SkylarkList<FileT> nonCodeInputs,
+      SkylarkList<FileT> additionalInputs,
       boolean disallowStaticLibraries,
-      boolean disallowDynamicLibraries)
+      boolean disallowDynamicLibraries,
+      Location location,
+      Environment environment,
+      StarlarkContext bazelStarlarkContext)
       throws InterruptedException, EvalException;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcBootstrap.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcBootstrap.java
index 88c36c9..023de9c 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcBootstrap.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcBootstrap.java
@@ -33,8 +33,9 @@
           ? extends CcCompilationContextApi,
           ? extends CcCompilationOutputsApi<? extends FileApi>,
           ? extends CcLinkingOutputsApi<? extends FileApi>,
-          ? extends CcLinkingContextApi,
-          ? extends LibraryToLinkApi,
+          ? extends LibraryToLinkApi<? extends FileApi>,
+          ? extends
+              CcLinkingContextApi<? extends FileApi, ? extends LibraryToLinkApi<? extends FileApi>>,
           ? extends CcToolchainVariablesApi,
           ? extends CcToolchainConfigInfoApi>
       ccModule;
@@ -49,8 +50,10 @@
               ? extends CcCompilationContextApi,
               ? extends CcCompilationOutputsApi<? extends FileApi>,
               ? extends CcLinkingOutputsApi<? extends FileApi>,
-              ? extends CcLinkingContextApi,
-              ? extends LibraryToLinkApi,
+              ? extends LibraryToLinkApi<? extends FileApi>,
+              ? extends
+                  CcLinkingContextApi<
+                      ? extends FileApi, ? extends LibraryToLinkApi<? extends FileApi>>,
               ? extends CcToolchainVariablesApi,
               ? extends CcToolchainConfigInfoApi>
           ccModule) {
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcCompilationOutputsApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcCompilationOutputsApi.java
index eaa22b6..82d064b 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcCompilationOutputsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcCompilationOutputsApi.java
@@ -14,11 +14,14 @@
 
 package com.google.devtools.build.lib.skylarkbuildapi.cpp;
 
+import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
 import com.google.devtools.build.lib.skylarkinterface.Param;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 
 /** Interface for a structured representation of the compilation outputs of a C++ rule. */
@@ -28,11 +31,35 @@
     documented = false,
     doc = "Helper class containing CC compilation outputs.")
 public interface CcCompilationOutputsApi<FileT extends FileApi> {
+
+  /** @deprecated use {@link #getSkylarkObjects} or {@link #getSkylarkPicObjects}. */
   @SkylarkCallable(
       name = "object_files",
       documented = false,
+      useEnvironment = true,
+      useLocation = true,
       parameters = {
         @Param(name = "use_pic", doc = "use_pic", positional = false, named = true),
       })
-  SkylarkList<FileT> getSkylarkObjectFiles(boolean usePic);
+  @Deprecated
+  SkylarkList<FileT> getSkylarkObjectFiles(
+      boolean usePic, Location location, Environment environment) throws EvalException;
+
+  @SkylarkCallable(
+      name = "objects",
+      documented = false,
+      useEnvironment = true,
+      useLocation = true,
+      structField = true)
+  SkylarkList<FileT> getSkylarkObjects(Location location, Environment environment)
+      throws EvalException;
+
+  @SkylarkCallable(
+      name = "pic_objects",
+      documented = false,
+      useEnvironment = true,
+      useLocation = true,
+      structField = true)
+  SkylarkList<FileT> getSkylarkPicObjects(Location location, Environment environment)
+      throws EvalException;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcLinkingOutputsApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcLinkingOutputsApi.java
index d0c61dc..f44f6e5 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcLinkingOutputsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcLinkingOutputsApi.java
@@ -28,4 +28,7 @@
 public interface CcLinkingOutputsApi<FileT extends FileApi> {
   @SkylarkCallable(name = "library_to_link", structField = true, documented = false)
   LibraryToLinkApi<FileT> getLibraryToLink();
+
+  @SkylarkCallable(name = "executable", structField = true, documented = false)
+  FileT getExecutable();
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LibraryToLinkApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LibraryToLinkApi.java
index 9e365ef..14585cc 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LibraryToLinkApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LibraryToLinkApi.java
@@ -52,6 +52,15 @@
   FileT getDynamicLibrary();
 
   @SkylarkCallable(
+      name = "resolved_symlink_dynamic_library",
+      doc =
+          "The resolved <code>Artifact</code> of the dynamic library to be linked if "
+              + "<code>dynamic_library</code> is a symlink, otherwise this is None.",
+      allowReturnNones = true,
+      structField = true)
+  FileT getResolvedSymlinkDynamicLibrary();
+
+  @SkylarkCallable(
       name = "interface_library",
       doc = "<code>Artifact</code> of interface library to be linked.",
       allowReturnNones = true,
@@ -59,6 +68,15 @@
   FileT getInterfaceLibrary();
 
   @SkylarkCallable(
+      name = "resolved_symlink_interface_library",
+      doc =
+          "The resolved <code>Artifact</code> of the interface library to be linked if "
+              + "<code>interface_library</code> is a symlink, otherwise this is None.",
+      allowReturnNones = true,
+      structField = true)
+  FileT getResolvedSymlinkInterfaceLibrary();
+
+  @SkylarkCallable(
       name = "alwayslink",
       doc = "Whether to link the static library/objects in the --whole_archive block.",
       allowReturnNones = true,
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeCcModule.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeCcModule.java
index 4e67320..db89916 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeCcModule.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/FakeCcModule.java
@@ -50,8 +50,8 @@
         CcCompilationContextApi,
         CcCompilationOutputsApi<FileApi>,
         CcLinkingOutputsApi<FileApi>,
-        CcLinkingContextApi,
-        LibraryToLinkApi,
+        LibraryToLinkApi<FileApi>,
+        CcLinkingContextApi<FileApi, LibraryToLinkApi<FileApi>>,
         CcToolchainVariablesApi,
         CcToolchainConfigInfoApi> {
 
@@ -179,29 +179,36 @@
       SkylarkList<FileApi> privateHeaders,
       SkylarkList<String> includes,
       SkylarkList<String> quoteIncludes,
+      SkylarkList<String> defines,
       SkylarkList<String> systemIncludes,
       SkylarkList<String> userCompileFlags,
       SkylarkList<CcCompilationContextApi> ccCompilationContexts,
       String name,
       boolean disallowPicOutputs,
-      boolean disallowNopicOutputs)
+      boolean disallowNopicOutputs,
+      Location location,
+      Environment environment)
       throws EvalException, InterruptedException {
     return null;
   }
 
   @Override
-  public CcLinkingContextApi createLinkingContextFromCompilationOutputs(
+  public Tuple<Object> createLinkingContextFromCompilationOutputs(
       SkylarkActionFactoryApi skylarkActionFactoryApi,
       FeatureConfigurationApi skylarkFeatureConfiguration,
       CcToolchainProviderApi<FeatureConfigurationApi> skylarkCcToolchainProvider,
       CcCompilationOutputsApi<FileApi> compilationOutputs,
       SkylarkList<String> userLinkFlags,
+      SkylarkList<CcLinkingContextApi<FileApi, LibraryToLinkApi<FileApi>>> ccLinkingContextApis,
       String name,
       String language,
       boolean alwayslink,
       SkylarkList<FileApi> nonCodeInputs,
       boolean disallowStaticLibraries,
-      boolean disallowDynamicLibraries)
+      boolean disallowDynamicLibraries,
+      Location location,
+      Environment environment,
+      StarlarkContext starlarkContext)
       throws InterruptedException, EvalException {
     return null;
   }
@@ -211,14 +218,17 @@
       SkylarkActionFactoryApi skylarkActionFactoryApi,
       FeatureConfigurationApi skylarkFeatureConfiguration,
       CcToolchainProviderApi<FeatureConfigurationApi> skylarkCcToolchainProvider,
-      Object compilationOutputs,
+      CcCompilationOutputsApi<FileApi> compilationOutputs,
       SkylarkList<String> userLinkFlags,
-      SkylarkList<CcLinkingContextApi> linkingContexts,
+      SkylarkList<CcLinkingContextApi<FileApi, LibraryToLinkApi<FileApi>>> linkingContexts,
       String name,
       String language,
       String outputType,
       boolean linkDepsStatically,
-      SkylarkList<FileApi> nonCodeInputs)
+      SkylarkList<FileApi> additionalInputs,
+      Location location,
+      Environment environment,
+      StarlarkContext starlarkContext)
       throws InterruptedException, EvalException {
     return null;
   }
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 1cf5c94..2ece60f 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -38,6 +38,7 @@
     name = "test-deps-wo-bazel",
     testonly = 1,
     srcs = [
+        "cc_api_rules.bzl",
         "remote_helpers.sh",
         "testing_server.py",
         ":langtools-copy",
diff --git a/src/test/shell/bazel/cc_api_rules.bzl b/src/test/shell/bazel/cc_api_rules.bzl
new file mode 100644
index 0000000..201b0a7
--- /dev/null
+++ b/src/test/shell/bazel/cc_api_rules.bzl
@@ -0,0 +1,181 @@
+# 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.
+
+"""Example C++ API usage"""
+
+load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
+
+def _filter_none(input_list):
+    filtered_list = []
+    for element in input_list:
+        if element != None:
+            filtered_list.append(element)
+    return filtered_list
+
+def _cc_lib_impl(ctx):
+    cc_toolchain = find_cpp_toolchain(ctx)
+    feature_configuration = cc_common.configure_features(
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    compilation_contexts = []
+    linking_contexts = []
+    for dep in ctx.attr.deps:
+        if CcInfo in dep:
+            compilation_contexts.append(dep[CcInfo].compilation_context)
+            linking_contexts.append(dep[CcInfo].linking_context)
+
+    (compilation_context, compilation_outputs) = cc_common.compile(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        public_hdrs = ctx.files.public_hdrs,
+        private_hdrs = ctx.files.private_hdrs,
+        srcs = ctx.files.srcs,
+        includes = ctx.attr.includes,
+        quote_includes = ctx.attr.quote_includes,
+        system_includes = ctx.attr.system_includes,
+        defines = ctx.attr.defines,
+        user_compile_flags = ctx.attr.user_compile_flags,
+        compilation_contexts = compilation_contexts,
+    )
+    (linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        language = "c++",
+        compilation_outputs = compilation_outputs,
+        linking_contexts = linking_contexts,
+    )
+    library = linking_outputs.library_to_link
+    files = []
+    files.extend(compilation_outputs.objects)
+    files.extend(compilation_outputs.pic_objects)
+    files.append(library.pic_static_library)
+    files.append(library.static_library)
+
+    files.append(library.dynamic_library)
+
+    return [
+        DefaultInfo(
+            files = depset(_filter_none(files)),
+        ),
+        CcInfo(
+            compilation_context = compilation_context,
+            linking_context = linking_context,
+        ),
+    ]
+
+cc_lib = rule(
+    implementation = _cc_lib_impl,
+    attrs = {
+        "public_hdrs": attr.label_list(allow_files = [".h"]),
+        "private_hdrs": attr.label_list(allow_files = [".h"]),
+        "srcs": attr.label_list(allow_files = [".cc"]),
+        "deps": attr.label_list(
+            allow_empty = True,
+            providers = [[CcInfo]],
+        ),
+        "user_compile_flags": attr.string_list(),
+        "user_link_flags": attr.string_list(),
+        "includes": attr.string_list(),
+        "quote_includes": attr.string_list(),
+        "system_includes": attr.string_list(),
+        "defines": attr.string_list(),
+        "alwayslink": attr.bool(default = False),
+        "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
+    },
+    fragments = ["cpp"],
+)
+
+def _cc_bin_impl(ctx):
+    cc_toolchain = find_cpp_toolchain(ctx)
+    feature_configuration = cc_common.configure_features(
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    compilation_contexts = []
+    linking_contexts = []
+    for dep in ctx.attr.deps:
+        if CcInfo in dep:
+            compilation_contexts.append(dep[CcInfo].compilation_context)
+            linking_contexts.append(dep[CcInfo].linking_context)
+
+    (_compilation_context, compilation_outputs) = cc_common.compile(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        srcs = ctx.files.srcs,
+        compilation_contexts = compilation_contexts,
+    )
+    output_type = "dynamic_library" if ctx.attr.linkshared else "executable"
+    user_link_flags = []
+    for user_link_flag in ctx.attr.user_link_flags:
+        user_link_flags.append(ctx.expand_location(user_link_flag, targets = ctx.attr.additional_linker_inputs))
+
+    linking_outputs = cc_common.link(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        language = "c++",
+        compilation_outputs = compilation_outputs,
+        linking_contexts = linking_contexts,
+        user_link_flags = user_link_flags,
+        link_deps_statically = ctx.attr.linkstatic,
+        additional_inputs = ctx.files.additional_linker_inputs,
+        output_type = output_type,
+    )
+    files = []
+    if output_type == "executable":
+        files.append(linking_outputs.executable)
+    elif output_type == "dynamic_library":
+        files.append(linking_outputs.library_to_link.dynamic_library)
+        files.append(linking_outputs.library_to_link.resolved_symlink_dynamic_library)
+
+    return [
+        DefaultInfo(
+            files = depset(_filter_none(files)),
+            runfiles = ctx.runfiles(files = ctx.files.data),
+        ),
+    ]
+
+cc_bin = rule(
+    implementation = _cc_bin_impl,
+    attrs = {
+        "srcs": attr.label_list(allow_files = [".cc"]),
+        "additional_linker_inputs": attr.label_list(
+            allow_empty = True,
+            allow_files = [".lds"],
+        ),
+        "deps": attr.label_list(
+            allow_empty = True,
+            providers = [CcInfo],
+        ),
+        "data": attr.label_list(
+            default = [],
+            allow_files = True,
+        ),
+        "user_link_flags": attr.string_list(),
+        "linkstatic": attr.bool(default = True),
+        "linkshared": attr.bool(default = False),
+        "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
+    },
+    fragments = ["cpp"],
+)
diff --git a/src/test/shell/bazel/cc_integration_test.sh b/src/test/shell/bazel/cc_integration_test.sh
index c41a8da..5b21c80 100755
--- a/src/test/shell/bazel/cc_integration_test.sh
+++ b/src/test/shell/bazel/cc_integration_test.sh
@@ -277,4 +277,329 @@
   grep "test_feature" bazel-bin/ea/feature_debug/cc/requested_features.txt || "test_feature should have been found in  requested_features."
 }
 
-run_suite "cc_integration_test"
+# TODO: test include dirs and defines
+function setup_cc_starlark_api_test() {
+  local pkg="$1"
+
+  touch "$pkg"/WORKSPACE
+
+  mkdir "$pkg"/include_dir
+  touch "$pkg"/include_dir/include.h
+  mkdir "$pkg"/system_include_dir
+  touch "$pkg"/system_include_dir/system_include.h
+  mkdir "$pkg"/quote_include_dir
+  touch "$pkg"/quote_include_dir/quote_include.h
+
+
+
+  cat > "$pkg"/BUILD << EOF
+load("//${pkg}:cc_api_rules.bzl", "cc_lib", "cc_bin")
+
+cc_lib(
+    name = "a",
+    srcs = [
+        "a1.cc",
+        "a2.cc",
+    ],
+    private_hdrs = [
+      "a2.h",
+      "include_dir/include.h",
+      "system_include_dir/system_include.h",
+      "quote_include_dir/quote_include.h"
+    ],
+    user_compile_flags = ["-DA_DEFINITION_LOCAL"],
+    public_hdrs = ["a.h"],
+    includes = ["$pkg/include_dir"],
+    system_includes = ["$pkg/system_include_dir"],
+    quote_includes = ["$pkg/quote_include_dir"],
+    defines = ["A_DEFINITION"],
+    deps = [
+        ":b",
+        ":d",
+    ],
+)
+
+cc_lib(
+    name = "b",
+    srcs = [
+        "b.cc",
+    ],
+    public_hdrs = ["b.h"],
+    deps = [":c"],
+)
+
+cc_lib(
+    name = "c",
+    srcs = [
+        "c.cc",
+    ],
+    public_hdrs = ["c.h"],
+)
+
+cc_lib(
+    name = "d",
+    srcs = ["d.cc"],
+    public_hdrs = ["d.h"],
+)
+
+cc_bin(
+    name = "e",
+    srcs = ["e.cc"],
+    data = [":f"],
+    linkstatic = 1,
+    user_link_flags = [
+        "-ldl",
+        "-lm",
+        "-Wl,-rpath,bazel-bin/${pkg}",
+    ],
+    deps = [
+        ":a",
+    ],
+)
+
+cc_bin(
+    name = "f",
+    srcs = ["f.cc"],
+    linkshared = 1,
+    deps = [
+        ":a",
+    ],
+)
+EOF
+
+  cat > $pkg/a1.cc << EOF
+#include <system_include.h>
+#include "include.h"
+
+#include "quote_include.h"
+#include "a.h"
+#include "a2.h"
+
+#ifdef A_DEFINITION_LOCAL
+#include "b.h"
+#include "d.h"
+#endif
+
+using namespace std;
+
+string alongernamethanusual() { return "a1" + a2() + b() + d(); }
+EOF
+
+  cat > $pkg/a2.cc << EOF
+#include <string>
+using namespace std;
+
+string a2() { return "a2"; }
+EOF
+
+  cat > $pkg/a.h << EOF
+#ifndef HEADER_A
+#define HEADER_A
+#include <string>
+using namespace std;
+string alongernamethanusual();
+#endif
+EOF
+
+  cat > $pkg/a2.h << EOF
+#ifndef HEADER_A2
+#define HEADER_A2
+#include <string>
+using namespace std;
+string a2();
+#endif
+EOF
+
+  cat > $pkg/b.cc << EOF
+#include "b.h"
+#include <string>
+#include "c.h"
+using namespace std;
+
+string b() { return "b" + c(); }
+EOF
+
+  cat > $pkg/b.h << EOF
+#ifndef HEADER_B
+#define HEADER_B
+#include <string>
+using namespace std;
+string b();
+#endif
+EOF
+
+  cat > $pkg/c.cc << EOF
+#include "c.h"
+#include <algorithm>
+#include <string>
+
+using namespace std;
+
+string c() { return "c"; }
+EOF
+
+  cat > $pkg/c.h << EOF
+#ifndef HEADER_C
+#define HEADER_C
+#include <string>
+using namespace std;
+string c();
+#endif
+EOF
+
+  cat > $pkg/d.cc << EOF
+#include "d.h"
+#include <string>
+using namespace std;
+
+string d() { return "d"; }
+EOF
+
+  cat > $pkg/d.h << EOF
+#ifndef HEADER_D
+#define HEADER_D
+#include <string>
+using namespace std;
+string d();
+#endif
+EOF
+
+  cat > $pkg/e.cc << EOF
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+
+#ifdef A_DEFINITION
+#include "a.h"
+#endif
+
+#ifdef A_DEFINITION_LOCAL
+#include "thisdoesntexist.h"
+#endif
+
+using namespace std;
+
+int main() {
+  void* handle = dlopen("libf.so", RTLD_LAZY);
+
+  typedef string (*f_t)();
+
+  f_t f = (f_t)dlsym(handle, "f");
+  cout << alongernamethanusual() + f() << endl;
+  return 0;
+}
+
+EOF
+
+  cat > $pkg/f.cc << EOF
+#include <algorithm>
+#include <string>
+#include "a.h"
+
+using namespace std;
+
+extern "C" string f() {
+  string str = alongernamethanusual();
+  reverse(str.begin(), str.end());
+  return str;
+}
+EOF
+
+  cat > $pkg/script.lds << EOF
+VERS_42.0 {
+  local:
+    *;
+};
+EOF
+
+  cp "$CURRENT_DIR"/cc_api_rules.bzl "$pkg"/cc_api_rules.bzl
+
+}
+
+function test_cc_starlark_api_default_values() {
+  local pkg="${FUNCNAME[0]}"
+  mkdir -p "$pkg"
+
+  setup_cc_starlark_api_test "${FUNCNAME[0]}"
+
+  bazel build --experimental_cc_skylark_api_enabled_packages=, --verbose_failures \
+    //"$pkg":e  &>"$TEST_log" || fail "Build failed"
+
+  nm -u bazel-bin/"$pkg"/e  | grep alongernamethanusual && \
+    fail "alongernamethanusual is not defined"
+
+  bazel-bin/"$pkg"/e | grep a1a2bcddcb2a1a || fail "output is incorrect"
+}
+
+
+function test_cc_starlark_api_link_static_false() {
+  local pkg="${FUNCNAME[0]}"
+  mkdir -p "$pkg"
+
+  setup_cc_starlark_api_test "${FUNCNAME[0]}"
+
+  cat >> "$pkg"/BUILD << EOF
+cc_bin(
+    name = "g",
+    srcs = ["e.cc"],
+    data = [":f"],
+    linkstatic = 0,
+    user_link_flags = [
+        "-ldl",
+        "-lm",
+        "-Wl,-rpath,bazel-bin/${pkg}",
+    ],
+    deps = [
+        ":a",
+    ],
+)
+EOF
+
+  bazel build --experimental_cc_skylark_api_enabled_packages=, --verbose_failures \
+    //"$pkg":g  &>"$TEST_log" || fail "Build failed"
+
+  nm -u bazel-bin/"$pkg"/g  | grep alongernamethanusual || fail "alongernamethanusual is defined"
+
+  bazel-bin/"$pkg"/g | grep a1a2bcddcb2a1a || fail "output is incorrect"
+}
+
+function test_cc_starlark_api_additional_inputs() {
+  # This uses --version-script which isn't available on Mac linker.
+  [ "$PLATFORM" != "darwin" ] || return 0
+
+  local pkg="${FUNCNAME[0]}"
+  mkdir -p "$pkg"
+
+  setup_cc_starlark_api_test "${FUNCNAME[0]}"
+
+  cat >> "$pkg"/BUILD << EOF
+cc_bin(
+    name = "g",
+    srcs = ["e.cc"],
+    data = [":f"],
+    linkstatic = 1,
+    additional_linker_inputs = ["script.lds"],
+    user_link_flags = [
+        "-ldl",
+        "-lm",
+        "-Wl,-rpath,bazel-bin/${pkg}",
+        "-Wl,--version-script=\$(location script.lds)",
+    ],
+    deps = [
+        ":a",
+    ],
+)
+EOF
+
+  bazel build --experimental_cc_skylark_api_enabled_packages=, --verbose_failures \
+    //"$pkg":g  &>"$TEST_log" || fail "Build failed"
+
+  nm bazel-bin/"$pkg"/g  | grep VERS_42.0 || fail "VERS_42.0 not in binary"
+
+  bazel-bin/"$pkg"/g | grep a1a2bcddcb2a1a || fail "output is incorrect"
+}
+
+
+
+run_suite "cc_integration_test"
\ No newline at end of file