blob: 09a24a2ca0d41dac194f85d600d3a82bdb44072a [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import re
import textwrap
import sys
def usage():
print(
"Usage:\n"
" magma_h_gen.py INPUT OUTPUT\n"
" INPUT json file containing the magma interface definition\n"
" OUTPUT destination path for the magma header file to generate\n"
" Example: ./magma_h_gen.py ./magma.json ./magma.h\n"
" Generates the magma header based on a provided json definition.\n"
" Description fields are generated in the Doxygen format."
)
# Generate a string for a comment with a Doxygen tag, wrapping text as necessary.
def format_comment(type, comment):
wrap = 100
prefix_first = "/// \\" + type + " "
prefix_len = len(prefix_first)
prefix_rest = "///".ljust(prefix_len)
lines = textwrap.wrap(comment, wrap - prefix_len)
formatted = [f"{prefix_first}{lines[0]}"]
formatted.extend(f"{prefix_rest}{line}" for line in lines[1:])
return "\n".join(formatted)
# Generate a string for a magma export object, including its comment block.
def format_export(export):
# Verify no use of "struct" in types - these should use type_t instead.
for arg in export["arguments"]:
m = re.search(r"(.*)struct ([^\*]*)(\**)", arg["type"])
if m:
print(
f'Error in {export["name"]}: Argument type "{arg["type"]}" appears to be a struct.'
f' Did you mean to use "{m.group(1)}{m.group(2)}_t{m.group(3)}"?'
)
sys.exit(-1)
# Leading comment
lines = [f'///\n{format_comment("brief", export["description"])}']
lines.extend(
format_comment("param", f'{arg["name"]} {arg["description"]}')
for arg in export["arguments"]
)
lines.append("///")
# Function signature
lines.append(f'MAGMA_EXPORT {export["type"]} {export["name"]}(')
lines.append(
",\n".join(
f' {arg["type"]} {arg["name"]}' for arg in export["arguments"]
)
)
lines[-1] = f"{lines[-1]});"
lines.append("")
return "\n".join(lines)
# License string for the top of the file.
def license():
return (
"// Copyright 2016 The Fuchsia Authors. All rights reserved.\n"
"// Use of this source code is governed by a BSD-style license that can be\n"
"// found in the LICENSE file.\n"
)
def top_level_docs():
return (
"// Magma is the driver model for GPUs/NPUs on Fuchsia.\n"
"//\n"
'// Magma has two driver components: a hardware-specific library loaded into an "application"’s\n'
'// address space ("client driver", sometimes known as "Installable client driver" or "ICD");\n'
"// and a system driver that interfaces with the hardware. Magma is described in detail at [0].\n"
"//\n"
"// The Magma APIs are vendor independent. Some drivers may implement only the subset of the\n"
"// APIs that are relevant. The format of data carried inside commands/command buffers is not\n"
"// defined. Some APIs are explicitly extensible, such as magma_query, to allow for specific\n"
"// vendor/driver needs. Vendor specifics must be contained in a separate definitions file.\n"
"//\n"
"// Since client driver implementations may be written in a variety of languages (possibly C),\n"
"// the Magma bindings have a C interface, and may be used to interact with both Magma and Sysmem.\n"
"//\n"
"// The Magma bindings are OS independent so a client driver targeting Magma can easily be built\n"
"// for Fuchsia or Linux. On Fuchsia the APIs are backed by Zircon and FIDL; for virtualized Linux\n"
"// they are backed by a virtualization transport (virtmagma). APIs prefixed by 'magma_virt_' are\n"
"// for virtualization only. 32-bit and 64-bit builds are supported for virtualized clients.\n"
"//\n"
"// On Fuchsia the system driver is a separate process, so Magma APIs follow a feed forward design\n"
"// where possible to allow for efficient pipelining of requests.\n"
"//\n"
"// [0] https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0198_magma_api_design\n"
"//\n"
)
# Guard macro that goes at the beginning/end of the header (after license).
def guards(begin):
macro = "LIB_MAGMA_MAGMA_H_"
if begin:
return f"#ifndef {macro}\n#define {macro}\n"
return f"#endif // {macro}"
# Begin/end C linkage scope.
def externs(begin):
if begin:
return '#if defined(__cplusplus)\nextern "C" {\n#endif\n'
return "#if defined(__cplusplus)\n}\n#endif\n"
# Includes list.
def includes():
return (
"#include <lib/magma/magma_common_defs.h> // IWYU pragma: export\n"
"#include <stdint.h>\n"
)
# Warning comment about auto-generation.
def genwarning():
return (
"// NOTE: DO NOT EDIT THIS FILE!\n"
"// It is automatically generated by //src/graphics/lib/magma/include/magma/magma_h_gen.py\n"
)
def genformatoff():
return "// clang-format off\n"
def ifchange():
return "// LINT.IfChange"
def thenchange():
return "// LINT.ThenChange(magma_common_defs.h:version)"
def main():
if len(sys.argv) != 3:
usage()
return 2
try:
with open(sys.argv[1], "r") as file:
with open(sys.argv[2], "w") as dest:
magma = json.load(file)["magma-interface"]
lines = [
license(),
top_level_docs(),
genwarning(),
genformatoff(),
guards(True),
includes(),
ifchange(),
externs(True),
]
lines.extend(format_export(e) for e in magma["exports"])
lines.append(externs(False))
lines.append(thenchange())
lines.append(guards(False))
lines.append("")
dest.write("\n".join(lines))
except Exception as e:
print(f"Error accessing files: {e}")
usage()
return 1
if __name__ == "__main__":
sys.exit(main())