/*
* Copyright 2013-present Facebook, Inc.
*
* 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.facebook.buck.cxx;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildRules;
import com.facebook.buck.rules.DependencyAggregation;
import com.facebook.buck.rules.NoopBuildRule;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.coercer.FrameworkPath;
import com.facebook.buck.util.MoreCollectors;
import com.facebook.buck.util.RichStream;
import com.google.common.base.Suppliers;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Map;
import java.util.Optional;
/**
* Represents a precompilable header file, along with dependencies.
*
* <p>Rules which depend on this will inherit this rule's of dependencies. For example if a given
* rule R uses a precompiled header rule P, then all of P's {@code deps} will get merged into R's
* {@code deps} list.
*/
public class CxxPrecompiledHeaderTemplate extends NoopBuildRule
implements NativeLinkable, CxxPreprocessorDep {
private static final Flavor AGGREGATED_PREPROCESS_DEPS_FLAVOR =
InternalFlavor.of("preprocessor-deps");
public final BuildRuleParams params;
public final BuildRuleResolver ruleResolver;
public final SourcePath sourcePath;
public final SourcePathRuleFinder ruleFinder;
public final SourcePathResolver pathResolver;
/** @param buildRuleParams the params for this PCH rule, <b>including</b> {@code deps} */
CxxPrecompiledHeaderTemplate(
BuildRuleParams buildRuleParams, BuildRuleResolver ruleResolver, SourcePath sourcePath) {
super(buildRuleParams);
this.params = buildRuleParams;
this.ruleResolver = ruleResolver;
this.sourcePath = sourcePath;
this.ruleFinder = new SourcePathRuleFinder(ruleResolver);
this.pathResolver = new SourcePathResolver(ruleFinder);
}
private ImmutableSortedSet<BuildRule> getExportedDeps() {
return BuildRules.getExportedRules(getBuildDeps());
}
/**
* Returns our {@link #getBuildDeps()}, limited to the subset of those which are {@link
* NativeLinkable}.
*/
@Override
public Iterable<? extends NativeLinkable> getNativeLinkableDeps() {
return RichStream.from(getBuildDeps()).filter(NativeLinkable.class).toImmutableList();
}
/**
* Returns our {@link #getExportedDeps()}, limited to the subset of those which are {@link
* NativeLinkable}.
*/
@Override
public Iterable<? extends NativeLinkable> getNativeLinkableExportedDeps() {
return RichStream.from(getExportedDeps()).filter(NativeLinkable.class).toImmutableList();
}
/**
* Pick a linkage, any linkage. Just pick your favorite. This will be overridden by config anyway.
*/
@Override
public Linkage getPreferredLinkage(CxxPlatform cxxPlatform) {
return Linkage.SHARED;
}
/** Doesn't really apply to us. No shared libraries to add here. */
@Override
public ImmutableMap<String, SourcePath> getSharedLibraries(CxxPlatform cxxPlatform) {
return ImmutableMap.of();
}
/**
* This class doesn't add any native linkable code of its own, it just has deps which need to be
* passed along and up to the top-level (e.g. a `cxx_binary`) rule. Take all our linkable deps,
* then, and pass it along as our linker input.
*/
@Override
public NativeLinkableInput getNativeLinkableInput(
CxxPlatform cxxPlatform, Linker.LinkableDepType type) throws NoSuchBuildTargetException {
return NativeLinkables.getTransitiveNativeLinkableInput(
cxxPlatform,
getBuildDeps(),
Linker.LinkableDepType.SHARED,
NativeLinkable.class::isInstance);
}
@Override
public Iterable<? extends CxxPreprocessorDep> getCxxPreprocessorDeps(CxxPlatform cxxPlatform) {
return RichStream.from(getBuildDeps()).filter(CxxPreprocessorDep.class).toImmutableList();
}
@Override
public CxxPreprocessorInput getCxxPreprocessorInput(
CxxPlatform cxxPlatform, HeaderVisibility headerVisibility)
throws NoSuchBuildTargetException {
return CxxPreprocessorInput.EMPTY;
}
private final LoadingCache<
CxxPreprocessables.CxxPreprocessorInputCacheKey,
ImmutableMap<BuildTarget, CxxPreprocessorInput>>
transitiveCxxPreprocessorInputCache =
CxxPreprocessables.getTransitiveCxxPreprocessorInputCache(this);
@Override
public ImmutableMap<BuildTarget, CxxPreprocessorInput> getTransitiveCxxPreprocessorInput(
CxxPlatform cxxPlatform, HeaderVisibility headerVisibility)
throws NoSuchBuildTargetException {
return transitiveCxxPreprocessorInputCache.getUnchecked(
ImmutableCxxPreprocessorInputCacheKey.of(cxxPlatform, headerVisibility));
}
private ImmutableList<CxxPreprocessorInput> getCxxPreprocessorInputs(CxxPlatform cxxPlatform) {
ImmutableList.Builder<CxxPreprocessorInput> builder = ImmutableList.builder();
try {
for (Map.Entry<BuildTarget, CxxPreprocessorInput> entry :
getTransitiveCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC).entrySet()) {
builder.add(entry.getValue());
}
} catch (NoSuchBuildTargetException e) {
throw new RuntimeException(e);
}
return builder.build();
}
private ImmutableList<CxxHeaders> getIncludes(CxxPlatform cxxPlatform) {
return getCxxPreprocessorInputs(cxxPlatform)
.stream()
.flatMap(input -> input.getIncludes().stream())
.collect(MoreCollectors.toImmutableList());
}
private ImmutableSet<FrameworkPath> getFrameworks(CxxPlatform cxxPlatform) {
return getCxxPreprocessorInputs(cxxPlatform)
.stream()
.flatMap(input -> input.getFrameworks().stream())
.collect(MoreCollectors.toImmutableSet());
}
private ImmutableSortedSet<BuildRule> getPreprocessDeps(CxxPlatform cxxPlatform) {
ImmutableSortedSet.Builder<BuildRule> builder = ImmutableSortedSet.naturalOrder();
for (CxxPreprocessorInput input : getCxxPreprocessorInputs(cxxPlatform)) {
builder.addAll(input.getDeps(ruleResolver, ruleFinder));
}
for (CxxHeaders cxxHeaders : getIncludes(cxxPlatform)) {
builder.addAll(cxxHeaders.getDeps(ruleFinder));
}
for (FrameworkPath frameworkPath : getFrameworks(cxxPlatform)) {
builder.addAll(frameworkPath.getDeps(ruleFinder));
}
builder.addAll(getBuildDeps());
builder.addAll(getExportedDeps());
return builder.build();
}
private BuildTarget createAggregatedDepsTarget(CxxPlatform cxxPlatform) {
return params
.getBuildTarget()
.withAppendedFlavors(cxxPlatform.getFlavor(), AGGREGATED_PREPROCESS_DEPS_FLAVOR);
}
public DependencyAggregation requireAggregatedDepsRule(CxxPlatform cxxPlatform) {
BuildTarget depAggTarget = createAggregatedDepsTarget(cxxPlatform);
Optional<DependencyAggregation> existingRule =
ruleResolver.getRuleOptionalWithType(depAggTarget, DependencyAggregation.class);
if (existingRule.isPresent()) {
return existingRule.get();
}
BuildRuleParams depAggParams =
params
.withBuildTarget(depAggTarget)
.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(getPreprocessDeps(cxxPlatform)),
Suppliers.ofInstance(ImmutableSortedSet.of()));
DependencyAggregation depAgg = new DependencyAggregation(depAggParams);
ruleResolver.addToIndex(depAgg);
return depAgg;
}
public PreprocessorDelegate buildPreprocessorDelegate(
CxxPlatform cxxPlatform, Preprocessor preprocessor, CxxToolFlags preprocessorFlags) {
try {
return new PreprocessorDelegate(
pathResolver,
cxxPlatform.getCompilerDebugPathSanitizer(),
cxxPlatform.getHeaderVerification(),
params.getProjectFilesystem().getRootPath(),
preprocessor,
PreprocessorFlags.of(
/* getPrefixHeader() */ Optional.empty(),
preprocessorFlags,
getIncludes(cxxPlatform),
getFrameworks(cxxPlatform)),
CxxDescriptionEnhancer.frameworkPathToSearchPath(cxxPlatform, pathResolver),
/* getSandboxTree() */ Optional.empty(),
/* leadingIncludePaths */ Optional.empty());
} catch (PreprocessorDelegate.ConflictingHeadersException e) {
throw new RuntimeException(e);
}
}
}