1*b6fb3261SAndroid Build Coastguard Worker"""Utility functions for generating protobuf code.""" 2*b6fb3261SAndroid Build Coastguard Worker 3*b6fb3261SAndroid Build Coastguard Worker_PROTO_EXTENSION = ".proto" 4*b6fb3261SAndroid Build Coastguard Worker_VIRTUAL_IMPORTS = "/_virtual_imports/" 5*b6fb3261SAndroid Build Coastguard Worker 6*b6fb3261SAndroid Build Coastguard Workerdef well_known_proto_libs(): 7*b6fb3261SAndroid Build Coastguard Worker return [ 8*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:any_proto", 9*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:api_proto", 10*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:compiler_plugin_proto", 11*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:descriptor_proto", 12*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:duration_proto", 13*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:empty_proto", 14*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:field_mask_proto", 15*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:source_context_proto", 16*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:struct_proto", 17*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:timestamp_proto", 18*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:type_proto", 19*b6fb3261SAndroid Build Coastguard Worker "@com_google_protobuf//:wrappers_proto", 20*b6fb3261SAndroid Build Coastguard Worker ] 21*b6fb3261SAndroid Build Coastguard Worker 22*b6fb3261SAndroid Build Coastguard Workerdef get_proto_root(workspace_root): 23*b6fb3261SAndroid Build Coastguard Worker """Gets the root protobuf directory. 24*b6fb3261SAndroid Build Coastguard Worker 25*b6fb3261SAndroid Build Coastguard Worker Args: 26*b6fb3261SAndroid Build Coastguard Worker workspace_root: context.label.workspace_root 27*b6fb3261SAndroid Build Coastguard Worker 28*b6fb3261SAndroid Build Coastguard Worker Returns: 29*b6fb3261SAndroid Build Coastguard Worker The directory relative to which generated include paths should be. 30*b6fb3261SAndroid Build Coastguard Worker """ 31*b6fb3261SAndroid Build Coastguard Worker if workspace_root: 32*b6fb3261SAndroid Build Coastguard Worker return "/{}".format(workspace_root) 33*b6fb3261SAndroid Build Coastguard Worker else: 34*b6fb3261SAndroid Build Coastguard Worker return "" 35*b6fb3261SAndroid Build Coastguard Worker 36*b6fb3261SAndroid Build Coastguard Workerdef _strip_proto_extension(proto_filename): 37*b6fb3261SAndroid Build Coastguard Worker if not proto_filename.endswith(_PROTO_EXTENSION): 38*b6fb3261SAndroid Build Coastguard Worker fail('"{}" does not end with "{}"'.format( 39*b6fb3261SAndroid Build Coastguard Worker proto_filename, 40*b6fb3261SAndroid Build Coastguard Worker _PROTO_EXTENSION, 41*b6fb3261SAndroid Build Coastguard Worker )) 42*b6fb3261SAndroid Build Coastguard Worker return proto_filename[:-len(_PROTO_EXTENSION)] 43*b6fb3261SAndroid Build Coastguard Worker 44*b6fb3261SAndroid Build Coastguard Workerdef proto_path_to_generated_filename(proto_path, fmt_str): 45*b6fb3261SAndroid Build Coastguard Worker """Calculates the name of a generated file for a protobuf path. 46*b6fb3261SAndroid Build Coastguard Worker 47*b6fb3261SAndroid Build Coastguard Worker For example, "examples/protos/helloworld.proto" might map to 48*b6fb3261SAndroid Build Coastguard Worker "helloworld.pb.h". 49*b6fb3261SAndroid Build Coastguard Worker 50*b6fb3261SAndroid Build Coastguard Worker Args: 51*b6fb3261SAndroid Build Coastguard Worker proto_path: The path to the .proto file. 52*b6fb3261SAndroid Build Coastguard Worker fmt_str: A format string used to calculate the generated filename. For 53*b6fb3261SAndroid Build Coastguard Worker example, "{}.pb.h" might be used to calculate a C++ header filename. 54*b6fb3261SAndroid Build Coastguard Worker 55*b6fb3261SAndroid Build Coastguard Worker Returns: 56*b6fb3261SAndroid Build Coastguard Worker The generated filename. 57*b6fb3261SAndroid Build Coastguard Worker """ 58*b6fb3261SAndroid Build Coastguard Worker return fmt_str.format(_strip_proto_extension(proto_path)) 59*b6fb3261SAndroid Build Coastguard Worker 60*b6fb3261SAndroid Build Coastguard Workerdef get_include_directory(source_file): 61*b6fb3261SAndroid Build Coastguard Worker """Returns the include directory path for the source_file. 62*b6fb3261SAndroid Build Coastguard Worker 63*b6fb3261SAndroid Build Coastguard Worker I.e. all of the include statements within the given source_file 64*b6fb3261SAndroid Build Coastguard Worker are calculated relative to the directory returned by this method. 65*b6fb3261SAndroid Build Coastguard Worker 66*b6fb3261SAndroid Build Coastguard Worker The returned directory path can be used as the "--proto_path=" argument 67*b6fb3261SAndroid Build Coastguard Worker value. 68*b6fb3261SAndroid Build Coastguard Worker 69*b6fb3261SAndroid Build Coastguard Worker Args: 70*b6fb3261SAndroid Build Coastguard Worker source_file: A proto file. 71*b6fb3261SAndroid Build Coastguard Worker 72*b6fb3261SAndroid Build Coastguard Worker Returns: 73*b6fb3261SAndroid Build Coastguard Worker The include directory path for the source_file. 74*b6fb3261SAndroid Build Coastguard Worker """ 75*b6fb3261SAndroid Build Coastguard Worker directory = source_file.path 76*b6fb3261SAndroid Build Coastguard Worker prefix_len = 0 77*b6fb3261SAndroid Build Coastguard Worker 78*b6fb3261SAndroid Build Coastguard Worker if is_in_virtual_imports(source_file): 79*b6fb3261SAndroid Build Coastguard Worker root, relative = source_file.path.split(_VIRTUAL_IMPORTS, 2) 80*b6fb3261SAndroid Build Coastguard Worker result = root + _VIRTUAL_IMPORTS + relative.split("/", 1)[0] 81*b6fb3261SAndroid Build Coastguard Worker return result 82*b6fb3261SAndroid Build Coastguard Worker 83*b6fb3261SAndroid Build Coastguard Worker if not source_file.is_source and directory.startswith(source_file.root.path): 84*b6fb3261SAndroid Build Coastguard Worker prefix_len = len(source_file.root.path) + 1 85*b6fb3261SAndroid Build Coastguard Worker 86*b6fb3261SAndroid Build Coastguard Worker if directory.startswith("external", prefix_len): 87*b6fb3261SAndroid Build Coastguard Worker external_separator = directory.find("/", prefix_len) 88*b6fb3261SAndroid Build Coastguard Worker repository_separator = directory.find("/", external_separator + 1) 89*b6fb3261SAndroid Build Coastguard Worker return directory[:repository_separator] 90*b6fb3261SAndroid Build Coastguard Worker else: 91*b6fb3261SAndroid Build Coastguard Worker return source_file.root.path if source_file.root.path else "." 92*b6fb3261SAndroid Build Coastguard Worker 93*b6fb3261SAndroid Build Coastguard Workerdef get_plugin_args( 94*b6fb3261SAndroid Build Coastguard Worker plugin, 95*b6fb3261SAndroid Build Coastguard Worker flags, 96*b6fb3261SAndroid Build Coastguard Worker dir_out, 97*b6fb3261SAndroid Build Coastguard Worker generate_mocks, 98*b6fb3261SAndroid Build Coastguard Worker plugin_name = "PLUGIN"): 99*b6fb3261SAndroid Build Coastguard Worker """Returns arguments configuring protoc to use a plugin for a language. 100*b6fb3261SAndroid Build Coastguard Worker 101*b6fb3261SAndroid Build Coastguard Worker Args: 102*b6fb3261SAndroid Build Coastguard Worker plugin: An executable file to run as the protoc plugin. 103*b6fb3261SAndroid Build Coastguard Worker flags: The plugin flags to be passed to protoc. 104*b6fb3261SAndroid Build Coastguard Worker dir_out: The output directory for the plugin. 105*b6fb3261SAndroid Build Coastguard Worker generate_mocks: A bool indicating whether to generate mocks. 106*b6fb3261SAndroid Build Coastguard Worker plugin_name: A name of the plugin, it is required to be unique when there 107*b6fb3261SAndroid Build Coastguard Worker are more than one plugin used in a single protoc command. 108*b6fb3261SAndroid Build Coastguard Worker Returns: 109*b6fb3261SAndroid Build Coastguard Worker A list of protoc arguments configuring the plugin. 110*b6fb3261SAndroid Build Coastguard Worker """ 111*b6fb3261SAndroid Build Coastguard Worker augmented_flags = list(flags) 112*b6fb3261SAndroid Build Coastguard Worker if generate_mocks: 113*b6fb3261SAndroid Build Coastguard Worker augmented_flags.append("generate_mock_code=true") 114*b6fb3261SAndroid Build Coastguard Worker 115*b6fb3261SAndroid Build Coastguard Worker augmented_dir_out = dir_out 116*b6fb3261SAndroid Build Coastguard Worker if augmented_flags: 117*b6fb3261SAndroid Build Coastguard Worker augmented_dir_out = ",".join(augmented_flags) + ":" + dir_out 118*b6fb3261SAndroid Build Coastguard Worker 119*b6fb3261SAndroid Build Coastguard Worker return [ 120*b6fb3261SAndroid Build Coastguard Worker "--plugin=protoc-gen-{plugin_name}={plugin_path}".format( 121*b6fb3261SAndroid Build Coastguard Worker plugin_name = plugin_name, 122*b6fb3261SAndroid Build Coastguard Worker plugin_path = plugin.path, 123*b6fb3261SAndroid Build Coastguard Worker ), 124*b6fb3261SAndroid Build Coastguard Worker "--{plugin_name}_out={dir_out}".format( 125*b6fb3261SAndroid Build Coastguard Worker plugin_name = plugin_name, 126*b6fb3261SAndroid Build Coastguard Worker dir_out = augmented_dir_out, 127*b6fb3261SAndroid Build Coastguard Worker ), 128*b6fb3261SAndroid Build Coastguard Worker ] 129*b6fb3261SAndroid Build Coastguard Worker 130*b6fb3261SAndroid Build Coastguard Workerdef _get_staged_proto_file(context, source_file): 131*b6fb3261SAndroid Build Coastguard Worker if (source_file.dirname == context.label.package or 132*b6fb3261SAndroid Build Coastguard Worker is_in_virtual_imports(source_file)): 133*b6fb3261SAndroid Build Coastguard Worker return source_file 134*b6fb3261SAndroid Build Coastguard Worker else: 135*b6fb3261SAndroid Build Coastguard Worker copied_proto = context.actions.declare_file(source_file.basename) 136*b6fb3261SAndroid Build Coastguard Worker context.actions.run_shell( 137*b6fb3261SAndroid Build Coastguard Worker inputs = [source_file], 138*b6fb3261SAndroid Build Coastguard Worker outputs = [copied_proto], 139*b6fb3261SAndroid Build Coastguard Worker command = "cp {} {}".format(source_file.path, copied_proto.path), 140*b6fb3261SAndroid Build Coastguard Worker mnemonic = "CopySourceProto", 141*b6fb3261SAndroid Build Coastguard Worker ) 142*b6fb3261SAndroid Build Coastguard Worker return copied_proto 143*b6fb3261SAndroid Build Coastguard Worker 144*b6fb3261SAndroid Build Coastguard Workerdef protos_from_context(context): 145*b6fb3261SAndroid Build Coastguard Worker """Copies proto files to the appropriate location. 146*b6fb3261SAndroid Build Coastguard Worker 147*b6fb3261SAndroid Build Coastguard Worker Args: 148*b6fb3261SAndroid Build Coastguard Worker context: The ctx object for the rule. 149*b6fb3261SAndroid Build Coastguard Worker 150*b6fb3261SAndroid Build Coastguard Worker Returns: 151*b6fb3261SAndroid Build Coastguard Worker A list of the protos. 152*b6fb3261SAndroid Build Coastguard Worker """ 153*b6fb3261SAndroid Build Coastguard Worker protos = [] 154*b6fb3261SAndroid Build Coastguard Worker for src in context.attr.deps: 155*b6fb3261SAndroid Build Coastguard Worker for file in src[ProtoInfo].direct_sources: 156*b6fb3261SAndroid Build Coastguard Worker protos.append(_get_staged_proto_file(context, file)) 157*b6fb3261SAndroid Build Coastguard Worker return protos 158*b6fb3261SAndroid Build Coastguard Worker 159*b6fb3261SAndroid Build Coastguard Workerdef includes_from_deps(deps): 160*b6fb3261SAndroid Build Coastguard Worker """Get includes from rule dependencies.""" 161*b6fb3261SAndroid Build Coastguard Worker return [ 162*b6fb3261SAndroid Build Coastguard Worker file 163*b6fb3261SAndroid Build Coastguard Worker for src in deps 164*b6fb3261SAndroid Build Coastguard Worker for file in src[ProtoInfo].transitive_imports.to_list() 165*b6fb3261SAndroid Build Coastguard Worker ] 166*b6fb3261SAndroid Build Coastguard Worker 167*b6fb3261SAndroid Build Coastguard Workerdef get_proto_arguments(protos, genfiles_dir_path): 168*b6fb3261SAndroid Build Coastguard Worker """Get the protoc arguments specifying which protos to compile.""" 169*b6fb3261SAndroid Build Coastguard Worker arguments = [] 170*b6fb3261SAndroid Build Coastguard Worker for proto in protos: 171*b6fb3261SAndroid Build Coastguard Worker strip_prefix_len = 0 172*b6fb3261SAndroid Build Coastguard Worker if is_in_virtual_imports(proto): 173*b6fb3261SAndroid Build Coastguard Worker incl_directory = get_include_directory(proto) 174*b6fb3261SAndroid Build Coastguard Worker if proto.path.startswith(incl_directory): 175*b6fb3261SAndroid Build Coastguard Worker strip_prefix_len = len(incl_directory) + 1 176*b6fb3261SAndroid Build Coastguard Worker elif proto.path.startswith(genfiles_dir_path): 177*b6fb3261SAndroid Build Coastguard Worker strip_prefix_len = len(genfiles_dir_path) + 1 178*b6fb3261SAndroid Build Coastguard Worker 179*b6fb3261SAndroid Build Coastguard Worker arguments.append(proto.path[strip_prefix_len:]) 180*b6fb3261SAndroid Build Coastguard Worker 181*b6fb3261SAndroid Build Coastguard Worker return arguments 182*b6fb3261SAndroid Build Coastguard Worker 183*b6fb3261SAndroid Build Coastguard Workerdef declare_out_files(protos, context, generated_file_format): 184*b6fb3261SAndroid Build Coastguard Worker """Declares and returns the files to be generated.""" 185*b6fb3261SAndroid Build Coastguard Worker 186*b6fb3261SAndroid Build Coastguard Worker out_file_paths = [] 187*b6fb3261SAndroid Build Coastguard Worker for proto in protos: 188*b6fb3261SAndroid Build Coastguard Worker if not is_in_virtual_imports(proto): 189*b6fb3261SAndroid Build Coastguard Worker out_file_paths.append(proto.basename) 190*b6fb3261SAndroid Build Coastguard Worker else: 191*b6fb3261SAndroid Build Coastguard Worker path = proto.path[proto.path.index(_VIRTUAL_IMPORTS) + 1:] 192*b6fb3261SAndroid Build Coastguard Worker out_file_paths.append(path) 193*b6fb3261SAndroid Build Coastguard Worker 194*b6fb3261SAndroid Build Coastguard Worker return [ 195*b6fb3261SAndroid Build Coastguard Worker context.actions.declare_file( 196*b6fb3261SAndroid Build Coastguard Worker proto_path_to_generated_filename( 197*b6fb3261SAndroid Build Coastguard Worker out_file_path, 198*b6fb3261SAndroid Build Coastguard Worker generated_file_format, 199*b6fb3261SAndroid Build Coastguard Worker ), 200*b6fb3261SAndroid Build Coastguard Worker ) 201*b6fb3261SAndroid Build Coastguard Worker for out_file_path in out_file_paths 202*b6fb3261SAndroid Build Coastguard Worker ] 203*b6fb3261SAndroid Build Coastguard Worker 204*b6fb3261SAndroid Build Coastguard Workerdef get_out_dir(protos, context): 205*b6fb3261SAndroid Build Coastguard Worker """ Returns the calculated value for --<lang>_out= protoc argument based on 206*b6fb3261SAndroid Build Coastguard Worker the input source proto files and current context. 207*b6fb3261SAndroid Build Coastguard Worker 208*b6fb3261SAndroid Build Coastguard Worker Args: 209*b6fb3261SAndroid Build Coastguard Worker protos: A list of protos to be used as source files in protoc command 210*b6fb3261SAndroid Build Coastguard Worker context: A ctx object for the rule. 211*b6fb3261SAndroid Build Coastguard Worker Returns: 212*b6fb3261SAndroid Build Coastguard Worker The value of --<lang>_out= argument. 213*b6fb3261SAndroid Build Coastguard Worker """ 214*b6fb3261SAndroid Build Coastguard Worker at_least_one_virtual = 0 215*b6fb3261SAndroid Build Coastguard Worker for proto in protos: 216*b6fb3261SAndroid Build Coastguard Worker if is_in_virtual_imports(proto): 217*b6fb3261SAndroid Build Coastguard Worker at_least_one_virtual = True 218*b6fb3261SAndroid Build Coastguard Worker elif at_least_one_virtual: 219*b6fb3261SAndroid Build Coastguard Worker fail("Proto sources must be either all virtual imports or all real") 220*b6fb3261SAndroid Build Coastguard Worker if at_least_one_virtual: 221*b6fb3261SAndroid Build Coastguard Worker out_dir = get_include_directory(protos[0]) 222*b6fb3261SAndroid Build Coastguard Worker ws_root = protos[0].owner.workspace_root 223*b6fb3261SAndroid Build Coastguard Worker if ws_root and out_dir.find(ws_root) >= 0: 224*b6fb3261SAndroid Build Coastguard Worker out_dir = "".join(out_dir.rsplit(ws_root, 1)) 225*b6fb3261SAndroid Build Coastguard Worker return struct( 226*b6fb3261SAndroid Build Coastguard Worker path = out_dir, 227*b6fb3261SAndroid Build Coastguard Worker import_path = out_dir[out_dir.find(_VIRTUAL_IMPORTS) + 1:], 228*b6fb3261SAndroid Build Coastguard Worker ) 229*b6fb3261SAndroid Build Coastguard Worker return struct(path = context.genfiles_dir.path, import_path = None) 230*b6fb3261SAndroid Build Coastguard Worker 231*b6fb3261SAndroid Build Coastguard Workerdef is_in_virtual_imports(source_file, virtual_folder = _VIRTUAL_IMPORTS): 232*b6fb3261SAndroid Build Coastguard Worker """Determines if source_file is virtual (is placed in _virtual_imports 233*b6fb3261SAndroid Build Coastguard Worker subdirectory). The output of all proto_library targets which use 234*b6fb3261SAndroid Build Coastguard Worker import_prefix and/or strip_import_prefix arguments is placed under 235*b6fb3261SAndroid Build Coastguard Worker _virtual_imports directory. 236*b6fb3261SAndroid Build Coastguard Worker 237*b6fb3261SAndroid Build Coastguard Worker Args: 238*b6fb3261SAndroid Build Coastguard Worker source_file: A proto file. 239*b6fb3261SAndroid Build Coastguard Worker virtual_folder: The virtual folder name (is set to "_virtual_imports" 240*b6fb3261SAndroid Build Coastguard Worker by default) 241*b6fb3261SAndroid Build Coastguard Worker Returns: 242*b6fb3261SAndroid Build Coastguard Worker True if source_file is located under _virtual_imports, False otherwise. 243*b6fb3261SAndroid Build Coastguard Worker """ 244*b6fb3261SAndroid Build Coastguard Worker return not source_file.is_source and virtual_folder in source_file.path 245