/*
* 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.cli.BuckConfig;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.rules.BinaryBuildRuleToolProvider;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.RuleScheduleInfo;
import com.facebook.buck.rules.Tool;
import com.facebook.buck.rules.ToolProvider;
import com.facebook.buck.util.environment.Platform;
import com.facebook.buck.util.immutables.BuckStyleImmutable;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import java.nio.file.Path;
import java.util.Optional;
import org.immutables.value.Value;
/** Contains platform independent settings for C/C++ rules. */
public class CxxBuckConfig {
private static final String FLAVORED_CXX_SECTION_PREFIX = "cxx#";
private static final String UNFLAVORED_CXX_SECTION_PREFIX = "cxx";
private static final long DEFAULT_MAX_TEST_OUTPUT_SIZE = 8096;
private final BuckConfig delegate;
private final String cxxSection;
public static final String DEFAULT_FLAVOR_LIBRARY_TYPE = "type";
public static final String DEFAULT_FLAVOR_PLATFORM = "platform";
/**
* Constructs set of flavors given in a .buckconfig file, as is specified by section names of the
* form cxx#{flavor name}.
*/
public static ImmutableSet<Flavor> getCxxFlavors(BuckConfig config) {
ImmutableSet.Builder<Flavor> builder = ImmutableSet.builder();
ImmutableSet<String> sections = config.getSections();
for (String section : sections) {
if (section.startsWith(FLAVORED_CXX_SECTION_PREFIX)) {
builder.add(InternalFlavor.of(section.substring(FLAVORED_CXX_SECTION_PREFIX.length())));
}
}
return builder.build();
}
public CxxBuckConfig(BuckConfig delegate) {
this.delegate = delegate;
this.cxxSection = UNFLAVORED_CXX_SECTION_PREFIX;
}
/*
* A special constructor for a section of the form cxx#{flavor name}
* which represents a generated flavor that uses the cxx options defined
* in that section.
*/
public CxxBuckConfig(BuckConfig delegate, Flavor flavor) {
this.delegate = delegate;
this.cxxSection = FLAVORED_CXX_SECTION_PREFIX + flavor.getName();
}
/** @return the environment in which {@link BuckConfig} was created. */
public ImmutableMap<String, String> getEnvironment() {
return delegate.getEnvironment();
}
/** @return the {@link BuildTarget} which represents the gtest library. */
public Optional<BuildTarget> getGtestDep() {
return delegate.getBuildTarget(cxxSection, "gtest_dep");
}
/**
* @return the {@link BuildTarget} which represents the main function that gtest tests should use
* by default (if no other main is given).
*/
public Optional<BuildTarget> getGtestDefaultTestMainDep() {
return delegate.getBuildTarget(cxxSection, "gtest_default_test_main_dep");
}
/** @return the {@link BuildTarget} which represents the boost testing library. */
public Optional<BuildTarget> getBoostTestDep() {
return delegate.getBuildTarget(cxxSection, "boost_test_dep");
}
public Optional<Path> getPath(String name) {
return delegate.getPath(cxxSection, name);
}
public Optional<String> getDefaultPlatform() {
return delegate.getValue(cxxSection, "default_platform");
}
public Optional<String> getHostPlatform() {
return delegate.getValue(cxxSection, "host_platform");
}
public Optional<ImmutableList<String>> getFlags(String field) {
Optional<String> value = delegate.getValue(cxxSection, field);
if (!value.isPresent()) {
return Optional.empty();
}
ImmutableList.Builder<String> split = ImmutableList.builder();
if (!value.get().trim().isEmpty()) {
split.addAll(Splitter.on(" ").split(value.get().trim()));
}
return Optional.of(split.build());
}
/*
* Constructs the appropriate Archiver for the specified platform.
*/
public Optional<Archiver> getArchiver(Tool ar) {
Optional<Platform> archiverPlatform =
delegate.getEnum(cxxSection, "archiver_platform", Platform.class);
if (!archiverPlatform.isPresent()) {
return Optional.empty();
}
Archiver result;
switch (archiverPlatform.get()) {
case MACOS:
case FREEBSD:
result = new BsdArchiver(ar);
break;
case LINUX:
result = new GnuArchiver(ar);
break;
case WINDOWS:
result = new WindowsArchiver(ar);
break;
case UNKNOWN:
default:
throw new RuntimeException(
"Invalid platform for archiver. Must be one of {MACOS, LINUX, WINDOWS}");
}
return Optional.of(result);
}
/** @return the maximum size in bytes of test output to report in test results. */
public long getMaximumTestOutputSize() {
return delegate
.getLong(cxxSection, "max_test_output_size")
.orElse(DEFAULT_MAX_TEST_OUTPUT_SIZE);
}
private Optional<CxxToolProviderParams> getCxxToolProviderParams(
String field, Optional<CxxToolProvider.Type> defaultType) {
Optional<String> value = delegate.getValue(cxxSection, field);
if (!value.isPresent()) {
return Optional.empty();
}
String source = String.format("[%s] %s", cxxSection, field);
Optional<BuildTarget> target = delegate.getMaybeBuildTarget(cxxSection, field);
Optional<CxxToolProvider.Type> type =
delegate
.getEnum(cxxSection, field + "_type", CxxToolProvider.Type.class)
.map(Optional::of)
.orElse(defaultType);
if (type.isPresent() && type.get() == CxxToolProvider.Type.DEFAULT) {
type = Optional.of(CxxToolProvider.Type.GCC);
}
if (target.isPresent()) {
return Optional.of(
CxxToolProviderParams.builder()
.setSource(source)
.setBuildTarget(target.get())
.setType(type.orElse(CxxToolProvider.Type.GCC))
.build());
} else {
return Optional.of(
CxxToolProviderParams.builder()
.setSource(source)
.setPath(delegate.getRequiredPath(cxxSection, field))
.setType(type)
.build());
}
}
public Optional<PreprocessorProvider> getPreprocessorProvider(String field) {
Optional<CxxToolProvider.Type> defaultType = Optional.empty();
Optional<CxxToolProviderParams> params = getCxxToolProviderParams(field, defaultType);
if (!params.isPresent()) {
return Optional.empty();
}
return Optional.of(params.get().getPreprocessorProvider());
}
public Optional<CompilerProvider> getCompilerProvider(String field) {
Optional<CxxToolProviderParams> params = getCxxToolProviderParams(field, Optional.empty());
if (!params.isPresent()) {
return Optional.empty();
}
return Optional.of(params.get().getCompilerProvider());
}
public Optional<LinkerProvider> getLinkerProvider(String field, LinkerProvider.Type defaultType) {
Optional<ToolProvider> toolProvider = delegate.getToolProvider(cxxSection, field);
if (!toolProvider.isPresent()) {
return Optional.empty();
}
Optional<LinkerProvider.Type> type =
delegate.getEnum(cxxSection, "linker_platform", LinkerProvider.Type.class);
return Optional.of(new DefaultLinkerProvider(type.orElse(defaultType), toolProvider.get()));
}
public HeaderVerification getHeaderVerification() {
return HeaderVerification.builder()
.setMode(
delegate
.getEnum(cxxSection, "untracked_headers", HeaderVerification.Mode.class)
.orElse(HeaderVerification.Mode.IGNORE))
.addAllWhitelist(delegate.getListWithoutComments(cxxSection, "untracked_headers_whitelist"))
.build();
}
public boolean getPublicHeadersSymlinksEnabled() {
return delegate.getBooleanValue(cxxSection, "exported_headers_symlinks_enabled", true);
}
public boolean getPrivateHeadersSymlinksEnabled() {
return delegate.getBooleanValue(cxxSection, "headers_symlinks_enabled", true);
}
public Optional<RuleScheduleInfo> getLinkScheduleInfo() {
Optional<Long> linkWeight = delegate.getLong(cxxSection, "link_weight");
if (!linkWeight.isPresent()) {
return Optional.empty();
}
return Optional.of(
RuleScheduleInfo.builder().setJobsMultiplier(linkWeight.get().intValue()).build());
}
public boolean shouldCacheLinks() {
return delegate.getBooleanValue(cxxSection, "cache_links", true);
}
public boolean isPCHEnabled() {
return delegate.getBooleanValue(cxxSection, "pch_enabled", true);
}
public boolean sandboxSources() {
return delegate.getBooleanValue(cxxSection, "sandbox_sources", false);
}
public Archive.Contents getArchiveContents() {
return delegate
.getEnum(cxxSection, "archive_contents", Archive.Contents.class)
.orElse(Archive.Contents.NORMAL);
}
public ImmutableMap<String, Flavor> getDefaultFlavorsForRuleType(BuildRuleType type) {
return ImmutableMap.copyOf(
Maps.transformValues(
delegate.getEntriesForSection("defaults." + type.getName()), InternalFlavor::of));
}
public int getDebugPathSanitizerLimit() {
return delegate.getInteger(cxxSection, "debug_path_sanitizer_limit").orElse(250);
}
public Optional<ToolProvider> getToolProvider(String name) {
return delegate.getToolProvider(cxxSection, name);
}
/** @return whether to enabled shared library interfaces. */
public boolean shouldUseSharedLibraryInterfaces() {
return delegate.getBooleanValue(cxxSection, "shared_library_interfaces", false);
}
@Value.Immutable
@BuckStyleImmutable
abstract static class AbstractCxxToolProviderParams {
public abstract String getSource();
public abstract Optional<BuildTarget> getBuildTarget();
public abstract Optional<Path> getPath();
public abstract Optional<CxxToolProvider.Type> getType();
@Value.Check
protected void check() {
Preconditions.checkState(getBuildTarget().isPresent() || getPath().isPresent());
Preconditions.checkState(!getBuildTarget().isPresent() || getType().isPresent());
}
public PreprocessorProvider getPreprocessorProvider() {
if (getBuildTarget().isPresent()) {
return new PreprocessorProvider(
new BinaryBuildRuleToolProvider(getBuildTarget().get(), getSource()), getType().get());
} else {
return new PreprocessorProvider(getPath().get(), getType());
}
}
public CompilerProvider getCompilerProvider() {
if (getBuildTarget().isPresent()) {
return new CompilerProvider(
new BinaryBuildRuleToolProvider(getBuildTarget().get(), getSource()), getType().get());
} else {
return new CompilerProvider(getPath().get(), getType());
}
}
}
}