From 769f72eb475cea0f4113ac49dd66dc3fda0608de Mon Sep 17 00:00:00 2001 From: plf Date: Thu, 19 Jul 2018 11:37:58 -0700 Subject: [PATCH] C++: Implements Skylark cc_common.compile()/link(). Working towards #4570. RELNOTES:none PiperOrigin-RevId: 205274676 --- .../build/lib/bazel/rules/CcRules.java | 4 +- .../lib/bazel/rules/cpp/BazelCcModule.java | 107 ++++++++ .../build/lib/rules/cpp/CcModule.java | 234 ++++++++++++++++++ .../skylarkbuildapi/cpp/BazelCcModuleApi.java | 204 +++++++++++++++ .../lib/skylarkbuildapi/cpp/CcBootstrap.java | 29 ++- .../skydoc/fakebuildapi/cpp/FakeCcModule.java | 39 ++- .../google/devtools/build/lib/rules/cpp/BUILD | 16 +- .../lib/rules/cpp/SkylarkCcCommonTest.java | 111 +++++++++ .../rules/cpp/SkylarkCcCommonTestHelper.java | 123 +++++++++ 9 files changed, 861 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcModule.java create mode 100644 src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/BazelCcModuleApi.java create mode 100644 src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTestHelper.java diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java index eb8c6a9a568103..620d810f61e32b 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java @@ -19,6 +19,7 @@ import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcBinaryRule; import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcImportRule; import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcLibraryRule; +import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcModule; import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcTestRule; import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses; import com.google.devtools.build.lib.rules.core.CoreRules; @@ -27,7 +28,6 @@ import com.google.devtools.build.lib.rules.cpp.CcImportRule; import com.google.devtools.build.lib.rules.cpp.CcLibcTopAlias; import com.google.devtools.build.lib.rules.cpp.CcLinkingInfo; -import com.google.devtools.build.lib.rules.cpp.CcModule; import com.google.devtools.build.lib.rules.cpp.CcToolchainAliasRule; import com.google.devtools.build.lib.rules.cpp.CcToolchainRule; import com.google.devtools.build.lib.rules.cpp.CcToolchainSuiteRule; @@ -77,7 +77,7 @@ public void init(ConfiguredRuleClassProvider.Builder builder) { builder.addRuleDefinition(new FdoProfileRule()); builder.addRuleDefinition(new FdoPrefetchHintsRule()); - builder.addSkylarkBootstrap(new CcBootstrap(new CcModule())); + builder.addSkylarkBootstrap(new CcBootstrap(new BazelCcModule())); builder.addSkylarkAccessibleTopLevels("CcCompilationInfo", CcCompilationInfo.PROVIDER); builder.addSkylarkAccessibleTopLevels("CcLinkingInfo", CcLinkingInfo.PROVIDER); } 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 new file mode 100644 index 00000000000000..3b3d944e74ab6c --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcModule.java @@ -0,0 +1,107 @@ +// Copyright 2018 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.bazel.rules.cpp; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext; +import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo; +import com.google.devtools.build.lib.rules.cpp.CcCompilationInfo; +import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs; +import com.google.devtools.build.lib.rules.cpp.CcLinkParams; +import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo; +import com.google.devtools.build.lib.rules.cpp.CcLinkingInfo; +import com.google.devtools.build.lib.rules.cpp.CcModule; +import com.google.devtools.build.lib.rules.cpp.CcModule.CcSkylarkInfo; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; +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.LinkerInputs.LibraryToLink; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.BazelCcModuleApi; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.Runtime; +import com.google.devtools.build.lib.syntax.SkylarkList; + +/** + * A module that contains Skylark utilities for C++ support. + * + *

