1""" 2Internal helpers for building the Python protobuf runtime. 3""" 4 5load("@rules_python//python:py_test.bzl", "py_test") 6 7def _remove_cross_repo_path(path): 8 components = path.split("/") 9 if components[0] == "..": 10 return "/".join(components[2:]) 11 return path 12 13def _internal_copy_files_impl(ctx): 14 strip_prefix = ctx.attr.strip_prefix 15 if strip_prefix[-1] != "/": 16 strip_prefix += "/" 17 18 src_dests = [] 19 for src in ctx.files.srcs: 20 short_path = _remove_cross_repo_path(src.short_path) 21 if short_path[:len(strip_prefix)] != strip_prefix: 22 fail("Source does not start with %s: %s" % 23 (strip_prefix, short_path)) 24 dest = ctx.actions.declare_file(short_path[len(strip_prefix):]) 25 src_dests.append([src, dest]) 26 27 if ctx.attr.is_windows: 28 bat_file = ctx.actions.declare_file(ctx.label.name + "_copy.bat") 29 ctx.actions.write( 30 output = bat_file, 31 content = "\r\n".join([ 32 '@copy /Y "{}" "{}" >NUL'.format( 33 src.path.replace("/", "\\"), 34 dest.path.replace("/", "\\"), 35 ) 36 for src, dest in src_dests 37 ]) + "\r\n", 38 ) 39 ctx.actions.run( 40 inputs = ctx.files.srcs, 41 tools = [bat_file], 42 outputs = [dest for src, dest in src_dests], 43 executable = "cmd.exe", 44 arguments = ["/C", bat_file.path.replace("/", "\\")], 45 mnemonic = "InternalCopyFile", 46 progress_message = "Copying files", 47 use_default_shell_env = True, 48 ) 49 50 else: 51 sh_file = ctx.actions.declare_file(ctx.label.name + "_copy.sh") 52 ctx.actions.write( 53 output = sh_file, 54 content = "\n".join([ 55 'cp -f "{}" "{}"'.format(src.path, dest.path) 56 for src, dest in src_dests 57 ]), 58 ) 59 ctx.actions.run( 60 inputs = ctx.files.srcs, 61 tools = [sh_file], 62 outputs = [dest for src, dest in src_dests], 63 executable = "bash", 64 arguments = [sh_file.path], 65 mnemonic = "InternalCopyFile", 66 progress_message = "Copying files", 67 use_default_shell_env = True, 68 ) 69 70 return [ 71 DefaultInfo(files = depset([dest for src, dest in src_dests])), 72 ] 73 74internal_copy_files_impl = rule( 75 doc = """ 76Implementation for internal_copy_files macro. 77 78This rule implements file copying, including a compatibility mode for Windows. 79""", 80 implementation = _internal_copy_files_impl, 81 attrs = { 82 "srcs": attr.label_list(allow_files = True, providers = [DefaultInfo]), 83 "strip_prefix": attr.string(), 84 "is_windows": attr.bool(), 85 }, 86) 87 88def internal_copy_files(name, srcs, strip_prefix, **kwargs): 89 """Copies common proto files to the python tree. 90 91 In order for Python imports to work, generated proto interfaces under 92 the google.protobuf package need to be in the same directory as other 93 source files. This rule copies the .proto files themselves, e.g. with 94 strip_prefix = 'src', 'src/google/protobuf/blah.proto' could be copied 95 to '<package>/google/protobuf/blah.proto'. 96 97 (An alternative might be to implement a separate rule to generate 98 Python code in a different location for the sources. However, this 99 would be strange behavior that doesn't match any other language's proto 100 library generation.) 101 102 Args: 103 name: the name for the rule. 104 srcs: the sources. 105 strip_prefix: the prefix to remove from each of the paths in 'srcs'. The 106 remainder will be used to construct the output path. 107 **kwargs: common rule arguments. 108 109 """ 110 internal_copy_files_impl( 111 name = name, 112 srcs = srcs, 113 strip_prefix = strip_prefix, 114 is_windows = select({ 115 "@bazel_tools//src/conditions:host_windows": True, 116 "//conditions:default": False, 117 }), 118 **kwargs 119 ) 120 121def internal_py_test(deps = [], **kwargs): 122 """Internal wrapper for shared test configuration 123 124 Args: 125 deps: any additional dependencies of the test. 126 **kwargs: arguments forwarded to py_test. 127 """ 128 py_test( 129 imports = ["."], 130 deps = deps + ["//python:python_test_lib"], 131 target_compatible_with = select({ 132 "@system_python//:supported": [], 133 "//conditions:default": ["@platforms//:incompatible"], 134 }), 135 **kwargs 136 ) 137