| author | |
| committer | |
| log | ba56f365c813440b79c1710c6a8b0fd591883e13 |
| tree | 2b0594a07e88fbb401c10021cff11f3f18601229 |
| parent | 77a4e7b37468ee7fb5dd3193972050808732f66c |
| signature |
4 files changed, 305 insertions(+), 7 deletions(-)
CMakeLists.txt+5| ... | @@ -652,6 +652,7 @@ set(ZIG_STD_FILES | ... | @@ -652,6 +652,7 @@ set(ZIG_STD_FILES |
| 652 | "special/compiler_rt/udivmodti4.zig" | 652 | "special/compiler_rt/udivmodti4.zig" |
| 653 | "special/compiler_rt/udivti3.zig" | 653 | "special/compiler_rt/udivti3.zig" |
| 654 | "special/compiler_rt/umodti3.zig" | 654 | "special/compiler_rt/umodti3.zig" |
| 655 | "special/fmt_runner.zig" | ||
| 655 | "special/init-exe/build.zig" | 656 | "special/init-exe/build.zig" |
| 656 | "special/init-exe/src/main.zig" | 657 | "special/init-exe/src/main.zig" |
| 657 | "special/init-lib/build.zig" | 658 | "special/init-lib/build.zig" |
| ... | @@ -905,3 +906,7 @@ foreach(file ${ZIG_STD_FILES}) | ... | @@ -905,3 +906,7 @@ foreach(file ${ZIG_STD_FILES}) |
| 905 | get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY) | 906 | get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY) |
| 906 | install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}") | 907 | install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}") |
| 907 | endforeach() | 908 | endforeach() |
| 909 | |||
| 910 | install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") | ||
| 911 | install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") | ||
| 912 | install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") |
src-self-hosted/main.zig+3-3| ... | @@ -24,7 +24,7 @@ var stderr_file: os.File = undefined; | ... | @@ -24,7 +24,7 @@ var stderr_file: os.File = undefined; |
| 24 | var stderr: *io.OutStream(os.File.WriteError) = undefined; | 24 | var stderr: *io.OutStream(os.File.WriteError) = undefined; |
| 25 | var stdout: *io.OutStream(os.File.WriteError) = undefined; | 25 | var stdout: *io.OutStream(os.File.WriteError) = undefined; |
| 26 | 26 | ||
| 27 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB | 27 | pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB |
| 28 | 28 | ||
| 29 | const usage = | 29 | const usage = |
| 30 | \\usage: zig [command] [options] | 30 | \\usage: zig [command] [options] |
| ... | @@ -510,7 +510,7 @@ fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { | ... | @@ -510,7 +510,7 @@ fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { |
| 510 | return buildOutputType(allocator, args, Compilation.Kind.Obj); | 510 | return buildOutputType(allocator, args, Compilation.Kind.Obj); |
| 511 | } | 511 | } |
| 512 | 512 | ||
| 513 | const usage_fmt = | 513 | pub const usage_fmt = |
| 514 | \\usage: zig fmt [file]... | 514 | \\usage: zig fmt [file]... |
| 515 | \\ | 515 | \\ |
| 516 | \\ Formats the input files and modifies them in-place. | 516 | \\ Formats the input files and modifies them in-place. |
| ... | @@ -527,7 +527,7 @@ const usage_fmt = | ... | @@ -527,7 +527,7 @@ const usage_fmt = |
| 527 | \\ | 527 | \\ |
| 528 | ; | 528 | ; |
| 529 | 529 | ||
| 530 | const args_fmt_spec = []Flag{ | 530 | pub const args_fmt_spec = []Flag{ |
| 531 | Flag.Bool("--help"), | 531 | Flag.Bool("--help"), |
| 532 | Flag.Bool("--check"), | 532 | Flag.Bool("--check"), |
| 533 | Flag.Option("--color", []const []const u8{ | 533 | Flag.Option("--color", []const []const u8{ |
src/main.cpp+32-4| ... | @@ -21,8 +21,8 @@ static int print_error_usage(const char *arg0) { | ... | @@ -21,8 +21,8 @@ static int print_error_usage(const char *arg0) { |
| 21 | return EXIT_FAILURE; | 21 | return EXIT_FAILURE; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | static int print_full_usage(const char *arg0) { | 24 | static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 25 | fprintf(stdout, | 25 | fprintf(file, |
| 26 | "Usage: %s [command] [options]\n" | 26 | "Usage: %s [command] [options]\n" |
| 27 | "\n" | 27 | "\n" |
| 28 | "Commands:\n" | 28 | "Commands:\n" |
| ... | @@ -31,6 +31,7 @@ static int print_full_usage(const char *arg0) { | ... | @@ -31,6 +31,7 @@ static int print_full_usage(const char *arg0) { |
| 31 | " build-lib [source] create library from source or object files\n" | 31 | " build-lib [source] create library from source or object files\n" |
| 32 | " build-obj [source] create object from source or assembly\n" | 32 | " build-obj [source] create object from source or assembly\n" |
| 33 | " builtin show the source code of that @import(\"builtin\")\n" | 33 | " builtin show the source code of that @import(\"builtin\")\n" |
| 34 | " fmt parse files and render in canonical zig format\n" | ||
| 34 | " help show this usage information\n" | 35 | " help show this usage information\n" |
| 35 | " id print the base64-encoded compiler id\n" | 36 | " id print the base64-encoded compiler id\n" |
| 36 | " init-exe initialize a `zig build` application in the cwd\n" | 37 | " init-exe initialize a `zig build` application in the cwd\n" |
| ... | @@ -106,7 +107,7 @@ static int print_full_usage(const char *arg0) { | ... | @@ -106,7 +107,7 @@ static int print_full_usage(const char *arg0) { |
| 106 | " --test-cmd [arg] specify test execution command one arg at a time\n" | 107 | " --test-cmd [arg] specify test execution command one arg at a time\n" |
| 107 | " --test-cmd-bin appends test binary path to test cmd args\n" | 108 | " --test-cmd-bin appends test binary path to test cmd args\n" |
| 108 | , arg0); | 109 | , arg0); |
| 109 | return EXIT_SUCCESS; | 110 | return return_code; |
| 110 | } | 111 | } |
| 111 | 112 | ||
| 112 | static const char *ZIG_ZEN = "\n" | 113 | static const char *ZIG_ZEN = "\n" |
| ... | @@ -515,6 +516,31 @@ int main(int argc, char **argv) { | ... | @@ -515,6 +516,31 @@ int main(int argc, char **argv) { |
| 515 | fprintf(stderr, "\n"); | 516 | fprintf(stderr, "\n"); |
| 516 | } | 517 | } |
| 517 | return (term.how == TerminationIdClean) ? term.code : -1; | 518 | return (term.how == TerminationIdClean) ? term.code : -1; |
| 519 | } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { | ||
| 520 | init_all_targets(); | ||
| 521 | Buf *fmt_runner_path = buf_alloc(); | ||
| 522 | os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); | ||
| 523 | CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), | ||
| 524 | nullptr); | ||
| 525 | g->is_single_threaded = true; | ||
| 526 | codegen_set_out_name(g, buf_create_from_str("fmt")); | ||
| 527 | g->enable_cache = true; | ||
| 528 | |||
| 529 | codegen_build_and_link(g); | ||
| 530 | |||
| 531 | ZigList<const char*> args = {0}; | ||
| 532 | for (int i = 2; i < argc; i += 1) { | ||
| 533 | args.append(argv[i]); | ||
| 534 | } | ||
| 535 | args.append(nullptr); | ||
| 536 | const char *exec_path = buf_ptr(&g->output_file_path); | ||
| 537 | |||
| 538 | os_execv(exec_path, args.items); | ||
| 539 | |||
| 540 | args.pop(); | ||
| 541 | Termination term; | ||
| 542 | os_spawn_process(exec_path, args, &term); | ||
| 543 | return term.code; | ||
| 518 | } | 544 | } |
| 519 | 545 | ||
| 520 | for (int i = 1; i < argc; i += 1) { | 546 | for (int i = 1; i < argc; i += 1) { |
| ... | @@ -527,6 +553,8 @@ int main(int argc, char **argv) { | ... | @@ -527,6 +553,8 @@ int main(int argc, char **argv) { |
| 527 | build_mode = BuildModeSafeRelease; | 553 | build_mode = BuildModeSafeRelease; |
| 528 | } else if (strcmp(arg, "--release-small") == 0) { | 554 | } else if (strcmp(arg, "--release-small") == 0) { |
| 529 | build_mode = BuildModeSmallRelease; | 555 | build_mode = BuildModeSmallRelease; |
| 556 | } else if (strcmp(arg, "--help") == 0) { | ||
| 557 | return print_full_usage(arg0, stderr, EXIT_FAILURE); | ||
| 530 | } else if (strcmp(arg, "--strip") == 0) { | 558 | } else if (strcmp(arg, "--strip") == 0) { |
| 531 | strip = true; | 559 | strip = true; |
| 532 | } else if (strcmp(arg, "--static") == 0) { | 560 | } else if (strcmp(arg, "--static") == 0) { |
| ... | @@ -1080,7 +1108,7 @@ int main(int argc, char **argv) { | ... | @@ -1080,7 +1108,7 @@ int main(int argc, char **argv) { |
| 1080 | } | 1108 | } |
| 1081 | } | 1109 | } |
| 1082 | case CmdHelp: | 1110 | case CmdHelp: |
| 1083 | return print_full_usage(arg0); | 1111 | return print_full_usage(arg0, stdout, EXIT_SUCCESS); |
| 1084 | case CmdVersion: | 1112 | case CmdVersion: |
| 1085 | printf("%s\n", ZIG_VERSION_STRING); | 1113 | printf("%s\n", ZIG_VERSION_STRING); |
| 1086 | return EXIT_SUCCESS; | 1114 | return EXIT_SUCCESS; |
std/special/fmt_runner.zig created+265| ... | @@ -0,0 +1,265 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const os = std.os; | ||
| 5 | const io = std.io; | ||
| 6 | const mem = std.mem; | ||
| 7 | const Allocator = mem.Allocator; | ||
| 8 | const ArrayList = std.ArrayList; | ||
| 9 | const Buffer = std.Buffer; | ||
| 10 | const ast = std.zig.ast; | ||
| 11 | |||
| 12 | const arg = @import("fmt/arg.zig"); | ||
| 13 | const self_hosted_main = @import("fmt/main.zig"); | ||
| 14 | const Args = arg.Args; | ||
| 15 | const Flag = arg.Flag; | ||
| 16 | const errmsg = @import("fmt/errmsg.zig"); | ||
| 17 | |||
| 18 | var stderr_file: os.File = undefined; | ||
| 19 | var stderr: *io.OutStream(os.File.WriteError) = undefined; | ||
| 20 | var stdout: *io.OutStream(os.File.WriteError) = undefined; | ||
| 21 | |||
| 22 | // This brings `zig fmt` to stage 1. | ||
| 23 | pub fn main() !void { | ||
| 24 | // Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` is a short-lived, | ||
| 25 | // one shot program. We don't need to waste time freeing memory and finding places to squish | ||
| 26 | // bytes into. So we free everything all at once at the very end. | ||
| 27 | var direct_allocator = std.heap.DirectAllocator.init(); | ||
| 28 | var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator); | ||
| 29 | const allocator = &arena.allocator; | ||
| 30 | |||
| 31 | var stdout_file = try std.io.getStdOut(); | ||
| 32 | var stdout_out_stream = stdout_file.outStream(); | ||
| 33 | stdout = &stdout_out_stream.stream; | ||
| 34 | |||
| 35 | stderr_file = try std.io.getStdErr(); | ||
| 36 | var stderr_out_stream = stderr_file.outStream(); | ||
| 37 | stderr = &stderr_out_stream.stream; | ||
| 38 | const args = try std.os.argsAlloc(allocator); | ||
| 39 | |||
| 40 | var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args); | ||
| 41 | defer flags.deinit(); | ||
| 42 | |||
| 43 | if (flags.present("help")) { | ||
| 44 | try stdout.write(self_hosted_main.usage_fmt); | ||
| 45 | os.exit(0); | ||
| 46 | } | ||
| 47 | |||
| 48 | const color = blk: { | ||
| 49 | if (flags.single("color")) |color_flag| { | ||
| 50 | if (mem.eql(u8, color_flag, "auto")) { | ||
| 51 | break :blk errmsg.Color.Auto; | ||
| 52 | } else if (mem.eql(u8, color_flag, "on")) { | ||
| 53 | break :blk errmsg.Color.On; | ||
| 54 | } else if (mem.eql(u8, color_flag, "off")) { | ||
| 55 | break :blk errmsg.Color.Off; | ||
| 56 | } else unreachable; | ||
| 57 | } else { | ||
| 58 | break :blk errmsg.Color.Auto; | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | if (flags.present("stdin")) { | ||
| 63 | if (flags.positionals.len != 0) { | ||
| 64 | try stderr.write("cannot use --stdin with positional arguments\n"); | ||
| 65 | os.exit(1); | ||
| 66 | } | ||
| 67 | |||
| 68 | var stdin_file = try io.getStdIn(); | ||
| 69 | var stdin = stdin_file.inStream(); | ||
| 70 | |||
| 71 | const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size); | ||
| 72 | defer allocator.free(source_code); | ||
| 73 | |||
| 74 | var tree = std.zig.parse(allocator, source_code) catch |err| { | ||
| 75 | try stderr.print("error parsing stdin: {}\n", err); | ||
| 76 | os.exit(1); | ||
| 77 | }; | ||
| 78 | defer tree.deinit(); | ||
| 79 | |||
| 80 | var error_it = tree.errors.iterator(0); | ||
| 81 | while (error_it.next()) |parse_error| { | ||
| 82 | try printErrMsgToFile(allocator, parse_error, &tree, "<stdin>", stderr_file, color); | ||
| 83 | } | ||
| 84 | if (tree.errors.len != 0) { | ||
| 85 | os.exit(1); | ||
| 86 | } | ||
| 87 | if (flags.present("check")) { | ||
| 88 | const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree); | ||
| 89 | const code = if (anything_changed) u8(1) else u8(0); | ||
| 90 | os.exit(code); | ||
| 91 | } | ||
| 92 | |||
| 93 | _ = try std.zig.render(allocator, stdout, &tree); | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (flags.positionals.len == 0) { | ||
| 98 | try stderr.write("expected at least one source file argument\n"); | ||
| 99 | os.exit(1); | ||
| 100 | } | ||
| 101 | |||
| 102 | if (flags.positionals.len == 0) { | ||
| 103 | try stderr.write("expected at least one source file argument\n"); | ||
| 104 | os.exit(1); | ||
| 105 | } | ||
| 106 | |||
| 107 | var fmt = Fmt{ | ||
| 108 | .seen = Fmt.SeenMap.init(allocator), | ||
| 109 | .any_error = false, | ||
| 110 | .color = color, | ||
| 111 | .allocator = allocator, | ||
| 112 | }; | ||
| 113 | |||
| 114 | const check_mode = flags.present("check"); | ||
| 115 | |||
| 116 | for (flags.positionals.toSliceConst()) |file_path| { | ||
| 117 | try fmtPath(&fmt, file_path, check_mode); | ||
| 118 | } | ||
| 119 | if (fmt.any_error) { | ||
| 120 | os.exit(1); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | const FmtError = error{ | ||
| 125 | SystemResources, | ||
| 126 | OperationAborted, | ||
| 127 | IoPending, | ||
| 128 | BrokenPipe, | ||
| 129 | Unexpected, | ||
| 130 | WouldBlock, | ||
| 131 | FileClosed, | ||
| 132 | DestinationAddressRequired, | ||
| 133 | DiskQuota, | ||
| 134 | FileTooBig, | ||
| 135 | InputOutput, | ||
| 136 | NoSpaceLeft, | ||
| 137 | AccessDenied, | ||
| 138 | OutOfMemory, | ||
| 139 | RenameAcrossMountPoints, | ||
| 140 | ReadOnlyFileSystem, | ||
| 141 | LinkQuotaExceeded, | ||
| 142 | FileBusy, | ||
| 143 | } || os.File.OpenError; | ||
| 144 | |||
| 145 | fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { | ||
| 146 | const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); | ||
| 147 | defer fmt.allocator.free(file_path); | ||
| 148 | |||
| 149 | if (try fmt.seen.put(file_path, {})) |_| return; | ||
| 150 | |||
| 151 | const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { | ||
| 152 | error.IsDir, error.AccessDenied => { | ||
| 153 | // TODO make event based (and dir.next()) | ||
| 154 | var dir = try std.os.Dir.open(fmt.allocator, file_path); | ||
| 155 | defer dir.close(); | ||
| 156 | |||
| 157 | while (try dir.next()) |entry| { | ||
| 158 | if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { | ||
| 159 | const full_path = try os.path.join(fmt.allocator, [][]const u8{ file_path, entry.name }); | ||
| 160 | try fmtPath(fmt, full_path, check_mode); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | return; | ||
| 164 | }, | ||
| 165 | else => { | ||
| 166 | // TODO lock stderr printing | ||
| 167 | try stderr.print("unable to open '{}': {}\n", file_path, err); | ||
| 168 | fmt.any_error = true; | ||
| 169 | return; | ||
| 170 | }, | ||
| 171 | }; | ||
| 172 | defer fmt.allocator.free(source_code); | ||
| 173 | |||
| 174 | var tree = std.zig.parse(fmt.allocator, source_code) catch |err| { | ||
| 175 | try stderr.print("error parsing file '{}': {}\n", file_path, err); | ||
| 176 | fmt.any_error = true; | ||
| 177 | return; | ||
| 178 | }; | ||
| 179 | defer tree.deinit(); | ||
| 180 | |||
| 181 | var error_it = tree.errors.iterator(0); | ||
| 182 | while (error_it.next()) |parse_error| { | ||
| 183 | try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color); | ||
| 184 | } | ||
| 185 | if (tree.errors.len != 0) { | ||
| 186 | fmt.any_error = true; | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | |||
| 190 | if (check_mode) { | ||
| 191 | const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, &tree); | ||
| 192 | if (anything_changed) { | ||
| 193 | try stderr.print("{}\n", file_path); | ||
| 194 | fmt.any_error = true; | ||
| 195 | } | ||
| 196 | } else { | ||
| 197 | // TODO make this evented | ||
| 198 | const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); | ||
| 199 | defer baf.destroy(); | ||
| 200 | |||
| 201 | const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree); | ||
| 202 | if (anything_changed) { | ||
| 203 | try stderr.print("{}\n", file_path); | ||
| 204 | try baf.finish(); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | const Fmt = struct { | ||
| 210 | seen: SeenMap, | ||
| 211 | any_error: bool, | ||
| 212 | color: errmsg.Color, | ||
| 213 | allocator: *mem.Allocator, | ||
| 214 | |||
| 215 | const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); | ||
| 216 | }; | ||
| 217 | |||
| 218 | fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree, | ||
| 219 | path: []const u8, file: os.File, color: errmsg.Color,) !void | ||
| 220 | { | ||
| 221 | const color_on = switch (color) { | ||
| 222 | errmsg.Color.Auto => file.isTty(), | ||
| 223 | errmsg.Color.On => true, | ||
| 224 | errmsg.Color.Off => false, | ||
| 225 | }; | ||
| 226 | const lok_token = parse_error.loc(); | ||
| 227 | const span = errmsg.Span{ | ||
| 228 | .first = lok_token, | ||
| 229 | .last = lok_token, | ||
| 230 | }; | ||
| 231 | |||
| 232 | const first_token = tree.tokens.at(span.first); | ||
| 233 | const last_token = tree.tokens.at(span.last); | ||
| 234 | const start_loc = tree.tokenLocationPtr(0, first_token); | ||
| 235 | const end_loc = tree.tokenLocationPtr(first_token.end, last_token); | ||
| 236 | |||
| 237 | var text_buf = try std.Buffer.initSize(allocator, 0); | ||
| 238 | var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; | ||
| 239 | try parse_error.render(&tree.tokens, out_stream); | ||
| 240 | const text = text_buf.toOwnedSlice(); | ||
| 241 | |||
| 242 | const stream = &file.outStream().stream; | ||
| 243 | if (!color_on) { | ||
| 244 | try stream.print( | ||
| 245 | "{}:{}:{}: error: {}\n", | ||
| 246 | path, | ||
| 247 | start_loc.line + 1, | ||
| 248 | start_loc.column + 1, | ||
| 249 | text, | ||
| 250 | ); | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | |||
| 254 | try stream.print( | ||
| 255 | "{}:{}:{}: error: {}\n{}\n", | ||
| 256 | path, | ||
| 257 | start_loc.line + 1, | ||
| 258 | start_loc.column + 1, | ||
| 259 | text, | ||
| 260 | tree.source[start_loc.line_start..start_loc.line_end], | ||
| 261 | ); | ||
| 262 | try stream.writeByteNTimes(' ', start_loc.column); | ||
| 263 | try stream.writeByteNTimes('~', last_token.end - first_token.start); | ||
| 264 | try stream.write("\n"); | ||
| 265 | } | ||