| # 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(fields = ["key", "value", "type_name"]) | 
 |  | 
 | def env_entry(key, value): | 
 |     """ 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. | 
 |  | 
 |     Returns: | 
 |         An EnvEntryInfo provider. | 
 |     """ | 
 |     _check_is_nonempty_string(key, "key", "env_entry") | 
 |     _check_is_nonempty_string(value, "value", "env_entry") | 
 |     return EnvEntryInfo(key = key, value = value, type_name = "env_entry") | 
 |  | 
 | VariableWithValueInfo = provider(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(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(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(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(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(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(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(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(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(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-darwin" | 
 |  | 
 |     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(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(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", | 
 |     ) |