Permalink
Browse files

Add "toolchain-identifier" attribute to cc_toolchain rule

Currently the selection of C++ toolchain configuration depends on 2 different things:
1. CROSSTOOL file: a proto text file that contains multiple CrosstoolConfig.CToolchain messages, out of which we want to select the appropriate CToolchain;
2. cc_toolchain_suite rule, specified by --crosstool_top option: this rule contains "toolchains" map attribute. The map's keys are of type <cpu>|<compiler>, or just <cpu>, and the values are labels of cc_toolchain rules (which contain additional C++ information).

If there is an entry in cc_toolchain_suite.toolchains that corresponds to the --cpu and --compiler options, we use it, otherwise we loop through all the CToolchains in the CROSSTOOL file, select the one that corresponds to the --cpu and --compiler options, and then get the cc_toolchain label from cc_toolchain_suite.toolchains[toolchain.targetCpu|toolchain.compiler].
In both cases we read the CROSSTOOL file and pass all its information forward, to be used in creation of CppConfiguration and CcToolchainProvider creation.

As part of the efforts of rewriting CROSSTOOL in Skylark, we need to make obtaining the cc_toolchain label independent of the CROSSTOOL file and toolchain selection. As a step towards that goal, we add a new "toolchain_identifier" attribute to cc_toolchain, which uniquely identifies a CToolchain in the CROSSTOOL file.

Now the process of getting the CToolchain goes as follows:
Check for existence of cc_toolchain_suite.toolchains[<cpu>|<compiler>], if --compiler is specified, otherwise check for cc_toolchain_suite.toolchains[<cpu>].

1. if a value is found, load the cc_toolchain rule and look for the toolchain_identifier attribute.
    1.a if the attribute exists, loop through all the CToolchains in CROSSTOOL and select the one with the matching toolchain identifier.
    1.b otherwise fall back to selecting the CToolchain from CROSSTOOL by matching the --cpu and --compiler values.

2. If a value is not found, select the CToolchain from CROSSTOOL by matching the --cpu and --compiler values, and construct the key as follows: <toolchain.cpu>|<toolchain.compiler>.

In the future we will get rid of 2. by making sure that cc_toolchain_suite.toolchains[<cpu>|<compiler>] and cc_toolchain_suite.toolchains[<cpu>] are always defined.
1.b will be removed by making the cc_toolchain.toolchain_identifier attribute mandatory
After this deriving the cc_toolchain label will be independent of the CROSSTOOL file

1.a will ultimately be replaced by an attribute that points to a skylark_crosstool rule, that will contain all the information that CROSSTOOL contains, allowing us to remove CROSSTOOL altogether.

Work towards issue #5380
RELNOTES: None.
PiperOrigin-RevId: 200388550
  • Loading branch information...
scentini authored and Copybara-Service committed Jun 13, 2018
1 parent f9bf2e7 commit 857d4664ce939f240b1d10d8d2baca6c6893cfcb
@@ -176,7 +176,6 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
// TODO(b/78578234): Make this the default and remove the late-bound versions.
.add(attr("libc_top", LABEL).allowedFileTypes())
.add(attr(LIBC_TOP_ATTR, LABEL).value(LIBC_TOP_VALUE))

.add(attr(FDO_OPTIMIZE_ATTR, LABEL).singleArtifact().value(FDO_OPTIMIZE_VALUE))
.add(
attr(FDO_PROFILE_ATTR, LABEL)
@@ -194,6 +193,10 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
.value(CppRuleClasses.LIPO_CONTEXT_COLLECTOR)
.skipPrereqValidatorCheck())
.add(attr("proto", Type.STRING))
.add(
attr("toolchain_identifier", Type.STRING)
.nonconfigurable("Used in configuration creation")
.value(""))
.build();
}

