blob: b9400daaf13cd2a5dda6d7e39e7b9ff811d61ef0 [file] [log] [blame]
# 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()},
)