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 f4efdad6a6d0d8..eb8c6a9a568103 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 @@ -22,9 +22,11 @@ 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; +import com.google.devtools.build.lib.rules.cpp.CcCompilationInfo; import com.google.devtools.build.lib.rules.cpp.CcHostToolchainAliasRule; 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; @@ -76,6 +78,8 @@ public void init(ConfiguredRuleClassProvider.Builder builder) { builder.addRuleDefinition(new FdoPrefetchHintsRule()); builder.addSkylarkBootstrap(new CcBootstrap(new CcModule())); + builder.addSkylarkAccessibleTopLevels("CcCompilationInfo", CcCompilationInfo.PROVIDER); + builder.addSkylarkAccessibleTopLevels("CcLinkingInfo", CcLinkingInfo.PROVIDER); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java index a026a0a1b35a78..6a7219c68f777a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java @@ -52,7 +52,9 @@ import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; +import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; import com.google.devtools.build.lib.packages.TriState; +import com.google.devtools.build.lib.rules.cpp.CcModule.CcSkylarkInfo; import com.google.devtools.build.lib.rules.cpp.CcToolchain; import com.google.devtools.build.lib.rules.cpp.CppConfiguration; import com.google.devtools.build.lib.rules.cpp.CppFileTypes; @@ -349,7 +351,9 @@ public Object getDefault(AttributeMap rule) { attr("deps", LABEL_LIST) .allowedRuleClasses(DEPS_ALLOWED_RULES) .allowedFileTypes(CppFileTypes.LINKER_SCRIPT) - .skipAnalysisTimeFileTypeCheck()) + .skipAnalysisTimeFileTypeCheck() + .mandatoryProviders( + SkylarkProviderIdentifier.forKey(CcSkylarkInfo.PROVIDER.getKey()))) /* The Windows DEF file to be passed to linker.

This attribute should only be used when Windows is the target platform. 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 0e7eca0a516365..baaf365864baa1 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 @@ -17,13 +17,24 @@ 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.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.packages.NativeInfo; +import com.google.devtools.build.lib.packages.NativeProvider; import com.google.devtools.build.lib.packages.Provider; +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; +import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcModuleApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcSkylarkInfoApi; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.SkylarkDict; @@ -33,7 +44,32 @@ /** A module that contains Skylark utilities for C++ support. */ public class CcModule - implements CcModuleApi { + implements CcModuleApi< + CcToolchainProvider, + FeatureConfiguration, + CcToolchainVariables, + LibraryToLink, + CcLinkParams, + CcSkylarkInfo> { + + /** + * 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. + */ + @Immutable + @AutoCodec + public static final class CcSkylarkInfo extends NativeInfo implements CcSkylarkInfoApi { + public static final ObjectCodec CODEC = new CcModule_CcSkylarkInfo_AutoCodec(); + + public static final NativeProvider PROVIDER = + new NativeProvider(CcSkylarkInfo.class, "CcSkylarkInfo") {}; + + @AutoCodec.Instantiator + @VisibleForSerialization + CcSkylarkInfo() { + super(PROVIDER); + } + } @Override public Provider getCcToolchainProvider() { @@ -182,7 +218,7 @@ protected static T convertFromNoneable(Object obj, @Nullable T defaultValue) } /** Converts an object that can be the either SkylarkNestedSet or None into NestedSet. */ - protected NestedSet asStringNestedSet(Object o) throws EvalException { + protected NestedSet asStringNestedSet(Object o) { SkylarkNestedSet skylarkNestedSet = convertFromNoneable(o, /* defaultValue= */ null); if (skylarkNestedSet != null) { return skylarkNestedSet.getSet(String.class); @@ -190,4 +226,71 @@ protected NestedSet asStringNestedSet(Object o) throws EvalException { return NestedSetBuilder.emptySet(Order.STABLE_ORDER); } } + + @Override + public LibraryToLink createLibraryLinkerInput( + SkylarkRuleContext skylarkRuleContext, Artifact library, String skylarkArtifactCategory) + throws EvalException { + CcCommon.checkRuleWhitelisted(skylarkRuleContext); + ArtifactCategory artifactCategory = + ArtifactCategory.fromString( + skylarkArtifactCategory, + skylarkRuleContext.getRuleContext().getRule().getLocation(), + "artifact_category"); + return LinkerInputs.opaqueLibraryToLink( + library, artifactCategory, CcLinkingOutputs.libraryIdentifierOf(library)); + } + + @Override + public LibraryToLink createSymlinkLibraryLinkerInput( + SkylarkRuleContext skylarkRuleContext, CcToolchainProvider ccToolchain, Artifact library) { + Artifact dynamicLibrarySymlink = + SolibSymlinkAction.getDynamicLibrarySymlink( + skylarkRuleContext.getRuleContext(), + ccToolchain.getSolibDirectory(), + library, + /* preserveName= */ true, + /* prefixConsumer= */ true, + skylarkRuleContext.getRuleContext().getConfiguration()); + return LinkerInputs.solibLibraryToLink( + dynamicLibrarySymlink, library, CcLinkingOutputs.libraryIdentifierOf(library)); + } + + @Override + public CcLinkParams createCcLinkParams( + SkylarkRuleContext skylarkRuleContext, + Object skylarkLibrariesToLink, + Object skylarkDynamicLibrariesForRuntime, + Object skylarkUserLinkFlags) + throws EvalException { + CcCommon.checkRuleWhitelisted(skylarkRuleContext); + + SkylarkNestedSet librariesToLink = convertFromNoneable(skylarkLibrariesToLink, null); + SkylarkNestedSet dynamicLibrariesForRuntime = + convertFromNoneable(skylarkDynamicLibrariesForRuntime, null); + SkylarkNestedSet userLinkFlags = convertFromNoneable(skylarkUserLinkFlags, null); + + CcLinkParams.Builder builder = CcLinkParams.builder(); + if (librariesToLink != null) { + builder.addLibraries(librariesToLink.toCollection(LibraryToLink.class)); + } + if (dynamicLibrariesForRuntime != null) { + builder.addDynamicLibrariesForRuntime( + dynamicLibrariesForRuntime.toCollection(Artifact.class)); + } + if (userLinkFlags != null) { + builder.addLinkOpts(userLinkFlags.toCollection(String.class)); + } + return builder.build(); + } + + @Override + public CcSkylarkInfo createCcSkylarkInfo(Object skylarkRuleContextObject) throws EvalException { + SkylarkRuleContext skylarkRuleContext = + convertFromNoneable(skylarkRuleContextObject, /* defaultValue= */ null); + if (skylarkRuleContext != null) { + CcCommon.checkRuleWhitelisted(skylarkRuleContext); + } + return new CcSkylarkInfo(); + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInput.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInput.java index 9cbcf6743445a6..c2da5135bf30d8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInput.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInput.java @@ -15,23 +15,19 @@ package com.google.devtools.build.lib.rules.cpp; import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.LinkerInputApi; /** * Something that appears on the command line of the linker. Since we sometimes expand archive files * to their constituent object files, we need to keep information whether a certain file contains * embedded objects and if so, the list of the object files themselves. */ -public interface LinkerInput { +public interface LinkerInput extends LinkerInputApi { /** * Returns the type of the linker input. */ ArtifactCategory getArtifactCategory(); - /** Returns the artifact that is the input of the linker. */ - @SkylarkCallable(name = "artifact", doc = "Artifact passed to the linker.") - Artifact getArtifact(); - /** * Returns the original library to link. If this library is a solib symlink, returns the * artifact the symlink points to, otherwise, the library itself. diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java index 599710579235cc..5ccef60859f60e 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java @@ -22,8 +22,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; -import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; -import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.LibraryToLinkApi; /** * Factory for creating new {@link LinkerInput} objects. @@ -154,12 +153,7 @@ public boolean isFake() { * A library the user can link to. This is different from a simple linker input in that it also * has a library identifier. */ - @SkylarkModule( - name = "LibraryToLink", - category = SkylarkModuleCategory.BUILTIN, - documented = false, - doc = "A library the user can link to.") - public interface LibraryToLink extends LinkerInput { + public interface LibraryToLink extends LinkerInput, LibraryToLinkApi { ImmutableMap getLtoBitcodeFiles(); /** diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java index 73c5e498a837ac..f1e3a65c85103c 100644 --- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java @@ -14,8 +14,10 @@ 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.ProviderApi; +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; @@ -33,7 +35,10 @@ public interface CcModuleApi< CcToolchainProviderT extends CcToolchainProviderApi, FeatureConfigurationT extends FeatureConfigurationApi, - CcToolchainVariablesT extends CcToolchainVariablesApi> { + CcToolchainVariablesT extends CcToolchainVariablesApi, + LibraryToLinkT extends LibraryToLinkApi, + CcLinkParamsT extends CcLinkParamsApi, + CcSkylarkInfoT extends CcSkylarkInfoApi> { @SkylarkCallable( name = "CcToolchainInfo", @@ -450,4 +455,135 @@ CcToolchainVariablesT getLinkBuildVariables( @SkylarkCallable(name = "empty_variables", documented = false) CcToolchainVariablesT getVariables(); + + @SkylarkCallable( + name = "create_library_to_link", + documented = false, + parameters = { + @Param( + name = "ctx", + doc = "Skylark rule context.", + positional = false, + named = true, + type = SkylarkRuleContext.class), + @Param( + name = "library", + doc = "Library to be linked.", + positional = false, + named = true, + type = Artifact.class), + @Param( + name = "artifact_category", + doc = + "Artifact category. Can be: static_library, alwayslink_static_library, " + + "dynamic_library or interface_library", + positional = false, + named = true, + type = String.class) + }) + LibraryToLinkT createLibraryLinkerInput( + SkylarkRuleContext skylarkRuleContext, Artifact library, String skylarkArtifactCategory) + throws EvalException; + + @SkylarkCallable( + name = "create_symlink_library_to_link", + documented = false, + parameters = { + @Param( + name = "ctx", + doc = "Skylark rule context.", + positional = false, + named = true, + type = SkylarkRuleContext.class), + @Param( + name = "cc_toolchain", + doc = "C++ toolchain provider to be used.", + positional = false, + named = true, + type = CcToolchainProviderApi.class), + @Param( + name = "library", + doc = "Library that should be symlinked.", + positional = false, + named = true, + type = Artifact.class), + }) + LibraryToLinkT createSymlinkLibraryLinkerInput( + SkylarkRuleContext skylarkRuleContext, CcToolchainProviderT ccToolchain, Artifact library); + + @SkylarkCallable( + name = "create_cc_link_params", + doc = "Creates cc link parameters", + parameters = { + @Param( + name = "ctx", + positional = false, + named = true, + type = SkylarkRuleContextApi.class, + doc = "The rule context."), + @Param( + name = "libraries_to_link", + doc = + "List of libraries that should be passed to the linker/archiver. They can be " + + "static and/or dynamic libraries.", + positional = false, + named = true, + noneable = true, + defaultValue = "None", + allowedTypes = { + @ParamType(type = SkylarkNestedSet.class), + @ParamType(type = NoneType.class) + }), + @Param( + name = "dynamic_libraries_for_runtime", + doc = + "When 'libraries_to_link' has dynamic libraries, then the runtime library can " + + "be specified as well. This is not obligatory though, as we may provide a " + + "library for linking and at runtime the actual library will be provided by " + + "the system.", + positional = false, + named = true, + noneable = true, + defaultValue = "None", + allowedTypes = { + @ParamType(type = SkylarkNestedSet.class), + @ParamType(type = NoneType.class) + }), + @Param( + name = "user_link_flags", + doc = "List of user provided linker flags.", + positional = false, + named = true, + noneable = true, + defaultValue = "None", + allowedTypes = { + @ParamType(type = SkylarkNestedSet.class), + @ParamType(type = NoneType.class) + }) + }) + CcLinkParamsT createCcLinkParams( + SkylarkRuleContext skylarkRuleContext, + Object skylarkLibrariesToLink, + Object skylarkDynamicLibrariesForRuntime, + Object skylarkUserLinkFlags) + throws EvalException; + + @SkylarkCallable( + name = "create_cc_skylark_info", + documented = false, + parameters = { + // TODO(plf): Make this parameter mandatory. Change cc_embed_data.bzl first. + @Param( + name = "ctx", + doc = "Skylark rule context.", + positional = false, + named = true, + noneable = true, + defaultValue = "None", + allowedTypes = { + @ParamType(type = SkylarkRuleContextApi.class), + @ParamType(type = NoneType.class) + }) + }) + CcSkylarkInfoT createCcSkylarkInfo(Object skylarkRuleContextObject) throws EvalException; } diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcSkylarkInfoApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcSkylarkInfoApi.java new file mode 100644 index 00000000000000..dc764a064fe931 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcSkylarkInfoApi.java @@ -0,0 +1,26 @@ +// 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.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; + +/** Skylark rules need to provide this to be a dependency of native cc_* rules. */ +@SkylarkModule( + name = "cc_skylark_info", + category = SkylarkModuleCategory.PROVIDER, + doc = "Temporary provider to mark C++ Skylark rules") +// TODO(b/77669139): Delete this class. Use {@link CcCompilationInfo} instead. +public interface CcSkylarkInfoApi {} 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 new file mode 100644 index 00000000000000..a8421654c76a24 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LibraryToLinkApi.java @@ -0,0 +1,29 @@ +// 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.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; + +/** + * A library the user can link to. This is different from a simple linker input in that it also has + * a library identifier. + */ +@SkylarkModule( + name = "LibraryToLink", + category = SkylarkModuleCategory.BUILTIN, + documented = false, + doc = "A library the user can link to.") +public interface LibraryToLinkApi {} diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LinkerInputApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LinkerInputApi.java new file mode 100644 index 00000000000000..13707d21e6232a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/LinkerInputApi.java @@ -0,0 +1,29 @@ +// Copyright 2014 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.skylarkinterface.SkylarkCallable; + +/** + * Something that appears on the command line of the linker. Since we sometimes expand archive files + * to their constituent object files, we need to keep information whether a certain file contains + * embedded objects and if so, the list of the object files themselves. + */ +public interface LinkerInputApi { + /** Returns the artifact that is the input of the linker. */ + @SkylarkCallable(name = "artifact", doc = "Artifact passed to the linker.") + Artifact getArtifact(); +} diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/BUILD b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/BUILD index a8fc8a287ce34e..defdfe7fe70460 100644 --- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/BUILD +++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp/BUILD @@ -13,8 +13,10 @@ java_library( name = "cpp", srcs = glob(["*.java"]), deps = [ + "//src/main/java/com/google/devtools/build/lib:build-base", "//src/main/java/com/google/devtools/build/lib:skylarkinterface", "//src/main/java/com/google/devtools/build/lib:syntax", + "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi", "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp", "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi", 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 c8f7a067789d63..6e3ab8377ac5e5 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 @@ -14,21 +14,30 @@ package com.google.devtools.build.skydoc.fakebuildapi.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.ProviderApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcLinkParamsApi; 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.FeatureConfigurationApi; +import com.google.devtools.build.lib.skylarkbuildapi.cpp.LibraryToLinkApi; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.skydoc.fakebuildapi.FakeProviderApi; -/** - * Fake implementation of {@link CcModuleApi}. - */ -public class FakeCcModule implements CcModuleApi { +/** Fake implementation of {@link CcModuleApi}. */ +public class FakeCcModule + implements CcModuleApi< + CcToolchainProviderApi, + FeatureConfigurationApi, + CcToolchainVariablesApi, + LibraryToLinkApi, + CcLinkParamsApi, + CcSkylarkInfoApi> { @Override public ProviderApi getCcToolchainProvider() { @@ -88,4 +97,33 @@ public CcToolchainVariablesApi getLinkBuildVariables(CcToolchainProviderApi ccTo public CcToolchainVariablesApi getVariables() { return null; } + + @Override + public LibraryToLinkApi createLibraryLinkerInput( + SkylarkRuleContext skylarkRuleContext, Artifact library, String skylarkArtifactCategory) + throws EvalException { + return null; + } + + @Override + public LibraryToLinkApi createSymlinkLibraryLinkerInput( + SkylarkRuleContext skylarkRuleContext, CcToolchainProviderApi ccToolchain, Artifact library) { + return null; + } + + @Override + public CcLinkParamsApi createCcLinkParams( + SkylarkRuleContext skylarkRuleContext, + Object skylarkLibrariesToLink, + Object skylarkDynamicLibrariesForRuntime, + Object skylarkUserLinkFlags) + throws EvalException { + return null; + } + + @Override + public CcSkylarkInfoApi createCcSkylarkInfo(Object skylarkRuleContextObject) + throws EvalException { + return null; + } } 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 5f4fa9c5729f99..3d4a6d5cd0965b 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 @@ -14,9 +14,12 @@ package com.google.devtools.build.lib.rules.cpp; import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; @@ -24,9 +27,11 @@ import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; 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.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.testutil.TestConstants; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -36,6 +41,12 @@ */ @RunWith(JUnit4.class) public class SkylarkCcCommonTest extends BuildViewTestCase { + + @Before + public void setConfiguration() throws Exception { + useConfiguration("--experimental_enable_cc_skylark_api"); + } + @Test public void testGetToolForAction() throws Exception { scratch.file( @@ -121,7 +132,7 @@ public void testFeatureConfigurationWithAdditionalUnsupportedFeature() throws Ex AnalysisMock.get() .ccSupport() .setupCrosstool(mockToolsConfig, "feature { name: 'foo_feature' }"); - useConfiguration("--features=foo_feature"); + useConfiguration("--features=foo_feature", "--experimental_enable_cc_skylark_api"); scratch.file( "a/BUILD", "load(':rule.bzl', 'crule')", @@ -663,7 +674,7 @@ public void testMustKeepDebugLinkVariables() throws Exception { @Test public void testIsLinkingDynamicLibraryLinkVariables() throws Exception { - useConfiguration("--linkopt=-pie"); + useConfiguration("--linkopt=-pie", "--experimental_enable_cc_skylark_api"); assertThat( commandLineForVariables( CppActionNames.CPP_LINK_EXECUTABLE, @@ -688,7 +699,9 @@ public void testIsLinkingDynamicLibraryLinkVariables() throws Exception { @Test public void testIsUsingLinkerLinkVariables() throws Exception { - useConfiguration("--linkopt=-i_dont_want_to_see_this_on_archiver_command_line"); + useConfiguration( + "--linkopt=-i_dont_want_to_see_this_on_archiver_command_line", + "--experimental_enable_cc_skylark_api"); assertThat( commandLineForVariables( CppActionNames.CPP_LINK_EXECUTABLE, @@ -821,4 +834,206 @@ private SkylarkList commandLineForVariables( SkylarkList result = (SkylarkList) r.get("command_line"); return result; } + + @Test + public void testCcCompilationProvider() throws Exception { + scratch.file( + "a/BUILD", + "load('//tools/build_defs/cc:rule.bzl', 'crule')", + "cc_library(", + " name='lib',", + " hdrs = ['lib.h'],", + " srcs = ['lib.cc'],", + " deps = ['r']", + ")", + "crule(name='r')"); + scratch.file("tools/build_defs/cc/BUILD", ""); + scratch.file( + "tools/build_defs/cc/rule.bzl", + "def _impl(ctx):", + " cc_compilation_info = CcCompilationInfo(headers=depset([ctx.file._header]))", + " return [", + " cc_compilation_info, cc_common.create_cc_skylark_info(ctx=ctx),", + " ]", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_header': attr.label(allow_single_file=True, default=Label('//a:header.h'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:lib"); + @SuppressWarnings("unchecked") + CcCompilationContext ccCompilationContext = + r.get(CcCompilationInfo.PROVIDER).getCcCompilationContext(); + assertThat( + ccCompilationContext + .getDeclaredIncludeSrcs() + .toCollection() + .stream() + .map(Artifact::getFilename) + .collect(ImmutableList.toImmutableList())) + .containsExactly("lib.h", "header.h"); + } + + @Test + public void testLibraryLinkerInputs() throws Exception { + scratch.file("a/BUILD", "load('//tools/build_defs/cc:rule.bzl', 'crule')", "crule(name='r')"); + scratch.file("a/lib.a", ""); + scratch.file("a/lib.lo", ""); + scratch.file("a/lib.so", ""); + scratch.file("a/lib.ifso", ""); + scratch.file("tools/build_defs/cc/BUILD", ""); + scratch.file( + "tools/build_defs/cc/rule.bzl", + "def _create(ctx, lib, c):", + " return cc_common.create_library_to_link(ctx=ctx, library=lib, artifact_category=c)", + "def _impl(ctx):", + " static_library = _create(ctx, ctx.file.liba, 'static_library')", + " alwayslink_static_library = _create(ctx, ctx.file.liblo, 'alwayslink_static_library')", + " dynamic_library = _create(ctx, ctx.file.libso, 'dynamic_library')", + " interface_library = _create(ctx, ctx.file.libifso, 'interface_library')", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " symlink_library = cc_common.create_symlink_library_to_link(", + " ctx=ctx, cc_toolchain=toolchain, library=ctx.file.libso)", + " return struct(", + " static_library = static_library,", + " alwayslink_static_library = alwayslink_static_library,", + " dynamic_library = dynamic_library,", + " interface_library = interface_library,", + " symlink_library = symlink_library", + " )", + "crule = rule(", + " _impl,", + " attrs = { ", + " 'liba': attr.label(default='//a:lib.a', allow_single_file=True),", + " 'liblo': attr.label(default='//a:lib.lo', allow_single_file=True),", + " 'libso': attr.label(default='//a:lib.so', allow_single_file=True),", + " 'libifso': attr.label(default='//a:lib.ifso', allow_single_file=True),", + " '_cc_toolchain': attr.label(default =", + " configuration_field(fragment = 'cpp', name = 'cc_toolchain'))", + " },", + " fragments = ['cpp'],", + ");"); + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + LibraryToLink staticLibrary = (LibraryToLink) r.get("static_library"); + assertThat(staticLibrary.getArtifact().getFilename()).isEqualTo("lib.a"); + LibraryToLink alwaysLinkStaticLibrary = (LibraryToLink) r.get("alwayslink_static_library"); + assertThat(alwaysLinkStaticLibrary.getArtifact().getFilename()).isEqualTo("lib.lo"); + LibraryToLink dynamicLibrary = (LibraryToLink) r.get("dynamic_library"); + assertThat(dynamicLibrary.getArtifact().getFilename()).isEqualTo("lib.so"); + LibraryToLink interfaceLibrary = (LibraryToLink) r.get("interface_library"); + assertThat(interfaceLibrary.getArtifact().getFilename()).isEqualTo("lib.ifso"); + LibraryToLink symlinkLibrary = (LibraryToLink) r.get("symlink_library"); + assertThat(symlinkLibrary.getArtifact().getFilename()).isEqualTo("lib.so"); + assertThat(symlinkLibrary.getArtifact().getPath()) + .isNotEqualTo(symlinkLibrary.getOriginalLibraryArtifact().getPath()); + } + + @Test + public void testLibraryLinkerInputArtifactCategoryError() throws Exception { + scratch.file("a/BUILD", "load('//tools/build_defs/cc:rule.bzl', 'crule')", "crule(name='r')"); + scratch.file("a/lib.a", ""); + scratch.file("tools/build_defs/cc/BUILD", ""); + scratch.file( + "tools/build_defs/cc/rule.bzl", + "def _impl(ctx):", + " executable = cc_common.create_library_to_link(", + " ctx=ctx, library=ctx.file.lib, artifact_category='executable')", + " return struct(", + " executable = executable,", + " )", + "crule = rule(", + " _impl,", + " attrs = { ", + " 'lib': attr.label(default='//a:lib.a', allow_single_file=True),", + " },", + " fragments = ['cpp'],", + ");"); + AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:r")); + assertThat(e) + .hasMessageThat() + .contains( + "Possible values for artifact_category: static_library, alwayslink_static_library, " + + "dynamic_library, interface_library"); + } + + @Test + public void testCcLinkingProviderParamsWithoutFlag() throws Exception { + useConfiguration("--noexperimental_enable_cc_skylark_api"); + setUpCcLinkingProviderParamsTest(); + AssertionError e = assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:r")); + assertThat(e) + .hasMessageThat() + .contains( + "Pass --experimental_enable_cc_skylark_api in order to " + + "use the C++ API. Beware that we will be making breaking changes to this API " + + "without prior warning."); + } + + @Test + public void testCcLinkingProviderParamsWithFlag() throws Exception { + setUpCcLinkingProviderParamsTest(); + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + CcLinkParamsStore ccLinkParamsStore = + ((CcLinkingInfo) r.get("cc_linking_info")).getCcLinkParamsStore(); + assertThat(ccLinkParamsStore).isNotNull(); + assertThat( + ccLinkParamsStore + .get(/* linkingStatically= */ true, /* linkShared= */ true) + .flattenedLinkopts()) + .containsExactly("-static_shared"); + assertThat( + ccLinkParamsStore + .get(/* linkingStatically= */ true, /* linkShared= */ false) + .flattenedLinkopts()) + .containsExactly("-static_no_shared"); + assertThat( + ccLinkParamsStore + .get(/* linkingStatically= */ false, /* linkShared= */ true) + .flattenedLinkopts()) + .containsExactly("-no_static_shared"); + assertThat( + ccLinkParamsStore + .get(/* linkingStatically= */ false, /* linkShared= */ false) + .flattenedLinkopts()) + .containsExactly("-no_static_no_shared"); + } + + private void setUpCcLinkingProviderParamsTest() throws Exception { + scratch.file("a/BUILD", "load('//tools/build_defs/cc:rule.bzl', 'crule')", "crule(name='r')"); + scratch.file("a/lib.a", ""); + scratch.file("a/lib.so", ""); + scratch.file("tools/build_defs/cc/BUILD", ""); + scratch.file( + "tools/build_defs/cc/rule.bzl", + "def _create(ctx, l, r, f):", + " return cc_common.create_cc_link_params(", + " ctx=ctx, libraries_to_link=depset(l), dynamic_libraries_for_runtime=depset(r),", + " user_link_flags=depset(f))", + "def _impl(ctx):", + " liba = cc_common.create_library_to_link(ctx=ctx, library=ctx.file.liba,", + " artifact_category='static_library')", + " ss = _create(ctx, [liba], [ctx.file.libso], ['-static_shared'])", + " sns = _create(ctx, [liba], [ctx.file.libso], ['-static_no_shared'])", + " nss = _create(ctx, [liba], [ctx.file.libso], ['-no_static_shared'])", + " nsns = _create(ctx, [liba], [ctx.file.libso], ['-no_static_no_shared'])", + " cc_linking_info = CcLinkingInfo(", + " static_shared_params=ss, static_no_shared_params=sns,", + " no_static_shared_params=nss, no_static_no_shared_params=nsns)", + " return struct(", + " cc_linking_info = cc_linking_info,", + " )", + "crule = rule(", + " _impl,", + " attrs = { ", + " 'liba': attr.label(default='//a:lib.a', allow_single_file=True),", + " 'libso': attr.label(default='//a:lib.so', allow_single_file=True),", + " },", + " fragments = ['cpp'],", + ");"); + } }