blob: 7b37f3d40f912cfde24091cddaf060d72a4ec8da [file] [log] [blame]
/*
* Copyright 2014-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.thrift;
import com.facebook.buck.cxx.CxxBuckConfig;
import com.facebook.buck.cxx.CxxCompilables;
import com.facebook.buck.cxx.CxxLibraryDescription;
import com.facebook.buck.cxx.CxxPlatform;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.FlavorDomain;
import com.facebook.buck.model.ImmutableFlavor;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildTargetSourcePath;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.coercer.Either;
import com.facebook.buck.rules.coercer.SourceWithFlags;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.annotations.VisibleForTesting;
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.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.io.Files;
import java.nio.file.Path;
public class ThriftCxxEnhancer implements ThriftLanguageSpecificEnhancer {
private static final Flavor CPP_FLAVOR = ImmutableFlavor.of("cpp");
private static final Flavor CPP2_FLAVOR = ImmutableFlavor.of("cpp2");
private final ThriftBuckConfig thriftBuckConfig;
private final CxxLibraryDescription cxxLibraryDescription;
private final boolean cpp2;
public ThriftCxxEnhancer(
ThriftBuckConfig thriftBuckConfig,
CxxBuckConfig cxxBuckConfig,
FlavorDomain<CxxPlatform> cxxPlatforms,
boolean cpp2) {
this.thriftBuckConfig = thriftBuckConfig;
this.cxxLibraryDescription = new CxxLibraryDescription(cxxBuckConfig, cxxPlatforms);
this.cpp2 = cpp2;
}
@Override
public String getLanguage() {
return cpp2 ? "cpp2" : "cpp";
}
@Override
public Flavor getFlavor() {
return cpp2 ? CPP2_FLAVOR : CPP_FLAVOR;
}
// Return the gen-dir relative sources for the given services and options.
@VisibleForTesting
protected ImmutableList<String> getGeneratedThriftSources(
ImmutableSet<String> options,
String name,
ImmutableList<String> services) {
final String base = Files.getNameWithoutExtension(name);
final boolean bootstrap = options.contains("bootstrap");
final boolean layouts = options.contains("frozen");
final boolean templates = cpp2 || options.contains("templates");
final boolean perfhash = !cpp2 && options.contains("perfhash");
ImmutableList.Builder<String> sources = ImmutableList.builder();
sources.add(base + "_constants.h");
sources.add(base + "_constants.cpp");
sources.add(base + "_types.h");
sources.add(base + "_types.cpp");
if (templates) {
sources.add(base + "_types.tcc");
}
if (layouts) {
sources.add(base + "_layouts.h");
sources.add(base + "_layouts.cpp");
}
if (!bootstrap && !cpp2) {
sources.add(base + "_reflection.h");
sources.add(base + "_reflection.cpp");
}
for (String service : services) {
sources.add(service + ".h");
sources.add(service + ".cpp");
if (templates) {
sources.add(service + ".tcc");
}
if (perfhash) {
sources.add(service + "_gperf.tcc");
}
}
return sources.build();
}
// Find all the generated sources and headers generated by these thrift sources.
private CxxHeadersAndSources getThriftHeaderSourceSpec(
BuildRuleParams params,
ThriftConstructorArg args,
ImmutableMap<String, ThriftSource> sources) {
ImmutableSet<String> options =
(cpp2 ? args.cpp2Options : args.cppOptions).or(ImmutableSet.<String>of());
ImmutableMap.Builder<String, SourceWithFlags> cxxSourcesBuilder = ImmutableMap.builder();
ImmutableMap.Builder<String, SourcePath> headersBuilder = ImmutableMap.builder();
for (ImmutableMap.Entry<String, ThriftSource> ent : sources.entrySet()) {
final String thriftName = ent.getKey();
final ThriftSource source = ent.getValue();
final Path outputDir = source.getOutputDir();
for (String partialName : getGeneratedThriftSources(
options,
thriftName,
source.getServices())) {
String name = String.format("gen-%s/%s", getLanguage(), partialName);
String extension = Files.getFileExtension(name);
if (CxxCompilables.SOURCE_EXTENSIONS.contains(extension)) {
cxxSourcesBuilder.put(
name,
SourceWithFlags.of(
new BuildTargetSourcePath(
source.getCompileRule().getProjectFilesystem(),
source.getCompileRule().getBuildTarget(),
outputDir.resolve(name))));
} else if (CxxCompilables.HEADER_EXTENSIONS.contains(extension)) {
headersBuilder.put(
name,
new BuildTargetSourcePath(
source.getCompileRule().getProjectFilesystem(),
source.getCompileRule().getBuildTarget(),
outputDir.resolve(name)));
} else {
throw new HumanReadableException(String.format(
"%s: unexpected extension for \"%s\"",
params.getBuildTarget(),
name));
}
}
}
return new CxxHeadersAndSources(
headersBuilder.build(),
cxxSourcesBuilder.build());
}
@Override
public BuildRule createBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
ThriftConstructorArg args,
ImmutableMap<String, ThriftSource> sources,
ImmutableSortedSet<BuildRule> deps) {
// Grab all the sources and headers generated from the passed in thrift sources.
CxxHeadersAndSources spec = getThriftHeaderSourceSpec(params, args, sources);
// Add all the passed in language-specific thrift deps, and any C/C++ specific deps
// passed in via the constructor arg.
ImmutableSortedSet<BuildRule> allDeps =
ImmutableSortedSet.<BuildRule>naturalOrder()
.addAll(deps)
.addAll(
resolver.getAllRules(
(cpp2 ? args.cpp2Deps : args.cppDeps).or(ImmutableSortedSet.<BuildTarget>of())))
.build();
// Create language specific build params by using the deps we formed above.
BuildRuleParams langParams = params.copyWithDeps(
Suppliers.ofInstance(allDeps),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));
// Construct the C/C++ library description argument to pass to the
CxxLibraryDescription.Arg langArgs = cxxLibraryDescription.createEmptyConstructorArg();
langArgs.srcs =
Optional.of(
Either.<ImmutableList<SourceWithFlags>, ImmutableMap<String, SourceWithFlags>>ofRight(
spec.getSources()));
langArgs.exportedHeaders =
Optional.of(
Either.<ImmutableList<SourcePath>, ImmutableMap<String, SourcePath>>ofRight(
spec.getHeaders()));
return cxxLibraryDescription.createBuildRule(langParams, resolver, langArgs);
}
private ImmutableSet<BuildTarget> getImplicitDepsFromOptions(
BuildTarget target,
ImmutableSet<String> options) {
ImmutableSet.Builder<BuildTarget> implicitDeps = ImmutableSet.builder();
if (!options.contains("bootstrap")) {
if (cpp2) {
implicitDeps.add(thriftBuckConfig.getCpp2Dep());
} else {
implicitDeps.add(thriftBuckConfig.getCppDep());
}
implicitDeps.add(thriftBuckConfig.getCppReflectionDep());
}
if (options.contains("frozen2")) {
implicitDeps.add(thriftBuckConfig.getCppFrozenDep());
}
if (options.contains("json")) {
implicitDeps.add(thriftBuckConfig.getCppJsonDep());
}
if (cpp2 && options.contains("compatibility")) {
implicitDeps.add(thriftBuckConfig.getCppDep());
BuildTarget cppTarget = BuildTargets.createFlavoredBuildTarget(
target.getUnflavoredBuildTarget(),
CPP_FLAVOR);
implicitDeps.add(cppTarget);
}
if (!cpp2 && options.contains("cob_style")) {
implicitDeps.add(thriftBuckConfig.getCppAyncDep());
}
return implicitDeps.build();
}
@Override
public ImmutableSet<BuildTarget> getImplicitDepsForTargetFromConstructorArg(
BuildTarget target,
ThriftConstructorArg arg) {
Optional<ImmutableSet<String>> options = cpp2 ? arg.cpp2Options : arg.cppOptions;
return getImplicitDepsFromOptions(target, options.or(ImmutableSet.<String>of()));
}
@Override
public ImmutableSet<String> getOptions(
BuildTarget target,
ThriftConstructorArg args) {
return ImmutableSet.<String>builder()
.add(String.format("include_prefix=%s", target.getBasePath()))
.addAll((cpp2 ? args.cpp2Options : args.cppOptions).or(ImmutableSet.<String>of()))
.build();
}
private static class CxxHeadersAndSources {
private final ImmutableMap<String, SourcePath> headers;
private final ImmutableMap<String, SourceWithFlags> sources;
public CxxHeadersAndSources(
ImmutableMap<String, SourcePath> headers,
ImmutableMap<String, SourceWithFlags> sources) {
this.headers = headers;
this.sources = sources;
}
public ImmutableMap<String, SourcePath> getHeaders() {
return headers;
}
public ImmutableMap<String, SourceWithFlags> getSources() {
return sources;
}
}
}