Generators: main.py is now dawn_json_generator.py
This merges all the files for main.py together again except generator_lib.py because there doesn't seeem to be a good way to separate the pure dawn.json generators and the dawn wire generator. Also updates the GN templates to make it easier to define new generators based on generator_lib, and move the Jinja2 template "stdlib" to generator_lib.py BUG=dawn:165 Change-Id: I1b5b2ef0a59cb142e214f3af9a58048a88ae949a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7880 Commit-Queue: Kai Ninomiya <kainino@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
88860d1ced
commit
031fbbbaa1
6
BUILD.gn
6
BUILD.gn
|
@ -33,7 +33,7 @@ config("libdawn_native_internal") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dawn_generator("libdawn_native_utils_gen") {
|
dawn_json_generator("libdawn_native_utils_gen") {
|
||||||
target = "dawn_native_utils"
|
target = "dawn_native_utils"
|
||||||
outputs = [
|
outputs = [
|
||||||
"dawn_native/ProcTable.cpp",
|
"dawn_native/ProcTable.cpp",
|
||||||
|
@ -394,7 +394,7 @@ dawn_component("libdawn_native") {
|
||||||
# libdawn_wire
|
# libdawn_wire
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
dawn_generator("libdawn_wire_gen") {
|
dawn_json_generator("libdawn_wire_gen") {
|
||||||
target = "dawn_wire"
|
target = "dawn_wire"
|
||||||
outputs = [
|
outputs = [
|
||||||
"dawn_wire/WireCmd_autogen.h",
|
"dawn_wire/WireCmd_autogen.h",
|
||||||
|
@ -524,7 +524,7 @@ static_library("dawn_utils") {
|
||||||
# Dawn test targets
|
# Dawn test targets
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
dawn_generator("mock_dawn_gen") {
|
dawn_json_generator("mock_dawn_gen") {
|
||||||
target = "mock_dawn"
|
target = "mock_dawn"
|
||||||
outputs = [
|
outputs = [
|
||||||
"mock/mock_dawn.h",
|
"mock/mock_dawn.h",
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
# Copyright 2017 The Dawn Authors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
class Name:
|
|
||||||
def __init__(self, name, native=False):
|
|
||||||
self.native = native
|
|
||||||
if native:
|
|
||||||
self.chunks = [name]
|
|
||||||
else:
|
|
||||||
self.chunks = name.split(' ')
|
|
||||||
|
|
||||||
def CamelChunk(self, chunk):
|
|
||||||
return chunk[0].upper() + chunk[1:]
|
|
||||||
|
|
||||||
def canonical_case(self):
|
|
||||||
return (' '.join(self.chunks)).lower()
|
|
||||||
|
|
||||||
def concatcase(self):
|
|
||||||
return ''.join(self.chunks)
|
|
||||||
|
|
||||||
def camelCase(self):
|
|
||||||
return self.chunks[0] + ''.join([self.CamelChunk(chunk) for chunk in self.chunks[1:]])
|
|
||||||
|
|
||||||
def CamelCase(self):
|
|
||||||
return ''.join([self.CamelChunk(chunk) for chunk in self.chunks])
|
|
||||||
|
|
||||||
def SNAKE_CASE(self):
|
|
||||||
return '_'.join([chunk.upper() for chunk in self.chunks])
|
|
||||||
|
|
||||||
def snake_case(self):
|
|
||||||
return '_'.join(self.chunks)
|
|
||||||
|
|
||||||
class Type:
|
|
||||||
def __init__(self, name, json_data, native=False):
|
|
||||||
self.json_data = json_data
|
|
||||||
self.dict_name = name
|
|
||||||
self.name = Name(name, native=native)
|
|
||||||
self.category = json_data['category']
|
|
||||||
|
|
||||||
EnumValue = namedtuple('EnumValue', ['name', 'value'])
|
|
||||||
class EnumType(Type):
|
|
||||||
def __init__(self, name, json_data):
|
|
||||||
Type.__init__(self, name, json_data)
|
|
||||||
self.values = [EnumValue(Name(m['name']), m['value']) for m in self.json_data['values']]
|
|
||||||
|
|
||||||
BitmaskValue = namedtuple('BitmaskValue', ['name', 'value'])
|
|
||||||
class BitmaskType(Type):
|
|
||||||
def __init__(self, name, json_data):
|
|
||||||
Type.__init__(self, name, json_data)
|
|
||||||
self.values = [BitmaskValue(Name(m['name']), m['value']) for m in self.json_data['values']]
|
|
||||||
self.full_mask = 0
|
|
||||||
for value in self.values:
|
|
||||||
self.full_mask = self.full_mask | value.value
|
|
||||||
|
|
||||||
class NativeType(Type):
|
|
||||||
def __init__(self, name, json_data):
|
|
||||||
Type.__init__(self, name, json_data, native=True)
|
|
||||||
|
|
||||||
class NativelyDefined(Type):
|
|
||||||
def __init__(self, name, json_data):
|
|
||||||
Type.__init__(self, name, json_data)
|
|
||||||
|
|
||||||
# Methods and structures are both "records", so record members correspond to
|
|
||||||
# method arguments or structure members.
|
|
||||||
class RecordMember:
|
|
||||||
def __init__(self, name, typ, annotation, optional, is_return_value):
|
|
||||||
self.name = name
|
|
||||||
self.type = typ
|
|
||||||
self.annotation = annotation
|
|
||||||
self.length = None
|
|
||||||
self.optional = optional
|
|
||||||
self.is_return_value = is_return_value
|
|
||||||
self.handle_type = None
|
|
||||||
|
|
||||||
def set_handle_type(self, handle_type):
|
|
||||||
assert self.type.dict_name == "ObjectHandle"
|
|
||||||
self.handle_type = handle_type
|
|
||||||
|
|
||||||
Method = namedtuple('Method', ['name', 'return_type', 'arguments'])
|
|
||||||
class ObjectType(Type):
|
|
||||||
def __init__(self, name, json_data):
|
|
||||||
Type.__init__(self, name, json_data)
|
|
||||||
self.methods = []
|
|
||||||
self.native_methods = []
|
|
||||||
self.built_type = None
|
|
||||||
|
|
||||||
class Record:
|
|
||||||
def __init__(self, name):
|
|
||||||
self.name = Name(name)
|
|
||||||
self.members = []
|
|
||||||
self.has_dawn_object = False
|
|
||||||
|
|
||||||
def update_metadata(self):
|
|
||||||
def has_dawn_object(member):
|
|
||||||
if isinstance(member.type, ObjectType):
|
|
||||||
return True
|
|
||||||
elif isinstance(member.type, StructureType):
|
|
||||||
return member.type.has_dawn_object
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.has_dawn_object = any(has_dawn_object(member) for member in self.members)
|
|
||||||
|
|
||||||
class StructureType(Record, Type):
|
|
||||||
def __init__(self, name, json_data):
|
|
||||||
Record.__init__(self, name)
|
|
||||||
Type.__init__(self, name, json_data)
|
|
||||||
self.extensible = json_data.get("extensible", False)
|
|
||||||
|
|
||||||
class Command(Record):
|
|
||||||
def __init__(self, name, members=None):
|
|
||||||
Record.__init__(self, name)
|
|
||||||
self.members = members or []
|
|
||||||
self.derived_object = None
|
|
||||||
self.derived_method = None
|
|
||||||
|
|
||||||
def linked_record_members(json_data, types):
|
|
||||||
members = []
|
|
||||||
members_by_name = {}
|
|
||||||
for m in json_data:
|
|
||||||
member = RecordMember(Name(m['name']), types[m['type']],
|
|
||||||
m.get('annotation', 'value'), m.get('optional', False),
|
|
||||||
m.get('is_return_value', False))
|
|
||||||
handle_type = m.get('handle_type')
|
|
||||||
if handle_type:
|
|
||||||
member.set_handle_type(types[handle_type])
|
|
||||||
members.append(member)
|
|
||||||
members_by_name[member.name.canonical_case()] = member
|
|
||||||
|
|
||||||
for (member, m) in zip(members, json_data):
|
|
||||||
if member.annotation != 'value':
|
|
||||||
if not 'length' in m:
|
|
||||||
if member.type.category != 'object':
|
|
||||||
member.length = "constant"
|
|
||||||
member.constant_length = 1
|
|
||||||
else:
|
|
||||||
assert(False)
|
|
||||||
elif m['length'] == 'strlen':
|
|
||||||
member.length = 'strlen'
|
|
||||||
else:
|
|
||||||
member.length = members_by_name[m['length']]
|
|
||||||
|
|
||||||
return members
|
|
|
@ -14,19 +14,20 @@
|
||||||
|
|
||||||
import("../scripts/dawn_overrides_with_defaults.gni")
|
import("../scripts/dawn_overrides_with_defaults.gni")
|
||||||
|
|
||||||
###############################################################################
|
# Template to help invoking Dawn code generators based on generator_lib
|
||||||
# Template to wrap the Dawn code generator
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
# Template to help with invocation of the Dawn code generator, looks like this:
|
|
||||||
#
|
#
|
||||||
# dawn_generator("my_target_gen") {
|
# dawn_generator("my_target_gen") {
|
||||||
# # Which generator target to output
|
# # The script and generator specific arguments
|
||||||
# target = "my_target"
|
# script = [ "my_awesome_generator.py" ]
|
||||||
|
# args = [
|
||||||
|
# "--be-awesome",
|
||||||
|
# "yes"
|
||||||
|
# ]
|
||||||
|
#
|
||||||
# # The list of expected outputs, generation fails if there's a mismatch
|
# # The list of expected outputs, generation fails if there's a mismatch
|
||||||
# outputs = [
|
# outputs = [
|
||||||
# "MyTarget.cpp",
|
# "MyAwesomeTarget.cpp",
|
||||||
# "MyTarget.h",
|
# "MyAwesomeTarget.h",
|
||||||
# ]
|
# ]
|
||||||
#
|
#
|
||||||
# # Optional, use a custom generated file directory.
|
# # Optional, use a custom generated file directory.
|
||||||
|
@ -41,18 +42,10 @@ import("../scripts/dawn_overrides_with_defaults.gni")
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
template("dawn_generator") {
|
template("dawn_generator") {
|
||||||
# The base arguments for the generator: from this dawn.json, generate this
|
generator_args = []
|
||||||
# target using templates in this directory.
|
if (defined(invoker.args)) {
|
||||||
generator_args = [
|
generator_args += invoker.args
|
||||||
"--dawn-json",
|
}
|
||||||
rebase_path("${dawn_root}/dawn.json", root_build_dir),
|
|
||||||
"--wire-json",
|
|
||||||
rebase_path("${dawn_root}/dawn_wire.json", root_build_dir),
|
|
||||||
"--template-dir",
|
|
||||||
rebase_path("${dawn_root}/generator/templates", root_build_dir),
|
|
||||||
"--targets",
|
|
||||||
invoker.target,
|
|
||||||
]
|
|
||||||
|
|
||||||
# Use the Jinja2 version pulled from the DEPS file. We do it so we don't
|
# Use the Jinja2 version pulled from the DEPS file. We do it so we don't
|
||||||
# have version problems, and users don't have to install Jinja2.
|
# have version problems, and users don't have to install Jinja2.
|
||||||
|
@ -103,7 +96,7 @@ template("dawn_generator") {
|
||||||
# The code generator invocation that will write the JSON tarball, check the
|
# The code generator invocation that will write the JSON tarball, check the
|
||||||
# outputs are what's expected and write a depfile for Ninja.
|
# outputs are what's expected and write a depfile for Ninja.
|
||||||
action("${target_name}_json_tarball") {
|
action("${target_name}_json_tarball") {
|
||||||
script = "${dawn_root}/generator/main.py"
|
script = invoker.script
|
||||||
outputs = [
|
outputs = [
|
||||||
json_tarball,
|
json_tarball,
|
||||||
]
|
]
|
||||||
|
@ -112,7 +105,7 @@ template("dawn_generator") {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Extract the JSON tarball into the gen_dir
|
# Extract the JSON tarball into the gen_dir
|
||||||
action("${target_name}") {
|
action(target_name) {
|
||||||
script = "${dawn_root}/generator/extract_json.py"
|
script = "${dawn_root}/generator/extract_json.py"
|
||||||
args = [
|
args = [
|
||||||
rebase_path(json_tarball, root_build_dir),
|
rebase_path(json_tarball, root_build_dir),
|
||||||
|
@ -134,3 +127,33 @@ template("dawn_generator") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Helper generator for calling the generator from dawn.json
|
||||||
|
#
|
||||||
|
# dawn_json_generator("my_target_gen") {
|
||||||
|
# # Which generator target to output
|
||||||
|
# target = "my_target"
|
||||||
|
#
|
||||||
|
# # Also supports `outputs` and `custom_gen_dir` like dawn_generator.
|
||||||
|
# }
|
||||||
|
template("dawn_json_generator") {
|
||||||
|
dawn_generator(target_name) {
|
||||||
|
|
||||||
|
script = "${dawn_root}/generator/dawn_json_generator.py"
|
||||||
|
|
||||||
|
# The base arguments for the generator: from this dawn.json, generate this
|
||||||
|
# target using templates in this directory.
|
||||||
|
args = [
|
||||||
|
"--dawn-json",
|
||||||
|
rebase_path("${dawn_root}/dawn.json", root_build_dir),
|
||||||
|
"--wire-json",
|
||||||
|
rebase_path("${dawn_root}/dawn_wire.json", root_build_dir),
|
||||||
|
"--template-dir",
|
||||||
|
rebase_path("${dawn_root}/generator/templates", root_build_dir),
|
||||||
|
"--targets",
|
||||||
|
invoker.target,
|
||||||
|
]
|
||||||
|
|
||||||
|
forward_variables_from(invoker, "*", ["target"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,10 +16,155 @@
|
||||||
import json, os, sys
|
import json, os, sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
import common
|
|
||||||
from common import Name
|
|
||||||
from generator_lib import Generator, run_generator, FileRender
|
from generator_lib import Generator, run_generator, FileRender
|
||||||
import wire_cmd
|
|
||||||
|
############################################################
|
||||||
|
# OBJECT MODEL
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
class Name:
|
||||||
|
def __init__(self, name, native=False):
|
||||||
|
self.native = native
|
||||||
|
if native:
|
||||||
|
self.chunks = [name]
|
||||||
|
else:
|
||||||
|
self.chunks = name.split(' ')
|
||||||
|
|
||||||
|
def CamelChunk(self, chunk):
|
||||||
|
return chunk[0].upper() + chunk[1:]
|
||||||
|
|
||||||
|
def canonical_case(self):
|
||||||
|
return (' '.join(self.chunks)).lower()
|
||||||
|
|
||||||
|
def concatcase(self):
|
||||||
|
return ''.join(self.chunks)
|
||||||
|
|
||||||
|
def camelCase(self):
|
||||||
|
return self.chunks[0] + ''.join([self.CamelChunk(chunk) for chunk in self.chunks[1:]])
|
||||||
|
|
||||||
|
def CamelCase(self):
|
||||||
|
return ''.join([self.CamelChunk(chunk) for chunk in self.chunks])
|
||||||
|
|
||||||
|
def SNAKE_CASE(self):
|
||||||
|
return '_'.join([chunk.upper() for chunk in self.chunks])
|
||||||
|
|
||||||
|
def snake_case(self):
|
||||||
|
return '_'.join(self.chunks)
|
||||||
|
|
||||||
|
def concat_names(*names):
|
||||||
|
return ' '.join([name.canonical_case() for name in names])
|
||||||
|
|
||||||
|
class Type:
|
||||||
|
def __init__(self, name, json_data, native=False):
|
||||||
|
self.json_data = json_data
|
||||||
|
self.dict_name = name
|
||||||
|
self.name = Name(name, native=native)
|
||||||
|
self.category = json_data['category']
|
||||||
|
|
||||||
|
EnumValue = namedtuple('EnumValue', ['name', 'value'])
|
||||||
|
class EnumType(Type):
|
||||||
|
def __init__(self, name, json_data):
|
||||||
|
Type.__init__(self, name, json_data)
|
||||||
|
self.values = [EnumValue(Name(m['name']), m['value']) for m in self.json_data['values']]
|
||||||
|
|
||||||
|
BitmaskValue = namedtuple('BitmaskValue', ['name', 'value'])
|
||||||
|
class BitmaskType(Type):
|
||||||
|
def __init__(self, name, json_data):
|
||||||
|
Type.__init__(self, name, json_data)
|
||||||
|
self.values = [BitmaskValue(Name(m['name']), m['value']) for m in self.json_data['values']]
|
||||||
|
self.full_mask = 0
|
||||||
|
for value in self.values:
|
||||||
|
self.full_mask = self.full_mask | value.value
|
||||||
|
|
||||||
|
class NativeType(Type):
|
||||||
|
def __init__(self, name, json_data):
|
||||||
|
Type.__init__(self, name, json_data, native=True)
|
||||||
|
|
||||||
|
class NativelyDefined(Type):
|
||||||
|
def __init__(self, name, json_data):
|
||||||
|
Type.__init__(self, name, json_data)
|
||||||
|
|
||||||
|
# Methods and structures are both "records", so record members correspond to
|
||||||
|
# method arguments or structure members.
|
||||||
|
class RecordMember:
|
||||||
|
def __init__(self, name, typ, annotation, optional, is_return_value):
|
||||||
|
self.name = name
|
||||||
|
self.type = typ
|
||||||
|
self.annotation = annotation
|
||||||
|
self.length = None
|
||||||
|
self.optional = optional
|
||||||
|
self.is_return_value = is_return_value
|
||||||
|
self.handle_type = None
|
||||||
|
|
||||||
|
def set_handle_type(self, handle_type):
|
||||||
|
assert self.type.dict_name == "ObjectHandle"
|
||||||
|
self.handle_type = handle_type
|
||||||
|
|
||||||
|
Method = namedtuple('Method', ['name', 'return_type', 'arguments'])
|
||||||
|
class ObjectType(Type):
|
||||||
|
def __init__(self, name, json_data):
|
||||||
|
Type.__init__(self, name, json_data)
|
||||||
|
self.methods = []
|
||||||
|
self.native_methods = []
|
||||||
|
self.built_type = None
|
||||||
|
|
||||||
|
class Record:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = Name(name)
|
||||||
|
self.members = []
|
||||||
|
self.has_dawn_object = False
|
||||||
|
|
||||||
|
def update_metadata(self):
|
||||||
|
def has_dawn_object(member):
|
||||||
|
if isinstance(member.type, ObjectType):
|
||||||
|
return True
|
||||||
|
elif isinstance(member.type, StructureType):
|
||||||
|
return member.type.has_dawn_object
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.has_dawn_object = any(has_dawn_object(member) for member in self.members)
|
||||||
|
|
||||||
|
class StructureType(Record, Type):
|
||||||
|
def __init__(self, name, json_data):
|
||||||
|
Record.__init__(self, name)
|
||||||
|
Type.__init__(self, name, json_data)
|
||||||
|
self.extensible = json_data.get("extensible", False)
|
||||||
|
|
||||||
|
class Command(Record):
|
||||||
|
def __init__(self, name, members=None):
|
||||||
|
Record.__init__(self, name)
|
||||||
|
self.members = members or []
|
||||||
|
self.derived_object = None
|
||||||
|
self.derived_method = None
|
||||||
|
|
||||||
|
def linked_record_members(json_data, types):
|
||||||
|
members = []
|
||||||
|
members_by_name = {}
|
||||||
|
for m in json_data:
|
||||||
|
member = RecordMember(Name(m['name']), types[m['type']],
|
||||||
|
m.get('annotation', 'value'), m.get('optional', False),
|
||||||
|
m.get('is_return_value', False))
|
||||||
|
handle_type = m.get('handle_type')
|
||||||
|
if handle_type:
|
||||||
|
member.set_handle_type(types[handle_type])
|
||||||
|
members.append(member)
|
||||||
|
members_by_name[member.name.canonical_case()] = member
|
||||||
|
|
||||||
|
for (member, m) in zip(members, json_data):
|
||||||
|
if member.annotation != 'value':
|
||||||
|
if not 'length' in m:
|
||||||
|
if member.type.category != 'object':
|
||||||
|
member.length = "constant"
|
||||||
|
member.constant_length = 1
|
||||||
|
else:
|
||||||
|
assert(False)
|
||||||
|
elif m['length'] == 'strlen':
|
||||||
|
member.length = 'strlen'
|
||||||
|
else:
|
||||||
|
member.length = members_by_name[m['length']]
|
||||||
|
|
||||||
|
return members
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# PARSE
|
# PARSE
|
||||||
|
@ -31,15 +176,15 @@ def is_native_method(method):
|
||||||
|
|
||||||
def link_object(obj, types):
|
def link_object(obj, types):
|
||||||
def make_method(json_data):
|
def make_method(json_data):
|
||||||
arguments = common.linked_record_members(json_data.get('args', []), types)
|
arguments = linked_record_members(json_data.get('args', []), types)
|
||||||
return common.Method(Name(json_data['name']), types[json_data.get('returns', 'void')], arguments)
|
return Method(Name(json_data['name']), types[json_data.get('returns', 'void')], arguments)
|
||||||
|
|
||||||
methods = [make_method(m) for m in obj.json_data.get('methods', [])]
|
methods = [make_method(m) for m in obj.json_data.get('methods', [])]
|
||||||
obj.methods = [method for method in methods if not is_native_method(method)]
|
obj.methods = [method for method in methods if not is_native_method(method)]
|
||||||
obj.native_methods = [method for method in methods if is_native_method(method)]
|
obj.native_methods = [method for method in methods if is_native_method(method)]
|
||||||
|
|
||||||
def link_structure(struct, types):
|
def link_structure(struct, types):
|
||||||
struct.members = common.linked_record_members(struct.json_data['members'], types)
|
struct.members = linked_record_members(struct.json_data['members'], types)
|
||||||
|
|
||||||
# Sort structures so that if struct A has struct B as a member, then B is listed before A
|
# Sort structures so that if struct A has struct B as a member, then B is listed before A
|
||||||
# This is a form of topological sort where we try to keep the order reasonably similar to the
|
# This is a form of topological sort where we try to keep the order reasonably similar to the
|
||||||
|
@ -79,12 +224,12 @@ def topo_sort_structure(structs):
|
||||||
|
|
||||||
def parse_json(json):
|
def parse_json(json):
|
||||||
category_to_parser = {
|
category_to_parser = {
|
||||||
'bitmask': common.BitmaskType,
|
'bitmask': BitmaskType,
|
||||||
'enum': common.EnumType,
|
'enum': EnumType,
|
||||||
'native': common.NativeType,
|
'native': NativeType,
|
||||||
'natively defined': common.NativelyDefined,
|
'natively defined': NativelyDefined,
|
||||||
'object': common.ObjectType,
|
'object': ObjectType,
|
||||||
'structure': common.StructureType,
|
'structure': StructureType,
|
||||||
}
|
}
|
||||||
|
|
||||||
types = {}
|
types = {}
|
||||||
|
@ -120,6 +265,67 @@ def parse_json(json):
|
||||||
'by_category': by_category
|
'by_category': by_category
|
||||||
}
|
}
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# WIRE STUFF
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
# Create wire commands from api methods
|
||||||
|
def compute_wire_params(api_params, wire_json):
|
||||||
|
wire_params = api_params.copy()
|
||||||
|
types = wire_params['types']
|
||||||
|
|
||||||
|
commands = []
|
||||||
|
return_commands = []
|
||||||
|
|
||||||
|
# Generate commands from object methods
|
||||||
|
for api_object in wire_params['by_category']['object']:
|
||||||
|
for method in api_object.methods:
|
||||||
|
command_name = concat_names(api_object.name, method.name)
|
||||||
|
command_suffix = Name(command_name).CamelCase()
|
||||||
|
|
||||||
|
# Only object return values or void are supported. Other methods must be handwritten.
|
||||||
|
if method.return_type.category != 'object' and method.return_type.name.canonical_case() != 'void':
|
||||||
|
assert(command_suffix in wire_json['special items']['client_handwritten_commands'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
if command_suffix in wire_json['special items']['client_side_commands']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Create object method commands by prepending "self"
|
||||||
|
members = [RecordMember(Name('self'), types[api_object.dict_name], 'value', False, False)]
|
||||||
|
members += method.arguments
|
||||||
|
|
||||||
|
# Client->Server commands that return an object return the result object handle
|
||||||
|
if method.return_type.category == 'object':
|
||||||
|
result = RecordMember(Name('result'), types['ObjectHandle'], 'value', False, True)
|
||||||
|
result.set_handle_type(method.return_type)
|
||||||
|
members.append(result)
|
||||||
|
|
||||||
|
command = Command(command_name, members)
|
||||||
|
command.derived_object = api_object
|
||||||
|
command.derived_method = method
|
||||||
|
commands.append(command)
|
||||||
|
|
||||||
|
for (name, json_data) in wire_json['commands'].items():
|
||||||
|
commands.append(Command(name, linked_record_members(json_data, types)))
|
||||||
|
|
||||||
|
for (name, json_data) in wire_json['return commands'].items():
|
||||||
|
return_commands.append(Command(name, linked_record_members(json_data, types)))
|
||||||
|
|
||||||
|
wire_params['cmd_records'] = {
|
||||||
|
'command': commands,
|
||||||
|
'return command': return_commands
|
||||||
|
}
|
||||||
|
|
||||||
|
for commands in wire_params['cmd_records'].values():
|
||||||
|
for command in commands:
|
||||||
|
command.update_metadata()
|
||||||
|
commands.sort(key=lambda c: c.name.canonical_case())
|
||||||
|
|
||||||
|
wire_params.update(wire_json.get('special items', {}))
|
||||||
|
|
||||||
|
return wire_params
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
# Generator
|
# Generator
|
||||||
#############################################################
|
#############################################################
|
||||||
|
@ -215,20 +421,10 @@ def cpp_native_methods(types, typ):
|
||||||
|
|
||||||
def c_native_methods(types, typ):
|
def c_native_methods(types, typ):
|
||||||
return cpp_native_methods(types, typ) + [
|
return cpp_native_methods(types, typ) + [
|
||||||
common.Method(Name('reference'), types['void'], []),
|
Method(Name('reference'), types['void'], []),
|
||||||
common.Method(Name('release'), types['void'], []),
|
Method(Name('release'), types['void'], []),
|
||||||
]
|
]
|
||||||
|
|
||||||
def js_native_methods(types, typ):
|
|
||||||
return cpp_native_methods(types, typ)
|
|
||||||
|
|
||||||
def debug(text):
|
|
||||||
print(text)
|
|
||||||
|
|
||||||
def do_assert(expr):
|
|
||||||
assert expr
|
|
||||||
return ''
|
|
||||||
|
|
||||||
class MultiGeneratorFromDawnJSON(Generator):
|
class MultiGeneratorFromDawnJSON(Generator):
|
||||||
def get_description(self):
|
def get_description(self):
|
||||||
return 'Generates code for various target from Dawn.json.'
|
return 'Generates code for various target from Dawn.json.'
|
||||||
|
@ -238,7 +434,7 @@ class MultiGeneratorFromDawnJSON(Generator):
|
||||||
|
|
||||||
parser.add_argument('--dawn-json', required=True, type=str, help ='The DAWN JSON definition to use.')
|
parser.add_argument('--dawn-json', required=True, type=str, help ='The DAWN JSON definition to use.')
|
||||||
parser.add_argument('--wire-json', default=None, type=str, help='The DAWN WIRE JSON definition to use.')
|
parser.add_argument('--wire-json', default=None, type=str, help='The DAWN WIRE JSON definition to use.')
|
||||||
parser.add_argument('-T', '--targets', required=True, type=str, help='Comma-separated subset of targets to output. Available targets: ' + ', '.join(allowed_targets))
|
parser.add_argument('--targets', required=True, type=str, help='Comma-separated subset of targets to output. Available targets: ' + ', '.join(allowed_targets))
|
||||||
|
|
||||||
def get_file_renders(self, args):
|
def get_file_renders(self, args):
|
||||||
with open(args.dawn_json) as f:
|
with open(args.dawn_json) as f:
|
||||||
|
@ -253,12 +449,6 @@ class MultiGeneratorFromDawnJSON(Generator):
|
||||||
wire_json = json.loads(f.read())
|
wire_json = json.loads(f.read())
|
||||||
|
|
||||||
base_params = {
|
base_params = {
|
||||||
'enumerate': enumerate,
|
|
||||||
'format': format,
|
|
||||||
'len': len,
|
|
||||||
'debug': debug,
|
|
||||||
'assert': do_assert,
|
|
||||||
|
|
||||||
'Name': lambda name: Name(name),
|
'Name': lambda name: Name(name),
|
||||||
|
|
||||||
'as_annotated_cType': lambda arg: annotated(as_cType(arg.type.name), arg),
|
'as_annotated_cType': lambda arg: annotated(as_cType(arg.type.name), arg),
|
||||||
|
@ -311,7 +501,7 @@ class MultiGeneratorFromDawnJSON(Generator):
|
||||||
renders.append(FileRender('dawn_native/ProcTable.cpp', 'dawn_native/ProcTable.cpp', frontend_params))
|
renders.append(FileRender('dawn_native/ProcTable.cpp', 'dawn_native/ProcTable.cpp', frontend_params))
|
||||||
|
|
||||||
if 'dawn_wire' in targets:
|
if 'dawn_wire' in targets:
|
||||||
additional_params = wire_cmd.compute_wire_params(api_params, wire_json)
|
additional_params = compute_wire_params(api_params, wire_json)
|
||||||
|
|
||||||
wire_params = [
|
wire_params = [
|
||||||
base_params,
|
base_params,
|
|
@ -96,9 +96,25 @@ def _do_renders(renders, template_dir):
|
||||||
loader = PreprocessingLoader(template_dir)
|
loader = PreprocessingLoader(template_dir)
|
||||||
env = jinja2.Environment(loader=loader, lstrip_blocks=True, trim_blocks=True, line_comment_prefix='//*')
|
env = jinja2.Environment(loader=loader, lstrip_blocks=True, trim_blocks=True, line_comment_prefix='//*')
|
||||||
|
|
||||||
|
def do_assert(expr):
|
||||||
|
assert expr
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def debug(text):
|
||||||
|
print(text)
|
||||||
|
|
||||||
|
base_params = {
|
||||||
|
'enumerate': enumerate,
|
||||||
|
'format': format,
|
||||||
|
'len': len,
|
||||||
|
'debug': debug,
|
||||||
|
'assert': do_assert,
|
||||||
|
}
|
||||||
|
|
||||||
outputs = []
|
outputs = []
|
||||||
for render in renders:
|
for render in renders:
|
||||||
params = {}
|
params = {}
|
||||||
|
params.update(base_params)
|
||||||
for param_dict in render.params_dicts:
|
for param_dict in render.params_dicts:
|
||||||
params.update(param_dict)
|
params.update(param_dict)
|
||||||
content = env.get_template(render.template).render(**params)
|
content = env.get_template(render.template).render(**params)
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
# Copyright 2019 The Dawn Authors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
from common import Name
|
|
||||||
import common
|
|
||||||
|
|
||||||
def concat_names(*names):
|
|
||||||
return ' '.join([name.canonical_case() for name in names])
|
|
||||||
|
|
||||||
# Create wire commands from api methods
|
|
||||||
def compute_wire_params(api_params, wire_json):
|
|
||||||
wire_params = api_params.copy()
|
|
||||||
types = wire_params['types']
|
|
||||||
|
|
||||||
commands = []
|
|
||||||
return_commands = []
|
|
||||||
|
|
||||||
# Generate commands from object methods
|
|
||||||
for api_object in wire_params['by_category']['object']:
|
|
||||||
for method in api_object.methods:
|
|
||||||
command_name = concat_names(api_object.name, method.name)
|
|
||||||
command_suffix = Name(command_name).CamelCase()
|
|
||||||
|
|
||||||
# Only object return values or void are supported. Other methods must be handwritten.
|
|
||||||
if method.return_type.category != 'object' and method.return_type.name.canonical_case() != 'void':
|
|
||||||
assert(command_suffix in wire_json['special items']['client_handwritten_commands'])
|
|
||||||
continue
|
|
||||||
|
|
||||||
if command_suffix in wire_json['special items']['client_side_commands']:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Create object method commands by prepending "self"
|
|
||||||
members = [common.RecordMember(Name('self'), types[api_object.dict_name], 'value', False, False)]
|
|
||||||
members += method.arguments
|
|
||||||
|
|
||||||
# Client->Server commands that return an object return the result object handle
|
|
||||||
if method.return_type.category == 'object':
|
|
||||||
result = common.RecordMember(Name('result'), types['ObjectHandle'], 'value', False, True)
|
|
||||||
result.set_handle_type(method.return_type)
|
|
||||||
members.append(result)
|
|
||||||
|
|
||||||
command = common.Command(command_name, members)
|
|
||||||
command.derived_object = api_object
|
|
||||||
command.derived_method = method
|
|
||||||
commands.append(command)
|
|
||||||
|
|
||||||
for (name, json_data) in wire_json['commands'].items():
|
|
||||||
commands.append(common.Command(name, common.linked_record_members(json_data, types)))
|
|
||||||
|
|
||||||
for (name, json_data) in wire_json['return commands'].items():
|
|
||||||
return_commands.append(common.Command(name, common.linked_record_members(json_data, types)))
|
|
||||||
|
|
||||||
wire_params['cmd_records'] = {
|
|
||||||
'command': commands,
|
|
||||||
'return command': return_commands
|
|
||||||
}
|
|
||||||
|
|
||||||
for commands in wire_params['cmd_records'].values():
|
|
||||||
for command in commands:
|
|
||||||
command.update_metadata()
|
|
||||||
commands.sort(key=lambda c: c.name.canonical_case())
|
|
||||||
|
|
||||||
wire_params.update(wire_json.get('special items', {}))
|
|
||||||
|
|
||||||
return wire_params
|
|
|
@ -21,7 +21,7 @@ import("${dawn_root}/generator/dawn_generator.gni")
|
||||||
# Dawn headers
|
# Dawn headers
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
dawn_generator("dawn_headers_gen") {
|
dawn_json_generator("dawn_headers_gen") {
|
||||||
target = "dawn_headers"
|
target = "dawn_headers"
|
||||||
|
|
||||||
# Generate as if we were in the main BUILD.gn because that was historically
|
# Generate as if we were in the main BUILD.gn because that was historically
|
||||||
|
@ -51,7 +51,7 @@ source_set("dawn_headers") {
|
||||||
# libdawn
|
# libdawn
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
dawn_generator("libdawn_gen") {
|
dawn_json_generator("libdawn_gen") {
|
||||||
target = "libdawn"
|
target = "libdawn"
|
||||||
outputs = [
|
outputs = [
|
||||||
"dawn/dawncpp.cpp",
|
"dawn/dawncpp.cpp",
|
||||||
|
|
Loading…
Reference in New Issue