emugen: generate wrapper library files

Add functionality to generate a wrapper library. A wrapper library includes:
1. entry points
2. dispatch table (accessed from the library entry points)
3. dispatch table accessor callback
4. dispatch table initialization function

Note that the dispatch table initialization function used to be part of the decoder. This
change moves it to be part of the dispatch table layer where it belongs.

Change-Id: Ide6764a17cc029056f9946e778a513cdc2a49003
This commit is contained in:
Jacky Romano
2011-04-17 23:49:13 +03:00
committed by David 'Digit' Turner
parent 139308015e
commit ea3a3584f5
5 changed files with 147 additions and 50 deletions

View File

@@ -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

View File

@@ -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());

View File

@@ -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);

View File

@@ -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:

View File

@@ -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()));