@@ -55,6 +55,35 @@
* This class represents the C/C++ parts of the {@link BuildConfiguration}, including the host
* architecture, target architecture, compiler version, and a standard library version. It has
* information about the tools locations and the flags required for compiling.
*
* <p>Before {@link CppConfiguration} is created, two things need to be done:
*
* <ol>
* <li>choosing a {@link CcToolchainRule} label from {@code toolchains} map attribute of {@link
* CcToolchainSuiteRule}.
* <li>selection of a {@link
* com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain} from the
* CROSSTOOL file.
* </ol>
*
* <p>The process goes as follows:
*
* <p>Check for existence of {@link CcToolchainSuiteRule}.toolchains[<cpu>|<compiler>], if
* --compiler is specified, otherwise check for {@link CcToolchainSuiteRule}.toolchains[<cpu>].
*
* <ul>
* <li>if a value is found, load the {@link CcToolchainRule} rule and look for the {@code
* toolchain_identifier} attribute.
* <li>
* <ul>
* <li>if the attribute exists, loop through all the {@code CToolchain}s in CROSSTOOL and
* select the one with the matching toolchain identifier.
* <li>otherwise fall back to selecting the CToolchain from CROSSTOOL by matching the --cpu
* and --compiler values.
* </ul>
* <li>If a value is not found, select the CToolchain from CROSSTOOL by matching the --cpu and
* --compiler values, and construct the key as follows: <toolchain.cpu>|<toolchain.compiler>.
* </ul>
*/
@AutoCodec
@Immutable
@@ -32,6 +32,7 @@
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.syntax.Type;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig;
@@ -142,9 +143,6 @@ protected CppConfigurationParameters createParameters(
if (file == null) {
return null;
}
CrosstoolConfig.CToolchain toolchain =
CrosstoolConfigurationLoader.selectToolchain(
file.getProto(), options, cpuTransformer.getTransformer());

PathFragment fdoPath = null;
Label fdoProfileLabel = null;
@@ -168,32 +166,39 @@ protected CppConfigurationParameters createParameters(

Label ccToolchainLabel;
Target crosstoolTop;

CrosstoolConfig.CToolchain toolchain = null;
try {
crosstoolTop = env.getTarget(crosstoolTopLabel);
} catch (NoSuchThingException e) {
throw new IllegalStateException(e); // Should have been found out during redirect chasing
}

String desiredCpu = cpuTransformer.getTransformer().apply(options.get(Options.class).cpu);
if (crosstoolTop instanceof Rule
&& ((Rule) crosstoolTop).getRuleClass().equals("cc_toolchain_suite")) {
Rule ccToolchainSuite = (Rule) crosstoolTop;

String desiredCpu = cpuTransformer.getTransformer().apply(options.get(Options.class).cpu);
String key =
desiredCpu + (cppOptions.cppCompiler == null ? "" : ("|" + cppOptions.cppCompiler));
Map<String, Label> toolchains =
NonconfigurableAttributeMapper.of(ccToolchainSuite)
.get("toolchains", BuildType.LABEL_DICT_UNARY);
ccToolchainLabel = toolchains.get(key);
if (ccToolchainLabel == null) {
// If the cc_toolchain_suite does not contain entry for --cpu|--compiler (or only --cpu if
// --compiler is not present) we select the toolchain by looping through all the toolchains
// in the CROSSTOOL file and selecting the one that matches --cpu (and --compiler, if
// present). Then we use the toolchain.target_cpu|toolchain.compiler key to get the
// cc_toolchain label.
toolchain =
CrosstoolConfigurationLoader.selectToolchain(
file.getProto(), options, cpuTransformer.getTransformer());
ccToolchainLabel = toolchains.get(toolchain.getTargetCpu() + "|" + toolchain.getCompiler());
}
if (ccToolchainLabel == null) {
String errorMessage =
String.format(
"cc_toolchain_suite '%s' does not contain a toolchain for CPU '%s'",
crosstoolTopLabel, toolchain.getTargetCpu());
crosstoolTopLabel, desiredCpu);
if (cppOptions.cppCompiler != null) {
errorMessage = errorMessage + " and compiler " + cppOptions.cppCompiler;
}
@@ -221,6 +226,22 @@ protected CppConfigurationParameters createParameters(
"The label '%s' is not a cc_toolchain rule", ccToolchainLabel));
}

if (toolchain == null) {
// If cc_toolchain_suite contains an entry for the given --cpu and --compiler options, we
// select the toolchain by its identifier if "toolchain_identifier" attribute is present.
// Otherwise, we fall back to going through the CROSSTOOL file to select the toolchain using
// the legacy selection mechanism.
String identifier =
NonconfigurableAttributeMapper.of((Rule) ccToolchain)
.get("toolchain_identifier", Type.STRING);
toolchain =
identifier.isEmpty()
? CrosstoolConfigurationLoader.selectToolchain(
file.getProto(), options, cpuTransformer.getTransformer())
: CrosstoolConfigurationLoader.getToolchainByIdentifier(
file.getProto(), identifier, desiredCpu, cppOptions.cppCompiler);
}

Label sysrootLabel = getSysrootLabel(toolchain, cppOptions.libcTopLabel);

return new CppConfigurationParameters(
@@ -18,6 +18,7 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.io.BaseEncoding;
@@ -375,7 +376,7 @@ private static CrosstoolFile findCrosstoolConfiguration(
+ cpuBuilder
+ "]");
}
checkToolChain(selectedIdentifier, desiredCpu);
checkToolchain(selectedIdentifier, desiredCpu);

for (CrosstoolConfig.CToolchain toolchain : release.getToolchainList()) {
if (toolchain.getToolchainIdentifier().equals(selectedIdentifier)) {
@@ -406,15 +407,14 @@ private static void describeToolchainList(StringBuilder message,
}

/**
* 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]*").
* 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.
* @throws InvalidConfigurationException if selectedIdentifier does not match the aforementioned
* regular expression.
*/
private static void checkToolChain(String selectedIdentifier, String cpu)
private static void checkToolchain(String selectedIdentifier, String cpu)
throws InvalidConfigurationException {
// If you update this regex, please do so in the javadoc comment too, and also in the
// crosstool_config.proto file.
@@ -438,4 +438,47 @@ private static void checkToolChain(String selectedIdentifier, String cpu)
selectToolchain(file.getProto(), options, cpuTransformer);
return file.getProto();
}

/**
* Selects a crosstool toolchain based on the toolchain identifier.
*
* @throws InvalidConfigurationException if no matching toolchain can be found, if multiple
* toolchains with the same identifier are found, or if the target_cpu or compiler of the
* selected toolchain are not the same as --cpu and --compiler options.
*/
public static CrosstoolConfig.CToolchain getToolchainByIdentifier(
CrosstoolConfig.CrosstoolRelease proto,
String toolchainIdentifier,
String cpu,
@Nullable String compiler)
throws InvalidConfigurationException {
checkToolchain(toolchainIdentifier, cpu);
CrosstoolConfig.CToolchain selectedToolchain = null;
for (CrosstoolConfig.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));
}
if ((compiler != null && !selectedToolchain.getCompiler().equals(compiler))
|| !selectedToolchain.getTargetCpu().equals(cpu)) {
throw new InvalidConfigurationException(
String.format(
"The selected toolchain's cpu and compiler must match the command line options:\n"
+ " --cpu: %s, toolchain.target_cpu: %s\n"
+ " --compiler: %s, toolchain.compiler: %s",
cpu,
selectedToolchain.getTargetCpu(),
Strings.nullToEmpty(compiler),
selectedToolchain.getCompiler()));
}
return selectedToolchain;
}
}
Oops, something went wrong.

0 comments on commit 857d466

Please sign in to comment.