• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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