blob f3ebde58 (29853B) - Raw
1 const std = @import("std"); 2 const mem = std.mem; 3 const Allocator = mem.Allocator; 4 const process = std.process; 5 const Codegen = @import("Codegen_legacy.zig"); 6 const Compilation = @import("Compilation.zig"); 7 const LangOpts = @import("LangOpts.zig"); 8 const Preprocessor = @import("Preprocessor.zig"); 9 const Parser = @import("Parser.zig"); 10 const Source = @import("Source.zig"); 11 const Toolchain = @import("Toolchain.zig"); 12 const util = @import("util.zig"); 13 const target_util = @import("target.zig"); 14 15 const Driver = @This(); 16 17 pub const Linker = enum { 18 ld, 19 bfd, 20 gold, 21 lld, 22 mold, 23 }; 24 25 comp: *Compilation, 26 inputs: std.ArrayListUnmanaged(Source) = .{}, 27 link_objects: std.ArrayListUnmanaged([]const u8) = .{}, 28 output_name: ?[]const u8 = null, 29 sysroot: ?[]const u8 = null, 30 temp_file_count: u32 = 0, 31 only_preprocess: bool = false, 32 only_syntax: bool = false, 33 only_compile: bool = false, 34 only_preprocess_and_compile: bool = false, 35 verbose_ast: bool = false, 36 verbose_pp: bool = false, 37 verbose_ir: bool = false, 38 verbose_linker_args: bool = false, 39 40 /// Full path to the aro executable 41 aro_name: []const u8 = "", 42 43 /// Value of --triple= passed via CLI 44 raw_target_triple: ?[]const u8 = null, 45 46 // linker options 47 use_linker: ?[]const u8 = null, 48 linker_path: ?[]const u8 = null, 49 nodefaultlibs: bool = false, 50 nolibc: bool = false, 51 nostartfiles: bool = false, 52 nostdlib: bool = false, 53 pie: ?bool = null, 54 rdynamic: bool = false, 55 relocatable: bool = false, 56 rtlib: ?[]const u8 = null, 57 shared: bool = false, 58 shared_libgcc: bool = false, 59 static: bool = false, 60 static_libgcc: bool = false, 61 static_pie: bool = false, 62 strip: bool = false, 63 unwindlib: ?[]const u8 = null, 64 65 pub fn deinit(d: *Driver) void { 66 for (d.link_objects.items[d.link_objects.items.len - d.temp_file_count ..]) |obj| { 67 std.fs.deleteFileAbsolute(obj) catch {}; 68 d.comp.gpa.free(obj); 69 } 70 d.inputs.deinit(d.comp.gpa); 71 d.link_objects.deinit(d.comp.gpa); 72 d.* = undefined; 73 } 74 75 pub const usage = 76 \\Usage {s}: [options] file.. 77 \\ 78 \\General options: 79 \\ -h, --help Print this message. 80 \\ -v, --version Print aro version. 81 \\ 82 \\Compile options: 83 \\ -c, --compile Only run preprocess, compile, and assemble steps 84 \\ -D <macro>=<value> Define <macro> to <value> (defaults to 1) 85 \\ -E Only run the preprocessor 86 \\ -fchar8_t Enable char8_t (enabled by default in C2X and later) 87 \\ -fno-char8_t Disable char8_t (disabled by default for pre-C2X) 88 \\ -fcolor-diagnostics Enable colors in diagnostics 89 \\ -fno-color-diagnostics Disable colors in diagnostics 90 \\ -fdeclspec Enable support for __declspec attributes 91 \\ -fno-declspec Disable support for __declspec attributes 92 \\ -ffp-eval-method=[source|double|extended] 93 \\ Evaluation method to use for floating-point arithmetic 94 \\ -fgnu-inline-asm Enable GNU style inline asm (default: enabled) 95 \\ -fno-gnu-inline-asm Disable GNU style inline asm 96 \\ -fms-extensions Enable support for Microsoft extensions 97 \\ -fno-ms-extensions Disable support for Microsoft extensions 98 \\ -fdollars-in-identifiers 99 \\ Allow '$' in identifiers 100 \\ -fno-dollars-in-identifiers 101 \\ Disallow '$' in identifiers 102 \\ -fmacro-backtrace-limit=<limit> 103 \\ Set limit on how many macro expansion traces are shown in errors (default 6) 104 \\ -fnative-half-type Use the native half type for __fp16 instead of promoting to float 105 \\ -fnative-half-arguments-and-returns 106 \\ Allow half-precision function arguments and return values 107 \\ -fshort-enums Use the narrowest possible integer type for enums 108 \\ -fno-short-enums Use "int" as the tag type for enums 109 \\ -fsigned-char "char" is signed 110 \\ -fno-signed-char "char" is unsigned 111 \\ -fsyntax-only Only run the preprocessor, parser, and semantic analysis stages 112 \\ -funsigned-char "char" is unsigned 113 \\ -fno-unsigned-char "char" is signed 114 \\ -I <dir> Add directory to include search path 115 \\ -isystem Add directory to SYSTEM include search path 116 \\ --emulate=[clang|gcc|msvc] 117 \\ Select which C compiler to emulate (default clang) 118 \\ -o <file> Write output to <file> 119 \\ -pedantic Warn on language extensions 120 \\ --rtlib=<arg> Compiler runtime library to use (libgcc or compiler-rt) 121 \\ -std=<standard> Specify language standard 122 \\ -S, --assemble Only run preprocess and compilation steps 123 \\ --sysroot=<dir> Use dir as the logical root directory for headers and libraries (not fully implemented) 124 \\ --target=<value> Generate code for the given target 125 \\ -U <macro> Undefine <macro> 126 \\ -Werror Treat all warnings as errors 127 \\ -Werror=<warning> Treat warning as error 128 \\ -W<warning> Enable the specified warning 129 \\ -Wno-<warning> Disable the specified warning 130 \\ 131 \\Link options: 132 \\ -fuse-ld=[bfd|gold|lld|mold] 133 \\ Use specific linker 134 \\ -nodefaultlibs Do not use the standard system libraries when linking. 135 \\ -nolibc Do not use the C library or system libraries tightly coupled with it when linking. 136 \\ -nostdlib Do not use the standard system startup files or libraries when linking 137 \\ -nostartfiles Do not use the standard system startup files when linking. 138 \\ -pie Produce a dynamically linked position independent executable on targets that support it. 139 \\ --ld-path=<path> Use linker specified by <path> 140 \\ -r Produce a relocatable object as output. 141 \\ -rdynamic Pass the flag -export-dynamic to the ELF linker, on targets that support it. 142 \\ -s Remove all symbol table and relocation information from the executable. 143 \\ -shared Produce a shared object which can then be linked with other objects to form an executable. 144 \\ -shared-libgcc On systems that provide libgcc as a shared library, force the use of the shared version 145 \\ -static On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. 146 \\ -static-libgcc On systems that provide libgcc as a shared library, force the use of the static version 147 \\ -static-pie Produce a static position independent executable on targets that support it. 148 \\ --unwindlib=<arg> Unwind library to use ("none", "libgcc", or "libunwind") If not specified, will match runtime library 149 \\ 150 \\Debug options: 151 \\ --verbose-ast Dump produced AST to stdout 152 \\ --verbose-pp Dump preprocessor state 153 \\ --verbose-ir Dump ir to stdout 154 \\ --verbose-linker-args Dump linker args to stdout 155 \\ 156 \\ 157 ; 158 159 /// Process command line arguments, returns true if something was written to std_out. 160 pub fn parseArgs( 161 d: *Driver, 162 std_out: anytype, 163 macro_buf: anytype, 164 args: []const []const u8, 165 ) !bool { 166 var i: usize = 1; 167 var color_setting: enum { 168 on, 169 off, 170 unset, 171 } = .unset; 172 while (i < args.len) : (i += 1) { 173 const arg = args[i]; 174 if (mem.startsWith(u8, arg, "-") and arg.len > 1) { 175 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 176 std_out.print(usage, .{args[0]}) catch |er| { 177 return d.fatal("unable to print usage: {s}", .{util.errorDescription(er)}); 178 }; 179 return true; 180 } else if (mem.eql(u8, arg, "-v") or mem.eql(u8, arg, "--version")) { 181 std_out.writeAll(@import("lib.zig").version_str ++ "\n") catch |er| { 182 return d.fatal("unable to print version: {s}", .{util.errorDescription(er)}); 183 }; 184 return true; 185 } else if (mem.startsWith(u8, arg, "-D")) { 186 var macro = arg["-D".len..]; 187 if (macro.len == 0) { 188 i += 1; 189 if (i >= args.len) { 190 try d.err("expected argument after -I"); 191 continue; 192 } 193 macro = args[i]; 194 } 195 var value: []const u8 = "1"; 196 if (mem.indexOfScalar(u8, macro, '=')) |some| { 197 value = macro[some + 1 ..]; 198 macro = macro[0..some]; 199 } 200 try macro_buf.print("#define {s} {s}\n", .{ macro, value }); 201 } else if (mem.startsWith(u8, arg, "-U")) { 202 var macro = arg["-U".len..]; 203 if (macro.len == 0) { 204 i += 1; 205 if (i >= args.len) { 206 try d.err("expected argument after -I"); 207 continue; 208 } 209 macro = args[i]; 210 } 211 try macro_buf.print("#undef {s}\n", .{macro}); 212 } else if (mem.eql(u8, arg, "-c") or mem.eql(u8, arg, "--compile")) { 213 d.only_compile = true; 214 } else if (mem.eql(u8, arg, "-E")) { 215 d.only_preprocess = true; 216 } else if (mem.eql(u8, arg, "-fchar8_t")) { 217 d.comp.langopts.has_char8_t_override = true; 218 } else if (mem.eql(u8, arg, "-fno-char8_t")) { 219 d.comp.langopts.has_char8_t_override = false; 220 } else if (mem.eql(u8, arg, "-fcolor-diagnostics")) { 221 color_setting = .on; 222 } else if (mem.eql(u8, arg, "-fno-color-diagnostics")) { 223 color_setting = .off; 224 } else if (mem.eql(u8, arg, "-fdollars-in-identifiers")) { 225 d.comp.langopts.dollars_in_identifiers = true; 226 } else if (mem.eql(u8, arg, "-fno-dollars-in-identifiers")) { 227 d.comp.langopts.dollars_in_identifiers = false; 228 } else if (mem.eql(u8, arg, "-fdigraphs")) { 229 d.comp.langopts.digraphs = true; 230 } else if (mem.eql(u8, arg, "-fgnu-inline-asm")) { 231 d.comp.langopts.gnu_asm = true; 232 } else if (mem.eql(u8, arg, "-fno-gnu-inline-asm")) { 233 d.comp.langopts.gnu_asm = false; 234 } else if (mem.eql(u8, arg, "-fno-digraphs")) { 235 d.comp.langopts.digraphs = false; 236 } else if (option(arg, "-fmacro-backtrace-limit=")) |limit_str| { 237 var limit = std.fmt.parseInt(u32, limit_str, 10) catch { 238 try d.err("-fmacro-backtrace-limit takes a number argument"); 239 continue; 240 }; 241 242 if (limit == 0) limit = std.math.maxInt(u32); 243 d.comp.diag.macro_backtrace_limit = limit; 244 } else if (mem.eql(u8, arg, "-fnative-half-type")) { 245 d.comp.langopts.use_native_half_type = true; 246 } else if (mem.eql(u8, arg, "-fnative-half-arguments-and-returns")) { 247 d.comp.langopts.allow_half_args_and_returns = true; 248 } else if (mem.eql(u8, arg, "-fshort-enums")) { 249 d.comp.langopts.short_enums = true; 250 } else if (mem.eql(u8, arg, "-fno-short-enums")) { 251 d.comp.langopts.short_enums = false; 252 } else if (mem.eql(u8, arg, "-fsigned-char")) { 253 d.comp.langopts.setCharSignedness(.signed); 254 } else if (mem.eql(u8, arg, "-fno-signed-char")) { 255 d.comp.langopts.setCharSignedness(.unsigned); 256 } else if (mem.eql(u8, arg, "-funsigned-char")) { 257 d.comp.langopts.setCharSignedness(.unsigned); 258 } else if (mem.eql(u8, arg, "-fno-unsigned-char")) { 259 d.comp.langopts.setCharSignedness(.signed); 260 } else if (mem.eql(u8, arg, "-fdeclspec")) { 261 d.comp.langopts.declspec_attrs = true; 262 } else if (mem.eql(u8, arg, "-fno-declspec")) { 263 d.comp.langopts.declspec_attrs = false; 264 } else if (mem.eql(u8, arg, "-fms-extensions")) { 265 d.comp.langopts.enableMSExtensions(); 266 } else if (mem.eql(u8, arg, "-fno-ms-extensions")) { 267 d.comp.langopts.disableMSExtensions(); 268 } else if (mem.startsWith(u8, arg, "-I")) { 269 var path = arg["-I".len..]; 270 if (path.len == 0) { 271 i += 1; 272 if (i >= args.len) { 273 try d.err("expected argument after -I"); 274 continue; 275 } 276 path = args[i]; 277 } 278 try d.comp.include_dirs.append(path); 279 } else if (mem.startsWith(u8, arg, "-fsyntax-only")) { 280 d.only_syntax = true; 281 } else if (mem.startsWith(u8, arg, "-fno-syntax-only")) { 282 d.only_syntax = false; 283 } else if (mem.startsWith(u8, arg, "-isystem")) { 284 var path = arg["-isystem".len..]; 285 if (path.len == 0) { 286 i += 1; 287 if (i >= args.len) { 288 try d.err("expected argument after -isystem"); 289 continue; 290 } 291 path = args[i]; 292 } 293 const duped = try d.comp.gpa.dupe(u8, path); 294 errdefer d.comp.gpa.free(duped); 295 try d.comp.system_include_dirs.append(duped); 296 } else if (option(arg, "--emulate=")) |compiler_str| { 297 const compiler = std.meta.stringToEnum(LangOpts.Compiler, compiler_str) orelse { 298 try d.comp.diag.add(.{ .tag = .cli_invalid_emulate, .extra = .{ .str = arg } }, &.{}); 299 continue; 300 }; 301 d.comp.langopts.setEmulatedCompiler(compiler); 302 } else if (option(arg, "-ffp-eval-method=")) |fp_method_str| { 303 const fp_eval_method = std.meta.stringToEnum(LangOpts.FPEvalMethod, fp_method_str) orelse .indeterminate; 304 if (fp_eval_method == .indeterminate) { 305 try d.comp.diag.add(.{ .tag = .cli_invalid_fp_eval_method, .extra = .{ .str = fp_method_str } }, &.{}); 306 continue; 307 } 308 d.comp.langopts.setFpEvalMethod(fp_eval_method); 309 } else if (mem.startsWith(u8, arg, "-o")) { 310 var file = arg["-o".len..]; 311 if (file.len == 0) { 312 i += 1; 313 if (i >= args.len) { 314 try d.err("expected argument after -o"); 315 continue; 316 } 317 file = args[i]; 318 } 319 d.output_name = file; 320 } else if (option(arg, "--sysroot=")) |sysroot| { 321 d.sysroot = sysroot; 322 } else if (mem.eql(u8, arg, "-pedantic")) { 323 d.comp.diag.options.pedantic = .warning; 324 } else if (option(arg, "--rtlib=")) |rtlib| { 325 if (mem.eql(u8, rtlib, "compiler-rt") or mem.eql(u8, rtlib, "libgcc") or mem.eql(u8, rtlib, "platform")) { 326 d.rtlib = rtlib; 327 } else { 328 try d.comp.diag.add(.{ .tag = .invalid_rtlib, .extra = .{ .str = rtlib } }, &.{}); 329 } 330 } else if (option(arg, "-Werror=")) |err_name| { 331 try d.comp.diag.set(err_name, .@"error"); 332 } else if (mem.eql(u8, arg, "-Wno-fatal-errors")) { 333 d.comp.diag.fatal_errors = false; 334 } else if (option(arg, "-Wno-")) |err_name| { 335 try d.comp.diag.set(err_name, .off); 336 } else if (mem.eql(u8, arg, "-Wfatal-errors")) { 337 d.comp.diag.fatal_errors = true; 338 } else if (option(arg, "-W")) |err_name| { 339 try d.comp.diag.set(err_name, .warning); 340 } else if (option(arg, "-std=")) |standard| { 341 d.comp.langopts.setStandard(standard) catch 342 try d.comp.diag.add(.{ .tag = .cli_invalid_standard, .extra = .{ .str = arg } }, &.{}); 343 } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--assemble")) { 344 d.only_preprocess_and_compile = true; 345 } else if (option(arg, "--target=")) |triple| { 346 const cross = std.zig.CrossTarget.parse(.{ .arch_os_abi = triple }) catch { 347 try d.comp.diag.add(.{ .tag = .cli_invalid_target, .extra = .{ .str = arg } }, &.{}); 348 continue; 349 }; 350 d.comp.target = cross.toTarget(); // TODO deprecated 351 d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(d.comp.target)); 352 d.raw_target_triple = triple; 353 } else if (mem.eql(u8, arg, "--verbose-ast")) { 354 d.verbose_ast = true; 355 } else if (mem.eql(u8, arg, "--verbose-pp")) { 356 d.verbose_pp = true; 357 } else if (mem.eql(u8, arg, "--verbose-ir")) { 358 d.verbose_ir = true; 359 } else if (mem.eql(u8, arg, "--verbose-linker-args")) { 360 d.verbose_linker_args = true; 361 } else if (option(arg, "-fuse-ld=")) |linker_name| { 362 d.use_linker = linker_name; 363 } else if (mem.eql(u8, arg, "-fuse-ld=")) { 364 d.use_linker = null; 365 } else if (option(arg, "--ld-path=")) |linker_path| { 366 d.linker_path = linker_path; 367 } else if (mem.eql(u8, arg, "-r")) { 368 d.relocatable = true; 369 } else if (mem.eql(u8, arg, "-shared")) { 370 d.shared = true; 371 } else if (mem.eql(u8, arg, "-shared-libgcc")) { 372 d.shared_libgcc = true; 373 } else if (mem.eql(u8, arg, "-static")) { 374 d.static = true; 375 } else if (mem.eql(u8, arg, "-static-libgcc")) { 376 d.static_libgcc = true; 377 } else if (mem.eql(u8, arg, "-static-pie")) { 378 d.static_pie = true; 379 } else if (mem.eql(u8, arg, "-pie")) { 380 d.pie = true; 381 } else if (mem.eql(u8, arg, "-no-pie") or mem.eql(u8, arg, "-nopie")) { 382 d.pie = false; 383 } else if (mem.eql(u8, arg, "-rdynamic")) { 384 d.rdynamic = true; 385 } else if (mem.eql(u8, arg, "-s")) { 386 d.strip = true; 387 } else if (mem.eql(u8, arg, "-nodefaultlibs")) { 388 d.nodefaultlibs = true; 389 } else if (mem.eql(u8, arg, "-nolibc")) { 390 d.nolibc = true; 391 } else if (mem.eql(u8, arg, "-nostdlib")) { 392 d.nostdlib = true; 393 } else if (mem.eql(u8, arg, "-nostartfiles")) { 394 d.nostartfiles = true; 395 } else if (option(arg, "--unwindlib=")) |unwindlib| { 396 const valid_unwindlibs: [5][]const u8 = .{ "", "none", "platform", "libunwind", "libgcc" }; 397 for (valid_unwindlibs) |name| { 398 if (mem.eql(u8, name, unwindlib)) { 399 d.unwindlib = unwindlib; 400 break; 401 } 402 } else { 403 try d.comp.diag.add(.{ .tag = .invalid_unwindlib, .extra = .{ .str = unwindlib } }, &.{}); 404 } 405 } else { 406 try d.comp.diag.add(.{ .tag = .cli_unknown_arg, .extra = .{ .str = arg } }, &.{}); 407 } 408 } else if (std.mem.endsWith(u8, arg, ".o") or std.mem.endsWith(u8, arg, ".obj")) { 409 try d.link_objects.append(d.comp.gpa, arg); 410 } else { 411 const source = d.addSource(arg) catch |er| { 412 return d.fatal("unable to add source file '{s}': {s}", .{ arg, util.errorDescription(er) }); 413 }; 414 try d.inputs.append(d.comp.gpa, source); 415 } 416 } 417 d.comp.diag.color = switch (color_setting) { 418 .on => true, 419 .off => false, 420 .unset => util.fileSupportsColor(std.io.getStdErr()) and !std.process.hasEnvVarConstant("NO_COLOR"), 421 }; 422 return false; 423 } 424 425 fn option(arg: []const u8, name: []const u8) ?[]const u8 { 426 if (std.mem.startsWith(u8, arg, name) and arg.len > name.len) { 427 return arg[name.len..]; 428 } 429 return null; 430 } 431 432 fn addSource(d: *Driver, path: []const u8) !Source { 433 if (mem.eql(u8, "-", path)) { 434 const stdin = std.io.getStdIn().reader(); 435 const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32)); 436 defer d.comp.gpa.free(input); 437 return d.comp.addSourceFromBuffer("<stdin>", input); 438 } 439 return d.comp.addSourceFromPath(path); 440 } 441 442 pub fn err(d: *Driver, msg: []const u8) !void { 443 try d.comp.diag.add(.{ .tag = .cli_error, .extra = .{ .str = msg } }, &.{}); 444 } 445 446 pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{FatalError} { 447 d.comp.renderErrors(); 448 return d.comp.diag.fatalNoSrc(fmt, args); 449 } 450 451 pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8) !void { 452 var macro_buf = std.ArrayList(u8).init(d.comp.gpa); 453 defer macro_buf.deinit(); 454 455 const std_out = std.io.getStdOut().writer(); 456 if (try parseArgs(d, std_out, macro_buf.writer(), args)) return; 457 458 const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile); 459 460 if (d.inputs.items.len == 0) { 461 return d.fatal("no input files", .{}); 462 } else if (d.inputs.items.len != 1 and d.output_name != null and !linking) { 463 return d.fatal("cannot specify -o when generating multiple output files", .{}); 464 } 465 466 if (!linking) for (d.link_objects.items) |obj| { 467 try d.comp.diag.add(.{ .tag = .cli_unused_link_object, .extra = .{ .str = obj } }, &.{}); 468 }; 469 470 d.comp.defineSystemIncludes(d.aro_name) catch |er| switch (er) { 471 error.OutOfMemory => return error.OutOfMemory, 472 error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}), 473 }; 474 475 const builtin = try d.comp.generateBuiltinMacros(); 476 const user_macros = try d.comp.addSourceFromBuffer("<command line>", macro_buf.items); 477 478 const fast_exit = @import("builtin").mode != .Debug; 479 480 if (fast_exit and d.inputs.items.len == 1) { 481 d.processSource(tc, d.inputs.items[0], builtin, user_macros, fast_exit) catch |e| switch (e) { 482 error.FatalError => { 483 d.comp.renderErrors(); 484 d.exitWithCleanup(1); 485 }, 486 else => |er| return er, 487 }; 488 unreachable; 489 } 490 491 for (d.inputs.items) |source| { 492 d.processSource(tc, source, builtin, user_macros, fast_exit) catch |e| switch (e) { 493 error.FatalError => { 494 d.comp.renderErrors(); 495 }, 496 else => |er| return er, 497 }; 498 } 499 if (d.comp.diag.errors != 0) { 500 if (fast_exit) d.exitWithCleanup(1); 501 return; 502 } 503 if (linking) { 504 try d.invokeLinker(tc, fast_exit); 505 } 506 if (fast_exit) std.process.exit(0); 507 } 508 509 fn processSource( 510 d: *Driver, 511 tc: *Toolchain, 512 source: Source, 513 builtin: Source, 514 user_macros: Source, 515 comptime fast_exit: bool, 516 ) !void { 517 d.comp.generated_buf.items.len = 0; 518 var pp = Preprocessor.init(d.comp); 519 defer pp.deinit(); 520 521 if (d.verbose_pp) pp.verbose = true; 522 if (d.only_preprocess) pp.preserve_whitespace = true; 523 try pp.addBuiltinMacros(); 524 525 _ = try pp.preprocess(builtin); 526 _ = try pp.preprocess(user_macros); 527 const eof = try pp.preprocess(source); 528 try pp.tokens.append(pp.comp.gpa, eof); 529 530 if (d.only_preprocess) { 531 d.comp.renderErrors(); 532 533 const file = if (d.output_name) |some| 534 std.fs.cwd().createFile(some, .{}) catch |er| 535 return d.fatal("unable to create output file '{s}': {s}", .{ some, util.errorDescription(er) }) 536 else 537 std.io.getStdOut(); 538 defer if (d.output_name != null) file.close(); 539 540 var buf_w = std.io.bufferedWriter(file.writer()); 541 pp.prettyPrintTokens(buf_w.writer()) catch |er| 542 return d.fatal("unable to write result: {s}", .{util.errorDescription(er)}); 543 544 buf_w.flush() catch |er| 545 return d.fatal("unable to write result: {s}", .{util.errorDescription(er)}); 546 if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup. 547 return; 548 } 549 550 var tree = try Parser.parse(&pp); 551 defer tree.deinit(); 552 553 if (d.verbose_ast) { 554 const stdout = std.io.getStdOut(); 555 var buf_writer = std.io.bufferedWriter(stdout.writer()); 556 const color = d.comp.diag.color and util.fileSupportsColor(stdout); 557 tree.dump(color, buf_writer.writer()) catch {}; 558 buf_writer.flush() catch {}; 559 } 560 561 const prev_errors = d.comp.diag.errors; 562 d.comp.renderErrors(); 563 564 if (d.comp.diag.errors != prev_errors) { 565 if (fast_exit) d.exitWithCleanup(1); 566 return; // do not compile if there were errors 567 } 568 569 if (d.only_syntax) { 570 if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup. 571 return; 572 } 573 574 if (d.comp.target.ofmt != .elf or d.comp.target.cpu.arch != .x86_64) { 575 return d.fatal( 576 "unsupported target {s}-{s}-{s}, currently only x86-64 elf is supported", 577 .{ @tagName(d.comp.target.cpu.arch), @tagName(d.comp.target.os.tag), @tagName(d.comp.target.abi) }, 578 ); 579 } 580 581 if (d.verbose_ir) { 582 try @import("CodeGen.zig").generateTree(d.comp, tree); 583 } 584 585 const obj = try Codegen.generateTree(d.comp, tree); 586 defer obj.deinit(); 587 588 // If it's used, name_buf will either hold a filename or `/tmp/<12 random bytes with base-64 encoding>.<extension>` 589 // both of which should fit into MAX_NAME_BYTES for all systems 590 var name_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined; 591 592 const out_file_name = if (d.only_compile) blk: { 593 const fmt_template = "{s}{s}"; 594 const fmt_args = .{ 595 std.fs.path.stem(source.path), 596 d.comp.target.ofmt.fileExt(d.comp.target.cpu.arch), 597 }; 598 break :blk d.output_name orelse 599 std.fmt.bufPrint(&name_buf, fmt_template, fmt_args) catch return d.fatal("Filename too long for filesystem: " ++ fmt_template, fmt_args); 600 } else blk: { 601 const random_bytes_count = 12; 602 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); 603 604 var random_bytes: [random_bytes_count]u8 = undefined; 605 std.crypto.random.bytes(&random_bytes); 606 var random_name: [sub_path_len]u8 = undefined; 607 _ = std.fs.base64_encoder.encode(&random_name, &random_bytes); 608 609 const fmt_template = "/tmp/{s}{s}"; 610 const fmt_args = .{ 611 random_name, 612 d.comp.target.ofmt.fileExt(d.comp.target.cpu.arch), 613 }; 614 break :blk std.fmt.bufPrint(&name_buf, fmt_template, fmt_args) catch return d.fatal("Filename too long for filesystem: " ++ fmt_template, fmt_args); 615 }; 616 617 const out_file = std.fs.cwd().createFile(out_file_name, .{}) catch |er| 618 return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, util.errorDescription(er) }); 619 defer out_file.close(); 620 621 obj.finish(out_file) catch |er| 622 return d.fatal("could not output to object file '{s}': {s}", .{ out_file_name, util.errorDescription(er) }); 623 624 if (d.only_compile) { 625 if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup. 626 return; 627 } 628 try d.link_objects.ensureUnusedCapacity(d.comp.gpa, 1); 629 d.link_objects.appendAssumeCapacity(try d.comp.gpa.dupe(u8, out_file_name)); 630 d.temp_file_count += 1; 631 if (fast_exit) { 632 try d.invokeLinker(tc, fast_exit); 633 } 634 } 635 636 fn dumpLinkerArgs(items: []const []const u8) !void { 637 const stdout = std.io.getStdOut().writer(); 638 for (items, 0..) |item, i| { 639 if (i > 0) try stdout.writeByte(' '); 640 try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)}); 641 } 642 try stdout.writeByte('\n'); 643 } 644 645 pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) !void { 646 try tc.discover(); 647 648 var argv = std.ArrayList([]const u8).init(d.comp.gpa); 649 defer argv.deinit(); 650 651 var linker_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; 652 const linker_path = try tc.getLinkerPath(&linker_path_buf); 653 try argv.append(linker_path); 654 655 try tc.buildLinkerArgs(&argv); 656 657 if (d.verbose_linker_args) { 658 dumpLinkerArgs(argv.items) catch |er| { 659 return d.fatal("unable to dump linker args: {s}", .{util.errorDescription(er)}); 660 }; 661 } 662 var child = std.ChildProcess.init(argv.items, d.comp.gpa); 663 // TODO handle better 664 child.stdin_behavior = .Inherit; 665 child.stdout_behavior = .Inherit; 666 child.stderr_behavior = .Inherit; 667 668 const term = child.spawnAndWait() catch |er| { 669 return d.fatal("unable to spawn linker: {s}", .{util.errorDescription(er)}); 670 }; 671 switch (term) { 672 .Exited => |code| if (code != 0) d.exitWithCleanup(code), 673 else => std.process.abort(), 674 } 675 if (fast_exit) d.exitWithCleanup(0); 676 } 677 678 fn exitWithCleanup(d: *Driver, code: u8) noreturn { 679 for (d.link_objects.items[d.link_objects.items.len - d.temp_file_count ..]) |obj| { 680 std.fs.deleteFileAbsolute(obj) catch {}; 681 } 682 std.process.exit(code); 683 }