blob: ebdf33d7ab3fed7f7579a8a4f54847ee432ef2ac [file] [log] [blame]
// 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.config;
import static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_KEYED_STRING_DICT;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.syntax.Type.STRING;
import static com.google.devtools.build.lib.syntax.Type.STRING_DICT;
import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.analysis.BaseRuleClasses;
import com.google.devtools.build.lib.analysis.PlatformConfiguration;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.packages.Attribute.ComputedDefault;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.syntax.Type;
/**
* Definitions for rule classes that specify or manipulate configuration settings.
*
* <p>These are not "traditional" rule classes in that they can't be requested as top-level
* targets and don't translate input artifacts into output artifacts. Instead, they affect
* how *other* rules work. See individual class comments for details.
*/
public class ConfigRuleClasses {
private static final String NONCONFIGURABLE_ATTRIBUTE_REASON =
"part of a rule class that *triggers* configurable behavior";
/**
* Common settings for all configurability rules.
*/
public static final class ConfigBaseRule implements RuleDefinition {
@Override
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
return builder
.override(attr("tags", Type.STRING_LIST)
// No need to show up in ":all", etc. target patterns.
.value(ImmutableList.of("manual"))
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
.exemptFromConstraintChecking(
"these rules don't include content that gets built into their dependers")
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$config_base_rule")
.type(RuleClass.Builder.RuleClassType.ABSTRACT)
.ancestors(BaseRuleClasses.BaseRule.class)
.build();
}
}
/**
* A named "partial configuration setting" that specifies a set of command-line "flag=value"
* bindings.
*
* <p>For example:
*
* <pre>
* config_setting(
* name = 'foo',
* values = {
* 'flag1': 'aValue'
* 'flag2': 'bValue'
* })
* </pre>
*
* <p>declares a setting that binds command-line flag
*
* <pre>flag1</pre>
*
* to value
*
* <pre>aValue</pre>
*
* and
*
* <pre>flag2</pre>
*
* to
*
* <pre>bValue</pre>
*
* .
*
* <p>This is used by configurable attributes to determine which branch to follow based on which
*
* <pre>config_setting</pre>
*
* instance matches all its flag values in the configurable attribute owner's configuration.
*
* <p>This rule isn't accessed through the standard {@link RuleContext#getPrerequisites}
* interface. This is because Bazel constructs a rule's configured attribute map *before* its
* {@link RuleContext} is created (in fact, the map is an input to the context's constructor). And
* the config_settings referenced by the rule's configurable attributes are themselves inputs to
* that map. So Bazel has special logic to read and properly apply config_setting instances. See
* {@link com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction#getConfigConditions} for
* details.
*/
public static final class ConfigSettingRule implements RuleDefinition {
/**
* The name of this rule.
*/
public static final String RULE_NAME = "config_setting";
/** The name of the attribute that declares flag bindings. */
public static final String SETTINGS_ATTRIBUTE = "values";
/** The name of the attribute that declares "--define foo=bar" flag bindings.*/
public static final String DEFINE_SETTINGS_ATTRIBUTE = "define_values";
/** The name of the attribute that declares user-defined flag bindings. */
public static final String FLAG_SETTINGS_ATTRIBUTE = "flag_values";
/** The name of the attribute that declares constraint_values. */
public static final String CONSTRAINT_VALUES_ATTRIBUTE = "constraint_values";
/** The name of the tools repository. */
public static final String TOOLS_REPOSITORY_ATTRIBUTE = "$tools_repository";
@Override
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
return builder
.setIgnoreLicenses()
.requiresConfigurationFragments(PlatformConfiguration.class)
.add(
attr(TOOLS_REPOSITORY_ATTRIBUTE, STRING)
.value(
new ComputedDefault() {
@Override
public Object getDefault(AttributeMap rule) {
return env.getToolsRepository();
}
}))
/* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(values) -->
The set of configuration values that match this rule (expressed as Bazel flags)
<p>This rule inherits the configuration of the configured target that
references it in a <code>select</code> statement. It is considered to
"match" a Bazel invocation if, for every entry in the dictionary, its
configuration matches the entry's expected value. For example
<code>values = {"compilation_mode": "opt"}</code> matches the invocations
<code>bazel build --compilation_mode=opt ...</code> and
<code>bazel build -c opt ...</code> on target-configured rules.
</p>
<p>For convenience's sake, configuration values are specified as Bazel flags (without
the preceding <code>"--"</code>). But keep in mind that the two are not the same. This
is because targets can be built in multiple configurations within the same
build. For example, a host configuration's "cpu" matches the value of
<code>--host_cpu</code>, not <code>--cpu</code>. So different instances of the
same <code>config_setting</code> may match the same invocation differently
depending on the configuration of the rule using them.
</p>
<p>If a flag is not explicitly set at the command line, its default value is used.
If a key appears multiple times in the dictionary, only the last instance is used.
If a key references a flag that can be set multiple times on the command line (e.g.
<code>bazel build --copt=foo --copt=bar --copt=baz ...</code>), a match occurs if
<i>any</i> of those settings match.
<p>
<p>This and <a href="${link config_setting.define_values}"><code>define_values</code></a>
cannot both be empty.
</p>
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(
attr(SETTINGS_ATTRIBUTE, STRING_DICT)
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
/* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(define_values) -->
The same as <a href="${link config_setting.values}"><code>values</code></a> but
specifically for the <code>--define</code> flag.
<p><code>--define</code> is special because its syntax (<code>--define KEY=VAL</code>)
means <code>KEY=VAL</code> is a <i>value</i> from a Bazel flag perspective.
</p>
<p>That means:
<pre class="code">
config_setting(
name = "a_and_b",
values = {
"define": "a=1",
"define": "b=2",
})
</pre>
<p>doesn't work because the same key (<code>define</code>) appears twice in the
dictionary. This attribute solves that problem:
<pre class="code">
config_setting(
name = "a_and_b",
define_values = {
"a": "1",
"b": "2",
})
</pre>
<p>correctly matches <code>bazel build //foo --define a=1 --define b=2</code>.
<p><code>--define</code> can still appear in
<a href="${link config_setting.values}"><code>values</code></a> with normal flag syntax,
and can be mixed freely with this attribute as long as dictionary keys remain distinct.
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(
attr(DEFINE_SETTINGS_ATTRIBUTE, STRING_DICT)
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
/* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(flag_values) -->
The same as <a href="${link config_setting.values}"><code>values</code></a> but
for <a href="https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings">
Starlark-defined flags</a>.
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
// Originally this attribute was a map of feature flags targets -> feature flag values,
// the latter of which are always strings. Now it also includes starlark build setting
// targets -> starlark build setting values. In other places in the starlark configuration
// API, starlark setting values are passed as their actual object instead of a string
// representation. It would be more consistent to be able to pass starlark setting values
// as objects to this attribute as well. But attributes are strongly-typed so
// label->object dict is not an option for attribute types right now.
.add(
attr(FLAG_SETTINGS_ATTRIBUTE, LABEL_KEYED_STRING_DICT)
.allowedFileTypes()
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
/* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(constraint_values) -->
The minimum set of <code>constraint_values</code> that the target platform must specify
in order to match this <code>config_setting</code>. (The execution platform is not
considered here.) Any additional constraint values that the platform has are ignored. See
<a href="https://docs.bazel.build/versions/master/configurable-attributes.html#platforms">
Configurable Build Attributes</a> for details.
<p>In the case where two <code>config_setting</code>s both match in the same
<code>select</code>, this attribute is not considered for the purpose of determining
whether one of the <code>config_setting</code>s is a specialization of the other. In other
words, one <code>config_setting</code> cannot match a platform more strongly than another.
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(
attr(CONSTRAINT_VALUES_ATTRIBUTE, LABEL_LIST)
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON)
.allowedFileTypes())
.setOptionReferenceFunctionForConfigSettingOnly(
rule ->
NonconfigurableAttributeMapper.of(rule)
.get(SETTINGS_ATTRIBUTE, Type.STRING_DICT)
.keySet())
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name(RULE_NAME)
.type(RuleClass.Builder.RuleClassType.NORMAL)
.ancestors(ConfigBaseRule.class)
.factoryClass(ConfigSetting.class)
.build();
}
}
/*<!-- #BLAZE_RULE (NAME = config_setting, TYPE = OTHER, FAMILY = General)[GENERIC_RULE] -->
<p>
Matches an expected configuration state (expressed as Bazel flags or platform constraints) for
the purpose of triggering configurable attributes. See <a href="${link select}">select</a> for
how to consume this rule and <a href="${link common-definitions#configurable-attributes}">
Configurable attributes</a> for an overview of the general feature.
<h4 id="config_setting_examples">Examples</h4>
<p>The following matches any Bazel invocation that specifies <code>--compilation_mode=opt</code>
or <code>-c opt</code> (either explicitly at the command line or implicitly from .blazerc
files):
</p>
<pre class="code">
config_setting(
name = "simple",
values = {"compilation_mode": "opt"}
)
</pre>
<p>The following matches any Bazel invocation that builds for ARM and that applies the custom
define <code>FOO=bar</code> (for instance, <code>bazel build --cpu=arm --define FOO=bar ...
</code>):
</p>
<pre class="code">
config_setting(
name = "two_conditions",
values = {
"cpu": "arm",
"define": "FOO=bar"
}
)
</pre>
<p>The following matches any Bazel invocation that builds for a platform that has an x86_64
architecture and glibc version 2.25, assuming the existence of a <code>constraint_value</code>
with label <code>//example:glibc_2_25</code>. Note that a platform still matches if it defines
additional constraint values beyond these two.
</p>
<pre class=""code">
config_setting(
name = "64bit_glibc_2_25",
constraint_values = [
"@platforms//cpu:x86_64",
"//example:glibc_2_25",
]
)
</pre>
In all these cases, it is possible for the configuration state to change during the build, for
instance if a dependency of a target needs to be built for a different platform than the target
itself. This means that even when a <code>config_setting</code> does not match the top-level
command-line flags, it may still match a deeper part of the build, and vice versa.
<h4 id="config_setting_notes">Notes</h4>
<p>See <a href="${link select}">select</a> for what happens when multiple
<code>config_setting</code>s match the current configuration state.
</p>
<p>For flags that support shorthand forms (e.g. <code>--compilation_mode</code> vs.
<code>-c</code>), <code>values</code> definitions must use the full form. These automatically
match invocations using either form.
</p>
<p>
If a flag takes multiple values (like <code>--define a=b --define c=d</code> or a list-typed
<a href="https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings">
Starlark flag</a>), a <code>config_setting</code> that expects <code>"foo"</code> matches if
<code>"foo"</code> is present <i>anywhere</i> in the actual list.
</p>
<p>If you need to define conditions that aren't modeled by built-in Bazel flags, use
<a href="https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings">
Starlark-defined flags</a>. You can also use <code>--define</code>, but this offers weaker
support and is not recommended. See <a href="${link common-definitions#configurable-attributes}">
for more discussion.
</p>
<p>Avoid repeating identical <code>config_setting</code> definitions in different packages.
Instead, prefer to reference a common <code>config_setting</code> target that is defined in a
single package.
</p>
<p><a href="general.html#config_setting.values"><code>values</code></a>,
<a href="general.html#config_setting.define_values"><code>define_values</code></a>, and
<a href="general.html#config_setting.constraint_values"><code>constraint_values</code></a>
can be used in any combination in the same <code>config_setting</code> but at least one must be
set for any given <code>config_setting</code>.
</p>
<!-- #END_BLAZE_RULE -->*/
/** Rule definition for Android's config_feature_flag rule. */
public static final class ConfigFeatureFlagRule implements RuleDefinition {
public static final String RULE_NAME = "config_feature_flag";
@Override
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
return builder
.setUndocumented(/* the feature flag feature has not yet been launched */)
.requiresConfigurationFragments(ConfigFeatureFlagConfiguration.class)
.add(
attr("allowed_values", STRING_LIST)
.mandatory()
.nonEmpty()
.orderIndependent()
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
.add(
attr("default_value", STRING)
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
.add(ConfigFeatureFlag.getWhitelistAttribute(env))
.removeAttribute(BaseRuleClasses.TAGGED_TRIMMING_ATTR)
.build();
}
@Override
public RuleDefinition.Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name(RULE_NAME)
.ancestors(ConfigBaseRule.class)
.factoryClass(ConfigFeatureFlag.class)
.build();
}
}
}