summaryrefslogtreecommitdiff
path: root/src/upbc.c
blob: 4cfa6e6982fbbbbd7ec0255d51859f70f97c9ee9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * upbc is the upb compiler.  This is some deep code that I wish could be
 * easier to understand, but by its nature it is doing some very "meta"
 * kinds of things.
 *
 * Copyright (c) 2009 Joshua Haberman.  See LICENSE for details.
 */

#include <ctype.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include "descriptor.h"
#include "upb_def.h"
#include "upb_msg.h"
#include "upb_glue.h"
#include "upb_strstream.h"
#include "upb_decoder.h"

/* These are in-place string transformations that do not change the length of
 * the string (and thus never need to re-allocate). */

// Convert to C identifier: foo.bar.Baz -> foo_bar_Baz.
static void to_cident(upb_string *str)
{
  upb_strlen_t len = upb_string_len(str);
  char *buf = upb_string_getrwbuf(str, len);
  for(int32_t i = 0; i < len; i++)
    if(buf[i] == '.' || buf[i] == '/')
      buf[i] = '_';
}

// Convert to C proprocessor identifier: foo.bar.Baz -> FOO_BAR_BAZ.
static void to_preproc(upb_string *str)
{
  to_cident(str);
  upb_strlen_t len = upb_string_len(str);
  char *buf = upb_string_getrwbuf(str, len);
  for(int32_t i = 0; i < len; i++)
    buf[i] = toupper(buf[i]);
}

static int my_memrchr(const char *data, char c, size_t len)
{
  int off = len-1;
  while(off > 0 && data[off] != c) --off;
  return off;
}

/* The _const.h file defines the constants (enums) defined in the .proto
 * file. */
static void write_const_h(upb_def *defs[], int num_entries, char *outfile_name,
                          FILE *stream)
{
  /* Header file prologue. */
  upb_string *include_guard_name = upb_strdupc(outfile_name);
  to_preproc(include_guard_name);
  /* A bit cheesy, but will do the job. */
  upb_strlen_t len = upb_string_len(include_guard_name);
  char *buf = upb_string_getrwbuf(include_guard_name, len);
  buf[len-1] = 'C';

  fputs("/* This file was generated by upbc (the upb compiler).  "
        "Do not edit. */\n\n", stream),
  fprintf(stream, "#ifndef " UPB_STRFMT "\n", UPB_STRARG(include_guard_name));
  fprintf(stream, "#define " UPB_STRFMT "\n\n", UPB_STRARG(include_guard_name));
  fputs("#ifdef __cplusplus\n", stream);
  fputs("extern \"C\" {\n", stream);
  fputs("#endif\n\n", stream);

  /* Enums. */
  fprintf(stream, "/* Enums. */\n\n");
  for(int i = 0; i < num_entries; i++) {  /* Foreach enum */
    if(defs[i]->type != UPB_DEF_ENUM) continue;
    upb_enumdef *enumdef = upb_downcast_enumdef(defs[i]);
    upb_string *enum_name = upb_strdup(UPB_UPCAST(enumdef)->fqname);
    upb_string *enum_val_prefix = upb_strdup(enum_name);
    to_cident(enum_name);

    const char *data = upb_string_getrobuf(enum_val_prefix);
    upb_strlen_t len = upb_string_len(enum_val_prefix);
    upb_strlen_t lastsep = my_memrchr(data, UPB_SYMBOL_SEPARATOR, len);
    upb_string_getrwbuf(enum_val_prefix, lastsep + 1);
    to_preproc(enum_val_prefix);

    fprintf(stream, "typedef enum " UPB_STRFMT " {\n", UPB_STRARG(enum_name));
    bool first = true;
    /* Foreach enum value. */
    for (upb_enum_iter iter = upb_enum_begin(enumdef);
         !upb_enum_done(iter);
         iter = upb_enum_next(enumdef, iter)) {
      upb_string *value_name = upb_strdup(upb_enum_iter_name(iter));
      to_preproc(value_name);
      /* "  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT32 = 13," */
      if (!first) fputs(",\n", stream);
      first = false;
      fprintf(stream, "  " UPB_STRFMT UPB_STRFMT " = %" PRIu32,
              UPB_STRARG(enum_val_prefix), UPB_STRARG(value_name), upb_enum_iter_number(iter));
      upb_string_unref(value_name);
    }
    fprintf(stream, "\n} " UPB_STRFMT ";\n\n", UPB_STRARG(enum_name));
    upb_string_unref(enum_name);
    upb_string_unref(enum_val_prefix);
  }

  /* Constants for field names and numbers. */
  fprintf(stream, "/* Constants for field names and numbers. */\n\n");
  for(int i = 0; i < num_entries; i++) {  /* Foreach enum */
    upb_msgdef *m = upb_dyncast_msgdef(defs[i]);
    if(!m) continue;
    upb_string *msg_name = upb_strdup(UPB_UPCAST(m)->fqname);
    upb_string *msg_val_prefix = upb_strdup(msg_name);
    to_preproc(msg_val_prefix);
    upb_msg_iter i;
    for(i = upb_msg_begin(m); !upb_msg_done(i); i = upb_msg_next(m, i)) {
      upb_fielddef *f = upb_msg_iter_field(i);
      upb_string *preproc_field_name = upb_strdup(f->name);
      to_preproc(preproc_field_name);
      fprintf(stream, "#define " UPB_STRFMT "_" UPB_STRFMT "_FIELDNUM %d\n",
              UPB_STRARG(msg_val_prefix), UPB_STRARG(preproc_field_name), f->number);
      fprintf(stream, "#define " UPB_STRFMT "_" UPB_STRFMT "_FIELDNAME \""
              UPB_STRFMT "\"\n", UPB_STRARG(msg_val_prefix), UPB_STRARG(preproc_field_name), UPB_STRARG(f->name));
      upb_string_unref(preproc_field_name);
    }
    upb_string_unref(msg_val_prefix);
    upb_string_unref(msg_name);
  }

  /* Epilogue. */
  fputs("#ifdef __cplusplus\n", stream);
  fputs("}  /* extern \"C\" */\n", stream);
  fputs("#endif\n\n", stream);
  fprintf(stream, "#endif  /* " UPB_STRFMT " */\n", UPB_STRARG(include_guard_name));
  upb_string_unref(include_guard_name);
}

