mirror of https://github.com/bazelbuild/rules_cc
618 lines
23 KiB
Python
618 lines
23 KiB
Python
# 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.
|
|
|
|
""" A library of functions creating structs for CcToolchainConfigInfo."""
|
|
|
|
def _check_is_none(obj, parameter_name, method_name):
|
|
if obj != None:
|
|
fail("{} parameter of {} should be None, found {}."
|
|
.format(parameter_name, method_name, type(obj)))
|
|
|
|
def _check_is_none_or_right_type(obj, obj_of_right_type, parameter_name, method_name):
|
|
if obj != None:
|
|
_check_same_type(obj, obj_of_right_type, parameter_name, method_name)
|
|
|
|
def _check_right_type(obj, expected_type, parameter_name, method_name):
|
|
if type(obj) != expected_type:
|
|
fail("{} parameter of {} should be a {}, found {}."
|
|
.format(parameter_name, method_name, expected_type, type(obj)))
|
|
|
|
def _check_same_type(obj, obj_of_right_type, parameter_name, method_name):
|
|
_check_right_type(obj, type(obj_of_right_type), parameter_name, method_name)
|
|
|
|
def _check_is_nonempty_string(obj, parameter_name, method_name):
|
|
_check_same_type(obj, "", parameter_name, method_name)
|
|
if obj == "":
|
|
fail("{} parameter of {} must be a nonempty string."
|
|
.format(parameter_name, method_name))
|
|
|
|
def _check_is_nonempty_list(obj, parameter_name, method_name):
|
|
_check_same_type(obj, [], parameter_name, method_name)
|
|
if len(obj) == 0:
|
|
fail("{} parameter of {} must be a nonempty list."
|
|
.format(parameter_name, method_name))
|
|
|
|
EnvEntryInfo = provider(
|
|
"A key/value pair to be added as an environment variable.",
|
|
fields = ["key", "value", "expand_if_available", "type_name"],
|
|
)
|
|
|
|
def env_entry(key, value, expand_if_available = None):
|
|
""" A key/value pair to be added as an environment variable.
|
|
|
|
The returned EnvEntry provider finds its use in EnvSet creation through
|
|
the env_entries parameter of env_set(); EnvSet groups environment variables
|
|
that need to be expanded for specific actions.
|
|
The value of this pair is expanded in the same way as is described in
|
|
flag_group. The key remains an unexpanded string literal.
|
|
|
|
Args:
|
|
key: a string literal representing the name of the variable.
|
|
value: the value to be expanded.
|
|
expand_if_available: A build variable that needs to be available
|
|
in order to expand the env_entry.
|
|
|
|
Returns:
|
|
An EnvEntryInfo provider.
|
|
"""
|
|
_check_is_nonempty_string(key, "key", "env_entry")
|
|
_check_is_nonempty_string(value, "value", "env_entry")
|
|
_check_is_none_or_right_type(expand_if_available, "string", "expand_if_available", "env_entry")
|
|
return EnvEntryInfo(
|
|
key = key,
|
|
value = value,
|
|
expand_if_available = expand_if_available,
|
|
type_name = "env_entry",
|
|
)
|
|
|
|
VariableWithValueInfo = provider(
|
|
"Represents equality check between a variable and a certain value.",
|
|
fields = ["name", "value", "type_name"],
|
|
)
|
|
|
|
def variable_with_value(name, value):
|
|
""" Represents equality check between a variable and a certain value.
|
|
|
|
The returned provider finds its use through flag_group.expand_if_equal,
|
|
making the expansion of the flag_group conditional on the value of the
|
|
variable.
|
|
|
|
Args:
|
|
name: name of the variable.
|
|
value: the value the variable should be compared against.
|
|
|
|
Returns:
|
|
A VariableWithValueInfo provider.
|
|
"""
|
|
_check_is_nonempty_string(name, "name", "variable_with_value")
|
|
_check_is_nonempty_string(value, "value", "variable_with_value")
|
|
return VariableWithValueInfo(
|
|
name = name,
|
|
value = value,
|
|
type_name = "variable_with_value",
|
|
)
|
|
|
|
MakeVariableInfo = provider(
|
|
"A make variable that is made accessible to rules.",
|
|
fields = ["name", "value", "type_name"],
|
|
)
|
|
|
|
def make_variable(name, value):
|
|
""" A make variable that is made accessible to rules."""
|
|
_check_is_nonempty_string(name, "name", "make_variable")
|
|
_check_is_nonempty_string(value, "value", "make_variable")
|
|
return MakeVariableInfo(
|
|
name = name,
|
|
value = value,
|
|
type_name = "make_variable",
|
|
)
|
|
|
|
FeatureSetInfo = provider(
|
|
"A set of features.",
|
|
fields = ["features", "type_name"],
|
|
)
|
|
|
|
def feature_set(features = []):
|
|
""" A set of features.
|
|
|
|
Used to support logical 'and' when specifying feature requirements in a
|
|
feature.
|
|
|
|
Args:
|
|
features: A list of unordered feature names.
|
|
|
|
Returns:
|
|
A FeatureSetInfo provider.
|
|
"""
|
|
_check_same_type(features, [], "features", "feature_set")
|
|
return FeatureSetInfo(features = features, type_name = "feature_set")
|
|
|
|
WithFeatureSetInfo = provider(
|
|
"A set of positive and negative features.",
|
|
fields = ["features", "not_features", "type_name"],
|
|
)
|
|
|
|
def with_feature_set(features = [], not_features = []):
|
|
""" A set of positive and negative features.
|
|
|
|
This stanza will evaluate to true when every 'feature' is enabled, and
|
|
every 'not_feature' is not enabled.
|
|
|
|
Args:
|
|
features: A list of feature names that need to be enabled.
|
|
not_features: A list of feature names that need to not be enabled.
|
|
|
|
Returns:
|
|
A WithFeatureSetInfo provider.
|
|
"""
|
|
_check_same_type(features, [], "features", "with_feature_set")
|
|
_check_same_type(not_features, [], "not_features", "with_feature_set")
|
|
return WithFeatureSetInfo(
|
|
features = features,
|
|
not_features = not_features,
|
|
type_name = "with_feature_set",
|
|
)
|
|
|
|
EnvSetInfo = provider(
|
|
"Groups a set of environment variables to apply for certain actions.",
|
|
fields = ["actions", "env_entries", "with_features", "type_name"],
|
|
)
|
|
|
|
def env_set(actions, env_entries = [], with_features = []):
|
|
""" Groups a set of environment variables to apply for certain actions.
|
|
|
|
EnvSet providers are passed to feature() and action_config(), to be applied to
|
|
the actions they are specified for.
|
|
|
|
Args:
|
|
actions: A list of actions this env set applies to; each env set must
|
|
specify at least one action.
|
|
env_entries: A list of EnvEntry - the environment variables applied
|
|
via this env set.
|
|
with_features: A list of feature sets defining when this env set gets
|
|
applied. The env set will be applied when any one of the feature
|
|
sets evaluate to true. (That is, when when every 'feature' is
|
|
enabled, and every 'not_feature' is not enabled.)
|
|
If 'with_features' is omitted, the env set will be applied
|
|
unconditionally for every action specified.
|
|
|
|
Returns:
|
|
An EnvSetInfo provider.
|
|
"""
|
|
_check_is_nonempty_list(actions, "actions", "env_set")
|
|
_check_same_type(env_entries, [], "env_entries", "env_set")
|
|
_check_same_type(with_features, [], "with_features", "env_set")
|
|
return EnvSetInfo(
|
|
actions = actions,
|
|
env_entries = env_entries,
|
|
with_features = with_features,
|
|
type_name = "env_set",
|
|
)
|
|
|
|
FlagGroupInfo = provider(
|
|
"A group of flags. Supports parametrization via variable expansion.",
|
|
fields = [
|
|
"flags",
|
|
"flag_groups",
|
|
"iterate_over",
|
|
"expand_if_available",
|
|
"expand_if_not_available",
|
|
"expand_if_true",
|
|
"expand_if_false",
|
|
"expand_if_equal",
|
|
"type_name",
|
|
],
|
|
)
|
|
|
|
def flag_group(
|
|
flags = [],
|
|
flag_groups = [],
|
|
iterate_over = None,
|
|
expand_if_available = None,
|
|
expand_if_not_available = None,
|
|
expand_if_true = None,
|
|
expand_if_false = None,
|
|
expand_if_equal = None):
|
|
""" A group of flags. Supports parametrization via variable expansion.
|
|
|
|
To expand a variable of list type, flag_group has to be annotated with
|
|
`iterate_over` message. Then all nested flags or flag_groups will be
|
|
expanded repeatedly for each element of the list.
|
|
For example:
|
|
flag_group(
|
|
iterate_over = 'include_path',
|
|
flags = ['-I', '%{include_path}'],
|
|
)
|
|
... will get expanded to -I /to/path1 -I /to/path2 ... for each
|
|
include_path /to/pathN.
|
|
|
|
To expand a variable of structure type, use dot-notation, e.g.:
|
|
flag_group(
|
|
iterate_over = "libraries_to_link",
|
|
flag_groups = [
|
|
flag_group(
|
|
iterate_over = "libraries_to_link.libraries",
|
|
flags = ["-L%{libraries_to_link.libraries.directory}"],
|
|
)
|
|
]
|
|
)
|
|
|
|
Flag groups can be nested; if they are, the flag group must only contain
|
|
other flag groups (no flags) so the order is unambiguously specified.
|
|
In order to expand a variable of nested lists, 'iterate_over' can be used.
|
|
For example:
|
|
flag_group (
|
|
iterate_over = 'object_files',
|
|
flag_groups = [
|
|
flag_group (
|
|
flags = ['--start-lib'],
|
|
),
|
|
flag_group (
|
|
iterate_over = 'object_files',
|
|
flags = ['%{object_files}'],
|
|
),
|
|
flag_group (
|
|
flags = ['--end-lib'],
|
|
)
|
|
]
|
|
)
|
|
... will get expanded to
|
|
--start-lib a1.o a2.o ... --end-lib --start-lib b1.o b2.o .. --end-lib
|
|
with %{object_files} being a variable of nested list type
|
|
[['a1.o', 'a2.o', ...], ['b1.o', 'b2.o', ...], ...].
|
|
|
|
Args:
|
|
flags: a string list, representing flags. Only one of flags and
|
|
flag_groups can be set, as to avoid ambiguity.
|
|
flag_groups: a list of FlagGroup entries. Only one of flags and
|
|
flag_groups can be set, as to avoid ambiguity.
|
|
iterate_over: a string, representing a variable of list type.
|
|
expand_if_available: A build variable that needs to be available
|
|
in order to expand the flag_group.
|
|
expand_if_not_available: A build variable that needs to be
|
|
unavailable in order for this flag_group to be expanded.
|
|
expand_if_true: if set, this variable needs to evaluate to True in
|
|
order for the flag_group to be expanded.
|
|
expand_if_false: if set, this variable needs to evalate to False in
|
|
order for the flag_group to be expanded.
|
|
expand_if_equal: a VariableWithValue, the flag_group is expanded in
|
|
case of equality.
|
|
|
|
Returns:
|
|
A FlagGroupInfo provider.
|
|
"""
|
|
|
|
_check_same_type(flags, [], "flags", "flag_group")
|
|
_check_same_type(flag_groups, [], "flag_groups", "flag_group")
|
|
if len(flags) > 0 and len(flag_groups) > 0:
|
|
fail("flag_group must not contain both a flag and another flag_group.")
|
|
if len(flags) == 0 and len(flag_groups) == 0:
|
|
fail("flag_group must contain either a list of flags or a list of flag_groups.")
|
|
_check_is_none_or_right_type(expand_if_true, "string", "expand_if_true", "flag_group")
|
|
_check_is_none_or_right_type(expand_if_false, "string", "expand_if_false", "flag_group")
|
|
_check_is_none_or_right_type(expand_if_available, "string", "expand_if_available", "flag_group")
|
|
_check_is_none_or_right_type(
|
|
expand_if_not_available,
|
|
"string",
|
|
"expand_if_not_available",
|
|
"flag_group",
|
|
)
|
|
_check_is_none_or_right_type(iterate_over, "string", "iterate_over", "flag_group")
|
|
|
|
return FlagGroupInfo(
|
|
flags = flags,
|
|
flag_groups = flag_groups,
|
|
iterate_over = iterate_over,
|
|
expand_if_available = expand_if_available,
|
|
expand_if_not_available = expand_if_not_available,
|
|
expand_if_true = expand_if_true,
|
|
expand_if_false = expand_if_false,
|
|
expand_if_equal = expand_if_equal,
|
|
type_name = "flag_group",
|
|
)
|
|
|
|
FlagSetInfo = provider(
|
|
"A set of flags to be expanded in the command line for specific actions.",
|
|
fields = [
|
|
"actions",
|
|
"with_features",
|
|
"flag_groups",
|
|
"type_name",
|
|
],
|
|
)
|
|
|
|
def flag_set(
|
|
actions = [],
|
|
with_features = [],
|
|
flag_groups = []):
|
|
""" A set of flags to be expanded in the command line for specific actions.
|
|
|
|
Args:
|
|
actions: The actions this flag set applies to; each flag set must
|
|
specify at least one action.
|
|
with_features: A list of feature sets defining when this flag set gets
|
|
applied. The flag set will be applied when any one of the feature
|
|
sets evaluate to true. (That is, when when every 'feature' is
|
|
enabled, and every 'not_feature' is not enabled.)
|
|
If 'with_feature' is omitted, the flag set will be applied
|
|
unconditionally for every action specified.
|
|
flag_groups: A FlagGroup list - the flags applied via this flag set.
|
|
|
|
Returns:
|
|
A FlagSetInfo provider.
|
|
"""
|
|
_check_same_type(actions, [], "actions", "flag_set")
|
|
_check_same_type(with_features, [], "with_features", "flag_set")
|
|
_check_same_type(flag_groups, [], "flag_groups", "flag_set")
|
|
return FlagSetInfo(
|
|
actions = actions,
|
|
with_features = with_features,
|
|
flag_groups = flag_groups,
|
|
type_name = "flag_set",
|
|
)
|
|
|
|
FeatureInfo = provider(
|
|
"Contains all flag specifications for one feature.",
|
|
fields = [
|
|
"name",
|
|
"enabled",
|
|
"flag_sets",
|
|
"env_sets",
|
|
"requires",
|
|
"implies",
|
|
"provides",
|
|
"type_name",
|
|
],
|
|
)
|
|
|
|
def feature(
|
|
name,
|
|
enabled = False,
|
|
flag_sets = [],
|
|
env_sets = [],
|
|
requires = [],
|
|
implies = [],
|
|
provides = []):
|
|
""" Contains all flag specifications for one feature.
|
|
|
|
Args:
|
|
name: The feature's name. It is possible to introduce a feature without
|
|
a change to Bazel by adding a 'feature' section to the toolchain
|
|
and adding the corresponding string as feature in the BUILD file.
|
|
enabled: If 'True', this feature is enabled unless a rule type
|
|
explicitly marks it as unsupported.
|
|
flag_sets: A FlagSet list - If the given feature is enabled, the flag
|
|
sets will be applied for the actions are specified for.
|
|
env_sets: an EnvSet list - If the given feature is enabled, the env
|
|
sets will be applied for the actions they are specified for.
|
|
requires: A list of feature sets defining when this feature is
|
|
supported by the toolchain. The feature is supported if any of the
|
|
feature sets fully apply, that is, when all features of a feature
|
|
set are enabled.
|
|
If 'requires' is omitted, the feature is supported independently of
|
|
which other features are enabled.
|
|
Use this for example to filter flags depending on the build mode
|
|
enabled (opt / fastbuild / dbg).
|
|
implies: A string list of features or action configs that are
|
|
automatically enabled when this feature is enabled. If any of the
|
|
implied features or action configs cannot be enabled, this feature
|
|
will (silently) not be enabled either.
|
|
provides: A list of names this feature conflicts with.
|
|
A feature cannot be enabled if:
|
|
- 'provides' contains the name of a different feature or action
|
|
config that we want to enable.
|
|
- 'provides' contains the same value as a 'provides' in a
|
|
different feature or action config that we want to enable.
|
|
Use this in order to ensure that incompatible features cannot be
|
|
accidentally activated at the same time, leading to hard to
|
|
diagnose compiler errors.
|
|
|
|
Returns:
|
|
A FeatureInfo provider.
|
|
"""
|
|
_check_same_type(enabled, True, "enabled", "feature")
|
|
_check_same_type(flag_sets, [], "flag_sets", "feature")
|
|
_check_same_type(env_sets, [], "env_sets", "feature")
|
|
_check_same_type(requires, [], "requires", "feature")
|
|
_check_same_type(provides, [], "provides", "feature")
|
|
_check_same_type(implies, [], "implies", "feature")
|
|
return FeatureInfo(
|
|
name = name,
|
|
enabled = enabled,
|
|
flag_sets = flag_sets,
|
|
env_sets = env_sets,
|
|
requires = requires,
|
|
implies = implies,
|
|
provides = provides,
|
|
type_name = "feature",
|
|
)
|
|
|
|
ToolPathInfo = provider(
|
|
"Tool locations.",
|
|
fields = ["name", "path", "type_name"],
|
|
)
|
|
|
|
def tool_path(name, path):
|
|
""" Tool locations.
|
|
|
|
Args:
|
|
name: Name of the tool.
|
|
path: Location of the tool; Can be absolute path (in case of non hermetic
|
|
toolchain), or path relative to the cc_toolchain's package.
|
|
|
|
Returns:
|
|
A ToolPathInfo provider.
|
|
|
|
Deprecated:
|
|
Prefer specifying an ActionConfig for the action that needs the tool.
|
|
TODO(b/27903698) migrate to ActionConfig.
|
|
"""
|
|
_check_is_nonempty_string(name, "name", "tool_path")
|
|
_check_is_nonempty_string(path, "path", "tool_path")
|
|
return ToolPathInfo(name = name, path = path, type_name = "tool_path")
|
|
|
|
ToolInfo = provider(
|
|
doc = "Tool information. This differs from ToolPathInfo as it is intended to be used\
|
|
in action_configs and can accept labels.",
|
|
fields = [
|
|
"path",
|
|
"tool",
|
|
"with_features",
|
|
"execution_requirements",
|
|
"type_name",
|
|
],
|
|
)
|
|
|
|
def tool(path = None, with_features = [], execution_requirements = [], tool = None):
|
|
""" Describes a tool associated with a crosstool action config.
|
|
|
|
Args:
|
|
path: Location of the tool; Can be absolute path (in case of non hermetic
|
|
toolchain), or path relative to the cc_toolchain's package. If this
|
|
parameter is set, tool must not be set.
|
|
tool: The built-artifact that should be used as this tool. If this is
|
|
set, path must not be set.
|
|
with_features: A list of feature sets defining when this tool is
|
|
applicable. The tool will used when any one of the feature sets
|
|
evaluate to true. (That is, when when every 'feature' is enabled,
|
|
and every 'not_feature' is not enabled.)
|
|
If 'with_feature' is omitted, the tool will apply for any feature
|
|
configuration.
|
|
execution_requirements: Requirements on the execution environment for
|
|
the execution of this tool, to be passed as out-of-band "hints" to
|
|
the execution backend.
|
|
Ex. "requires-mem:24g"
|
|
|
|
Returns:
|
|
A ToolInfo provider.
|
|
"""
|
|
if path == None and tool == None:
|
|
fail("Parameter path or parameter tool of tool should not be None.")
|
|
|
|
if path != None:
|
|
_check_is_nonempty_string(path, "path", "tool")
|
|
_check_is_none(tool, "tool", "tool")
|
|
if tool != None:
|
|
_check_is_none(path, "path", "tool")
|
|
_check_right_type(tool, "File", "tool", "tool")
|
|
|
|
_check_same_type(with_features, [], "with_features", "tool")
|
|
_check_same_type(execution_requirements, [], "execution_requirements", "tool")
|
|
return ToolInfo(
|
|
path = path,
|
|
tool = tool,
|
|
with_features = with_features,
|
|
execution_requirements = execution_requirements,
|
|
type_name = "tool",
|
|
)
|
|
|
|
ActionConfigInfo = provider(
|
|
"Configuration of a Bazel action.",
|
|
fields = [
|
|
"config_name",
|
|
"action_name",
|
|
"enabled",
|
|
"tools",
|
|
"flag_sets",
|
|
"implies",
|
|
"type_name",
|
|
],
|
|
)
|
|
|
|
def action_config(
|
|
action_name,
|
|
enabled = False,
|
|
tools = [],
|
|
flag_sets = [],
|
|
implies = []):
|
|
""" Configuration of a Bazel action.
|
|
|
|
An action config corresponds to a Bazel action, and allows selection of
|
|
a tool based on activated features.
|
|
Action config activation occurs by the same semantics as features: a
|
|
feature can 'require' or 'imply' an action config in the same way that it
|
|
would another feature.
|
|
|
|
Args:
|
|
action_name: The name of the Bazel action that this config applies to,
|
|
ex. 'c-compile' or 'c-module-compile'.
|
|
enabled: If 'True', this action is enabled unless a rule type
|
|
explicitly marks it as unsupported.
|
|
tools: The tool applied to the action will be the first Tool with a
|
|
feature set that matches the feature configuration. An error will
|
|
be thrown if no tool matches a provided feature configuration - for
|
|
that reason, it's a good idea to provide a default tool with an
|
|
empty feature set.
|
|
flag_sets: If the given action config is enabled, the flag sets will be
|
|
applied to the corresponding action.
|
|
implies: A list of features or action configs that are automatically
|
|
enabled when this action config is enabled. If any of the implied
|
|
features or action configs cannot be enabled, this action config
|
|
will (silently) not be enabled either.
|
|
|
|
Returns:
|
|
An ActionConfigInfo provider.
|
|
"""
|
|
_check_is_nonempty_string(action_name, "name", "action_config")
|
|
_check_same_type(enabled, True, "enabled", "action_config")
|
|
_check_same_type(tools, [], "tools", "action_config")
|
|
_check_same_type(flag_sets, [], "flag_sets", "action_config")
|
|
_check_same_type(implies, [], "implies", "action_config")
|
|
return ActionConfigInfo(
|
|
action_name = action_name,
|
|
enabled = enabled,
|
|
tools = tools,
|
|
flag_sets = flag_sets,
|
|
implies = implies,
|
|
type_name = "action_config",
|
|
)
|
|
|
|
ArtifactNamePatternInfo = provider(
|
|
"The name for an artifact of a given category of input or output artifacts to an action.",
|
|
fields = [
|
|
"category_name",
|
|
"prefix",
|
|
"extension",
|
|
"type_name",
|
|
],
|
|
)
|
|
|
|
def artifact_name_pattern(category_name, prefix, extension):
|
|
""" The name for an artifact of a given category of input or output artifacts to an action.
|
|
|
|
Args:
|
|
category_name: The category of artifacts that this selection applies
|
|
to. This field is compared against a list of categories defined
|
|
in bazel. Example categories include "linked_output" or
|
|
"debug_symbols". An error is thrown if no category is matched.
|
|
prefix: The prefix for creating the artifact for this selection.
|
|
Together with the extension it is used to create an artifact name
|
|
based on the target name.
|
|
extension: The extension for creating the artifact for this selection.
|
|
Together with the prefix it is used to create an artifact name
|
|
based on the target name.
|
|
|
|
Returns:
|
|
An ArtifactNamePatternInfo provider
|
|
"""
|
|
_check_is_nonempty_string(category_name, "category_name", "artifact_name_pattern")
|
|
_check_is_none_or_right_type(prefix, "", "prefix", "artifact_name_pattern")
|
|
_check_is_none_or_right_type(extension, "", "extension", "artifact_name_pattern")
|
|
return ArtifactNamePatternInfo(
|
|
category_name = category_name,
|
|
prefix = prefix,
|
|
extension = extension,
|
|
type_name = "artifact_name_pattern",
|
|
)
|