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