1# Protocol Buffers - Google's data interchange format 2# Copyright 2024 Google Inc. All rights reserved. 3# 4# Use of this source code is governed by a BSD-style 5# license that can be found in the LICENSE file or at 6# https://developers.google.com/open-source/licenses/bsd 7# 8""" 9Definition of ProtoInfo provider. 10""" 11 12_warning = """ Don't use this field. It's intended for internal use and will be changed or removed 13 without warning.""" 14 15def _uniq(iterable): 16 unique_elements = {element: None for element in iterable} 17 return list(unique_elements.keys()) 18 19def _join(*path): 20 return "/".join([p for p in path if p != ""]) 21 22def _empty_to_dot(path): 23 return path if path else "." 24 25def _from_root(root, repo, relpath): 26 """Constructs an exec path from root to relpath""" 27 if not root: 28 # `relpath` is a directory with an input source file, the exec path is one of: 29 # - when in main repo: `package/path` 30 # - when in a external repository: `external/repo/package/path` 31 # - with sibling layout: `../repo/package/path` 32 return _join(repo, relpath) 33 else: 34 # `relpath` is a directory with a generated file or an output directory: 35 # - when in main repo: `{root}/package/path` 36 # - when in an external repository: `{root}/external/repo/package/path` 37 # - with sibling layout: `{root}/package/path` 38 return _join(root, "" if repo.startswith("../") else repo, relpath) 39 40def _create_proto_info(*, srcs, deps, descriptor_set, proto_path = "", workspace_root = "", bin_dir = None, allow_exports = None): 41 """Constructs ProtoInfo. 42 43 Args: 44 srcs: ([File]) List of .proto files (possibly under _virtual path) 45 deps: ([ProtoInfo]) List of dependencies 46 descriptor_set: (File) Descriptor set for this Proto 47 proto_path: (str) Path that should be stripped from files in srcs. When 48 stripping is needed, the files should be symlinked into `_virtual_imports/target_name` 49 directory. Only such paths are accepted. 50 workspace_root: (str) Set to ctx.workspace_root if this is not the main repository. 51 bin_dir: (str) Set to ctx.bin_dir if _virtual_imports are used. 52 allow_exports: (Target) The packages where this proto_library can be exported. 53 54 Returns: 55 (ProtoInfo) 56 """ 57 58 # Validate parameters 59 src_prefix = _join(workspace_root.replace("external/", "../"), proto_path) 60 for src in srcs: 61 if type(src) != "File": 62 fail("srcs parameter expects a list of Files") 63 if src.owner.workspace_root != workspace_root: 64 fail("srcs parameter expects all files to have the same workspace_root: ", workspace_root) 65 if not src.short_path.startswith(src_prefix): 66 fail("srcs parameter expects all files start with %s" % src_prefix) 67 if type(descriptor_set) != "File": 68 fail("descriptor_set parameter expected to be a File") 69 if proto_path: 70 if "_virtual_imports/" not in proto_path: 71 fail("proto_path needs to contain '_virtual_imports' directory") 72 if proto_path.split("/")[-2] != "_virtual_imports": 73 fail("proto_path needs to be formed like '_virtual_imports/target_name'") 74 if not bin_dir: 75 fail("bin_dir parameter should be set when _virtual_imports are used") 76 77 direct_proto_sources = srcs 78 transitive_proto_sources = depset( 79 direct = direct_proto_sources, 80 transitive = [dep._transitive_proto_sources for dep in deps], 81 order = "preorder", 82 ) 83 transitive_sources = depset( 84 direct = srcs, 85 transitive = [dep.transitive_sources for dep in deps], 86 order = "preorder", 87 ) 88 89 # There can be up more than 1 direct proto_paths, for example when there's 90 # a generated and non-generated .proto file in srcs 91 root_paths = _uniq([src.root.path for src in srcs]) 92 transitive_proto_path = depset( 93 direct = [_empty_to_dot(_from_root(root, workspace_root, proto_path)) for root in root_paths], 94 transitive = [dep.transitive_proto_path for dep in deps], 95 ) 96 97 if srcs: 98 check_deps_sources = depset(direct = srcs) 99 else: 100 check_deps_sources = depset(transitive = [dep.check_deps_sources for dep in deps]) 101 102 transitive_descriptor_sets = depset( 103 direct = [descriptor_set], 104 transitive = [dep.transitive_descriptor_sets for dep in deps], 105 ) 106 107 # Layering checks. 108 if srcs: 109 exported_sources = depset(direct = direct_proto_sources) 110 else: 111 exported_sources = depset(transitive = [dep._exported_sources for dep in deps]) 112 113 if "_virtual_imports/" in proto_path: 114 #TODO: remove bin_dir from proto_source_root (when users assuming it's there are migrated) 115 proto_source_root = _empty_to_dot(_from_root(bin_dir, workspace_root, proto_path)) 116 elif workspace_root.startswith("../"): 117 proto_source_root = proto_path 118 else: 119 proto_source_root = _empty_to_dot(_join(workspace_root, proto_path)) 120 121 proto_info = dict( 122 direct_sources = srcs, 123 transitive_sources = transitive_sources, 124 direct_descriptor_set = descriptor_set, 125 transitive_descriptor_sets = transitive_descriptor_sets, 126 proto_source_root = proto_source_root, 127 transitive_proto_path = transitive_proto_path, 128 check_deps_sources = check_deps_sources, 129 transitive_imports = transitive_sources, 130 _direct_proto_sources = direct_proto_sources, 131 _transitive_proto_sources = transitive_proto_sources, 132 _exported_sources = exported_sources, 133 ) 134 if allow_exports: 135 proto_info["allow_exports"] = allow_exports 136 return proto_info 137 138ProtoInfo, _ = provider( 139 doc = "Encapsulates information provided by a `proto_library.`", 140 fields = { 141 "direct_sources": "(list[File]) The `.proto` source files from the `srcs` attribute.", 142 "transitive_sources": """(depset[File]) The `.proto` source files from this rule and all 143 its dependent protocol buffer rules.""", 144 "direct_descriptor_set": """(File) The descriptor set of the direct sources. If no srcs, 145 contains an empty file.""", 146 "transitive_descriptor_sets": """(depset[File]) A set of descriptor set files of all 147 dependent `proto_library` rules, and this one's. This is not the same as passing 148 --include_imports to proto-compiler. Will be empty if no dependencies.""", 149 "proto_source_root": """(str) The directory relative to which the `.proto` files defined in 150 the `proto_library` are defined. For example, if this is `a/b` and the rule has the 151 file `a/b/c/d.proto` as a source, that source file would be imported as 152 `import c/d.proto` 153 154 In principle, the `proto_source_root` directory itself should always 155 be relative to the output directory (`ctx.bin_dir`). 156 157 This is at the moment not true for `proto_libraries` using (additional and/or strip) 158 import prefixes. `proto_source_root` is in this case prefixed with the output 159 directory. For example, the value is similar to 160 `bazel-out/k8-fastbuild/bin/a/_virtual_includes/b` for an input file in 161 `a/_virtual_includes/b/c.proto` that should be imported as `c.proto`. 162 163 When using the value please account for both cases in a general way. 164 That is assume the value is either prefixed with the output directory or not. 165 This will make it possible to fix `proto_library` in the future. 166 """, 167 "transitive_proto_path": """(depset(str) A set of `proto_source_root`s collected from the 168 transitive closure of this rule.""", 169 "check_deps_sources": """(depset[File]) The `.proto` sources from the 'srcs' attribute. 170 If the library is a proxy library that has no sources, it contains the 171 `check_deps_sources` from this library's direct deps.""", 172 "allow_exports": """(Target) The packages where this proto_library can be exported.""", 173 174 # Deprecated fields: 175 "transitive_imports": """(depset[File]) Deprecated: use `transitive_sources` instead.""", 176 177 # Internal fields: 178 "_direct_proto_sources": """(list[File]) The `ProtoSourceInfo`s from the `srcs` 179 attribute.""" + _warning, 180 "_transitive_proto_sources": """(depset[File]) The `ProtoSourceInfo`s from this 181 rule and all its dependent protocol buffer rules.""" + _warning, 182 "_exported_sources": """(depset[File]) A set of `ProtoSourceInfo`s that may be 183 imported by another `proto_library` depending on this one.""" + _warning, 184 }, 185 init = _create_proto_info, 186) 187