in src/com/facebook/buck/cxx/PrebuiltCxxLibraryDescription.java [377:957]
public BuildRule createBuildRule(
BuildRuleCreationContextWithTargetGraph context,
BuildTarget buildTarget,
BuildRuleParams params,
PrebuiltCxxLibraryDescriptionArg args) {
// See if we're building a particular "type" of this library, and if so, extract
// it as an enum.
Optional<Map.Entry<Flavor, Type>> type = LIBRARY_TYPE.getFlavorAndValue(buildTarget);
Optional<Map.Entry<Flavor, UnresolvedCxxPlatform>> platform =
getUnresolvedCxxPlatform(buildTarget);
Optional<ImmutableMap<BuildTarget, Version>> selectedVersions =
context.getTargetGraph().get(buildTarget).getSelectedVersions();
ProjectFilesystem projectFilesystem = context.getProjectFilesystem();
CellPathResolver cellRoots = context.getCellPathResolver();
ActionGraphBuilder graphBuilder = context.getActionGraphBuilder();
// If we *are* building a specific type of this lib, call into the type specific
// rule builder methods. Currently, we only support building a shared lib from the
// pre-existing static lib, which we do here.
if (type.isPresent()) {
Preconditions.checkState(platform.isPresent());
BuildTarget baseTarget =
buildTarget.withoutFlavors(type.get().getKey(), platform.get().getKey());
CxxPlatform cxxPlatform =
platform.get().getValue().resolve(graphBuilder, buildTarget.getTargetConfiguration());
if (type.get().getValue() == Type.EXPORTED_HEADERS) {
return createExportedHeaderSymlinkTreeBuildRule(
buildTarget, projectFilesystem, graphBuilder, cxxPlatform, args);
} else if (type.get().getValue() == Type.SHARED) {
return createSharedLibraryBuildRule(
buildTarget,
projectFilesystem,
graphBuilder,
cellRoots,
cxxPlatform,
selectedVersions,
args);
} else if (type.get().getValue() == Type.SHARED_INTERFACE) {
return createSharedLibraryInterface(
baseTarget,
projectFilesystem,
graphBuilder,
cellRoots,
cxxPlatform,
selectedVersions,
args);
}
}
// Build up complete list of exported preprocessor flags (including versioned flags).
ImmutableList.Builder<StringWithMacros> exportedPreprocessorFlagsBuilder =
ImmutableList.builder();
exportedPreprocessorFlagsBuilder.addAll(args.getExportedPreprocessorFlags());
selectedVersions.ifPresent(
versions ->
args.getVersionedExportedPreprocessorFlags()
.getMatchingValues(versions)
.forEach(exportedPreprocessorFlagsBuilder::addAll));
ImmutableList<StringWithMacros> exportedPreprocessorFlags =
exportedPreprocessorFlagsBuilder.build();
// Build up complete list of exported platform preprocessor flags (including versioned flags).
PatternMatchedCollection.Builder<ImmutableList<StringWithMacros>>
exportedPlatformPreprocessorFlagsBuilder = PatternMatchedCollection.builder();
args.getExportedPlatformPreprocessorFlags()
.getPatternsAndValues()
.forEach(
pair ->
exportedPlatformPreprocessorFlagsBuilder.add(pair.getFirst(), pair.getSecond()));
selectedVersions.ifPresent(
versions ->
args.getVersionedExportedPlatformPreprocessorFlags()
.getMatchingValues(versions)
.forEach(
flags ->
flags
.getPatternsAndValues()
.forEach(
pair ->
exportedPlatformPreprocessorFlagsBuilder.add(
pair.getFirst(), pair.getSecond()))));
PatternMatchedCollection<ImmutableList<StringWithMacros>> exportedPlatformPreprocessorFlags =
exportedPlatformPreprocessorFlagsBuilder.build();
// Build up complete list of exported language preprocessor flags (including versioned flags).
ImmutableListMultimap.Builder<CxxSource.Type, StringWithMacros>
exportedLangPreprocessorFlagsBuilder = ImmutableListMultimap.builder();
args.getExportedLangPreprocessorFlags().forEach(exportedLangPreprocessorFlagsBuilder::putAll);
selectedVersions.ifPresent(
versions ->
args.getVersionedExportedLangPreprocessorFlags()
.getMatchingValues(versions)
.forEach(value -> value.forEach(exportedLangPreprocessorFlagsBuilder::putAll)));
ImmutableMap<CxxSource.Type, Collection<StringWithMacros>> exportedLangPreprocessorFlags =
exportedLangPreprocessorFlagsBuilder.build().asMap();
// Build up complete list of exported language-platform preprocessor flags (including versioned
// flags).
ListMultimap<CxxSource.Type, PatternMatchedCollection<ImmutableList<StringWithMacros>>>
exportedLangPlatformPreprocessorFlagsBuilder = ArrayListMultimap.create();
args.getExportedLangPlatformPreprocessorFlags()
.forEach(exportedLangPlatformPreprocessorFlagsBuilder::put);
selectedVersions.ifPresent(
versions ->
args.getVersionedExportedLangPlatformPreprocessorFlags()
.getMatchingValues(versions)
.forEach(
value -> value.forEach(exportedLangPlatformPreprocessorFlagsBuilder::put)));
ImmutableMap<CxxSource.Type, PatternMatchedCollection<ImmutableList<StringWithMacros>>>
exportedLangPlatformPreprocessorFlags =
ImmutableMap.copyOf(
Maps.transformValues(
exportedLangPlatformPreprocessorFlagsBuilder.asMap(),
PatternMatchedCollection::concat));
// Otherwise, we return the generic placeholder of this library, that dependents can use
// get the real build rules via querying the action graph.
PrebuiltCxxLibraryPaths paths = getPaths(buildTarget, args);
return new PrebuiltCxxLibrary(buildTarget, projectFilesystem, params) {
private final TransitiveCxxPreprocessorInputCache transitiveCxxPreprocessorInputCache =
new TransitiveCxxPreprocessorInputCache(this);
private boolean hasHeaders(CxxPlatform cxxPlatform) {
if (!args.getExportedHeaders().isEmpty()) {
return true;
}
for (SourceSortedSet sourceList :
args.getExportedPlatformHeaders()
.getMatchingValues(cxxPlatform.getFlavor().toString())) {
if (!sourceList.isEmpty()) {
return true;
}
}
return false;
}
private ImmutableListMultimap<CxxSource.Type, Arg> getExportedPreprocessorFlags(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
return ImmutableListMultimap.copyOf(
Multimaps.transformValues(
CxxFlags.getLanguageFlagsWithMacros(
exportedPreprocessorFlags,
exportedPlatformPreprocessorFlags,
exportedLangPreprocessorFlags,
exportedLangPlatformPreprocessorFlags,
cxxPlatform),
CxxDescriptionEnhancer.getStringWithMacrosArgsConverter(
getBuildTarget(), cellRoots, graphBuilder, cxxPlatform)
::convert));
}
private String getSoname(CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
Optional<String> soname = args.getSoname();
if (!soname.isPresent()
&& cxxPlatform.getLd().getType() == LinkerProvider.Type.WINDOWS
&& getImportLibrary(cxxPlatform, graphBuilder).isPresent()) {
// TODO(fishb): We should allow `shared_lib` to be absent (ex. link against import lib
// of pre-deployed DLL)
SourcePath sharedLibraryPath = requireSharedLibrary(cxxPlatform, false, graphBuilder);
soname =
Optional.ofNullable(
((PathSourcePath) sharedLibraryPath).getRelativePath().getFileName().toString());
}
return PrebuiltCxxLibraryDescription.getSoname(getBuildTarget(), cxxPlatform, soname);
}
private boolean isPlatformSupported(CxxPlatform cxxPlatform) {
return !args.getSupportedPlatformsRegex().isPresent()
|| args.getSupportedPlatformsRegex()
.get()
.matcher(cxxPlatform.getFlavor().toString())
.find();
}
private Optional<SourcePath> getImportLibrary(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
return PrebuiltCxxLibraryDescription.this.getImportLibrary(
buildTarget,
graphBuilder,
cellRoots,
projectFilesystem,
cxxPlatform,
selectedVersions,
args);
}
/**
* Makes sure all build rules needed to produce the shared library are added to the action
* graph.
*
* @return the {@link SourcePath} representing the actual shared library.
*/
private SourcePath requireSharedLibrary(
CxxPlatform cxxPlatform, boolean link, ActionGraphBuilder graphBuilder) {
if (link
&& args.isSupportsSharedLibraryInterface()
&& cxxPlatform.getSharedLibraryInterfaceParams().isPresent()) {
BuildTarget target =
buildTarget.withAppendedFlavors(
cxxPlatform.getFlavor(), Type.SHARED_INTERFACE.getFlavor());
BuildRule rule = graphBuilder.requireRule(target);
return Objects.requireNonNull(rule.getSourcePathToOutput());
}
return PrebuiltCxxLibraryDescription.this.requireSharedLibrary(
buildTarget,
graphBuilder,
cellRoots,
projectFilesystem,
cxxPlatform,
selectedVersions,
args);
}
/**
* @return the {@link Optional} containing a {@link SourcePath} representing the actual static
* library.
*/
@Override
Optional<SourcePath> getStaticLibrary(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
return getStaticLibraryForSelectedVersions(cxxPlatform, graphBuilder, selectedVersions);
}
private Optional<SourcePath> getStaticLibraryForSelectedVersions(
CxxPlatform cxxPlatform,
ActionGraphBuilder graphBuilder,
Optional<ImmutableMap<BuildTarget, Version>> versions) {
return paths.getStaticLibrary(
getProjectFilesystem(), graphBuilder, cellRoots, cxxPlatform, versions);
}
/**
* @return the {@link Optional} containing a {@link SourcePath} representing the actual static
* PIC library.
*/
@Override
Optional<SourcePath> getStaticPicLibrary(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
return getStaticPicLibraryForSelectedVersions(cxxPlatform, graphBuilder, selectedVersions);
}
private Optional<SourcePath> getStaticPicLibraryForSelectedVersions(
CxxPlatform cxxPlatform,
ActionGraphBuilder graphBuilder,
Optional<ImmutableMap<BuildTarget, Version>> versions) {
Optional<SourcePath> staticPicLibraryPath =
paths.getStaticPicLibrary(
getProjectFilesystem(), graphBuilder, cellRoots, cxxPlatform, versions);
// If a specific static-pic variant isn't available, then just use the static variant.
return staticPicLibraryPath.isPresent()
? staticPicLibraryPath
: getStaticLibraryForSelectedVersions(cxxPlatform, graphBuilder, versions);
}
@Override
public Iterable<CxxPreprocessorDep> getCxxPreprocessorDeps(
CxxPlatform cxxPlatform, BuildRuleResolver ruleResolver) {
if (!isPlatformSupported(cxxPlatform)) {
return ImmutableList.of();
}
return args.getCxxDeps().get(ruleResolver, cxxPlatform).stream()
.filter(CxxPreprocessorDep.class::isInstance)
.map(CxxPreprocessorDep.class::cast)
.collect(ImmutableList.toImmutableList());
}
@Override
public CxxPreprocessorInput getCxxPreprocessorInput(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
CxxPreprocessorInput.Builder builder = CxxPreprocessorInput.builder();
if (hasHeaders(cxxPlatform)) {
CxxPreprocessables.addHeaderSymlinkTree(
builder,
getBuildTarget(),
graphBuilder,
cxxPlatform,
HeaderVisibility.PUBLIC,
CxxPreprocessables.IncludeType.SYSTEM);
}
builder.putAllPreprocessorFlags(getExportedPreprocessorFlags(cxxPlatform, graphBuilder));
builder.addAllFrameworks(args.getFrameworks());
for (SourcePath includePath :
paths.getIncludeDirs(
projectFilesystem, graphBuilder, cellRoots, cxxPlatform, selectedVersions)) {
builder.addIncludes(CxxHeadersDir.of(CxxPreprocessables.IncludeType.SYSTEM, includePath));
}
return builder.build();
}
@Override
public ImmutableMap<BuildTarget, CxxPreprocessorInput> getTransitiveCxxPreprocessorInput(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
return transitiveCxxPreprocessorInputCache.getUnchecked(cxxPlatform, graphBuilder);
}
public ImmutableList<Arg> getExportedLinkerFlags(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
return PrebuiltCxxLibraryDescription.this.getExportedLinkerArgs(
cxxPlatform, args, buildTarget, cellRoots, graphBuilder);
}
public ImmutableList<Arg> getExportedPostLinkerFlags(CxxPlatform cxxPlatform) {
// TODO(cjhopman): Why wasn't this updated to handle macros correctly like
// exported_linker_flags?
return CxxFlags.getFlagsWithPlatformMacroExpansion(
args.getExportedPostLinkerFlags(),
args.getExportedPostPlatformLinkerFlags(),
cxxPlatform)
.stream()
.map(s -> (Arg) StringArg.from(s))
.collect(ImmutableList.toImmutableList());
}
@Override
protected NativeLinkableInfo createNativeLinkable(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
if (!isPlatformSupported(cxxPlatform)) {
return new NativeLinkableInfo(
getBuildTarget(),
getType(),
ImmutableList.of(),
ImmutableList.of(),
Linkage.ANY,
NativeLinkableInfo.fixedDelegate(NativeLinkableInput.of(), ImmutableMap.of()),
NativeLinkableInfo.defaults());
}
ImmutableList<NativeLinkable> deps =
args.getPrivateCxxDeps().get(graphBuilder, cxxPlatform).stream()
.filter(NativeLinkableGroup.class::isInstance)
.map(NativeLinkableGroup.class::cast)
.map(g -> g.getNativeLinkable(cxxPlatform, graphBuilder))
.collect(ImmutableList.toImmutableList());
ImmutableList<NativeLinkable> exportedDeps =
args.getExportedCxxDeps().get(graphBuilder, cxxPlatform).stream()
.filter(NativeLinkableGroup.class::isInstance)
.map(NativeLinkableGroup.class::cast)
.map(g -> g.getNativeLinkable(cxxPlatform, graphBuilder))
.collect(ImmutableList.toImmutableList());
ImmutableList<Arg> exportedLinkerFlags = getExportedLinkerFlags(cxxPlatform, graphBuilder);
ImmutableList<Arg> exportedPostLinkerFlags = getExportedPostLinkerFlags(cxxPlatform);
Optional<NativeLinkTargetInfo> linkTargetInfo =
getNativeLinkTargetInfo(
cxxPlatform,
graphBuilder,
deps,
exportedDeps,
exportedLinkerFlags,
exportedPostLinkerFlags);
return new NativeLinkableInfo(
getBuildTarget(),
getType(),
deps,
exportedDeps,
getPreferredLinkage(cxxPlatform),
new NativeLinkableInfo.Delegate() {
@Override
public NativeLinkableInput computeInput(
ActionGraphBuilder graphBuilder,
Linker.LinkableDepType type,
boolean forceLinkWhole,
TargetConfiguration targetConfiguration) {
return computeNativeLinkableInputUncached(
graphBuilder, cxxPlatform, type, forceLinkWhole);
}
@Override
public ImmutableMap<String, SourcePath> getSharedLibraries(
ActionGraphBuilder graphBuilder) {
return resolveSharedLibraries(cxxPlatform, graphBuilder);
}
@Override
public boolean isPrebuiltSOForHaskellOmnibus(ActionGraphBuilder graphBuilder) {
ImmutableMap<String, SourcePath> sharedLibraries = getSharedLibraries(graphBuilder);
for (Map.Entry<String, SourcePath> ent : sharedLibraries.entrySet()) {
if (!(ent.getValue() instanceof PathSourcePath)) {
return false;
}
}
return true;
}
},
NativeLinkableInfo.defaults()
.setExportedLinkerFlags(exportedLinkerFlags)
.setExportedPostLinkerFlags(exportedPostLinkerFlags)
.setSupportsOmnibusLinking(supportsOmnibusLinking(cxxPlatform))
.setHaskellOmnibusLinkingOptions(true, true)
.setNativeLinkTarget(linkTargetInfo));
}
private NativeLinkableInput computeNativeLinkableInputUncached(
ActionGraphBuilder graphBuilder,
CxxPlatform cxxPlatform,
Linker.LinkableDepType type,
boolean forceLinkWhole) {
Verify.verify(isPlatformSupported(cxxPlatform));
NativeLinkable linkable = getNativeLinkable(cxxPlatform, graphBuilder);
// Build the library path and linker arguments that we pass through the
// {@link NativeLinkable} interface for linking.
ImmutableList.Builder<Arg> linkerArgsBuilder = ImmutableList.builder();
linkerArgsBuilder.addAll(linkable.getExportedLinkerFlags(graphBuilder));
if (!args.isHeaderOnly()) {
if (type == Linker.LinkableDepType.SHARED) {
Preconditions.checkState(linkable.getPreferredLinkage() != Linkage.STATIC);
Optional<SourcePath> importLibraryPath = getImportLibrary(cxxPlatform, graphBuilder);
if (importLibraryPath.isPresent()
&& cxxPlatform.getLd().getType() == LinkerProvider.Type.WINDOWS) {
SourcePathArg importLibrary =
SourcePathArg.of(
importLibraryPath.orElseThrow(
() ->
new HumanReadableException(
"Could not find import library for %s.", getBuildTarget())));
if (args.isLinkWhole() || forceLinkWhole) {
throw new HumanReadableException(
"%s: whole linking is not supported for import libraries", getBuildTarget());
} else {
linkerArgsBuilder.add(FileListableLinkerInputArg.withSourcePathArg(importLibrary));
}
} else {
SourcePath sharedLibrary = requireSharedLibrary(cxxPlatform, true, graphBuilder);
if (args.getLinkWithoutSoname()) {
if (!(sharedLibrary instanceof PathSourcePath)) {
throw new HumanReadableException(
"%s: can only link prebuilt DSOs without sonames", getBuildTarget());
}
linkerArgsBuilder.add(new RelativeLinkArg((PathSourcePath) sharedLibrary));
} else {
linkerArgsBuilder.add(
SourcePathArg.of(requireSharedLibrary(cxxPlatform, true, graphBuilder)));
}
}
} else {
Preconditions.checkState(linkable.getPreferredLinkage() != Linkage.SHARED);
Optional<SourcePath> staticLibraryPath =
type == Linker.LinkableDepType.STATIC_PIC
? getStaticPicLibrary(cxxPlatform, graphBuilder)
: getStaticLibrary(cxxPlatform, graphBuilder);
SourcePathArg staticLibrary =
SourcePathArg.of(
staticLibraryPath.orElseThrow(
() ->
new HumanReadableException(
"Could not find static library for %s.", getBuildTarget())));
if (args.isLinkWhole() || forceLinkWhole) {
Linker linker =
cxxPlatform.getLd().resolve(graphBuilder, buildTarget.getTargetConfiguration());
linkerArgsBuilder.addAll(
linker.linkWhole(
staticLibrary, context.getActionGraphBuilder().getSourcePathResolver()));
} else {
linkerArgsBuilder.add(FileListableLinkerInputArg.withSourcePathArg(staticLibrary));
}
}
}
// Add any post exported flags, if any.
linkerArgsBuilder.addAll(linkable.getExportedPostLinkerFlags(graphBuilder));
ImmutableList<Arg> linkerArgs = linkerArgsBuilder.build();
return NativeLinkableInput.of(linkerArgs, args.getFrameworks(), args.getLibraries());
}
public Linkage getPreferredLinkage(CxxPlatform cxxPlatform) {
if (args.isHeaderOnly()) {
return Linkage.ANY;
}
if (cxxPlatform.getLd().getType() == LinkerProvider.Type.WINDOWS) {
Optional<SourcePath> importLibraryPath = getImportLibrary(cxxPlatform, graphBuilder);
if (importLibraryPath.isPresent()) {
return Linkage.SHARED;
}
}
if (args.isForceStatic()) {
return Linkage.STATIC;
}
if (args.getPreferredLinkage().orElse(Linkage.ANY) != Linkage.ANY) {
return args.getPreferredLinkage().get();
}
if (args.isProvided()) {
return Linkage.SHARED;
}
Optional<Linkage> inferredLinkage =
paths.getLinkage(projectFilesystem, cellRoots, cxxPlatform);
return inferredLinkage.orElse(Linkage.ANY);
}
public boolean supportsOmnibusLinking(CxxPlatform cxxPlatform) {
return args.getSupportsMergedLinking()
.orElse(getPreferredLinkage(cxxPlatform) != Linkage.SHARED);
}
@Override
public Iterable<AndroidPackageable> getRequiredPackageables(BuildRuleResolver ruleResolver) {
return AndroidPackageableCollector.getPackageableRules(
args.getCxxDeps().getForAllPlatforms(ruleResolver));
}
@Override
public void addToCollector(AndroidPackageableCollector collector) {
if (args.getCanBeAsset()) {
collector.addNativeLinkableAsset(this);
} else {
collector.addNativeLinkable(this);
}
}
public ImmutableMap<String, SourcePath> resolveSharedLibraries(
CxxPlatform cxxPlatform, ActionGraphBuilder graphBuilder) {
Verify.verify(isPlatformSupported(cxxPlatform));
ImmutableMap.Builder<String, SourcePath> solibs = ImmutableMap.builder();
if (!args.isHeaderOnly() && !args.isProvided()) {
String resolvedSoname = getSoname(cxxPlatform, graphBuilder);
SourcePath sharedLibrary = requireSharedLibrary(cxxPlatform, false, graphBuilder);
solibs.put(resolvedSoname, sharedLibrary);
}
return solibs.build();
}
private Optional<NativeLinkTargetInfo> getNativeLinkTargetInfo(
CxxPlatform cxxPlatform,
ActionGraphBuilder graphBuilder,
ImmutableList<NativeLinkable> deps,
ImmutableList<NativeLinkable> exportedDeps,
ImmutableList<Arg> exportedLinkerFlags,
ImmutableList<Arg> exportedPostLinkerFlags) {
if (getPreferredLinkage(cxxPlatform) == Linkage.SHARED) {
return Optional.empty();
}
if (args.isHeaderOnly()) {
return Optional.empty();
}
Optional<NativeLinkTargetInfo> linkTargetInfo = Optional.empty();
if (getPreferredLinkage(cxxPlatform) != Linkage.SHARED) {
Optional<SourcePath> staticPicLibrary = getStaticPicLibrary(cxxPlatform, graphBuilder);
NativeLinkableInput linkableInput = null;
if (staticPicLibrary.isPresent()) {
linkableInput =
NativeLinkableInput.builder()
.addAllArgs(exportedLinkerFlags)
.addAllArgs(
cxxPlatform
.getLd()
.resolve(graphBuilder, buildTarget.getTargetConfiguration())
.linkWhole(
SourcePathArg.of(staticPicLibrary.get()),
// TODO(cjhopman): We should only require SourcePathResolvers at the
// action execution stage.
graphBuilder.getSourcePathResolver()))
.addAllArgs(exportedPostLinkerFlags)
.build();
}
linkTargetInfo =
Optional.of(
new NativeLinkTargetInfo(
getBuildTarget(),
NativeLinkTargetMode.library(getSoname(cxxPlatform, graphBuilder)),
Iterables.concat(deps, exportedDeps),
linkableInput,
Optional.empty()));
}
return linkTargetInfo;
}
};
}