blob: 4abd25ab9e0e29c08c976bfbdcd65af7416aa8de [file] [log] [blame]
/*
* 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.FlavorDomain;
import com.facebook.buck.model.FlavorDomainException;
import com.facebook.buck.model.Flavored;
import com.facebook.buck.model.ImmutableFlavor;
import com.facebook.buck.model.Pair;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.ImmutableBuildRuleType;
import com.facebook.buck.rules.ImplicitDepsInferringDescription;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SymlinkTree;
import com.facebook.buck.rules.coercer.Either;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.immutables.BuckStyleImmutable;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.immutables.value.Value;
import java.nio.file.Path;
import java.util.Map;
import java.util.Set;
@Value.Nested
public class CxxLibraryDescription implements
Description<CxxLibraryDescription.Arg>,
ImplicitDepsInferringDescription<CxxLibraryDescription.Arg>,
Flavored {
public static enum Type {
HEADERS,
EXPORTED_HEADERS,
SHARED,
STATIC,
}
public static final BuildRuleType TYPE = ImmutableBuildRuleType.of("cxx_library");
private static final FlavorDomain<Type> LIBRARY_TYPE =
new FlavorDomain<>(
"C/C++ Library Type",
ImmutableMap.of(
CxxDescriptionEnhancer.HEADER_SYMLINK_TREE_FLAVOR, Type.HEADERS,
CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR, Type.EXPORTED_HEADERS,
CxxDescriptionEnhancer.SHARED_FLAVOR, Type.SHARED,
CxxDescriptionEnhancer.STATIC_FLAVOR, Type.STATIC));
private final CxxBuckConfig cxxBuckConfig;
private final FlavorDomain<CxxPlatform> cxxPlatforms;
public CxxLibraryDescription(
CxxBuckConfig cxxBuckConfig,
FlavorDomain<CxxPlatform> cxxPlatforms) {
this.cxxBuckConfig = cxxBuckConfig;
this.cxxPlatforms = cxxPlatforms;
}
@Override
public boolean hasFlavors(ImmutableSet<Flavor> flavors) {
return cxxPlatforms.containsAnyOf(flavors);
}
private static final Flavor LEX_YACC_SOURCE_FLAVOR = ImmutableFlavor.of("lex_yacc_sources");
private BuildTarget createLexYaccSourcesBuildTarget(BuildTarget target) {
return BuildTarget.builder(target).addFlavors(LEX_YACC_SOURCE_FLAVOR).build();
}
private CxxHeaderSourceSpec requireLexYaccSources(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
ImmutableMap<String, SourcePath> lexSources,
ImmutableMap<String, SourcePath> yaccSources) {
BuildTarget lexYaccTarget = createLexYaccSourcesBuildTarget(params.getBuildTarget());
// Check the cache...
Optional<BuildRule> rule = ruleResolver.getRuleOptional(lexYaccTarget);
if (rule.isPresent()) {
@SuppressWarnings("unchecked")
ContainerBuildRule<CxxHeaderSourceSpec> containerRule =
(ContainerBuildRule<CxxHeaderSourceSpec>) rule.get();
return containerRule.get();
}
// Setup the rules to run lex/yacc.
CxxHeaderSourceSpec lexYaccSources =
CxxDescriptionEnhancer.createLexYaccBuildRules(
params,
ruleResolver,
cxxPlatform,
ImmutableList.<String>of(),
lexSources,
ImmutableList.<String>of(),
yaccSources);
ruleResolver.addToIndex(
ContainerBuildRule.of(
params,
pathResolver,
lexYaccTarget,
lexYaccSources));
return lexYaccSources;
}
private SymlinkTree createHeaderSymlinkTree(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
boolean includeLexYaccHeaders,
ImmutableMap<String, SourcePath> lexSources,
ImmutableMap<String, SourcePath> yaccSources,
ImmutableMap<Path, SourcePath> headers,
CxxDescriptionEnhancer.HeaderVisibility headerVisibility) {
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
params.getBuildTarget(),
cxxPlatform.getFlavor(),
headerVisibility);
Path headerSymlinkTreeRoot =
CxxDescriptionEnhancer.getHeaderSymlinkTreePath(
params.getBuildTarget(),
cxxPlatform.getFlavor(),
headerVisibility);
CxxHeaderSourceSpec lexYaccSources;
if (includeLexYaccHeaders) {
lexYaccSources = requireLexYaccSources(
params,
ruleResolver,
pathResolver,
cxxPlatform,
lexSources,
yaccSources);
} else {
lexYaccSources = ImmutableCxxHeaderSourceSpec.builder().build();
}
return CxxPreprocessables.createHeaderSymlinkTreeBuildRule(
pathResolver,
headerSymlinkTreeTarget,
params,
headerSymlinkTreeRoot,
ImmutableMap.<Path, SourcePath>builder()
.putAll(headers)
.putAll(lexYaccSources.getCxxHeaders())
.build());
}
/**
* Make sure all build rules needed to generate the headers symlink tree are added to the action
* graph.
*
* @return the {@link com.facebook.buck.rules.SymlinkTree} rule representing the header tree.
*/
private SymlinkTree requireHeaderSymlinkTree(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
boolean includeLexYaccHeaders,
ImmutableMap<String, SourcePath> lexSources,
ImmutableMap<String, SourcePath> yaccSources,
ImmutableMap<Path, SourcePath> headers,
CxxDescriptionEnhancer.HeaderVisibility headerVisibility) {
BuildTarget headerTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
params.getBuildTarget(),
cxxPlatform.getFlavor(),
headerVisibility);
// Check the cache...
Optional<BuildRule> rule = ruleResolver.getRuleOptional(headerTarget);
if (rule.isPresent()) {
return (SymlinkTree) rule.get();
}
SymlinkTree symlinkTree =
createHeaderSymlinkTree(
params,
ruleResolver,
pathResolver,
cxxPlatform,
includeLexYaccHeaders,
lexSources,
yaccSources,
headers,
headerVisibility);
ruleResolver.addToIndex(symlinkTree);
return symlinkTree;
}
private ImmutableList<SourcePath> requireObjects(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
ImmutableMap<String, SourcePath> lexSources,
ImmutableMap<String, SourcePath> yaccSources,
ImmutableMultimap<CxxSource.Type, String> preprocessorFlags,
ImmutableList<SourcePath> prefixHeaders,
ImmutableMap<Path, SourcePath> headers,
ImmutableMap<Path, SourcePath> exportedHeaders,
ImmutableList<String> compilerFlags,
ImmutableMap<String, CxxSource> sources,
ImmutableList<Path> frameworkSearchPaths,
boolean pic) {
CxxHeaderSourceSpec lexYaccSources =
requireLexYaccSources(
params,
ruleResolver,
pathResolver,
cxxPlatform,
lexSources,
yaccSources);
SymlinkTree headerSymlinkTree =
requireHeaderSymlinkTree(
params,
ruleResolver,
pathResolver,
cxxPlatform,
/* includeLexYaccHeaders */ true,
lexSources,
yaccSources,
headers,
CxxDescriptionEnhancer.HeaderVisibility.PRIVATE);
SymlinkTree exportedHeaderSymlinkTree =
requireHeaderSymlinkTree(
params,
ruleResolver,
pathResolver,
cxxPlatform,
/* includeLexYaccHeaders */ false,
ImmutableMap.<String, SourcePath>of(),
ImmutableMap.<String, SourcePath>of(),
exportedHeaders,
CxxDescriptionEnhancer.HeaderVisibility.PUBLIC);
CxxPreprocessorInput cxxPreprocessorInputFromDependencies =
CxxDescriptionEnhancer.combineCxxPreprocessorInput(
params,
cxxPlatform,
preprocessorFlags,
prefixHeaders,
ImmutableList.of(
headerSymlinkTree,
exportedHeaderSymlinkTree),
frameworkSearchPaths);
ImmutableMap<String, CxxSource> allSources =
ImmutableMap.<String, CxxSource>builder()
.putAll(sources)
.putAll(lexYaccSources.getCxxSources())
.build();
ImmutableMap<String, CxxSource> preprocessed =
CxxPreprocessables.createPreprocessBuildRules(
params,
ruleResolver,
cxxPlatform,
cxxPreprocessorInputFromDependencies,
pic,
allSources);
// Create rules for compiling the non-PIC object files.
return CxxDescriptionEnhancer.createCompileBuildRules(
params,
ruleResolver,
cxxPlatform,
compilerFlags,
pic,
preprocessed);
}
/**
* Create all build rules needed to generate the static library.
*
* @return the {@link Archive} rule representing the actual static library.
*/
private Archive createStaticLibrary(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
ImmutableMap<String, SourcePath> lexSources,
ImmutableMap<String, SourcePath> yaccSources,
ImmutableMultimap<CxxSource.Type, String> preprocessorFlags,
ImmutableList<SourcePath> prefixHeaders,
ImmutableMap<Path, SourcePath> headers,
ImmutableMap<Path, SourcePath> exportedHeaders,
ImmutableList<String> compilerFlags,
ImmutableMap<String, CxxSource> sources,
ImmutableList<Path> frameworkSearchPaths) {
// Create rules for compiling the non-PIC object files.
ImmutableList<SourcePath> objects = requireObjects(
params,
ruleResolver,
pathResolver,
cxxPlatform,
lexSources,
yaccSources,
preprocessorFlags,
prefixHeaders,
headers,
exportedHeaders,
compilerFlags,
sources,
frameworkSearchPaths,
/* pic */ false);
// Write a build rule to create the archive for this C/C++ library.
BuildTarget staticTarget =
CxxDescriptionEnhancer.createStaticLibraryBuildTarget(
params.getBuildTarget(),
cxxPlatform.getFlavor());
Path staticLibraryPath =
CxxDescriptionEnhancer.getStaticLibraryPath(
params.getBuildTarget(),
cxxPlatform.getFlavor());
Archive staticLibraryBuildRule = Archives.createArchiveRule(
pathResolver,
staticTarget,
params,
cxxPlatform.getAr(),
staticLibraryPath,
objects);
return staticLibraryBuildRule;
}
/**
* Create all build rules needed to generate the shared library.
*
* @return the {@link CxxLink} rule representing the actual shared library.
*/
private CxxLink createSharedLibrary(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
ImmutableMap<String, SourcePath> lexSources,
ImmutableMap<String, SourcePath> yaccSources,
ImmutableMultimap<CxxSource.Type, String> preprocessorFlags,
ImmutableList<SourcePath> prefixHeaders,
ImmutableMap<Path, SourcePath> headers,
ImmutableMap<Path, SourcePath> exportedHeaders,
ImmutableList<String> compilerFlags,
ImmutableMap<String, CxxSource> sources,
ImmutableList<String> linkerFlags,
ImmutableList<Path> frameworkSearchPaths,
Optional<String> soname) {
// Create rules for compiling the PIC object files.
ImmutableList<SourcePath> objects = requireObjects(
params,
ruleResolver,
pathResolver,
cxxPlatform,
lexSources,
yaccSources,
preprocessorFlags,
prefixHeaders,
headers,
exportedHeaders,
compilerFlags,
sources,
frameworkSearchPaths,
/* pic */ true);
// Setup the rules to link the shared library.
BuildTarget sharedTarget =
CxxDescriptionEnhancer.createSharedLibraryBuildTarget(
params.getBuildTarget(),
cxxPlatform.getFlavor());
String sharedLibrarySoname =
soname.or(
CxxDescriptionEnhancer.getSharedLibrarySoname(params.getBuildTarget(), cxxPlatform));
Path sharedLibraryPath = CxxDescriptionEnhancer.getSharedLibraryPath(
params.getBuildTarget(),
cxxPlatform);
CxxLink sharedLibraryBuildRule =
CxxLinkableEnhancer.createCxxLinkableBuildRule(
cxxPlatform,
params,
pathResolver,
/* extraCxxLdFlags */ ImmutableList.<String>of(),
/* extraLdFlags */ linkerFlags,
sharedTarget,
Linker.LinkType.SHARED,
Optional.of(sharedLibrarySoname),
sharedLibraryPath,
objects,
Linker.LinkableDepType.SHARED,
params.getDeps());
return sharedLibraryBuildRule;
}
@Override
public Arg createUnpopulatedConstructorArg() {
return new Arg();
}
public Arg createEmptyConstructorArg() {
Arg arg = new Arg();
arg.deps = Optional.absent();
arg.srcs = Optional.absent();
arg.prefixHeaders = Optional.of(ImmutableList.<SourcePath>of());
arg.headers = Optional.absent();
arg.exportedHeaders = Optional.absent();
arg.compilerFlags = Optional.absent();
arg.exportedPreprocessorFlags = Optional.absent();
arg.exportedLangPreprocessorFlags = Optional.absent();
arg.preprocessorFlags = Optional.absent();
arg.langPreprocessorFlags = Optional.absent();
arg.linkerFlags = Optional.absent();
arg.platformLinkerFlags = Optional.of(ImmutableList.<Pair<String, ImmutableList<String>>>of());
arg.linkWhole = Optional.absent();
arg.lexSrcs = Optional.absent();
arg.yaccSrcs = Optional.absent();
arg.headerNamespace = Optional.absent();
arg.soname = Optional.absent();
arg.frameworkSearchPaths = Optional.of(ImmutableList.<Path>of());
return arg;
}
/**
* @return a {@link SymlinkTree} for the headers of this C/C++ library.
*/
public <A extends Arg> SymlinkTree createHeaderSymlinkTreeBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
A args) {
return createHeaderSymlinkTree(
params,
resolver,
new SourcePathResolver(resolver),
cxxPlatform,
/* includeLexYaccHeaders */ true,
CxxDescriptionEnhancer.parseLexSources(params, resolver, args),
CxxDescriptionEnhancer.parseYaccSources(params, resolver, args),
CxxDescriptionEnhancer.parseHeaders(params, resolver, args),
CxxDescriptionEnhancer.HeaderVisibility.PRIVATE);
}
/**
* @return a {@link SymlinkTree} for the exported headers of this C/C++ library.
*/
public <A extends Arg> SymlinkTree createExportedHeaderSymlinkTreeBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
A args) {
return createHeaderSymlinkTree(
params,
resolver,
new SourcePathResolver(resolver),
cxxPlatform,
/* includeLexYaccHeaders */ false,
ImmutableMap.<String, SourcePath>of(),
ImmutableMap.<String, SourcePath>of(),
CxxDescriptionEnhancer.parseExportedHeaders(params, resolver, args),
CxxDescriptionEnhancer.HeaderVisibility.PUBLIC);
}
/**
* @return a {@link Archive} rule which builds a static library version of this C/C++ library.
*/
public <A extends Arg> Archive createStaticLibraryBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
A args) {
return createStaticLibrary(
params,
resolver,
new SourcePathResolver(resolver),
cxxPlatform,
CxxDescriptionEnhancer.parseLexSources(params, resolver, args),
CxxDescriptionEnhancer.parseYaccSources(params, resolver, args),
ImmutableMultimap.<CxxSource.Type, String>builder()
.putAll(
CxxPreprocessorFlags.fromArgs(
args.preprocessorFlags,
args.langPreprocessorFlags))
.putAll(
CxxPreprocessorFlags.fromArgs(
args.exportedPreprocessorFlags,
args.exportedLangPreprocessorFlags))
.build(),
args.prefixHeaders.get(),
CxxDescriptionEnhancer.parseHeaders(params, resolver, args),
CxxDescriptionEnhancer.parseExportedHeaders(params, resolver, args),
args.compilerFlags.or(ImmutableList.<String>of()),
CxxDescriptionEnhancer.parseCxxSources(params, resolver, args),
args.frameworkSearchPaths.get());
}
/**
* @return a {@link CxxLink} rule which builds a shared library version of this C/C++ library.
*/
public <A extends Arg> CxxLink createSharedLibraryBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
A args) {
return createSharedLibrary(
params,
resolver,
new SourcePathResolver(resolver),
cxxPlatform,
CxxDescriptionEnhancer.parseLexSources(params, resolver, args),
CxxDescriptionEnhancer.parseYaccSources(params, resolver, args),
ImmutableMultimap.<CxxSource.Type, String>builder()
.putAll(
CxxPreprocessorFlags.fromArgs(
args.preprocessorFlags,
args.langPreprocessorFlags))
.putAll(
CxxPreprocessorFlags.fromArgs(
args.exportedPreprocessorFlags,
args.exportedLangPreprocessorFlags))
.build(),
args.prefixHeaders.get(),
CxxDescriptionEnhancer.parseHeaders(params, resolver, args),
CxxDescriptionEnhancer.parseExportedHeaders(params, resolver, args),
args.compilerFlags.or(ImmutableList.<String>of()),
CxxDescriptionEnhancer.parseCxxSources(params, resolver, args),
ImmutableList.<String>builder()
.addAll(args.linkerFlags.or(ImmutableList.<String>of()))
.addAll(
CxxDescriptionEnhancer.getPlatformFlags(
args.platformLinkerFlags.get(),
cxxPlatform.getFlavor().toString()))
.build(),
args.frameworkSearchPaths.get(),
args.soname);
}
@Value.Immutable
@BuckStyleImmutable
public static interface TypeAndPlatform {
@Value.Parameter
Optional<Map.Entry<Flavor, Type>> getType();
@Value.Parameter
Optional<Map.Entry<Flavor, CxxPlatform>> getPlatform();
}
public static TypeAndPlatform getTypeAndPlatform(
BuildTarget buildTarget,
FlavorDomain<CxxPlatform> platforms) {
// See if we're building a particular "type" and "platform" of this library, and if so, extract
// them from the flavors attached to the build target.
Optional<Map.Entry<Flavor, Type>> type;
Optional<Map.Entry<Flavor, CxxPlatform>> platform;
try {
type = LIBRARY_TYPE.getFlavorAndValue(
ImmutableSet.copyOf(buildTarget.getFlavors()));
platform = platforms.getFlavorAndValue(
ImmutableSet.copyOf(buildTarget.getFlavors()));
} catch (FlavorDomainException e) {
throw new HumanReadableException("%s: %s", buildTarget, e.getMessage());
}
return ImmutableCxxLibraryDescription.TypeAndPlatform.of(type, platform);
}
@Override
public <A extends Arg> BuildRule createBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
A args) {
TypeAndPlatform typeAndPlatform = getTypeAndPlatform(
params.getBuildTarget(),
cxxPlatforms);
return createBuildRule(params, resolver, args, typeAndPlatform);
}
public <A extends Arg> BuildRule createBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
A args,
TypeAndPlatform typeAndPlatform) {
Optional<Map.Entry<Flavor, Type>> type = typeAndPlatform.getType();
Optional<Map.Entry<Flavor, CxxPlatform>> platform = typeAndPlatform.getPlatform();
// If we *are* building a specific type of this lib, call into the type specific
// rule builder methods.
if (type.isPresent() && platform.isPresent()) {
Set<Flavor> flavors = Sets.newHashSet(params.getBuildTarget().getFlavors());
flavors.remove(type.get().getKey());
BuildTarget target = BuildTarget
.builder(params.getBuildTarget().getUnflavoredBuildTarget())
.addAllFlavors(flavors)
.build();
BuildRuleParams typeParams =
params.copyWithChanges(
params.getBuildRuleType(),
target,
Suppliers.ofInstance(params.getDeclaredDeps()),
Suppliers.ofInstance(params.getExtraDeps()));
if (type.get().getValue().equals(Type.HEADERS)) {
return createHeaderSymlinkTreeBuildRule(
typeParams,
resolver,
platform.get().getValue(),
args);
} else if (type.get().getValue().equals(Type.EXPORTED_HEADERS)) {
return createExportedHeaderSymlinkTreeBuildRule(
typeParams,
resolver,
platform.get().getValue(),
args);
} else if (type.get().getValue().equals(Type.SHARED)) {
return createSharedLibraryBuildRule(
typeParams,
resolver,
platform.get().getValue(),
args);
} else {
return createStaticLibraryBuildRule(
typeParams,
resolver,
platform.get().getValue(),
args);
}
}
// Otherwise, we return the generic placeholder of this library, that dependents can use
// get the real build rules via querying the action graph.
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
return new CxxLibrary(
params,
resolver,
pathResolver,
CxxPreprocessorFlags.fromArgs(
args.exportedPreprocessorFlags,
args.exportedLangPreprocessorFlags),
args.linkerFlags.or(ImmutableList.<String>of()),
args.platformLinkerFlags.get(),
args.linkWhole.or(false),
args.soname);
}
@Override
public BuildRuleType getBuildRuleType() {
return TYPE;
}
@Override
public Iterable<BuildTarget> findDepsForTargetFromConstructorArgs(
BuildTarget buildTarget,
Arg constructorArg) {
ImmutableSet.Builder<BuildTarget> deps = ImmutableSet.builder();
if (constructorArg.lexSrcs.isPresent() && !constructorArg.lexSrcs.get().isEmpty()) {
deps.add(cxxBuckConfig.getLexDep());
}
return deps.build();
}
@SuppressFieldNotInitialized
public static class Arg extends CxxConstructorArg {
public Optional<Either<ImmutableList<SourcePath>, ImmutableMap<String, SourcePath>>>
exportedHeaders;
public Optional<ImmutableList<String>> exportedPreprocessorFlags;
public Optional<ImmutableMap<CxxSource.Type, ImmutableList<String>>>
exportedLangPreprocessorFlags;
public Optional<String> soname;
public Optional<Boolean> linkWhole;
}
}