summaryrefslogtreecommitdiffhomepage
path: root/tools/go_marshal/defs.bzl
blob: 60a992b7f280d08c6f40320beae7eee7ddef0a8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""Marshal is a tool for generating marshalling interfaces for Go types.

The recommended way is to use the go_library rule defined below with mostly
identical configuration as the native go_library rule.

load("//tools/go_marshal:defs.bzl", "go_library")

go_library(
    name = "foo",
    srcs = ["foo.go"],
)

Under the hood, the go_marshal rule is used to generate a file that will
appear in a Go target; the output file should appear explicitly in a srcs list.
For example (the above is still the preferred way):

load("//tools/go_marshal:defs.bzl", "go_marshal")

go_marshal(
    name = "foo_abi",
    srcs = ["foo.go"],
    out = "foo_abi.go",
    package = "foo",
)

go_library(
    name = "foo",
    srcs = [
        "foo.go",
        "foo_abi.go",
    ],
    deps = [
       "//tools/go_marshal:marshal",
       "//pkg/sentry/platform/safecopy",
       "//pkg/sentry/usermem",
    ],
)
"""

load("@io_bazel_rules_go//go:def.bzl", _go_library = "go_library", _go_test = "go_test")

def _go_marshal_impl(ctx):
    """Execute the go_marshal tool."""
    output = ctx.outputs.lib
    output_test = ctx.outputs.test
    (build_dir, _, _) = ctx.build_file_path.rpartition("/BUILD")

    decl = "/".join(["gvisor.dev/gvisor", build_dir])

    # Run the marshal command.
    args = ["-output=%s" % output.path]
    args += ["-pkg=%s" % ctx.attr.package]
    args += ["-output_test=%s" % output_test.path]
    args += ["-declarationPkg=%s" % decl]

    if ctx.attr.debug:
        args += ["-debug"]

    args += ["--"]
    for src in ctx.attr.srcs:
        args += [f.path for f in src.files.to_list()]
    ctx.actions.run(
        inputs = ctx.files.srcs,
        outputs = [output, output_test],
        mnemonic = "GoMarshal",
        progress_message = "go_marshal: %s" % ctx.label,
        arguments = args,
        executable = ctx.executable._tool,
    )

# Generates save and restore logic from a set of Go files.
#
# Args:
#   name: the name of the rule.
#   srcs: the input source files. These files should include all structs in the
#         package that need to be saved.
#   imports: an optional list of extra, non-aliased, Go-style absolute import
#            paths.
#   out: the name of the generated file output. This must not conflict with any
#        other files and must be added to the srcs of the relevant go_library.
#   package: the package name for the input sources.
go_marshal = rule(
    implementation = _go_marshal_impl,
    attrs = {
        "srcs": attr.label_list(mandatory = True, allow_files = True),
        "libname": attr.string(mandatory = True),
        "imports": attr.string_list(mandatory = False),
        "package": attr.string(mandatory = True),
        "debug": attr.bool(doc = "enable debugging output from the go_marshal tool"),
        "_tool": attr.label(executable = True, cfg = "host", default = Label("//tools/go_marshal:go_marshal")),
    },
    outputs = {
        "lib": "%{name}_unsafe.go",
        "test": "%{name}_test.go",
    },
)

def go_library(name, srcs, deps = [], imports = [], debug = False, **kwargs):
    """wraps the standard go_library and does mashalling interface generation.

    Args:
      name: Same as native go_library.
      srcs: Same as native go_library.
      deps: Same as native go_library.
      imports: Extra import paths to pass to the go_marshal tool.
      debug: Enables debugging output from the go_marshal tool.
      **kwargs: Remaining args to pass to the native go_library rule unmodified.
    """
    go_marshal(
        name = name + "_abi_autogen",
        libname = name,
        srcs = [src for src in srcs if src.endswith(".go")],
        debug = debug,
        imports = imports,
        package = name,
    )

    extra_deps = [
        "//tools/go_marshal/marshal",
        "//pkg/sentry/platform/safecopy",
        "//pkg/sentry/usermem",
    ]

    all_srcs = srcs + [name + "_abi_autogen_unsafe.go"]
    all_deps = deps + []  #  + extra_deps

    for extra in extra_deps:
        if extra not in deps:
            all_deps.append(extra)

    _go_library(
        name = name,
        srcs = all_srcs,
        deps = all_deps,
        **kwargs
    )

    # Don't pass importpath arg to go_test.
    kwargs.pop("importpath", "")

    _go_test(
        name = name + "_abi_autogen_test",
        srcs = [name + "_abi_autogen_test.go"],
        # Generated test has a fixed set of dependencies since we generate these
        # tests. They should only depend on the library generated above, and the
        # Marshallable interface.
        deps = [
            ":" + name,
            "//tools/go_marshal/analysis",
        ],
        **kwargs
    )

def go_test(**kwargs):
    """Wraps the standard go_test."""
    _go_test(
        **kwargs
    )