• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2024 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Conversion helper functions to legacy cc_toolchain_config_info."""
15
16load(
17    "//cc:cc_toolchain_config_lib.bzl",
18    legacy_action_config = "action_config",
19    legacy_env_entry = "env_entry",
20    legacy_env_set = "env_set",
21    legacy_feature = "feature",
22    legacy_feature_set = "feature_set",
23    legacy_flag_set = "flag_set",
24    legacy_tool = "tool",
25    legacy_with_feature_set = "with_feature_set",
26)
27
28visibility([
29    "//cc/toolchains/...",
30    "//tests/rule_based_toolchain/...",
31])
32
33# Note that throughout this file, we sort anything for which the order is
34# nondeterministic (eg. depset's .to_list(), dictionary iteration).
35# This allows our tests to call equals() on the output,
36# and *may* provide better caching properties.
37
38def _convert_actions(actions):
39    return sorted([action.name for action in actions.to_list()])
40
41def convert_feature_constraint(constraint):
42    return legacy_with_feature_set(
43        features = sorted([ft.name for ft in constraint.all_of.to_list()]),
44        not_features = sorted([ft.name for ft in constraint.none_of.to_list()]),
45    )
46
47def convert_args(args, strip_actions = False):
48    """Converts an ArgsInfo to flag_sets and env_sets.
49
50    Args:
51        args: (ArgsInfo) The args to convert
52        strip_actions: (bool) Whether to strip the actions from the resulting flag_set.
53    Returns:
54        struct(flag_sets = List[flag_set], env_sets = List[env_sets])
55    """
56    actions = _convert_actions(args.actions)
57    with_features = [
58        convert_feature_constraint(fc)
59        for fc in args.requires_any_of
60    ]
61
62    flag_sets = []
63    if args.nested != None:
64        flag_sets.append(legacy_flag_set(
65            actions = [] if strip_actions else actions,
66            with_features = with_features,
67            flag_groups = [args.nested.legacy_flag_group],
68        ))
69
70    env_sets = []
71    if args.env:
72        env_sets.append(legacy_env_set(
73            actions = actions,
74            with_features = with_features,
75            env_entries = [
76                legacy_env_entry(
77                    key = key,
78                    value = value,
79                )
80                for key, value in args.env.items()
81            ],
82        ))
83    return struct(
84        flag_sets = flag_sets,
85        env_sets = env_sets,
86    )
87
88def _convert_args_sequence(args_sequence, strip_actions = False):
89    flag_sets = []
90    env_sets = []
91    for args in args_sequence:
92        legacy_args = convert_args(args, strip_actions)
93        flag_sets.extend(legacy_args.flag_sets)
94        env_sets.extend(legacy_args.env_sets)
95
96    return struct(flag_sets = flag_sets, env_sets = env_sets)
97
98def convert_feature(feature, enabled = False):
99    if feature.external:
100        return None
101
102    args = _convert_args_sequence(feature.args.args)
103
104    return legacy_feature(
105        name = feature.name,
106        enabled = enabled or feature.enabled,
107        flag_sets = args.flag_sets,
108        env_sets = args.env_sets,
109        implies = sorted([ft.name for ft in feature.implies.to_list()]),
110        requires = [
111            legacy_feature_set(sorted([
112                feature.name
113                for feature in requirement.features.to_list()
114            ]))
115            for requirement in feature.requires_any_of
116        ],
117        provides = [
118            mutex.name
119            for mutex in feature.mutually_exclusive
120        ],
121    )
122
123def convert_tool(tool):
124    return legacy_tool(
125        tool = tool.exe,
126        execution_requirements = list(tool.execution_requirements),
127        with_features = [],
128    )
129
130def convert_capability(capability):
131    return legacy_feature(
132        name = capability.name,
133        enabled = False,
134    )
135
136def _convert_tool_map(tool_map, args_by_action):
137    action_configs = []
138    caps = {}
139    for action_type, tool in tool_map.configs.items():
140        action_args = args_by_action.get(action_type.name, default = None)
141        flag_sets = action_args.flag_sets if action_args != None else []
142        action_configs.append(legacy_action_config(
143            action_name = action_type.name,
144            enabled = True,
145            flag_sets = flag_sets,
146            tools = [convert_tool(tool)],
147            implies = [cap.feature.name for cap in tool.capabilities],
148        ))
149        for cap in tool.capabilities:
150            caps[cap] = None
151
152    cap_features = [
153        legacy_feature(name = cap.feature.name, enabled = False)
154        for cap in caps
155    ]
156    return action_configs, cap_features
157
158def convert_toolchain(toolchain):
159    """Converts a rule-based toolchain into the legacy providers.
160
161    Args:
162        toolchain: (ToolchainConfigInfo) The toolchain config to convert.
163    Returns:
164        A struct containing parameters suitable to pass to
165          cc_common.create_cc_toolchain_config_info.
166    """
167
168    # Ordering of arguments is important! Intended argument ordering is:
169    #   1. Arguments listed in `args`.
170    #   2. Legacy/built-in features.
171    #   3. User-defined features.
172    # While we could just attach arguments to a feature, legacy/built-in features will appear
173    # before the user-defined features if we do not bind args directly to the action configs.
174    # For that reason, there's additional logic in this function to ensure that the args are
175    # attached to the action configs directly, as that is the only way to ensure the correct
176    # ordering.
177    args_by_action = {}
178    for a in toolchain.args.by_action:
179        args = args_by_action.setdefault(a.action.name, struct(flag_sets = [], env_sets = []))
180        new_args = _convert_args_sequence(a.args, strip_actions = True)
181        args.flag_sets.extend(new_args.flag_sets)
182        args.env_sets.extend(new_args.env_sets)
183
184    action_configs, cap_features = _convert_tool_map(toolchain.tool_map, args_by_action)
185    features = [
186        convert_feature(feature, enabled = feature in toolchain.enabled_features)
187        for feature in toolchain.features
188    ]
189    features.extend(cap_features)
190
191    features.append(legacy_feature(
192        # We reserve names starting with implied_by. This ensures we don't
193        # conflict with the name of a feature the user creates.
194        name = "implied_by_always_enabled_env_sets",
195        enabled = True,
196        env_sets = _convert_args_sequence(toolchain.args.args).env_sets,
197    ))
198
199    cxx_builtin_include_directories = [
200        d.path
201        for d in toolchain.allowlist_include_directories.to_list()
202    ]
203
204    return struct(
205        features = [ft for ft in features if ft != None],
206        action_configs = sorted(action_configs, key = lambda ac: ac.action_name),
207        cxx_builtin_include_directories = cxx_builtin_include_directories,
208    )
209