kit

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

cpp.c (10734B)


      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 #include "hosted.h"
      9 #include "runtime.h"
     10 
     11 /* `kit cpp` — standalone C preprocessor. Reads one C source (path or
     12  * `-` for stdin), writes the preprocessed token stream to `-o PATH` or
     13  * stdout. Feature-equivalent to `kit cc -E` but without cc's source-
     14  * classification / link-input scaffolding.
     15  *
     16  * Flag surface is the cflags subset shared with cc + as:
     17  *   -I DIR, -IDIR        user include path
     18  *   -isystem DIR         system include path
     19  *   -D NAME[=BODY]       define
     20  *   -U NAME              undefine
     21  *   -o PATH              output (default: stdout)
     22  *   -target TRIPLE       cross-target (default: host)
     23  *   -                    stdin source
     24  *
     25  * The classical cpp-only flags (-P, -dM, -C, -CC) are not yet supported
     26  * by kit's preprocessor — they would land equally in `kit cc -E`. */
     27 
     28 #define CPP_TOOL "cpp"
     29 
     30 typedef struct CppOptions {
     31   const char* output_path;
     32   const char* source_path; /* NULL when stdin source */
     33   int from_stdin;
     34   int freestanding;
     35   int nostdinc;
     36   const char* sysroot;
     37   const char* support_dir;
     38   KitTargetSpec target;
     39 } CppOptions;
     40 
     41 void driver_help_cpp(void) {
     42   driver_printf(
     43       "%.*s",
     44       KIT_SLICE_ARG(KIT_SLICE_LIT(
     45           "kit cpp — standalone C preprocessor\n"
     46           "\n"
     47           "USAGE\n"
     48           "  kit cpp [options] INPUT.c\n"
     49           "  kit cpp [options] -            (read from stdin)\n"
     50           "\n"
     51           "DESCRIPTION\n"
     52           "  Runs the C preprocessor on a single input and writes the "
     53           "resulting\n"
     54           "  token stream to -o (or stdout when -o is absent). Functionally\n"
     55           "  equivalent to `kit cc -E` but without cc's link/source-input\n"
     56           "  classification.\n"
     57           "\n"
     58           "OPTIONS\n"
     59           "  -o PATH           Output path (default: stdout)\n"
     60           "  -I DIR, -IDIR     Add a user include directory\n"
     61           "  -isystem DIR      Add a system include directory\n"
     62           "  -D NAME[=BODY]    Define a preprocessor macro\n"
     63           "  -U NAME           Undefine a preprocessor macro\n"
     64           "  -target TRIPLE    Cross-target (default: host)\n"
     65           "  -h, --help        Show this help and exit\n"
     66           "  --version         Show Kit version and exit\n"
     67           "\n"
     68           "TARGETS\n"
     69           "  Hosted triples: aarch64/x86_64/riscv64 with linux-gnu,\n"
     70           "  linux-musl, or freebsd where listed by the release;\n"
     71           "  aarch64/x86_64-windows, aarch64-linux-android21, and\n"
     72           "  aarch64/x86_64-apple-darwin. Freestanding triples are\n"
     73           "  aarch64-none-elf, x86_64-none-elf, riscv64-none-elf,\n"
     74           "  riscv32-none-elf, and arm-none-eabi. Hosted cross headers require\n"
     75           "  explicit -isystem/-I paths or the sysroot workflow in `kit cc`.\n"
     76           "\n"
     77           "INPUT/OUTPUT NOTES\n"
     78           "  `-` is accepted as stdin. A leading-dash file is not introduced\n"
     79           "  with `--` in this release; spell it as ./-name.c. Normal output\n"
     80           "  is stdout, so diagnostics remain on stderr when output is piped.\n"
     81           "\n"
     82           "EXAMPLES\n"
     83           "  kit cpp -I include -DVALUE=42 source.c -o source.i\n"
     84           "  printf '#if FLAG\\nok\\n#endif\\n' | kit cpp -DFLAG=1 -\n"
     85           "  kit cpp ./-generated.c\n"
     86           "\n"
     87           "EXIT CODES\n"
     88           "  0   success           1   preprocess / I/O error           2   "
     89           "bad "
     90           "usage\n")));
     91 }
     92 
     93 static void cpp_usage(void) {
     94   driver_errf(CPP_TOOL, "%.*s",
     95               KIT_SLICE_ARG(KIT_SLICE_LIT(
     96                   "usage: kit cpp [options] INPUT.c\n"
     97                   "       kit cpp --help    for full option reference")));
     98 }
     99 
    100 static int cpp_parse(int argc, char** argv, CppOptions* o, DriverEnv* env,
    101                      DriverCflags* cf, DriverTargetFeatures* tf) {
    102   int i;
    103 
    104   o->target = driver_host_target();
    105 
    106   for (i = 1; i < argc; ++i) {
    107     const char* a = argv[i];
    108     int r;
    109 
    110     r = driver_cflags_try_consume(cf, env, CPP_TOOL, argc, argv, &i);
    111     if (r < 0) return 1;
    112     if (r > 0) continue;
    113 
    114     if (driver_streq(a, "-o")) {
    115       if (++i >= argc) {
    116         driver_errf(CPP_TOOL, "-o requires an argument");
    117         return 1;
    118       }
    119       o->output_path = argv[i];
    120       continue;
    121     }
    122 
    123     if (driver_streq(a, "-target")) {
    124       if (++i >= argc) {
    125         driver_errf(CPP_TOOL, "-target requires an argument");
    126         return 1;
    127       }
    128       if (driver_target_from_triple(argv[i], &o->target) != 0) {
    129         driver_err_unknown_target(CPP_TOOL, argv[i]);
    130         return 1;
    131       }
    132       continue;
    133     }
    134 
    135     if (driver_streq(a, "--sysroot") || driver_streq(a, "-isysroot")) {
    136       if (++i >= argc) {
    137         driver_errf(CPP_TOOL, "%s requires an argument", a);
    138         return 1;
    139       }
    140       o->sysroot = argv[i];
    141       continue;
    142     }
    143     if (driver_strneq(a, "--sysroot=", 10)) {
    144       o->sysroot = a + 10;
    145       continue;
    146     }
    147     if (driver_streq(a, "--support-dir")) {
    148       if (++i >= argc) {
    149         driver_errf(CPP_TOOL, "--support-dir requires an argument");
    150         return 1;
    151       }
    152       o->support_dir = argv[i];
    153       continue;
    154     }
    155     if (driver_strneq(a, "--support-dir=", 14)) {
    156       o->support_dir = a + 14;
    157       continue;
    158     }
    159     if (driver_streq(a, "-ffreestanding")) {
    160       o->freestanding = 1;
    161       continue;
    162     }
    163     if (driver_streq(a, "-fhosted")) {
    164       o->freestanding = 0;
    165       continue;
    166     }
    167     if (driver_streq(a, "-nostdinc")) {
    168       o->nostdinc = 1;
    169       continue;
    170     }
    171 
    172     {
    173       int tr =
    174           driver_target_features_try_consume(tf, env, CPP_TOOL, argc, argv, &i);
    175       if (tr < 0) return 1;
    176       if (tr > 0) continue;
    177     }
    178 
    179     if (driver_streq(a, "-")) {
    180       if (o->source_path || o->from_stdin) {
    181         driver_errf(CPP_TOOL, "multiple inputs not supported");
    182         return 1;
    183       }
    184       o->from_stdin = 1;
    185       continue;
    186     }
    187 
    188     if (a[0] == '-' && a[1] != '\0') {
    189       driver_errf(CPP_TOOL, "unknown flag: %.*s",
    190                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    191       return 1;
    192     }
    193 
    194     if (o->source_path || o->from_stdin) {
    195       driver_errf(CPP_TOOL, "multiple inputs not supported");
    196       return 1;
    197     }
    198     o->source_path = a;
    199   }
    200 
    201   if (!o->source_path && !o->from_stdin) {
    202     driver_errf(CPP_TOOL, "no input file");
    203     cpp_usage();
    204     return 1;
    205   }
    206   return 0;
    207 }
    208 
    209 int driver_cpp(int argc, char** argv) {
    210   DriverEnv env;
    211   CppOptions o = {0};
    212   DriverCflags cf = {0};
    213   DriverTargetFeatures tf = {0};
    214   DriverHostedPlan hosted = {0};
    215   DriverRuntimeSupport runtime = {0};
    216   KitContext ctx;
    217   KitPreprocessOptions pp;
    218   KitTarget* target = NULL;
    219   KitCompiler* compiler = NULL;
    220   KitWriter* writer = NULL;
    221   KitFileData src = {0};
    222   KitSlice input;
    223   KitSlice input_name = KIT_SLICE_NULL;
    224   uint8_t* stdin_buf = NULL;
    225   size_t stdin_size = 0;
    226   int rc = 1;
    227   int loaded = 0;
    228   int runtime_resolved = 0;
    229   int hosted_engaged = 0;
    230 
    231   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    232     driver_help_cpp();
    233     return 0;
    234   }
    235 
    236   driver_env_init(&env);
    237 
    238   if (driver_cflags_init(&cf, &env,
    239                          argc + DRIVER_HOSTED_MAX_DEFINES +
    240                              DRIVER_HOSTED_MAX_INCLUDES) != 0 ||
    241       driver_target_features_init(&tf, &env, argc) != 0) {
    242     driver_errf(CPP_TOOL, "out of memory");
    243     driver_target_features_fini(&tf, &env);
    244     driver_cflags_fini(&cf, &env);
    245     driver_env_fini(&env);
    246     return 2;
    247   }
    248 
    249   if (cpp_parse(argc, argv, &o, &env, &cf, &tf) != 0) {
    250     driver_target_features_fini(&tf, &env);
    251     driver_cflags_fini(&cf, &env);
    252     driver_env_fini(&env);
    253     return 2;
    254   }
    255   ctx = driver_env_to_context(&env);
    256 
    257   if (!o.freestanding && !o.nostdinc &&
    258       (o.sysroot || driver_target_default_hosted_profile(o.target))) {
    259     DriverHostedRequest req = {0};
    260     uint32_t i;
    261     req.env = &env;
    262     req.tool = CPP_TOOL;
    263     req.target = o.target;
    264     req.sysroot = o.sysroot;
    265     req.link_inputs = 0;
    266     if (driver_hosted_resolve(&req, &hosted) != 0) goto out;
    267     hosted_engaged = hosted.profile_name != NULL;
    268     for (i = 0; i < hosted.nsystem_includes; ++i)
    269       cf.system_include_dirs[cf.nsystem_include_dirs++] =
    270           hosted.system_includes[i];
    271     for (i = 0; i < hosted.ndefines; ++i)
    272       cf.defines[cf.ndefines++] = hosted.defines[i];
    273   }
    274 
    275   if (driver_runtime_resolve(&env, o.support_dir, argv[0], &runtime) != 0) {
    276     driver_errf(CPP_TOOL, "support dir not found");
    277     goto out;
    278   }
    279   runtime_resolved = 1;
    280   if (!o.nostdinc &&
    281       (hosted_engaged
    282            ? driver_runtime_append_freestanding_headers(&runtime, &cf)
    283            : driver_runtime_add_freestanding_headers(&runtime, &cf)) != 0) {
    284     driver_errf(CPP_TOOL, "failed to add freestanding headers");
    285     goto out;
    286   }
    287   driver_cflags_fill_pp(&cf, &pp);
    288 
    289   if (o.from_stdin) {
    290     if (!driver_read_stdin(&env, &stdin_buf, &stdin_size)) {
    291       driver_errf(CPP_TOOL, "failed to read stdin");
    292       goto out;
    293     }
    294     input_name = KIT_SLICE_LIT("<stdin>");
    295     input.data = stdin_buf;
    296     input.len = stdin_size;
    297   } else {
    298     if (ctx.file_io->read_all(ctx.file_io->user, o.source_path, &src) !=
    299         KIT_OK) {
    300       driver_errf(CPP_TOOL, "failed to read: %.*s",
    301                   KIT_SLICE_ARG(kit_slice_cstr(o.source_path)));
    302       goto out;
    303     }
    304     loaded = 1;
    305     input_name = kit_slice_cstr(o.source_path);
    306     input.data = src.data;
    307     input.len = src.size;
    308   }
    309 
    310   if (o.output_path) {
    311     if (ctx.file_io->open_writer(ctx.file_io->user, o.output_path, &writer) !=
    312         KIT_OK) {
    313       driver_errf(CPP_TOOL, "failed to open output: %.*s",
    314                   KIT_SLICE_ARG(kit_slice_cstr(o.output_path)));
    315       goto out;
    316     }
    317   } else {
    318     writer = driver_stdout_writer(&env);
    319     if (!writer) {
    320       driver_errf(CPP_TOOL, "out of memory");
    321       goto out;
    322     }
    323   }
    324 
    325   if (driver_target_new(&ctx, o.target, &tf, CPP_TOOL, &target) != KIT_OK ||
    326       driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
    327     driver_errf(CPP_TOOL, "failed to initialize compiler");
    328     goto out;
    329   }
    330 
    331   rc = kit_cpp_preprocess(compiler, &pp, input_name, &input, writer) == KIT_OK
    332            ? 0
    333            : 1;
    334 
    335 out:
    336   if (compiler) driver_compiler_free(compiler);
    337   kit_target_free(target);
    338   if (writer) kit_writer_close(writer);
    339   if (loaded) ctx.file_io->release(ctx.file_io->user, &src);
    340   if (stdin_buf) driver_free(&env, stdin_buf, stdin_size);
    341   if (runtime_resolved) driver_runtime_support_fini(&env, &runtime);
    342   driver_hosted_plan_fini(&env, &hosted);
    343   driver_target_features_fini(&tf, &env);
    344   driver_cflags_fini(&cf, &env);
    345   driver_env_fini(&env);
    346   return rc;
    347 }