authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-13 11:16:06-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-04-13 11:16:06-04:00
log30c5f3c441e6090f29c0540b1fa8a20d6bd2fc0d
tree420a68427efe9c2e3f95f50f3f56ebc9ca7aa14d
parent1999f0daad505f414f97845ecde0a56b3c2fedfd
parentfe9489ad63ea8231bb0366d7608e52d0d30bfefb
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #915 from zig-lang/self-hosted-cli

Revise self-hosted command line interface

11 files changed, 1334 insertions(+), 726 deletions(-)

src-self-hosted/arg.zig created+284
...@@ -0,0 +1,284 @@
1const std = @import("std");
2const debug = std.debug;
3const mem = std.mem;
4
5const Allocator = mem.Allocator;
6const ArrayList = std.ArrayList;
7const HashMap = std.HashMap;
8
9fn trimStart(slice: []const u8, ch: u8) []const u8 {
10 var i: usize = 0;
11 for (slice) |b| {
12 if (b != '-') break;
13 i += 1;
14 }
15
16 return slice[i..];
17}
18
19fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool {
20 if (maybe_set) |set| {
21 for (set) |possible| {
22 if (mem.eql(u8, arg, possible)) {
23 return true;
24 }
25 }
26 return false;
27 } else {
28 return true;
29 }
30}
31
32// Modifies the current argument index during iteration
33fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize,
34 allowed_set: ?[]const []const u8, index: &usize) !FlagArg {
35
36 switch (required) {
37 0 => return FlagArg { .None = undefined }, // TODO: Required to force non-tag but value?
38 1 => {
39 if (*index + 1 >= args.len) {
40 return error.MissingFlagArguments;
41 }
42
43 *index += 1;
44 const arg = args[*index];
45
46 if (!argInAllowedSet(allowed_set, arg)) {
47 return error.ArgumentNotInAllowedSet;
48 }
49
50 return FlagArg { .Single = arg };
51 },
52 else => |needed| {
53 var extra = ArrayList([]const u8).init(allocator);
54 errdefer extra.deinit();
55
56 var j: usize = 0;
57 while (j < needed) : (j += 1) {
58 if (*index + 1 >= args.len) {
59 return error.MissingFlagArguments;
60 }
61
62 *index += 1;
63 const arg = args[*index];
64
65 if (!argInAllowedSet(allowed_set, arg)) {
66 return error.ArgumentNotInAllowedSet;
67 }
68
69 try extra.append(arg);
70 }
71
72 return FlagArg { .Many = extra };
73 },
74 }
75}
76
77const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8);
78
79// A store for querying found flags and positional arguments.
80pub const Args = struct {
81 flags: HashMapFlags,
82 positionals: ArrayList([]const u8),
83
84 pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
85 var parsed = Args {
86 .flags = HashMapFlags.init(allocator),
87 .positionals = ArrayList([]const u8).init(allocator),
88 };
89
90 var i: usize = 0;
91 next: while (i < args.len) : (i += 1) {
92 const arg = args[i];
93
94 if (arg.len != 0 and arg[0] == '-') {
95 // TODO: hashmap, although the linear scan is okay for small argument sets as is
96 for (spec) |flag| {
97 if (mem.eql(u8, arg, flag.name)) {
98 const flag_name_trimmed = trimStart(flag.name, '-');
99 const flag_args = readFlagArguments(allocator, args, flag.required, flag.allowed_set, &i) catch |err| {
100 switch (err) {
101 error.ArgumentNotInAllowedSet => {
102 std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
103 std.debug.warn("allowed options are ");
104 for (??flag.allowed_set) |possible| {
105 std.debug.warn("'{}' ", possible);
106 }
107 std.debug.warn("\n");
108 },
109 error.MissingFlagArguments => {
110 std.debug.warn("missing argument for flag: {}\n", arg);
111 },
112 else => {},
113 }
114
115 return err;
116 };
117
118 if (flag.mergable) {
119 var prev =
120 if (parsed.flags.get(flag_name_trimmed)) |entry|
121 entry.value.Many
122 else
123 ArrayList([]const u8).init(allocator);
124
125 // MergeN creation disallows 0 length flag entry (doesn't make sense)
126 switch (flag_args) {
127 FlagArg.None => unreachable,
128 FlagArg.Single => |inner| try prev.append(inner),
129 FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()),
130 }
131
132 _ = try parsed.flags.put(flag_name_trimmed, FlagArg { .Many = prev });
133 } else {
134 _ = try parsed.flags.put(flag_name_trimmed, flag_args);
135 }
136
137 continue :next;
138 }
139 }
140
141 // TODO: Better errors with context, global error state and return is sufficient.
142 std.debug.warn("could not match flag: {}\n", arg);
143 return error.UnknownFlag;
144 } else {
145 try parsed.positionals.append(arg);
146 }
147 }
148
149 return parsed;
150 }
151
152 pub fn deinit(self: &Args) void {
153 self.flags.deinit();
154 self.positionals.deinit();
155 }
156
157 // e.g. --help
158 pub fn present(self: &Args, name: []const u8) bool {
159 return self.flags.contains(name);
160 }
161
162 // e.g. --name value
163 pub fn single(self: &Args, name: []const u8) ?[]const u8 {
164 if (self.flags.get(name)) |entry| {
165 switch (entry.value) {
166 FlagArg.Single => |inner| { return inner; },
167 else => @panic("attempted to retrieve flag with wrong type"),
168 }
169 } else {
170 return null;
171 }
172 }
173
174 // e.g. --names value1 value2 value3
175 pub fn many(self: &Args, name: []const u8) ?[]const []const u8 {
176 if (self.flags.get(name)) |entry| {
177 switch (entry.value) {
178 FlagArg.Many => |inner| { return inner.toSliceConst(); },
179 else => @panic("attempted to retrieve flag with wrong type"),
180 }
181 } else {
182 return null;
183 }
184 }
185};
186
187// Arguments for a flag. e.g. arg1, arg2 in `--command arg1 arg2`.
188const FlagArg = union(enum) {
189 None,
190 Single: []const u8,
191 Many: ArrayList([]const u8),
192};
193
194// Specification for how a flag should be parsed.
195pub const Flag = struct {
196 name: []const u8,
197 required: usize,
198 mergable: bool,
199 allowed_set: ?[]const []const u8,
200
201 pub fn Bool(comptime name: []const u8) Flag {
202 return ArgN(name, 0);
203 }
204
205 pub fn Arg1(comptime name: []const u8) Flag {
206 return ArgN(name, 1);
207 }
208
209 pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag {
210 return Flag {
211 .name = name,
212 .required = n,
213 .mergable = false,
214 .allowed_set = null,
215 };
216 }
217
218 pub fn ArgMergeN(comptime name: []const u8, comptime n: usize) Flag {
219 if (n == 0) {
220 @compileError("n must be greater than 0");
221 }
222
223 return Flag {
224 .name = name,
225 .required = n,
226 .mergable = true,
227 .allowed_set = null,
228 };
229 }
230
231 pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag {
232 return Flag {
233 .name = name,
234 .required = 1,
235 .mergable = false,
236 .allowed_set = set,
237 };
238 }
239};
240
241test "parse arguments" {
242 const spec1 = comptime []const Flag {
243 Flag.Bool("--help"),
244 Flag.Bool("--init"),
245 Flag.Arg1("--build-file"),
246 Flag.Option("--color", []const []const u8 { "on", "off", "auto" }),
247 Flag.ArgN("--pkg-begin", 2),
248 Flag.ArgMergeN("--object", 1),
249 Flag.ArgN("--library", 1),
250 };
251
252 const cliargs = []const []const u8 {
253 "build",
254 "--help",
255 "pos1",
256 "--build-file", "build.zig",
257 "--object", "obj1",
258 "--object", "obj2",
259 "--library", "lib1",
260 "--library", "lib2",
261 "--color", "on",
262 "pos2",
263 };
264
265 var args = try Args.parse(std.debug.global_allocator, spec1, cliargs);
266
267 debug.assert(args.present("help"));
268 debug.assert(!args.present("help2"));
269 debug.assert(!args.present("init"));
270
271 debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));
272 debug.assert(mem.eql(u8, ??args.single("color"), "on"));
273
274 const objects = ??args.many("object");
275 debug.assert(mem.eql(u8, objects[0], "obj1"));
276 debug.assert(mem.eql(u8, objects[1], "obj2"));
277
278 debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));
279
280 const pos = args.positionals.toSliceConst();
281 debug.assert(mem.eql(u8, pos[0], "build"));
282 debug.assert(mem.eql(u8, pos[1], "pos1"));
283 debug.assert(mem.eql(u8, pos[2], "pos2"));
284}
src-self-hosted/introspect.zig created+57
...@@ -0,0 +1,57 @@
1// Introspection and determination of system libraries needed by zig.
2
3const std = @import("std");
4const mem = std.mem;
5const os = std.os;
6
7const warn = std.debug.warn;
8
9/// Caller must free result
10pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
11 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
12 errdefer allocator.free(test_zig_dir);
13
14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
15 defer allocator.free(test_index_file);
16
17 var file = try os.File.openRead(allocator, test_index_file);
18 file.close();
19
20 return test_zig_dir;
21}
22
23/// Caller must free result
24pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);
26 defer allocator.free(self_exe_path);
27
28 var cur_path: []const u8 = self_exe_path;
29 while (true) {
30 const test_dir = os.path.dirname(cur_path);
31
32 if (mem.eql(u8, test_dir, cur_path)) {
33 break;
34 }
35
36 return testZigInstallPrefix(allocator, test_dir) catch |err| {
37 cur_path = test_dir;
38 continue;
39 };
40 }
41
42 return error.FileNotFound;
43}
44
45pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 {
46 return findZigLibDir(allocator) catch |err| {
47 warn(
48 \\Unable to find zig lib directory: {}.
49 \\Reinstall Zig or use --zig-install-prefix.
50 \\
51 ,
52 @errorName(err)
53 );
54
55 return error.ZigLibDirNotFound;
56 };
57}
src-self-hosted/main.zig+881-700
...@@ -1,613 +1,165 @@...@@ -1,613 +1,165 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;
3const io = std.io;
4const os = std.os;
5const heap = std.heap;
6const warn = std.debug.warn;
7const assert = std.debug.assert;
8const target = @import("target.zig");
9const Target = target.Target;
10const Module = @import("module.zig").Module;
11const ErrColor = Module.ErrColor;
12const Emit = Module.Emit;
13const builtin = @import("builtin");2const builtin = @import("builtin");
14const ArrayList = std.ArrayList;
15const c = @import("c.zig");
163
17const default_zig_cache_name = "zig-cache";4const os = std.os;
5const io = std.io;
6const mem = std.mem;
7const Allocator = mem.Allocator;
8const ArrayList = std.ArrayList;
9const Buffer = std.Buffer;
1810
19const Cmd = enum {11const arg = @import("arg.zig");
20 None,12const c = @import("c.zig");
21 Build,13const introspect = @import("introspect.zig");
22 Test,14const Args = arg.Args;
23 Version,15const Flag = arg.Flag;
24 Zen,16const Module = @import("module.zig").Module;
25 TranslateC,17const Target = @import("target.zig").Target;
26 Targets,18
19var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;
20var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;
21
22const usage =
23 \\usage: zig [command] [options]
24 \\
25 \\Commands:
26 \\
27 \\ build Build project from build.zig
28 \\ build-exe [source] Create executable from source or object files
29 \\ build-lib [source] Create library from source or object files
30 \\ build-obj [source] Create object from source or assembly
31 \\ fmt [source] Parse file and render in canonical zig format
32 \\ run [source] Create executable and run immediately
33 \\ targets List available compilation targets
34 \\ test [source] Create and run a test build
35 \\ translate-c [source] Convert c code to zig code
36 \\ version Print version number and exit
37 \\ zen Print zen of zig and exit
38 \\
39 \\
40 ;
41
42const Command = struct {
43 name: []const u8,
44 exec: fn(&Allocator, []const []const u8) error!void,
27};45};
2846
29fn badArgs(comptime format: []const u8, args: ...) noreturn {
30 var stderr = io.getStdErr() catch std.os.exit(1);
31 var stderr_stream_adapter = io.FileOutStream.init(&stderr);
32 const stderr_stream = &stderr_stream_adapter.stream;
33 stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
34 printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
35 std.os.exit(1);
36}
37
38pub fn main() !void {47pub fn main() !void {
39 const allocator = std.heap.c_allocator;48 var allocator = std.heap.c_allocator;
49
50 var stdout_file = try std.io.getStdOut();
51 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
52 stdout = &stdout_out_stream.stream;
53
54 var stderr_file = try std.io.getStdErr();
55 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
56 stderr = &stderr_out_stream.stream;
4057
41 const args = try os.argsAlloc(allocator);58 const args = try os.argsAlloc(allocator);
42 defer os.argsFree(allocator, args);59 defer os.argsFree(allocator, args);
4360
44 if (args.len >= 2 and mem.eql(u8, args[1], "build")) {61 if (args.len <= 1) {
45 return buildMain(allocator, args[2..]);62 try stderr.write(usage);
46 }63 os.exit(1);
47
48 if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
49 return fmtMain(allocator, args[2..]);
50 }
51
52 var cmd = Cmd.None;
53 var build_kind: Module.Kind = undefined;
54 var build_mode: builtin.Mode = builtin.Mode.Debug;
55 var color = ErrColor.Auto;
56 var emit_file_type = Emit.Binary;
57
58 var strip = false;
59 var is_static = false;
60 var verbose_tokenize = false;
61 var verbose_ast_tree = false;
62 var verbose_ast_fmt = false;
63 var verbose_link = false;
64 var verbose_ir = false;
65 var verbose_llvm_ir = false;
66 var verbose_cimport = false;
67 var mwindows = false;
68 var mconsole = false;
69 var rdynamic = false;
70 var each_lib_rpath = false;
71 var timing_info = false;
72
73 var in_file_arg: ?[]u8 = null;
74 var out_file: ?[]u8 = null;
75 var out_file_h: ?[]u8 = null;
76 var out_name_arg: ?[]u8 = null;
77 var libc_lib_dir_arg: ?[]u8 = null;
78 var libc_static_lib_dir_arg: ?[]u8 = null;
79 var libc_include_dir_arg: ?[]u8 = null;
80 var msvc_lib_dir_arg: ?[]u8 = null;
81 var kernel32_lib_dir_arg: ?[]u8 = null;
82 var zig_install_prefix: ?[]u8 = null;
83 var dynamic_linker_arg: ?[]u8 = null;
84 var cache_dir_arg: ?[]const u8 = null;
85 var target_arch: ?[]u8 = null;
86 var target_os: ?[]u8 = null;
87 var target_environ: ?[]u8 = null;
88 var mmacosx_version_min: ?[]u8 = null;
89 var mios_version_min: ?[]u8 = null;
90 var linker_script_arg: ?[]u8 = null;
91 var test_name_prefix_arg: ?[]u8 = null;
92
93 var test_filters = ArrayList([]const u8).init(allocator);
94 defer test_filters.deinit();
95
96 var lib_dirs = ArrayList([]const u8).init(allocator);
97 defer lib_dirs.deinit();
98
99 var clang_argv = ArrayList([]const u8).init(allocator);
100 defer clang_argv.deinit();
101
102 var llvm_argv = ArrayList([]const u8).init(allocator);
103 defer llvm_argv.deinit();
104
105 var link_libs = ArrayList([]const u8).init(allocator);
106 defer link_libs.deinit();
107
108 var frameworks = ArrayList([]const u8).init(allocator);
109 defer frameworks.deinit();
110
111 var objects = ArrayList([]const u8).init(allocator);
112 defer objects.deinit();
113
114 var asm_files = ArrayList([]const u8).init(allocator);
115 defer asm_files.deinit();
116
117 var rpath_list = ArrayList([]const u8).init(allocator);
118 defer rpath_list.deinit();
119
120 var ver_major: u32 = 0;
121 var ver_minor: u32 = 0;
122 var ver_patch: u32 = 0;
123
124 var arg_i: usize = 1;
125 while (arg_i < args.len) : (arg_i += 1) {
126 const arg = args[arg_i];
127
128 if (arg.len != 0 and arg[0] == '-') {
129 if (mem.eql(u8, arg, "--release-fast")) {
130 build_mode = builtin.Mode.ReleaseFast;
131 } else if (mem.eql(u8, arg, "--release-safe")) {
132 build_mode = builtin.Mode.ReleaseSafe;
133 } else if (mem.eql(u8, arg, "--strip")) {
134 strip = true;
135 } else if (mem.eql(u8, arg, "--static")) {
136 is_static = true;
137 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
138 verbose_tokenize = true;
139 } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
140 verbose_ast_tree = true;
141 } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
142 verbose_ast_fmt = true;
143 } else if (mem.eql(u8, arg, "--verbose-link")) {
144 verbose_link = true;
145 } else if (mem.eql(u8, arg, "--verbose-ir")) {
146 verbose_ir = true;
147 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
148 verbose_llvm_ir = true;
149 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
150 verbose_cimport = true;
151 } else if (mem.eql(u8, arg, "-mwindows")) {
152 mwindows = true;
153 } else if (mem.eql(u8, arg, "-mconsole")) {
154 mconsole = true;
155 } else if (mem.eql(u8, arg, "-rdynamic")) {
156 rdynamic = true;
157 } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
158 each_lib_rpath = true;
159 } else if (mem.eql(u8, arg, "--enable-timing-info")) {
160 timing_info = true;
161 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
162 @panic("TODO --test-cmd-bin");
163 } else if (arg[1] == 'L' and arg.len > 2) {
164 // alias for --library-path
165 try lib_dirs.append(arg[1..]);
166 } else if (mem.eql(u8, arg, "--pkg-begin")) {
167 @panic("TODO --pkg-begin");
168 } else if (mem.eql(u8, arg, "--pkg-end")) {
169 @panic("TODO --pkg-end");
170 } else if (arg_i + 1 >= args.len) {
171 badArgs("expected another argument after {}", arg);
172 } else {
173 arg_i += 1;
174 if (mem.eql(u8, arg, "--output")) {
175 out_file = args[arg_i];
176 } else if (mem.eql(u8, arg, "--output-h")) {
177 out_file_h = args[arg_i];
178 } else if (mem.eql(u8, arg, "--color")) {
179 if (mem.eql(u8, args[arg_i], "auto")) {
180 color = ErrColor.Auto;
181 } else if (mem.eql(u8, args[arg_i], "on")) {
182 color = ErrColor.On;
183 } else if (mem.eql(u8, args[arg_i], "off")) {
184 color = ErrColor.Off;
185 } else {
186 badArgs("--color options are 'auto', 'on', or 'off'");
187 }
188 } else if (mem.eql(u8, arg, "--emit")) {
189 if (mem.eql(u8, args[arg_i], "asm")) {
190 emit_file_type = Emit.Assembly;
191 } else if (mem.eql(u8, args[arg_i], "bin")) {
192 emit_file_type = Emit.Binary;
193 } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
194 emit_file_type = Emit.LlvmIr;
195 } else {
196 badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
197 }
198 } else if (mem.eql(u8, arg, "--name")) {
199 out_name_arg = args[arg_i];
200 } else if (mem.eql(u8, arg, "--libc-lib-dir")) {
201 libc_lib_dir_arg = args[arg_i];
202 } else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
203 libc_static_lib_dir_arg = args[arg_i];
204 } else if (mem.eql(u8, arg, "--libc-include-dir")) {
205 libc_include_dir_arg = args[arg_i];
206 } else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
207 msvc_lib_dir_arg = args[arg_i];
208 } else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
209 kernel32_lib_dir_arg = args[arg_i];
210 } else if (mem.eql(u8, arg, "--zig-install-prefix")) {
211 zig_install_prefix = args[arg_i];
212 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
213 dynamic_linker_arg = args[arg_i];
214 } else if (mem.eql(u8, arg, "-isystem")) {
215 try clang_argv.append("-isystem");
216 try clang_argv.append(args[arg_i]);
217 } else if (mem.eql(u8, arg, "-dirafter")) {
218 try clang_argv.append("-dirafter");
219 try clang_argv.append(args[arg_i]);
220 } else if (mem.eql(u8, arg, "-mllvm")) {
221 try clang_argv.append("-mllvm");
222 try clang_argv.append(args[arg_i]);
223
224 try llvm_argv.append(args[arg_i]);
225 } else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
226 try lib_dirs.append(args[arg_i]);
227 } else if (mem.eql(u8, arg, "--library")) {
228 try link_libs.append(args[arg_i]);
229 } else if (mem.eql(u8, arg, "--object")) {
230 try objects.append(args[arg_i]);
231 } else if (mem.eql(u8, arg, "--assembly")) {
232 try asm_files.append(args[arg_i]);
233 } else if (mem.eql(u8, arg, "--cache-dir")) {
234 cache_dir_arg = args[arg_i];
235 } else if (mem.eql(u8, arg, "--target-arch")) {
236 target_arch = args[arg_i];
237 } else if (mem.eql(u8, arg, "--target-os")) {
238 target_os = args[arg_i];
239 } else if (mem.eql(u8, arg, "--target-environ")) {
240 target_environ = args[arg_i];
241 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
242 mmacosx_version_min = args[arg_i];
243 } else if (mem.eql(u8, arg, "-mios-version-min")) {
244 mios_version_min = args[arg_i];
245 } else if (mem.eql(u8, arg, "-framework")) {
246 try frameworks.append(args[arg_i]);
247 } else if (mem.eql(u8, arg, "--linker-script")) {
248 linker_script_arg = args[arg_i];
249 } else if (mem.eql(u8, arg, "-rpath")) {
250 try rpath_list.append(args[arg_i]);
251 } else if (mem.eql(u8, arg, "--test-filter")) {
252 try test_filters.append(args[arg_i]);
253 } else if (mem.eql(u8, arg, "--test-name-prefix")) {
254 test_name_prefix_arg = args[arg_i];
255 } else if (mem.eql(u8, arg, "--ver-major")) {
256 ver_major = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
257 } else if (mem.eql(u8, arg, "--ver-minor")) {
258 ver_minor = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
259 } else if (mem.eql(u8, arg, "--ver-patch")) {
260 ver_patch = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
261 } else if (mem.eql(u8, arg, "--test-cmd")) {
262 @panic("TODO --test-cmd");
263 } else {
264 badArgs("invalid argument: {}", arg);
265 }
266 }
267 } else if (cmd == Cmd.None) {
268 if (mem.eql(u8, arg, "build-obj")) {
269 cmd = Cmd.Build;
270 build_kind = Module.Kind.Obj;
271 } else if (mem.eql(u8, arg, "build-exe")) {
272 cmd = Cmd.Build;
273 build_kind = Module.Kind.Exe;
274 } else if (mem.eql(u8, arg, "build-lib")) {
275 cmd = Cmd.Build;
276 build_kind = Module.Kind.Lib;
277 } else if (mem.eql(u8, arg, "version")) {
278 cmd = Cmd.Version;
279 } else if (mem.eql(u8, arg, "zen")) {
280 cmd = Cmd.Zen;
281 } else if (mem.eql(u8, arg, "translate-c")) {
282 cmd = Cmd.TranslateC;
283 } else if (mem.eql(u8, arg, "test")) {
284 cmd = Cmd.Test;
285 build_kind = Module.Kind.Exe;
286 } else {
287 badArgs("unrecognized command: {}", arg);
288 }
289 } else switch (cmd) {
290 Cmd.Build, Cmd.TranslateC, Cmd.Test => {
291 if (in_file_arg == null) {
292 in_file_arg = arg;
293 } else {
294 badArgs("unexpected extra parameter: {}", arg);
295 }
296 },
297 Cmd.Version, Cmd.Zen, Cmd.Targets => {
298 badArgs("unexpected extra parameter: {}", arg);
299 },
300 Cmd.None => unreachable,
301 }
302 }64 }
30365
304 target.initializeAll();66 const commands = []Command {
30567 Command { .name = "build", .exec = cmdBuild },
306 // TODO68 Command { .name = "build-exe", .exec = cmdBuildExe },
307// ZigTarget alloc_target;69 Command { .name = "build-lib", .exec = cmdBuildLib },
308// ZigTarget *target;70 Command { .name = "build-obj", .exec = cmdBuildObj },
309// if (!target_arch && !target_os && !target_environ) {71 Command { .name = "fmt", .exec = cmdFmt },
310// target = nullptr;72 Command { .name = "run", .exec = cmdRun },
311// } else {73 Command { .name = "targets", .exec = cmdTargets },
312// target = &alloc_target;74 Command { .name = "test", .exec = cmdTest },
313// get_unknown_target(target);75 Command { .name = "translate-c", .exec = cmdTranslateC },
314// if (target_arch) {76 Command { .name = "version", .exec = cmdVersion },
315// if (parse_target_arch(target_arch, &target->arch)) {77 Command { .name = "zen", .exec = cmdZen },
316// fprintf(stderr, "invalid --target-arch argument\n");78
317// return usage(arg0);79 // undocumented commands
318// }80 Command { .name = "help", .exec = cmdHelp },
319// }81 Command { .name = "internal", .exec = cmdInternal },
320// if (target_os) {82 };
321// if (parse_target_os(target_os, &target->os)) {83
322// fprintf(stderr, "invalid --target-os argument\n");84 for (commands) |command| {
323// return usage(arg0);85 if (mem.eql(u8, command.name, args[1])) {
324// }86 try command.exec(allocator, args[2..]);
325// }87 return;
326// if (target_environ) {88 }
327// if (parse_target_environ(target_environ, &target->env_type)) {
328// fprintf(stderr, "invalid --target-environ argument\n");
329// return usage(arg0);
330// }
331// }
332// }
333
334 switch (cmd) {
335 Cmd.None => badArgs("expected command"),
336 Cmd.Zen => return printZen(),
337 Cmd.Build, Cmd.Test, Cmd.TranslateC => {
338 if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
339 badArgs("expected source file argument or at least one --object or --assembly argument");
340 } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
341 badArgs("expected source file argument");
342 } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
343 badArgs("When building an object file, --object arguments are invalid");
344 }
345
346 const root_name = switch (cmd) {
347 Cmd.Build, Cmd.TranslateC => x: {
348 if (out_name_arg) |out_name| {
349 break :x out_name;
350 } else if (in_file_arg) |in_file_path| {
351 const basename = os.path.basename(in_file_path);
352 var it = mem.split(basename, ".");
353 break :x it.next() ?? badArgs("file name cannot be empty");
354 } else {
355 badArgs("--name [name] not provided and unable to infer");
356 }
357 },
358 Cmd.Test => "test",
359 else => unreachable,
360 };
361
362 const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
363
364 const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
365 const full_cache_dir = try os.path.resolve(allocator, ".", chosen_cache_dir);
366 defer allocator.free(full_cache_dir);
367
368 const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
369 errdefer allocator.free(zig_lib_dir);
370
371 const module = try Module.create(allocator, root_name, zig_root_source_file,
372 Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
373 defer module.destroy();
374
375 module.version_major = ver_major;
376 module.version_minor = ver_minor;
377 module.version_patch = ver_patch;
378
379 module.is_test = cmd == Cmd.Test;
380 if (linker_script_arg) |linker_script| {
381 module.linker_script = linker_script;
382 }
383 module.each_lib_rpath = each_lib_rpath;
384 module.clang_argv = clang_argv.toSliceConst();
385 module.llvm_argv = llvm_argv.toSliceConst();
386 module.strip = strip;
387 module.is_static = is_static;
388
389 if (libc_lib_dir_arg) |libc_lib_dir| {
390 module.libc_lib_dir = libc_lib_dir;
391 }
392 if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
393 module.libc_static_lib_dir = libc_static_lib_dir;
394 }
395 if (libc_include_dir_arg) |libc_include_dir| {
396 module.libc_include_dir = libc_include_dir;
397 }
398 if (msvc_lib_dir_arg) |msvc_lib_dir| {
399 module.msvc_lib_dir = msvc_lib_dir;
400 }
401 if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
402 module.kernel32_lib_dir = kernel32_lib_dir;
403 }
404 if (dynamic_linker_arg) |dynamic_linker| {
405 module.dynamic_linker = dynamic_linker;
406 }
407 module.verbose_tokenize = verbose_tokenize;
408 module.verbose_ast_tree = verbose_ast_tree;
409 module.verbose_ast_fmt = verbose_ast_fmt;
410 module.verbose_link = verbose_link;
411 module.verbose_ir = verbose_ir;
412 module.verbose_llvm_ir = verbose_llvm_ir;
413 module.verbose_cimport = verbose_cimport;
414
415 module.err_color = color;
416
417 module.lib_dirs = lib_dirs.toSliceConst();
418 module.darwin_frameworks = frameworks.toSliceConst();
419 module.rpath_list = rpath_list.toSliceConst();
420
421 for (link_libs.toSliceConst()) |name| {
422 _ = try module.addLinkLib(name, true);
423 }
424
425 module.windows_subsystem_windows = mwindows;
426 module.windows_subsystem_console = mconsole;
427 module.linker_rdynamic = rdynamic;
428
429 if (mmacosx_version_min != null and mios_version_min != null) {
430 badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
431 }
432
433 if (mmacosx_version_min) |ver| {
434 module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
435 } else if (mios_version_min) |ver| {
436 module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
437 }
438
439 module.test_filters = test_filters.toSliceConst();
440 module.test_name_prefix = test_name_prefix_arg;
441 module.out_h_path = out_file_h;
442
443 // TODO
444 //add_package(g, cur_pkg, g->root_package);
445
446 switch (cmd) {
447 Cmd.Build => {
448 module.emit_file_type = emit_file_type;
449
450 module.link_objects = objects.toSliceConst();
451 module.assembly_files = asm_files.toSliceConst();
452
453 try module.build();
454 try module.link(out_file);
455 },
456 Cmd.TranslateC => @panic("TODO translate-c"),
457 Cmd.Test => @panic("TODO test cmd"),
458 else => unreachable,
459 }
460 },
461 Cmd.Version => {
462 var stdout_file = try io.getStdErr();
463 try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
464 try stdout_file.write("\n");
465 },
466 Cmd.Targets => @panic("TODO zig targets"),
467 }89 }
468}
46990
470fn printUsage(stream: var) !void {91 try stderr.print("unknown command: {}\n\n", args[1]);
471 try stream.write(92 try stderr.write(usage);
472 \\Usage: zig [command] [options]
473 \\
474 \\Commands:
475 \\ build build project from build.zig
476 \\ build-exe [source] create executable from source or object files
477 \\ build-lib [source] create library from source or object files
478 \\ build-obj [source] create object from source or assembly
479 \\ fmt [file] parse file and render in canonical zig format
480 \\ translate-c [source] convert c code to zig code
481 \\ targets list available compilation targets
482 \\ test [source] create and run a test build
483 \\ version print version number and exit
484 \\ zen print zen of zig and exit
485 \\Compile Options:
486 \\ --assembly [source] add assembly file to build
487 \\ --cache-dir [path] override the cache directory
488 \\ --color [auto|off|on] enable or disable colored error messages
489 \\ --emit [filetype] emit a specific file format as compilation output
490 \\ --enable-timing-info print timing diagnostics
491 \\ --libc-include-dir [path] directory where libc stdlib.h resides
492 \\ --name [name] override output name
493 \\ --output [file] override destination path
494 \\ --output-h [file] override generated header file path
495 \\ --pkg-begin [name] [path] make package available to import and push current pkg
496 \\ --pkg-end pop current pkg
497 \\ --release-fast build with optimizations on and safety off
498 \\ --release-safe build with optimizations on and safety on
499 \\ --static output will be statically linked
500 \\ --strip exclude debug symbols
501 \\ --target-arch [name] specify target architecture
502 \\ --target-environ [name] specify target environment
503 \\ --target-os [name] specify target operating system
504 \\ --verbose-tokenize enable compiler debug info: tokenization
505 \\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
506 \\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
507 \\ --verbose-cimport enable compiler debug info: C imports
508 \\ --verbose-ir enable compiler debug info: Zig IR
509 \\ --verbose-llvm-ir enable compiler debug info: LLVM IR
510 \\ --verbose-link enable compiler debug info: linking
511 \\ --zig-install-prefix [path] override directory where zig thinks it is installed
512 \\ -dirafter [dir] same as -isystem but do it last
513 \\ -isystem [dir] add additional search path for other .h files
514 \\ -mllvm [arg] additional arguments to forward to LLVM's option processing
515 \\Link Options:
516 \\ --ar-path [path] set the path to ar
517 \\ --dynamic-linker [path] set the path to ld.so
518 \\ --each-lib-rpath add rpath for each used dynamic library
519 \\ --libc-lib-dir [path] directory where libc crt1.o resides
520 \\ --libc-static-lib-dir [path] directory where libc crtbegin.o resides
521 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
522 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
523 \\ --library [lib] link against lib
524 \\ --library-path [dir] add a directory to the library search path
525 \\ --linker-script [path] use a custom linker script
526 \\ --object [obj] add object file to build
527 \\ -L[dir] alias for --library-path
528 \\ -rdynamic add all symbols to the dynamic symbol table
529 \\ -rpath [path] add directory to the runtime library search path
530 \\ -mconsole (windows) --subsystem console to the linker
531 \\ -mwindows (windows) --subsystem windows to the linker
532 \\ -framework [name] (darwin) link against framework
533 \\ -mios-version-min [ver] (darwin) set iOS deployment target
534 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
535 \\ --ver-major [ver] dynamic library semver major version
536 \\ --ver-minor [ver] dynamic library semver minor version
537 \\ --ver-patch [ver] dynamic library semver patch version
538 \\Test Options:
539 \\ --test-filter [text] skip tests that do not match filter
540 \\ --test-name-prefix [text] add prefix to all tests
541 \\ --test-cmd [arg] specify test execution command one arg at a time
542 \\ --test-cmd-bin appends test binary path to test cmd args
543 \\
544 );
545}
546
547fn printZen() !void {
548 var stdout_file = try io.getStdErr();
549 try stdout_file.write(
550 \\
551 \\ * Communicate intent precisely.
552 \\ * Edge cases matter.
553 \\ * Favor reading code over writing code.
554 \\ * Only one obvious way to do things.
555 \\ * Runtime crashes are better than bugs.
556 \\ * Compile errors are better than runtime crashes.
557 \\ * Incremental improvements.
558 \\ * Avoid local maximums.
559 \\ * Reduce the amount one must remember.
560 \\ * Minimize energy spent on coding style.
561 \\ * Together we serve end users.
562 \\
563 \\
564 );
565}93}
56694
567fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {95// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
568 var build_file: [] const u8 = "build.zig";96
569 var cache_dir: ?[] const u8 = null;97const usage_build =
570 var zig_install_prefix: ?[] const u8 = null;98 \\usage: zig build <options>
571 var asked_for_help = false;99 \\
572 var asked_for_init = false;100 \\General Options:
573101 \\ --help Print this help and exit
574 var args = ArrayList([] const u8).init(allocator);102 \\ --init Generate a build.zig template
575 defer args.deinit();103 \\ --build-file [file] Override path to build.zig
576104 \\ --cache-dir [path] Override path to cache directory
577 var zig_exe_path = try os.selfExePath(allocator);105 \\ --verbose Print commands before executing them
578 defer allocator.free(zig_exe_path);106 \\ --prefix [path] Override default install prefix
579107 \\
580 try args.append(""); // Placeholder for zig-cache/build108 \\Project-Specific Options:
581 try args.append(""); // Placeholder for zig_exe_path109 \\
582 try args.append(""); // Placeholder for build_file_dirname110 \\ Project-specific options become available when the build file is found.
583 try args.append(""); // Placeholder for full_cache_dir111 \\
112 \\Advanced Options:
113 \\ --build-file [file] Override path to build.zig
114 \\ --cache-dir [path] Override path to cache directory
115 \\ --verbose-tokenize Enable compiler debug output for tokenization
116 \\ --verbose-ast Enable compiler debug output for parsing into an AST
117 \\ --verbose-link Enable compiler debug output for linking
118 \\ --verbose-ir Enable compiler debug output for Zig IR
119 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
120 \\ --verbose-cimport Enable compiler debug output for C imports
121 \\
122 \\
123 ;
124
125const args_build_spec = []Flag {
126 Flag.Bool("--help"),
127 Flag.Bool("--init"),
128 Flag.Arg1("--build-file"),
129 Flag.Arg1("--cache-dir"),
130 Flag.Bool("--verbose"),
131 Flag.Arg1("--prefix"),
132
133 Flag.Arg1("--build-file"),
134 Flag.Arg1("--cache-dir"),
135 Flag.Bool("--verbose-tokenize"),
136 Flag.Bool("--verbose-ast"),
137 Flag.Bool("--verbose-link"),
138 Flag.Bool("--verbose-ir"),
139 Flag.Bool("--verbose-llvm-ir"),
140 Flag.Bool("--verbose-cimport"),
141};
584142
585 var i: usize = 0;143const missing_build_file =
586 while (i < argv.len) : (i += 1) {144 \\No 'build.zig' file found.
587 var arg = argv[i];145 \\
588 if (mem.eql(u8, arg, "--help")) {146 \\Initialize a 'build.zig' template file with `zig build --init`,
589 asked_for_help = true;147 \\or build an executable directly with `zig build-exe $FILENAME.zig`.
590 try args.append(argv[i]);148 \\
591 } else if (mem.eql(u8, arg, "--init")) {149 \\See: `zig build --help` or `zig help` for more options.
592 asked_for_init = true;150 \\
593 try args.append(argv[i]);151 ;
594 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) {152
595 build_file = argv[i + 1];153fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
596 i += 1;154 var flags = try Args.parse(allocator, args_build_spec, args);
597 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) {155 defer flags.deinit();
598 cache_dir = argv[i + 1];156
599 i += 1;157 if (flags.present("help")) {
600 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {158 try stderr.write(usage_build);
601 try args.append(arg);159 os.exit(0);
602 i += 1;
603 zig_install_prefix = argv[i];
604 try args.append(argv[i]);
605 } else {
606 try args.append(arg);
607 }
608 }160 }
609161
610 const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);162 const zig_lib_dir = try introspect.resolveZigLibDir(allocator);
611 defer allocator.free(zig_lib_dir);163 defer allocator.free(zig_lib_dir);
612164
613 const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");165 const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
...@@ -619,113 +171,502 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {...@@ -619,113 +171,502 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
619 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");171 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
620 defer allocator.free(build_runner_path);172 defer allocator.free(build_runner_path);
621173
622 // g = codegen_create(build_runner_path, ...)174 const build_file = flags.single("build-file") ?? "build.zig";
623 // codegen_set_out_name(g, "build")
624
625 const build_file_abs = try os.path.resolve(allocator, ".", build_file);175 const build_file_abs = try os.path.resolve(allocator, ".", build_file);
626 defer allocator.free(build_file_abs);176 defer allocator.free(build_file_abs);
627177
628 const build_file_basename = os.path.basename(build_file_abs);178 const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
629 const build_file_dirname = os.path.dirname(build_file_abs);
630179
631 var full_cache_dir: []u8 = undefined;180 if (flags.present("init")) {
632 if (cache_dir == null) {181 if (build_file_exists) {
633 full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");182 try stderr.print("build.zig already exists\n");
634 } else {183 os.exit(1);
635 full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir);184 }
636 }
637 defer allocator.free(full_cache_dir);
638185
639 const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");186 // need a new scope for proper defer scope finalization on exit
640 defer allocator.free(path_to_build_exe);187 {
641 // codegen_set_cache_dir(g, full_cache_dir)188 const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
189 defer allocator.free(build_template_path);
642190
643 args.items[0] = path_to_build_exe;191 try os.copyFile(allocator, build_template_path, build_file_abs);
644 args.items[1] = zig_exe_path;192 try stderr.print("wrote build.zig template\n");
645 args.items[2] = build_file_dirname;193 }
646 args.items[3] = full_cache_dir;
647194
648 var build_file_exists: bool = undefined;195 os.exit(0);
649 if (os.File.openRead(allocator, build_file_abs)) |*file| {
650 file.close();
651 build_file_exists = true;
652 } else |_| {
653 build_file_exists = false;
654 }196 }
655197
656 if (!build_file_exists and asked_for_help) {198 if (!build_file_exists) {
657 // TODO(bnoordhuis) Print help message from std/special/build_runner.zig199 try stderr.write(missing_build_file);
658 return;200 os.exit(1);
659 }201 }
660202
661 if (!build_file_exists and asked_for_init) {203 // TODO: Invoke build.zig entrypoint directly?
662 const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");204 var zig_exe_path = try os.selfExePath(allocator);
663 defer allocator.free(build_template_path);205 defer allocator.free(zig_exe_path);
664
665 var srcfile = try os.File.openRead(allocator, build_template_path);
666 defer srcfile.close();
667206
668 var dstfile = try os.File.openWrite(allocator, build_file_abs);207 var build_args = ArrayList([]const u8).init(allocator);
669 defer dstfile.close();208 defer build_args.deinit();
670209
671 while (true) {210 const build_file_basename = os.path.basename(build_file_abs);
672 var buffer: [4096]u8 = undefined;211 const build_file_dirname = os.path.dirname(build_file_abs);
673 const n = try srcfile.read(buffer[0..]);
674 if (n == 0) break;
675 try dstfile.write(buffer[0..n]);
676 }
677212
678 return;213 var full_cache_dir: []u8 = undefined;
214 if (flags.single("cache-dir")) |cache_dir| {
215 full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir);
216 } else {
217 full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
679 }218 }
219 defer allocator.free(full_cache_dir);
680220
681 if (!build_file_exists) {221 const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
682 warn(222 defer allocator.free(path_to_build_exe);
683 \\No 'build.zig' file found.
684 \\Initialize a 'build.zig' template file with `zig build --init`,
685 \\or build an executable directly with `zig build-exe $FILENAME.zig`.
686 \\See: `zig build --help` or `zig help` for more options.
687 \\
688 );
689 os.exit(1);
690 }
691223
692 // codegen_build(g)224 try build_args.append(path_to_build_exe);
693 // codegen_link(g, path_to_build_exe)225 try build_args.append(zig_exe_path);
694 // codegen_destroy(g)226 try build_args.append(build_file_dirname);
227 try build_args.append(full_cache_dir);
695228
696 var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);229 var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
697 defer proc.deinit();230 defer proc.deinit();
698231
699 var term = try proc.spawnAndWait();232 var term = try proc.spawnAndWait();
700 switch (term) {233 switch (term) {
701 os.ChildProcess.Term.Exited => |status| {234 os.ChildProcess.Term.Exited => |status| {
702 if (status != 0) {235 if (status != 0) {
703 warn("{} exited with status {}\n", args.at(0), status);236 try stderr.print("{} exited with status {}\n", build_args.at(0), status);
704 os.exit(1);237 os.exit(1);
705 }238 }
706 },239 },
707 os.ChildProcess.Term.Signal => |signal| {240 os.ChildProcess.Term.Signal => |signal| {
708 warn("{} killed by signal {}\n", args.at(0), signal);241 try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
709 os.exit(1);242 os.exit(1);
710 },243 },
711 os.ChildProcess.Term.Stopped => |signal| {244 os.ChildProcess.Term.Stopped => |signal| {
712 warn("{} stopped by signal {}\n", args.at(0), signal);245 try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
713 os.exit(1);246 os.exit(1);
714 },247 },
715 os.ChildProcess.Term.Unknown => |status| {248 os.ChildProcess.Term.Unknown => |status| {
716 warn("{} encountered unknown failure {}\n", args.at(0), status);249 try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
250 os.exit(1);
251 },
252 }
253}
254
255// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
256
257const usage_build_generic =
258 \\usage: zig build-exe <options> [file]
259 \\ zig build-lib <options> [file]
260 \\ zig build-obj <options> [file]
261 \\
262 \\General Options:
263 \\ --help Print this help and exit
264 \\ --color [auto|off|on] Enable or disable colored error messages
265 \\
266 \\Compile Options:
267 \\ --assembly [source] Add assembly file to build
268 \\ --cache-dir [path] Override the cache directory
269 \\ --emit [filetype] Emit a specific file format as compilation output
270 \\ --enable-timing-info Print timing diagnostics
271 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
272 \\ --name [name] Override output name
273 \\ --output [file] Override destination path
274 \\ --output-h [file] Override generated header file path
275 \\ --pkg-begin [name] [path] Make package available to import and push current pkg
276 \\ --pkg-end Pop current pkg
277 \\ --release-fast Build with optimizations on and safety off
278 \\ --release-safe Build with optimizations on and safety on
279 \\ --static Output will be statically linked
280 \\ --strip Exclude debug symbols
281 \\ --target-arch [name] Specify target architecture
282 \\ --target-environ [name] Specify target environment
283 \\ --target-os [name] Specify target operating system
284 \\ --verbose-tokenize Turn on compiler debug output for tokenization
285 \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
286 \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
287 \\ --verbose-link Turn on compiler debug output for linking
288 \\ --verbose-ir Turn on compiler debug output for Zig IR
289 \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
290 \\ --verbose-cimport Turn on compiler debug output for C imports
291 \\ -dirafter [dir] Same as -isystem but do it last
292 \\ -isystem [dir] Add additional search path for other .h files
293 \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
294 \\
295 \\Link Options:
296 \\ --ar-path [path] Set the path to ar
297 \\ --dynamic-linker [path] Set the path to ld.so
298 \\ --each-lib-rpath Add rpath for each used dynamic library
299 \\ --libc-lib-dir [path] Directory where libc crt1.o resides
300 \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
301 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
302 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
303 \\ --library [lib] Link against lib
304 \\ --forbid-library [lib] Make it an error to link against lib
305 \\ --library-path [dir] Add a directory to the library search path
306 \\ --linker-script [path] Use a custom linker script
307 \\ --object [obj] Add object file to build
308 \\ -rdynamic Add all symbols to the dynamic symbol table
309 \\ -rpath [path] Add directory to the runtime library search path
310 \\ -mconsole (windows) --subsystem console to the linker
311 \\ -mwindows (windows) --subsystem windows to the linker
312 \\ -framework [name] (darwin) link against framework
313 \\ -mios-version-min [ver] (darwin) set iOS deployment target
314 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
315 \\ --ver-major [ver] Dynamic library semver major version
316 \\ --ver-minor [ver] Dynamic library semver minor version
317 \\ --ver-patch [ver] Dynamic library semver patch version
318 \\
319 \\
320 ;
321
322const args_build_generic = []Flag {
323 Flag.Bool("--help"),
324 Flag.Option("--color", []const []const u8 { "auto", "off", "on" }),
325
326 Flag.ArgMergeN("--assembly", 1),
327 Flag.Arg1("--cache-dir"),
328 Flag.Option("--emit", []const []const u8 { "asm", "bin", "llvm-ir" }),
329 Flag.Bool("--enable-timing-info"),
330 Flag.Arg1("--libc-include-dir"),
331 Flag.Arg1("--name"),
332 Flag.Arg1("--output"),
333 Flag.Arg1("--output-h"),
334 // NOTE: Parsed manually after initial check
335 Flag.ArgN("--pkg-begin", 2),
336 Flag.Bool("--pkg-end"),
337 Flag.Bool("--release-fast"),
338 Flag.Bool("--release-safe"),
339 Flag.Bool("--static"),
340 Flag.Bool("--strip"),
341 Flag.Arg1("--target-arch"),
342 Flag.Arg1("--target-environ"),
343 Flag.Arg1("--target-os"),
344 Flag.Bool("--verbose-tokenize"),
345 Flag.Bool("--verbose-ast-tree"),
346 Flag.Bool("--verbose-ast-fmt"),
347 Flag.Bool("--verbose-link"),
348 Flag.Bool("--verbose-ir"),
349 Flag.Bool("--verbose-llvm-ir"),
350 Flag.Bool("--verbose-cimport"),
351 Flag.Arg1("-dirafter"),
352 Flag.ArgMergeN("-isystem", 1),
353 Flag.Arg1("-mllvm"),
354
355 Flag.Arg1("--ar-path"),
356 Flag.Arg1("--dynamic-linker"),
357 Flag.Bool("--each-lib-rpath"),
358 Flag.Arg1("--libc-lib-dir"),
359 Flag.Arg1("--libc-static-lib-dir"),
360 Flag.Arg1("--msvc-lib-dir"),
361 Flag.Arg1("--kernel32-lib-dir"),
362 Flag.ArgMergeN("--library", 1),
363 Flag.ArgMergeN("--forbid-library", 1),
364 Flag.ArgMergeN("--library-path", 1),
365 Flag.Arg1("--linker-script"),
366 Flag.ArgMergeN("--object", 1),
367 // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path
368 Flag.Bool("-rdynamic"),
369 Flag.Arg1("-rpath"),
370 Flag.Bool("-mconsole"),
371 Flag.Bool("-mwindows"),
372 Flag.ArgMergeN("-framework", 1),
373 Flag.Arg1("-mios-version-min"),
374 Flag.Arg1("-mmacosx-version-min"),
375 Flag.Arg1("--ver-major"),
376 Flag.Arg1("--ver-minor"),
377 Flag.Arg1("--ver-patch"),
378};
379
380fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void {
381 var flags = try Args.parse(allocator, args_build_generic, args);
382 defer flags.deinit();
383
384 if (flags.present("help")) {
385 try stderr.write(usage_build_generic);
386 os.exit(0);
387 }
388
389 var build_mode = builtin.Mode.Debug;
390 if (flags.present("release-fast")) {
391 build_mode = builtin.Mode.ReleaseFast;
392 } else if (flags.present("release-safe")) {
393 build_mode = builtin.Mode.ReleaseSafe;
394 }
395
396 var color = Module.ErrColor.Auto;
397 if (flags.single("color")) |color_flag| {
398 if (mem.eql(u8, color_flag, "auto")) {
399 color = Module.ErrColor.Auto;
400 } else if (mem.eql(u8, color_flag, "on")) {
401 color = Module.ErrColor.On;
402 } else if (mem.eql(u8, color_flag, "off")) {
403 color = Module.ErrColor.Off;
404 } else {
405 unreachable;
406 }
407 }
408
409 var emit_type = Module.Emit.Binary;
410 if (flags.single("emit")) |emit_flag| {
411 if (mem.eql(u8, emit_flag, "asm")) {
412 emit_type = Module.Emit.Assembly;
413 } else if (mem.eql(u8, emit_flag, "bin")) {
414 emit_type = Module.Emit.Binary;
415 } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
416 emit_type = Module.Emit.LlvmIr;
417 } else {
418 unreachable;
419 }
420 }
421
422 var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name?
423 defer cur_pkg.deinit();
424
425 var i: usize = 0;
426 while (i < args.len) : (i += 1) {
427 const arg_name = args[i];
428 if (mem.eql(u8, "--pkg-begin", arg_name)) {
429 // following two arguments guaranteed to exist due to arg parsing
430 i += 1;
431 const new_pkg_name = args[i];
432 i += 1;
433 const new_pkg_path = args[i];
434
435 var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
436 try cur_pkg.children.append(new_cur_pkg);
437 cur_pkg = new_cur_pkg;
438 } else if (mem.eql(u8, "--pkg-end", arg_name)) {
439 if (cur_pkg.parent == null) {
440 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
441 os.exit(1);
442 }
443 cur_pkg = ??cur_pkg.parent;
444 }
445 }
446
447 if (cur_pkg.parent != null) {
448 try stderr.print("unmatched --pkg-begin\n");
449 os.exit(1);
450 }
451
452 var in_file: ?[]const u8 = undefined;
453 switch (flags.positionals.len) {
454 0 => {
455 try stderr.write("--name [name] not provided and unable to infer\n");
717 os.exit(1);456 os.exit(1);
718 },457 },
458 1 => {
459 in_file = flags.positionals.at(0);
460 },
461 else => {
462 try stderr.write("only one zig input file is accepted during build\n");
463 os.exit(1);
464 },
465 }
466
467 const basename = os.path.basename(??in_file);
468 var it = mem.split(basename, ".");
469 const root_name = it.next() ?? {
470 try stderr.write("file name cannot be empty\n");
471 os.exit(1);
472 };
473
474 const asm_a= flags.many("assembly");
475 const obj_a = flags.many("object");
476 if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) {
477 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
478 os.exit(1);
479 }
480
481 if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) {
482 try stderr.write("When building an object file, --object arguments are invalid\n");
483 os.exit(1);
484 }
485
486 const zig_root_source_file = in_file;
487
488 const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
489 os.exit(1);
490 };
491 defer allocator.free(full_cache_dir);
492
493 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
494 defer allocator.free(zig_lib_dir);
495
496 var module =
497 try Module.create(
498 allocator,
499 root_name,
500 zig_root_source_file,
501 Target.Native,
502 out_type,
503 build_mode,
504 zig_lib_dir,
505 full_cache_dir
506 );
507 defer module.destroy();
508
509 module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
510 module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
511 module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
512
513 module.is_test = false;
514
515 if (flags.single("linker-script")) |linker_script| {
516 module.linker_script = linker_script;
517 }
518
519 module.each_lib_rpath = flags.present("each-lib-rpath");
520
521 var clang_argv_buf = ArrayList([]const u8).init(allocator);
522 defer clang_argv_buf.deinit();
523 if (flags.many("mllvm")) |mllvm_flags| {
524 for (mllvm_flags) |mllvm| {
525 try clang_argv_buf.append("-mllvm");
526 try clang_argv_buf.append(mllvm);
527 }
528
529 module.llvm_argv = mllvm_flags;
530 module.clang_argv = clang_argv_buf.toSliceConst();
531 }
532
533 module.strip = flags.present("strip");
534 module.is_static = flags.present("static");
535
536 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
537 module.libc_lib_dir = libc_lib_dir;
538 }
539 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
540 module.libc_static_lib_dir = libc_static_lib_dir;
541 }
542 if (flags.single("libc-include-dir")) |libc_include_dir| {
543 module.libc_include_dir = libc_include_dir;
544 }
545 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
546 module.msvc_lib_dir = msvc_lib_dir;
547 }
548 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
549 module.kernel32_lib_dir = kernel32_lib_dir;
550 }
551 if (flags.single("dynamic-linker")) |dynamic_linker| {
552 module.dynamic_linker = dynamic_linker;
553 }
554
555 module.verbose_tokenize = flags.present("verbose-tokenize");
556 module.verbose_ast_tree = flags.present("verbose-ast-tree");
557 module.verbose_ast_fmt = flags.present("verbose-ast-fmt");
558 module.verbose_link = flags.present("verbose-link");
559 module.verbose_ir = flags.present("verbose-ir");
560 module.verbose_llvm_ir = flags.present("verbose-llvm-ir");
561 module.verbose_cimport = flags.present("verbose-cimport");
562
563 module.err_color = color;
564
565 if (flags.many("library-path")) |lib_dirs| {
566 module.lib_dirs = lib_dirs;
567 }
568
569 if (flags.many("framework")) |frameworks| {
570 module.darwin_frameworks = frameworks;
571 }
572
573 if (flags.many("rpath")) |rpath_list| {
574 module.rpath_list = rpath_list;
719 }575 }
576
577 if (flags.single("output-h")) |output_h| {
578 module.out_h_path = output_h;
579 }
580
581 module.windows_subsystem_windows = flags.present("mwindows");
582 module.windows_subsystem_console = flags.present("mconsole");
583 module.linker_rdynamic = flags.present("rdynamic");
584
585 if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
586 try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
587 os.exit(1);
588 }
589
590 if (flags.single("mmacosx-version-min")) |ver| {
591 module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
592 }
593 if (flags.single("mios-version-min")) |ver| {
594 module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
595 }
596
597 module.emit_file_type = emit_type;
598 if (flags.many("object")) |objects| {
599 module.link_objects = objects;
600 }
601 if (flags.many("assembly")) |assembly_files| {
602 module.assembly_files = assembly_files;
603 }
604
605 try module.build();
606 try module.link(flags.single("out-file") ?? null);
607
608 if (flags.present("print-timing-info")) {
609 // codegen_print_timing_info(g, stderr);
610 }
611
612 try stderr.print("building {}: {}\n", @tagName(out_type), in_file);
613}
614
615fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void {
616 try buildOutputType(allocator, args, Module.Kind.Exe);
720}617}
721618
722fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {619// cmd:build-lib ///////////////////////////////////////////////////////////////////////////////////
723 for (file_paths) |file_path| {620
621fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void {
622 try buildOutputType(allocator, args, Module.Kind.Lib);
623}
624
625// cmd:build-obj ///////////////////////////////////////////////////////////////////////////////////
626
627fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void {
628 try buildOutputType(allocator, args, Module.Kind.Obj);
629}
630
631// cmd:fmt /////////////////////////////////////////////////////////////////////////////////////////
632
633const usage_fmt =
634 \\usage: zig fmt [file]...
635 \\
636 \\ Formats the input files and modifies them in-place.
637 \\
638 \\Options:
639 \\ --help Print this help and exit
640 \\ --keep-backups Retain backup entries for every file
641 \\
642 \\
643 ;
644
645const args_fmt_spec = []Flag {
646 Flag.Bool("--help"),
647 Flag.Bool("--keep-backups"),
648};
649
650fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
651 var flags = try Args.parse(allocator, args_fmt_spec, args);
652 defer flags.deinit();
653
654 if (flags.present("help")) {
655 try stderr.write(usage_fmt);
656 os.exit(0);
657 }
658
659 if (flags.positionals.len == 0) {
660 try stderr.write("expected at least one source file argument\n");
661 os.exit(1);
662 }
663
664 for (flags.positionals.toSliceConst()) |file_path| {
724 var file = try os.File.openRead(allocator, file_path);665 var file = try os.File.openRead(allocator, file_path);
725 defer file.close();666 defer file.close();
726667
727 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {668 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
728 warn("unable to open '{}': {}", file_path, err);669 try stderr.print("unable to open '{}': {}", file_path, err);
729 continue;670 continue;
730 };671 };
731 defer allocator.free(source_code);672 defer allocator.free(source_code);
...@@ -734,72 +675,312 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {...@@ -734,72 +675,312 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
734 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);675 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
735 defer parser.deinit();676 defer parser.deinit();
736677
737 var tree = try parser.parse();678 var tree = parser.parse() catch |err| {
679 try stderr.print("error parsing file '{}': {}\n", file_path, err);
680 continue;
681 };
738 defer tree.deinit();682 defer tree.deinit();
739683
740 const baf = try io.BufferedAtomicFile.create(allocator, file_path);684 var original_file_backup = try Buffer.init(allocator, file_path);
741 defer baf.destroy();685 defer original_file_backup.deinit();
686 try original_file_backup.append(".backup");
687
688 try os.rename(allocator, file_path, original_file_backup.toSliceConst());
742689
743 try parser.renderSource(baf.stream(), tree.root_node);690 try stderr.print("{}\n", file_path);
744 try baf.finish();691
692 // TODO: BufferedAtomicFile has some access problems.
693 var out_file = try os.File.openWrite(allocator, file_path);
694 defer out_file.close();
695
696 var out_file_stream = io.FileOutStream.init(&out_file);
697 try parser.renderSource(out_file_stream.stream, tree.root_node);
698
699 if (!flags.present("keep-backups")) {
700 try os.deleteFile(allocator, original_file_backup.toSliceConst());
701 }
745 }702 }
746}703}
747704
748/// Caller must free result705// cmd:targets /////////////////////////////////////////////////////////////////////////////////////
749fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {706
750 if (zig_install_prefix_arg) |zig_install_prefix| {707fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void {
751 return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {708 try stdout.write("Architectures:\n");
752 warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));709 {
753 return error.ZigInstallationNotFound;710 comptime var i: usize = 0;
754 };711 inline while (i < @memberCount(builtin.Arch)) : (i += 1) {
755 } else {712 comptime const arch_tag = @memberName(builtin.Arch, i);
756 return findZigLibDir(allocator) catch |err| {713 // NOTE: Cannot use empty string, see #918.
757 warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",714 comptime const native_str =
758 @errorName(err));715 if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n";
759 return error.ZigLibDirNotFound;716
760 };717 try stdout.print(" {}{}", arch_tag, native_str);
718 }
719 }
720 try stdout.write("\n");
721
722 try stdout.write("Operating Systems:\n");
723 {
724 comptime var i: usize = 0;
725 inline while (i < @memberCount(builtin.Os)) : (i += 1) {
726 comptime const os_tag = @memberName(builtin.Os, i);
727 // NOTE: Cannot use empty string, see #918.
728 comptime const native_str =
729 if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n";
730
731 try stdout.print(" {}{}", os_tag, native_str);
732 }
733 }
734 try stdout.write("\n");
735
736 try stdout.write("Environments:\n");
737 {
738 comptime var i: usize = 0;
739 inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
740 comptime const environ_tag = @memberName(builtin.Environ, i);
741 // NOTE: Cannot use empty string, see #918.
742 comptime const native_str =
743 if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
744
745 try stdout.print(" {}{}", environ_tag, native_str);
746 }
761 }747 }
762}748}
763749
764/// Caller must free result750// cmd:version /////////////////////////////////////////////////////////////////////////////////////
765fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {751
766 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");752fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void {
767 errdefer allocator.free(test_zig_dir);753 try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
754}
768755
769 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");756// cmd:test ////////////////////////////////////////////////////////////////////////////////////////
770 defer allocator.free(test_index_file);
771757
772 var file = try os.File.openRead(allocator, test_index_file);758const usage_test =
773 file.close();759 \\usage: zig test [file]...
760 \\
761 \\Options:
762 \\ --help Print this help and exit
763 \\
764 \\
765 ;
766
767const args_test_spec = []Flag {
768 Flag.Bool("--help"),
769};
770
771
772fn cmdTest(allocator: &Allocator, args: []const []const u8) !void {
773 var flags = try Args.parse(allocator, args_build_spec, args);
774 defer flags.deinit();
775
776 if (flags.present("help")) {
777 try stderr.write(usage_test);
778 os.exit(0);
779 }
774780
775 return test_zig_dir;781 if (flags.positionals.len != 1) {
782 try stderr.write("expected exactly one zig source file\n");
783 os.exit(1);
784 }
785
786 // compile the test program into the cache and run
787
788 // NOTE: May be overlap with buildOutput, take the shared part out.
789 try stderr.print("testing file {}\n", flags.positionals.at(0));
776}790}
777791
778/// Caller must free result792// cmd:run /////////////////////////////////////////////////////////////////////////////////////////
779fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {793
780 const self_exe_path = try os.selfExeDirPath(allocator);794// Run should be simple and not expose the full set of arguments provided by build-exe. If specific
781 defer allocator.free(self_exe_path);795// build requirements are need, the user should `build-exe` then `run` manually.
796const usage_run =
797 \\usage: zig run [file] -- <runtime args>
798 \\
799 \\Options:
800 \\ --help Print this help and exit
801 \\
802 \\
803 ;
804
805const args_run_spec = []Flag {
806 Flag.Bool("--help"),
807};
782808
783 var cur_path: []const u8 = self_exe_path;
784 while (true) {
785 const test_dir = os.path.dirname(cur_path);
786809
787 if (mem.eql(u8, test_dir, cur_path)) {810fn cmdRun(allocator: &Allocator, args: []const []const u8) !void {
811 var compile_args = args;
812 var runtime_args: []const []const u8 = []const []const u8 {};
813
814 for (args) |argv, i| {
815 if (mem.eql(u8, argv, "--")) {
816 compile_args = args[0..i];
817 runtime_args = args[i+1..];
788 break;818 break;
789 }819 }
820 }
821 var flags = try Args.parse(allocator, args_run_spec, compile_args);
822 defer flags.deinit();
790823
791 return testZigInstallPrefix(allocator, test_dir) catch |err| {824 if (flags.present("help")) {
792 cur_path = test_dir;825 try stderr.write(usage_run);
793 continue;826 os.exit(0);
794 };827 }
828
829 if (flags.positionals.len != 1) {
830 try stderr.write("expected exactly one zig source file\n");
831 os.exit(1);
832 }
833
834 try stderr.print("runtime args:\n");
835 for (runtime_args) |cargs| {
836 try stderr.print("{}\n", cargs);
837 }
838}
839
840// cmd:translate-c /////////////////////////////////////////////////////////////////////////////////
841
842const usage_translate_c =
843 \\usage: zig translate-c [file]
844 \\
845 \\Options:
846 \\ --help Print this help and exit
847 \\ --enable-timing-info Print timing diagnostics
848 \\ --output [path] Output file to write generated zig file (default: stdout)
849 \\
850 \\
851 ;
852
853const args_translate_c_spec = []Flag {
854 Flag.Bool("--help"),
855 Flag.Bool("--enable-timing-info"),
856 Flag.Arg1("--libc-include-dir"),
857 Flag.Arg1("--output"),
858};
859
860fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void {
861 var flags = try Args.parse(allocator, args_translate_c_spec, args);
862 defer flags.deinit();
863
864 if (flags.present("help")) {
865 try stderr.write(usage_translate_c);
866 os.exit(0);
867 }
868
869 if (flags.positionals.len != 1) {
870 try stderr.write("expected exactly one c source file\n");
871 os.exit(1);
872 }
873
874 // set up codegen
875
876 const zig_root_source_file = null;
877
878 // NOTE: translate-c shouldn't require setting up the full codegen instance as it does in
879 // the C++ compiler.
880
881 // codegen_create(g);
882 // codegen_set_out_name(g, null);
883 // codegen_translate_c(g, flags.positional.at(0))
884
885 var output_stream = stdout;
886 if (flags.single("output")) |output_file| {
887 var file = try os.File.openWrite(allocator, output_file);
888 defer file.close();
889
890 var file_stream = io.FileOutStream.init(&file);
891 // TODO: Not being set correctly, still stdout
892 output_stream = &file_stream.stream;
795 }893 }
796894
797 // TODO look in hard coded installation path from configuration895 // ast_render(g, output_stream, g->root_import->root, 4);
798 //if (ZIG_INSTALL_PREFIX != nullptr) {896 try output_stream.write("pub const example = 10;\n");
799 // if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {897
800 // return 0;898 if (flags.present("enable-timing-info")) {
801 // }899 // codegen_print_timing_info(g, stdout);
802 //}900 try stderr.write("printing timing info for translate-c\n");
901 }
902}
903
904// cmd:help ////////////////////////////////////////////////////////////////////////////////////////
803905
804 return error.FileNotFound;906fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void {
907 try stderr.write(usage);
908}
909
910// cmd:zen /////////////////////////////////////////////////////////////////////////////////////////
911
912const info_zen =
913 \\
914 \\ * Communicate intent precisely.
915 \\ * Edge cases matter.
916 \\ * Favor reading code over writing code.
917 \\ * Only one obvious way to do things.
918 \\ * Runtime crashes are better than bugs.
919 \\ * Compile errors are better than runtime crashes.
920 \\ * Incremental improvements.
921 \\ * Avoid local maximums.
922 \\ * Reduce the amount one must remember.
923 \\ * Minimize energy spent on coding style.
924 \\ * Together we serve end users.
925 \\
926 \\
927 ;
928
929fn cmdZen(allocator: &Allocator, args: []const []const u8) !void {
930 try stdout.write(info_zen);
931}
932
933// cmd:internal ////////////////////////////////////////////////////////////////////////////////////
934
935const usage_internal =
936 \\usage: zig internal [subcommand]
937 \\
938 \\Sub-Commands:
939 \\ build-info Print static compiler build-info
940 \\
941 \\
942 ;
943
944fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void {
945 if (args.len == 0) {
946 try stderr.write(usage_internal);
947 os.exit(1);
948 }
949
950 const sub_commands = []Command {
951 Command { .name = "build-info", .exec = cmdInternalBuildInfo },
952 };
953
954 for (sub_commands) |sub_command| {
955 if (mem.eql(u8, sub_command.name, args[0])) {
956 try sub_command.exec(allocator, args[1..]);
957 return;
958 }
959 }
960
961 try stderr.print("unknown sub command: {}\n\n", args[0]);
962 try stderr.write(usage_internal);
963}
964
965fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void {
966 try stdout.print(
967 \\ZIG_CMAKE_BINARY_DIR {}
968 \\ZIG_CXX_COMPILER {}
969 \\ZIG_LLVM_CONFIG_EXE {}
970 \\ZIG_LLD_INCLUDE_PATH {}
971 \\ZIG_LLD_LIBRARIES {}
972 \\ZIG_STD_FILES {}
973 \\ZIG_C_HEADER_FILES {}
974 \\ZIG_DIA_GUIDS_LIB {}
975 \\
976 ,
977 std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
978 std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
979 std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE),
980 std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH),
981 std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES),
982 std.cstr.toSliceConst(c.ZIG_STD_FILES),
983 std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES),
984 std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
985 );
805}986}
src-self-hosted/module.zig+23
...@@ -109,6 +109,29 @@ pub const Module = struct {...@@ -109,6 +109,29 @@ pub const Module = struct {
109 LlvmIr,109 LlvmIr,
110 };110 };
111111
112 pub const CliPkg = struct {
113 name: []const u8,
114 path: []const u8,
115 children: ArrayList(&CliPkg),
116 parent: ?&CliPkg,
117
118 pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg {
119 var pkg = try allocator.create(CliPkg);
120 pkg.name = name;
121 pkg.path = path;
122 pkg.children = ArrayList(&CliPkg).init(allocator);
123 pkg.parent = parent;
124 return pkg;
125 }
126
127 pub fn deinit(self: &CliPkg) void {
128 for (self.children.toSliceConst()) |child| {
129 child.deinit();
130 }
131 self.children.deinit();
132 }
133 };
134
112 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,135 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
113 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module136 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
114 {137 {
src/main.cpp+7-25
...@@ -54,7 +54,6 @@ static int usage(const char *arg0) {...@@ -54,7 +54,6 @@ static int usage(const char *arg0) {
54 " --verbose-ir turn on compiler debug output for Zig IR\n"54 " --verbose-ir turn on compiler debug output for Zig IR\n"
55 " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"55 " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"
56 " --verbose-cimport turn on compiler debug output for C imports\n"56 " --verbose-cimport turn on compiler debug output for C imports\n"
57 " --zig-install-prefix [path] override directory where zig thinks it is installed\n"
58 " -dirafter [dir] same as -isystem but do it last\n"57 " -dirafter [dir] same as -isystem but do it last\n"
59 " -isystem [dir] add additional search path for other .h files\n"58 " -isystem [dir] add additional search path for other .h files\n"
60 " -mllvm [arg] additional arguments to forward to LLVM's option processing\n"59 " -mllvm [arg] additional arguments to forward to LLVM's option processing\n"
...@@ -200,23 +199,14 @@ static int find_zig_lib_dir(Buf *out_path) {...@@ -200,23 +199,14 @@ static int find_zig_lib_dir(Buf *out_path) {
200 return ErrorFileNotFound;199 return ErrorFileNotFound;
201}200}
202201
203static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {202static Buf *resolve_zig_lib_dir(void) {
204 int err;203 int err;
205 Buf *result = buf_alloc();204 Buf *result = buf_alloc();
206 if (zig_install_prefix_arg == nullptr) {205 if ((err = find_zig_lib_dir(result))) {
207 if ((err = find_zig_lib_dir(result))) {206 fprintf(stderr, "Unable to find zig lib directory\n");
208 fprintf(stderr, "Unable to find zig lib directory. Reinstall Zig or use --zig-install-prefix.\n");207 exit(EXIT_FAILURE);
209 exit(EXIT_FAILURE);
210 }
211 return result;
212 }
213 Buf *zig_lib_dir_buf = buf_create_from_str(zig_install_prefix_arg);
214 if (test_zig_install_prefix(zig_lib_dir_buf, result)) {
215 return result;
216 }208 }
217209 return result;
218 fprintf(stderr, "No Zig installation found at prefix: %s\n", zig_install_prefix_arg);
219 exit(EXIT_FAILURE);
220}210}
221211
222enum Cmd {212enum Cmd {
...@@ -300,7 +290,6 @@ int main(int argc, char **argv) {...@@ -300,7 +290,6 @@ int main(int argc, char **argv) {
300 const char *libc_include_dir = nullptr;290 const char *libc_include_dir = nullptr;
301 const char *msvc_lib_dir = nullptr;291 const char *msvc_lib_dir = nullptr;
302 const char *kernel32_lib_dir = nullptr;292 const char *kernel32_lib_dir = nullptr;
303 const char *zig_install_prefix = nullptr;
304 const char *dynamic_linker = nullptr;293 const char *dynamic_linker = nullptr;
305 ZigList<const char *> clang_argv = {0};294 ZigList<const char *> clang_argv = {0};
306 ZigList<const char *> llvm_argv = {0};295 ZigList<const char *> llvm_argv = {0};
...@@ -360,17 +349,12 @@ int main(int argc, char **argv) {...@@ -360,17 +349,12 @@ int main(int argc, char **argv) {
360 } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {349 } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
361 cache_dir = argv[i + 1];350 cache_dir = argv[i + 1];
362 i += 1;351 i += 1;
363 } else if (i + 1 < argc && strcmp(argv[i], "--zig-install-prefix") == 0) {
364 args.append(argv[i]);
365 i += 1;
366 zig_install_prefix = argv[i];
367 args.append(zig_install_prefix);
368 } else {352 } else {
369 args.append(argv[i]);353 args.append(argv[i]);
370 }354 }
371 }355 }
372356
373 Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);357 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
374358
375 Buf *zig_std_dir = buf_alloc();359 Buf *zig_std_dir = buf_alloc();
376 os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);360 os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);
...@@ -591,8 +575,6 @@ int main(int argc, char **argv) {...@@ -591,8 +575,6 @@ int main(int argc, char **argv) {
591 msvc_lib_dir = argv[i];575 msvc_lib_dir = argv[i];
592 } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {576 } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
593 kernel32_lib_dir = argv[i];577 kernel32_lib_dir = argv[i];
594 } else if (strcmp(arg, "--zig-install-prefix") == 0) {
595 zig_install_prefix = argv[i];
596 } else if (strcmp(arg, "--dynamic-linker") == 0) {578 } else if (strcmp(arg, "--dynamic-linker") == 0) {
597 dynamic_linker = argv[i];579 dynamic_linker = argv[i];
598 } else if (strcmp(arg, "-isystem") == 0) {580 } else if (strcmp(arg, "-isystem") == 0) {
...@@ -804,7 +786,7 @@ int main(int argc, char **argv) {...@@ -804,7 +786,7 @@ int main(int argc, char **argv) {
804 full_cache_dir);786 full_cache_dir);
805 }787 }
806788
807 Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);789 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
808790
809 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);791 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);
810 codegen_set_out_name(g, buf_out_name);792 codegen_set_out_name(g, buf_out_name);
std/c/index.zig+1
...@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;...@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;
28pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;28pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
29pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;29pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
30pub extern "c" fn fork() c_int;30pub extern "c" fn fork() c_int;
31pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
31pub extern "c" fn pipe(fds: &c_int) c_int;32pub extern "c" fn pipe(fds: &c_int) c_int;
32pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;33pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
33pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;34pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
std/os/darwin.zig+9
...@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64...@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64
41pub const O_LARGEFILE = 0x0000;41pub const O_LARGEFILE = 0x0000;
42pub const O_PATH = 0x0000;42pub const O_PATH = 0x0000;
4343
44pub const F_OK = 0;
45pub const X_OK = 1;
46pub const W_OK = 2;
47pub const R_OK = 4;
48
44pub const O_RDONLY = 0x0000; /// open for reading only49pub const O_RDONLY = 0x0000; /// open for reading only
45pub const O_WRONLY = 0x0001; /// open for writing only50pub const O_WRONLY = 0x0001; /// open for writing only
46pub const O_RDWR = 0x0002; /// open for reading and writing51pub const O_RDWR = 0x0002; /// open for reading and writing
...@@ -209,6 +214,10 @@ pub fn fork() usize {...@@ -209,6 +214,10 @@ pub fn fork() usize {
209 return errnoWrap(c.fork());214 return errnoWrap(c.fork());
210}215}
211216
217pub fn access(path: &const u8, mode: u32) usize {
218 return errnoWrap(c.access(path, mode));
219}
220
212pub fn pipe(fds: &[2]i32) usize {221pub fn pipe(fds: &[2]i32) usize {
213 comptime assert(i32.bit_count == c_int.bit_count);222 comptime assert(i32.bit_count == c_int.bit_count);
214 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));223 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
std/os/file.zig+44-1
...@@ -85,6 +85,47 @@ pub const File = struct {...@@ -85,6 +85,47 @@ pub const File = struct {
85 };85 };
86 }86 }
8787
88 pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
89 const path_with_null = try std.cstr.addNullByte(allocator, path);
90 defer allocator.free(path_with_null);
91
92 if (is_posix) {
93 // mode is ignored and is always F_OK for now
94 const result = posix.access(path_with_null.ptr, posix.F_OK);
95 const err = posix.getErrno(result);
96 if (err > 0) {
97 return switch (err) {
98 posix.EACCES => error.PermissionDenied,
99 posix.EROFS => error.PermissionDenied,
100 posix.ELOOP => error.PermissionDenied,
101 posix.ETXTBSY => error.PermissionDenied,
102 posix.ENOTDIR => error.NotFound,
103 posix.ENOENT => error.NotFound,
104
105 posix.ENAMETOOLONG => error.NameTooLong,
106 posix.EINVAL => error.BadMode,
107 posix.EFAULT => error.BadPathName,
108 posix.EIO => error.Io,
109 posix.ENOMEM => error.SystemResources,
110 else => os.unexpectedErrorPosix(err),
111 };
112 }
113 return true;
114 } else if (is_windows) {
115 if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
116 return true;
117 }
118
119 const err = windows.GetLastError();
120 return switch (err) {
121 windows.ERROR.FILE_NOT_FOUND => error.NotFound,
122 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
123 else => os.unexpectedErrorWindows(err),
124 };
125 } else {
126 @compileError("TODO implement access for this OS");
127 }
128 }
88129
89 /// Upon success, the stream is in an uninitialized state. To continue using it,130 /// Upon success, the stream is in an uninitialized state. To continue using it,
90 /// you must use the open() function.131 /// you must use the open() function.
...@@ -245,7 +286,9 @@ pub const File = struct {...@@ -245,7 +286,9 @@ pub const File = struct {
245 };286 };
246 }287 }
247288
248 return stat.mode;289 // TODO: we should be able to cast u16 to ModeError!u32, making this
290 // explicit cast not necessary
291 return os.FileMode(stat.mode);
249 } else if (is_windows) {292 } else if (is_windows) {
250 return {};293 return {};
251 } else {294 } else {
std/os/linux/index.zig+9
...@@ -38,6 +38,11 @@ pub const MAP_STACK = 0x20000;...@@ -38,6 +38,11 @@ pub const MAP_STACK = 0x20000;
38pub const MAP_HUGETLB = 0x40000;38pub const MAP_HUGETLB = 0x40000;
39pub const MAP_FILE = 0;39pub const MAP_FILE = 0;
4040
41pub const F_OK = 0;
42pub const X_OK = 1;
43pub const W_OK = 2;
44pub const R_OK = 4;
45
41pub const WNOHANG = 1;46pub const WNOHANG = 1;
42pub const WUNTRACED = 2;47pub const WUNTRACED = 2;
43pub const WSTOPPED = 2;48pub const WSTOPPED = 2;
...@@ -705,6 +710,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {...@@ -705,6 +710,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
705 return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);710 return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
706}711}
707712
713pub fn access(path: &const u8, mode: u32) usize {
714 return syscall2(SYS_access, @ptrToInt(path), mode);
715}
716
708pub fn pipe(fd: &[2]i32) usize {717pub fn pipe(fd: &[2]i32) usize {
709 return pipe2(fd, 0);718 return pipe2(fd, 0);
710}719}
std/os/test.zig+17
...@@ -23,3 +23,20 @@ test "makePath, put some files in it, deleteTree" {...@@ -23,3 +23,20 @@ test "makePath, put some files in it, deleteTree" {
23 assert(err == error.PathNotFound);23 assert(err == error.PathNotFound);
24 }24 }
25}25}
26
27test "access file" {
28 if (builtin.os == builtin.Os.windows) {
29 return;
30 }
31
32 try os.makePath(a, "os_test_tmp");
33 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
34 unreachable;
35 } else |err| {
36 assert(err == error.NotFound);
37 }
38
39 try io.writeFile(a, "os_test_tmp/file.txt", "");
40 assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
41 try os.deleteTree(a, "os_test_tmp");
42}
std/os/windows/index.zig+2
...@@ -78,6 +78,8 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem...@@ -78,6 +78,8 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
78pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,78pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
79 dwFlags: DWORD) BOOL;79 dwFlags: DWORD) BOOL;
8080
81pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
82
81pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,83pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
82 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,84 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
83 in_out_lpOverlapped: ?&OVERLAPPED) BOOL;85 in_out_lpOverlapped: ?&OVERLAPPED) BOOL;