xref: /aosp_15_r20/external/bazelbuild-rules_rust/crate_universe/src/rendering/templates/module_bzl.j2 (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1{#
2To keep line numbers consistent with the rendered version, empty space is
3intentionally placed here which should match the line length of `partials/header.j2`.
4
5Expected length = 6 lines
6#}{%- include "partials/header.j2" %}
7"""
8# `crates_repository` API
9
10- [aliases](#aliases)
11- [crate_deps](#crate_deps)
12- [all_crate_deps](#all_crate_deps)
13- [crate_repositories](#crate_repositories)
14
15"""
16
17load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
18load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
19load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
20load("@bazel_skylib//lib:selects.bzl", "selects")
21
22###############################################################################
23# MACROS API
24###############################################################################
25
26# An identifier that represent common dependencies (unconditional).
27_COMMON_CONDITION = ""
28
29def _flatten_dependency_maps(all_dependency_maps):
30    """Flatten a list of dependency maps into one dictionary.
31
32    Dependency maps have the following structure:
33
34    ```python
35    DEPENDENCIES_MAP = {
36        # The first key in the map is a Bazel package
37        # name of the workspace this file is defined in.
38        "workspace_member_package": {
39
40            # Not all dependencies are supported for all platforms.
41            # the condition key is the condition required to be true
42            # on the host platform.
43            "condition": {
44
45                # An alias to a crate target.     # The label of the crate target the
46                # Aliases are only crate names.   # package name refers to.
47                "package_name":                   "@full//:label",
48            }
49        }
50    }
51    ```
52
53    Args:
54        all_dependency_maps (list): A list of dicts as described above
55
56    Returns:
57        dict: A dictionary as described above
58    """
59    dependencies = {}
60
61    for workspace_deps_map in all_dependency_maps:
62        for pkg_name, conditional_deps_map in workspace_deps_map.items():
63            if pkg_name not in dependencies:
64                non_frozen_map = dict()
65                for key, values in conditional_deps_map.items():
66                    non_frozen_map.update({key: dict(values.items())})
67                dependencies.setdefault(pkg_name, non_frozen_map)
68                continue
69
70            for condition, deps_map in conditional_deps_map.items():
71                # If the condition has not been recorded, do so and continue
72                if condition not in dependencies[pkg_name]:
73                    dependencies[pkg_name].setdefault(condition, dict(deps_map.items()))
74                    continue
75
76                # Alert on any miss-matched dependencies
77                inconsistent_entries = []
78                for crate_name, crate_label in deps_map.items():
79                    existing = dependencies[pkg_name][condition].get(crate_name)
80                    if existing and existing != crate_label:
81                        inconsistent_entries.append((crate_name, existing, crate_label))
82                    dependencies[pkg_name][condition].update({crate_name: crate_label})
83
84    return dependencies
85
86def crate_deps(deps, package_name = {{ default_package_name }}):
87    """Finds the fully qualified label of the requested crates for the package where this macro is called.
88
89    Args:
90        deps (list): The desired list of crate targets.
91        package_name (str, optional): The package name of the set of dependencies to look up.
92            Defaults to `native.package_name()`.
93
94    Returns:
95        list: A list of labels to generated rust targets (str)
96    """
97
98    if not deps:
99        return []
100
101    if package_name == None:
102        package_name = native.package_name()
103
104    # Join both sets of dependencies
105    dependencies = _flatten_dependency_maps([
106        _NORMAL_DEPENDENCIES,
107        _NORMAL_DEV_DEPENDENCIES,
108        _PROC_MACRO_DEPENDENCIES,
109        _PROC_MACRO_DEV_DEPENDENCIES,
110        _BUILD_DEPENDENCIES,
111        _BUILD_PROC_MACRO_DEPENDENCIES,
112    ]).pop(package_name, {})
113
114    # Combine all conditional packages so we can easily index over a flat list
115    # TODO: Perhaps this should actually return select statements and maintain
116    # the conditionals of the dependencies
117    flat_deps = {}
118    for deps_set in dependencies.values():
119        for crate_name, crate_label in deps_set.items():
120            flat_deps.update({crate_name: crate_label})
121
122    missing_crates = []
123    crate_targets = []
124    for crate_target in deps:
125        if crate_target not in flat_deps:
126            missing_crates.append(crate_target)
127        else:
128            crate_targets.append(flat_deps[crate_target])
129
130    if missing_crates:
131        fail("Could not find crates `{}` among dependencies of `{}`. Available dependencies were `{}`".format(
132            missing_crates,
133            package_name,
134            dependencies,
135        ))
136
137    return crate_targets
138
139def all_crate_deps(
140        normal = False,
141        normal_dev = False,
142        proc_macro = False,
143        proc_macro_dev = False,
144        build = False,
145        build_proc_macro = False,
146        package_name = {{ default_package_name }}):
147    """Finds the fully qualified label of all requested direct crate dependencies \
148    for the package where this macro is called.
149
150    If no parameters are set, all normal dependencies are returned. Setting any one flag will
151    otherwise impact the contents of the returned list.
152
153    Args:
154        normal (bool, optional): If True, normal dependencies are included in the
155            output list.
156        normal_dev (bool, optional): If True, normal dev dependencies will be
157            included in the output list..
158        proc_macro (bool, optional): If True, proc_macro dependencies are included
159            in the output list.
160        proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are
161            included in the output list.
162        build (bool, optional): If True, build dependencies are included
163            in the output list.
164        build_proc_macro (bool, optional): If True, build proc_macro dependencies are
165            included in the output list.
166        package_name (str, optional): The package name of the set of dependencies to look up.
167            Defaults to `native.package_name()` when unset.
168
169    Returns:
170        list: A list of labels to generated rust targets (str)
171    """
172
173    if package_name == None:
174        package_name = native.package_name()
175
176    # Determine the relevant maps to use
177    all_dependency_maps = []
178    if normal:
179        all_dependency_maps.append(_NORMAL_DEPENDENCIES)
180    if normal_dev:
181        all_dependency_maps.append(_NORMAL_DEV_DEPENDENCIES)
182    if proc_macro:
183        all_dependency_maps.append(_PROC_MACRO_DEPENDENCIES)
184    if proc_macro_dev:
185        all_dependency_maps.append(_PROC_MACRO_DEV_DEPENDENCIES)
186    if build:
187        all_dependency_maps.append(_BUILD_DEPENDENCIES)
188    if build_proc_macro:
189        all_dependency_maps.append(_BUILD_PROC_MACRO_DEPENDENCIES)
190
191    # Default to always using normal dependencies
192    if not all_dependency_maps:
193        all_dependency_maps.append(_NORMAL_DEPENDENCIES)
194
195    dependencies = _flatten_dependency_maps(all_dependency_maps).pop(package_name, None)
196
197    if not dependencies:
198        if dependencies == None:
199            fail("Tried to get all_crate_deps for package " + package_name + " but that package had no Cargo.toml file")
200        else:
201            return []
202
203    crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values())
204    for condition, deps in dependencies.items():
205        crate_deps += selects.with_or({
206            tuple(_CONDITIONS[condition]): deps.values(),
207            "//conditions:default": [],
208        })
209
210    return crate_deps
211
212def aliases(
213        normal = False,
214        normal_dev = False,
215        proc_macro = False,
216        proc_macro_dev = False,
217        build = False,
218        build_proc_macro = False,
219        package_name = {{ default_package_name }}):
220    """Produces a map of Crate alias names to their original label
221
222    If no dependency kinds are specified, `normal` and `proc_macro` are used by default.
223    Setting any one flag will otherwise determine the contents of the returned dict.
224
225    Args:
226        normal (bool, optional): If True, normal dependencies are included in the
227            output list.
228        normal_dev (bool, optional): If True, normal dev dependencies will be
229            included in the output list..
230        proc_macro (bool, optional): If True, proc_macro dependencies are included
231            in the output list.
232        proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are
233            included in the output list.
234        build (bool, optional): If True, build dependencies are included
235            in the output list.
236        build_proc_macro (bool, optional): If True, build proc_macro dependencies are
237            included in the output list.
238        package_name (str, optional): The package name of the set of dependencies to look up.
239            Defaults to `native.package_name()` when unset.
240
241    Returns:
242        dict: The aliases of all associated packages
243    """
244    if package_name == None:
245        package_name = native.package_name()
246
247    # Determine the relevant maps to use
248    all_aliases_maps = []
249    if normal:
250        all_aliases_maps.append(_NORMAL_ALIASES)
251    if normal_dev:
252        all_aliases_maps.append(_NORMAL_DEV_ALIASES)
253    if proc_macro:
254        all_aliases_maps.append(_PROC_MACRO_ALIASES)
255    if proc_macro_dev:
256        all_aliases_maps.append(_PROC_MACRO_DEV_ALIASES)
257    if build:
258        all_aliases_maps.append(_BUILD_ALIASES)
259    if build_proc_macro:
260        all_aliases_maps.append(_BUILD_PROC_MACRO_ALIASES)
261
262    # Default to always using normal aliases
263    if not all_aliases_maps:
264        all_aliases_maps.append(_NORMAL_ALIASES)
265        all_aliases_maps.append(_PROC_MACRO_ALIASES)
266
267    aliases = _flatten_dependency_maps(all_aliases_maps).pop(package_name, None)
268
269    if not aliases:
270        return dict()
271
272    common_items = aliases.pop(_COMMON_CONDITION, {}).items()
273
274    # If there are only common items in the dictionary, immediately return them
275    if not len(aliases.keys()) == 1:
276        return dict(common_items)
277
278    # Build a single select statement where each conditional has accounted for the
279    # common set of aliases.
280    crate_aliases = {"//conditions:default": dict(common_items)}
281    for condition, deps in aliases.items():
282        condition_triples = _CONDITIONS[condition]
283        for triple in condition_triples:
284            if triple in crate_aliases:
285                crate_aliases[triple].update(deps)
286            else:
287                crate_aliases.update({triple: dict(deps.items() + common_items)})
288
289    return select(crate_aliases)
290
291###############################################################################
292# WORKSPACE MEMBER DEPS AND ALIASES
293###############################################################################
294
295_NORMAL_DEPENDENCIES = {% set deps_type = "normal" %}{% include "partials/module/deps_map.j2" %}
296
297_NORMAL_ALIASES = {% set deps_type = "normal" %}{% include "partials/module/aliases_map.j2" %}
298
299_NORMAL_DEV_DEPENDENCIES = {% set deps_type = "normal-dev" %}{% include "partials/module/deps_map.j2" %}
300
301_NORMAL_DEV_ALIASES = {% set deps_type = "normal-dev" %}{% include "partials/module/aliases_map.j2" %}
302
303_PROC_MACRO_DEPENDENCIES = {% set deps_type = "proc-macro" %}{% include "partials/module/deps_map.j2" %}
304
305_PROC_MACRO_ALIASES = {% set deps_type = "proc-macro-dev" %}{% include "partials/module/aliases_map.j2" %}
306
307_PROC_MACRO_DEV_DEPENDENCIES = {% set deps_type = "proc-macro-dev" %}{% include "partials/module/deps_map.j2" %}
308
309_PROC_MACRO_DEV_ALIASES = {% set deps_type = "normal-dev" %}{% include "partials/module/aliases_map.j2" %}
310
311_BUILD_DEPENDENCIES = {% set deps_type = "build" %}{% include "partials/module/deps_map.j2" %}
312
313_BUILD_ALIASES = {% set deps_type = "build" %}{% include "partials/module/aliases_map.j2" %}
314
315_BUILD_PROC_MACRO_DEPENDENCIES = {% set deps_type = "build-proc-macro" %}{% include "partials/module/deps_map.j2" %}
316
317_BUILD_PROC_MACRO_ALIASES = {% set deps_type = "build-proc-macro" %}{% include "partials/module/aliases_map.j2" %}
318
319_CONDITIONS = {
320{%- for condition, triples in platforms %}
321    "{{ condition | addslashes }}": {{ triples | sort | json_encode | safe }},
322{%- endfor %}
323}
324{% set current_vendor_mode = vendor_mode | default(value="remote") %}{% if current_vendor_mode == "remote" %}
325###############################################################################
326
327def crate_repositories():
328    """A macro for defining repositories for all generated crates.
329
330    Returns:
331      A list of repos visible to the module through the module extension.
332    """
333{%- for id, crate in context.crates %}
334{%- if not crate.repository %}{% continue %}{% endif %}
335{%- for repository_type, attrs in crate.repository %}
336{%- if repository_type in ["Http"] %}
337{% include "partials/module/repo_http.j2" %}
338{%- elif repository_type in ["Git"] %}
339{% include "partials/module/repo_git.j2" %}
340{%- else %}
341    {{ throw(message = "Unsupported checksum type: " ~ repository_type) }}
342{%- endif %}
343{%- endfor %}
344{%- endfor %}
345    return [
346        {%- for id in context.direct_deps %}
347        {%- set crate = context.crates | get(key=id) %}
348        {%- if crate.repository %}
349       struct(repo="{{ crate_repository(name = crate.name, version = crate.version) }}", is_dev_dep = False),
350        {%- endif %}
351        {%- endfor %}
352        {%- for id in context.direct_dev_deps %}
353        {%- set crate = context.crates | get(key=id) %}
354        {%- if crate.repository %}
355         struct(repo = "{{ crate_repository(name = crate.name, version = crate.version) }}", is_dev_dep = True),
356        {%- endif %}
357        {%- endfor %}
358    ]
359{%- endif %}
360