kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

cpp.c (7292B)


      1 #include <kit/compile.h>
      2 #include <kit/core.h>
      3 #include <kit/preprocess.h>
      4 #include <stdint.h>
      5 
      6 #include "cflags.h"
      7 #include "driver.h"
      8 
      9 /* `kit cpp` — standalone C preprocessor. Reads one C source (path or
     10  * `-` for stdin), writes the preprocessed token stream to `-o PATH` or
     11  * stdout. Feature-equivalent to `kit cc -E` but without cc's source-
     12  * classification / link-input scaffolding.
     13  *
     14  * Flag surface is the cflags subset shared with cc + as:
     15  *   -I DIR, -IDIR        user include path
     16  *   -isystem DIR         system include path
     17  *   -D NAME[=BODY]       define
     18  *   -U NAME              undefine
     19  *   -o PATH              output (default: stdout)
     20  *   -target TRIPLE       cross-target (default: host)
     21  *   -                    stdin source
     22  *
     23  * The classical cpp-only flags (-P, -dM, -C, -CC) are not yet supported
     24  * by kit's preprocessor — they would land equally in `kit cc -E`. */
     25 
     26 #define CPP_TOOL "cpp"
     27 
     28 typedef struct CppOptions {
     29   const char* output_path;
     30   const char* source_path; /* NULL when stdin source */
     31   int from_stdin;
     32   KitTargetSpec target;
     33 } CppOptions;
     34 
     35 void driver_help_cpp(void) {
     36   driver_printf(
     37       "%.*s",
     38       KIT_SLICE_ARG(KIT_SLICE_LIT(
     39           "kit cpp — standalone C preprocessor\n"
     40           "\n"
     41           "USAGE\n"
     42           "  kit cpp [options] INPUT.c\n"
     43           "  kit cpp [options] -            (read from stdin)\n"
     44           "\n"
     45           "DESCRIPTION\n"
     46           "  Runs the C preprocessor on a single input and writes the "
     47           "resulting\n"
     48           "  token stream to -o (or stdout when -o is absent). Functionally\n"
     49           "  equivalent to `kit cc -E` but without cc's link/source-input\n"
     50           "  classification.\n"
     51           "\n"
     52           "OPTIONS\n"
     53           "  -o PATH           Output path (default: stdout)\n"
     54           "  -I DIR, -IDIR     Add a user include directory\n"
     55           "  -isystem DIR      Add a system include directory\n"
     56           "  -D NAME[=BODY]    Define a preprocessor macro\n"
     57           "  -U NAME           Undefine a preprocessor macro\n"
     58           "  -target TRIPLE    Cross-target. See `kit cc --help` for the\n"
     59           "                    accepted arches/OSes. Default: host.\n"
     60           "  -h, --help        Show this help and exit\n"
     61           "\n"
     62           "EXIT CODES\n"
     63           "  0   success           1   preprocess / I/O error           2   "
     64           "bad "
     65           "usage\n")));
     66 }
     67 
     68 static void cpp_usage(void) {
     69   driver_errf(CPP_TOOL, "%.*s",
     70               KIT_SLICE_ARG(KIT_SLICE_LIT(
     71                   "usage: kit cpp [options] INPUT.c\n"
     72                   "       kit cpp --help    for full option reference")));
     73 }
     74 
     75 static int cpp_parse(int argc, char** argv, CppOptions* o, DriverEnv* env,
     76                      DriverCflags* cf, DriverTargetFeatures* tf) {
     77   int i;
     78 
     79   o->target = driver_host_target();
     80 
     81   for (i = 1; i < argc; ++i) {
     82     const char* a = argv[i];
     83     int r;
     84 
     85     r = driver_cflags_try_consume(cf, env, CPP_TOOL, argc, argv, &i);
     86     if (r < 0) return 1;
     87     if (r > 0) continue;
     88 
     89     if (driver_streq(a, "-o")) {
     90       if (++i >= argc) {
     91         driver_errf(CPP_TOOL, "-o requires an argument");
     92         return 1;
     93       }
     94       o->output_path = argv[i];
     95       continue;
     96     }
     97 
     98     if (driver_streq(a, "-target")) {
     99       if (++i >= argc) {
    100         driver_errf(CPP_TOOL, "-target requires an argument");
    101         return 1;
    102       }
    103       if (driver_target_from_triple(argv[i], &o->target) != 0) {
    104         driver_errf(CPP_TOOL, "unrecognized target: %.*s",
    105                     KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
    106         return 1;
    107       }
    108       continue;
    109     }
    110 
    111     {
    112       int tr =
    113           driver_target_features_try_consume(tf, env, CPP_TOOL, argc, argv, &i);
    114       if (tr < 0) return 1;
    115       if (tr > 0) continue;
    116     }
    117 
    118     if (driver_streq(a, "-")) {
    119       if (o->source_path || o->from_stdin) {
    120         driver_errf(CPP_TOOL, "multiple inputs not supported");
    121         return 1;
    122       }
    123       o->from_stdin = 1;
    124       continue;
    125     }
    126 
    127     if (a[0] == '-' && a[1] != '\0') {
    128       driver_errf(CPP_TOOL, "unknown flag: %.*s",
    129                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    130       return 1;
    131     }
    132 
    133     if (o->source_path || o->from_stdin) {
    134       driver_errf(CPP_TOOL, "multiple inputs not supported");
    135       return 1;
    136     }
    137     o->source_path = a;
    138   }
    139 
    140   if (!o->source_path && !o->from_stdin) {
    141     driver_errf(CPP_TOOL, "no input file");
    142     cpp_usage();
    143     return 1;
    144   }
    145   return 0;
    146 }
    147 
    148 int driver_cpp(int argc, char** argv) {
    149   DriverEnv env;
    150   CppOptions o = {0};
    151   DriverCflags cf = {0};
    152   DriverTargetFeatures tf = {0};
    153   KitContext ctx;
    154   KitPreprocessOptions pp;
    155   KitTarget* target = NULL;
    156   KitCompiler* compiler = NULL;
    157   KitWriter* writer = NULL;
    158   KitFileData src = {0};
    159   KitSlice input;
    160   KitSlice input_name = KIT_SLICE_NULL;
    161   uint8_t* stdin_buf = NULL;
    162   size_t stdin_size = 0;
    163   int rc = 1;
    164   int loaded = 0;
    165 
    166   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    167     driver_help_cpp();
    168     return 0;
    169   }
    170 
    171   driver_env_init(&env);
    172 
    173   if (driver_cflags_init(&cf, &env, argc) != 0 ||
    174       driver_target_features_init(&tf, &env, argc) != 0) {
    175     driver_errf(CPP_TOOL, "out of memory");
    176     driver_target_features_fini(&tf, &env);
    177     driver_cflags_fini(&cf, &env);
    178     driver_env_fini(&env);
    179     return 2;
    180   }
    181 
    182   if (cpp_parse(argc, argv, &o, &env, &cf, &tf) != 0) {
    183     driver_target_features_fini(&tf, &env);
    184     driver_cflags_fini(&cf, &env);
    185     driver_env_fini(&env);
    186     return 2;
    187   }
    188   driver_cflags_fill_pp(&cf, &pp);
    189 
    190   ctx = driver_env_to_context(&env);
    191 
    192   if (o.from_stdin) {
    193     if (!driver_read_stdin(&env, &stdin_buf, &stdin_size)) {
    194       driver_errf(CPP_TOOL, "failed to read stdin");
    195       goto out;
    196     }
    197     input_name = KIT_SLICE_LIT("<stdin>");
    198     input.data = stdin_buf;
    199     input.len = stdin_size;
    200   } else {
    201     if (ctx.file_io->read_all(ctx.file_io->user, o.source_path, &src) !=
    202         KIT_OK) {
    203       driver_errf(CPP_TOOL, "failed to read: %.*s",
    204                   KIT_SLICE_ARG(kit_slice_cstr(o.source_path)));
    205       goto out;
    206     }
    207     loaded = 1;
    208     input_name = kit_slice_cstr(o.source_path);
    209     input.data = src.data;
    210     input.len = src.size;
    211   }
    212 
    213   if (o.output_path) {
    214     if (ctx.file_io->open_writer(ctx.file_io->user, o.output_path, &writer) !=
    215         KIT_OK) {
    216       driver_errf(CPP_TOOL, "failed to open output: %.*s",
    217                   KIT_SLICE_ARG(kit_slice_cstr(o.output_path)));
    218       goto out;
    219     }
    220   } else {
    221     writer = driver_stdout_writer(&env);
    222     if (!writer) {
    223       driver_errf(CPP_TOOL, "out of memory");
    224       goto out;
    225     }
    226   }
    227 
    228   if (driver_target_new(&ctx, o.target, &tf, CPP_TOOL, &target) != KIT_OK ||
    229       driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
    230     driver_errf(CPP_TOOL, "failed to initialize compiler");
    231     goto out;
    232   }
    233 
    234   rc = kit_cpp_preprocess(compiler, &pp, input_name, &input, writer) == KIT_OK
    235            ? 0
    236            : 1;
    237 
    238 out:
    239   if (compiler) driver_compiler_free(compiler);
    240   kit_target_free(target);
    241   if (writer) kit_writer_close(writer);
    242   if (loaded) ctx.file_io->release(ctx.file_io->user, &src);
    243   if (stdin_buf) driver_free(&env, stdin_buf, stdin_size);
    244   driver_target_features_fini(&tf, &env);
    245   driver_cflags_fini(&cf, &env);
    246   driver_env_fini(&env);
    247   return rc;
    248 }