-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Do not rely on CppConfiguration package loading when selecting toolch…
…ain in cc_toolchain Rather re-select the toolchain for yourself. The next step will be to stop relying on CROSSTOOL in the CppConfiguration. #6072. RELNOTES: None. PiperOrigin-RevId: 212813981
- Loading branch information
Showing
15 changed files
with
403 additions
and
317 deletions.
There are no files selected for viewing
270 changes: 270 additions & 0 deletions
270
src/main/java/com/google/devtools/build/lib/rules/cpp/CToolchainSelectionUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
// Copyright 2018 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.base.Preconditions; | ||
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; | ||
import com.google.devtools.build.lib.util.StringUtil; | ||
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig; | ||
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain; | ||
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import javax.annotation.Nullable; | ||
|
||
/** | ||
* Utils class for logic responsible for selecting a CToolchain from the CROSSTOOL file given the | ||
* cc_toolchain. | ||
*/ | ||
public class CToolchainSelectionUtils { | ||
|
||
/** | ||
* Do-it-all CToolchain selection method that considers toolchain identifiers, cpu/compiler | ||
* attributes, and cpu/compiler options. Returns the CToolchain from the CROSSTOOL. If you need to | ||
* call anything else than this, you're holding it wrong. | ||
* | ||
* @param identifierAttribute value of the cc_toolchain.toolchain_identifier attribute | ||
* @param cpuAttribute value of the cc_toolchain.cpu attribute | ||
* @param compilerAttribute value of the cc_toolchain.compiler attribute | ||
* @param cpuOption value of the --cpu option | ||
* @param compilerOption value of the --compiler option | ||
* @param proto content of the CROSSTOOL file | ||
* @param cpuTransformer because life is hard | ||
* @return selected CToolchain or throws InvalidConfigurationException when not found. Never | ||
* returns null. | ||
*/ | ||
public static CToolchain selectCToolchain( | ||
@Nullable String identifierAttribute, | ||
@Nullable String cpuAttribute, | ||
@Nullable String compilerAttribute, | ||
String cpuOption, | ||
@Nullable String compilerOption, | ||
CrosstoolRelease proto, | ||
Function<String, String> cpuTransformer) | ||
throws InvalidConfigurationException { | ||
String identifierAttributeOrNull = StringUtil.emptyToNull(identifierAttribute); | ||
String cpuAttributeOrNull = StringUtil.emptyToNull(cpuAttribute); | ||
String compilerAttributeOrNull = StringUtil.emptyToNull(compilerAttribute); | ||
|
||
Preconditions.checkNotNull(cpuOption); | ||
String compilerOptionOrNull = StringUtil.emptyToNull(compilerOption); | ||
|
||
return selectCToolchainNoEmptyStrings( | ||
identifierAttributeOrNull, | ||
cpuAttributeOrNull, | ||
compilerAttributeOrNull, | ||
cpuOption, | ||
compilerOptionOrNull, | ||
proto, | ||
cpuTransformer); | ||
} | ||
|
||
private static CToolchain selectCToolchainNoEmptyStrings( | ||
String identifierAttribute, | ||
String cpuAttribute, | ||
String compilerAttribute, | ||
String cpuOption, | ||
String compilerOption, | ||
CrosstoolRelease proto, | ||
Function<String, String> cpuTransformer) | ||
throws InvalidConfigurationException { | ||
CToolchain cToolchain = null; | ||
// Use the identifier to find the CToolchain from the CROSSTOOL (this is the way how | ||
// cc_toolchain will select CToolchain in the happy future, since it works with platforms). | ||
if (identifierAttribute != null) { | ||
cToolchain = getToolchainByIdentifier(proto, identifierAttribute); | ||
} | ||
if (cToolchain == null && cpuAttribute != null) { | ||
// Let's try to select the CToolchain using cpu and compiler rule attributes (the semi-new | ||
// way, compatible with platforms). | ||
try { | ||
cToolchain = | ||
selectToolchainUsingCpuAndMaybeCompiler( | ||
proto, | ||
new CrosstoolConfigurationIdentifier(cpuAttribute, compilerAttribute), | ||
cpuTransformer); | ||
} catch (InvalidConfigurationException e) { | ||
// We couldn't find the CToolchain using attributes, let's catch the exception and try | ||
// with options. It's safe to ignore the exception here, since if it was caused by | ||
// something else than the selection, it will be re-thrown below. | ||
} | ||
} | ||
if (cToolchain == null) { | ||
// We couldn't find the CToolchain using cpu and compiler attributes, let's try to select | ||
// it using --cpu/--compiler options (the legacy way, doesn't work with platforms). | ||
cToolchain = | ||
selectToolchainUsingCpuAndMaybeCompiler( | ||
proto, | ||
new CrosstoolConfigurationIdentifier(cpuOption, compilerOption), | ||
cpuTransformer); | ||
} | ||
return cToolchain; | ||
} | ||
|
||
/** | ||
* Selects a crosstool toolchain based on the toolchain identifier. | ||
* | ||
* @throws InvalidConfigurationException if no matching toolchain can be found, or if multiple | ||
* toolchains with the same identifier are found. | ||
*/ | ||
private static CToolchain getToolchainByIdentifier( | ||
CrosstoolRelease proto, String toolchainIdentifier) throws InvalidConfigurationException { | ||
checkToolchain(toolchainIdentifier); | ||
CToolchain selectedToolchain = null; | ||
for (CToolchain toolchain : proto.getToolchainList()) { | ||
if (toolchain.getToolchainIdentifier().equals(toolchainIdentifier)) { | ||
if (selectedToolchain != null) { | ||
throw new InvalidConfigurationException( | ||
String.format("Multiple toolchains with '%s' identifier", toolchainIdentifier)); | ||
} | ||
selectedToolchain = toolchain; | ||
} | ||
} | ||
if (selectedToolchain == null) { | ||
throw new InvalidConfigurationException( | ||
String.format("Toolchain identifier '%s' was not found", toolchainIdentifier)); | ||
} | ||
return selectedToolchain; | ||
} | ||
|
||
/** | ||
* Makes sure that {@code selectedIdentifier} is a valid identifier for a toolchain, i.e. it | ||
* starts with a letter or an underscore and continues with only dots, dashes, spaces, letters, | ||
* digits or underscores (i.e. matches the following regular expression: "[a-zA-Z_][\.\- \w]*"). | ||
* | ||
* @throws InvalidConfigurationException if selectedIdentifier does not match the aforementioned | ||
* regular expression. | ||
*/ | ||
private static void checkToolchain(String selectedIdentifier) | ||
throws InvalidConfigurationException { | ||
// If you update this regex, please do so in the javadoc comment too, and also in the | ||
// crosstool_config.proto file. | ||
String rx = "[a-zA-Z_][\\.\\- \\w]*"; | ||
if (!selectedIdentifier.matches(rx)) { | ||
throw new InvalidConfigurationException( | ||
String.format( | ||
"Toolchain identifier '%s' is illegal (does not match '%s')", | ||
selectedIdentifier, rx)); | ||
} | ||
} | ||
|
||
/** | ||
* Selects a crosstool toolchain corresponding to the given crosstool configuration options. If | ||
* all of these options are null, it returns the default toolchain specified in the crosstool | ||
* release. If only cpu is non-null, it returns the default toolchain for that cpu, as specified | ||
* in the crosstool release. Otherwise, all values must be non-null, and this method returns the | ||
* toolchain which matches all of the values. | ||
* | ||
* @throws NullPointerException if {@code release} is null | ||
* @throws InvalidConfigurationException if no matching toolchain can be found, or if the input | ||
* parameters do not obey the constraints described above | ||
*/ | ||
public static CToolchain selectToolchainUsingCpuAndMaybeCompiler( | ||
CrosstoolRelease release, | ||
CrosstoolConfigurationIdentifier config, | ||
Function<String, String> cpuTransformer) | ||
throws InvalidConfigurationException { | ||
if (config.getCompiler() != null) { | ||
ArrayList<CToolchain> candidateToolchains = new ArrayList<>(); | ||
for (CToolchain toolchain : release.getToolchainList()) { | ||
if (config.isCandidateToolchain(toolchain)) { | ||
candidateToolchains.add(toolchain); | ||
} | ||
} | ||
switch (candidateToolchains.size()) { | ||
case 0: | ||
{ | ||
StringBuilder message = new StringBuilder(); | ||
message.append("No toolchain found for"); | ||
message.append(config.describeFlags()); | ||
message.append(". Valid toolchains are: "); | ||
describeToolchainList(message, release.getToolchainList()); | ||
throw new InvalidConfigurationException(message.toString()); | ||
} | ||
case 1: | ||
return candidateToolchains.get(0); | ||
default: | ||
{ | ||
StringBuilder message = new StringBuilder(); | ||
message.append("Multiple toolchains found for"); | ||
message.append(config.describeFlags()); | ||
message.append(": "); | ||
describeToolchainList(message, candidateToolchains); | ||
throw new InvalidConfigurationException(message.toString()); | ||
} | ||
} | ||
} | ||
String selectedIdentifier = null; | ||
// We use fake CPU values to allow cross-platform builds for other languages that use the | ||
// C++ toolchain. Translate to the actual target architecture. | ||
String desiredCpu = cpuTransformer.apply(config.getCpu()); | ||
for (CrosstoolConfig.DefaultCpuToolchain selector : release.getDefaultToolchainList()) { | ||
if (selector.getCpu().equals(desiredCpu)) { | ||
selectedIdentifier = selector.getToolchainIdentifier(); | ||
break; | ||
} | ||
} | ||
|
||
if (selectedIdentifier == null) { | ||
HashSet<String> seenCpus = new HashSet<>(); | ||
StringBuilder cpuBuilder = new StringBuilder(); | ||
for (CrosstoolConfig.DefaultCpuToolchain selector : release.getDefaultToolchainList()) { | ||
if (seenCpus.add(selector.getCpu())) { | ||
cpuBuilder.append(" ").append(selector.getCpu()).append(",\n"); | ||
} | ||
} | ||
throw new InvalidConfigurationException( | ||
"No default_toolchain found for cpu '" | ||
+ desiredCpu | ||
+ "'. Valid cpus are: [\n" | ||
+ cpuBuilder | ||
+ "]"); | ||
} | ||
checkToolchain(selectedIdentifier); | ||
|
||
for (CToolchain toolchain : release.getToolchainList()) { | ||
if (toolchain.getToolchainIdentifier().equals(selectedIdentifier)) { | ||
return toolchain; | ||
} | ||
} | ||
|
||
throw new InvalidConfigurationException( | ||
"Inconsistent crosstool configuration; no toolchain " | ||
+ "corresponding to '" | ||
+ selectedIdentifier | ||
+ "' found for cpu '" | ||
+ config.getCpu() | ||
+ "'"); | ||
} | ||
|
||
/** | ||
* Appends a series of toolchain descriptions (as the blaze command line flags that would specify | ||
* that toolchain) to 'message'. | ||
*/ | ||
private static void describeToolchainList( | ||
StringBuilder message, Collection<CToolchain> toolchains) { | ||
message.append("[\n"); | ||
for (CrosstoolConfig.CToolchain toolchain : toolchains) { | ||
message.append(" "); | ||
message.append(toolchain.getToolchainIdentifier()); | ||
message.append(": "); | ||
message.append( | ||
CrosstoolConfigurationIdentifier.fromToolchain(toolchain).describeFlags().trim()); | ||
message.append(",\n"); | ||
} | ||
message.append("]"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.