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 }