// Copyright 2014 The Bazel Authors. All rights reserved. // // 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.google.devtools.build.lib.rules.cpp; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.RedirectChaser; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment; import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; import com.google.devtools.build.lib.analysis.config.FragmentOptions; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.InputFile; import com.google.devtools.build.lib.packages.NoSuchPackageException; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.NoSuchThingException; import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig; import com.google.devtools.common.options.OptionsParsingException; import javax.annotation.Nullable; /** * Loader for C++ configurations. */ public class CppConfigurationLoader implements ConfigurationFragmentFactory { @Override public Class<? extends Fragment> creates() { return CppConfiguration.class; } @Override public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() { return ImmutableSet.<Class<? extends FragmentOptions>>of(CppOptions.class); } private final Function<String, String> cpuTransformer; /** * Creates a new CrosstoolConfigurationLoader instance with the given * configuration provider. The configuration provider is used to perform * caller-specific configuration file lookup. */ public CppConfigurationLoader(Function<String, String> cpuTransformer) { this.cpuTransformer = cpuTransformer; } @Override public CppConfiguration create(ConfigurationEnvironment env, BuildOptions options) throws InvalidConfigurationException, InterruptedException { CppConfigurationParameters params = createParameters(env, options); if (params == null) { return null; } CppConfiguration cppConfig = new CppConfiguration(params); if (options.get(BuildConfiguration.Options.class).useDynamicConfigurations != BuildConfiguration.Options.DynamicConfigsMode.OFF && (cppConfig.isFdo() || cppConfig.getLipoMode() != CrosstoolConfig.LipoMode.OFF)) { throw new InvalidConfigurationException( "LIPO does not currently work with dynamic configurations"); } return cppConfig; } /** * Value class for all the data needed to create a {@link CppConfiguration}. */ public static class CppConfigurationParameters { protected final CrosstoolConfig.CToolchain toolchain; protected final String cacheKeySuffix; protected final BuildConfiguration.Options commonOptions; protected final CppOptions cppOptions; protected final Label crosstoolTop; protected final Label ccToolchainLabel; protected final Label stlLabel; protected final Path fdoZip; protected final Label sysrootLabel; CppConfigurationParameters( CrosstoolConfig.CToolchain toolchain, String cacheKeySuffix, BuildOptions buildOptions, Path fdoZip, Label crosstoolTop, Label ccToolchainLabel, Label stlLabel, Label sysrootLabel) { this.toolchain = toolchain; this.cacheKeySuffix = cacheKeySuffix; this.commonOptions = buildOptions.get(BuildConfiguration.Options.class); this.cppOptions = buildOptions.get(CppOptions.class); this.fdoZip = fdoZip; this.crosstoolTop = crosstoolTop; this.ccToolchainLabel = ccToolchainLabel; this.stlLabel = stlLabel; this.sysrootLabel = sysrootLabel; } } @Nullable protected CppConfigurationParameters createParameters( ConfigurationEnvironment env, BuildOptions options) throws InvalidConfigurationException, InterruptedException { BlazeDirectories directories = env.getBlazeDirectories(); if (directories == null) { return null; } Label crosstoolTopLabel = RedirectChaser.followRedirects(env, options.get(CppOptions.class).crosstoolTop, "crosstool_top"); if (crosstoolTopLabel == null) { return null; } CppOptions cppOptions = options.get(CppOptions.class); Label stlLabel = null; if (cppOptions.stl != null) { stlLabel = RedirectChaser.followRedirects(env, cppOptions.stl, "stl"); if (stlLabel == null) { return null; } } CrosstoolConfigurationLoader.CrosstoolFile file = CrosstoolConfigurationLoader.readCrosstool(env, crosstoolTopLabel); if (file == null) { return null; } CrosstoolConfig.CToolchain toolchain = CrosstoolConfigurationLoader.selectToolchain(file.getProto(), options, cpuTransformer); // FDO // TODO(bazel-team): move this to CppConfiguration.prepareHook Path fdoZip; if (cppOptions.getFdoOptimize() == null) { fdoZip = null; } else if (cppOptions.getFdoOptimize().startsWith("//")) { try { Target target = env.getTarget(Label.parseAbsolute(cppOptions.getFdoOptimize())); if (target == null) { return null; } if (!(target instanceof InputFile)) { throw new InvalidConfigurationException( "--fdo_optimize cannot accept targets that do not refer to input files"); } fdoZip = env.getPath(target.getPackage(), target.getName()); if (fdoZip == null) { throw new InvalidConfigurationException( "The --fdo_optimize parameter you specified resolves to a file that does not exist"); } } catch (NoSuchPackageException | NoSuchTargetException | LabelSyntaxException e) { env.getEventHandler().handle(Event.error(e.getMessage())); throw new InvalidConfigurationException(e); } } else { fdoZip = directories.getWorkspace().getRelative(cppOptions.getFdoOptimize()); try { // We don't check for file existence, but at least the filename should be well-formed. FileSystemUtils.checkBaseName(fdoZip.getBaseName()); } catch (IllegalArgumentException e) { throw new InvalidConfigurationException(e); } } Label ccToolchainLabel; Target crosstoolTop; try { crosstoolTop = env.getTarget(crosstoolTopLabel); } catch (NoSuchThingException e) { throw new IllegalStateException(e); // Should have been found out during redirect chasing } if (crosstoolTop instanceof Rule && ((Rule) crosstoolTop).getRuleClass().equals("cc_toolchain_suite")) { Rule ccToolchainSuite = (Rule) crosstoolTop; ccToolchainLabel = NonconfigurableAttributeMapper.of(ccToolchainSuite) .get("toolchains", BuildType.LABEL_DICT_UNARY) .get(toolchain.getTargetCpu() + "|" + toolchain.getCompiler()); if (ccToolchainLabel == null) { throw new InvalidConfigurationException(String.format( "cc_toolchain_suite '%s' does not contain a toolchain for CPU '%s' and compiler '%s'", crosstoolTopLabel, toolchain.getTargetCpu(), toolchain.getCompiler())); } } else { throw new InvalidConfigurationException(String.format( "The specified --crosstool_top '%s' is not a valid cc_toolchain_suite rule", crosstoolTopLabel)); } Target ccToolchain; try { ccToolchain = env.getTarget(ccToolchainLabel); if (ccToolchain == null) { return null; } } catch (NoSuchThingException e) { throw new InvalidConfigurationException(String.format( "The toolchain rule '%s' does not exist", ccToolchainLabel)); } if (!(ccToolchain instanceof Rule) || !CcToolchainRule.isCcToolchain(ccToolchain)) { throw new InvalidConfigurationException(String.format( "The label '%s' is not a cc_toolchain rule", ccToolchainLabel)); } Label sysrootLabel = getSysrootLabel(toolchain, cppOptions.libcTopLabel); return new CppConfigurationParameters( toolchain, file.getMd5(), options, fdoZip, crosstoolTopLabel, ccToolchainLabel, stlLabel, sysrootLabel); } @Nullable public static Label getSysrootLabel(CrosstoolConfig.CToolchain toolchain, Label libcTopLabel) throws InvalidConfigurationException { PathFragment defaultSysroot = CppConfiguration.computeDefaultSysroot(toolchain); if ((libcTopLabel != null) && (defaultSysroot == null)) { throw new InvalidConfigurationException( "The selected toolchain " + toolchain.getToolchainIdentifier() + " does not support setting --grte_top."); } if (libcTopLabel != null) { return libcTopLabel; } if (!toolchain.getDefaultGrteTop().isEmpty()) { try { Label grteTopLabel = new CppOptions.LibcTopLabelConverter().convert(toolchain.getDefaultGrteTop()); return grteTopLabel; } catch (OptionsParsingException e) { throw new InvalidConfigurationException(e.getMessage(), e); } } return null; } }