const char usage[] =
  "upbc -- upb compiler.\n"
  "upb v0.1  http://blog.reverberate.org/upb/\n"
  "\n"
  "Usage: upbc [options] descriptor-file\n"
  "\n"
  "  -o OUTFILE-BASE    Write to OUTFILE-BASE.h and OUTFILE-BASE.c instead\n"
  "                     of using the input file as a basename.\n"
;

void usage_err(char *err)
{
  fprintf(stderr, "upbc: %s\n\n", err);
  fputs(usage, stderr);
  exit(1);
}

void error(char *err, ...)
{
  va_list args;
  va_start(args, err);
  fprintf(stderr, "upbc: ");
  vfprintf(stderr, err, args);
  va_end(args);
  exit(1);
}

int main(int argc, char *argv[])
{
  /* Parse arguments. */
  char *outfile_base = NULL, *input_file = NULL;
  for(int i = 1; i < argc; i++) {
    if(strcmp(argv[i], "-o") == 0) {
      if(++i == argc)
        usage_err("-o must be followed by a FILE-BASE.");
      else if(outfile_base)
        usage_err("-o was specified multiple times.");
      outfile_base = argv[i];
    } else {
      if(input_file)
        usage_err("You can only specify one input file.");
      input_file = argv[i];
    }
  }
  if(!input_file) usage_err("You must specify an input file.");
  if(!outfile_base) outfile_base = input_file;

  // Read and parse input file.
  upb_string *descriptor = upb_strreadfile(input_file);
  if(!descriptor)
    error("Couldn't read input file.");

  // TODO: make upb_parsedesc use a separate symtab, so we can use it here when
  // importing descriptor.proto.
  upb_symtab *s = upb_symtab_new();
  upb_symtab_add_descriptorproto(s);
  upb_symtab *s2 = upb_symtab_new();
  upb_status status = UPB_STATUS_INIT;

  upb_stringsrc strsrc;
  upb_stringsrc_init(&strsrc);
  upb_stringsrc_reset(&strsrc, descriptor);

  upb_decoder d;
  upb_msgdef *fds_msgdef = upb_symtab_fds_def(s);
  upb_decoder_init(&d, fds_msgdef);
  upb_decoder_reset(&d, upb_stringsrc_bytesrc(&strsrc));

  upb_symtab_addfds(s2, upb_decoder_src(&d), &status);
  upb_stringsrc_uninit(&strsrc);
  upb_decoder_uninit(&d);
  upb_def_unref(UPB_UPCAST(fds_msgdef));

  if(!upb_ok(&status)) {
    upb_printerr(&status);
    error("Failed to parse input file descriptor\n");
  }

  upb_status_uninit(&status);

  /* Emit output files. */
  const int maxsize = 256;
  char h_const_filename[maxsize];
  if(snprintf(h_const_filename, maxsize, "%s_const.h", outfile_base) >= maxsize)
    error("File base too long.\n");

  FILE *h_const_file = fopen(h_const_filename, "w");
  if(!h_const_file) error("Failed to open _const.h output file\n");

  int symcount;
  upb_def **defs = upb_symtab_getdefs(s, &symcount, UPB_DEF_ANY);
  write_const_h(defs, symcount, h_const_filename, h_const_file);
  for (int i = 0; i < symcount; i++) upb_def_unref(defs[i]);
  free(defs);
  upb_string_unref(descriptor);
  upb_symtab_unref(s);
  upb_symtab_unref(s2);
  fclose(h_const_file);

  return 0;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback