/*
* 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.ProjectFilesystem;
import com.facebook.buck.json.JsonConcatenate;
import com.facebook.buck.log.Logger;
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.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.CellPathResolver;
import com.facebook.buck.rules.CommandTool;
import com.facebook.buck.rules.RuleKeyObjectSink;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.SourceWithFlags;
import com.facebook.buck.rules.SymlinkTree;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.args.Arg;
import com.facebook.buck.rules.args.FileListableLinkerInputArg;
import com.facebook.buck.rules.args.RuleKeyAppendableFunction;
import com.facebook.buck.rules.args.SourcePathArg;
import com.facebook.buck.rules.args.StringArg;
import com.facebook.buck.rules.args.StringWithMacrosArg;
import com.facebook.buck.rules.coercer.FrameworkPath;
import com.facebook.buck.rules.coercer.PatternMatchedCollection;
import com.facebook.buck.rules.coercer.SourceList;
import com.facebook.buck.rules.macros.LocationMacroExpander;
import com.facebook.buck.rules.macros.MacroHandler;
import com.facebook.buck.rules.macros.StringWithMacros;
import com.facebook.buck.rules.query.QueryUtils;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.MoreCollectors;
import com.facebook.buck.util.RichStream;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableCollection;
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.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.io.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.StreamSupport;
public class CxxDescriptionEnhancer {
private static final Logger LOG = Logger.get(CxxDescriptionEnhancer.class);
public static final Flavor SANDBOX_TREE_FLAVOR = InternalFlavor.of("sandbox");
public static final Flavor HEADER_SYMLINK_TREE_FLAVOR = InternalFlavor.of("private-headers");
public static final Flavor EXPORTED_HEADER_SYMLINK_TREE_FLAVOR = InternalFlavor.of("headers");
public static final Flavor STATIC_FLAVOR = InternalFlavor.of("static");
public static final Flavor STATIC_PIC_FLAVOR = InternalFlavor.of("static-pic");
public static final Flavor SHARED_FLAVOR = InternalFlavor.of("shared");
public static final Flavor MACH_O_BUNDLE_FLAVOR = InternalFlavor.of("mach-o-bundle");
public static final Flavor SHARED_LIBRARY_SYMLINK_TREE_FLAVOR =
InternalFlavor.of("shared-library-symlink-tree");
public static final Flavor CXX_LINK_BINARY_FLAVOR = InternalFlavor.of("binary");
protected static final MacroHandler MACRO_HANDLER =
new MacroHandler(ImmutableMap.of("location", new LocationMacroExpander()));
private static final Pattern SONAME_EXT_MACRO_PATTERN =
Pattern.compile("\\$\\(ext(?: ([.0-9]+))?\\)");
private CxxDescriptionEnhancer() {}
public static CxxPreprocessables.HeaderMode getHeaderModeForPlatform(
BuildRuleResolver resolver, CxxPlatform cxxPlatform, boolean shouldCreateHeadersSymlinks) {
boolean useHeaderMap =
(cxxPlatform.getCpp().resolve(resolver).supportsHeaderMaps()
&& cxxPlatform.getCxxpp().resolve(resolver).supportsHeaderMaps());
return !useHeaderMap
? CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY
: (shouldCreateHeadersSymlinks
? CxxPreprocessables.HeaderMode.SYMLINK_TREE_WITH_HEADER_MAP
: CxxPreprocessables.HeaderMode.HEADER_MAP_ONLY);
}
public static HeaderSymlinkTree createHeaderSymlinkTree(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPreprocessables.HeaderMode mode,
ImmutableMap<Path, SourcePath> headers,
HeaderVisibility headerVisibility,
Flavor... flavors) {
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
params.getBuildTarget(), headerVisibility, flavors);
Path headerSymlinkTreeRoot =
CxxDescriptionEnhancer.getHeaderSymlinkTreePath(
params.getProjectFilesystem(), params.getBuildTarget(), headerVisibility, flavors);
return CxxPreprocessables.createHeaderSymlinkTreeBuildRule(
headerSymlinkTreeTarget,
params.getProjectFilesystem(),
headerSymlinkTreeRoot,
headers,
mode,
new SourcePathRuleFinder(resolver));
}
public static HeaderSymlinkTree createHeaderSymlinkTree(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
ImmutableMap<Path, SourcePath> headers,
HeaderVisibility headerVisibility,
boolean shouldCreateHeadersSymlinks) {
return createHeaderSymlinkTree(
params,
resolver,
getHeaderModeForPlatform(resolver, cxxPlatform, shouldCreateHeadersSymlinks),
headers,
headerVisibility,
cxxPlatform.getFlavor());
}
public static SymlinkTree createSandboxSymlinkTree(
BuildRuleParams params,
CxxPlatform cxxPlatform,
ImmutableMap<Path, SourcePath> map,
SourcePathRuleFinder ruleFinder) {
BuildTarget sandboxSymlinkTreeTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(
params.getBuildTarget(), cxxPlatform.getFlavor());
Path sandboxSymlinkTreeRoot =
CxxDescriptionEnhancer.getSandboxSymlinkTreePath(
params.getProjectFilesystem(), sandboxSymlinkTreeTarget);
return new SymlinkTree(
sandboxSymlinkTreeTarget,
params.getProjectFilesystem(),
sandboxSymlinkTreeRoot,
map,
ruleFinder);
}
public static HeaderSymlinkTree requireHeaderSymlinkTree(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
CxxPlatform cxxPlatform,
ImmutableMap<Path, SourcePath> headers,
HeaderVisibility headerVisibility,
boolean shouldCreateHeadersSymlinks) {
BuildRuleParams untypedParams = CxxLibraryDescription.getUntypedParams(params);
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
untypedParams.getBuildTarget(), headerVisibility, cxxPlatform.getFlavor());
// Check the cache...
Optional<BuildRule> rule = ruleResolver.getRuleOptional(headerSymlinkTreeTarget);
if (rule.isPresent()) {
Preconditions.checkState(rule.get() instanceof HeaderSymlinkTree);
return (HeaderSymlinkTree) rule.get();
}
HeaderSymlinkTree symlinkTree =
createHeaderSymlinkTree(
untypedParams,
ruleResolver,
cxxPlatform,
headers,
headerVisibility,
shouldCreateHeadersSymlinks);
ruleResolver.addToIndex(symlinkTree);
return symlinkTree;
}
private static SymlinkTree requireSandboxSymlinkTree(
BuildRuleParams params, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform)
throws NoSuchBuildTargetException {
BuildRuleParams untypedParams = CxxLibraryDescription.getUntypedParams(params);
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(
untypedParams.getBuildTarget(), cxxPlatform.getFlavor());
BuildRule rule = ruleResolver.requireRule(headerSymlinkTreeTarget);
Preconditions.checkState(
rule instanceof SymlinkTree, rule.getBuildTarget() + " " + rule.getClass().toString());
return (SymlinkTree) rule;
}
/**
* @return the {@link BuildTarget} to use for the {@link BuildRule} generating the symlink tree of
* headers.
*/
@VisibleForTesting
public static BuildTarget createHeaderSymlinkTreeTarget(
BuildTarget target, HeaderVisibility headerVisibility, Flavor... flavors) {
return BuildTarget.builder(target)
.addFlavors(getHeaderSymlinkTreeFlavor(headerVisibility))
.addFlavors(flavors)
.build();
}
@VisibleForTesting
public static BuildTarget createSandboxSymlinkTreeTarget(BuildTarget target, Flavor platform) {
return BuildTarget.builder(target).addFlavors(platform).addFlavors(SANDBOX_TREE_FLAVOR).build();
}
/** @return the absolute {@link Path} to use for the symlink tree of headers. */
public static Path getHeaderSymlinkTreePath(
ProjectFilesystem filesystem,
BuildTarget target,
HeaderVisibility headerVisibility,
Flavor... flavors) {
return BuildTargets.getGenPath(
filesystem, createHeaderSymlinkTreeTarget(target, headerVisibility, flavors), "%s");
}
public static Path getSandboxSymlinkTreePath(ProjectFilesystem filesystem, BuildTarget target) {
return BuildTargets.getGenPath(filesystem, target, "%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");
}
}
static ImmutableMap<String, SourcePath> parseOnlyHeaders(
BuildTarget buildTarget,
SourcePathRuleFinder ruleFinder,
SourcePathResolver sourcePathResolver,
String parameterName,
SourceList exportedHeaders) {
return exportedHeaders.toNameMap(
buildTarget,
sourcePathResolver,
parameterName,
path -> !CxxGenruleDescription.wrapsCxxGenrule(ruleFinder, path),
path -> path);
}
static ImmutableMap<String, SourcePath> parseOnlyPlatformHeaders(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver sourcePathResolver,
CxxPlatform cxxPlatform,
String headersParameterName,
SourceList headers,
String platformHeadersParameterName,
PatternMatchedCollection<SourceList> platformHeaders)
throws NoSuchBuildTargetException {
ImmutableMap.Builder<String, SourcePath> parsed = ImmutableMap.builder();
java.util.function.Function<SourcePath, SourcePath> fixup =
path -> {
try {
return CxxGenruleDescription.fixupSourcePath(resolver, ruleFinder, cxxPlatform, path);
} catch (NoSuchBuildTargetException e) {
throw new RuntimeException(e);
}
};
// Include all normal exported headers that are generated by `cxx_genrule`.
parsed.putAll(
headers.toNameMap(
buildTarget,
sourcePathResolver,
headersParameterName,
path -> CxxGenruleDescription.wrapsCxxGenrule(ruleFinder, path),
fixup));
// Include all platform specific headers.
for (SourceList sourceList :
platformHeaders.getMatchingValues(cxxPlatform.getFlavor().toString())) {
parsed.putAll(
sourceList.toNameMap(
buildTarget, sourcePathResolver, platformHeadersParameterName, path -> true, fixup));
}
return parsed.build();
}
/**
* @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(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver sourcePathResolver,
Optional<CxxPlatform> cxxPlatform,
CxxConstructorArg args)
throws NoSuchBuildTargetException {
ImmutableMap.Builder<String, SourcePath> headers = ImmutableMap.builder();
// Add platform-agnostic headers.
headers.putAll(
parseOnlyHeaders(
buildTarget, ruleFinder, sourcePathResolver, "headers", args.getHeaders()));
// Add platform-specific headers.
if (cxxPlatform.isPresent()) {
headers.putAll(
parseOnlyPlatformHeaders(
buildTarget,
resolver,
ruleFinder,
sourcePathResolver,
cxxPlatform.get(),
"headers",
args.getHeaders(),
"platform_headers",
args.getPlatformHeaders()));
}
return CxxPreprocessables.resolveHeaderMap(
args.getHeaderNamespace().map(Paths::get).orElse(buildTarget.getBasePath()),
headers.build());
}
/**
* @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(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver sourcePathResolver,
Optional<CxxPlatform> cxxPlatform,
CxxLibraryDescription.CommonArg args)
throws NoSuchBuildTargetException {
ImmutableMap.Builder<String, SourcePath> headers = ImmutableMap.builder();
// Include platform-agnostic headers.
headers.putAll(
parseOnlyHeaders(
buildTarget,
ruleFinder,
sourcePathResolver,
"exported_headers",
args.getExportedHeaders()));
// If a platform is specific, include platform-specific headers.
if (cxxPlatform.isPresent()) {
headers.putAll(
parseOnlyPlatformHeaders(
buildTarget,
resolver,
ruleFinder,
sourcePathResolver,
cxxPlatform.get(),
"exported_headers",
args.getExportedHeaders(),
"exported_platform_headers",
args.getExportedPlatformHeaders()));
}
return CxxPreprocessables.resolveHeaderMap(
args.getHeaderNamespace().map(Paths::get).orElse(buildTarget.getBasePath()),
headers.build());
}
/**
* @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> parseExportedPlatformHeaders(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver sourcePathResolver,
CxxPlatform cxxPlatform,
CxxLibraryDescription.CommonArg args)
throws NoSuchBuildTargetException {
return CxxPreprocessables.resolveHeaderMap(
args.getHeaderNamespace().map(Paths::get).orElse(buildTarget.getBasePath()),
parseOnlyPlatformHeaders(
buildTarget,
resolver,
ruleFinder,
sourcePathResolver,
cxxPlatform,
"exported_headers",
args.getExportedHeaders(),
"exported_platform_headers",
args.getExportedPlatformHeaders()));
}
/**
* @return a list {@link CxxSource} objects formed by parsing the input {@link SourcePath} objects
* for the "srcs" parameter.
*/
public static ImmutableMap<String, CxxSource> parseCxxSources(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
CxxConstructorArg args) {
return parseCxxSources(
buildTarget,
resolver,
ruleFinder,
pathResolver,
cxxPlatform,
args.getSrcs(),
args.getPlatformSrcs());
}
public static ImmutableMap<String, CxxSource> parseCxxSources(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
ImmutableSortedSet<SourceWithFlags> srcs,
PatternMatchedCollection<ImmutableSortedSet<SourceWithFlags>> platformSrcs) {
ImmutableMap.Builder<String, SourceWithFlags> sources = ImmutableMap.builder();
putAllSources(buildTarget, resolver, ruleFinder, pathResolver, cxxPlatform, srcs, sources);
for (ImmutableSortedSet<SourceWithFlags> sourcesWithFlags :
platformSrcs.getMatchingValues(cxxPlatform.getFlavor().toString())) {
putAllSources(
buildTarget, resolver, ruleFinder, pathResolver, cxxPlatform, sourcesWithFlags, sources);
}
return resolveCxxSources(sources.build());
}
private static void putAllSources(
BuildTarget buildTarget,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
CxxPlatform cxxPlatform,
ImmutableSortedSet<SourceWithFlags> sourcesWithFlags,
ImmutableMap.Builder<String, SourceWithFlags> sources) {
sources.putAll(
pathResolver.getSourcePathNames(
buildTarget,
"srcs",
sourcesWithFlags
.stream()
.map(
s -> {
try {
return s.withSourcePath(
CxxGenruleDescription.fixupSourcePath(
resolver,
ruleFinder,
cxxPlatform,
Preconditions.checkNotNull(s.getSourcePath())));
} catch (NoSuchBuildTargetException e) {
throw new RuntimeException(e);
}
})
.collect(MoreCollectors.toImmutableList()),
x -> true,
SourceWithFlags::getSourcePath));
}
public static ImmutableList<CxxPreprocessorInput> collectCxxPreprocessorInput(
BuildRuleParams params,
CxxPlatform cxxPlatform,
Iterable<BuildRule> deps,
ImmutableMultimap<CxxSource.Type, String> preprocessorFlags,
ImmutableList<HeaderSymlinkTree> headerSymlinkTrees,
ImmutableSet<FrameworkPath> frameworks,
Iterable<CxxPreprocessorInput> cxxPreprocessorInputFromDeps,
ImmutableList<String> includeDirs,
Optional<SymlinkTree> symlinkTree)
throws NoSuchBuildTargetException {
// Add the private includes of any rules which this rule depends on, and which list this rule as
// a test.
BuildTarget targetWithoutFlavor =
BuildTarget.of(params.getBuildTarget().getUnflavoredBuildTarget());
ImmutableList.Builder<CxxPreprocessorInput> cxxPreprocessorInputFromTestedRulesBuilder =
ImmutableList.builder();
for (BuildRule rule : deps) {
if (rule instanceof NativeTestable) {
NativeTestable testable = (NativeTestable) rule;
if (testable.isTestedBy(targetWithoutFlavor)) {
LOG.debug(
"Adding private includes of tested rule %s to testing rule %s",
rule.getBuildTarget(), params.getBuildTarget());
cxxPreprocessorInputFromTestedRulesBuilder.add(
testable.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PRIVATE));
// Add any dependent headers
cxxPreprocessorInputFromTestedRulesBuilder.addAll(
CxxPreprocessables.getTransitiveCxxPreprocessorInput(
cxxPlatform, ImmutableList.of(rule)));
}
}
}
ImmutableList<CxxPreprocessorInput> cxxPreprocessorInputFromTestedRules =
cxxPreprocessorInputFromTestedRulesBuilder.build();
LOG.verbose(
"Rules tested by target %s added private includes %s",
params.getBuildTarget(), cxxPreprocessorInputFromTestedRules);
ImmutableList.Builder<CxxHeaders> allIncludes = ImmutableList.builder();
for (HeaderSymlinkTree headerSymlinkTree : headerSymlinkTrees) {
allIncludes.add(
CxxSymlinkTreeHeaders.from(headerSymlinkTree, CxxPreprocessables.IncludeType.LOCAL));
}
CxxPreprocessorInput.Builder builder = CxxPreprocessorInput.builder();
builder.putAllPreprocessorFlags(preprocessorFlags);
// headers from #sandbox are put before #private-headers and #headers on purpose
// this is the only way to control windows behavior
if (symlinkTree.isPresent()) {
for (String includeDir : includeDirs) {
builder.addIncludes(
CxxSandboxInclude.from(
symlinkTree.get(), includeDir, CxxPreprocessables.IncludeType.LOCAL));
}
}
builder.addAllIncludes(allIncludes.build()).addAllFrameworks(frameworks);
CxxPreprocessorInput localPreprocessorInput = builder.build();
return ImmutableList.<CxxPreprocessorInput>builder()
.add(localPreprocessorInput)
.addAll(cxxPreprocessorInputFromDeps)
.addAll(cxxPreprocessorInputFromTestedRules)
.build();
}
public static BuildTarget createStaticLibraryBuildTarget(
BuildTarget target, Flavor platform, CxxSourceRuleFactory.PicType pic) {
return BuildTarget.builder(target)
.addFlavors(platform)
.addFlavors(pic == CxxSourceRuleFactory.PicType.PDC ? STATIC_FLAVOR : STATIC_PIC_FLAVOR)
.build();
}
public static BuildTarget createSharedLibraryBuildTarget(
BuildTarget target, Flavor platform, Linker.LinkType linkType) {
Flavor linkFlavor;
switch (linkType) {
case SHARED:
linkFlavor = SHARED_FLAVOR;
break;
case MACH_O_BUNDLE:
linkFlavor = MACH_O_BUNDLE_FLAVOR;
break;
case EXECUTABLE:
default:
throw new IllegalStateException(
"Only SHARED and MACH_O_BUNDLE types expected, got: " + linkType);
}
return BuildTarget.builder(target).addFlavors(platform).addFlavors(linkFlavor).build();
}
public static Path getStaticLibraryPath(
ProjectFilesystem filesystem,
BuildTarget target,
Flavor platform,
CxxSourceRuleFactory.PicType pic,
String extension) {
String name = String.format("lib%s.%s", target.getShortName(), extension);
return BuildTargets.getGenPath(
filesystem, createStaticLibraryBuildTarget(target, platform, pic), "%s")
.resolve(name);
}
public static String getSharedLibrarySoname(
Optional<String> declaredSoname, BuildTarget target, CxxPlatform platform) {
if (!declaredSoname.isPresent()) {
return getDefaultSharedLibrarySoname(target, platform);
}
return getNonDefaultSharedLibrarySoname(
declaredSoname.get(),
platform.getSharedLibraryExtension(),
platform.getSharedLibraryVersionedExtensionFormat());
}
@VisibleForTesting
static String getNonDefaultSharedLibrarySoname(
String declared,
String sharedLibraryExtension,
String sharedLibraryVersionedExtensionFormat) {
Matcher match = SONAME_EXT_MACRO_PATTERN.matcher(declared);
if (!match.find()) {
return declared;
}
String version = match.group(1);
if (version == null) {
return match.replaceFirst(sharedLibraryExtension);
}
return match.replaceFirst(String.format(sharedLibraryVersionedExtensionFormat, version));
}
public static String getDefaultSharedLibrarySoname(BuildTarget target, CxxPlatform platform) {
String libName =
Joiner.on('_')
.join(
ImmutableList.builder()
.addAll(
StreamSupport.stream(target.getBasePath().spliterator(), false)
.map(Object::toString)
.filter(x -> !x.isEmpty())
.iterator())
.add(target.getShortName())
.build());
String extension = platform.getSharedLibraryExtension();
return String.format("lib%s.%s", libName, extension);
}
public static Path getSharedLibraryPath(
ProjectFilesystem filesystem, BuildTarget sharedLibraryTarget, String soname) {
return BuildTargets.getGenPath(filesystem, sharedLibraryTarget, "%s/" + soname);
}
private static Path getBinaryOutputPath(
BuildTarget target, ProjectFilesystem filesystem, Optional<String> extension) {
String format = extension.map(ext -> "%s." + ext).orElse("%s");
return BuildTargets.getGenPath(filesystem, target, format);
}
@VisibleForTesting
public static BuildTarget createCxxLinkTarget(
BuildTarget target, Optional<LinkerMapMode> flavoredLinkerMapMode) {
if (flavoredLinkerMapMode.isPresent()) {
target = target.withAppendedFlavors(flavoredLinkerMapMode.get().getFlavor());
}
return target.withAppendedFlavors(CXX_LINK_BINARY_FLAVOR);
}
/**
* @return a function that transforms the {@link FrameworkPath} to search paths with any embedded
* macros expanded.
*/
public static RuleKeyAppendableFunction<FrameworkPath, Path> frameworkPathToSearchPath(
final CxxPlatform cxxPlatform, final SourcePathResolver resolver) {
return new RuleKeyAppendableFunction<FrameworkPath, Path>() {
private RuleKeyAppendableFunction<String, String> translateMacrosFn =
CxxFlags.getTranslateMacrosFn(cxxPlatform);
@Override
public void appendToRuleKey(RuleKeyObjectSink sink) {
sink.setReflectively("translateMacrosFn", translateMacrosFn);
}
@Override
public Path apply(FrameworkPath input) {
String pathAsString =
FrameworkPath.getUnexpandedSearchPath(
resolver::getAbsolutePath, Functions.identity(), input)
.toString();
return Paths.get(translateMacrosFn.apply(pathAsString));
}
};
}
public static CxxLinkAndCompileRules createBuildRulesForCxxBinaryDescriptionArg(
TargetGraph targetGraph,
BuildRuleParams params,
BuildRuleResolver resolver,
CellPathResolver cellRoots,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
CxxBinaryDescription.CommonArg args,
ImmutableSet<BuildTarget> extraDeps,
Optional<StripStyle> stripStyle,
Optional<LinkerMapMode> flavoredLinkerMapMode)
throws NoSuchBuildTargetException {
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
ImmutableMap<String, CxxSource> srcs =
parseCxxSources(
params.getBuildTarget(), resolver, ruleFinder, pathResolver, cxxPlatform, args);
ImmutableMap<Path, SourcePath> headers =
parseHeaders(
params.getBuildTarget(),
resolver,
ruleFinder,
pathResolver,
Optional.of(cxxPlatform),
args);
// Build the binary deps.
ImmutableSortedSet.Builder<BuildRule> depsBuilder = ImmutableSortedSet.naturalOrder();
// Add original declared and extra deps.
args.getCxxDeps().get(resolver, cxxPlatform).forEach(depsBuilder::add);
// Add in deps found via deps query.
args.getDepsQuery()
.ifPresent(
query ->
QueryUtils.resolveDepQuery(
params.getBuildTarget(),
query,
resolver,
cellRoots,
targetGraph,
args.getDeps())
.forEach(depsBuilder::add));
// Add any extra deps passed in.
extraDeps.stream().map(resolver::getRule).forEach(depsBuilder::add);
ImmutableSortedSet<BuildRule> deps = depsBuilder.build();
return createBuildRulesForCxxBinary(
params,
resolver,
cellRoots,
cxxBuckConfig,
cxxPlatform,
srcs,
headers,
deps,
stripStyle,
flavoredLinkerMapMode,
args.getLinkStyle().orElse(Linker.LinkableDepType.STATIC),
args.getThinLto(),
args.getPreprocessorFlags(),
args.getPlatformPreprocessorFlags(),
args.getLangPreprocessorFlags(),
args.getFrameworks(),
args.getLibraries(),
args.getCompilerFlags(),
args.getLangCompilerFlags(),
args.getPlatformCompilerFlags(),
args.getPrefixHeader(),
args.getPrecompiledHeader(),
args.getLinkerFlags(),
args.getPlatformLinkerFlags(),
args.getCxxRuntimeType(),
args.getIncludeDirs(),
Optional.empty());
}
public static CxxLinkAndCompileRules createBuildRulesForCxxBinary(
BuildRuleParams params,
BuildRuleResolver resolver,
CellPathResolver cellRoots,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableMap<String, CxxSource> srcs,
ImmutableMap<Path, SourcePath> headers,
ImmutableSortedSet<BuildRule> deps,
Optional<StripStyle> stripStyle,
Optional<LinkerMapMode> flavoredLinkerMapMode,
Linker.LinkableDepType linkStyle,
boolean thinLto,
ImmutableList<String> preprocessorFlags,
PatternMatchedCollection<ImmutableList<String>> platformPreprocessorFlags,
ImmutableMap<CxxSource.Type, ImmutableList<String>> langPreprocessorFlags,
ImmutableSortedSet<FrameworkPath> frameworks,
ImmutableSortedSet<FrameworkPath> libraries,
ImmutableList<String> compilerFlags,
ImmutableMap<CxxSource.Type, ImmutableList<String>> langCompilerFlags,
PatternMatchedCollection<ImmutableList<String>> platformCompilerFlags,
Optional<SourcePath> prefixHeader,
Optional<SourcePath> precompiledHeader,
ImmutableList<StringWithMacros> linkerFlags,
PatternMatchedCollection<ImmutableList<StringWithMacros>> platformLinkerFlags,
Optional<Linker.CxxRuntimeType> cxxRuntimeType,
ImmutableList<String> includeDirs,
Optional<Boolean> xcodePrivateHeadersSymlinks)
throws NoSuchBuildTargetException {
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder);
// TODO(beefon): should be:
// Path linkOutput = getLinkOutputPath(
// createCxxLinkTarget(params.getBuildTarget(), flavoredLinkerMapMode),
// params.getProjectFilesystem());
BuildTarget target = params.getBuildTarget();
if (flavoredLinkerMapMode.isPresent()) {
target = target.withAppendedFlavors(flavoredLinkerMapMode.get().getFlavor());
}
Path linkOutput =
getBinaryOutputPath(
target, params.getProjectFilesystem(), cxxPlatform.getBinaryExtension());
ImmutableList.Builder<Arg> argsBuilder = ImmutableList.builder();
CommandTool.Builder executableBuilder = new CommandTool.Builder();
// Setup the header symlink tree and combine all the preprocessor input from this rule
// and all dependencies.
boolean shouldCreatePrivateHeadersSymlinks =
xcodePrivateHeadersSymlinks.orElse(cxxBuckConfig.getPrivateHeadersSymlinksEnabled());
HeaderSymlinkTree headerSymlinkTree =
requireHeaderSymlinkTree(
params,
resolver,
cxxPlatform,
headers,
HeaderVisibility.PRIVATE,
shouldCreatePrivateHeadersSymlinks);
Optional<SymlinkTree> sandboxTree = Optional.empty();
if (cxxBuckConfig.sandboxSources()) {
sandboxTree = createSandboxTree(params, resolver, cxxPlatform);
}
ImmutableList<CxxPreprocessorInput> cxxPreprocessorInput =
collectCxxPreprocessorInput(
params,
cxxPlatform,
deps,
CxxFlags.getLanguageFlags(
preprocessorFlags, platformPreprocessorFlags, langPreprocessorFlags, cxxPlatform),
ImmutableList.of(headerSymlinkTree),
frameworks,
CxxPreprocessables.getTransitiveCxxPreprocessorInput(
cxxPlatform,
RichStream.from(deps)
.filter(CxxPreprocessorDep.class::isInstance)
.toImmutableList()),
includeDirs,
sandboxTree);
ImmutableList.Builder<String> compilerFlagsWithLto = ImmutableList.builder();
compilerFlagsWithLto.addAll(compilerFlags);
if (thinLto) {
compilerFlagsWithLto.add("-flto=thin");
}
// Generate and add all the build rules to preprocess and compile the source to the
// resolver and get the `SourcePath`s representing the generated object files.
ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects =
CxxSourceRuleFactory.requirePreprocessAndCompileRules(
params,
resolver,
sourcePathResolver,
ruleFinder,
cxxBuckConfig,
cxxPlatform,
cxxPreprocessorInput,
CxxFlags.getLanguageFlags(
compilerFlagsWithLto.build(),
platformCompilerFlags,
langCompilerFlags,
cxxPlatform),
prefixHeader,
precompiledHeader,
srcs,
linkStyle == Linker.LinkableDepType.STATIC
? CxxSourceRuleFactory.PicType.PDC
: CxxSourceRuleFactory.PicType.PIC,
sandboxTree);
// Build up the linker flags, which support macro expansion.
argsBuilder.addAll(
toStringWithMacrosArgs(
target,
cellRoots,
resolver,
cxxPlatform,
CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(
linkerFlags, platformLinkerFlags, cxxPlatform)));
// Special handling for dynamically linked binaries.
if (linkStyle == Linker.LinkableDepType.SHARED) {
// Create a symlink tree with for all shared libraries needed by this binary.
SymlinkTree sharedLibraries =
requireSharedLibrarySymlinkTree(
params.getBuildTarget(),
params.getProjectFilesystem(),
resolver,
ruleFinder,
cxxPlatform,
deps,
NativeLinkable.class::isInstance);
// Embed a origin-relative library path into the binary so it can find the shared libraries.
// The shared libraries root is absolute. Also need an absolute path to the linkOutput
Path absLinkOut = params.getBuildTarget().getCellPath().resolve(linkOutput);
argsBuilder.addAll(
StringArg.from(
Linkers.iXlinker(
"-rpath",
String.format(
"%s/%s",
cxxPlatform.getLd().resolve(resolver).origin(),
absLinkOut.getParent().relativize(sharedLibraries.getRoot()).toString()))));
// Add all the shared libraries and the symlink tree as inputs to the tool that represents
// this binary, so that users can attach the proper deps.
executableBuilder.addDep(sharedLibraries);
executableBuilder.addInputs(sharedLibraries.getLinks().values());
}
// Add object files into the args.
ImmutableList<SourcePathArg> objectArgs =
SourcePathArg.from(objects.values())
.stream()
.map(
input -> {
Preconditions.checkArgument(input instanceof SourcePathArg);
return (SourcePathArg) input;
})
.collect(MoreCollectors.toImmutableList());
argsBuilder.addAll(FileListableLinkerInputArg.from(objectArgs));
BuildTarget linkRuleTarget =
createCxxLinkTarget(params.getBuildTarget(), flavoredLinkerMapMode);
CxxLink cxxLink =
createCxxLinkRule(
params,
resolver,
cxxBuckConfig,
cxxPlatform,
RichStream.from(deps).filter(NativeLinkable.class).toImmutableList(),
linkStyle,
thinLto,
frameworks,
libraries,
cxxRuntimeType,
sourcePathResolver,
ruleFinder,
linkOutput,
argsBuilder,
linkRuleTarget);
BuildRule binaryRuleForExecutable;
Optional<CxxStrip> cxxStrip = Optional.empty();
if (stripStyle.isPresent()) {
BuildRuleParams cxxParams = params;
if (flavoredLinkerMapMode.isPresent()) {
cxxParams = params.withAppendedFlavor(flavoredLinkerMapMode.get().getFlavor());
}
CxxStrip stripRule =
createCxxStripRule(cxxParams, resolver, stripStyle.get(), cxxLink, cxxPlatform);
cxxStrip = Optional.of(stripRule);
binaryRuleForExecutable = stripRule;
} else {
binaryRuleForExecutable = cxxLink;
}
// Add the output of the link as the lone argument needed to invoke this binary as a tool.
executableBuilder.addArg(SourcePathArg.of(binaryRuleForExecutable.getSourcePathToOutput()));
return new CxxLinkAndCompileRules(
cxxLink,
cxxStrip,
ImmutableSortedSet.copyOf(objects.keySet()),
executableBuilder.build(),
deps);
}
private static CxxLink createCxxLinkRule(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
Iterable<? extends NativeLinkable> deps,
Linker.LinkableDepType linkStyle,
boolean thinLto,
ImmutableSortedSet<FrameworkPath> frameworks,
ImmutableSortedSet<FrameworkPath> libraries,
Optional<Linker.CxxRuntimeType> cxxRuntimeType,
SourcePathResolver sourcePathResolver,
SourcePathRuleFinder ruleFinder,
Path linkOutput,
ImmutableList.Builder<Arg> argsBuilder,
BuildTarget linkRuleTarget)
throws NoSuchBuildTargetException {
CxxLink cxxLink;
Optional<BuildRule> existingCxxLinkRule = resolver.getRuleOptional(linkRuleTarget);
if (existingCxxLinkRule.isPresent()) {
Preconditions.checkArgument(existingCxxLinkRule.get() instanceof CxxLink);
cxxLink = (CxxLink) existingCxxLinkRule.get();
} else {
// 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.
cxxLink =
CxxLinkableEnhancer.createCxxLinkableBuildRule(
cxxBuckConfig,
cxxPlatform,
params,
resolver,
sourcePathResolver,
ruleFinder,
linkRuleTarget,
Linker.LinkType.EXECUTABLE,
Optional.empty(),
linkOutput,
linkStyle,
thinLto,
deps,
cxxRuntimeType,
Optional.empty(),
ImmutableSet.of(),
NativeLinkableInput.builder()
.setArgs(argsBuilder.build())
.setFrameworks(frameworks)
.setLibraries(libraries)
.build(),
Optional.empty());
resolver.addToIndex(cxxLink);
}
return cxxLink;
}
public static CxxStrip createCxxStripRule(
BuildRuleParams params,
BuildRuleResolver resolver,
StripStyle stripStyle,
BuildRule unstrippedBinaryRule,
CxxPlatform cxxPlatform) {
BuildRuleParams stripRuleParams =
params
.withBuildTarget(
params
.getBuildTarget()
.withAppendedFlavors(CxxStrip.RULE_FLAVOR, stripStyle.getFlavor()))
.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(ImmutableSortedSet.of(unstrippedBinaryRule)),
Suppliers.ofInstance(ImmutableSortedSet.of()));
Optional<BuildRule> exisitingRule = resolver.getRuleOptional(stripRuleParams.getBuildTarget());
if (exisitingRule.isPresent()) {
Preconditions.checkArgument(exisitingRule.get() instanceof CxxStrip);
return (CxxStrip) exisitingRule.get();
} else {
CxxStrip cxxStrip =
new CxxStrip(
stripRuleParams,
stripStyle,
Preconditions.checkNotNull(unstrippedBinaryRule.getSourcePathToOutput()),
cxxPlatform.getStrip(),
CxxDescriptionEnhancer.getBinaryOutputPath(
stripRuleParams.getBuildTarget(),
params.getProjectFilesystem(),
cxxPlatform.getBinaryExtension()));
resolver.addToIndex(cxxStrip);
return cxxStrip;
}
}
public static BuildRule createUberCompilationDatabase(
BuildRuleParams params, BuildRuleResolver ruleResolver) throws NoSuchBuildTargetException {
Optional<CxxCompilationDatabaseDependencies> compilationDatabases =
ruleResolver.requireMetadata(
params
.withoutFlavor(CxxCompilationDatabase.UBER_COMPILATION_DATABASE)
.withAppendedFlavor(CxxCompilationDatabase.COMPILATION_DATABASE)
.getBuildTarget(),
CxxCompilationDatabaseDependencies.class);
Preconditions.checkState(compilationDatabases.isPresent());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
return new JsonConcatenate(
params.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(
ImmutableSortedSet.copyOf(
ruleFinder.filterBuildRuleInputs(compilationDatabases.get().getSourcePaths()))),
Suppliers.ofInstance(ImmutableSortedSet.of())),
pathResolver.getAllAbsolutePaths(compilationDatabases.get().getSourcePaths()),
"compilation-database-concatenate",
"Concatenate compilation databases",
"uber-compilation-database",
"compile_commands.json");
}
public static Optional<CxxCompilationDatabaseDependencies> createCompilationDatabaseDependencies(
BuildTarget buildTarget,
FlavorDomain<CxxPlatform> platforms,
BuildRuleResolver resolver,
CxxConstructorArg args)
throws NoSuchBuildTargetException {
Preconditions.checkState(
buildTarget.getFlavors().contains(CxxCompilationDatabase.COMPILATION_DATABASE));
Optional<Flavor> cxxPlatformFlavor = platforms.getFlavor(buildTarget);
Preconditions.checkState(
cxxPlatformFlavor.isPresent(),
"Could not find cxx platform in:\n%s",
Joiner.on(", ").join(buildTarget.getFlavors()));
ImmutableSet.Builder<SourcePath> sourcePaths = ImmutableSet.builder();
for (BuildTarget dep : args.getDeps()) {
Optional<CxxCompilationDatabaseDependencies> compilationDatabases =
resolver.requireMetadata(
BuildTarget.builder(dep)
.addFlavors(CxxCompilationDatabase.COMPILATION_DATABASE)
.addFlavors(cxxPlatformFlavor.get())
.build(),
CxxCompilationDatabaseDependencies.class);
if (compilationDatabases.isPresent()) {
sourcePaths.addAll(compilationDatabases.get().getSourcePaths());
}
}
// Not all parts of Buck use require yet, so require the rule here so it's available in the
// resolver for the parts that don't.
BuildRule buildRule = resolver.requireRule(buildTarget);
sourcePaths.add(buildRule.getSourcePathToOutput());
return Optional.of(CxxCompilationDatabaseDependencies.of(sourcePaths.build()));
}
public static Optional<SymlinkTree> createSandboxTree(
BuildRuleParams params, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform)
throws NoSuchBuildTargetException {
return Optional.of(requireSandboxSymlinkTree(params, ruleResolver, cxxPlatform));
}
/**
* @return the {@link BuildTarget} to use for the {@link BuildRule} generating the symlink tree of
* shared libraries.
*/
public static BuildTarget createSharedLibrarySymlinkTreeTarget(
BuildTarget target, Flavor platform) {
return BuildTarget.builder(target)
.addFlavors(SHARED_LIBRARY_SYMLINK_TREE_FLAVOR)
.addFlavors(platform)
.build();
}
/** @return the {@link Path} to use for the symlink tree of headers. */
public static Path getSharedLibrarySymlinkTreePath(
ProjectFilesystem filesystem, BuildTarget target, Flavor platform) {
return BuildTargets.getGenPath(
filesystem, createSharedLibrarySymlinkTreeTarget(target, platform), "%s");
}
/**
* Build a {@link HeaderSymlinkTree} of all the shared libraries found via the top-level rule's
* transitive dependencies.
*/
public static SymlinkTree createSharedLibrarySymlinkTree(
SourcePathRuleFinder ruleFinder,
BuildTarget baseBuildTarget,
ProjectFilesystem filesystem,
CxxPlatform cxxPlatform,
Iterable<? extends BuildRule> deps,
Predicate<Object> traverse,
Predicate<Object> skip)
throws NoSuchBuildTargetException {
BuildTarget symlinkTreeTarget =
createSharedLibrarySymlinkTreeTarget(baseBuildTarget, cxxPlatform.getFlavor());
Path symlinkTreeRoot =
getSharedLibrarySymlinkTreePath(filesystem, baseBuildTarget, cxxPlatform.getFlavor());
ImmutableSortedMap<String, SourcePath> libraries =
NativeLinkables.getTransitiveSharedLibraries(cxxPlatform, deps, traverse, skip);
ImmutableMap.Builder<Path, SourcePath> links = ImmutableMap.builder();
for (Map.Entry<String, SourcePath> ent : libraries.entrySet()) {
links.put(Paths.get(ent.getKey()), ent.getValue());
}
return new SymlinkTree(
symlinkTreeTarget, filesystem, symlinkTreeRoot, links.build(), ruleFinder);
}
public static SymlinkTree createSharedLibrarySymlinkTree(
SourcePathRuleFinder ruleFinder,
BuildTarget baseBuildTarget,
ProjectFilesystem filesystem,
CxxPlatform cxxPlatform,
Iterable<? extends BuildRule> deps,
Predicate<Object> traverse)
throws NoSuchBuildTargetException {
return createSharedLibrarySymlinkTree(
ruleFinder, baseBuildTarget, filesystem, cxxPlatform, deps, traverse, x -> false);
}
public static SymlinkTree requireSharedLibrarySymlinkTree(
BuildTarget buildTarget,
ProjectFilesystem filesystem,
BuildRuleResolver resolver,
SourcePathRuleFinder ruleFinder,
CxxPlatform cxxPlatform,
Iterable<? extends BuildRule> deps,
Predicate<Object> traverse)
throws NoSuchBuildTargetException {
BuildTarget target = createSharedLibrarySymlinkTreeTarget(buildTarget, cxxPlatform.getFlavor());
SymlinkTree tree = resolver.getRuleOptionalWithType(target, SymlinkTree.class).orElse(null);
if (tree == null) {
tree =
resolver.addToIndex(
createSharedLibrarySymlinkTree(
ruleFinder, buildTarget, filesystem, cxxPlatform, deps, traverse));
}
return tree;
}
public static Flavor flavorForLinkableDepType(Linker.LinkableDepType linkableDepType) {
switch (linkableDepType) {
case STATIC:
return STATIC_FLAVOR;
case STATIC_PIC:
return STATIC_PIC_FLAVOR;
case SHARED:
return SHARED_FLAVOR;
}
throw new RuntimeException(String.format("Unsupported LinkableDepType: '%s'", linkableDepType));
}
public static SymlinkTree createSandboxTreeBuildRule(
BuildRuleResolver resolver,
CxxConstructorArg args,
CxxPlatform platform,
BuildRuleParams params)
throws NoSuchBuildTargetException {
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder);
ImmutableCollection<SourcePath> privateHeaders =
parseHeaders(
params.getBuildTarget(),
resolver,
ruleFinder,
sourcePathResolver,
Optional.of(platform),
args)
.values();
ImmutableCollection<CxxSource> sources =
parseCxxSources(
params.getBuildTarget(), resolver, ruleFinder, sourcePathResolver, platform, args)
.values();
HashMap<Path, SourcePath> links = new HashMap<>();
for (SourcePath headerPath : privateHeaders) {
links.put(
Paths.get(sourcePathResolver.getSourcePathName(params.getBuildTarget(), headerPath)),
headerPath);
}
if (args instanceof CxxLibraryDescription.CommonArg) {
ImmutableCollection<SourcePath> publicHeaders =
CxxDescriptionEnhancer.parseExportedHeaders(
params.getBuildTarget(),
resolver,
ruleFinder,
sourcePathResolver,
Optional.of(platform),
(CxxLibraryDescription.CommonArg) args)
.values();
for (SourcePath headerPath : publicHeaders) {
links.put(
Paths.get(sourcePathResolver.getSourcePathName(params.getBuildTarget(), headerPath)),
headerPath);
}
}
for (CxxSource source : sources) {
SourcePath sourcePath = source.getPath();
links.put(
Paths.get(sourcePathResolver.getSourcePathName(params.getBuildTarget(), sourcePath)),
sourcePath);
}
return createSandboxSymlinkTree(params, platform, ImmutableMap.copyOf(links), ruleFinder);
}
/** Resolve the map of names to SourcePaths to a map of names to CxxSource objects. */
private static ImmutableMap<String, CxxSource> resolveCxxSources(
ImmutableMap<String, SourceWithFlags> sources) {
ImmutableMap.Builder<String, CxxSource> cxxSources = ImmutableMap.builder();
// For each entry in the input C/C++ source, build a CxxSource object to wrap
// it's name, input path, and output object file path.
for (ImmutableMap.Entry<String, SourceWithFlags> ent : sources.entrySet()) {
String extension = Files.getFileExtension(ent.getKey());
Optional<CxxSource.Type> type = CxxSource.Type.fromExtension(extension);
if (!type.isPresent()) {
throw new HumanReadableException("invalid extension \"%s\": %s", extension, ent.getKey());
}
cxxSources.put(
ent.getKey(),
CxxSource.of(type.get(), ent.getValue().getSourcePath(), ent.getValue().getFlags()));
}
return cxxSources.build();
}
public static ImmutableList<StringWithMacrosArg> toStringWithMacrosArgs(
BuildTarget target,
CellPathResolver cellPathResolver,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
Iterable<StringWithMacros> flags) {
ImmutableList.Builder<StringWithMacrosArg> args = ImmutableList.builder();
for (StringWithMacros flag : flags) {
args.add(
StringWithMacrosArg.of(
flag,
ImmutableList.of(new CxxLocationMacroExpander(cxxPlatform)),
target,
cellPathResolver,
resolver));
}
return args.build();
}
public static String normalizeModuleName(String moduleName) {
return moduleName.replaceAll("[^A-Za-z0-9]", "_");
}
}