flatcc_cli.c (17440B)
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 #include "flatcc/flatcc.h" 6 #include "config.h" 7 8 #define VERSION FLATCC_VERSION_TEXT 9 #define TITLE FLATCC_TITLE_TEXT 10 11 void usage(FILE *fp) 12 { 13 fprintf(fp, "%s\n", TITLE); 14 fprintf(fp, "version: %s\n", VERSION); 15 fprintf(fp, "usage: flatcc [options] file [...]\n"); 16 fprintf(fp, "options:\n" 17 " --reader (default) Generate reader\n" 18 " -c, --common Generate common include header(s)\n" 19 " --common_reader Generate common reader include header(s)\n" 20 " --common_builder Generate common builder include header(s)\n" 21 " -w, --builder Generate builders (writable buffers)\n" 22 " -v, --verifier Generate verifier\n" 23 " -r, --recursive Recursively generate included schema files\n" 24 " -a Generate all (like -cwvr)\n" 25 " -g Use _get suffix only to avoid conflicts\n" 26 " -d Dependency file like gcc -MMD\n" 27 " -I<inpath> Search path for include files (multiple allowed)\n" 28 " -o<outpath> Write files relative to this path (dir must exist)\n" 29 " --stdout Concatenate all output to stdout\n" 30 " --outfile=<file> Like --stdout, but to a file.\n" 31 " --depfile=<file> Dependency file like gcc -MF.\n" 32 " --deptarget=<file> Override --depfile target like gcc -MT.\n" 33 " --prefix=<prefix> Add prefix to all generated names (no _ added)\n" 34 " --common-prefix=<prefix> Replace 'flatbuffers' prefix in common files\n" 35 #if FLATCC_REFLECTION 36 " --schema Generate binary schema (.bfbs)\n" 37 " --schema-length=no Add length prefix to binary schema\n" 38 #endif 39 " --verifier Generate verifier for schema\n" 40 " --json-parser Generate json parser for schema\n" 41 " --json-printer Generate json printer for schema\n" 42 " --json Generate both json parser and printer for schema\n" 43 " --version Show version\n" 44 " -h | --help Help message\n" 45 ); 46 } 47 48 void help(FILE *fp) 49 { 50 usage(fp); 51 fprintf(fp, 52 "\n" 53 "This is a flatbuffer compatible compiler implemented in C generating C\n" 54 "source. It is largely compatible with the flatc compiler provided by\n" 55 "Google Fun Propulsion Lab but does not support JSON objects or binary\n" 56 "schema.\n" 57 "\n" 58 "By example 'flatcc monster.fbs' generates a 'monster.h' file which\n" 59 "provides functions to read a flatbuffer. A common include header is also\n" 60 "required. The common file is generated with the -c option. The reader\n" 61 "has no external dependencies.\n" 62 "\n" 63 "The -w (--builder) option enables code generation to build buffers:\n" 64 "`flatbuffers -w monster.fbs` will generate `monster.h` and\n" 65 "`monster_builder.h`, and also a builder specific common file with the\n" 66 "-cw option. The builder must link with the extern `flatbuilder` library.\n" 67 "\n" 68 "-v (--verifier) generates a verifier file per schema. It depends on the\n" 69 "runtime library but not on other generated files, except other included\n" 70 "verifiers.\n" 71 "\n" 72 "-r (--recursive) generates all schema included recursively.\n" 73 "\n" 74 "--reader is the default option to generate reader output but can be used\n" 75 "explicitly together with other options that would otherwise disable it.\n" 76 "\n" 77 "All C output can be concated to a single file using --stdout or\n" 78 "--outfile with content produced in dependency order. The outfile is\n" 79 "relative to cwd.\n" 80 "\n" 81 "-g Only add '_get' suffix to read accessors such that, for example,\n" 82 "only 'Monster_name_get(monster)` will be generated and not also\n" 83 "'Monster_name(monster)'. This avoids potential conflicts with\n" 84 "other generated symbols when a schema change is impractical.\n" 85 "\n" 86 "-d generates a dependency file, e.g. 'monster.fbs.d' in the output dir.\n" 87 "\n" 88 "--depfile implies -d but accepts an explicit filename with a path\n" 89 "relative to cwd. The dependency files content is a gnu make rule with a\n" 90 "target followed by the included schema files The target must match how\n" 91 "it is seen by the rest of the build system and defaults to e.g.\n" 92 "'monster_reader.h' or 'monster.bfbs' paths relative to the working\n" 93 "directory.\n" 94 "\n" 95 "--deptarget overrides the default target for --depfile, simiar to gcc -MT.\n" 96 "\n" 97 98 #if FLATCC_REFLECTION 99 "--schema will generate a binary .bfbs file for each top-level schema file.\n" 100 "Can be used with --stdout if no C output is specified. When used with multiple\n" 101 "files --schema-length=yes is recommend.\n" 102 "\n" 103 "--schema-length adds a length prefix of type uoffset_t to binary schema so\n" 104 "they can be concatenated - the aligned buffer starts after the prefix.\n" 105 "\n" 106 #else 107 "Flatbuffers binary schema support (--schema) has been disabled." 108 "\n" 109 #endif 110 "--json-parser generates a file that implements a fast typed json parser for\n" 111 "the schema. It depends on some flatcc headers and the runtime library but\n" 112 "not on other generated files except other parsers from included schema.\n" 113 "\n" 114 "--json-printer generates a file that implements json printers for the schema\n" 115 "and has dependencies similar to --json-parser.\n" 116 "\n" 117 "--json is generates both printer and parser.\n" 118 "\n" 119 #if FLATCC_REFLECTION 120 #if 0 /* Disable deprecated features. */ 121 "DEPRECATED:\n" 122 " --schema-namespace controls if typenames in schema are prefixed a namespace.\n" 123 " namespaces should always be present.\n" 124 "\n" 125 #endif 126 #endif 127 "The generated source can redefine offset sizes by including a modified\n" 128 "`flatcc_types.h` file. The flatbuilder library must then be compiled with the\n" 129 "same `flatcc_types.h` file. In this case --prefix and --common-prefix options\n" 130 "may be helpful to avoid conflict with standard offset sizes.\n" 131 "\n" 132 "The output size may seem bulky, but most content is rarely used inline\n" 133 "functions and macros. The compiled binary need not be large.\n" 134 "\n" 135 "The generated source assumes C11 functionality for alignment, compile\n" 136 "time assertions and inline functions but an optional set of portability\n" 137 "headers can be included to work with most any compiler. The portability\n" 138 "layer is not throughly tested so a platform specific test is required\n" 139 "before production use. Upstream patches are welcome.\n"); 140 } 141 142 enum { noarg, suffixarg, nextarg }; 143 144 int parse_bool_arg(const char *a) 145 { 146 if (strcmp(a, "0") == 0 || strcmp(a, "no") == 0) { 147 return 0; 148 } 149 if (strcmp(a, "1") == 0 || strcmp(a, "yes") == 0) { 150 return 1; 151 } 152 fprintf(stderr, "invalid boolean argument: '%s', must be '0', '1', 'yes' or 'no'\n", a); 153 return -1; 154 } 155 156 int match_long_arg(const char *option, const char *s, size_t n) 157 { 158 return strncmp(option, s, n) == 0 && strlen(option) == n; 159 } 160 161 int set_opt(flatcc_options_t *opts, const char *s, const char *a) 162 { 163 int ret = noarg; 164 size_t n = strlen(s); 165 const char *v = strchr(s, '='); 166 if (v) { 167 a = v + 1; 168 n = (size_t)(v - s); 169 } 170 if (*s == 'h' || 0 == strcmp("-help", s)) { 171 /* stdout so less and more works. */ 172 help(stdout); 173 exit(0); 174 } 175 if (0 == strcmp("-version", s)) { 176 fprintf(stdout, "%s\n", TITLE); 177 fprintf(stdout, "version: %s\n", VERSION); 178 exit(0); 179 } 180 if (0 == strcmp("-stdout", s)) { 181 opts->gen_stdout = 1; 182 return noarg; 183 } 184 if (0 == strcmp("-common", s)) { 185 opts->cgen_common_reader = 1; 186 opts->cgen_common_builder = 1; 187 return noarg; 188 } 189 if (0 == strcmp("-common_reader", s)) { 190 opts->cgen_common_reader = 1; 191 return noarg; 192 } 193 if (0 == strcmp("-common_builder", s)) { 194 opts->cgen_common_builder = 1; 195 return noarg; 196 } 197 if (0 == strcmp("-reader", s)) { 198 opts->cgen_reader = 1; 199 return noarg; 200 } 201 if (0 == strcmp("-builder", s)) { 202 opts->cgen_builder = 1; 203 return noarg; 204 } 205 if (0 == strcmp("-verifier", s)) { 206 opts->cgen_verifier = 1; 207 return noarg; 208 } 209 if (0 == strcmp("-recursive", s)) { 210 opts->cgen_recursive = 1; 211 return noarg; 212 } 213 #if FLATCC_REFLECTION 214 if (0 == strcmp("-schema", s)) { 215 opts->bgen_bfbs = 1; 216 return noarg; 217 } 218 #endif 219 if (0 == strcmp("-json-parser", s)) { 220 opts->cgen_json_parser = 1; 221 return noarg; 222 } 223 if (0 == strcmp("-json-printer", s)) { 224 opts->cgen_json_printer = 1; 225 return noarg; 226 } 227 if (0 == strcmp("-json", s)) { 228 opts->cgen_json_parser = 1; 229 opts->cgen_json_printer = 1; 230 return noarg; 231 } 232 #if FLATCC_REFLECTION 233 #if 0 /* Disable deprecated features. */ 234 if (match_long_arg("-schema-namespace", s, n)) { 235 fprintf(stderr, "warning: --schema-namespace is deprecated\n" 236 " a namespace is added by default and should always be present\n"); 237 if (!a) { 238 fprintf(stderr, "--schema-namespace option needs an argument\n"); 239 exit(-1); 240 } 241 if(0 > (opts->bgen_qualify_names = parse_bool_arg(a))) { 242 exit(-1); 243 } 244 return v ? noarg : nextarg; 245 } 246 if (match_long_arg("-schema-length", s, n)) { 247 if (!a) { 248 fprintf(stderr, "--schema-length option needs an argument\n"); 249 exit(-1); 250 } 251 if(0 > (opts->bgen_length_prefix = parse_bool_arg(a))) { 252 exit(-1); 253 } 254 return v ? noarg : nextarg; 255 } 256 #endif 257 #endif 258 if (match_long_arg("-depfile", s, n)) { 259 if (!a) { 260 fprintf(stderr, "--depfile option needs an argument\n"); 261 exit(-1); 262 } 263 opts->gen_depfile = a; 264 opts->gen_dep = 1; 265 return v ? noarg : nextarg; 266 } 267 if (match_long_arg("-deptarget", s, n)) { 268 if (!a) { 269 fprintf(stderr, "--deptarget option needs an argument\n"); 270 exit(-1); 271 } 272 opts->gen_deptarget = a; 273 return v ? noarg : nextarg; 274 } 275 if (match_long_arg("-outfile", s, n)) { 276 if (!a) { 277 fprintf(stderr, "--outfile option needs an argument\n"); 278 exit(-1); 279 } 280 opts->gen_outfile= a; 281 return v ? noarg : nextarg; 282 } 283 if (match_long_arg("-common-prefix", s, n)) { 284 if (!a) { 285 fprintf(stderr, "--common-prefix option needs an argument\n"); 286 exit(-1); 287 } 288 opts->nsc = a; 289 return v ? noarg : nextarg; 290 } 291 if (match_long_arg("-prefix", s, n)) { 292 if (!a) { 293 fprintf(stderr, "-n option needs an argument\n"); 294 exit(-1); 295 } 296 opts->ns = a; 297 return v ? noarg : nextarg; 298 } 299 switch (*s) { 300 case '-': 301 fprintf(stderr, "invalid option: -%s\n", s); 302 exit(-1); 303 case 'I': 304 if (s[1]) { 305 ret = suffixarg; 306 a = s + 1; 307 } else if (!a) { 308 fprintf(stderr, "-I option needs an argument\n"); 309 exit(-1); 310 } else { 311 ret = nextarg; 312 } 313 opts->inpaths[opts->inpath_count++] = a; 314 return ret; 315 case 'o': 316 if (opts->outpath) { 317 fprintf(stderr, "-o option can only be specified once\n"); 318 exit(-1); 319 } 320 if (s[1]) { 321 ret = suffixarg; 322 a = s + 1; 323 } else if (!a) { 324 fprintf(stderr, "-o option needs an argument\n"); 325 exit(-1); 326 } else { 327 ret = nextarg; 328 } 329 opts->outpath = a; 330 return ret; 331 case 'w': 332 opts->cgen_builder = 1; 333 return noarg; 334 case 'v': 335 opts->cgen_verifier = 1; 336 return noarg; 337 case 'c': 338 opts->cgen_common_reader = 1; 339 opts->cgen_common_builder = 1; 340 return noarg; 341 case 'r': 342 opts->cgen_recursive = 1; 343 return noarg; 344 case 'g': 345 opts->cgen_no_conflicts = 1; 346 return noarg; 347 case 'd': 348 opts->gen_dep = 1; 349 return noarg; 350 case 'a': 351 opts->cgen_reader = 1; 352 opts->cgen_builder = 1; 353 opts->cgen_verifier = 1; 354 opts->cgen_common_reader = 1; 355 opts->cgen_common_builder = 1; 356 opts->cgen_recursive = 1; 357 return noarg; 358 default: 359 fprintf(stderr, "invalid option: -%c\n", *s); 360 exit(-1); 361 } 362 return noarg; 363 } 364 365 int get_opt(flatcc_options_t *opts, const char *s, const char *a) 366 { 367 if (s[1] == '-') { 368 return nextarg == set_opt(opts, s + 1, a); 369 } 370 ++s; 371 if (*s == 0) { 372 fprintf(stderr, "- is not a valid option\n"); 373 exit(-1); 374 } 375 while (*s) { 376 switch (set_opt(opts, s, a)) { 377 case noarg: 378 ++s; 379 continue; 380 case suffixarg: 381 return 0; 382 case nextarg: 383 return 1; 384 } 385 } 386 return noarg; 387 } 388 389 void parse_opts(int argc, const char *argv[], flatcc_options_t *opts) 390 { 391 int i; 392 const char *s, *a; 393 394 for (i = 1; i < argc; ++i) { 395 if (argv[i][0] == '-') { 396 s = argv[i]; 397 a = i + 1 < argc ? argv[i + 1] : 0; 398 i += get_opt(opts, s, a); 399 } else { 400 opts->srcpaths[opts->srcpath_count++] = argv[i]; 401 } 402 } 403 } 404 405 int main(int argc, const char *argv[]) 406 { 407 flatcc_options_t opts; 408 flatcc_context_t ctx = 0; 409 int i, ret, cgen; 410 const char **src; 411 412 ctx = 0; 413 ret = 0; 414 if (argc < 2) { 415 usage(stderr); 416 exit(-1); 417 } 418 flatcc_init_options(&opts); 419 if (!(opts.inpaths = malloc((size_t)argc * sizeof(char *)))) { 420 fprintf(stderr, "memory allocation failure\n"); 421 exit(-1); 422 } 423 if (!(opts.srcpaths = malloc((size_t)argc * sizeof(char *)))) { 424 fprintf(stderr, "memory allocation failure\n"); 425 free((void *)opts.inpaths); 426 exit(-1); 427 } 428 429 parse_opts(argc, argv, &opts); 430 if (opts.cgen_builder && opts.cgen_common_reader) { 431 opts.cgen_common_builder = 1; 432 } 433 if (opts.srcpath_count == 0) { 434 /* No input files, so only generate header(s). */ 435 if (!(opts.cgen_common_reader || opts.cgen_common_builder) || opts.bgen_bfbs) { 436 fprintf(stderr, "filename missing\n"); 437 goto fail; 438 } 439 if (!(ctx = flatcc_create_context(&opts, 0, 0, 0))) { 440 fprintf(stderr, "internal error: failed to create parsing context\n"); 441 goto fail; 442 } 443 if (flatcc_generate_files(ctx)) { 444 goto fail; 445 } 446 flatcc_destroy_context(ctx); 447 ctx = 0; 448 goto done; 449 } 450 cgen = opts.cgen_reader || opts.cgen_builder || opts.cgen_verifier 451 || opts.cgen_common_reader || opts.cgen_common_builder 452 || opts.cgen_json_parser || opts.cgen_json_printer; 453 if (!opts.bgen_bfbs && (!cgen || opts.cgen_builder || opts.cgen_verifier)) { 454 /* Assume default if no other output specified when deps required it. */ 455 opts.cgen_reader = 1; 456 } 457 if (opts.bgen_bfbs && cgen) { 458 if (opts.gen_stdout) { 459 fprintf(stderr, "--stdout cannot be used with mixed text and binary output"); 460 goto fail; 461 } 462 if (opts.gen_outfile) { 463 fprintf(stderr, "--outfile cannot be used with mixed text and binary output"); 464 goto fail; 465 } 466 } 467 if (opts.gen_deptarget && !opts.gen_depfile) { 468 fprintf(stderr, "--deptarget cannot be used without --depfile"); 469 goto fail; 470 } 471 if (opts.gen_stdout && opts.gen_outfile) { 472 fprintf(stderr, "--outfile cannot be used with --stdout"); 473 goto fail; 474 } 475 for (i = 0, src = opts.srcpaths; i < opts.srcpath_count; ++i, ++src) { 476 if (!(ctx = flatcc_create_context(&opts, *src, 0, 0))) { 477 fprintf(stderr, "internal error: failed to create parsing context\n"); 478 goto fail; 479 } 480 if (flatcc_parse_file(ctx, *src)) { 481 goto fail; 482 } 483 if (flatcc_generate_files(ctx)) { 484 goto fail; 485 } 486 flatcc_destroy_context(ctx); 487 ctx = 0; 488 /* for --stdout and --outfile options: append to file and skip generating common headers. */ 489 opts.gen_append = 1; 490 } 491 goto done; 492 fail: 493 ret = -1; 494 done: 495 if (ctx) { 496 flatcc_destroy_context(ctx); 497 ctx = 0; 498 } 499 if (ret) { 500 fprintf(stderr, "output failed\n"); 501 } 502 free((void *)opts.inpaths); 503 free((void *)opts.srcpaths); 504 return ret; 505 }