am 13006a26: Merge "emugen: generate wrapper library files"
* commit '13006a265736ad992f4918707a895d3549e2f1f0': emugen: generate wrapper library files
This commit is contained in:
@@ -31,7 +31,7 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
LOCAL_LDLIBS := -ldl
|
||||
|
||||
|
||||
GEN := $(intermediates)/gl_dec.cpp $(intermediates)/gl_dec.h
|
||||
GEN := $(intermediates)/gl_server_context.cpp $(intermediates)/gl_dec.cpp $(intermediates)/gl_dec.h
|
||||
|
||||
$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
|
||||
$(GEN) : PRIVATE_CUSTOM_TOOL := $(EMUGEN) -D $(intermediates) -i $(emulatorOpengl)/system/GLESv1_enc gl
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
EntryPoint * ApiGen::findEntryByName(const std::string & name)
|
||||
{
|
||||
EntryPoint * entry = NULL;
|
||||
@@ -119,12 +118,15 @@ int ApiGen::genContext(const std::string & filename, SideType side)
|
||||
// virtual destructor
|
||||
fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side));
|
||||
// accessor
|
||||
if (side == CLIENT_SIDE) {
|
||||
if (side == CLIENT_SIDE || side == WRAPPER_SIDE) {
|
||||
fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
|
||||
m_basename.c_str(), sideString(side));
|
||||
fprintf(fp, "\tvoid setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
|
||||
}
|
||||
|
||||
// init function
|
||||
fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
|
||||
|
||||
fprintf(fp, "};\n");
|
||||
|
||||
fprintf(fp, "\n#endif\n");
|
||||
@@ -132,8 +134,15 @@ int ApiGen::genContext(const std::string & filename, SideType side)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApiGen::genClientEntryPoints(const std::string & filename)
|
||||
int ApiGen::genEntryPoints(const std::string & filename, SideType side)
|
||||
{
|
||||
|
||||
if (side != CLIENT_SIDE && side != WRAPPER_SIDE) {
|
||||
fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n");
|
||||
return -999;
|
||||
}
|
||||
|
||||
|
||||
FILE *fp = fopen(filename.c_str(), "wt");
|
||||
if (fp == NULL) {
|
||||
perror(filename.c_str());
|
||||
@@ -143,7 +152,7 @@ int ApiGen::genClientEntryPoints(const std::string & filename)
|
||||
printHeader(fp);
|
||||
fprintf(fp, "#include <stdio.h>\n");
|
||||
fprintf(fp, "#include <stdlib.h>\n");
|
||||
fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(CLIENT_SIDE));
|
||||
fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side));
|
||||
fprintf(fp, "\n");
|
||||
|
||||
fprintf(fp, "extern \"C\" {\n");
|
||||
@@ -154,11 +163,11 @@ int ApiGen::genClientEntryPoints(const std::string & filename)
|
||||
fprintf(fp, "};\n\n");
|
||||
|
||||
fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
|
||||
m_basename.c_str(), sideString(CLIENT_SIDE));
|
||||
m_basename.c_str(), sideString(side));
|
||||
|
||||
fprintf(fp,
|
||||
"void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n\n",
|
||||
m_basename.c_str(), sideString(CLIENT_SIDE));
|
||||
m_basename.c_str(), sideString(side));
|
||||
|
||||
|
||||
for (size_t i = 0; i < size(); i++) {
|
||||
@@ -166,18 +175,21 @@ int ApiGen::genClientEntryPoints(const std::string & filename)
|
||||
e->print(fp);
|
||||
fprintf(fp, "{\n");
|
||||
fprintf(fp, "\t %s_%s_context_t * ctx = getCurrentContext(); \n",
|
||||
m_basename.c_str(), sideString(CLIENT_SIDE));
|
||||
m_basename.c_str(), sideString(side));
|
||||
|
||||
bool shouldReturn = !e->retval().isVoid();
|
||||
|
||||
fprintf(fp, "\t %sctx->%s(ctx",
|
||||
bool shouldCallWithContext = (side == CLIENT_SIDE);
|
||||
fprintf(fp, "\t %sctx->%s(%s",
|
||||
shouldReturn ? "return " : "",
|
||||
e->name().c_str());
|
||||
e->name().c_str(),
|
||||
shouldCallWithContext ? "ctx" : "");
|
||||
size_t nvars = e->vars().size();
|
||||
|
||||
for (size_t j = 0; j < nvars; j++) {
|
||||
if (!e->vars()[j].isVoid()) {
|
||||
fprintf(fp, ", %s", e->vars()[j].name().c_str());
|
||||
fprintf(fp, "%s %s",
|
||||
j != 0 || shouldCallWithContext ? "," : "",
|
||||
e->vars()[j].name().c_str());
|
||||
}
|
||||
}
|
||||
fprintf(fp, ");\n");
|
||||
@@ -467,7 +479,6 @@ int ApiGen::genDecoderHeader(const std::string &filename)
|
||||
fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
|
||||
classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE));
|
||||
fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n");
|
||||
fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
|
||||
fprintf(fp, "\n};\n\n");
|
||||
fprintf(fp, "#endif");
|
||||
|
||||
@@ -475,6 +486,39 @@ int ApiGen::genDecoderHeader(const std::string &filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApiGen::genContextImpl(const std::string &filename, SideType side)
|
||||
{
|
||||
FILE *fp = fopen(filename.c_str(), "wt");
|
||||
if (fp == NULL) {
|
||||
perror(filename.c_str());
|
||||
return -1;
|
||||
}
|
||||
printHeader(fp);
|
||||
|
||||
std::string classname = m_basename + "_" + sideString(side) + "_context_t";
|
||||
size_t n = size();
|
||||
fprintf(fp, "\n\n#include <string.h>\n");
|
||||
fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side));
|
||||
fprintf(fp, "#include <stdio.h>\n\n");
|
||||
|
||||
// init function;
|
||||
fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
|
||||
fprintf(fp, "\tvoid *ptr;\n\n");
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
EntryPoint *e = &at(i);
|
||||
fprintf(fp, "\tptr = getProc(\"%s\", userData); set_%s((%s_%s_proc_t)ptr);\n",
|
||||
e->name().c_str(),
|
||||
e->name().c_str(),
|
||||
e->name().c_str(),
|
||||
sideString(SERVER_SIDE));
|
||||
|
||||
}
|
||||
fprintf(fp, "\treturn 0;\n");
|
||||
fprintf(fp, "}\n\n");
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApiGen::genDecoderImpl(const std::string &filename)
|
||||
{
|
||||
FILE *fp = fopen(filename.c_str(), "wt");
|
||||
@@ -492,22 +536,7 @@ int ApiGen::genDecoderImpl(const std::string &filename)
|
||||
fprintf(fp, "\n\n#include <string.h>\n");
|
||||
fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
|
||||
fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str());
|
||||
fprintf(fp, "#include <stdio.h>\n");
|
||||
|
||||
// init function;
|
||||
fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
|
||||
fprintf(fp, "\tvoid *ptr;\n\n");
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
EntryPoint *e = &at(i);
|
||||
fprintf(fp, "\tptr = getProc(\"%s\", userData); set_%s((%s_%s_proc_t)ptr);\n",
|
||||
e->name().c_str(),
|
||||
e->name().c_str(),
|
||||
e->name().c_str(),
|
||||
sideString(SERVER_SIDE));
|
||||
|
||||
}
|
||||
fprintf(fp, "\treturn 0;\n");
|
||||
fprintf(fp, "}\n\n");
|
||||
fprintf(fp, "#include <stdio.h>\n\n");
|
||||
|
||||
// decoder switch;
|
||||
fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str());
|
||||
|
||||
@@ -25,7 +25,7 @@ class ApiGen : public std::vector<EntryPoint> {
|
||||
|
||||
public:
|
||||
typedef std::vector<std::string> StringVec;
|
||||
typedef enum { CLIENT_SIDE, SERVER_SIDE } SideType;
|
||||
typedef enum { CLIENT_SIDE, SERVER_SIDE, WRAPPER_SIDE } SideType;
|
||||
|
||||
ApiGen(const std::string & basename) :
|
||||
m_basename(basename),
|
||||
@@ -42,7 +42,21 @@ public:
|
||||
int baseOpcode() { return m_baseOpcode; }
|
||||
void setBaseOpcode(int base) { m_baseOpcode = base; }
|
||||
|
||||
const char *sideString(SideType side) { return (side == CLIENT_SIDE) ? "client" : "server"; }
|
||||
const char *sideString(SideType side) {
|
||||
const char *retval;
|
||||
switch(side) {
|
||||
case CLIENT_SIDE:
|
||||
retval = "client";
|
||||
break;
|
||||
case SERVER_SIDE:
|
||||
retval = "server";
|
||||
break;
|
||||
case WRAPPER_SIDE:
|
||||
retval = "wrapper";
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
StringVec & clientContextHeaders() { return m_clientContextHeaders; }
|
||||
StringVec & encoderHeaders() { return m_encoderHeaders; }
|
||||
@@ -55,7 +69,9 @@ public:
|
||||
int genProcTypes(const std::string &filename, SideType side);
|
||||
|
||||
int genContext(const std::string &filename, SideType side);
|
||||
int genClientEntryPoints(const std::string &filename);
|
||||
int genContextImpl(const std::string &filename, SideType side);
|
||||
|
||||
int genEntryPoints(const std::string &filename, SideType side);
|
||||
|
||||
int genEncoderHeader(const std::string &filename);
|
||||
int genEncoderImpl(const std::string &filename);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
Introduction:
|
||||
|
||||
-------------
|
||||
The emugen tool is a tool to generate a wire protocol implementation
|
||||
based on provided API. The tool generates c++ encoder code that takes
|
||||
API calls and encodes them into the wire and decoder code that decodes
|
||||
the wire stream and calls server matching API.
|
||||
The emugen tool includes additional functionality that enables to
|
||||
generate an wrapper library. The wrapper library provides entry points
|
||||
for the specified API, where each entry routes the call via a dispatch
|
||||
table. The dispatch table may be initialized as required by a specific application.
|
||||
|
||||
The following paragraphs includes the following:
|
||||
* Wire Protocol Description
|
||||
@@ -18,7 +22,7 @@ are used interchangeably by the context.
|
||||
|
||||
|
||||
Wire Protocol packet structure:
|
||||
|
||||
-------------------------------
|
||||
A general Encoder->Decoder packet is structured as following:
|
||||
struct Packet {
|
||||
unsigned int opcode;
|
||||
@@ -52,7 +56,7 @@ Since ‘foo’ returns value, the caller is expected to read back the return pa
|
||||
|
||||
|
||||
Pointer decoding:
|
||||
|
||||
----------------
|
||||
The wire protocol also allows exchanging of pointer data
|
||||
(arrays). Pointers are defined with directions:
|
||||
|
||||
@@ -92,7 +96,7 @@ The return packet is;
|
||||
}
|
||||
|
||||
Endianess
|
||||
|
||||
---------
|
||||
The Wire protocol is designed to impose minimum overhead on the client
|
||||
side. Thus, the data endianness that is sent across the wire is
|
||||
determined by the ‘client’ side. It is up to the server side to
|
||||
@@ -101,7 +105,7 @@ determine the client endianess and marshal the packets as required.
|
||||
|
||||
|
||||
Emugen input files - protocol specification
|
||||
|
||||
-------------------------------------------
|
||||
The protocol generated by emugen consists of two input files:
|
||||
|
||||
1. basename.in - A sepcification of the protocol RPC procedures. This
|
||||
@@ -135,17 +139,22 @@ tree. The format of the .attrib file is described below.
|
||||
|
||||
This files describes the types that are described by the API. A type
|
||||
is defined as follows:
|
||||
<type name> <size in bits> <print format string>
|
||||
<type name> <size in bits> <print format string> <is a pointer? true|false>
|
||||
where:
|
||||
<type name> is the name of the type as described in the API
|
||||
<size in bits> 0, 8, 16, 32 sizes are accepted
|
||||
<print format string> a string to format the value of the type, as acceted by printf(3)
|
||||
<print format string> a string to format the value of the type, as
|
||||
acceted by printf(3)
|
||||
<is pointer?> true or false string species whether the type should be
|
||||
treated as a pointer.
|
||||
|
||||
example:
|
||||
GLint 32 %d
|
||||
GLint 32 %d false
|
||||
GLint* 32 %p true
|
||||
GLptr 32 %p true
|
||||
|
||||
Encoder generated code files
|
||||
|
||||
----------------------------
|
||||
In order to generate the encoder files, one should run the ‘emugen’
|
||||
tool as follows:
|
||||
|
||||
@@ -174,6 +183,9 @@ structure that stores the encoding functions. This data structure also
|
||||
includes ‘accessors’ methods such that library user can override
|
||||
default entries for special case handling.
|
||||
|
||||
api_client_context.cpp - defines an initialization function for
|
||||
dispatch table
|
||||
|
||||
api_enc.h - This header file defines the encoder data strcuture. The
|
||||
encoder data structure inherits its functionality from the
|
||||
‘client_context’ class above and adds encoding and streaming
|
||||
@@ -181,7 +193,8 @@ functionality.
|
||||
|
||||
api_enc.cpp - Encoder implementation.
|
||||
|
||||
5.1.2.2 Decoder generated files
|
||||
Decoder generated files
|
||||
-----------------------
|
||||
In order to generate the decoder files, one should run the ‘emugen’
|
||||
tool as follows:
|
||||
emugen -i <input directory> -D <decoder files output directory> basename
|
||||
@@ -197,8 +210,9 @@ api_opcodes.h - Protocol opcodes
|
||||
|
||||
api_server_proc.h - type definitions for the server side procedures
|
||||
|
||||
api_server_context.h - dispatch table the encoder functions
|
||||
api_server_context.h - dispatch table the decoder functions
|
||||
|
||||
api_server_context.cpp - dispatch table initialization function
|
||||
api_dec.h - Decoder header file
|
||||
|
||||
api_dec.cpp - Decoder implementation. In addtion, this file includes
|
||||
@@ -207,8 +221,30 @@ initialize the API server implementation. An example for such
|
||||
initialization is loading a set of functions from a shared library
|
||||
module.
|
||||
|
||||
.attrib file format description:
|
||||
Wrapper generated files
|
||||
-----------------------
|
||||
In order to generate a wrapper library files, one should run the
|
||||
'emugen' tool as follows:
|
||||
|
||||
emugen -i <input directory> -W <wrapper files output directory> basename
|
||||
where:
|
||||
<input directory> containes the api specification files (basename.in + basename.attrib)
|
||||
<wrapper directory> - a directory name to generate the wrapper output files
|
||||
basename - The basename for the api.
|
||||
|
||||
With resepct to the example above, Emugen will generate the following
|
||||
files:
|
||||
|
||||
api_wrapper_proc.h - type definitions for the wrapper procedures
|
||||
|
||||
api_wrapper_context.h - dispatch table the wrapper functions
|
||||
|
||||
api_wrapper_context.cpp - dispatch table initialization function
|
||||
api_wrapper_entry.cpp - entry points for the API
|
||||
|
||||
|
||||
.attrib file format description:
|
||||
-------------------------------
|
||||
The .attrib file is an input file to emugen and is used to provide
|
||||
additional information that is required for the code generation.
|
||||
The file format is as follows:
|
||||
|
||||
@@ -34,18 +34,23 @@ void usage(const char *filename)
|
||||
fprintf(stderr, "\t-D <dir>: generate decoder into dir\n");
|
||||
fprintf(stderr, "\t-i: input dir, local directory by default\n");
|
||||
fprintf(stderr, "\t-T : generate attribute template into the input directory\n\t\tno other files are generated\n");
|
||||
fprintf(stderr, "\t-W : generate wrapper into dir\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::string encoderDir = "";
|
||||
std::string decoderDir = "";
|
||||
std::string wrapperDir = "";
|
||||
std::string inDir = ".";
|
||||
bool generateAttributesTemplate = false;
|
||||
|
||||
int c;
|
||||
while((c = getopt(argc, argv, "TE:D:i:h")) != -1) {
|
||||
while((c = getopt(argc, argv, "TE:D:i:hW:")) != -1) {
|
||||
switch(c) {
|
||||
case 'W':
|
||||
wrapperDir = std::string(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
generateAttributesTemplate = true;
|
||||
break;
|
||||
@@ -76,7 +81,10 @@ int main(int argc, char *argv[])
|
||||
return BAD_USAGE;
|
||||
}
|
||||
|
||||
if (encoderDir.size() == 0 && decoderDir.size() == 0 && generateAttributesTemplate == false) {
|
||||
if (encoderDir.size() == 0 &&
|
||||
decoderDir.size() == 0 &&
|
||||
generateAttributesTemplate == false &&
|
||||
wrapperDir.size() == 0) {
|
||||
fprintf(stderr, "No output specified - aborting\n");
|
||||
return BAD_USAGE;
|
||||
}
|
||||
@@ -114,23 +122,31 @@ int main(int argc, char *argv[])
|
||||
|
||||
apiEntries.genOpcodes(encoderDir + "/" + baseName + "_opcodes.h");
|
||||
apiEntries.genContext(encoderDir + "/" + baseName + "_client_context.h", ApiGen::CLIENT_SIDE);
|
||||
apiEntries.genContextImpl(encoderDir + "/" + baseName + "_client_context.cpp", ApiGen::CLIENT_SIDE);
|
||||
|
||||
apiEntries.genProcTypes(encoderDir + "/" + baseName + "_client_proc.h", ApiGen::CLIENT_SIDE);
|
||||
|
||||
apiEntries.genClientEntryPoints(encoderDir + "/" + baseName + "_entry.cpp");
|
||||
apiEntries.genEntryPoints(encoderDir + "/" + baseName + "_entry.cpp", ApiGen::CLIENT_SIDE);
|
||||
apiEntries.genEncoderHeader(encoderDir + "/" + baseName + "_enc.h");
|
||||
apiEntries.genEncoderImpl(encoderDir + "/" + baseName + "_enc.cpp");
|
||||
}
|
||||
|
||||
if (decoderDir.size() != 0) {
|
||||
//apiEntries.genEntryPoints(decoderDir + "/" + baseName + "_entry.cpp", baseName);
|
||||
apiEntries.genOpcodes(decoderDir + "/" + baseName + "_opcodes.h");
|
||||
apiEntries.genProcTypes(decoderDir + "/" + baseName + "_server_proc.h", ApiGen::SERVER_SIDE);
|
||||
apiEntries.genContext(decoderDir + "/" + baseName + "_server_context.h", ApiGen::SERVER_SIDE);
|
||||
apiEntries.genContextImpl(decoderDir + "/" + baseName + "_server_context.cpp", ApiGen::SERVER_SIDE);
|
||||
apiEntries.genDecoderHeader(decoderDir + "/" + baseName + "_dec.h");
|
||||
apiEntries.genDecoderImpl(decoderDir + "/" + baseName + "_dec.cpp");
|
||||
// generate the encoder type;
|
||||
|
||||
}
|
||||
|
||||
if (wrapperDir.size() != 0) {
|
||||
apiEntries.genProcTypes(wrapperDir + "/" + baseName + "_wrapper_proc.h", ApiGen::WRAPPER_SIDE);
|
||||
apiEntries.genContext(wrapperDir + "/" + baseName + "_wrapper_context.h", ApiGen::WRAPPER_SIDE);
|
||||
apiEntries.genContextImpl(wrapperDir + "/" + baseName + "_wrapper_context.cpp", ApiGen::WRAPPER_SIDE);
|
||||
apiEntries.genEntryPoints(wrapperDir + "/" + baseName + "_wrapper_entry.cpp", ApiGen::WRAPPER_SIDE);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DUMP
|
||||
int withPointers = 0;
|
||||
printf("%d functions found\n", int(apiEntries.size()));
|
||||
|
||||
Reference in New Issue
Block a user