This is a work in progress. The API is guarded behind --experimental_enable_cc_skylark_api. + * The API is under development and unstable. + */ +public class BazelCcModule extends CcModule + implements BazelCcModuleApi< + CcToolchainProvider, + FeatureConfiguration, + CompilationInfo, + CcCompilationInfo, + CcCompilationOutputs, + LinkingInfo, + CcLinkingInfo, + CcToolchainVariables, + LibraryToLink, + CcLinkParams, + CcSkylarkInfo> { + + @Override + public CompilationInfo compile( + SkylarkRuleContext skylarkRuleContext, + FeatureConfiguration skylarkFeatureConfiguration, + CcToolchainProvider skylarkCcToolchainProvider, + SkylarkList sources, + SkylarkList headers, + Object skylarkIncludes, + Object skylarkCopts, + SkylarkList ccCompilationInfos) + throws EvalException { + return BazelCcModule.compile( + BazelCppSemantics.INSTANCE, + skylarkRuleContext, + skylarkFeatureConfiguration, + skylarkCcToolchainProvider, + sources, + headers, + skylarkIncludes, + skylarkCopts, + /* generateNoPicOutputs= */ "conditionally", + /* generatePicOutputs= */ "conditionally", + /* skylarkAdditionalCompilationInputs= */ Runtime.NONE, + /* skylarkAdditionalIncludeScanningRoots= */ Runtime.NONE, + ccCompilationInfos, + /* purpose= */ Runtime.NONE); + } + + @Override + public LinkingInfo link( + SkylarkRuleContext skylarkRuleContext, + FeatureConfiguration skylarkFeatureConfiguration, + CcToolchainProvider skylarkCcToolchainProvider, + CcCompilationOutputs ccCompilationOutputs, + Object skylarkLinkopts, + Object dynamicLibrary, + SkylarkList skylarkCcLinkingInfos, + boolean neverLink) + throws InterruptedException, EvalException { + return BazelCcModule.link( + BazelCppSemantics.INSTANCE, + skylarkRuleContext, + skylarkFeatureConfiguration, + skylarkCcToolchainProvider, + ccCompilationOutputs, + skylarkLinkopts, + /* shouldCreateStaticLibraries= */ true, + dynamicLibrary, + skylarkCcLinkingInfos, + neverLink); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java index d775a397931106..1a6911c667d211 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java @@ -14,19 +14,25 @@ package com.google.devtools.build.lib.rules.cpp; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.platform.ToolchainInfo; import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.NativeInfo; import com.google.devtools.build.lib.packages.NativeProvider; import com.google.devtools.build.lib.packages.Provider; +import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; +import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo; +import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo; import com.google.devtools.build.lib.rules.cpp.CcModule.CcSkylarkInfo; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; @@ -42,6 +48,11 @@ import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +import com.google.devtools.build.lib.util.Pair; +import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.annotation.Nullable; /** A module that contains Skylark utilities for C++ support. */ @@ -54,6 +65,41 @@ public class CcModule CcLinkParams, CcSkylarkInfo> { + private enum RegisterActions { + ALWAYS, + NEVER, + CONDITIONALLY; + + private final String skylarkName; + + RegisterActions() { + this.skylarkName = toString().toLowerCase(); + } + + public String getSkylarkName() { + return skylarkName; + } + + public static RegisterActions fromString( + String skylarkName, Location location, String fieldForError) throws EvalException { + for (RegisterActions registerActions : values()) { + if (registerActions.getSkylarkName().equals(skylarkName)) { + return registerActions; + } + } + throw new EvalException( + location, + String.format( + "Possibles values for %s: %s", + fieldForError, + Joiner.on(", ") + .join( + Arrays.stream(values()) + .map(RegisterActions::getSkylarkName) + .collect(ImmutableList.toImmutableList())))); + } + } + /** * C++ Skylark rules should have this provider so that native rules can depend on them. This will * eventually go away once b/73921130 is fixed. @@ -329,4 +375,192 @@ public CcCompilationInfo mergeCcCompilationInfos( SkylarkList ccCompilationInfos) { return CcCompilationInfo.merge(ccCompilationInfos); } + + protected static CompilationInfo compile( + CppSemantics cppSemantics, + SkylarkRuleContext skylarkRuleContext, + Object skylarkFeatureConfiguration, + Object skylarkCcToolchainProvider, + SkylarkList sources, + SkylarkList headers, + Object skylarkIncludes, + Object skylarkCopts, + String generateNoPicOutputs, + String generatePicOutputs, + Object skylarkAdditionalCompilationInputs, + Object skylarkAdditionalIncludeScanningRoots, + SkylarkList ccCompilationInfos, + Object purpose) + throws EvalException { + CcCommon.checkRuleWhitelisted(skylarkRuleContext); + RuleContext ruleContext = skylarkRuleContext.getRuleContext(); + CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null); + if (ccToolchainProvider == null) { + ccToolchainProvider = CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext); + } + FeatureConfiguration featureConfiguration = + convertFromNoneable(skylarkFeatureConfiguration, null); + if (featureConfiguration == null) { + featureConfiguration = + CcCommon.configureFeaturesOrReportRuleError(ruleContext, ccToolchainProvider); + } + Pair, List> separatedHeadersAndSources = + separateSourcesFromHeaders(sources); + FdoSupportProvider fdoSupport = + CppHelper.getFdoSupportUsingDefaultCcToolchainAttribute(ruleContext); + // TODO(plf): Need to flatten the nested set to convert the Strings to PathFragment. This could + // be avoided if path fragments are ever added to Skylark or in the C++ code we take Strings + // instead of PathFragments. + List includeDirs = convertSkylarkListOrNestedSetToList(skylarkIncludes, String.class); + CcCompilationHelper helper = + new CcCompilationHelper( + ruleContext, + cppSemantics, + featureConfiguration, + CcCompilationHelper.SourceCategory.CC, + ccToolchainProvider, + fdoSupport) + .addPublicHeaders(headers) + .addIncludeDirs( + includeDirs + .stream() + .map(PathFragment::create) + .collect(ImmutableList.toImmutableList())) + .addPrivateHeaders(separatedHeadersAndSources.first) + .addSources(separatedHeadersAndSources.second) + .addCcCompilationInfos(ccCompilationInfos) + .setPurpose(convertFromNoneable(purpose, null)); + + SkylarkNestedSet additionalCompilationInputs = + convertFromNoneable(skylarkAdditionalCompilationInputs, null); + if (additionalCompilationInputs != null) { + helper.addAdditionalCompilationInputs( + additionalCompilationInputs.toCollection(Artifact.class)); + } + + SkylarkNestedSet additionalIncludeScanningRoots = + convertFromNoneable(skylarkAdditionalIncludeScanningRoots, null); + if (additionalIncludeScanningRoots != null) { + helper.addAditionalIncludeScanningRoots( + additionalIncludeScanningRoots.toCollection(Artifact.class)); + } + + SkylarkNestedSet copts = convertFromNoneable(skylarkCopts, null); + if (copts != null) { + helper.setCopts(copts.getSet(String.class)); + } + + Location location = ruleContext.getRule().getLocation(); + RegisterActions generateNoPicOption = + RegisterActions.fromString(generateNoPicOutputs, location, "generate_no_pic_outputs"); + if (!generateNoPicOption.equals(RegisterActions.CONDITIONALLY)) { + helper.setGenerateNoPicAction(generateNoPicOption == RegisterActions.ALWAYS); + } + RegisterActions generatePicOption = + RegisterActions.fromString(generatePicOutputs, location, "generate_pic_outputs"); + if (!generatePicOption.equals(RegisterActions.CONDITIONALLY)) { + helper.setGeneratePicAction(generatePicOption == RegisterActions.ALWAYS); + } + try { + return helper.compile(); + } catch (RuleErrorException e) { + throw new EvalException(ruleContext.getRule().getLocation(), e); + } + } + + protected static LinkingInfo link( + CppSemantics cppSemantics, + SkylarkRuleContext skylarkRuleContext, + Object skylarkFeatureConfiguration, + Object skylarkCcToolchainProvider, + CcCompilationOutputs ccCompilationOutputs, + Object skylarkLinkopts, + boolean shouldCreateStaticLibraries, + Object dynamicLibrary, + SkylarkList skylarkCcLinkingInfos, + boolean neverLink) + throws InterruptedException, EvalException { + CcCommon.checkRuleWhitelisted(skylarkRuleContext); + RuleContext ruleContext = skylarkRuleContext.getRuleContext(); + CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null); + if (ccToolchainProvider == null) { + ccToolchainProvider = CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext); + } + FeatureConfiguration featureConfiguration = + convertFromNoneable(skylarkFeatureConfiguration, null); + if (featureConfiguration == null) { + featureConfiguration = + CcCommon.configureFeaturesOrReportRuleError(ruleContext, ccToolchainProvider); + } + FdoSupportProvider fdoSupport = + CppHelper.getFdoSupportUsingDefaultCcToolchainAttribute(ruleContext); + NestedSet linkopts = + convertSkylarkListOrNestedSetToNestedSet(skylarkLinkopts, String.class); + CcLinkingHelper helper = + new CcLinkingHelper( + ruleContext, + cppSemantics, + featureConfiguration, + ccToolchainProvider, + fdoSupport, + ruleContext.getConfiguration()) + .addLinkopts(linkopts) + .setShouldCreateStaticLibraries(shouldCreateStaticLibraries) + .setDynamicLibrary(convertFromNoneable(dynamicLibrary, null)) + .addCcLinkingInfos(skylarkCcLinkingInfos) + .setNeverLink(neverLink); + try { + return helper.link(ccCompilationOutputs, CcCompilationContext.EMPTY); + } catch (RuleErrorException e) { + throw new EvalException(ruleContext.getRule().getLocation(), e); + } + } + + /** + * TODO(plf): This method exists only temporarily. Once the existing C++ rules have been migrated, + * they should pass sources and headers separately. + */ + private static Pair, List> separateSourcesFromHeaders( + Iterable artifacts) { + List headers = new ArrayList<>(); + List sources = new ArrayList<>(); + for (Artifact artifact : artifacts) { + if (CppFileTypes.CPP_HEADER.matches(artifact.getExecPath())) { + headers.add(artifact); + } else { + sources.add(artifact); + } + } + return Pair.of(headers, sources); + } + + /** Converts an object that can be the either SkylarkNestedSet or None into NestedSet. */ + @SuppressWarnings("unchecked") + protected Object skylarkListToSkylarkNestedSet(Object o) throws EvalException { + if (o instanceof SkylarkList) { + SkylarkList list = (SkylarkList) o; + SkylarkNestedSet.Builder builder = + SkylarkNestedSet.builder(Order.STABLE_ORDER, Location.BUILTIN); + for (Object entry : list) { + builder.addDirect(entry); + } + return builder.build(); + } + return o; + } + + @SuppressWarnings("unchecked") + private static List convertSkylarkListOrNestedSetToList(Object o, Class type) { + return o instanceof SkylarkNestedSet + ? ((SkylarkNestedSet) o).getSet(type).toList() + : ((SkylarkList) o).getImmutableList(); + } + + @SuppressWarnings("unchecked") + private static NestedSet convertSkylarkListOrNestedSetToNestedSet( + Object o, Class type) { + return o instanceof SkylarkNestedSet + ? ((SkylarkNestedSet) o).getSet(type) + : NestedSetBuilder.wrap(Order.COMPILE_ORDER, (SkylarkList) o); + } } 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 new file mode 100644 index 00000000000000..0238e0c7ab46ec --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/BazelCcModuleApi.java @@ -0,0 +1,204 @@ +// Copyright 2018 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.skylarkbuildapi.cpp; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext; +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.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.SkylarkNestedSet; + +/** Utilites related to C++ support. */ +@SkylarkModule( + name = "cc_common", + doc = "Utilities for C++ compilation, linking, and command line generation.") +// TODO(b/111365281): Add experimental field once it's available. +public interface BazelCcModuleApi< + CcToolchainProviderT extends CcToolchainProviderApi, + FeatureConfigurationT extends FeatureConfigurationApi, + CompilationInfoT extends CompilationInfoApi, + CcCompilationInfoT extends CcCompilationInfoApi, + CcCompilationOutputsT extends CcCompilationOutputsApi, + LinkingInfoT extends LinkingInfoApi, + CcLinkingInfoT extends CcLinkingInfoApi, + CcToolchainVariablesT extends CcToolchainVariablesApi, + LibraryToLinkT extends LibraryToLinkApi, + CcLinkParamsT extends CcLinkParamsApi, + CcSkylarkInfoT extends CcSkylarkInfoApi> + extends CcModuleApi< + CcToolchainProviderT, + FeatureConfigurationT, + CcToolchainVariablesT, + LibraryToLinkT, + CcLinkParamsT, + CcSkylarkInfoT> { + + @SkylarkCallable( + name = "compile", + documented = false, + parameters = { + @Param( + name = "ctx", + positional = false, + named = true, + type = SkylarkRuleContextApi.class, + doc = "The rule context."), + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "cc_toolchain", + doc = "C++ toolchain provider to be used.", + positional = false, + named = true, + type = CcToolchainProviderApi.class), + @Param( + name = "srcs", + doc = "The list of source files to be compiled, see cc_library.srcs", + positional = false, + named = true, + defaultValue = "[]", + type = SkylarkList.class), + @Param( + name = "hdrs", + doc = "The list of public headers to be provided to dependents, see cc_library.hdrs", + positional = false, + named = true, + defaultValue = "[]", + type = SkylarkList.class), + @Param( + name = "includes", + doc = "Include directories", + positional = false, + named = true, + noneable = true, + defaultValue = "[]", + allowedTypes = { + @ParamType(type = SkylarkNestedSet.class), + @ParamType(type = SkylarkList.class) + }), + @Param( + name = "copts", + doc = "Additional list of compiler options.", + positional = false, + named = true, + noneable = true, + defaultValue = "None", + allowedTypes = { + @ParamType(type = SkylarkNestedSet.class), + @ParamType(type = NoneType.class) + }), + @Param( + name = "cc_compilation_infos", + doc = "cc_compilation_info instances affecting compilation, e.g. from dependencies", + positional = false, + named = true, + defaultValue = "[]", + type = SkylarkList.class) + }) + CompilationInfoT compile( + SkylarkRuleContext skylarkRuleContext, + FeatureConfigurationT skylarkFeatureConfiguration, + CcToolchainProviderT skylarkCcToolchainProvider, + SkylarkList sources, + SkylarkList headers, + Object skylarkIncludes, + Object skylarkCopts, + SkylarkList ccCompilationInfos) + throws EvalException; + + @SkylarkCallable( + name = "link", + documented = false, + parameters = { + @Param( + name = "ctx", + positional = false, + named = true, + type = SkylarkRuleContextApi.class, + doc = "The rule context."), + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "cc_toolchain", + doc = "C++ toolchain provider to be used.", + positional = false, + named = true, + type = CcToolchainProviderApi.class), + @Param( + name = "cc_compilation_outputs", + doc = "List of object files to be linked.", + positional = false, + named = true, + defaultValue = "[]", + type = CcCompilationOutputsApi.class), + @Param( + name = "linkopts", + doc = "Additional list of linker options.", + positional = false, + named = true, + defaultValue = "[]", + noneable = true, + allowedTypes = { + @ParamType(type = SkylarkList.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "dynamic_library", + doc = "Dynamic library artifact.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = {@ParamType(type = NoneType.class), @ParamType(type = Artifact.class)}), + @Param( + name = "cc_linking_infos", + doc = "cc_linking_info instances affecting linking, e.g. from dependencies", + positional = false, + named = true, + noneable = true, + defaultValue = "[]", + type = SkylarkList.class), + @Param( + name = "neverlink", + doc = "True if this should never be linked against other libraries.", + positional = false, + named = true, + defaultValue = "False"), + }) + LinkingInfoT link( + SkylarkRuleContext skylarkRuleContext, + FeatureConfigurationT skylarkFeatureConfiguration, + CcToolchainProviderT skylarkCcToolchainProvider, + CcCompilationOutputsT ccCompilationOutputs, + Object skylarkLinkopts, + Object dynamicLibrary, + SkylarkList skylarkCcLinkingInfos, + boolean neverLink) + 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 4286d3dbe21bd4..02be5be14fbcad 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 @@ -22,9 +22,34 @@ */ public class CcBootstrap implements Bootstrap { - private final CcModuleApi ccModule; + private final BazelCcModuleApi< + ? extends CcToolchainProviderApi, + ? extends FeatureConfigurationApi, + ? extends CompilationInfoApi, + ? extends CcCompilationInfoApi, + ? extends CcCompilationOutputsApi, + ? extends LinkingInfoApi, + ? extends CcLinkingInfoApi, + ? extends CcToolchainVariablesApi, + ? extends LibraryToLinkApi, + ? extends CcLinkParamsApi, + ? extends CcSkylarkInfoApi> + ccModule; - public CcBootstrap(CcModuleApi ccModule) { + public CcBootstrap( + BazelCcModuleApi< + ? extends CcToolchainProviderApi, + ? extends FeatureConfigurationApi, + ? extends CompilationInfoApi, + ? extends CcCompilationInfoApi, + ? extends CcCompilationOutputsApi, + ? extends LinkingInfoApi, + ? extends CcLinkingInfoApi, + ? extends CcToolchainVariablesApi, + ? extends LibraryToLinkApi, + ? extends CcLinkParamsApi, + ? extends CcSkylarkInfoApi> + ccModule) { this.ccModule = ccModule; } 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 6e3ab8377ac5e5..25a187a6210771 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 @@ -17,13 +17,19 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext; import com.google.devtools.build.lib.skylarkbuildapi.ProviderApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.BazelCcModuleApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcCompilationInfoApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcCompilationOutputsApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcLinkParamsApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcLinkingInfoApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcModuleApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcSkylarkInfoApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcToolchainProviderApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcToolchainVariablesApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.CompilationInfoApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.FeatureConfigurationApi; import com.google.devtools.build.lib.skylarkbuildapi.cpp.LibraryToLinkApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.LinkingInfoApi; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; @@ -31,9 +37,14 @@ /** Fake implementation of {@link CcModuleApi}. */ public class FakeCcModule - implements CcModuleApi< + implements BazelCcModuleApi< CcToolchainProviderApi, FeatureConfigurationApi, + CompilationInfoApi, + CcCompilationInfoApi, + CcCompilationOutputsApi, + LinkingInfoApi, + CcLinkingInfoApi, CcToolchainVariablesApi, LibraryToLinkApi, CcLinkParamsApi, @@ -126,4 +137,30 @@ public CcSkylarkInfoApi createCcSkylarkInfo(Object skylarkRuleContextObject) throws EvalException { return null; } + + @Override + public CompilationInfoApi compile( + SkylarkRuleContext skylarkRuleContext, + FeatureConfigurationApi skylarkFeatureConfiguration, + CcToolchainProviderApi skylarkCcToolchainProvider, + SkylarkList sources, + SkylarkList headers, + Object skylarkIncludes, + Object skylarkCopts, + SkylarkList ccCompilationInfos) { + return null; + } + + @Override + public LinkingInfoApi link( + SkylarkRuleContext skylarkRuleContext, + FeatureConfigurationApi skylarkFeatureConfiguration, + CcToolchainProviderApi skylarkCcToolchainProvider, + CcCompilationOutputsApi ccCompilationOutputs, + Object skylarkLinkopts, + Object dynamicLibrary, + SkylarkList skylarkCcLinkingInfos, + boolean neverLink) { + return null; + } } diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD index 8fa0c86c49c351..e15d4584cda8ca 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD @@ -14,7 +14,10 @@ java_test( name = "cpp-rules-tests", srcs = glob( ["*.java"], - exclude = ["CcImportBaseConfiguredTargetTest.java"], + exclude = [ + "CcImportBaseConfiguredTargetTest.java", + "SkylarkCcCommonTestHelper.java", + ], ) + ["proto/CcProtoLibraryTest.java"], resources = [ "//tools/build_defs/cc:action_names.bzl", @@ -25,6 +28,7 @@ java_test( test_class = "com.google.devtools.build.lib.AllTests", deps = [ ":CcImportBaseConfiguredTargetTest", + ":SkylarkCcCommonTestHelper", "//src/main/java/com/google/devtools/build/lib:bazel-main", "//src/main/java/com/google/devtools/build/lib:bazel-rules", "//src/main/java/com/google/devtools/build/lib:build-base", @@ -43,6 +47,7 @@ java_test( "//src/main/java/com/google/devtools/build/lib/skyframe/serialization", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils", + "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs", "//src/main/java/com/google/devtools/common/options", @@ -102,6 +107,15 @@ java_library( ], ) +java_library( + name = "SkylarkCcCommonTestHelper", + srcs = ["SkylarkCcCommonTestHelper.java"], + deps = [ + "//src/test/java/com/google/devtools/build/lib:analysis_testutil", + "//src/test/java/com/google/devtools/build/lib:testutil", + ], +) + test_suite( name = "windows_tests", tags = [ diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java index 86d914f073e46c..cbb84be9efd748 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java @@ -28,6 +28,7 @@ import com.google.devtools.build.lib.packages.util.ResourceLoader; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.LibraryToLinkApi; import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; @@ -1245,4 +1246,114 @@ private void setUpCcLinkingProviderParamsTest() throws Exception { " fragments = ['cpp'],", ");"); } + + @Test + public void testCcNativeRuleDependingOnSkylarkDefinedRule() throws Exception { + SkylarkCcCommonTestHelper.createFiles(scratch, "tools/build_defs/cc"); + assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); + } + + @Test + public void testWhitelist() throws Exception { + SkylarkCcCommonTestHelper.createFiles(scratch, "foo/bar"); + AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//foo:bin")); + assertThat(e).hasMessageThat().contains("C++ Skylark API is for the time being"); + } + + @Test + public void testCopts() throws Exception { + SkylarkCcCommonTestHelper.createFilesForTestingCompilation( + scratch, "tools/build_defs/foo", "copts=depset(['-COMPILATION_OPTION'])"); + assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); + ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); + CppCompileAction action = + (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); + assertThat(action.getArguments()).contains("-COMPILATION_OPTION"); + } + + @Test + public void testIncludeDirs() throws Exception { + SkylarkCcCommonTestHelper.createFilesForTestingCompilation( + scratch, "tools/build_defs/foo", "includes=depset(['foo/bar', 'baz/qux'])"); + ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); + assertThat(target).isNotNull(); + CppCompileAction action = + (CppCompileAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".o")); + assertThat(action.getArguments()).containsAllOf("-Ifoo/bar", "-Ibaz/qux"); + } + + @Test + public void testLinkingOutputs() throws Exception { + SkylarkCcCommonTestHelper.createFiles(scratch, "tools/build_defs/foo"); + ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); + assertThat(target).isNotNull(); + @SuppressWarnings("unchecked") + SkylarkList libraries = + (SkylarkList) target.get("libraries"); + assertThat( + libraries + .stream() + .map(x -> x.getOriginalLibraryArtifact().getFilename()) + .collect(ImmutableList.toImmutableList())) + .contains("libskylark_lib.so"); + } + + @Test + public void testLinkopts() throws Exception { + SkylarkCcCommonTestHelper.createFilesForTestingLinking( + scratch, "tools/build_defs/foo", "linkopts=depset(['-LINKING_OPTION'])"); + ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); + assertThat(target).isNotNull(); + CppLinkAction action = + (CppLinkAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), ".so")); + assertThat(action.getArguments()).contains("-LINKING_OPTION"); + } + + @Test + public void testSettingDynamicLibraryArtifact() throws Exception { + SkylarkCcCommonTestHelper.createFilesForTestingLinking( + scratch, + "tools/build_defs/foo", + "dynamic_library=ctx.actions.declare_file('dynamic_lib_artifact.so')"); + assertThat(getConfiguredTarget("//foo:skylark_lib")).isNotNull(); + ConfiguredTarget target = getConfiguredTarget("//foo:skylark_lib"); + assertThat( + getFilesToBuild(target) + .toCollection() + .stream() + .map(x -> x.getFilename()) + .collect(ImmutableList.toImmutableList())) + .contains("dynamic_lib_artifact.so"); + } + + @Test + public void testCcLinkingInfos() throws Exception { + SkylarkCcCommonTestHelper.createFilesForTestingLinking( + scratch, "tools/build_defs/foo", "cc_linking_infos=dep_cc_linking_infos"); + assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); + ConfiguredTarget target = getConfiguredTarget("//foo:bin"); + CppLinkAction action = + (CppLinkAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), "bin")); + assertThat(action.getArguments()).containsAllOf("-DEP1_LINKOPT", "-DEP2_LINKOPT"); + } + + @Test + public void testNeverlinkTrue() throws Exception { + assertThat(setUpNeverlinkTest("True").getArguments()).doesNotContain("-NEVERLINK_OPTION"); + } + + @Test + public void testNeverlinkFalse() throws Exception { + assertThat(setUpNeverlinkTest("False").getArguments()).contains("-NEVERLINK_OPTION"); + } + + private CppLinkAction setUpNeverlinkTest(String value) throws Exception { + SkylarkCcCommonTestHelper.createFilesForTestingLinking( + scratch, + "tools/build_defs/foo", + String.join(",", "linkopts=depset(['-NEVERLINK_OPTION'])", "neverlink=" + value)); + assertThat(getConfiguredTarget("//foo:bin")).isNotNull(); + ConfiguredTarget target = getConfiguredTarget("//foo:bin"); + return (CppLinkAction) getGeneratingAction(artifactByPath(getFilesToBuild(target), "bin")); + } } diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTestHelper.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTestHelper.java new file mode 100644 index 00000000000000..1aafab17b259b4 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTestHelper.java @@ -0,0 +1,123 @@ +// Copyright 2018 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.rules.cpp; + +import com.google.devtools.build.lib.analysis.util.AnalysisMock; +import com.google.devtools.build.lib.testutil.Scratch; + +/** Methods useful for tests testing the C++ Skylark API. */ +public final class SkylarkCcCommonTestHelper { + + public static void createFilesForTestingCompilation( + Scratch scratch, String bzlFilePath, String compileProviderLines) throws Exception { + createFiles(scratch, bzlFilePath, compileProviderLines, ""); + } + + public static void createFilesForTestingLinking( + Scratch scratch, String bzlFilePath, String linkProviderLines) throws Exception { + createFiles(scratch, bzlFilePath, "", linkProviderLines); + } + + public static void createFiles(Scratch scratch, String bzlFilePath) throws Exception { + createFiles(scratch, bzlFilePath, "", ""); + } + + public static void createFiles( + Scratch scratch, String bzlFilePath, String compileProviderLines, String linkProviderLines) + throws Exception { + String fragments = " fragments = ['google_cpp', 'cpp'],"; + if (AnalysisMock.get().isThisBazel()) { + fragments = " fragments = ['cpp'],"; + } + scratch.file(bzlFilePath + "/BUILD"); + scratch.file( + bzlFilePath + "/extension.bzl", + "def _cc_skylark_library_impl(ctx):", + " dep_cc_linking_infos = []", + " for dep in ctx.attr._deps:", + " dep_cc_linking_infos.append(dep[CcLinkingInfo])", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(", + " cc_toolchain=toolchain,", + " requested_features = ctx.features,", + " unsupported_features = ctx.disabled_features)", + " compilation_info = cc_common.compile(", + " ctx=ctx,", + " feature_configuration=feature_configuration,", + " cc_toolchain=toolchain,", + " srcs=ctx.files.srcs,", + " hdrs=ctx.files.hdrs" + (compileProviderLines.isEmpty() ? "" : ","), + " " + compileProviderLines, + " )", + " linking_info = cc_common.link(", + " ctx=ctx,", + " feature_configuration=feature_configuration,", + " cc_compilation_outputs=compilation_info.cc_compilation_outputs(),", + " cc_toolchain=toolchain" + (linkProviderLines.isEmpty() ? "" : ","), + " " + linkProviderLines, + " )", + " files_to_build = []", + " files_to_build.extend(compilation_info.cc_compilation_outputs()", + " .object_files(use_pic=True))", + " files_to_build.extend(compilation_info.cc_compilation_outputs()", + " .object_files(use_pic=False))", + " static_libs = linking_info.cc_linking_outputs().pic_static_libraries()", + " for static_lib in static_libs:", + " files_to_build.append(static_lib.original_artifact())", + " dynamic_libs = linking_info.cc_linking_outputs().dynamic_libraries_for_linking()", + " for dynamic_lib in dynamic_libs:", + " files_to_build.append(dynamic_lib.original_artifact())", + " return struct(", + " libraries=dynamic_libs,", + " providers=[DefaultInfo(files=depset(files_to_build)),", + " cc_common.create_cc_skylark_info(ctx=ctx),", + " compilation_info.cc_compilation_info(),", + " linking_info.cc_linking_info()])", + "cc_skylark_library = rule(", + " implementation = _cc_skylark_library_impl,", + " attrs = {", + " 'srcs': attr.label_list(allow_files=True),", + " 'hdrs': attr.label_list(allow_files=True),", + " '_deps': attr.label_list(default=['//foo:dep1', '//foo:dep2']),", + " '_cc_toolchain': attr.label(default =", + " configuration_field(fragment = 'cpp', name = 'cc_toolchain'))", + " },", + fragments, + ")"); + scratch.file( + "foo/BUILD", + "load('//" + bzlFilePath + ":extension.bzl', 'cc_skylark_library')", + "cc_library(", + " name = 'dep1',", + " srcs = ['dep1.cc'],", + " hdrs = ['dep1.h'],", + " linkopts = ['-DEP1_LINKOPT'],", + ")", + "cc_library(", + " name = 'dep2',", + " srcs = ['dep2.cc'],", + " hdrs = ['dep2.h'],", + " linkopts = ['-DEP2_LINKOPT'],", + ")", + "cc_skylark_library(", + " name = 'skylark_lib',", + " srcs = ['skylark_lib.cc'],", + " hdrs = ['skylark_lib.h'],", + ")", + "cc_binary(", + " name = 'bin',", + " deps = ['skylark_lib'],", + ")"); + } +}