| # 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", |
| ) |