blob: ad05f417073360c0e06632f0aed46c71960eeed5 [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;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.Runfiles;
import com.google.devtools.build.lib.analysis.RunfilesProvider;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider;
import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
/**
* An inclusion library that maps header files into a different directory. This
* is mostly used for third party libraries that have version-specific sub-directories,
* and also for libraries that provide architecture-dependent header files.
* In both cases, we want normal code to be able to include files without requiring
* the version number or the architecture name in the include statement.
*
* <p>Example: a <code>cc_inc_library</code> rule in
* <code>third_party/foo</code> has the name <code>bar</code>. It will create a
* symlink in <code>include_directory/third_party/foo/bar/third_party/foo</code>
* pointing to <code>third_party/foo/1.0</code>. This results in the include
* directory <code>include_directory/third_party/foo/bar</code> to be used for
* compilations, which makes inclusions like
* <code>#include "third_party/foo/header.h"</code> work.
*/
public abstract class CcIncLibrary implements RuleConfiguredTargetFactory {
private final CppSemantics semantics;
protected CcIncLibrary(CppSemantics semantics) {
this.semantics = semantics;
}
@Override
public ConfiguredTarget create(final RuleContext ruleContext)
throws RuleErrorException, InterruptedException {
CcToolchainProvider ccToolchain =
CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
FeatureConfiguration featureConfiguration =
CcCommon.configureFeatures(ruleContext, ccToolchain);
PathFragment packageFragment = ruleContext.getPackageDirectory();
// The rule needs a unique location for the include directory, which doesn't conflict with any
// other rule. For that reason, the include directory is at:
// configuration/package_name/_/target_name
// And then the symlink is placed at:
// configuration/package_name/_/target_name/package_name
// So that these inclusions can be resolved correctly:
// #include "package_name/a.h"
//
// The target of the symlink is:
// package_name/targetPrefix/
// All declared header files must be below that directory.
String expandedIncSymlinkAttr = ruleContext.attributes().get("prefix", Type.STRING);
// We use an additional "_" directory here to avoid conflicts between this and previous Blaze
// versions. Previous Blaze versions created a directory symlink; the new version does not
// detect that the output directory isn't a directory, and tries to put the symlinks into what
// is actually a symlink into the source tree.
PathFragment includeDirectory = PathFragment.create("_")
.getRelative(ruleContext.getTarget().getName());
ArtifactRoot configIncludeDirectory =
ruleContext.getConfiguration().getIncludeDirectory(ruleContext.getRule().getRepository());
PathFragment includePath =
configIncludeDirectory
.getExecPath()
.getRelative(packageFragment)
.getRelative(includeDirectory);
Path includeRoot =
configIncludeDirectory.getRoot().getRelative(packageFragment).getRelative(includeDirectory);
// For every source artifact, we compute a virtual artifact that is below the include directory.
// These are used for include checking.
PathFragment prefixFragment = packageFragment.getRelative(expandedIncSymlinkAttr);
if (!prefixFragment.isNormalized()) {
ruleContext.attributeWarning("prefix", "should not contain '.' or '..' elements");
}
ImmutableSortedMap.Builder<Artifact, Artifact> virtualArtifactMapBuilder =
ImmutableSortedMap.orderedBy(Artifact.EXEC_PATH_COMPARATOR);
prefixFragment = prefixFragment.normalize();
ImmutableList<Artifact> hdrs = ruleContext.getPrerequisiteArtifacts("hdrs", Mode.TARGET).list();
for (Artifact src : hdrs) {
// All declared header files must start with package/targetPrefix.
if (!src.getRootRelativePath().startsWith(prefixFragment)) {
ruleContext.attributeError("hdrs", src + " does not start with '"
+ prefixFragment.getPathString() + "'");
return null;
}
// Remove the targetPrefix from within the exec path of the source file, and prepend the
// unique directory prefix, e.g.:
// third_party/foo/1.2/bar/a.h -> third_party/foo/name/third_party/foo/bar/a.h
PathFragment suffix = src.getRootRelativePath().relativeTo(prefixFragment);
PathFragment virtualPath = includeDirectory.getRelative(packageFragment)
.getRelative(suffix);
// These virtual artifacts have the symlink action as generating action.
Artifact virtualArtifact =
ruleContext.getPackageRelativeArtifact(virtualPath, configIncludeDirectory);
virtualArtifactMapBuilder.put(virtualArtifact, src);
}
ImmutableSortedMap<Artifact, Artifact> virtualArtifactMap = virtualArtifactMapBuilder.build();
ruleContext.registerAction(
new CreateIncSymlinkAction(ruleContext.getActionOwner(), virtualArtifactMap, includeRoot));
FdoSupportProvider fdoSupport =
CppHelper.getFdoSupportUsingDefaultCcToolchainAttribute(ruleContext);
Info.CompilationInfo compilationInfo =
new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
.addIncludeDirs(Arrays.asList(includePath))
.addPublicHeaders(virtualArtifactMap.keySet())
.addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET))
.compile();
Info.LinkingInfo linkingInfo =
new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
.addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET))
.link(
compilationInfo.getCcCompilationOutputs(),
compilationInfo.getCppCompilationContext());
// cc_inc_library doesn't compile any file - no compilation outputs available.
InstrumentedFilesProvider instrumentedFilesProvider =
new CcCommon(ruleContext).getInstrumentedFilesProvider(
new ArrayList<Artifact>(),
/*withBaselineCoverage=*/true);
return new RuleConfiguredTargetBuilder(ruleContext)
.addProviders(compilationInfo.getProviders())
.addProviders(linkingInfo.getProviders())
.addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider())
.addOutputGroups(
Info.mergeOutputGroups(
compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups()))
.add(InstrumentedFilesProvider.class, instrumentedFilesProvider)
.add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY))
.build();
}
}