preprocess.zig (5102B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const Allocator = std.mem.Allocator; 4 const cli = @import("cli.zig"); 5 const aro = @import("aro"); 6 7 const PreprocessError = error{ ArgError, GeneratedSourceError, PreprocessError, StreamTooLong, OutOfMemory }; 8 9 pub fn preprocess( 10 comp: *aro.Compilation, 11 writer: anytype, 12 /// Expects argv[0] to be the command name 13 argv: []const []const u8, 14 maybe_dependencies_list: ?*std.array_list.Managed([]const u8), 15 ) PreprocessError!void { 16 try comp.addDefaultPragmaHandlers(); 17 18 var driver: aro.Driver = .{ .comp = comp, .aro_name = "arocc" }; 19 defer driver.deinit(); 20 21 var macro_buf = std.array_list.Managed(u8).init(comp.gpa); 22 defer macro_buf.deinit(); 23 24 _ = driver.parseArgs(std.io.null_writer, macro_buf.writer(), argv) catch |err| switch (err) { 25 error.FatalError => return error.ArgError, 26 error.OutOfMemory => |e| return e, 27 }; 28 29 if (hasAnyErrors(comp)) return error.ArgError; 30 31 // .include_system_defines gives us things like _WIN32 32 const builtin_macros = comp.generateBuiltinMacros(.include_system_defines) catch |err| switch (err) { 33 error.FatalError => return error.GeneratedSourceError, 34 else => |e| return e, 35 }; 36 const user_macros = comp.addSourceFromBuffer("<command line>", macro_buf.items) catch |err| switch (err) { 37 error.FatalError => return error.GeneratedSourceError, 38 else => |e| return e, 39 }; 40 const source = driver.inputs.items[0]; 41 42 if (hasAnyErrors(comp)) return error.GeneratedSourceError; 43 44 comp.generated_buf.items.len = 0; 45 var pp = try aro.Preprocessor.initDefault(comp); 46 defer pp.deinit(); 47 48 if (comp.langopts.ms_extensions) { 49 comp.ms_cwd_source_id = source.id; 50 } 51 52 pp.preserve_whitespace = true; 53 pp.linemarkers = .line_directives; 54 55 pp.preprocessSources(&.{ source, builtin_macros, user_macros }) catch |err| switch (err) { 56 error.FatalError => return error.PreprocessError, 57 else => |e| return e, 58 }; 59 60 if (hasAnyErrors(comp)) return error.PreprocessError; 61 62 try pp.prettyPrintTokens(writer, .result_only); 63 64 if (maybe_dependencies_list) |dependencies_list| { 65 for (comp.sources.values()) |comp_source| { 66 if (comp_source.id == builtin_macros.id or comp_source.id == user_macros.id) continue; 67 if (comp_source.id == .unused or comp_source.id == .generated) continue; 68 const duped_path = try dependencies_list.allocator.dupe(u8, comp_source.path); 69 errdefer dependencies_list.allocator.free(duped_path); 70 try dependencies_list.append(duped_path); 71 } 72 } 73 } 74 75 fn hasAnyErrors(comp: *aro.Compilation) bool { 76 // In theory we could just check Diagnostics.errors != 0, but that only 77 // gets set during rendering of the error messages, see: 78 // https://github.com/Vexu/arocc/issues/603 79 for (comp.diagnostics.list.items) |msg| { 80 switch (msg.kind) { 81 .@"fatal error", .@"error" => return true, 82 else => {}, 83 } 84 } 85 return false; 86 } 87 88 /// `arena` is used for temporary -D argument strings and the INCLUDE environment variable. 89 /// The arena should be kept alive at least as long as `argv`. 90 pub fn appendAroArgs(arena: Allocator, argv: *std.array_list.Managed([]const u8), options: cli.Options, system_include_paths: []const []const u8) !void { 91 try argv.appendSlice(&.{ 92 "-E", 93 "--comments", 94 "-fuse-line-directives", 95 "--target=x86_64-windows-msvc", 96 "--emulate=msvc", 97 "-nostdinc", 98 "-DRC_INVOKED", 99 "-D_WIN32", // undocumented, but defined by default 100 }); 101 for (options.extra_include_paths.items) |extra_include_path| { 102 try argv.append("-I"); 103 try argv.append(extra_include_path); 104 } 105 106 for (system_include_paths) |include_path| { 107 try argv.append("-isystem"); 108 try argv.append(include_path); 109 } 110 111 if (!options.ignore_include_env_var) { 112 const INCLUDE = std.process.getEnvVarOwned(arena, "INCLUDE") catch ""; 113 114 // The only precedence here is llvm-rc which also uses the platform-specific 115 // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. 116 const delimiter = switch (builtin.os.tag) { 117 .windows => ';', 118 else => ':', 119 }; 120 var it = std.mem.tokenizeScalar(u8, INCLUDE, delimiter); 121 while (it.next()) |include_path| { 122 try argv.append("-isystem"); 123 try argv.append(include_path); 124 } 125 } 126 127 var symbol_it = options.symbols.iterator(); 128 while (symbol_it.next()) |entry| { 129 switch (entry.value_ptr.*) { 130 .define => |value| { 131 try argv.append("-D"); 132 const define_arg = try std.fmt.allocPrint(arena, "{s}={s}", .{ entry.key_ptr.*, value }); 133 try argv.append(define_arg); 134 }, 135 .undefine => { 136 try argv.append("-U"); 137 try argv.append(entry.key_ptr.*); 138 }, 139 } 140 } 141 }