| # Copyright 2023 The Pigweed Authors |
| # |
| # 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 |
| # |
| # https://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. |
| """Private utilities and global variables.""" |
| |
| load( |
| "@rules_cc//cc:cc_toolchain_config_lib.bzl", |
| rules_cc_action_config = "action_config", |
| rules_cc_env_entry = "env_entry", |
| rules_cc_env_set = "env_set", |
| rules_cc_feature = "feature", |
| rules_cc_feature_set = "feature_set", |
| rules_cc_flag_set = "flag_set", |
| rules_cc_with_feature_set = "with_feature_set", |
| ) |
| |
| visibility(["//cc_toolchain/tests/..."]) |
| |
| ALL_FILE_GROUPS = { |
| "ar_files": ["@pw_toolchain//actions:all_ar_actions"], |
| "as_files": ["@pw_toolchain//actions:all_asm_actions"], |
| "compiler_files": ["@pw_toolchain//actions:all_compiler_actions"], |
| "coverage_files": ["@pw_toolchain//actions:llvm_cov"], |
| "dwp_files": [], |
| "linker_files": ["@pw_toolchain//actions:all_link_actions"], |
| "objcopy_files": ["@pw_toolchain//actions:objcopy_embed_data"], |
| "strip_files": ["@pw_toolchain//actions:strip"], |
| } |
| |
| def _ensure_fulfillable(any_of, known, label, fail = fail): |
| # Requirements can be fulfilled if there are no requirements. |
| fulfillable = not any_of |
| for group in any_of: |
| all_met = True |
| for entry in group.to_list(): |
| if entry.label not in known: |
| all_met = False |
| break |
| if all_met: |
| fulfillable = True |
| break |
| |
| if not fulfillable: |
| fail("%s cannot possibly be enabled (none of the constraints it requires fully exist). Either remove it from your toolchain, or add the requirements." % label) |
| |
| def _to_untyped_flag_set(flag_set, known, fail = fail): |
| """Converts a PwFlagSet to rules_cc's flag set.""" |
| _ensure_fulfillable( |
| any_of = [constraint.all_of for constraint in flag_set.requires_any_of], |
| known = known, |
| label = flag_set.label, |
| fail = fail, |
| ) |
| actions = list(flag_set.actions) |
| with_features = [ |
| _to_untyped_feature_constraint(fc) |
| for fc in flag_set.requires_any_of |
| ] |
| |
| out_flag_set = None |
| if flag_set.flag_groups: |
| out_flag_set = rules_cc_flag_set( |
| flag_groups = list(flag_set.flag_groups), |
| actions = actions, |
| with_features = with_features, |
| ) |
| |
| out_env_set = None |
| if flag_set.env: |
| out_env_set = rules_cc_env_set( |
| env_entries = [ |
| rules_cc_env_entry( |
| key = key, |
| value = value, |
| expand_if_available = flag_set.env_expand_if_available, |
| ) |
| for key, value in flag_set.env.items() |
| ], |
| actions = actions, |
| with_features = with_features, |
| ) |
| return struct( |
| flag_set = out_flag_set, |
| env_set = out_env_set, |
| ) |
| |
| def _to_untyped_flag_sets(flag_sets, known, fail): |
| out_flag_sets = [] |
| out_env_sets = [] |
| out = [_to_untyped_flag_set(flag_set, known, fail) for flag_set in flag_sets] |
| for entry in out: |
| if entry.flag_set != None: |
| out_flag_sets.append(entry.flag_set) |
| if entry.env_set != None: |
| out_env_sets.append(entry.env_set) |
| return struct(flag_sets = out_flag_sets, env_sets = out_env_sets) |
| |
| def _to_untyped_feature_set(feature_set): |
| return rules_cc_feature_set([ |
| feature.name |
| for feature in feature_set.features.to_list() |
| ]) |
| |
| def _to_untyped_feature_constraint(feature_constraint): |
| return rules_cc_with_feature_set( |
| features = [ft.name for ft in feature_constraint.all_of.to_list()], |
| not_features = [ft.name for ft in feature_constraint.none_of.to_list()], |
| ) |
| |
| def _to_untyped_implies(provider, known, fail = fail): |
| implies = [] |
| for feature in provider.implies_features.to_list(): |
| if feature.label not in known: |
| fail("%s implies %s, which is not explicitly mentioned in your toolchain configuration" % (provider.label, feature.label)) |
| implies.append(feature.name) |
| for action_config in provider.implies_action_configs.to_list(): |
| if action_config.label not in known: |
| fail("%s implies %s, which is not explicitly mentioned in your toolchain configuration" % (provider.label, action_config.label)) |
| implies.append(action_config.action_name) |
| return implies |
| |
| def _to_untyped_feature(feature, known, fail = fail): |
| if feature.known: |
| return None |
| |
| _ensure_fulfillable( |
| any_of = [fs.features for fs in feature.requires_any_of], |
| known = known, |
| label = feature.label, |
| fail = fail, |
| ) |
| |
| flags = _to_untyped_flag_sets(feature.flag_sets.to_list(), known, fail = fail) |
| |
| return rules_cc_feature( |
| name = feature.name, |
| enabled = feature.enabled, |
| flag_sets = flags.flag_sets, |
| env_sets = flags.env_sets, |
| implies = _to_untyped_implies(feature, known, fail = fail), |
| requires = [_to_untyped_feature_set(requirement) for requirement in feature.requires_any_of], |
| provides = list(feature.provides), |
| ) |
| |
| def _to_untyped_tool(tool, known, fail = fail): |
| _ensure_fulfillable( |
| any_of = [constraint.all_of for constraint in tool.requires_any_of], |
| known = known, |
| label = tool.label, |
| fail = fail, |
| ) |
| |
| # Rules_cc is missing the "tool" parameter. |
| return struct( |
| path = tool.path, |
| tool = tool.exe, |
| execution_requirements = list(tool.execution_requirements), |
| with_features = [ |
| _to_untyped_feature_constraint(fc) |
| for fc in tool.requires_any_of |
| ], |
| type_name = "tool", |
| ) |
| |
| def _to_untyped_action_config(action_config, extra_flag_sets, known, fail = fail): |
| # De-dupe, in case the same flag set was specified for both unconditional |
| # and for a specific action config. |
| flag_sets = depset( |
| list(action_config.flag_sets) + extra_flag_sets, |
| order = "preorder", |
| ).to_list() |
| untyped_flags = _to_untyped_flag_sets(flag_sets, known = known, fail = fail) |
| implies = _to_untyped_implies(action_config, known, fail = fail) |
| |
| # Action configs don't take in an env like they do a flag set. |
| # In order to support them, we create a feature with the env that the action |
| # config will enable, and imply it in the action config. |
| feature = None |
| if untyped_flags.env_sets: |
| feature = rules_cc_feature( |
| name = "implied_by_%s" % action_config.action_name, |
| env_sets = untyped_flags.env_sets, |
| ) |
| implies.append(feature.name) |
| |
| return struct( |
| action_config = rules_cc_action_config( |
| action_name = action_config.action_name, |
| enabled = action_config.enabled, |
| tools = [ |
| _to_untyped_tool(tool, known, fail = fail) |
| for tool in action_config.tools |
| ], |
| flag_sets = [ |
| # Make the flag sets actionless. |
| rules_cc_flag_set( |
| actions = [], |
| with_features = fs.with_features, |
| flag_groups = fs.flag_groups, |
| ) |
| for fs in untyped_flags.flag_sets |
| ], |
| implies = implies, |
| ), |
| features = [feature] if feature else [], |
| ) |
| |
| def to_untyped_config(feature_set, action_config_set, flag_sets, extra_action_files, fail = fail): |
| """Converts Pigweed providers into a format suitable for rules_cc. |
| |
| Args: |
| feature_set: PwFeatureSetInfo: Features available in the toolchain |
| action_config_set: ActionConfigSetInfo: Set of defined action configs |
| flag_sets: Flag sets that are unconditionally applied |
| extra_action_files: Files to be added to actions |
| fail: The fail function. Only change this during testing. |
| Returns: |
| A struct containing parameters suitable to pass to |
| cc_common.create_cc_toolchain_config_info. |
| """ |
| flag_sets_by_action = {} |
| for flag_set in flag_sets: |
| for action in flag_set.actions: |
| flag_sets_by_action.setdefault(action, []).append(flag_set) |
| |
| known_labels = {} |
| known_feature_names = {} |
| feature_list = feature_set.features.to_list() |
| for feature in feature_list: |
| known_labels[feature.label] = None |
| existing_feature = known_feature_names.get(feature.name, None) |
| if existing_feature != None and feature.overrides != existing_feature and existing_feature.overrides != feature: |
| fail("Conflicting features: %s and %s both have feature name %s" % (feature.label, existing_feature.label, feature.name)) |
| |
| known_feature_names[feature.name] = feature |
| |
| untyped_features = [] |
| for feature in feature_list: |
| untyped_feature = _to_untyped_feature(feature, known = known_labels, fail = fail) |
| if untyped_feature != None: |
| untyped_features.append(untyped_feature) |
| |
| acs = action_config_set.action_configs.to_list() |
| known_actions = {} |
| untyped_acs = [] |
| for ac in acs: |
| if ac.action_name in known_actions: |
| fail("In %s, both %s and %s implement %s" % ( |
| action_config_set.label, |
| ac.label, |
| known_actions[ac.action_name], |
| ac.action_name, |
| )) |
| known_actions[ac.action_name] = ac.label |
| out_ac = _to_untyped_action_config( |
| ac, |
| extra_flag_sets = flag_sets_by_action.get(ac.action_name, []), |
| known = known_labels, |
| fail = fail, |
| ) |
| untyped_acs.append(out_ac.action_config) |
| untyped_features.extend(out_ac.features) |
| |
| action_to_files = { |
| ac.action_name: [ac.files] |
| for ac in acs |
| } |
| for ffa in extra_action_files.srcs.to_list(): |
| action_to_files.setdefault(ffa.action, []).append(ffa.files) |
| |
| return struct( |
| features = untyped_features, |
| action_configs = untyped_acs, |
| action_to_files = {k: depset(transitive = v) for k, v in action_to_files.items()}, |
| ) |