blob 5c27e5f5 (30378B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 4 const event = std.event; 5 const os = std.os; 6 const io = std.io; 7 const mem = std.mem; 8 const Allocator = mem.Allocator; 9 const ArrayList = std.ArrayList; 10 const Buffer = std.Buffer; 11 12 const arg = @import("arg.zig"); 13 const c = @import("c.zig"); 14 const introspect = @import("introspect.zig"); 15 const Args = arg.Args; 16 const Flag = arg.Flag; 17 const EventLoopLocal = @import("compilation.zig").EventLoopLocal; 18 const Compilation = @import("compilation.zig").Compilation; 19 const Target = @import("target.zig").Target; 20 const errmsg = @import("errmsg.zig"); 21 const LibCInstallation = @import("libc_installation.zig").LibCInstallation; 22 23 var stderr_file: os.File = undefined; 24 var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; 25 var stdout: *io.OutStream(io.FileOutStream.Error) = undefined; 26 27 const usage = 28 \\usage: zig [command] [options] 29 \\ 30 \\Commands: 31 \\ 32 \\ build-exe [source] Create executable from source or object files 33 \\ build-lib [source] Create library from source or object files 34 \\ build-obj [source] Create object from source or assembly 35 \\ fmt [source] Parse file and render in canonical zig format 36 \\ libc [paths_file] Display native libc paths file or validate one 37 \\ targets List available compilation targets 38 \\ version Print version number and exit 39 \\ zen Print zen of zig and exit 40 \\ 41 \\ 42 ; 43 44 const Command = struct { 45 name: []const u8, 46 exec: fn (*Allocator, []const []const u8) error!void, 47 }; 48 49 pub fn main() !void { 50 // This allocator needs to be thread-safe because we use it for the event.Loop 51 // which multiplexes coroutines onto kernel threads. 52 // libc allocator is guaranteed to have this property. 53 const allocator = std.heap.c_allocator; 54 55 var stdout_file = try std.io.getStdOut(); 56 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); 57 stdout = &stdout_out_stream.stream; 58 59 stderr_file = try std.io.getStdErr(); 60 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file); 61 stderr = &stderr_out_stream.stream; 62 63 const args = try os.argsAlloc(allocator); 64 // TODO I'm getting unreachable code here, which shouldn't happen 65 //defer os.argsFree(allocator, args); 66 67 if (args.len <= 1) { 68 try stderr.write("expected command argument\n\n"); 69 try stderr.write(usage); 70 os.exit(1); 71 } 72 73 const commands = []Command{ 74 Command{ 75 .name = "build-exe", 76 .exec = cmdBuildExe, 77 }, 78 Command{ 79 .name = "build-lib", 80 .exec = cmdBuildLib, 81 }, 82 Command{ 83 .name = "build-obj", 84 .exec = cmdBuildObj, 85 }, 86 Command{ 87 .name = "fmt", 88 .exec = cmdFmt, 89 }, 90 Command{ 91 .name = "libc", 92 .exec = cmdLibC, 93 }, 94 Command{ 95 .name = "targets", 96 .exec = cmdTargets, 97 }, 98 Command{ 99 .name = "version", 100 .exec = cmdVersion, 101 }, 102 Command{ 103 .name = "zen", 104 .exec = cmdZen, 105 }, 106 107 // undocumented commands 108 Command{ 109 .name = "help", 110 .exec = cmdHelp, 111 }, 112 Command{ 113 .name = "internal", 114 .exec = cmdInternal, 115 }, 116 }; 117 118 for (commands) |command| { 119 if (mem.eql(u8, command.name, args[1])) { 120 return command.exec(allocator, args[2..]); 121 } 122 } 123 124 try stderr.print("unknown command: {}\n\n", args[1]); 125 try stderr.write(usage); 126 os.exit(1); 127 } 128 129 const usage_build_generic = 130 \\usage: zig build-exe <options> [file] 131 \\ zig build-lib <options> [file] 132 \\ zig build-obj <options> [file] 133 \\ 134 \\General Options: 135 \\ --help Print this help and exit 136 \\ --color [auto|off|on] Enable or disable colored error messages 137 \\ 138 \\Compile Options: 139 \\ --libc [file] Provide a file which specifies libc paths 140 \\ --assembly [source] Add assembly file to build 141 \\ --cache-dir [path] Override the cache directory 142 \\ --emit [filetype] Emit a specific file format as compilation output 143 \\ --enable-timing-info Print timing diagnostics 144 \\ --name [name] Override output name 145 \\ --output [file] Override destination path 146 \\ --output-h [file] Override generated header file path 147 \\ --pkg-begin [name] [path] Make package available to import and push current pkg 148 \\ --pkg-end Pop current pkg 149 \\ --mode [mode] Set the build mode 150 \\ debug (default) optimizations off, safety on 151 \\ release-fast optimizations on, safety off 152 \\ release-safe optimizations on, safety on 153 \\ release-small optimize for small binary, safety off 154 \\ --static Output will be statically linked 155 \\ --strip Exclude debug symbols 156 \\ --target-arch [name] Specify target architecture 157 \\ --target-environ [name] Specify target environment 158 \\ --target-os [name] Specify target operating system 159 \\ --verbose-tokenize Turn on compiler debug output for tokenization 160 \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view) 161 \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source) 162 \\ --verbose-link Turn on compiler debug output for linking 163 \\ --verbose-ir Turn on compiler debug output for Zig IR 164 \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR 165 \\ --verbose-cimport Turn on compiler debug output for C imports 166 \\ -dirafter [dir] Same as -isystem but do it last 167 \\ -isystem [dir] Add additional search path for other .h files 168 \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing 169 \\ 170 \\Link Options: 171 \\ --ar-path [path] Set the path to ar 172 \\ --each-lib-rpath Add rpath for each used dynamic library 173 \\ --library [lib] Link against lib 174 \\ --forbid-library [lib] Make it an error to link against lib 175 \\ --library-path [dir] Add a directory to the library search path 176 \\ --linker-script [path] Use a custom linker script 177 \\ --object [obj] Add object file to build 178 \\ -rdynamic Add all symbols to the dynamic symbol table 179 \\ -rpath [path] Add directory to the runtime library search path 180 \\ -mconsole (windows) --subsystem console to the linker 181 \\ -mwindows (windows) --subsystem windows to the linker 182 \\ -framework [name] (darwin) link against framework 183 \\ -mios-version-min [ver] (darwin) set iOS deployment target 184 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target 185 \\ --ver-major [ver] Dynamic library semver major version 186 \\ --ver-minor [ver] Dynamic library semver minor version 187 \\ --ver-patch [ver] Dynamic library semver patch version 188 \\ 189 \\ 190 ; 191 192 const args_build_generic = []Flag{ 193 Flag.Bool("--help"), 194 Flag.Option("--color", []const []const u8{ 195 "auto", 196 "off", 197 "on", 198 }), 199 Flag.Option("--mode", []const []const u8{ 200 "debug", 201 "release-fast", 202 "release-safe", 203 "release-small", 204 }), 205 206 Flag.ArgMergeN("--assembly", 1), 207 Flag.Arg1("--cache-dir"), 208 Flag.Option("--emit", []const []const u8{ 209 "asm", 210 "bin", 211 "llvm-ir", 212 }), 213 Flag.Bool("--enable-timing-info"), 214 Flag.Arg1("--libc"), 215 Flag.Arg1("--name"), 216 Flag.Arg1("--output"), 217 Flag.Arg1("--output-h"), 218 // NOTE: Parsed manually after initial check 219 Flag.ArgN("--pkg-begin", 2), 220 Flag.Bool("--pkg-end"), 221 Flag.Bool("--static"), 222 Flag.Bool("--strip"), 223 Flag.Arg1("--target-arch"), 224 Flag.Arg1("--target-environ"), 225 Flag.Arg1("--target-os"), 226 Flag.Bool("--verbose-tokenize"), 227 Flag.Bool("--verbose-ast-tree"), 228 Flag.Bool("--verbose-ast-fmt"), 229 Flag.Bool("--verbose-link"), 230 Flag.Bool("--verbose-ir"), 231 Flag.Bool("--verbose-llvm-ir"), 232 Flag.Bool("--verbose-cimport"), 233 Flag.Arg1("-dirafter"), 234 Flag.ArgMergeN("-isystem", 1), 235 Flag.Arg1("-mllvm"), 236 237 Flag.Arg1("--ar-path"), 238 Flag.Bool("--each-lib-rpath"), 239 Flag.ArgMergeN("--library", 1), 240 Flag.ArgMergeN("--forbid-library", 1), 241 Flag.ArgMergeN("--library-path", 1), 242 Flag.Arg1("--linker-script"), 243 Flag.ArgMergeN("--object", 1), 244 // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path 245 Flag.Bool("-rdynamic"), 246 Flag.Arg1("-rpath"), 247 Flag.Bool("-mconsole"), 248 Flag.Bool("-mwindows"), 249 Flag.ArgMergeN("-framework", 1), 250 Flag.Arg1("-mios-version-min"), 251 Flag.Arg1("-mmacosx-version-min"), 252 Flag.Arg1("--ver-major"), 253 Flag.Arg1("--ver-minor"), 254 Flag.Arg1("--ver-patch"), 255 }; 256 257 fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void { 258 var flags = try Args.parse(allocator, args_build_generic, args); 259 defer flags.deinit(); 260 261 if (flags.present("help")) { 262 try stdout.write(usage_build_generic); 263 os.exit(0); 264 } 265 266 const build_mode = blk: { 267 if (flags.single("mode")) |mode_flag| { 268 if (mem.eql(u8, mode_flag, "debug")) { 269 break :blk builtin.Mode.Debug; 270 } else if (mem.eql(u8, mode_flag, "release-fast")) { 271 break :blk builtin.Mode.ReleaseFast; 272 } else if (mem.eql(u8, mode_flag, "release-safe")) { 273 break :blk builtin.Mode.ReleaseSafe; 274 } else if (mem.eql(u8, mode_flag, "release-small")) { 275 break :blk builtin.Mode.ReleaseSmall; 276 } else unreachable; 277 } else { 278 break :blk builtin.Mode.Debug; 279 } 280 }; 281 282 const color = blk: { 283 if (flags.single("color")) |color_flag| { 284 if (mem.eql(u8, color_flag, "auto")) { 285 break :blk errmsg.Color.Auto; 286 } else if (mem.eql(u8, color_flag, "on")) { 287 break :blk errmsg.Color.On; 288 } else if (mem.eql(u8, color_flag, "off")) { 289 break :blk errmsg.Color.Off; 290 } else unreachable; 291 } else { 292 break :blk errmsg.Color.Auto; 293 } 294 }; 295 296 const emit_type = blk: { 297 if (flags.single("emit")) |emit_flag| { 298 if (mem.eql(u8, emit_flag, "asm")) { 299 break :blk Compilation.Emit.Assembly; 300 } else if (mem.eql(u8, emit_flag, "bin")) { 301 break :blk Compilation.Emit.Binary; 302 } else if (mem.eql(u8, emit_flag, "llvm-ir")) { 303 break :blk Compilation.Emit.LlvmIr; 304 } else unreachable; 305 } else { 306 break :blk Compilation.Emit.Binary; 307 } 308 }; 309 310 var cur_pkg = try CliPkg.init(allocator, "", "", null); 311 defer cur_pkg.deinit(); 312 313 var i: usize = 0; 314 while (i < args.len) : (i += 1) { 315 const arg_name = args[i]; 316 if (mem.eql(u8, "--pkg-begin", arg_name)) { 317 // following two arguments guaranteed to exist due to arg parsing 318 i += 1; 319 const new_pkg_name = args[i]; 320 i += 1; 321 const new_pkg_path = args[i]; 322 323 var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg); 324 try cur_pkg.children.append(new_cur_pkg); 325 cur_pkg = new_cur_pkg; 326 } else if (mem.eql(u8, "--pkg-end", arg_name)) { 327 if (cur_pkg.parent) |parent| { 328 cur_pkg = parent; 329 } else { 330 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n"); 331 os.exit(1); 332 } 333 } 334 } 335 336 if (cur_pkg.parent != null) { 337 try stderr.print("unmatched --pkg-begin\n"); 338 os.exit(1); 339 } 340 341 const provided_name = flags.single("name"); 342 const root_source_file = switch (flags.positionals.len) { 343 0 => null, 344 1 => flags.positionals.at(0), 345 else => { 346 try stderr.print("unexpected extra parameter: {}\n", flags.positionals.at(1)); 347 os.exit(1); 348 }, 349 }; 350 351 const root_name = if (provided_name) |n| n else blk: { 352 if (root_source_file) |file| { 353 const basename = os.path.basename(file); 354 var it = mem.split(basename, "."); 355 break :blk it.next() orelse basename; 356 } else { 357 try stderr.write("--name [name] not provided and unable to infer\n"); 358 os.exit(1); 359 } 360 }; 361 362 const is_static = flags.present("static"); 363 364 const assembly_files = flags.many("assembly"); 365 const link_objects = flags.many("object"); 366 if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { 367 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); 368 os.exit(1); 369 } 370 371 if (out_type == Compilation.Kind.Obj and link_objects.len != 0) { 372 try stderr.write("When building an object file, --object arguments are invalid\n"); 373 os.exit(1); 374 } 375 376 const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..]; 377 const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch { 378 try stderr.print("invalid cache dir: {}\n", rel_cache_dir); 379 os.exit(1); 380 }; 381 defer allocator.free(full_cache_dir); 382 383 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); 384 defer allocator.free(zig_lib_dir); 385 386 var override_libc: LibCInstallation = undefined; 387 388 var loop: event.Loop = undefined; 389 try loop.initMultiThreaded(allocator); 390 defer loop.deinit(); 391 392 var event_loop_local = try EventLoopLocal.init(&loop); 393 defer event_loop_local.deinit(); 394 395 var comp = try Compilation.create( 396 &event_loop_local, 397 root_name, 398 root_source_file, 399 Target.Native, 400 out_type, 401 build_mode, 402 is_static, 403 zig_lib_dir, 404 full_cache_dir, 405 ); 406 defer comp.destroy(); 407 408 if (flags.single("libc")) |libc_path| { 409 parseLibcPaths(loop.allocator, &override_libc, libc_path); 410 comp.override_libc = &override_libc; 411 } 412 413 for (flags.many("library")) |lib| { 414 _ = try comp.addLinkLib(lib, true); 415 } 416 417 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); 418 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); 419 comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); 420 421 comp.is_test = false; 422 423 comp.linker_script = flags.single("linker-script"); 424 comp.each_lib_rpath = flags.present("each-lib-rpath"); 425 426 var clang_argv_buf = ArrayList([]const u8).init(allocator); 427 defer clang_argv_buf.deinit(); 428 429 const mllvm_flags = flags.many("mllvm"); 430 for (mllvm_flags) |mllvm| { 431 try clang_argv_buf.append("-mllvm"); 432 try clang_argv_buf.append(mllvm); 433 } 434 435 comp.llvm_argv = mllvm_flags; 436 comp.clang_argv = clang_argv_buf.toSliceConst(); 437 438 comp.strip = flags.present("strip"); 439 440 comp.verbose_tokenize = flags.present("verbose-tokenize"); 441 comp.verbose_ast_tree = flags.present("verbose-ast-tree"); 442 comp.verbose_ast_fmt = flags.present("verbose-ast-fmt"); 443 comp.verbose_link = flags.present("verbose-link"); 444 comp.verbose_ir = flags.present("verbose-ir"); 445 comp.verbose_llvm_ir = flags.present("verbose-llvm-ir"); 446 comp.verbose_cimport = flags.present("verbose-cimport"); 447 448 comp.err_color = color; 449 comp.lib_dirs = flags.many("library-path"); 450 comp.darwin_frameworks = flags.many("framework"); 451 comp.rpath_list = flags.many("rpath"); 452 453 if (flags.single("output-h")) |output_h| { 454 comp.out_h_path = output_h; 455 } 456 457 comp.windows_subsystem_windows = flags.present("mwindows"); 458 comp.windows_subsystem_console = flags.present("mconsole"); 459 comp.linker_rdynamic = flags.present("rdynamic"); 460 461 if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) { 462 try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n"); 463 os.exit(1); 464 } 465 466 if (flags.single("mmacosx-version-min")) |ver| { 467 comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver }; 468 } 469 if (flags.single("mios-version-min")) |ver| { 470 comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver }; 471 } 472 473 comp.emit_file_type = emit_type; 474 comp.assembly_files = assembly_files; 475 comp.link_out_file = flags.single("out-file"); 476 comp.link_objects = link_objects; 477 478 try comp.build(); 479 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color); 480 defer cancel process_build_events_handle; 481 loop.run(); 482 } 483 484 async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { 485 // TODO directly awaiting async should guarantee memory allocation elision 486 const build_event = await (async comp.events.get() catch unreachable); 487 488 switch (build_event) { 489 Compilation.Event.Ok => { 490 return; 491 }, 492 Compilation.Event.Error => |err| { 493 std.debug.warn("build failed: {}\n", @errorName(err)); 494 os.exit(1); 495 }, 496 Compilation.Event.Fail => |msgs| { 497 for (msgs) |msg| { 498 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); 499 } 500 }, 501 } 502 } 503 504 fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { 505 return buildOutputType(allocator, args, Compilation.Kind.Exe); 506 } 507 508 fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void { 509 return buildOutputType(allocator, args, Compilation.Kind.Lib); 510 } 511 512 fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { 513 return buildOutputType(allocator, args, Compilation.Kind.Obj); 514 } 515 516 const usage_fmt = 517 \\usage: zig fmt [file]... 518 \\ 519 \\ Formats the input files and modifies them in-place. 520 \\ 521 \\Options: 522 \\ --help Print this help and exit 523 \\ --color [auto|off|on] Enable or disable colored error messages 524 \\ --stdin Format code from stdin 525 \\ 526 \\ 527 ; 528 529 const args_fmt_spec = []Flag{ 530 Flag.Bool("--help"), 531 Flag.Option("--color", []const []const u8{ 532 "auto", 533 "off", 534 "on", 535 }), 536 Flag.Bool("--stdin"), 537 }; 538 539 const Fmt = struct { 540 seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8), 541 queue: std.LinkedList([]const u8), 542 any_error: bool, 543 544 // file_path must outlive Fmt 545 fn addToQueue(self: *Fmt, file_path: []const u8) !void { 546 const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{ 547 .prev = undefined, 548 .next = undefined, 549 .data = file_path, 550 }); 551 552 if (try self.seen.put(file_path, {})) |_| return; 553 554 self.queue.append(new_node); 555 } 556 557 fn addDirToQueue(self: *Fmt, file_path: []const u8) !void { 558 var dir = try std.os.Dir.open(self.seen.allocator, file_path); 559 defer dir.close(); 560 while (try dir.next()) |entry| { 561 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { 562 const full_path = try os.path.join(self.seen.allocator, file_path, entry.name); 563 try self.addToQueue(full_path); 564 } 565 } 566 } 567 }; 568 569 fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { 570 libc.parse(allocator, libc_paths_file, stderr) catch |err| { 571 stderr.print( 572 "Unable to parse libc path file '{}': {}.\n" ++ 573 "Try running `zig libc` to see an example for the native target.\n", 574 libc_paths_file, 575 @errorName(err), 576 ) catch os.exit(1); 577 os.exit(1); 578 }; 579 } 580 581 fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { 582 switch (args.len) { 583 0 => {}, 584 1 => { 585 var libc_installation: LibCInstallation = undefined; 586 parseLibcPaths(allocator, &libc_installation, args[0]); 587 return; 588 }, 589 else => { 590 try stderr.print("unexpected extra parameter: {}\n", args[1]); 591 os.exit(1); 592 }, 593 } 594 595 var loop: event.Loop = undefined; 596 try loop.initMultiThreaded(allocator); 597 defer loop.deinit(); 598 599 var event_loop_local = try EventLoopLocal.init(&loop); 600 defer event_loop_local.deinit(); 601 602 const handle = try async<loop.allocator> findLibCAsync(&event_loop_local); 603 defer cancel handle; 604 605 loop.run(); 606 } 607 608 async fn findLibCAsync(event_loop_local: *EventLoopLocal) void { 609 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| { 610 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1); 611 os.exit(1); 612 }; 613 libc.render(stdout) catch os.exit(1); 614 } 615 616 fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { 617 var flags = try Args.parse(allocator, args_fmt_spec, args); 618 defer flags.deinit(); 619 620 if (flags.present("help")) { 621 try stdout.write(usage_fmt); 622 os.exit(0); 623 } 624 625 const color = blk: { 626 if (flags.single("color")) |color_flag| { 627 if (mem.eql(u8, color_flag, "auto")) { 628 break :blk errmsg.Color.Auto; 629 } else if (mem.eql(u8, color_flag, "on")) { 630 break :blk errmsg.Color.On; 631 } else if (mem.eql(u8, color_flag, "off")) { 632 break :blk errmsg.Color.Off; 633 } else unreachable; 634 } else { 635 break :blk errmsg.Color.Auto; 636 } 637 }; 638 639 if (flags.present("stdin")) { 640 if (flags.positionals.len != 0) { 641 try stderr.write("cannot use --stdin with positional arguments\n"); 642 os.exit(1); 643 } 644 645 var stdin_file = try io.getStdIn(); 646 var stdin = io.FileInStream.init(&stdin_file); 647 648 const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize)); 649 defer allocator.free(source_code); 650 651 var tree = std.zig.parse(allocator, source_code) catch |err| { 652 try stderr.print("error parsing stdin: {}\n", err); 653 os.exit(1); 654 }; 655 defer tree.deinit(); 656 657 var error_it = tree.errors.iterator(0); 658 while (error_it.next()) |parse_error| { 659 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>"); 660 defer allocator.destroy(msg); 661 662 try errmsg.printToFile(&stderr_file, msg, color); 663 } 664 if (tree.errors.len != 0) { 665 os.exit(1); 666 } 667 668 _ = try std.zig.render(allocator, stdout, &tree); 669 return; 670 } 671 672 if (flags.positionals.len == 0) { 673 try stderr.write("expected at least one source file argument\n"); 674 os.exit(1); 675 } 676 677 var fmt = Fmt{ 678 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator), 679 .queue = std.LinkedList([]const u8).init(), 680 .any_error = false, 681 }; 682 683 for (flags.positionals.toSliceConst()) |file_path| { 684 try fmt.addToQueue(file_path); 685 } 686 687 while (fmt.queue.popFirst()) |node| { 688 const file_path = node.data; 689 690 var file = try os.File.openRead(allocator, file_path); 691 defer file.close(); 692 693 const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) { 694 error.IsDir => { 695 try fmt.addDirToQueue(file_path); 696 continue; 697 }, 698 else => { 699 try stderr.print("unable to open '{}': {}\n", file_path, err); 700 fmt.any_error = true; 701 continue; 702 }, 703 }; 704 defer allocator.free(source_code); 705 706 var tree = std.zig.parse(allocator, source_code) catch |err| { 707 try stderr.print("error parsing file '{}': {}\n", file_path, err); 708 fmt.any_error = true; 709 continue; 710 }; 711 defer tree.deinit(); 712 713 var error_it = tree.errors.iterator(0); 714 while (error_it.next()) |parse_error| { 715 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path); 716 defer allocator.destroy(msg); 717 718 try errmsg.printToFile(&stderr_file, msg, color); 719 } 720 if (tree.errors.len != 0) { 721 fmt.any_error = true; 722 continue; 723 } 724 725 const baf = try io.BufferedAtomicFile.create(allocator, file_path); 726 defer baf.destroy(); 727 728 const anything_changed = try std.zig.render(allocator, baf.stream(), &tree); 729 if (anything_changed) { 730 try stderr.print("{}\n", file_path); 731 try baf.finish(); 732 } 733 } 734 735 if (fmt.any_error) { 736 os.exit(1); 737 } 738 } 739 740 // cmd:targets ///////////////////////////////////////////////////////////////////////////////////// 741 742 fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { 743 try stdout.write("Architectures:\n"); 744 { 745 comptime var i: usize = 0; 746 inline while (i < @memberCount(builtin.Arch)) : (i += 1) { 747 comptime const arch_tag = @memberName(builtin.Arch, i); 748 // NOTE: Cannot use empty string, see #918. 749 comptime const native_str = if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n"; 750 751 try stdout.print(" {}{}", arch_tag, native_str); 752 } 753 } 754 try stdout.write("\n"); 755 756 try stdout.write("Operating Systems:\n"); 757 { 758 comptime var i: usize = 0; 759 inline while (i < @memberCount(builtin.Os)) : (i += 1) { 760 comptime const os_tag = @memberName(builtin.Os, i); 761 // NOTE: Cannot use empty string, see #918. 762 comptime const native_str = if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n"; 763 764 try stdout.print(" {}{}", os_tag, native_str); 765 } 766 } 767 try stdout.write("\n"); 768 769 try stdout.write("Environments:\n"); 770 { 771 comptime var i: usize = 0; 772 inline while (i < @memberCount(builtin.Environ)) : (i += 1) { 773 comptime const environ_tag = @memberName(builtin.Environ, i); 774 // NOTE: Cannot use empty string, see #918. 775 comptime const native_str = if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n"; 776 777 try stdout.print(" {}{}", environ_tag, native_str); 778 } 779 } 780 } 781 782 fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { 783 try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING)); 784 } 785 786 const args_test_spec = []Flag{Flag.Bool("--help")}; 787 788 fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { 789 try stdout.write(usage); 790 } 791 792 const info_zen = 793 \\ 794 \\ * Communicate intent precisely. 795 \\ * Edge cases matter. 796 \\ * Favor reading code over writing code. 797 \\ * Only one obvious way to do things. 798 \\ * Runtime crashes are better than bugs. 799 \\ * Compile errors are better than runtime crashes. 800 \\ * Incremental improvements. 801 \\ * Avoid local maximums. 802 \\ * Reduce the amount one must remember. 803 \\ * Minimize energy spent on coding style. 804 \\ * Together we serve end users. 805 \\ 806 \\ 807 ; 808 809 fn cmdZen(allocator: *Allocator, args: []const []const u8) !void { 810 try stdout.write(info_zen); 811 } 812 813 const usage_internal = 814 \\usage: zig internal [subcommand] 815 \\ 816 \\Sub-Commands: 817 \\ build-info Print static compiler build-info 818 \\ 819 \\ 820 ; 821 822 fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { 823 if (args.len == 0) { 824 try stderr.write(usage_internal); 825 os.exit(1); 826 } 827 828 const sub_commands = []Command{Command{ 829 .name = "build-info", 830 .exec = cmdInternalBuildInfo, 831 }}; 832 833 for (sub_commands) |sub_command| { 834 if (mem.eql(u8, sub_command.name, args[0])) { 835 try sub_command.exec(allocator, args[1..]); 836 return; 837 } 838 } 839 840 try stderr.print("unknown sub command: {}\n\n", args[0]); 841 try stderr.write(usage_internal); 842 } 843 844 fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { 845 try stdout.print( 846 \\ZIG_CMAKE_BINARY_DIR {} 847 \\ZIG_CXX_COMPILER {} 848 \\ZIG_LLVM_CONFIG_EXE {} 849 \\ZIG_LLD_INCLUDE_PATH {} 850 \\ZIG_LLD_LIBRARIES {} 851 \\ZIG_STD_FILES {} 852 \\ZIG_C_HEADER_FILES {} 853 \\ZIG_DIA_GUIDS_LIB {} 854 \\ 855 , 856 std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR), 857 std.cstr.toSliceConst(c.ZIG_CXX_COMPILER), 858 std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE), 859 std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH), 860 std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES), 861 std.cstr.toSliceConst(c.ZIG_STD_FILES), 862 std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES), 863 std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB), 864 ); 865 } 866 867 const CliPkg = struct { 868 name: []const u8, 869 path: []const u8, 870 children: ArrayList(*CliPkg), 871 parent: ?*CliPkg, 872 873 pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { 874 var pkg = try allocator.create(CliPkg{ 875 .name = name, 876 .path = path, 877 .children = ArrayList(*CliPkg).init(allocator), 878 .parent = parent, 879 }); 880 return pkg; 881 } 882 883 pub fn deinit(self: *CliPkg) void { 884 for (self.children.toSliceConst()) |child| { 885 child.deinit(); 886 } 887 self.children.deinit(); 888 } 889 };