1# Copyright 2023 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 15"""Skylib module containing utilities for Bazel modules and module extensions.""" 16 17def _as_extension(macro, doc = None): 18 """Wraps a WORKSPACE dependency macro into a module extension. 19 20 Example: 21 ```starlark 22 def rules_foo_deps(optional_arg = True): 23 some_repo_rule(name = "foobar") 24 http_archive(name = "bazqux") 25 26 rules_foo_deps_ext = modules.as_extension(rules_foo_deps) 27 ``` 28 29 Args: 30 macro: A [WORKSPACE dependency macro](https://bazel.build/rules/deploying#dependencies), i.e., 31 a function with no required parameters that instantiates one or more repository rules. 32 doc: A description of the module extension that can be extracted by documentation generating 33 tools. 34 35 Returns: 36 A module extension that generates the repositories instantiated by the given macro and also 37 uses [`use_all_repos`](#use_all_repos) to indicate that all of those repositories should be 38 imported via `use_repo`. The extension is marked as reproducible if supported by the current 39 version of Bazel and thus doesn't result in a lockfile entry. 40 """ 41 42 def _ext_impl(module_ctx): 43 macro() 44 45 # Setting `reproducible` is safe since `macro`, as a function without parameters, must be 46 # deterministic. 47 return _use_all_repos(module_ctx, reproducible = True) 48 49 kwargs = {} 50 if doc != None: 51 kwargs["doc"] = doc 52 53 return module_extension( 54 implementation = _ext_impl, 55 **kwargs 56 ) 57 58def _use_all_repos(module_ctx, reproducible = False): 59 """Return from a module extension that should have all its repositories imported via `use_repo`. 60 61 Example: 62 ```starlark 63 def _ext_impl(module_ctx): 64 some_repo_rule(name = "foobar") 65 http_archive(name = "bazqux") 66 return modules.use_all_repos(module_ctx) 67 68 ext = module_extension(_ext_impl) 69 ``` 70 71 Args: 72 module_ctx: The [`module_ctx`](https://bazel.build/rules/lib/builtins/module_ctx) object 73 passed to the module extension's implementation function. 74 reproducible: The value of the `reproducible` parameter to pass to the 75 [`extension_metadata`](https://bazel.build/rules/lib/builtins/extension_metadata.html) 76 object returned by this function. This is safe to set with Bazel versions that don't 77 support this parameter and will be ignored in that case. 78 79 Returns: 80 An [`extension_metadata`](https://bazel.build/rules/lib/builtins/extension_metadata.html) 81 object that, when returned from a module extension implementation function, specifies that all 82 repositories generated by this extension should be imported via `use_repo`. If the current 83 version of Bazel doesn't support `extension_metadata`, returns `None` instead, which can 84 safely be returned from a module extension implementation function in all versions of Bazel. 85 """ 86 87 # module_ctx.extension_metadata is available in Bazel 6.2.0 and later. 88 # If not available, returning None from a module extension is equivalent to not returning 89 # anything. 90 extension_metadata = getattr(module_ctx, "extension_metadata", None) 91 if not extension_metadata: 92 return None 93 94 # module_ctx.root_module_has_non_dev_dependency is available in Bazel 6.3.0 and later. 95 root_module_has_non_dev_dependency = getattr( 96 module_ctx, 97 "root_module_has_non_dev_dependency", 98 None, 99 ) 100 if root_module_has_non_dev_dependency == None: 101 return None 102 103 # module_ctx.extension_metadata has the paramater `reproducible` as of Bazel 7.1.0. We can't 104 # test for it directly and would ideally use bazel_features to check for it, but adding a 105 # dependency on it would require complicating the WORKSPACE setup for skylib. Thus, test for 106 # it by checking the availability of another feature introduced in 7.1.0. 107 extension_metadata_kwargs = {} 108 if hasattr(module_ctx, "watch"): 109 extension_metadata_kwargs["reproducible"] = reproducible 110 111 return extension_metadata( 112 root_module_direct_deps = "all" if root_module_has_non_dev_dependency else [], 113 root_module_direct_dev_deps = [] if root_module_has_non_dev_dependency else "all", 114 **extension_metadata_kwargs 115 ) 116 117modules = struct( 118 as_extension = _as_extension, 119 use_all_repos = _use_all_repos, 120) 121