| // 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(); |
| } |
| } |
| |