blob: 6005815aeb5cdda7fe5424e7f52e7377e87747ee [file] [log] [blame]
// Copyright 2016 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.proto;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode.TARGET;
import static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
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.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.ConfiguredAspect;
import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.AspectDefinition;
import com.google.devtools.build.lib.packages.AspectParameters;
import com.google.devtools.build.lib.packages.Attribute.LabelLateBoundDefault;
import com.google.devtools.build.lib.packages.NativeAspectClass;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.rules.cpp.AspectLegalCppSemantics;
import com.google.devtools.build.lib.rules.cpp.CcCommon;
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.CcInfo;
import com.google.devtools.build.lib.rules.cpp.CcLinkingContext;
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.CcNativeLibraryProvider;
import com.google.devtools.build.lib.rules.cpp.CcToolchain;
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.CppConfiguration;
import com.google.devtools.build.lib.rules.cpp.CppHelper;
import com.google.devtools.build.lib.rules.cpp.CppRuleClasses;
import com.google.devtools.build.lib.rules.cpp.CppSemantics;
import com.google.devtools.build.lib.rules.cpp.LibraryToLink;
import com.google.devtools.build.lib.rules.proto.ProtoCommon;
import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder;
import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder.Exports;
import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder.Services;
import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder.ToolchainInvocation;
import com.google.devtools.build.lib.rules.proto.ProtoConfiguration;
import com.google.devtools.build.lib.rules.proto.ProtoInfo;
import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainProvider;
import com.google.devtools.build.lib.rules.proto.ProtoSourceFileBlacklist;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/** Part of the implementation of cc_proto_library. */
public abstract class CcProtoAspect extends NativeAspectClass implements ConfiguredAspectFactory {
private static final String PROTO_TOOLCHAIN_ATTR = ":aspect_cc_proto_toolchain";
private static final LabelLateBoundDefault<?> PROTO_TOOLCHAIN_LABEL =
LabelLateBoundDefault.fromTargetConfiguration(
ProtoConfiguration.class,
Label.parseAbsoluteUnchecked("@com_google_protobuf//:cc_toolchain"),
(rule, attributes, protoConfig) -> protoConfig.protoToolchainForCc());
private final CppSemantics cppSemantics;
private final LabelLateBoundDefault<?> ccToolchainAttrValue;
private final Label ccToolchainType;
protected CcProtoAspect(AspectLegalCppSemantics cppSemantics, RuleDefinitionEnvironment env) {
this.cppSemantics = cppSemantics;
this.ccToolchainAttrValue = CppRuleClasses.ccToolchainAttribute(env);
this.ccToolchainType = CppRuleClasses.ccToolchainTypeAttribute(env);
}
@Override
public ConfiguredAspect create(
ConfiguredTargetAndData ctadBase,
RuleContext ruleContext,
AspectParameters parameters,
String toolsRepository)
throws InterruptedException, ActionConflictException {
ProtoInfo protoInfo = checkNotNull(ctadBase.getConfiguredTarget().get(ProtoInfo.PROVIDER));
try {
ConfiguredAspect.Builder result = new ConfiguredAspect.Builder(this, parameters, ruleContext);
new Impl(ruleContext, protoInfo, cppSemantics, ccToolchainType).addProviders(result);
return result.build();
} catch (RuleErrorException e) {
ruleContext.ruleError(e.getMessage());
return null;
}
}
@Override
public AspectDefinition getDefinition(AspectParameters aspectParameters) {
AspectDefinition.Builder result =
new AspectDefinition.Builder(this)
.propagateAlongAttribute("deps")
.requiresConfigurationFragments(CppConfiguration.class, ProtoConfiguration.class)
.requireSkylarkProviders(ProtoInfo.PROVIDER.id())
.addRequiredToolchains(ccToolchainType)
.add(
attr(PROTO_TOOLCHAIN_ATTR, LABEL)
.mandatoryNativeProviders(ImmutableList.of(ProtoLangToolchainProvider.class))
.value(PROTO_TOOLCHAIN_LABEL))
.add(
attr(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME, LABEL)
.mandatoryProviders(CcToolchainProvider.PROVIDER.id())
.value(ccToolchainAttrValue));
return result.build();
}
private static class Impl {
private final TransitiveInfoProviderMap ccLibraryProviders;
private final ProtoCcHeaderProvider headerProvider;
private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
private final RuleContext ruleContext;
private final ProtoInfo protoInfo;
private final CppSemantics cppSemantics;
private final NestedSetBuilder<Artifact> filesBuilder;
private final Label ccToolchainType;
Impl(
RuleContext ruleContext,
ProtoInfo protoInfo,
CppSemantics cppSemantics,
Label ccToolchainType)
throws RuleErrorException, InterruptedException {
this.ruleContext = ruleContext;
this.protoInfo = protoInfo;
this.cppSemantics = cppSemantics;
this.ccToolchainType = ccToolchainType;
FeatureConfiguration featureConfiguration = getFeatureConfiguration();
ProtoConfiguration protoConfiguration = ruleContext.getFragment(ProtoConfiguration.class);
ImmutableList.Builder<TransitiveInfoCollection> depsBuilder = ImmutableList.builder();
TransitiveInfoCollection runtime = getProtoToolchainProvider().runtime();
if (runtime != null) {
depsBuilder.add(runtime);
}
depsBuilder.addAll(ruleContext.getPrerequisites("deps", TARGET));
ImmutableList<TransitiveInfoCollection> deps = depsBuilder.build();
CppHelper.checkProtoLibrariesInDeps(ruleContext, deps);
CcCompilationHelper compilationHelper =
initializeCompilationHelper(featureConfiguration, deps);
// Compute and register files generated by this proto library.
Collection<Artifact> outputs = new ArrayList<>();
if (areSrcsBlacklisted()) {
registerBlacklistedSrcs(protoInfo, compilationHelper);
headerProvider = null;
} else if (!protoInfo.getDirectProtoSources().isEmpty()) {
Collection<Artifact> headers =
getOutputFiles(protoConfiguration.ccProtoLibraryHeaderSuffixes());
Collection<Artifact> sources =
getOutputFiles(protoConfiguration.ccProtoLibrarySourceSuffixes());
outputs.addAll(headers);
outputs.addAll(sources);
compilationHelper.addSources(sources);
compilationHelper.addPublicHeaders(headers);
NestedSetBuilder<Artifact> publicHeaderPaths = NestedSetBuilder.stableOrder();
publicHeaderPaths.addAll(headers);
headerProvider = new ProtoCcHeaderProvider(publicHeaderPaths.build());
} else {
// If this proto_library doesn't have sources, it provides the combined headers of all its
// direct dependencies. Thus, if a direct dependency does have sources, the generated files
// are also provided by this library. If a direct dependency does not have sources, it will
// do the same thing, so that effectively this library looks through all source-less
// proto_libraries and provides all generated headers of the proto_libraries with sources
// that it depends on.
NestedSetBuilder<Artifact> transitiveHeaders = NestedSetBuilder.stableOrder();
for (ProtoCcHeaderProvider provider :
ruleContext.getPrerequisites("deps", TARGET, ProtoCcHeaderProvider.class)) {
compilationHelper.addPublicTextualHeaders(provider.getHeaders());
transitiveHeaders.addTransitive(provider.getHeaders());
}
headerProvider = new ProtoCcHeaderProvider(transitiveHeaders.build());
}
filesBuilder = NestedSetBuilder.stableOrder();
filesBuilder.addAll(outputs);
createProtoCompileAction(outputs);
CompilationInfo compilationInfo = compilationHelper.compile();
CcCompilationOutputs ccCompilationOutputs = compilationInfo.getCcCompilationOutputs();
CcLinkingHelper ccLinkingHelper = initializeLinkingHelper(featureConfiguration, deps);
if (ccToolchain(ruleContext).supportsInterfaceSharedLibraries(featureConfiguration)) {
ccLinkingHelper.emitInterfaceSharedLibraries(true);
}
ImmutableList<LibraryToLink> libraryToLink = ImmutableList.of();
if (!ccCompilationOutputs.isEmpty()) {
CcLinkingOutputs ccLinkingOutputs = ccLinkingHelper.link(ccCompilationOutputs);
if (!ccLinkingOutputs.isEmpty()) {
libraryToLink = ImmutableList.of(ccLinkingOutputs.getLibraryToLink());
}
}
CcNativeLibraryProvider ccNativeLibraryProvider =
CppHelper.collectNativeCcLibraries(deps, libraryToLink);
CcLinkingContext ccLinkingContext =
ccLinkingHelper.buildCcLinkingContextFromLibrariesToLink(
libraryToLink, compilationInfo.getCcCompilationContext());
ccLibraryProviders =
new TransitiveInfoProviderMapBuilder()
.put(
CcInfo.builder()
.setCcCompilationContext(compilationInfo.getCcCompilationContext())
.setCcLinkingContext(ccLinkingContext)
.setCcDebugInfoContext(
CppHelper.mergeCcDebugInfoContexts(
compilationInfo.getCcCompilationOutputs(),
AnalysisUtils.getProviders(deps, CcInfo.PROVIDER)))
.build())
.add(ccNativeLibraryProvider)
.build();
outputGroups =
ImmutableMap.copyOf(
CcCompilationHelper.buildOutputGroups(compilationInfo.getCcCompilationOutputs()));
// On Windows, dynamic library is not built by default, so don't add them to filesToBuild.
if (!libraryToLink.isEmpty()) {
LibraryToLink artifactsToBuild = libraryToLink.get(0);
if (artifactsToBuild.getStaticLibrary() != null) {
filesBuilder.add(artifactsToBuild.getStaticLibrary());
}
if (artifactsToBuild.getPicStaticLibrary() != null) {
filesBuilder.add(artifactsToBuild.getPicStaticLibrary());
}
if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
if (artifactsToBuild.getResolvedSymlinkDynamicLibrary() != null) {
filesBuilder.add(artifactsToBuild.getResolvedSymlinkDynamicLibrary());
} else if (artifactsToBuild.getDynamicLibrary() != null) {
filesBuilder.add(artifactsToBuild.getDynamicLibrary());
}
if (artifactsToBuild.getResolvedSymlinkInterfaceLibrary() != null) {
filesBuilder.add(artifactsToBuild.getResolvedSymlinkInterfaceLibrary());
} else if (artifactsToBuild.getInterfaceLibrary() != null) {
filesBuilder.add(artifactsToBuild.getInterfaceLibrary());
}
}
}
}
private boolean areSrcsBlacklisted() {
return !new ProtoSourceFileBlacklist(
ruleContext, getProtoToolchainProvider().blacklistedProtos())
.checkSrcs(protoInfo.getOriginalDirectProtoSources(), "cc_proto_library");
}
private FeatureConfiguration getFeatureConfiguration() {
ImmutableSet.Builder<String> requestedFeatures = new ImmutableSet.Builder<>();
requestedFeatures.addAll(ruleContext.getFeatures());
ImmutableSet.Builder<String> unsupportedFeatures = new ImmutableSet.Builder<>();
unsupportedFeatures.addAll(ruleContext.getDisabledFeatures());
unsupportedFeatures.add(CppRuleClasses.PARSE_HEADERS);
unsupportedFeatures.add(CppRuleClasses.LAYERING_CHECK);
if (!areSrcsBlacklisted() && !protoInfo.getDirectProtoSources().isEmpty()) {
requestedFeatures.add(CppRuleClasses.HEADER_MODULES);
} else {
unsupportedFeatures.add(CppRuleClasses.HEADER_MODULES);
}
FeatureConfiguration featureConfiguration =
CcCommon.configureFeaturesOrReportRuleError(
ruleContext,
requestedFeatures.build(),
unsupportedFeatures.build(),
ccToolchain(ruleContext));
return featureConfiguration;
}
private CcCompilationHelper initializeCompilationHelper(
FeatureConfiguration featureConfiguration, List<TransitiveInfoCollection> deps)
throws InterruptedException {
CcCommon common = new CcCommon(ruleContext);
CcToolchainProvider toolchain = ccToolchain(ruleContext);
CcCompilationHelper helper =
new CcCompilationHelper(
ruleContext,
ruleContext,
ruleContext.getLabel(),
CppHelper.getGrepIncludes(ruleContext),
cppSemantics,
featureConfiguration,
toolchain,
toolchain.getFdoContext(),
TargetUtils.getExecutionInfo(
ruleContext.getRule(), ruleContext.isAllowTagsPropagation()))
.addCcCompilationContexts(CppHelper.getCompilationContextsFromDeps(deps))
.addCcCompilationContexts(
ImmutableList.of(CcCompilationHelper.getStlCcCompilationContext(ruleContext)))
.setPurpose(common.getPurpose(cppSemantics));
// Don't instrument the generated C++ files even when --collect_code_coverage is set.
helper.setCodeCoverageEnabled(false);
String protoRoot = protoInfo.getDirectProtoSourceRoot();
PathFragment repositoryRoot =
ruleContext.getLabel().getPackageIdentifier().getRepository().getSourceRoot();
if (protoRoot.equals(".") || protoRoot.equals(repositoryRoot.getPathString())) {
return helper;
}
PathFragment protoRootFragment = PathFragment.create(protoRoot);
PathFragment binOrGenfiles = ruleContext.getBinOrGenfilesDirectory().getExecPath();
if (protoRootFragment.startsWith(binOrGenfiles)) {
protoRootFragment = protoRootFragment.relativeTo(binOrGenfiles);
}
PathFragment repositoryPath =
ruleContext
.getLabel()
.getPackageIdentifier()
.getRepository()
.getExecPath(
ruleContext
.getAnalysisEnvironment()
.getSkylarkSemantics()
.experimentalSiblingRepositoryLayout());
if (protoRootFragment.startsWith(repositoryPath)) {
protoRootFragment = protoRootFragment.relativeTo(repositoryPath);
}
String stripIncludePrefix =
PathFragment.create("//").getRelative(protoRootFragment).toString();
helper.setStripIncludePrefix(stripIncludePrefix);
return helper;
}
private CcLinkingHelper initializeLinkingHelper(
FeatureConfiguration featureConfiguration, ImmutableList<TransitiveInfoCollection> deps)
throws InterruptedException {
CcToolchainProvider toolchain = ccToolchain(ruleContext);
CcLinkingHelper helper =
new CcLinkingHelper(
ruleContext,
ruleContext.getLabel(),
ruleContext,
ruleContext,
cppSemantics,
featureConfiguration,
toolchain,
toolchain.getFdoContext(),
ruleContext.getConfiguration(),
ruleContext.getFragment(CppConfiguration.class),
ruleContext.getSymbolGenerator(),
TargetUtils.getExecutionInfo(
ruleContext.getRule(), ruleContext.isAllowTagsPropagation()))
.setGrepIncludes(CppHelper.getGrepIncludes(ruleContext))
.setTestOrTestOnlyTarget(ruleContext.isTestOnlyTarget());
helper.addCcLinkingContexts(CppHelper.getLinkingContextsFromDeps(deps));
// TODO(dougk): Configure output artifact with action_config
// once proto compile action is configurable from the crosstool.
if (!toolchain.supportsDynamicLinker(featureConfiguration)) {
helper.setShouldCreateDynamicLibrary(false);
}
return helper;
}
private CcToolchainProvider ccToolchain(RuleContext ruleContext) {
return CppHelper.getToolchain(
ruleContext,
ruleContext.getPrerequisite(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME, TARGET),
ccToolchainType);
}
private ImmutableSet<Artifact> getOutputFiles(Iterable<String> suffixes) {
ImmutableSet.Builder<Artifact> result = ImmutableSet.builder();
for (String suffix : suffixes) {
result.addAll(
ProtoCommon.getGeneratedOutputs(
ruleContext, protoInfo.getDirectProtoSources(), suffix));
}
return result.build();
}
private void registerBlacklistedSrcs(ProtoInfo protoInfo, CcCompilationHelper helper) {
// Hack: This is a proto_library for descriptor.proto or similar.
//
// The headers of those libraries are precomputed . They are also explicitly part of normal
// cc_library rules that export them in their 'hdrs' attribute, and compile them as header
// module if requested.
//
// The sole purpose of a proto_library with blacklisted srcs is so other proto_library rules
// can import them from a protocol buffer, as proto_library rules can only depend on other
// proto library rules.
ImmutableList.Builder<PathFragment> headers = new ImmutableList.Builder<>();
for (Artifact source : protoInfo.getDirectProtoSources()) {
headers.add(FileSystemUtils.replaceExtension(source.getRootRelativePath(), ".pb.h"));
headers.add(FileSystemUtils.replaceExtension(source.getRootRelativePath(), ".proto.h"));
}
// We add the header to the proto_library's module map as additional (textual) header for
// two reasons:
// 1. The header will be exported via a normal cc_library, and a header must only be exported
// non-textually from one library.
// 2. We want to allow proto_library rules that depend on the bootstrap-hack proto_library
// to be layering-checked; we need to provide a module map for the layering check to work.
helper.addAdditionalExportedHeaders(headers.build());
}
private void createProtoCompileAction(Collection<Artifact> outputs)
throws InterruptedException {
PathFragment protoRootFragment = PathFragment.create(protoInfo.getDirectProtoSourceRoot());
String genfilesPath;
PathFragment genfilesFragment = ruleContext.getConfiguration().getGenfilesFragment();
if (protoRootFragment.startsWith(genfilesFragment)) {
genfilesPath = protoRootFragment.getPathString();
} else {
genfilesPath = genfilesFragment.getRelative(protoRootFragment).getPathString();
}
ImmutableList.Builder<ToolchainInvocation> invocations = ImmutableList.builder();
invocations.add(
new ToolchainInvocation("C++", checkNotNull(getProtoToolchainProvider()), genfilesPath));
ProtoCompileActionBuilder.registerActions(
ruleContext,
invocations.build(),
protoInfo,
ruleContext.getLabel(),
outputs,
"C++",
Exports.DO_NOT_USE,
Services.ALLOW);
}
private ProtoLangToolchainProvider getProtoToolchainProvider() {
return ruleContext.getPrerequisite(
PROTO_TOOLCHAIN_ATTR, TARGET, ProtoLangToolchainProvider.class);
}
public void addProviders(ConfiguredAspect.Builder builder) {
OutputGroupInfo outputGroupInfo = new OutputGroupInfo(outputGroups);
builder.addProvider(
new CcProtoLibraryProviders(
filesBuilder.build(), ccLibraryProviders, outputGroupInfo));
builder.addProviders(ccLibraryProviders);
builder.addNativeDeclaredProvider(outputGroupInfo);
if (headerProvider != null) {
builder.addProvider(headerProvider);
}
}
}
}