blob: 00b0b8bc08528b780316550a85f6f969d81cf856 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2019 The Chromium 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 filecmp
import optparse
import os.path
import platform
import re
import subprocess
import sys
from xml.etree import ElementTree
_VK_XML_FILE = "third_party/vulkan-deps/vulkan-headers/src/registry/vk.xml"
_STRUCTS = [
"VkExtensionProperties",
"VkLayerProperties",
"VkPhysicalDeviceProperties",
"VkPhysicalDeviceFeatures",
"VkQueueFamilyProperties",
]
_SELF_LOCATION = os.path.dirname(os.path.abspath(__file__))
_MOJO_TYPES = set([
"uint8",
"uint16",
"uint32",
"int8",
"int16",
"int32",
"float",
"string",
])
_VULKAN_BASIC_TYPE_MAP = set([
"uint8_t",
"uint16_t",
"uint32_t",
"uint64_t",
"int8_t",
"int16_t",
"int32_t",
"int64_t",
"size_t",
"VkBool32",
"float",
"char",
])
# types to mojo type
_type_map = {
"uint8_t" : "uint8",
"uint16_t" : "uint16",
"uint32_t" : "uint32",
"uint64_t" : "uint64",
"int8_t" : "int8",
"int16_t" : "int16",
"int32_t" : "int32",
"int64_t" : "int64",
"size_t" : "uint64",
"VkBool32" : "bool",
"float" : "float",
"char" : "char",
}
_structs = {}
_enums = {}
_defines = {}
_handles = set([])
_generated_types = []
def ValueNameToVALUE_NAME(name):
return re.sub(
r'(?<=[a-z])[A-Z]|(?<!^)[A-Z](?=[a-z])', r"_\g<0>", name).upper()
def ParseEnums(reg):
for type_elm in reg.findall("enums"):
name = type_elm.get("name")
if name == "API Constants":
for enum in type_elm.findall("enum"):
enum_name = enum.get("name")
enum_value = enum.get("value")
enum_alias = enum.get("alias")
if enum_alias:
continue
_defines[enum_name] = enum_value
continue
# Skip VkResult and NameBits
if name == "VkResult":
value_name_prefix = "VK"
elif name.endswith("FlagBits"):
value_name_prefix = ValueNameToVALUE_NAME(name[:-len("FlagBits")])
elif name.endswith("FlagBitsKHR"):
value_name_prefix = ValueNameToVALUE_NAME(name[:-len("FlagBitsKHR")])
else:
value_name_prefix = ValueNameToVALUE_NAME(name)
values = []
for enum in type_elm.findall("enum"):
enum_name = enum.get("name")
enum_value = enum.get("value")
mojom_name = enum_name[len(value_name_prefix) + 1:]
values.append((enum_name, enum_value, mojom_name))
_enums[name] = values
def ParseHandleElement(element):
name = element.get("name") or element.find("name").text
_handles.add(name)
def ParseBaseTypeElement(element):
name = element.find("name").text
_type = None if element.find("type") is None else element.find("type").text
if name not in _type_map:
_type_map[name] = _type
def ParseBitmaskElement(element):
name = element.find("name")
if name is not None:
name = name.text
_type = None if element.find("type") is None else element.find("type").text
_type_map[name] = _type
def ParseStructElement(element):
name = element.get("name") or element.find("name").text
members = []
for member in element.findall("member"):
member_type = member.find("type").text
member_name = member.find("name").text
member_array_len = None
for text in member.itertext():
if text.startswith("["):
member_array_len = text[1:-1]
if not member_array_len:
member_array_len = member.find("enum").text
break
members.append((member_name, member_type, member_array_len))
_structs[name] = members
def ParseTypes(reg):
for type_elm in reg.findall("types/type"):
category = type_elm.get("category")
if category == "handle":
ParseHandleElement(type_elm)
elif category == "basetype":
ParseBaseTypeElement(type_elm)
elif category == "bitmask":
ParseBitmaskElement(type_elm)
elif category == "struct":
ParseStructElement(type_elm)
def ParseVkXMLFile(path):
tree = ElementTree.parse(path)
reg = tree.getroot()
ParseEnums(reg)
ParseTypes(reg)
def WriteMojomEnum(name, mojom_file):
if name in _generated_types:
return
_generated_types.append(name)
values = _enums[name]
mojom_file.write("\n")
mojom_file.write("enum %s {\n" % name)
for _, value, mojom_value_name in values:
mojom_file.write(" %s = %s,\n" % (mojom_value_name, value))
mojom_file.write(" INVALID_VALUE = -1,\n")
mojom_file.write("};\n")
def WriteMojomStruct(name, mojom_file):
if name in _generated_types:
return
_generated_types.append(name)
fields = _structs[name]
deps = []
for field_name, field_type, array_len in fields:
if field_type in _structs or field_type in _enums:
deps.append(field_type)
WriteMojomTypes(deps, mojom_file)
mojom_file.write("\n")
mojom_file.write("struct %s {\n" % name)
for field_name, field_type, array_len in fields:
if field_type in _type_map:
while field_type in _type_map and field_type != _type_map[field_type]:
field_type = _type_map[field_type]
else:
assert field_type in _structs or field_type in _enums or \
field_type in _handles, "Undefine type: '%s'" % field_type
if field_type == "char":
assert array_len
array_len = _defines[array_len]
mojom_file.write(" string %s;\n" % field_name)
elif not array_len:
mojom_file.write(" %s %s;\n" % (field_type, field_name))
else:
if not array_len.isdigit():
array_len = _defines[array_len]
assert array_len.isdigit(), "%s is not a digit." % array_len
mojom_file.write(
" array<%s, %s> %s;\n" % (field_type, array_len, field_name))
mojom_file.write("};\n")
def WriteMojomTypes(types, mojom_file):
for t in types:
if t in _structs:
WriteMojomStruct(t, mojom_file)
elif t in _enums:
WriteMojomEnum(t, mojom_file)
else:
pass
def GenerateMojom(mojom_file):
mojom_file.write(
'''// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is auto-generated from
// gpu/ipc/common/generate_vulkan_types.py
// It's formatted by clang-format using chromium coding style:
// clang-format -i -style=chromium filename
// DO NOT EDIT!
module gpu.mojom;
''')
WriteMojomTypes(_STRUCTS, mojom_file)
def NormalizedCamelCase(identifier):
result = identifier[0].upper()
lowercase_next = True
for i in range(1, len(identifier)):
if identifier[i].isupper():
if lowercase_next:
result += identifier[i].lower()
else:
result += identifier[i]
lowercase_next = True
else:
lowercase_next = False
result += identifier[i]
return result
def WriteStructTraits(name, traits_header_file, traits_source_file):
traits_header_file.write(
"""
template <>
struct StructTraits<gpu::mojom::%sDataView, %s> {
""" % (name, name)
)
fields = _structs[name]
for field_name, field_type, array_len in fields:
if field_type == "VkBool32":
field_type = "bool"
elif field_type == "VkDeviceSize":
field_type = "bool"
if field_type == "char":
assert array_len
traits_header_file.write(
"""
static base::StringPiece %s(const %s& input) {
return input.%s;
}
""" % (field_name, name, field_name))
elif array_len:
traits_header_file.write(
"""
static base::span<const %s> %s(const %s& input) {
return input.%s;
}
""" % (field_type, field_name, name, field_name))
elif field_type in _structs:
traits_header_file.write(
"""
static const %s& %s(const %s& input) {
return input.%s;
}
""" % (field_type, field_name, name, field_name))
else:
traits_header_file.write(
"""
static %s %s(const %s& input) {
return input.%s;
}
""" % (field_type, field_name, name, field_name))
traits_header_file.write(
"""
static bool Read(gpu::mojom::%sDataView data, %s* out);
""" % (name, name))
traits_source_file.write(
"""
// static
bool StructTraits<gpu::mojom::%sDataView, %s>::Read(
gpu::mojom::%sDataView data, %s* out) {
""" % (name, name, name, name))
fields = _structs[name]
for field_name, field_type, array_len in fields:
if field_type == "VkBool32":
field_type = "bool"
elif field_type == "VkDeviceSize":
field_type = "bool"
if field_type == "char":
assert array_len
read_method = "Read%s" % (NormalizedCamelCase(field_name))
traits_source_file.write(
"""
base::StringPiece %s;
if (!data.%s(&%s))
return false;
%s.copy(out->%s, sizeof(out->%s));
""" % (field_name, read_method, field_name, field_name, field_name, field_name))
elif array_len:
read_method = "Read%s" % (NormalizedCamelCase(field_name))
traits_source_file.write(
"""
base::span<%s> %s(out->%s);
if (!data.%s(&%s))
return false;
""" % (field_type, field_name, field_name, read_method, field_name))
elif field_type in _structs or field_type in _enums:
traits_source_file.write(
"""
if (!data.Read%s(&out->%s))
return false;
""" % (NormalizedCamelCase(field_name), field_name))
else:
traits_source_file.write(
"""
out->%s = data.%s();
""" % (field_name, field_name))
traits_source_file.write(
"""
return true;
}
""")
traits_header_file.write("};\n")
def WriteEnumTraits(name, traits_header_file):
traits_header_file.write(
"""
template <>
struct EnumTraits<gpu::mojom::%s, %s> {
static gpu::mojom::%s ToMojom(%s input) {
switch (input) {
""" % (name, name, name, name))
for value_name, _, mojom_value_name in _enums[name]:
traits_header_file.write(
"""
case %s::%s:
return gpu::mojom::%s::%s;"""
% (name, value_name, name, mojom_value_name))
traits_header_file.write(
"""
default:
NOTREACHED();
return gpu::mojom::%s::INVALID_VALUE;
}
}
static bool FromMojom(gpu::mojom::%s input, %s* out) {
switch (input) {
""" % (name, name, name))
for value_name, _, mojom_value_name in _enums[name]:
traits_header_file.write(
"""
case gpu::mojom::%s::%s:
*out = %s::%s;
return true;""" % (name, mojom_value_name, name, value_name))
traits_header_file.write(
"""
case gpu::mojom::%s::INVALID_VALUE:
NOTREACHED();
return false;
}
NOTREACHED();
return false;
}
};""" % name)
def GenerateTraitsFile(traits_header_file, traits_source_file):
traits_header_file.write(
"""// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is auto-generated from
// gpu/ipc/common/generate_vulkan_types.py
// It's formatted by clang-format using chromium coding style:
// clang-format -i -style=chromium filename
// DO NOT EDIT!
#ifndef GPU_IPC_COMMON_VULKAN_TYPES_MOJOM_TRAITS_H_
#define GPU_IPC_COMMON_VULKAN_TYPES_MOJOM_TRAITS_H_
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "gpu/ipc/common/vulkan_types.h"
#include "gpu/ipc/common/vulkan_types.mojom-shared.h"
namespace mojo {
""")
traits_source_file.write(
"""// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is auto-generated from
// gpu/ipc/common/generate_vulkan_types.py
// It's formatted by clang-format using chromium coding style:
// clang-format -i -style=chromium filename
// DO NOT EDIT!
#include "gpu/ipc/common/vulkan_info_mojom_traits.h"
namespace mojo {
""")
for t in _generated_types:
if t in _structs:
WriteStructTraits(t, traits_header_file, traits_source_file)
elif t in _enums:
WriteEnumTraits(t, traits_header_file)
traits_header_file.write(
"""
} // namespace mojo
#endif // GPU_IPC_COMMON_VULKAN_TYPES_MOJOM_TRAITS_H_""")
traits_source_file.write(
"""
} // namespace mojo""")
def GenerateTypemapFile(typemap_file):
typemap_file.write(
"""# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This file is auto-generated from
# gpu/ipc/common/generate_vulkan_types.py
# DO NOT EDIT!
generated_vulkan_type_mappings = [""")
for t in _generated_types:
typemap_file.write(
"""
{
mojom = "gpu.mojom.%s"
cpp = "::%s"
},""" % (t, t))
typemap_file.write("\n]\n")
def main(argv):
"""This is the main function."""
parser = optparse.OptionParser()
parser.add_option(
"--output-dir",
help="Output directory for generated files. Defaults to this script's "
"directory.")
parser.add_option(
"-c", "--check", action="store_true",
help="Check if output files match generated files in chromium root "
"directory. Use this in PRESUBMIT scripts with --output-dir.")
(options, _) = parser.parse_args(args=argv)
# Support generating files for PRESUBMIT.
if options.output_dir:
output_dir = options.output_dir
else:
output_dir = _SELF_LOCATION
def ClangFormat(filename):
formatter = "clang-format"
if platform.system() == "Windows":
formatter += ".bat"
subprocess.call([formatter, "-i", "-style=chromium", filename])
vk_xml_file_path = os.path.join(
_SELF_LOCATION, "../../..", _VK_XML_FILE)
ParseVkXMLFile(vk_xml_file_path)
mojom_file_name = "vulkan_types.mojom"
mojom_file = open(
os.path.join(output_dir, mojom_file_name), 'wb')
GenerateMojom(mojom_file)
mojom_file.close()
ClangFormat(mojom_file.name)
traits_header_file_name = "vulkan_types_mojom_traits.h"
traits_header_file = \
open(os.path.join(output_dir, traits_header_file_name), 'wb')
traits_source_file_name = "vulkan_types_mojom_traits.cc"
traits_source_file = \
open(os.path.join(output_dir, traits_source_file_name), 'wb')
GenerateTraitsFile(traits_header_file, traits_source_file)
traits_header_file.close()
ClangFormat(traits_header_file.name)
traits_source_file.close()
ClangFormat(traits_source_file.name)
typemap_file_name = "generated_vulkan_type_mappings.gni"
typemap_file = open(
os.path.join(output_dir, typemap_file_name), 'wb')
GenerateTypemapFile(typemap_file)
typemap_file.close()
check_failed_filenames = []
if options.check:
for filename in [mojom_file_name, traits_header_file_name,
traits_source_file_name, typemap_file_name]:
if not filecmp.cmp(os.path.join(output_dir, filename),
os.path.join(_SELF_LOCATION, filename)):
check_failed_filenames.append(filename)
if len(check_failed_filenames) > 0:
print 'Please run gpu/ipc/common/generate_vulkan_types.py'
print 'Failed check on generated files:'
for filename in check_failed_filenames:
print filename
return 1
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))