1#!/usr/bin/env python3
2# Copyright 2018 The ChromiumOS Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Generates bindings that are used gpu_renderer.
7
8A sysroot and virglrenderer source checkout is required. The defaults to the
9root directory.
10"""
11
12from __future__ import print_function
13import argparse
14import multiprocessing.pool
15import os
16import subprocess
17import sys
18import tempfile
19
20# Bright green.
21PASS_COLOR = "\033[1;32m"
22# Bright red.
23FAIL_COLOR = "\033[1;31m"
24# Default color.
25END_COLOR = "\033[0m"
26
27verbose = False
28
29
30def generate_module(
31    module_name, allowlist, blocklist, header, clang_args, lib_name, derive_default
32):
33    args = [
34        "bindgen",
35        "--no-layout-tests",
36        "--allowlist-function",
37        allowlist,
38        "--allowlist-var",
39        allowlist,
40        "--allowlist-type",
41        allowlist,
42        "--blocklist-function",
43        blocklist,
44        "--blocklist-item",
45        blocklist,
46        "--blocklist-type",
47        blocklist,
48        "--no-prepend-enum-name",
49        "-o",
50        module_name + "_bindings.rs",
51    ]
52
53    if lib_name:
54        args.extend(["--raw-line", '#[cfg(feature = "{}")]'.format(module_name)])
55        args.extend(["--raw-line", '#[link(name = "{}")] extern {{}}'.format(lib_name)])
56
57    if derive_default:
58        args.append("--with-derive-default")
59
60    args.extend([header, "--"])
61    args.extend(clang_args)
62
63    if verbose:
64        print(" ".join(args))
65
66    if subprocess.Popen(args).wait() == 0:
67        return "pass"
68    else:
69        return "bindgen failed"
70
71
72def download_virgl(src, dst, branch):
73    virgl_src = tempfile.TemporaryDirectory(prefix="virglrenderer-src")
74
75    args = ["git", "clone"]
76
77    if branch:
78        args.extend(["-b", branch])
79
80    args.extend([src, dst])
81
82    if verbose:
83        print(" ".join(args))
84
85    if subprocess.Popen(args).wait() == 0:
86        return True
87    else:
88        return False
89
90
91def get_parser():
92    """Gets the argument parser"""
93    parser = argparse.ArgumentParser(description=__doc__)
94    parser.add_argument("--sysroot", default="/", help="sysroot directory (default=%(default)s)")
95    parser.add_argument("--virglrenderer", help="virglrenderer src dir/repo (default=%(default)s)")
96    parser.add_argument(
97        "--virgl_branch",
98        default="master",
99        help="virglrenderer branch name (default=%(default)s)",
100    )
101    parser.add_argument(
102        "--verbose",
103        "-v",
104        action="store_true",
105        help="enable verbose output (default=%(default)s)",
106    )
107    return parser
108
109
110def main(argv):
111    global verbose
112    os.chdir(os.path.dirname(sys.argv[0]))
113    opts = get_parser().parse_args(argv)
114    if opts.verbose:
115        verbose = True
116
117    if opts.virglrenderer:
118        if "://" in opts.virglrenderer:
119            virgl_src_dir_temp = tempfile.TemporaryDirectory(prefix="virglrenderer-src")
120            virgl_src_dir = virgl_src_dir_temp.name
121            if not download_virgl(opts.virglrenderer, virgl_src_dir, opts.virgl_branch):
122                print("failed to clone '{}' to '{}'".format(virgl_src_dir, opts.virgl_branch))
123                sys.exit(1)
124        else:
125            virgl_src_dir = opts.virglrenderer
126
127        header = os.path.join(virgl_src_dir, "src/virglrenderer.h")
128    else:
129        header = os.path.join(opts.sysroot, "usr/include/virgl/virglrenderer.h")
130
131    clang_args = [
132        "-I",
133        os.path.join(opts.sysroot, "usr/include"),
134        "-D",
135        "VIRGL_RENDERER_UNSTABLE_APIS",
136    ]
137
138    modules = (
139        (
140            "virgl_renderer",
141            "(virgl|VIRGL)_.+",  # allowlist
142            ".*(va_list|debug_callback).*",  # blocklist
143            header,
144            clang_args,
145            "virglrenderer",
146            True,
147        ),
148    )
149
150    pool = multiprocessing.pool.Pool(len(modules))
151    results = pool.starmap(generate_module, modules, 1)
152
153    return_fail = False
154    print("---")
155    print("generate module summary:")
156    for module, result in zip(modules, results):
157        result_color = FAIL_COLOR
158        if result == "pass":
159            result_color = PASS_COLOR
160        else:
161            return_fail = True
162
163        print("%15s: %s%s%s" % (module[0], result_color, result, END_COLOR))
164
165    if return_fail:
166        sys.exit(1)
167
168    with open("mod.rs", "w") as f:
169        print("/* generated by generate.py */", file=f)
170        print("#![allow(dead_code)]", file=f)
171        print("#![allow(non_camel_case_types)]", file=f)
172        print("#![allow(non_snake_case)]", file=f)
173        print("#![allow(non_upper_case_globals)]", file=f)
174        print("pub mod virgl_debug_callback_bindings;", file=f)
175        for module in modules:
176            print("pub mod", module[0] + "_bindings;", file=f)
177
178
179if __name__ == "__main__":
180    sys.exit(main(sys.argv[1:]))
181