The SDK build system does not provide an output file and instead uses the -o<FOLDER> option and lets aidl figure out the intermediary folders that represents the packages, and the filename based on the input file (and its package). Because of this the -d<FILE> option to generate a dependency file is not convenient. Instead the new option, -a (no parameters), automatically generate a dependency files next to the output file. Also, when compiling parcelable aidl files, without the -b option, a dependency file is still generated. This is used by the SDK build system since it cannot parse the file separately and instead tries to compile every .aidl file. The generation of this dependency file (which shows no output) allows to know when any type of aidl file has been compiled. Change-Id: If81dc7e1e0a780592c94d1850a1d1b094d6e7908
1000 lines
30 KiB
C++
1000 lines
30 KiB
C++
|
|
#include "aidl_language.h"
|
|
#include "options.h"
|
|
#include "search_path.h"
|
|
#include "Type.h"
|
|
#include "generate_java.h"
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <map>
|
|
|
|
#ifdef HAVE_MS_C_RUNTIME
|
|
#include <io.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifndef O_BINARY
|
|
# define O_BINARY 0
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
static void
|
|
test_document(document_item_type* d)
|
|
{
|
|
while (d) {
|
|
if (d->item_type == INTERFACE_TYPE) {
|
|
interface_type* c = (interface_type*)d;
|
|
printf("interface %s %s {\n", c->package, c->name.data);
|
|
interface_item_type *q = (interface_item_type*)c->interface_items;
|
|
while (q) {
|
|
if (q->item_type == METHOD_TYPE) {
|
|
method_type *m = (method_type*)q;
|
|
printf(" %s %s(", m->type.type.data, m->name.data);
|
|
arg_type *p = m->args;
|
|
while (p) {
|
|
printf("%s %s",p->type.type.data,p->name.data);
|
|
if (p->next) printf(", ");
|
|
p=p->next;
|
|
}
|
|
printf(")");
|
|
printf(";\n");
|
|
}
|
|
q=q->next;
|
|
}
|
|
printf("}\n");
|
|
}
|
|
else if (d->item_type == PARCELABLE_TYPE) {
|
|
parcelable_type* b = (parcelable_type*)d;
|
|
printf("parcelable %s %s;\n", b->package, b->name.data);
|
|
}
|
|
else {
|
|
printf("UNKNOWN d=0x%08lx d->item_type=%d\n", (long)d, d->item_type);
|
|
}
|
|
d = d->next;
|
|
}
|
|
}
|
|
|
|
// ==========================================================
|
|
int
|
|
convert_direction(const char* direction)
|
|
{
|
|
if (direction == NULL) {
|
|
return IN_PARAMETER;
|
|
}
|
|
if (0 == strcmp(direction, "in")) {
|
|
return IN_PARAMETER;
|
|
}
|
|
if (0 == strcmp(direction, "out")) {
|
|
return OUT_PARAMETER;
|
|
}
|
|
return INOUT_PARAMETER;
|
|
}
|
|
|
|
// ==========================================================
|
|
struct import_info {
|
|
const char* from;
|
|
const char* filename;
|
|
buffer_type statement;
|
|
const char* neededClass;
|
|
document_item_type* doc;
|
|
struct import_info* next;
|
|
};
|
|
|
|
document_item_type* g_document = NULL;
|
|
import_info* g_imports = NULL;
|
|
|
|
static void
|
|
main_document_parsed(document_item_type* d)
|
|
{
|
|
g_document = d;
|
|
}
|
|
|
|
static void
|
|
main_import_parsed(buffer_type* statement)
|
|
{
|
|
import_info* import = (import_info*)malloc(sizeof(import_info));
|
|
memset(import, 0, sizeof(import_info));
|
|
import->from = strdup(g_currentFilename);
|
|
import->statement.lineno = statement->lineno;
|
|
import->statement.data = strdup(statement->data);
|
|
import->statement.extra = NULL;
|
|
import->next = g_imports;
|
|
import->neededClass = parse_import_statement(statement->data);
|
|
g_imports = import;
|
|
}
|
|
|
|
static ParserCallbacks g_mainCallbacks = {
|
|
&main_document_parsed,
|
|
&main_import_parsed
|
|
};
|
|
|
|
char*
|
|
parse_import_statement(const char* text)
|
|
{
|
|
const char* end;
|
|
int len;
|
|
|
|
while (isspace(*text)) {
|
|
text++;
|
|
}
|
|
while (!isspace(*text)) {
|
|
text++;
|
|
}
|
|
while (isspace(*text)) {
|
|
text++;
|
|
}
|
|
end = text;
|
|
while (!isspace(*end) && *end != ';') {
|
|
end++;
|
|
}
|
|
len = end-text;
|
|
|
|
char* rv = (char*)malloc(len+1);
|
|
memcpy(rv, text, len);
|
|
rv[len] = '\0';
|
|
|
|
return rv;
|
|
}
|
|
|
|
// ==========================================================
|
|
static void
|
|
import_import_parsed(buffer_type* statement)
|
|
{
|
|
}
|
|
|
|
static ParserCallbacks g_importCallbacks = {
|
|
&main_document_parsed,
|
|
&import_import_parsed
|
|
};
|
|
|
|
// ==========================================================
|
|
static int
|
|
check_filename(const char* filename, const char* package, buffer_type* name)
|
|
{
|
|
const char* p;
|
|
string expected;
|
|
string fn;
|
|
size_t len;
|
|
char cwd[MAXPATHLEN];
|
|
bool valid = false;
|
|
|
|
#ifdef HAVE_WINDOWS_PATHS
|
|
if (isalpha(filename[0]) && filename[1] == ':'
|
|
&& filename[2] == OS_PATH_SEPARATOR) {
|
|
#else
|
|
if (filename[0] == OS_PATH_SEPARATOR) {
|
|
#endif
|
|
fn = filename;
|
|
} else {
|
|
fn = getcwd(cwd, sizeof(cwd));
|
|
len = fn.length();
|
|
if (fn[len-1] != OS_PATH_SEPARATOR) {
|
|
fn += OS_PATH_SEPARATOR;
|
|
}
|
|
fn += filename;
|
|
}
|
|
|
|
if (package) {
|
|
expected = package;
|
|
expected += '.';
|
|
}
|
|
|
|
len = expected.length();
|
|
for (size_t i=0; i<len; i++) {
|
|
if (expected[i] == '.') {
|
|
expected[i] = OS_PATH_SEPARATOR;
|
|
}
|
|
}
|
|
|
|
p = strchr(name->data, '.');
|
|
len = p ? p-name->data : strlen(name->data);
|
|
expected.append(name->data, len);
|
|
|
|
expected += ".aidl";
|
|
|
|
len = fn.length();
|
|
valid = (len >= expected.length());
|
|
|
|
if (valid) {
|
|
p = fn.c_str() + (len - expected.length());
|
|
|
|
#ifdef HAVE_WINDOWS_PATHS
|
|
if (OS_PATH_SEPARATOR != '/') {
|
|
// Input filename under cygwin most likely has / separators
|
|
// whereas the expected string uses \\ separators. Adjust
|
|
// them accordingly.
|
|
for (char *c = const_cast<char *>(p); *c; ++c) {
|
|
if (*c == '/') *c = OS_PATH_SEPARATOR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef OS_CASE_SENSITIVE
|
|
valid = (expected == p);
|
|
#else
|
|
valid = !strcasecmp(expected.c_str(), p);
|
|
#endif
|
|
}
|
|
|
|
if (!valid) {
|
|
fprintf(stderr, "%s:%d interface %s should be declared in a file"
|
|
" called %s.\n",
|
|
filename, name->lineno, name->data, expected.c_str());
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
check_filenames(const char* filename, document_item_type* items)
|
|
{
|
|
int err = 0;
|
|
while (items) {
|
|
if (items->item_type == PARCELABLE_TYPE) {
|
|
parcelable_type* p = (parcelable_type*)items;
|
|
err |= check_filename(filename, p->package, &p->name);
|
|
}
|
|
else if (items->item_type == INTERFACE_TYPE) {
|
|
interface_type* c = (interface_type*)items;
|
|
err |= check_filename(filename, c->package, &c->name);
|
|
}
|
|
else {
|
|
fprintf(stderr, "aidl: internal error unkown document type %d.\n",
|
|
items->item_type);
|
|
return 1;
|
|
}
|
|
items = items->next;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
// ==========================================================
|
|
static const char*
|
|
kind_to_string(int kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case Type::INTERFACE:
|
|
return "an interface";
|
|
case Type::PARCELABLE:
|
|
return "a parcelable";
|
|
default:
|
|
return "ERROR";
|
|
}
|
|
}
|
|
|
|
static char*
|
|
rfind(char* str, char c)
|
|
{
|
|
char* p = str + strlen(str) - 1;
|
|
while (p >= str) {
|
|
if (*p == c) {
|
|
return p;
|
|
}
|
|
p--;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
gather_types(const char* filename, document_item_type* items)
|
|
{
|
|
int err = 0;
|
|
while (items) {
|
|
Type* type;
|
|
if (items->item_type == PARCELABLE_TYPE) {
|
|
parcelable_type* p = (parcelable_type*)items;
|
|
type = new ParcelableType(p->package ? p->package : "",
|
|
p->name.data, false, filename, p->name.lineno);
|
|
}
|
|
else if (items->item_type == INTERFACE_TYPE) {
|
|
interface_type* c = (interface_type*)items;
|
|
type = new InterfaceType(c->package ? c->package : "",
|
|
c->name.data, false, c->oneway,
|
|
filename, c->name.lineno);
|
|
}
|
|
else {
|
|
fprintf(stderr, "aidl: internal error %s:%d\n", __FILE__, __LINE__);
|
|
return 1;
|
|
}
|
|
|
|
Type* old = NAMES.Find(type->QualifiedName());
|
|
if (old == NULL) {
|
|
NAMES.Add(type);
|
|
|
|
if (items->item_type == INTERFACE_TYPE) {
|
|
// for interfaces, also add the stub and proxy types, we don't
|
|
// bother checking these for duplicates, because the parser
|
|
// won't let us do it.
|
|
interface_type* c = (interface_type*)items;
|
|
|
|
string name = c->name.data;
|
|
name += ".Stub";
|
|
Type* stub = new Type(c->package ? c->package : "",
|
|
name, Type::GENERATED, false, false,
|
|
filename, c->name.lineno);
|
|
NAMES.Add(stub);
|
|
|
|
name = c->name.data;
|
|
name += ".Stub.Proxy";
|
|
Type* proxy = new Type(c->package ? c->package : "",
|
|
name, Type::GENERATED, false, false,
|
|
filename, c->name.lineno);
|
|
NAMES.Add(proxy);
|
|
}
|
|
} else {
|
|
if (old->Kind() == Type::BUILT_IN) {
|
|
fprintf(stderr, "%s:%d attempt to redefine built in class %s\n",
|
|
filename, type->DeclLine(),
|
|
type->QualifiedName().c_str());
|
|
err = 1;
|
|
}
|
|
else if (type->Kind() != old->Kind()) {
|
|
const char* oldKind = kind_to_string(old->Kind());
|
|
const char* newKind = kind_to_string(type->Kind());
|
|
|
|
fprintf(stderr, "%s:%d attempt to redefine %s as %s,\n",
|
|
filename, type->DeclLine(),
|
|
type->QualifiedName().c_str(), newKind);
|
|
fprintf(stderr, "%s:%d previously defined here as %s.\n",
|
|
old->DeclFile().c_str(), old->DeclLine(), oldKind);
|
|
err = 1;
|
|
}
|
|
}
|
|
|
|
items = items->next;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
// ==========================================================
|
|
static bool
|
|
matches_keyword(const char* str)
|
|
{
|
|
static const char* KEYWORDS[] = { "abstract", "assert", "boolean", "break",
|
|
"byte", "case", "catch", "char", "class", "const", "continue",
|
|
"default", "do", "double", "else", "enum", "extends", "final",
|
|
"finally", "float", "for", "goto", "if", "implements", "import",
|
|
"instanceof", "int", "interface", "long", "native", "new", "package",
|
|
"private", "protected", "public", "return", "short", "static",
|
|
"strictfp", "super", "switch", "synchronized", "this", "throw",
|
|
"throws", "transient", "try", "void", "volatile", "while",
|
|
"true", "false", "null",
|
|
NULL
|
|
};
|
|
const char** k = KEYWORDS;
|
|
while (*k) {
|
|
if (0 == strcmp(str, *k)) {
|
|
return true;
|
|
}
|
|
k++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
check_method(const char* filename, method_type* m)
|
|
{
|
|
int err = 0;
|
|
|
|
// return type
|
|
Type* returnType = NAMES.Search(m->type.type.data);
|
|
if (returnType == NULL) {
|
|
fprintf(stderr, "%s:%d unknown return type %s\n", filename,
|
|
m->type.type.lineno, m->type.type.data);
|
|
err = 1;
|
|
return err;
|
|
}
|
|
|
|
if (!returnType->CanBeMarshalled()) {
|
|
fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename,
|
|
m->type.type.lineno, m->type.type.data);
|
|
err = 1;
|
|
}
|
|
|
|
if (m->type.dimension > 0 && !returnType->CanBeArray()) {
|
|
fprintf(stderr, "%s:%d return type %s%s can't be an array.\n", filename,
|
|
m->type.array_token.lineno, m->type.type.data,
|
|
m->type.array_token.data);
|
|
err = 1;
|
|
}
|
|
|
|
if (m->type.dimension > 1) {
|
|
fprintf(stderr, "%s:%d return type %s%s only one"
|
|
" dimensional arrays are supported\n", filename,
|
|
m->type.array_token.lineno, m->type.type.data,
|
|
m->type.array_token.data);
|
|
err = 1;
|
|
}
|
|
|
|
int index = 1;
|
|
|
|
arg_type* arg = m->args;
|
|
while (arg) {
|
|
Type* t = NAMES.Search(arg->type.type.data);
|
|
|
|
// check the arg type
|
|
if (t == NULL) {
|
|
fprintf(stderr, "%s:%d parameter %s (%d) unknown type %s\n",
|
|
filename, m->type.type.lineno, arg->name.data, index,
|
|
arg->type.type.data);
|
|
err = 1;
|
|
goto next;
|
|
}
|
|
|
|
if (!t->CanBeMarshalled()) {
|
|
fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n",
|
|
filename, m->type.type.lineno, index,
|
|
arg->type.type.data, arg->name.data);
|
|
err = 1;
|
|
}
|
|
|
|
if (arg->direction.data == NULL
|
|
&& (arg->type.dimension != 0 || t->CanBeOutParameter())) {
|
|
fprintf(stderr, "%s:%d parameter %d: '%s %s' can be an out"
|
|
" parameter, so you must declare it as in,"
|
|
" out or inout.\n",
|
|
filename, m->type.type.lineno, index,
|
|
arg->type.type.data, arg->name.data);
|
|
err = 1;
|
|
}
|
|
|
|
if (convert_direction(arg->direction.data) != IN_PARAMETER
|
|
&& !t->CanBeOutParameter()
|
|
&& arg->type.dimension == 0) {
|
|
fprintf(stderr, "%s:%d parameter %d: '%s %s %s' can only be an in"
|
|
" parameter.\n",
|
|
filename, m->type.type.lineno, index,
|
|
arg->direction.data, arg->type.type.data,
|
|
arg->name.data);
|
|
err = 1;
|
|
}
|
|
|
|
if (arg->type.dimension > 0 && !t->CanBeArray()) {
|
|
fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' can't be an"
|
|
" array.\n", filename,
|
|
m->type.array_token.lineno, index, arg->direction.data,
|
|
arg->type.type.data, arg->type.array_token.data,
|
|
arg->name.data);
|
|
err = 1;
|
|
}
|
|
|
|
if (arg->type.dimension > 1) {
|
|
fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' only one"
|
|
" dimensional arrays are supported\n", filename,
|
|
m->type.array_token.lineno, index, arg->direction.data,
|
|
arg->type.type.data, arg->type.array_token.data,
|
|
arg->name.data);
|
|
err = 1;
|
|
}
|
|
|
|
// check that the name doesn't match a keyword
|
|
if (matches_keyword(arg->name.data)) {
|
|
fprintf(stderr, "%s:%d parameter %d %s is named the same as a"
|
|
" Java keyword\n",
|
|
filename, m->name.lineno, index, arg->name.data);
|
|
err = 1;
|
|
}
|
|
|
|
next:
|
|
index++;
|
|
arg = arg->next;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
check_types(const char* filename, document_item_type* items)
|
|
{
|
|
int err = 0;
|
|
while (items) {
|
|
// (nothing to check for PARCELABLE_TYPE)
|
|
if (items->item_type == INTERFACE_TYPE) {
|
|
map<string,method_type*> methodNames;
|
|
interface_type* c = (interface_type*)items;
|
|
|
|
interface_item_type* member = c->interface_items;
|
|
while (member) {
|
|
if (member->item_type == METHOD_TYPE) {
|
|
method_type* m = (method_type*)member;
|
|
|
|
err |= check_method(filename, m);
|
|
|
|
// prevent duplicate methods
|
|
if (methodNames.find(m->name.data) == methodNames.end()) {
|
|
methodNames[m->name.data] = m;
|
|
} else {
|
|
fprintf(stderr,"%s:%d attempt to redefine method %s,\n",
|
|
filename, m->name.lineno, m->name.data);
|
|
method_type* old = methodNames[m->name.data];
|
|
fprintf(stderr, "%s:%d previously defined here.\n",
|
|
filename, old->name.lineno);
|
|
err = 1;
|
|
}
|
|
}
|
|
member = member->next;
|
|
}
|
|
}
|
|
|
|
items = items->next;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
// ==========================================================
|
|
static int
|
|
exactly_one_interface(const char* filename, const document_item_type* items, const Options& options,
|
|
bool* onlyParcelable)
|
|
{
|
|
if (items == NULL) {
|
|
fprintf(stderr, "%s: file does not contain any interfaces\n",
|
|
filename);
|
|
return 1;
|
|
}
|
|
|
|
const document_item_type* next = items->next;
|
|
if (items->next != NULL) {
|
|
int lineno = -1;
|
|
if (next->item_type == INTERFACE_TYPE) {
|
|
lineno = ((interface_type*)next)->interface_token.lineno;
|
|
}
|
|
else if (next->item_type == PARCELABLE_TYPE) {
|
|
lineno = ((parcelable_type*)next)->parcelable_token.lineno;
|
|
}
|
|
fprintf(stderr, "%s:%d aidl can only handle one interface per file\n",
|
|
filename, lineno);
|
|
return 1;
|
|
}
|
|
|
|
if (items->item_type == PARCELABLE_TYPE) {
|
|
*onlyParcelable = true;
|
|
if (options.failOnParcelable) {
|
|
fprintf(stderr, "%s:%d aidl can only generate code for interfaces, not"
|
|
" parcelables,\n", filename,
|
|
((parcelable_type*)items)->parcelable_token.lineno);
|
|
fprintf(stderr, "%s:%d .aidl files that only declare parcelables "
|
|
"don't need to go in the Makefile.\n", filename,
|
|
((parcelable_type*)items)->parcelable_token.lineno);
|
|
return 1;
|
|
}
|
|
} else {
|
|
*onlyParcelable = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ==========================================================
|
|
void
|
|
generate_dep_file(const Options& options, const document_item_type* items)
|
|
{
|
|
/* we open the file in binary mode to ensure that the same output is
|
|
* generated on all platforms !!
|
|
*/
|
|
FILE* to = NULL;
|
|
if (options.autoDepFile) {
|
|
string fileName = options.outputFileName + ".d";
|
|
to = fopen(fileName.c_str(), "wb");
|
|
} else {
|
|
to = fopen(options.depFileName.c_str(), "wb");
|
|
}
|
|
|
|
if (to == NULL) {
|
|
return;
|
|
}
|
|
|
|
const char* slash = "\\";
|
|
import_info* import = g_imports;
|
|
if (import == NULL) {
|
|
slash = "";
|
|
}
|
|
|
|
if (items->item_type == INTERFACE_TYPE) {
|
|
fprintf(to, "%s: \\\n", options.outputFileName.c_str());
|
|
} else {
|
|
// parcelable: there's no output file.
|
|
fprintf(to, " : \\\n");
|
|
}
|
|
fprintf(to, " %s %s\n", options.inputFileName.c_str(), slash);
|
|
|
|
while (import) {
|
|
if (import->next == NULL) {
|
|
slash = "";
|
|
}
|
|
if (import->filename) {
|
|
fprintf(to, " %s %s\n", import->filename, slash);
|
|
}
|
|
import = import->next;
|
|
}
|
|
|
|
fprintf(to, "\n");
|
|
|
|
fclose(to);
|
|
}
|
|
|
|
// ==========================================================
|
|
static string
|
|
generate_outputFileName2(const Options& options, const buffer_type& name, const char* package)
|
|
{
|
|
string result;
|
|
|
|
// create the path to the destination folder based on the
|
|
// interface package name
|
|
result = options.outputBaseFolder;
|
|
result += OS_PATH_SEPARATOR;
|
|
|
|
string packageStr = package;
|
|
size_t len = packageStr.length();
|
|
for (size_t i=0; i<len; i++) {
|
|
if (packageStr[i] == '.') {
|
|
packageStr[i] = OS_PATH_SEPARATOR;
|
|
}
|
|
}
|
|
|
|
result += packageStr;
|
|
|
|
// add the filename by replacing the .aidl extension to .java
|
|
const char* p = strchr(name.data, '.');
|
|
len = p ? p-name.data : strlen(name.data);
|
|
|
|
result += OS_PATH_SEPARATOR;
|
|
result.append(name.data, len);
|
|
result += ".java";
|
|
|
|
return result;
|
|
}
|
|
|
|
// ==========================================================
|
|
static string
|
|
generate_outputFileName(const Options& options, const document_item_type* items)
|
|
{
|
|
// items has already been checked to have only one interface.
|
|
if (items->item_type == INTERFACE_TYPE) {
|
|
interface_type* type = (interface_type*)items;
|
|
|
|
return generate_outputFileName2(options, type->name, type->package);
|
|
} else if (items->item_type == PARCELABLE_TYPE) {
|
|
parcelable_type* type = (parcelable_type*)items;
|
|
return generate_outputFileName2(options, type->name, type->package);
|
|
}
|
|
|
|
// I don't think we can come here, but safer than returning NULL.
|
|
string result;
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// ==========================================================
|
|
static void
|
|
check_outputFilePath(const string& path) {
|
|
size_t len = path.length();
|
|
for (size_t i=0; i<len ; i++) {
|
|
if (path[i] == OS_PATH_SEPARATOR) {
|
|
string p = path.substr(0, i);
|
|
if (access(path.data(), F_OK) != 0) {
|
|
#ifdef HAVE_MS_C_RUNTIME
|
|
_mkdir(p.data());
|
|
#else
|
|
mkdir(p.data(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ==========================================================
|
|
static int
|
|
parse_preprocessed_file(const string& filename)
|
|
{
|
|
int err;
|
|
|
|
FILE* f = fopen(filename.c_str(), "rb");
|
|
if (f == NULL) {
|
|
fprintf(stderr, "aidl: can't open preprocessed file: %s\n",
|
|
filename.c_str());
|
|
return 1;
|
|
}
|
|
|
|
int lineno = 1;
|
|
char line[1024];
|
|
char type[1024];
|
|
char fullname[1024];
|
|
while (fgets(line, sizeof(line), f)) {
|
|
// skip comments and empty lines
|
|
if (!line[0] || strncmp(line, "//", 2) == 0) {
|
|
continue;
|
|
}
|
|
|
|
sscanf(line, "%s %[^; \r\n\t];", type, fullname);
|
|
|
|
char* packagename;
|
|
char* classname = rfind(fullname, '.');
|
|
if (classname != NULL) {
|
|
*classname = '\0';
|
|
classname++;
|
|
packagename = fullname;
|
|
} else {
|
|
classname = fullname;
|
|
packagename = NULL;
|
|
}
|
|
|
|
//printf("%s:%d:...%s...%s...%s...\n", filename.c_str(), lineno,
|
|
// type, packagename, classname);
|
|
document_item_type* doc;
|
|
|
|
if (0 == strcmp("parcelable", type)) {
|
|
parcelable_type* parcl = (parcelable_type*)malloc(
|
|
sizeof(parcelable_type));
|
|
memset(parcl, 0, sizeof(parcelable_type));
|
|
parcl->document_item.item_type = PARCELABLE_TYPE;
|
|
parcl->parcelable_token.lineno = lineno;
|
|
parcl->parcelable_token.data = strdup(type);
|
|
parcl->package = packagename ? strdup(packagename) : NULL;
|
|
parcl->name.lineno = lineno;
|
|
parcl->name.data = strdup(classname);
|
|
parcl->semicolon_token.lineno = lineno;
|
|
parcl->semicolon_token.data = strdup(";");
|
|
doc = (document_item_type*)parcl;
|
|
}
|
|
else if (0 == strcmp("interface", type)) {
|
|
interface_type* iface = (interface_type*)malloc(
|
|
sizeof(interface_type));
|
|
memset(iface, 0, sizeof(interface_type));
|
|
iface->document_item.item_type = INTERFACE_TYPE;
|
|
iface->interface_token.lineno = lineno;
|
|
iface->interface_token.data = strdup(type);
|
|
iface->package = packagename ? strdup(packagename) : NULL;
|
|
iface->name.lineno = lineno;
|
|
iface->name.data = strdup(classname);
|
|
iface->open_brace_token.lineno = lineno;
|
|
iface->open_brace_token.data = strdup("{");
|
|
iface->close_brace_token.lineno = lineno;
|
|
iface->close_brace_token.data = strdup("}");
|
|
doc = (document_item_type*)iface;
|
|
}
|
|
else {
|
|
fprintf(stderr, "%s:%d: bad type in line: %s\n",
|
|
filename.c_str(), lineno, line);
|
|
return 1;
|
|
}
|
|
err = gather_types(filename.c_str(), doc);
|
|
lineno++;
|
|
}
|
|
|
|
if (!feof(f)) {
|
|
fprintf(stderr, "%s:%d: error reading file, line to long.\n",
|
|
filename.c_str(), lineno);
|
|
return 1;
|
|
}
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
// ==========================================================
|
|
static int
|
|
compile_aidl(Options& options)
|
|
{
|
|
int err = 0, N;
|
|
|
|
set_import_paths(options.importPaths);
|
|
|
|
register_base_types();
|
|
|
|
// import the preprocessed file
|
|
N = options.preprocessedFiles.size();
|
|
for (int i=0; i<N; i++) {
|
|
const string& s = options.preprocessedFiles[i];
|
|
err |= parse_preprocessed_file(s);
|
|
}
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
// parse the main file
|
|
g_callbacks = &g_mainCallbacks;
|
|
err = parse_aidl(options.inputFileName.c_str());
|
|
document_item_type* mainDoc = g_document;
|
|
g_document = NULL;
|
|
|
|
// parse the imports
|
|
g_callbacks = &g_mainCallbacks;
|
|
import_info* import = g_imports;
|
|
while (import) {
|
|
if (NAMES.Find(import->neededClass) == NULL) {
|
|
import->filename = find_import_file(import->neededClass);
|
|
if (!import->filename) {
|
|
fprintf(stderr, "%s:%d: couldn't find import for class %s\n",
|
|
import->from, import->statement.lineno,
|
|
import->neededClass);
|
|
err |= 1;
|
|
} else {
|
|
err |= parse_aidl(import->filename);
|
|
import->doc = g_document;
|
|
if (import->doc == NULL) {
|
|
err |= 1;
|
|
}
|
|
}
|
|
}
|
|
import = import->next;
|
|
}
|
|
// bail out now if parsing wasn't successful
|
|
if (err != 0 || mainDoc == NULL) {
|
|
//fprintf(stderr, "aidl: parsing failed, stopping.\n");
|
|
return 1;
|
|
}
|
|
|
|
// complain about ones that aren't in the right files
|
|
err |= check_filenames(options.inputFileName.c_str(), mainDoc);
|
|
import = g_imports;
|
|
while (import) {
|
|
err |= check_filenames(import->filename, import->doc);
|
|
import = import->next;
|
|
}
|
|
|
|
// gather the types that have been declared
|
|
err |= gather_types(options.inputFileName.c_str(), mainDoc);
|
|
import = g_imports;
|
|
while (import) {
|
|
err |= gather_types(import->filename, import->doc);
|
|
import = import->next;
|
|
}
|
|
|
|
#if 0
|
|
printf("---- main doc ----\n");
|
|
test_document(mainDoc);
|
|
|
|
import = g_imports;
|
|
while (import) {
|
|
printf("---- import doc ----\n");
|
|
test_document(import->doc);
|
|
import = import->next;
|
|
}
|
|
NAMES.Dump();
|
|
#endif
|
|
|
|
// check the referenced types in mainDoc to make sure we've imported them
|
|
err |= check_types(options.inputFileName.c_str(), mainDoc);
|
|
|
|
// finally, there really only needs to be one thing in mainDoc, and it
|
|
// needs to be an interface.
|
|
bool onlyParcelable = false;
|
|
err |= exactly_one_interface(options.inputFileName.c_str(), mainDoc, options, &onlyParcelable);
|
|
|
|
// after this, there shouldn't be any more errors because of the
|
|
// input.
|
|
if (err != 0 || mainDoc == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
// if needed, generate the outputFileName from the outputBaseFolder
|
|
if (options.outputFileName.length() == 0 &&
|
|
options.outputBaseFolder.length() > 0) {
|
|
options.outputFileName = generate_outputFileName(options, mainDoc);
|
|
}
|
|
|
|
// if we were asked to, generate a make dependency file
|
|
// unless it's a parcelable *and* it's supposed to fail on parcelable
|
|
if ((options.autoDepFile || options.depFileName != "") &&
|
|
!(onlyParcelable && options.failOnParcelable)) {
|
|
// make sure the folders of the output file all exists
|
|
check_outputFilePath(options.outputFileName);
|
|
generate_dep_file(options, mainDoc);
|
|
}
|
|
|
|
// they didn't ask to fail on parcelables, so just exit quietly.
|
|
if (onlyParcelable && !options.failOnParcelable) {
|
|
return 0;
|
|
}
|
|
|
|
// make sure the folders of the output file all exists
|
|
check_outputFilePath(options.outputFileName);
|
|
|
|
err = generate_java(options.outputFileName, options.inputFileName.c_str(),
|
|
(interface_type*)mainDoc);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
preprocess_aidl(const Options& options)
|
|
{
|
|
vector<string> lines;
|
|
int err;
|
|
|
|
// read files
|
|
int N = options.filesToPreprocess.size();
|
|
for (int i=0; i<N; i++) {
|
|
g_callbacks = &g_mainCallbacks;
|
|
err = parse_aidl(options.filesToPreprocess[i].c_str());
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
document_item_type* doc = g_document;
|
|
string line;
|
|
if (doc->item_type == PARCELABLE_TYPE) {
|
|
line = "parcelable ";
|
|
parcelable_type* parcelable = (parcelable_type*)doc;
|
|
if (parcelable->package) {
|
|
line += parcelable->package;
|
|
line += '.';
|
|
}
|
|
line += parcelable->name.data;
|
|
} else {
|
|
line = "interface ";
|
|
interface_type* iface = (interface_type*)doc;
|
|
if (iface->package) {
|
|
line += iface->package;
|
|
line += '.';
|
|
}
|
|
line += iface->name.data;
|
|
}
|
|
line += ";\n";
|
|
lines.push_back(line);
|
|
}
|
|
|
|
// write preprocessed file
|
|
int fd = open( options.outputFileName.c_str(),
|
|
O_RDWR|O_CREAT|O_TRUNC|O_BINARY,
|
|
#ifdef HAVE_MS_C_RUNTIME
|
|
_S_IREAD|_S_IWRITE);
|
|
#else
|
|
S_IRUSR|S_IWUSR|S_IRGRP);
|
|
#endif
|
|
if (fd == -1) {
|
|
fprintf(stderr, "aidl: could not open file for write: %s\n",
|
|
options.outputFileName.c_str());
|
|
return 1;
|
|
}
|
|
|
|
N = lines.size();
|
|
for (int i=0; i<N; i++) {
|
|
const string& s = lines[i];
|
|
int len = s.length();
|
|
if (len != write(fd, s.c_str(), len)) {
|
|
fprintf(stderr, "aidl: error writing to file %s\n",
|
|
options.outputFileName.c_str());
|
|
close(fd);
|
|
unlink(options.outputFileName.c_str());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
// ==========================================================
|
|
int
|
|
main(int argc, const char **argv)
|
|
{
|
|
Options options;
|
|
int result = parse_options(argc, argv, &options);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
|
|
switch (options.task)
|
|
{
|
|
case COMPILE_AIDL:
|
|
return compile_aidl(options);
|
|
case PREPROCESS_AIDL:
|
|
return preprocess_aidl(options);
|
|
}
|
|
fprintf(stderr, "aidl: internal error\n");
|
|
return 1;
|
|
}
|
|
|
|
|