com.facebook.buck.cxx.toolchain.CxxBuckConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.cxx.toolchain.CxxBuckConfig.java

Source

/*
 * 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.toolchain;

import com.facebook.buck.core.config.BuckConfig;
import com.facebook.buck.core.exceptions.HumanReadableException;
import com.facebook.buck.core.model.BuildTarget;
import com.facebook.buck.core.model.EmptyTargetConfiguration;
import com.facebook.buck.core.model.Flavor;
import com.facebook.buck.core.model.InternalFlavor;
import com.facebook.buck.core.model.RuleType;
import com.facebook.buck.core.model.TargetConfiguration;
import com.facebook.buck.core.model.UserFlavor;
import com.facebook.buck.core.rules.schedule.RuleScheduleInfo;
import com.facebook.buck.core.sourcepath.PathSourcePath;
import com.facebook.buck.core.sourcepath.SourcePath;
import com.facebook.buck.core.toolchain.tool.Tool;
import com.facebook.buck.core.toolchain.tool.impl.HashedFileTool;
import com.facebook.buck.core.toolchain.toolprovider.ToolProvider;
import com.facebook.buck.core.toolchain.toolprovider.impl.BinaryBuildRuleToolProvider;
import com.facebook.buck.core.toolchain.toolprovider.impl.ConstantToolProvider;
import com.facebook.buck.core.util.immutables.BuckStyleImmutable;
import com.facebook.buck.cxx.toolchain.ArchiverProvider.LegacyArchiverType;
import com.facebook.buck.cxx.toolchain.linker.DefaultLinkerProvider;
import com.facebook.buck.cxx.toolchain.linker.LinkerProvider;
import com.facebook.buck.rules.tool.config.ToolConfig;
import com.facebook.buck.util.environment.Platform;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
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.collect.Maps;
import com.google.common.collect.Sets;
import java.nio.file.Path;
import java.util.Optional;
import javax.annotation.Nullable;
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 = "cxx";

    private static final long DEFAULT_MAX_TEST_OUTPUT_SIZE = 8096;

    private static final String DEFAULT_PLATFORM = "default_platform";
    private static final String GTEST_DEP = "gtest_dep";
    private static final String GTEST_DEFAULT_TEST_MAIN_DEP = "gtest_default_test_main_dep";
    private static final String BOOST_TEST_DEP = "boost_test_dep";
    private static final String HOST_PLATFORM = "host_platform";
    private static final String ARCHIVER_PLATFORM = "archiver_platform";
    private static final String ARCHIVER_TYPE = "archiver_type";
    private static final String MAX_TEST_OUTPUT_SIZE = "max_test_output_size";
    private static final String LINKER_PLATFORM = "linker_platform";
    private static final String UNTRACKED_HEADERS = "untracked_headers";
    private static final String UNTRACKED_HEADERS_WHITELIST = "untracked_headers_whitelist";
    private static final String EXPORTED_HEADERS_SYMLINKS_ENABLED = "exported_headers_symlinks_enabled";
    private static final String HEADERS_SYMLINKS_ENABLED = "headers_symlinks_enabled";
    private static final String LINK_WEIGHT = "link_weight";
    private static final String CACHE_LINKS = "cache_links";
    private static final String CACHE_STRIPS = "cache_strips";
    private static final String CACHE_BINARIES = "cache_binaries";
    private static final String PCH_ENABLED = "pch_enabled";
    private static final String ARCHIVE_CONTENTS = "archive_contents";
    private static final String DEBUG_PATH_SANITIZER_LIMIT = "debug_path_sanitizer_limit";
    private static final String SHOULD_REMAP_HOST_PLATFORM = "should_remap_host_platform";
    private static final String UNIQUE_LIBRARY_NAME_ENABLED = "unique_library_name_enabled";
    private static final String DEFAULT_REEXPORT_ALL_HEADER_DEPENDENCIES = "default_reexport_all_header_dependencies";
    private static final String SHLIB_INTERFACES = "shlib_interfaces";
    private static final String SHARED_LIBRARY_INTERFACES = "shared_library_interfaces";
    private static final String INDEPENDENT_SHLIB_INTERFACES = "independent_shlib_interfaces";
    private static final String INDEPENDENT_SHLIB_INTERFACE_LDFLAGS = "independent_shlib_interface_ldflags";
    private static final String DECLARED_PLATFORMS = "declared_platforms";
    private static final String SHARED_LIBRARY_EXT = "shared_library_extension";
    private static final String STATIC_LIBRARY_EXT = "static_library_extension";
    private static final String OBJECT_FILE_EXT = "object_file_extension";
    private static final String CONFLICTING_HEADER_BASENAME_WHITELIST = "conflicting_header_basename_whitelist";
    private static final String HEADER_MODE = "header_mode";
    private static final String DETAILED_UNTRACKED_HEADER_MESSAGES = "detailed_untracked_header_messages";
    private static final String USE_ARG_FILE = "use_arg_file";
    private static final String TOOLCHAIN_TARGET = "toolchain_target";
    private static final String FILEPATH_LENGTH_LIMITED = "filepath_length_limited";

    private static final String ASFLAGS = "asflags";
    private static final String ASPPFLAGS = "asppflags";
    private static final String CFLAGS = "cflags";
    private static final String CXXFLAGS = "cxxflags";
    private static final String CPPFLAGS = "cppflags";
    private static final String CXXPPFLAGS = "cxxppflags";
    private static final String CUDAFLAGS = "cudaflags";
    private static final String CUDAPPFLAGS = "cudappflags";
    private static final String HIPFLAGS = "hipflags";
    private static final String HIPPPFLAGS = "hipppflags";
    private static final String ASMFLAGS = "asmflags";
    private static final String ASMPPFLAGS = "asmppflags";
    private static final String LDFLAGS = "ldflags";
    private static final String ARFLAGS = "arflags";
    private static final String RANLIBFLAGS = "ranlibflags";

    private static final String AR = "ar";
    private static final String RANLIB = "ranlib";
    private static final String OBJCOPY = "objcopy";
    private static final String NM = "nm";
    private static final String STRIP = "strip";
    private static final String AS = "as";
    private static final String ASPP = "aspp";
    private static final String CC = "cc";
    private static final String CPP = "cpp";
    private static final String CXX = "cxx";
    private static final String CXXPP = "cxxpp";
    private static final String CUDA = "cuda";
    private static final String CUDAPP = "cudapp";
    private static final String HIP = "hip";
    private static final String HIPPP = "hippp";
    private static final String ASM = "asm";
    private static final String ASMPP = "asmpp";
    private static final String LD = "ld";

    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;
    }

    /*
     * 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, EmptyTargetConfiguration.INSTANCE);
    }

    /**
     * @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, EmptyTargetConfiguration.INSTANCE);
    }

    /** @return the {@link BuildTarget} which represents the boost testing library. */
    public Optional<BuildTarget> getBoostTestDep() {
        return delegate.getBuildTarget(cxxSection, BOOST_TEST_DEP, EmptyTargetConfiguration.INSTANCE);
    }

    public Optional<Path> getPath(String name) {
        return delegate.getPath(cxxSection, name);
    }

    @Nullable
    public PathSourcePath getSourcePath(Path path) {
        return delegate.getPathSourcePath(path);
    }

    public Optional<SourcePath> getSourcePath(String name, TargetConfiguration targetConfiguration) {
        return delegate.getSourcePath(cxxSection, name, targetConfiguration);
    }

    public Optional<String> getDefaultPlatform() {
        return delegate.getValue(cxxSection, DEFAULT_PLATFORM);
    }

    public Optional<String> getHostPlatform() {
        return delegate.getValue(cxxSection, HOST_PLATFORM);
    }

    private Optional<ImmutableList<String>> getFlags(String field) {
        Optional<String> value = delegate.getValue(cxxSection, field);
        if (!value.isPresent()) {
            return Optional.empty();
        }
        return Optional.of(delegate.getListWithoutComments(cxxSection, field, ' '));
    }

    public Optional<ImmutableList<String>> getAsflags() {
        return getFlags(ASFLAGS);
    }

    public Optional<ImmutableList<String>> getAsppflags() {
        return getFlags(ASPPFLAGS);
    }

    public Optional<ImmutableList<String>> getCflags() {
        return getFlags(CFLAGS);
    }

    public Optional<ImmutableList<String>> getCxxflags() {
        return getFlags(CXXFLAGS);
    }

    public Optional<ImmutableList<String>> getCppflags() {
        return getFlags(CPPFLAGS);
    }

    public Optional<ImmutableList<String>> getCxxppflags() {
        return getFlags(CXXPPFLAGS);
    }

    public Optional<ImmutableList<String>> getCudaflags() {
        return getFlags(CUDAFLAGS);
    }

    public Optional<ImmutableList<String>> getCudappflags() {
        return getFlags(CUDAPPFLAGS);
    }

    public Optional<ImmutableList<String>> getHipflags() {
        return getFlags(HIPFLAGS);
    }

    public Optional<ImmutableList<String>> getHipppflags() {
        return getFlags(HIPPPFLAGS);
    }

    public Optional<ImmutableList<String>> getAsmflags() {
        return getFlags(ASMFLAGS);
    }

    public Optional<ImmutableList<String>> getAsmppflags() {
        return getFlags(ASMPPFLAGS);
    }

    public Optional<ImmutableList<String>> getLdflags() {
        return getFlags(LDFLAGS);
    }

    public Optional<ImmutableList<String>> getArflags() {
        return getFlags(ARFLAGS);
    }

    public Optional<ImmutableList<String>> getRanlibflags() {
        return getFlags(RANLIBFLAGS);
    }

    /*
     * Constructs the appropriate Archiver for the specified platform.
     */
    public Optional<ArchiverProvider> getArchiverProvider(Platform defaultPlatform) {
        Optional<ToolProvider> toolProvider = delegate.getView(ToolConfig.class).getToolProvider(cxxSection, AR);
        // TODO(cjhopman): This should probably accept ArchiverProvider.Type, not LegacyArchiverType.
        Optional<LegacyArchiverType> type = delegate.getEnum(cxxSection, ARCHIVER_TYPE, LegacyArchiverType.class);
        return toolProvider.map(archiver -> {
            Optional<Platform> archiverPlatform = delegate.getEnum(cxxSection, ARCHIVER_PLATFORM, Platform.class);

            Platform platform = archiverPlatform.orElse(defaultPlatform);
            return ArchiverProvider.from(archiver, platform, type);
        });
    }

    /** @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<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,
                EmptyTargetConfiguration.INSTANCE);
        Optional<CxxToolProvider.Type> type = delegate.getEnum(cxxSection, field + "_type",
                CxxToolProvider.Type.class);
        if (target.isPresent()) {
            return Optional.of(CxxToolProviderParams.builder().setSource(source).setBuildTarget(target.get())
                    .setType(type).setPreferDependencyTree(getUseDetailedUntrackedHeaderMessages()).build());
        } else {
            return Optional.of(CxxToolProviderParams.builder().setSource(source)
                    .setPath(delegate.getPathSourcePath(delegate.getRequiredPath(cxxSection, field))).setType(type)
                    .setPreferDependencyTree(getUseDetailedUntrackedHeaderMessages()).build());
        }
    }

    private Optional<PreprocessorProvider> getPreprocessorProvider(String field) {
        return getCxxToolProviderParams(field).map(AbstractCxxToolProviderParams::getPreprocessorProvider);
    }

    private Optional<CompilerProvider> getCompilerProvider(String field) {
        return getCxxToolProviderParams(field).map(AbstractCxxToolProviderParams::getCompilerProvider);
    }

    public Optional<CompilerProvider> getAs() {
        return getCompilerProvider(AS);
    }

    public Optional<PreprocessorProvider> getAspp() {
        return getPreprocessorProvider(ASPP);
    }

    public Optional<CompilerProvider> getCc() {
        return getCompilerProvider(CC);
    }

    public Optional<PreprocessorProvider> getCpp() {
        return getPreprocessorProvider(CPP);
    }

    public Optional<CompilerProvider> getCxx() {
        return getCompilerProvider(CXX);
    }

    public Optional<PreprocessorProvider> getCxxpp() {
        return getPreprocessorProvider(CXXPP);
    }

    public Optional<CompilerProvider> getCuda() {
        return getCompilerProvider(CUDA);
    }

    public Optional<PreprocessorProvider> getCudapp() {
        return getPreprocessorProvider(CUDAPP);
    }

    public Optional<CompilerProvider> getHip() {
        return getCompilerProvider(HIP);
    }

    public Optional<PreprocessorProvider> getHippp() {
        return getPreprocessorProvider(HIPPP);
    }

    public Optional<CompilerProvider> getAsm() {
        return getCompilerProvider(ASM);
    }

    public Optional<PreprocessorProvider> getAsmpp() {
        return getPreprocessorProvider(ASMPP);
    }

    public Optional<Boolean> getUseArgFile() {
        return delegate.getBoolean(cxxSection, USE_ARG_FILE);
    }

    /**
     * Construct a linker based on `ld` and `linker_platform` sections in the config.
     *
     * @param defaultType the default type for a linker if `linker_platform` is not specified in the
     *     config.
     */
    public Optional<LinkerProvider> getLinkerProvider(LinkerProvider.Type defaultType) {
        Optional<ToolProvider> toolProvider = delegate.getView(ToolConfig.class).getToolProvider(cxxSection, LD);
        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(), shouldCacheLinks()));
    }

    public HeaderVerification getHeaderVerificationOrIgnore() {
        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);
        return linkWeight.map(weight -> RuleScheduleInfo.builder().setJobsMultiplier(weight.intValue()).build());
    }

    public boolean shouldCacheLinks() {
        return delegate.getBooleanValue(cxxSection, CACHE_LINKS, true);
    }

    public boolean shouldCacheStrip() {
        return delegate.getBooleanValue(cxxSection, CACHE_STRIPS, true);
    }

    public boolean shouldCacheBinaries() {
        return delegate.getBooleanValue(cxxSection, CACHE_BINARIES, false);
    }

    public boolean isPCHEnabled() {
        return delegate.getBooleanValue(cxxSection, PCH_ENABLED, true);
    }

    public Optional<ArchiveContents> getArchiveContents() {
        return delegate.getEnum(cxxSection, ARCHIVE_CONTENTS, ArchiveContents.class);
    }

    public ImmutableMap<String, Flavor> getDefaultFlavorsForRuleType(RuleType 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);
    }

    /** @return whether to remap to the underlying host platform or to use #default */
    public boolean getShouldRemapHostPlatform() {
        return delegate.getBooleanValue(cxxSection, SHOULD_REMAP_HOST_PLATFORM, false);
    }

    private Optional<ToolProvider> getToolProvider(String name) {
        return delegate.getView(ToolConfig.class).getToolProvider(cxxSection, name);
    }

    public Optional<ToolProvider> getRanlib() {
        return getToolProvider(RANLIB);
    }

    public Optional<ToolProvider> getObjcopy() {
        return getToolProvider(OBJCOPY);
    }

    private Optional<Tool> getTool(String name) {
        return getPath(name).map(this::getSourcePath).map(HashedFileTool::new);
    }

    public Optional<Tool> getNm() {
        return getTool(NM);
    }

    public Optional<Tool> getStrip() {
        return getTool(STRIP);
    }

    public boolean isUniqueLibraryNameEnabled() {
        return delegate.getBooleanValue(cxxSection, UNIQUE_LIBRARY_NAME_ENABLED, false);
    }

    public boolean getDefaultReexportAllHeaderDependencies() {
        return delegate.getBooleanValue(cxxSection, DEFAULT_REEXPORT_ALL_HEADER_DEPENDENCIES, true);
    }

    /** @return whether to enable shared library interfaces. */
    public SharedLibraryInterfaceParams.Type getSharedLibraryInterfaces() {

        // Check for an explicit setting.
        Optional<SharedLibraryInterfaceParams.Type> setting = delegate.getEnum(cxxSection, SHLIB_INTERFACES,
                SharedLibraryInterfaceParams.Type.class);
        if (setting.isPresent()) {
            return setting.get();
        }

        // For backwards compatibility, check the older boolean setting.
        Optional<Boolean> oldSetting = delegate.getBoolean(cxxSection, SHARED_LIBRARY_INTERFACES);
        if (oldSetting.isPresent()) {
            return oldSetting.get() ? SharedLibraryInterfaceParams.Type.ENABLED
                    : SharedLibraryInterfaceParams.Type.DISABLED;
        }

        // Default.
        return SharedLibraryInterfaceParams.Type.DISABLED;
    }

    /**
     * @return additional flags to pass to the linker when linking independent shared library
     *     interfaces.
     */
    public Optional<ImmutableList<String>> getIndependentShlibInterfacesLdflags() {
        return getFlags(INDEPENDENT_SHLIB_INTERFACE_LDFLAGS);
    }

    /**
     * @return whether to generate a rule's shared library interface directly from it's object files,
     *     to avoid having to wait for it's shared library to build.
     */
    public boolean isIndependentSharedLibraryInterfaces() {
        return delegate.getBooleanValue(cxxSection, INDEPENDENT_SHLIB_INTERFACES, false);
    }

    /** @return the list of flavors that buck will consider valid when building the target graph. */
    public ImmutableSet<Flavor> getDeclaredPlatforms() {
        return delegate.getListWithoutComments(cxxSection, DECLARED_PLATFORMS).stream()
                .map(s -> UserFlavor.of(s, String.format("Declared platform: %s", s)))
                .collect(ImmutableSet.toImmutableSet());
    }

    /** @return the extension to use for shared libraries (e.g. ".so"). */
    public Optional<String> getSharedLibraryExtension() {
        return delegate.getValue(cxxSection, SHARED_LIBRARY_EXT);
    }

    /** @return the extension to use for static libraries (e.g. ".a"). */
    public Optional<String> getStaticLibraryExtension() {
        return delegate.getValue(cxxSection, STATIC_LIBRARY_EXT);
    }

    /** @return the extension to use for object files (e.g. ".o"). */
    public Optional<String> getObjectFileExtension() {
        return delegate.getValue(cxxSection, OBJECT_FILE_EXT);
    }

    public ImmutableSortedSet<String> getConflictingHeaderBasenameWhitelist() {
        return ImmutableSortedSet
                .copyOf(delegate.getListWithoutComments(cxxSection, CONFLICTING_HEADER_BASENAME_WHITELIST));
    }

    /** @return the configured C/C++ header mode. */
    public Optional<HeaderMode> getHeaderMode() {
        return delegate.getEnum(cxxSection, HEADER_MODE, HeaderMode.class);
    }

    /** @return whether to generate more detailed untracked header messages. */
    public Boolean getUseDetailedUntrackedHeaderMessages() {
        return delegate.getBooleanValue(cxxSection, DETAILED_UNTRACKED_HEADER_MESSAGES, false);
    }

    /** @return whether short names for intermediate files should be used */
    public Boolean getFilepathLengthLimited() {
        return delegate.getBooleanValue(cxxSection, FILEPATH_LENGTH_LIMITED, false);
    }

    public BuckConfig getDelegate() {
        return delegate;
    }

    /**
     * If the config specifies a value for "toolchain_target", returns a {@link UnresolvedCxxPlatform}
     * backed by the specified target.
     */
    public static Optional<UnresolvedCxxPlatform> getProviderBasedPlatform(BuckConfig config, Flavor flavor) {
        String cxxSection = new CxxBuckConfig(config, flavor).cxxSection;

        Optional<BuildTarget> toolchainTarget = config.getBuildTarget(cxxSection, TOOLCHAIN_TARGET,
                EmptyTargetConfiguration.INSTANCE);
        if (!toolchainTarget.isPresent()) {
            return Optional.empty();
        }

        if (!cxxSection.equals(UNFLAVORED_CXX_SECTION)) {
            // In a flavored cxx section, we don't allow any configuration except for configuration of the
            // platform.
            ImmutableMap<String, String> allEntries = config.getEntriesForSection(cxxSection);
            if (allEntries.size() != 1) {
                throw new HumanReadableException(
                        "When configuring a cxx %s, no other configuration is allowed in that section. Got unexpected keys [%s]",
                        TOOLCHAIN_TARGET, Joiner.on(", ")
                                .join(Sets.difference(allEntries.keySet(), ImmutableSet.of(TOOLCHAIN_TARGET))));
            }
        }

        return Optional.of(new ProviderBasedUnresolvedCxxPlatform(toolchainTarget.get(), flavor));
    }

    @Value.Immutable
    @BuckStyleImmutable
    abstract static class AbstractCxxToolProviderParams {

        public abstract String getSource();

        public abstract Optional<BuildTarget> getBuildTarget();

        public abstract Optional<PathSourcePath> getPath();

        public abstract Optional<CxxToolProvider.Type> getType();

        public abstract Optional<Boolean> getPreferDependencyTree();

        @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 {
                PathSourcePath path = getPath().get();
                return new PreprocessorProvider(new ConstantToolProvider(new HashedFileTool(path)),
                        () -> getType().orElseGet(() -> CxxToolTypeInferer.getTypeFromPath(path)));
            }
        }

        public CompilerProvider getCompilerProvider() {
            if (getBuildTarget().isPresent()) {
                return new CompilerProvider(new BinaryBuildRuleToolProvider(getBuildTarget().get(), getSource()),
                        getType().get(), false);
            } else {
                PathSourcePath path = getPath().get();
                return new CompilerProvider(new ConstantToolProvider(new HashedFileTool(path)),
                        () -> getType().orElseGet(() -> CxxToolTypeInferer.getTypeFromPath(path)),
                        getPreferDependencyTree().orElse(false));
            }
        }
    }
}