blob: f0bae458378c74ba44ac68468b2055fcf66ab9b6 [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.cxx;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.model.Flavor;
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.BuildRuleType;
import com.facebook.buck.rules.ImmutableBuildRuleType;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.coercer.SourceWithFlags;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.MoreIterables;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import java.nio.file.Path;
public class CxxCompilableEnhancer {
private static final BuildRuleType COMPILE_TYPE = ImmutableBuildRuleType.of("compile");
private CxxCompilableEnhancer() {}
/**
* Prefixes each of the given assembler arguments with "-Xassembler" so that the compiler
* assembler driver will pass these arguments directly down to the linker rather than
* interpreting them itself.
*
* e.g. ["--fatal-warnings"] -> ["-Xassembler", "--fatal-warnings"]
*
* @param args arguments for the assembler.
* @return arguments to be passed to the compiler assembler driver.
*/
public static Iterable<String> iXassembler(Iterable<String> args) {
return MoreIterables.zipAndConcat(
Iterables.cycle("-Xassembler"),
args);
}
/**
* Resolve the map of names to SourcePaths to a map of names to CxxSource objects.
*/
public 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(),
ImmutableCxxSource.of(
type.get(),
ent.getValue().getSourcePath(),
ent.getValue().getFlags()));
}
return cxxSources.build();
}
/**
* @return the object file name for the given source name.
*/
private static String getOutputName(String name) {
return name + ".o";
}
/**
* @return a build target for a {@link CxxCompile} rule for the source with the given name.
*/
public static BuildTarget createCompileBuildTarget(
BuildTarget target,
Flavor platform,
String name,
boolean pic) {
return BuildTarget
.builder(target)
.addFlavors(platform)
.addFlavors(
ImmutableFlavor.of(
String.format(
"compile-%s%s",
pic ? "pic-" : "",
getOutputName(name)
.replace('/', '-')
.replace('.', '-')
.replace('+', '-')
.replace(' ', '-'))))
.build();
}
/**
* @return the output path for an object file compiled from the source with the given name.
*/
public static Path getCompileOutputPath(BuildTarget target, String name) {
return BuildTargets.getBinPath(target, "%s").resolve(getOutputName(name));
}
/**
* @return a {@link CxxCompile} rule that preprocesses, compiles, and assembles the given
* {@link CxxSource}.
*/
public static CxxCompile createCompileBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform platform,
ImmutableList<String> compilerFlags,
boolean pic,
String name,
CxxSource source) {
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
Preconditions.checkArgument(CxxSourceTypes.isCompilableType(source.getType()));
BuildTarget target = createCompileBuildTarget(
params.getBuildTarget(),
platform.getFlavor(),
name,
pic);
ImmutableSortedSet.Builder<BuildRule> dependencies = ImmutableSortedSet.naturalOrder();
// If a build rule generates our input source, add that as a dependency.
dependencies.addAll(pathResolver.filterBuildRuleInputs(ImmutableList.of(source.getPath())));
// Pick the compiler to use. Basically, if we're dealing with C++ sources, use the C++
// compiler, and the C compiler for everything.
Tool compiler;
if (CxxSourceTypes.needsCxxCompiler(source.getType())) {
compiler = platform.getCxx();
} else {
compiler = platform.getCc();
}
ImmutableList.Builder<String> args = ImmutableList.builder();
// TODO(#5393669): We need to handle compiler drivers that don't support certain language
// options (e.g. the android NDK compilers don't support "c-cpp-output", although they can
// auto-detect via the extension). For the time being, we just fall back to the default
// of letting the compiler driver auto-detecting the language type via the extensions which
// should work, since we require proper extensions in the descriptions.
//args.add("-x", source.getType().getLanguage());
// If we're dealing with a C source that can be compiled, add the platform C compiler flags.
if (source.getType() == CxxSource.Type.C_CPP_OUTPUT ||
source.getType() == CxxSource.Type.OBJC_CPP_OUTPUT) {
args.addAll(platform.getCflags());
}
// If we're dealing with a C++ source that can be compiled, add the platform C++ compiler
// flags.
if (source.getType() == CxxSource.Type.CXX_CPP_OUTPUT ||
source.getType() == CxxSource.Type.OBJCXX_CPP_OUTPUT) {
args.addAll(platform.getCxxflags());
}
// Add in explicit additional compiler flags, if we're compiling.
if (source.getType() == CxxSource.Type.C_CPP_OUTPUT ||
source.getType() == CxxSource.Type.OBJC_CPP_OUTPUT ||
source.getType() == CxxSource.Type.CXX_CPP_OUTPUT ||
source.getType() == CxxSource.Type.OBJCXX_CPP_OUTPUT) {
args.addAll(compilerFlags);
}
// All source types require assembling, so add in platform-specific assembler flags.
args.addAll(iXassembler(platform.getAsflags()));
// If we're using pic, add in the appropriate flag.
if (pic) {
args.add("-fPIC");
}
// Add custom per-file flags.
args.addAll(source.getFlags());
// Build the CxxCompile rule and add it to our sorted set of build rules.
return new CxxCompile(
params.copyWithChanges(
COMPILE_TYPE,
target,
Suppliers.ofInstance(dependencies.build()),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())),
pathResolver,
compiler,
Optional.<CxxCompile.Plugin>absent(),
args.build(),
getCompileOutputPath(target, name),
source.getPath(),
platform.getDebugPathSanitizer());
}
/**
* @return a set of {@link CxxCompile} rules which compile and assemble the given input
* {@link CxxSource} sources.
*/
public static ImmutableSortedSet<BuildRule> createCompileBuildRules(
BuildRuleParams params,
BuildRuleResolver resolver,
CxxPlatform platform,
ImmutableList<String> compilerFlags,
boolean pic,
ImmutableMap<String, CxxSource> sources) {
ImmutableSortedSet.Builder<BuildRule> rules = ImmutableSortedSet.naturalOrder();
// Iterate over the input C/C++ sources that we need to compile and assemble and generate
// build rules for them.
for (ImmutableMap.Entry<String, CxxSource> entry : sources.entrySet()) {
rules.add(createCompileBuildRule(
params,
resolver,
platform,
compilerFlags,
pic,
entry.getKey(),
entry.getValue()));
}
return rules.build();
}
}