blob: 82df1bfef64f273e5179f2c26a6224347d7e07a6 [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.io.MorePaths;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.HasBuildTarget;
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.BuildTargetSourcePath;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.ImmutableBuildRuleType;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePaths;
import com.facebook.buck.rules.SymlinkTree;
import com.facebook.buck.rules.TargetNode;
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.Function;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CxxDescriptionEnhancer {
public static final Flavor HEADER_SYMLINK_TREE_FLAVOR = ImmutableFlavor.of("header-symlink-tree");
public static final Flavor EXPORTED_HEADER_SYMLINK_TREE_FLAVOR =
ImmutableFlavor.of("exported-header-symlink-tree");
public static final Flavor STATIC_FLAVOR = ImmutableFlavor.of("static");
public static final Flavor SHARED_FLAVOR = ImmutableFlavor.of("shared");
public static final Flavor CXX_LINK_BINARY_FLAVOR = ImmutableFlavor.of("binary");
public static final BuildRuleType LEX_TYPE = ImmutableBuildRuleType.of("lex");
public static final BuildRuleType YACC_TYPE = ImmutableBuildRuleType.of("yacc");
public static enum HeaderVisibility {
PUBLIC,
PRIVATE,
}
private CxxDescriptionEnhancer() {}
/**
* @return the {@link BuildTarget} to use for the {@link BuildRule} generating the
* symlink tree of headers.
*/
public static BuildTarget createHeaderSymlinkTreeTarget(
BuildTarget target,
Flavor platform,
HeaderVisibility headerVisibility) {
return BuildTarget
.builder(target)
.addFlavors(platform)
.addFlavors(getHeaderSymlinkTreeFlavor(headerVisibility))
.build();
}
/**
* @return the {@link Path} to use for the symlink tree of headers.
*/
public static Path getHeaderSymlinkTreePath(
BuildTarget target,
Flavor platform,
HeaderVisibility headerVisibility) {
return BuildTargets.getGenPath(
createHeaderSymlinkTreeTarget(target, platform, headerVisibility),
"%s");
}
public static Flavor getHeaderSymlinkTreeFlavor(HeaderVisibility headerVisibility) {
switch (headerVisibility) {
case PUBLIC:
return EXPORTED_HEADER_SYMLINK_TREE_FLAVOR;
case PRIVATE:
return HEADER_SYMLINK_TREE_FLAVOR;
default:
throw new RuntimeException("Unexpected value of enum ExportMode");
}
}
private static ImmutableMap<Path, SourcePath> getHeaderMapFromArgParameter(
SourcePathResolver pathResolver,
BuildTarget buildTarget,
Optional<String> headerNamespace,
String parameterName,
Optional<Either<ImmutableList<SourcePath>, ImmutableMap<String, SourcePath>>> parameter) {
ImmutableMap<String, SourcePath> headers;
if (!parameter.isPresent()) {
headers = ImmutableMap.of();
} else if (parameter.get().isRight()) {
headers = parameter.get().getRight();
} else {
headers = pathResolver.getSourcePathNames(
buildTarget,
parameterName,
parameter.get().getLeft());
}
return CxxPreprocessables.resolveHeaderMap(
headerNamespace.transform(MorePaths.TO_PATH)
.or(buildTarget.getBasePath()),
headers);
}
/**
* @return a map of header locations to input {@link SourcePath} objects formed by parsing the
* input {@link SourcePath} objects for the "headers" parameter.
*/
public static ImmutableMap<Path, SourcePath> parseHeaders(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxConstructorArg args) {
return getHeaderMapFromArgParameter(
new SourcePathResolver(resolver),
params.getBuildTarget(),
args.headerNamespace,
"headers",
args.headers);
}
/**
* @return a map of header locations to input {@link SourcePath} objects formed by parsing the
* input {@link SourcePath} objects for the "exportedHeaders" parameter.
*/
public static ImmutableMap<Path, SourcePath> parseExportedHeaders(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxLibraryDescription.Arg args) {
return getHeaderMapFromArgParameter(
new SourcePathResolver(resolver),
params.getBuildTarget(),
args.headerNamespace,
"exportedHeaders",
args.exportedHeaders);
}
/**
* @return a list {@link CxxSource} objects formed by parsing the input {@link SourcePath}
* objects for the "srcs" parameter.
*/
public static ImmutableMap<String, CxxSource> parseCxxSources(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxConstructorArg args) {
ImmutableMap<String, SourceWithFlags> sources;
if (!args.srcs.isPresent()) {
sources = ImmutableMap.of();
} else if (args.srcs.get().isRight()) {
sources = args.srcs.get().getRight();
} else {
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
sources = pathResolver.getSourcePathNames(
params.getBuildTarget(),
"srcs",
args.srcs.get().getLeft(),
SourceWithFlags.TO_SOURCE_PATH);
}
return CxxCompilableEnhancer.resolveCxxSources(sources);
}
public static ImmutableMap<String, SourcePath> parseLexSources(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxConstructorArg args) {
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
return pathResolver.getSourcePathNames(
params.getBuildTarget(),
"lexSrcs",
args.lexSrcs.or(ImmutableList.<SourcePath>of()));
}
public static ImmutableMap<String, SourcePath> parseYaccSources(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxConstructorArg args) {
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
return pathResolver.getSourcePathNames(
params.getBuildTarget(),
"yaccSrcs",
args.yaccSrcs.or(ImmutableList.<SourcePath>of()));
}
@VisibleForTesting
protected static BuildTarget createLexBuildTarget(BuildTarget target, String name) {
return BuildTarget
.builder(target.getUnflavoredBuildTarget())
.addFlavors(
ImmutableFlavor.of(
String.format(
"lex-%s",
name.replace('/', '-').replace('.', '-').replace('+', '-').replace(' ', '-'))))
.build();
}
@VisibleForTesting
protected static BuildTarget createYaccBuildTarget(BuildTarget target, String name) {
return BuildTarget
.builder(target.getUnflavoredBuildTarget())
.addFlavors(
ImmutableFlavor.of(
String.format(
"yacc-%s",
name.replace('/', '-').replace('.', '-').replace('+', '-').replace(' ', '-'))))
.build();
}
/**
* @return the output path prefix to use for yacc generated files.
*/
@VisibleForTesting
protected static Path getYaccOutputPrefix(BuildTarget target, String name) {
BuildTarget flavoredTarget = createYaccBuildTarget(target, name);
return BuildTargets.getGenPath(flavoredTarget, "%s/" + name);
}
/**
* @return the output path to use for the lex generated C/C++ source.
*/
@VisibleForTesting
protected static Path getLexSourceOutputPath(BuildTarget target, String name) {
BuildTarget flavoredTarget = createLexBuildTarget(target, name);
return BuildTargets.getGenPath(flavoredTarget, "%s/" + name + ".cc");
}
/**
* @return the output path to use for the lex generated C/C++ header.
*/
@VisibleForTesting
protected static Path getLexHeaderOutputPath(BuildTarget target, String name) {
BuildTarget flavoredTarget = createLexBuildTarget(target, name);
return BuildTargets.getGenPath(flavoredTarget, "%s/" + name + ".h");
}
/**
* Generate {@link Lex} and {@link Yacc} rules generating C/C++ sources from the
* given lex/yacc sources.
*
* @return {@link CxxHeaderSourceSpec} containing the generated headers/sources
*/
public static CxxHeaderSourceSpec createLexYaccBuildRules(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
ImmutableList<String> lexFlags,
ImmutableMap<String, SourcePath> lexSrcs,
ImmutableList<String> yaccFlags,
ImmutableMap<String, SourcePath> yaccSrcs) {
if (!lexSrcs.isEmpty() && !cxxPlatform.getLex().isPresent()) {
throw new HumanReadableException(
"Platform %s must support lex to compile srcs %s",
cxxPlatform,
lexSrcs);
}
if (!yaccSrcs.isEmpty() && !cxxPlatform.getYacc().isPresent()) {
throw new HumanReadableException(
"Platform %s must support yacc to compile srcs %s",
cxxPlatform,
yaccSrcs);
}
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
ImmutableMap.Builder<String, CxxSource> lexYaccCxxSourcesBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Path, SourcePath> lexYaccHeadersBuilder = ImmutableMap.builder();
// Loop over all lex sources, generating build rule for each one and adding the sources
// and headers it generates to our bookkeeping maps.
for (ImmutableMap.Entry<String, SourcePath> ent : lexSrcs.entrySet()) {
final String name = ent.getKey();
final SourcePath source = ent.getValue();
BuildTarget target = createLexBuildTarget(params.getBuildTarget(), name);
Path outputSource = getLexSourceOutputPath(target, name);
Path outputHeader = getLexHeaderOutputPath(target, name);
// Create the build rule to run lex on this source and add it to the resolver.
Lex lex = new Lex(
params.copyWithChanges(
LEX_TYPE,
target,
Suppliers.ofInstance(
ImmutableSortedSet.copyOf(
pathResolver.filterBuildRuleInputs(ImmutableList.of(source)))),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())),
pathResolver,
cxxPlatform.getLex().get(),
ImmutableList.<String>builder()
.addAll(cxxPlatform.getLexFlags())
.addAll(lexFlags)
.build(),
outputSource,
outputHeader,
source);
resolver.addToIndex(lex);
// Record the output source and header as {@link BuildRuleSourcePath} objects.
lexYaccCxxSourcesBuilder.put(
name + ".cc",
ImmutableCxxSource.of(
CxxSource.Type.CXX,
new BuildTargetSourcePath(
lex.getProjectFilesystem(),
lex.getBuildTarget(),
outputSource),
ImmutableList.<String>of()));
lexYaccHeadersBuilder.put(
params.getBuildTarget().getBasePath().resolve(name + ".h"),
new BuildTargetSourcePath(
lex.getProjectFilesystem(),
lex.getBuildTarget(),
outputHeader));
}
// Loop over all yaccc sources, generating build rule for each one and adding the sources
// and headers it generates to our bookkeeping maps.
for (ImmutableMap.Entry<String, SourcePath> ent : yaccSrcs.entrySet()) {
final String name = ent.getKey();
final SourcePath source = ent.getValue();
BuildTarget target = createYaccBuildTarget(params.getBuildTarget(), name);
Path outputPrefix = getYaccOutputPrefix(target, Files.getNameWithoutExtension(name));
// Create the build rule to run yacc on this source and add it to the resolver.
Yacc yacc = new Yacc(
params.copyWithChanges(
YACC_TYPE,
target,
Suppliers.ofInstance(
ImmutableSortedSet.copyOf(
pathResolver.filterBuildRuleInputs(ImmutableList.of(source)))),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())),
pathResolver,
cxxPlatform.getYacc().get(),
ImmutableList.<String>builder()
.addAll(cxxPlatform.getYaccFlags())
.addAll(yaccFlags)
.build(),
outputPrefix,
source);
resolver.addToIndex(yacc);
// Record the output source and header as {@link BuildRuleSourcePath} objects.
lexYaccCxxSourcesBuilder.put(
name + ".cc",
ImmutableCxxSource.of(
CxxSource.Type.CXX,
new BuildTargetSourcePath(
yacc.getProjectFilesystem(),
yacc.getBuildTarget(),
Yacc.getSourceOutputPath(outputPrefix)),
ImmutableList.<String>of()));
lexYaccHeadersBuilder.put(
params.getBuildTarget().getBasePath().resolve(name + ".h"),
new BuildTargetSourcePath(
yacc.getProjectFilesystem(),
yacc.getBuildTarget(),
Yacc.getHeaderOutputPath(outputPrefix)));
}
return ImmutableCxxHeaderSourceSpec.of(
lexYaccHeadersBuilder.build(),
lexYaccCxxSourcesBuilder.build());
}
public static SymlinkTree createHeaderSymlinkTreeBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
Flavor platform,
ImmutableMap<Path, SourcePath> headers,
HeaderVisibility headerVisibility) {
// Setup the header and symlink tree rules
BuildTarget headerSymlinkTreeTarget =
createHeaderSymlinkTreeTarget(params.getBuildTarget(), platform, headerVisibility);
Path headerSymlinkTreeRoot =
getHeaderSymlinkTreePath(params.getBuildTarget(), platform, headerVisibility);
final SymlinkTree headerSymlinkTree = CxxPreprocessables.createHeaderSymlinkTreeBuildRule(
new SourcePathResolver(resolver),
headerSymlinkTreeTarget,
params,
headerSymlinkTreeRoot,
headers);
resolver.addToIndex(headerSymlinkTree);
return headerSymlinkTree;
}
public static CxxPreprocessorInput combineCxxPreprocessorInput(
BuildRuleParams params,
CxxPlatform cxxPlatform,
ImmutableMultimap<CxxSource.Type, String> preprocessorFlags,
ImmutableList<SourcePath> prefixHeaders,
ImmutableList<SymlinkTree> headerSymlinkTrees,
ImmutableList<Path> frameworkSearchPaths) {
CxxPreprocessorInput cxxPreprocessorInputFromDeps;
try {
// Write the compile rules for all C/C++ sources in this rule.
cxxPreprocessorInputFromDeps =
CxxPreprocessables.getTransitiveCxxPreprocessorInput(
cxxPlatform,
FluentIterable.from(params.getDeps())
.filter(Predicates.instanceOf(CxxPreprocessorDep.class)));
} catch (CxxPreprocessorInput.ConflictingHeadersException e) {
throw e.getHumanReadableExceptionForBuildTarget(params.getBuildTarget());
}
ImmutableMap.Builder<Path, SourcePath> allLinks = ImmutableMap.builder();
ImmutableMap.Builder<Path, SourcePath> allFullLinks = ImmutableMap.builder();
ImmutableList.Builder<Path> allIncludeRoots = ImmutableList.builder();
for (SymlinkTree headerSymlinkTree : headerSymlinkTrees) {
allLinks.putAll(headerSymlinkTree.getLinks());
allFullLinks.putAll(headerSymlinkTree.getFullLinks());
allIncludeRoots.add(headerSymlinkTree.getRoot());
}
CxxPreprocessorInput localPreprocessorInput =
CxxPreprocessorInput.builder()
.addAllRules(Iterables.transform(headerSymlinkTrees, HasBuildTarget.TO_TARGET))
.putAllPreprocessorFlags(preprocessorFlags)
.setIncludes(
ImmutableCxxHeaders.builder()
.addAllPrefixHeaders(prefixHeaders)
.putAllNameToPathMap(allLinks.build())
.putAllFullNameToPathMap(allFullLinks.build())
.build())
.addAllIncludeRoots(allIncludeRoots.build())
.addAllFrameworkRoots(frameworkSearchPaths)
.build();
try {
return CxxPreprocessorInput.concat(
ImmutableList.of(
localPreprocessorInput,
cxxPreprocessorInputFromDeps));
} catch (CxxPreprocessorInput.ConflictingHeadersException e) {
throw e.getHumanReadableExceptionForBuildTarget(params.getBuildTarget());
}
}
/**
* Build up the rules to track headers and compile sources for descriptions which handle C/C++
* sources and headers.
*
* @return a list of {@link SourcePath} objects representing the object files from the result of
* compiling the given C/C++ source.
*/
public static ImmutableList<SourcePath> createCompileBuildRules(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform config,
ImmutableList<String> compilerFlags,
boolean pic,
ImmutableMap<String, CxxSource> sources) {
ImmutableSortedSet<BuildRule> objectRules = CxxCompilableEnhancer.createCompileBuildRules(
params,
resolver,
config,
compilerFlags,
pic,
sources);
resolver.addAllToIndex(objectRules);
return FluentIterable.from(objectRules)
.transform(SourcePaths.getToBuildTargetSourcePath(params.getProjectFilesystem()))
.toList();
}
public static BuildTarget createStaticLibraryBuildTarget(
BuildTarget target,
Flavor platform) {
return BuildTarget.builder(target).addFlavors(platform).addFlavors(STATIC_FLAVOR).build();
}
public static BuildTarget createSharedLibraryBuildTarget(
BuildTarget target,
Flavor platform) {
return BuildTarget.builder(target).addFlavors(platform).addFlavors(SHARED_FLAVOR).build();
}
public static Path getStaticLibraryPath(
BuildTarget target,
Flavor platform) {
String name = String.format("lib%s.a", target.getShortName());
return BuildTargets.getBinPath(createStaticLibraryBuildTarget(target, platform), "%s")
.resolve(name);
}
public static String getSharedLibrarySoname(BuildTarget target, CxxPlatform platform) {
String libName =
Joiner.on('_').join(
ImmutableList.builder()
.addAll(
FluentIterable.from(target.getBasePath())
.transform(Functions.toStringFunction())
.filter(Predicates.not(Predicates.equalTo(""))))
.add(target.getShortName())
.build());
String extension = platform.getSharedLibraryExtension();
return String.format("lib%s.%s", libName, extension);
}
public static Path getSharedLibraryPath(
BuildTarget target,
CxxPlatform platform) {
String extension = platform.getSharedLibraryExtension();
String name = String.format("lib%s.%s", target.getShortName(), extension);
return BuildTargets.getBinPath(
createSharedLibraryBuildTarget(target, platform.getFlavor()),
"%s/" + name);
}
@VisibleForTesting
protected static Path getOutputPath(BuildTarget target) {
return BuildTargets.getBinPath(target, "%s/" + target.getShortNameAndFlavorPostfix());
}
@VisibleForTesting
protected static BuildTarget createCxxLinkTarget(BuildTarget target) {
return BuildTarget.builder(target).addFlavors(CXX_LINK_BINARY_FLAVOR).build();
}
public static CxxLink createBuildRulesForCxxBinaryDescriptionArg(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
CxxBinaryDescription.Arg args) {
ImmutableMap<String, CxxSource> srcs = parseCxxSources(params, resolver, args);
ImmutableMap<Path, SourcePath> headers = parseHeaders(params, resolver, args);
ImmutableMap<String, SourcePath> lexSrcs = parseLexSources(params, resolver, args);
ImmutableMap<String, SourcePath> yaccSrcs = parseYaccSources(params, resolver, args);
// Setup the rules to run lex/yacc.
CxxHeaderSourceSpec lexYaccSources =
createLexYaccBuildRules(
params,
resolver,
cxxPlatform,
ImmutableList.<String>of(),
lexSrcs,
ImmutableList.<String>of(),
yaccSrcs);
// Setup the header symlink tree and combine all the preprocessor input from this rule
// and all dependencies.
SymlinkTree headerSymlinkTree = createHeaderSymlinkTreeBuildRule(
params,
resolver,
cxxPlatform.getFlavor(),
ImmutableMap.<Path, SourcePath>builder()
.putAll(headers)
.putAll(lexYaccSources.getCxxHeaders())
.build(),
HeaderVisibility.PRIVATE);
CxxPreprocessorInput cxxPreprocessorInput = combineCxxPreprocessorInput(
params,
cxxPlatform,
CxxPreprocessorFlags.fromArgs(
args.preprocessorFlags,
args.langPreprocessorFlags),
args.prefixHeaders.get(),
ImmutableList.of(headerSymlinkTree),
args.frameworkSearchPaths.get());
// The complete list of input sources.
ImmutableMap<String, CxxSource> sources =
ImmutableMap.<String, CxxSource>builder()
.putAll(srcs)
.putAll(lexYaccSources.getCxxSources())
.build();
// Generate whatever rules are needed to preprocess all the input sources.
ImmutableMap<String, CxxSource> preprocessed =
CxxPreprocessables.createPreprocessBuildRules(
params,
resolver,
cxxPlatform,
cxxPreprocessorInput,
/* pic */ false,
sources);
// Generate the rules for setting up and headers, preprocessing, and compiling the input
// sources and return the source paths for the object files.
ImmutableList<SourcePath> objects =
createCompileBuildRules(
params,
resolver,
cxxPlatform,
args.compilerFlags.or(ImmutableList.<String>of()),
/* pic */ false,
preprocessed);
// Generate the final link rule. We use the top-level target as the link rule's
// target, so that it corresponds to the actual binary we build.
Path output = getOutputPath(params.getBuildTarget());
CxxLink cxxLink = CxxLinkableEnhancer.createCxxLinkableBuildRule(
cxxPlatform,
params,
new SourcePathResolver(resolver),
/* extraCxxLdFlags */ ImmutableList.<String>of(),
/* extraLdFlags */ ImmutableList.<String>builder()
.addAll(args.linkerFlags.or(ImmutableList.<String>of()))
.addAll(
CxxDescriptionEnhancer.getPlatformFlags(
args.platformLinkerFlags.get(),
cxxPlatform.getFlavor().toString()))
.build(),
createCxxLinkTarget(params.getBuildTarget()),
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
output,
objects,
Linker.LinkableDepType.STATIC,
params.getDeps());
resolver.addToIndex(cxxLink);
return cxxLink;
}
private static <T> BuildRule requireBuildRule(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
TargetNode<T> node,
Flavor... flavors) {
BuildTarget target = BuildTarget.builder(params.getBuildTarget()).addFlavors(flavors).build();
Optional<BuildRule> rule = ruleResolver.getRuleOptional(target);
if (!rule.isPresent()) {
Description<T> description = node.getDescription();
T args = node.getConstructorArg();
rule = Optional.of(
description.createBuildRule(
params.copyWithChanges(
params.getBuildRuleType(),
target,
Suppliers.ofInstance(params.getDeclaredDeps()),
Suppliers.ofInstance(params.getExtraDeps())),
ruleResolver,
args));
ruleResolver.addToIndex(rule.get());
}
return rule.get();
}
/**
* Ensure that the build rule generated by the given {@link BuildRuleParams} had been generated
* by it's corresponding {@link Description} and added to the {@link BuildRuleResolver}. If not,
* call into it's associated {@link Description} to generate it's {@link BuildRule}.
*
* @return the {@link BuildRule} generated by the description corresponding to the supplied
* {@link BuildRuleParams}.
*/
public static BuildRule requireBuildRule(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
Flavor... flavors) {
TargetNode<?> node = params.getTargetGraph().get(params.getBuildTarget());
Preconditions.checkNotNull(
node,
String.format("%s not in target graph", params.getBuildTarget()));
return requireBuildRule(params, ruleResolver, node, flavors);
}
/**
* @return a {@link Function} object which transforms path names from the output of a compiler
* or preprocessor using {@code pathProcessor}.
*/
public static Function<String, String> createErrorMessagePathProcessor(
final Function<String, String> pathProcessor) {
return new Function<String, String>() {
private final ImmutableList<Pattern> patterns =
ImmutableList.of(
Pattern.compile(
"(?<=^(?:In file included |\\s+)from )" +
"(?<path>[^:]+)" +
"(?=[:,](?:\\d+[:,](?:\\d+[:,])?)?$)"),
Pattern.compile(
"^(?<path>[^:]+)(?=:(?:\\d+:(?:\\d+:)?)? )"));
@Override
public String apply(String line) {
for (Pattern pattern : patterns) {
Matcher m = pattern.matcher(line);
if (m.find()) {
return m.replaceAll(pathProcessor.apply(m.group("path")));
}
}
return line;
}
};
}
public static ImmutableList<String> getPlatformFlags(
ImmutableList<Pair<String, ImmutableList<String>>> platformFlags,
String platform) {
ImmutableList.Builder<String> platformFlagsBuilder = ImmutableList.builder();
for (Pair<String, ImmutableList<String>> pair : platformFlags) {
Pattern pattern = Pattern.compile(pair.getFirst());
Matcher matcher = pattern.matcher(platform);
if (matcher.find()) {
platformFlagsBuilder.addAll(pair.getSecond());
break;
}
}
return platformFlagsBuilder.build();
}
}