authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-06 16:36:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-10 17:51:06-07:00
log34faf9d12e3ce7b203a8e6e51e1652a985cfb626
treef8807b97ca9896af9dc93c7706be3b7c742c2df0
parent33f3443243059ce7634dcddf8a87d6ed7837df06

add skeleton of `zig std` command impl


1 files changed, 164 insertions(+), 0 deletions(-)

lib/compiler/std-docs.zig created+164
......@@ -0,0 +1,164 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const Allocator = std.mem.Allocator;
4
5pub fn main() !void {
6 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
7 defer arena_instance.deinit();
8 const arena = arena_instance.allocator();
9
10 var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
11 const gpa = general_purpose_allocator.allocator();
12
13 const args = try std.process.argsAlloc(arena);
14 const zig_lib_directory = args[1];
15 const zig_exe_path = args[2];
16 const global_cache_path = args[3];
17
18 const docs_path = try std.fs.path.join(arena, &.{ zig_lib_directory, "docs" });
19 var docs_dir = try std.fs.cwd().openDir(docs_path, .{});
20 defer docs_dir.close();
21
22 const listen_port: u16 = 0;
23 const address = std.net.Address.parseIp("127.0.0.1", listen_port) catch unreachable;
24 var http_server = try address.listen(.{});
25 const port = http_server.listen_address.in.getPort();
26 const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port});
27 std.io.getStdOut().writeAll(url) catch {};
28 openBrowserTab(gpa, url[0 .. url.len - 1 :'\n']) catch |err| {
29 std.log.err("unable to open browser: {s}", .{@errorName(err)});
30 };
31
32 var read_buffer: [8000]u8 = undefined;
33 accept: while (true) {
34 const connection = try http_server.accept();
35 defer connection.stream.close();
36
37 var server = std.http.Server.init(connection, &read_buffer);
38 while (server.state == .ready) {
39 var request = server.receiveHead() catch |err| switch (err) {
40 error.HttpConnectionClosing => continue :accept,
41 else => {
42 std.log.err("closing http connection: {s}", .{@errorName(err)});
43 continue :accept;
44 },
45 };
46 serveRequest(&request, gpa, docs_dir, zig_exe_path, global_cache_path) catch |err| {
47 std.log.err("unable to serve {s}: {s}", .{ request.head.target, @errorName(err) });
48 continue :accept;
49 };
50 }
51 }
52}
53
54fn serveRequest(
55 request: *std.http.Server.Request,
56 gpa: Allocator,
57 docs_dir: std.fs.Dir,
58 zig_exe_path: []const u8,
59 global_cache_path: []const u8,
60) !void {
61 if (std.mem.eql(u8, request.head.target, "/") or
62 std.mem.eql(u8, request.head.target, "/debug/"))
63 {
64 try serveDocsFile(request, gpa, docs_dir, "index.html", "text/html");
65 } else if (std.mem.eql(u8, request.head.target, "/main.js") or
66 std.mem.eql(u8, request.head.target, "/debug/main.js"))
67 {
68 try serveDocsFile(request, gpa, docs_dir, "main.js", "application/javascript");
69 } else if (std.mem.eql(u8, request.head.target, "/main.wasm")) {
70 try serveWasm(request, gpa, zig_exe_path, global_cache_path, .ReleaseFast);
71 } else if (std.mem.eql(u8, request.head.target, "/debug/main.wasm")) {
72 try serveWasm(request, gpa, zig_exe_path, global_cache_path, .Debug);
73 } else {
74 try request.respond("not found", .{
75 .status = .not_found,
76 .extra_headers = &.{
77 .{ .name = "content-type", .value = "text/plain" },
78 },
79 });
80 }
81}
82
83fn serveDocsFile(
84 request: *std.http.Server.Request,
85 gpa: Allocator,
86 docs_dir: std.fs.Dir,
87 name: []const u8,
88 content_type: []const u8,
89) !void {
90 // The desired API is actually sendfile, which will require enhancing std.http.Server.
91 // We load the file with every request so that the user can make changes to the file
92 // and refresh the HTML page without restarting this server.
93 const file_contents = try docs_dir.readFileAlloc(gpa, name, 10 * 1024 * 1024);
94 defer gpa.free(file_contents);
95 try request.respond(file_contents, .{
96 .status = .ok,
97 .extra_headers = &.{
98 .{ .name = "content-type", .value = content_type },
99 },
100 });
101}
102
103fn serveWasm(
104 request: *std.http.Server.Request,
105 gpa: Allocator,
106 zig_exe_path: []const u8,
107 global_cache_path: []const u8,
108 optimize_mode: std.builtin.OptimizeMode,
109) !void {
110 _ = request;
111 _ = gpa;
112 _ = zig_exe_path;
113 _ = global_cache_path;
114 _ = optimize_mode;
115 @panic("TODO serve wasm");
116}
117
118const BuildWasmBinaryOptions = struct {
119 zig_exe_path: []const u8,
120 global_cache_path: []const u8,
121 main_src_path: []const u8,
122};
123
124fn buildWasmBinary(arena: Allocator, options: BuildWasmBinaryOptions) ![]const u8 {
125 var argv: std.ArrayListUnmanaged([]const u8) = .{};
126 try argv.appendSlice(arena, &.{
127 options.zig_exe_path,
128 "build-exe",
129 "-fno-entry",
130 "-OReleaseSmall",
131 "-target",
132 "wasm32-freestanding",
133 "-mcpu",
134 "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext",
135 "--cache-dir",
136 options.global_cache_path,
137 "--global-cache-dir",
138 options.global_cache_path,
139 "--name",
140 "autodoc",
141 "-rdynamic",
142 options.main_src_path,
143 "--listen=-",
144 });
145}
146
147fn openBrowserTab(gpa: Allocator, url: []const u8) !void {
148 // Until https://github.com/ziglang/zig/issues/19205 is implemented, we
149 // spawn a thread for this child process.
150 _ = try std.Thread.spawn(.{}, openBrowserTabThread, .{ gpa, url });
151}
152
153fn openBrowserTabThread(gpa: Allocator, url: []const u8) !void {
154 const main_exe = switch (builtin.os.tag) {
155 .windows => "explorer",
156 else => "xdg-open",
157 };
158 var child = std.ChildProcess.init(&.{ main_exe, url }, gpa);
159 child.stdin_behavior = .Ignore;
160 child.stdout_behavior = .Ignore;
161 child.stderr_behavior = .Ignore;
162 try child.spawn();
163 _ = try child.wait();
164}