diff --git a/src/ir.cpp b/src/ir.cpp index 1fb839ea9d..a1ccce7f2c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -57,6 +57,7 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type); ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) { + assert(const_val->type->id == TypeTableEntryIdPointer); assert(const_val->special == ConstValSpecialStatic); switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -10350,10 +10351,21 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, if (type_is_invalid(target_value_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + if (target_value_ptr->value.type->id == TypeTableEntryIdMetaType) { + assert(instr_is_comptime(target_value_ptr)); + TypeTableEntry *ptr_type = target_value_ptr->value.data.x_type; + assert(ptr_type->id == TypeTableEntryIdPointer); + ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); + out_val->type = ira->codegen->builtin_types.entry_type; + out_val->data.x_type = ptr_type->data.pointer.child_type; + return out_val->type; + } + assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer); + TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; ConstExprValue *pointee_val = nullptr; - if (target_value_ptr->value.special != ConstValSpecialRuntime) { + if (instr_is_comptime(target_value_ptr)) { pointee_val = const_ptr_pointee(&target_value_ptr->value); if (pointee_val->special == ConstValSpecialRuntime) pointee_val = nullptr; diff --git a/src/main.cpp b/src/main.cpp index e9ca5feb64..48ddb5b95c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,9 +167,8 @@ int main(int argc, char **argv) { ZigList args = {0}; args.append(zig_exe_path); for (int i = 2; i < argc; i += 1) { - if (strcmp(argv[i], "--verbose") == 0) { + if (strcmp(argv[i], "--debug-build-verbose") == 0) { verbose = true; - args.append(argv[i]); } else { args.append(argv[i]); } diff --git a/std/buf_map.zig b/std/buf_map.zig index 61f6b3dd72..81834c6782 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -11,14 +11,13 @@ pub const BufMap = struct { pub fn init(allocator: &Allocator) -> BufMap { var self = BufMap { - .hash_map = undefined, + .hash_map = BufMapHashMap.init(allocator), }; - self.hash_map.init(allocator); return self; } pub fn deinit(self: &BufMap) { - var it = self.hash_map.entryIterator(); + var it = self.hash_map.iterator(); while (true) { const entry = it.next() ?? break; self.free(entry.key); @@ -54,7 +53,7 @@ pub const BufMap = struct { } pub fn iterator(self: &const BufMap) -> BufMapHashMap.Iterator { - return self.hash_map.entryIterator(); + return self.hash_map.iterator(); } fn free(self: &BufMap, value: []const u8) { diff --git a/std/buf_set.zig b/std/buf_set.zig index c923c0b99f..7c9f0f9058 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -9,14 +9,13 @@ pub const BufSet = struct { pub fn init(allocator: &Allocator) -> BufSet { var self = BufSet { - .hash_map = undefined, + .hash_map = BufSetHashMap.init(allocator), }; - self.hash_map.init(allocator); return self; } pub fn deinit(self: &BufSet) { - var it = self.hash_map.entryIterator(); + var it = self.hash_map.iterator(); while (true) { const entry = it.next() ?? break; self.free(entry.key); @@ -43,7 +42,7 @@ pub const BufSet = struct { } pub fn iterator(self: &const BufSet) -> BufSetHashMap.Iterator { - return self.hash_map.entryIterator(); + return self.hash_map.iterator(); } fn free(self: &BufSet, value: []const u8) { diff --git a/std/build.zig b/std/build.zig index 47b051d26c..a3c0286bed 100644 --- a/std/build.zig +++ b/std/build.zig @@ -2,6 +2,7 @@ const io = @import("io.zig"); const mem = @import("mem.zig"); const debug = @import("debug.zig"); const List = @import("list.zig").List; +const HashMap = @import("hash_map.zig").HashMap; const Allocator = @import("mem.zig").Allocator; const os = @import("os/index.zig"); const StdIo = os.ChildProcess.StdIo; @@ -12,21 +13,58 @@ error ExtraArg; error UncleanExit; pub const Builder = struct { - zig_exe: []const u8, allocator: &Allocator, exe_list: List(&Exe), lib_paths: List([]const u8), include_paths: List([]const u8), rpaths: List([]const u8), + user_input_options: UserInputOptionsMap, + available_options_map: AvailableOptionsMap, + available_options_list: List(AvailableOption), + verbose: bool, + invalid_user_input: bool, - pub fn init(zig_exe: []const u8, allocator: &Allocator) -> Builder { + const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8); + const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8); + + const AvailableOption = struct { + name: []const u8, + type_id: TypeId, + description: []const u8, + }; + + const UserInputOption = struct { + name: []const u8, + value: UserValue, + used: bool, + }; + + const UserValue = enum { + Flag, + Scalar: []const u8, + List: List([]const u8), + }; + + const TypeId = enum { + Bool, + Int, + Float, + String, + List, + }; + + pub fn init(allocator: &Allocator) -> Builder { var self = Builder { - .zig_exe = zig_exe, + .verbose = false, + .invalid_user_input = false, .allocator = allocator, .exe_list = List(&Exe).init(allocator), .lib_paths = List([]const u8).init(allocator), .include_paths = List([]const u8).init(allocator), .rpaths = List([]const u8).init(allocator), + .user_input_options = UserInputOptionsMap.init(allocator), + .available_options_map = AvailableOptionsMap.init(allocator), + .available_options_list = List(AvailableOption).init(allocator), }; self.processNixOSEnvVars(); return self; @@ -46,6 +84,8 @@ pub const Builder = struct { pub fn addExeErr(self: &Builder, root_src: []const u8, name: []const u8) -> %&Exe { const exe = %return self.allocator.create(Exe); *exe = Exe { + .verbose = false, + .release = false, .root_src = root_src, .name = name, .target = Target.Native, @@ -68,20 +108,13 @@ pub const Builder = struct { %%self.lib_paths.append(path); } - pub fn make(self: &Builder, leftover_arg_index: usize) -> %void { + pub fn make(self: &Builder, zig_exe: []const u8, targets: []const []const u8) -> %void { + if (targets.len != 0) { + debug.panic("TODO non default targets"); + } + var env_map = %return os.getEnvMap(self.allocator); - var verbose = false; - var arg_i: usize = leftover_arg_index; - while (arg_i < os.args.count(); arg_i += 1) { - const arg = os.args.at(arg_i); - if (mem.eql(u8, arg, "--verbose")) { - verbose = true; - } else { - %%io.stderr.printf("Unrecognized argument: '{}'\n", arg); - return error.ExtraArg; - } - } for (self.exe_list.toSlice()) |exe| { var zig_args = List([]const u8).init(self.allocator); defer zig_args.deinit(); @@ -89,10 +122,14 @@ pub const Builder = struct { %return zig_args.append("build_exe"[0...]); // TODO issue #296 %return zig_args.append(exe.root_src); - if (verbose) { + if (exe.verbose) { %return zig_args.append("--verbose"[0...]); // TODO issue #296 } + if (exe.release) { + %return zig_args.append("--release"[0...]); // TODO issue #296 + } + %return zig_args.append("--name"[0...]); // TODO issue #296 %return zig_args.append(exe.name); @@ -149,22 +186,21 @@ pub const Builder = struct { %return zig_args.append(lib_path); } + if (self.verbose) { + printInvocation(zig_exe, zig_args); + } // TODO issue #301 - var child = os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), &env_map, + var child = os.ChildProcess.spawn(zig_exe, zig_args.toSliceConst(), &env_map, StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator) %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err)); const term = %%child.wait(); - const exe_result = switch (term) { + switch (term) { Term.Clean => |code| { if (code != 0) { - %%io.stderr.printf("\nCompile failed with code {}. To reproduce:\n", code); - printInvocation(self.zig_exe, zig_args); return error.UncleanExit; } }, else => { - %%io.stderr.printf("\nCompile crashed. To reproduce:\n"); - printInvocation(self.zig_exe, zig_args); return error.UncleanExit; }, }; @@ -208,6 +244,144 @@ pub const Builder = struct { } } } + + pub fn option(self: &Builder, comptime T: type, name: []const u8, description: []const u8) -> ?T { + const type_id = typeToEnum(T); + const available_option = AvailableOption { + .name = name, + .type_id = type_id, + .description = description, + }; + if (const _ ?= %%self.available_options_map.put(name, available_option)) { + debug.panic("Option '{}' declared twice", name); + } + %%self.available_options_list.append(available_option); + + const entry = self.user_input_options.get(name) ?? return null; + entry.value.used = true; + switch (type_id) { + TypeId.Bool => switch (entry.value.value) { + UserValue.Flag => return true, + UserValue.Scalar => |s| { + if (mem.eql(u8, s, "true")) { + return true; + } else if (mem.eql(u8, s, "false")) { + return false; + } else { + %%io.stderr.printf("Expected -O{} to be a boolean, but received '{}'\n", name, s); + self.markInvalidUserInput(); + return null; + } + }, + UserValue.List => { + %%io.stderr.printf("Expected -O{} to be a boolean, but received a list.\n", name); + self.markInvalidUserInput(); + return null; + }, + }, + TypeId.Int => debug.panic("TODO integer options to build script"), + TypeId.Float => debug.panic("TODO float options to build script"), + TypeId.String => debug.panic("TODO string options to build script"), + TypeId.List => debug.panic("TODO list options to build script"), + } + } + + pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) -> bool { + if (var prev_value ?= %%self.user_input_options.put(name, UserInputOption { + .name = name, + .value = UserValue.Scalar { value }, + .used = false, + })) { + switch (prev_value.value) { + UserValue.Scalar => |s| { + var list = List([]const u8).init(self.allocator); + %%list.append(s); + %%list.append(value); + %%self.user_input_options.put(name, UserInputOption { + .name = name, + .value = UserValue.List { list }, + .used = false, + }); + }, + UserValue.List => |*list| { + %%list.append(value); + %%self.user_input_options.put(name, UserInputOption { + .name = name, + .value = UserValue.List { *list }, + .used = false, + }); + }, + UserValue.Flag => { + %%io.stderr.printf("Option '-O{}={}' conflicts with flag '-O{}'.\n", name, value, name); + return true; + }, + } + } + return false; + } + + pub fn addUserInputFlag(self: &Builder, name: []const u8) -> bool { + if (const prev_value ?= %%self.user_input_options.put(name, UserInputOption { + .name = name, + .value = UserValue.Flag, + .used = false, + })) { + switch (prev_value.value) { + UserValue.Scalar => |s| { + %%io.stderr.printf("Flag '-O{}' conflicts with option '-O{}={}'.\n", name, name, s); + return true; + }, + UserValue.List => { + %%io.stderr.printf("Flag '-O{}' conflicts with multiple options of the same name.\n", name); + return true; + }, + UserValue.Flag => {}, + } + } + return false; + } + + fn typeToEnum(comptime T: type) -> TypeId { + if (@isInteger(T)) { + TypeId.Int + } else if (@isFloat(T)) { + TypeId.Float + } else switch (T) { + bool => TypeId.Bool, + []const u8 => TypeId.String, + []const []const u8 => TypeId.List, + else => @compileError("Unsupported type: " ++ @typeName(T)), + } + } + + fn markInvalidUserInput(self: &Builder) { + self.invalid_user_input = true; + } + + pub fn typeIdName(id: TypeId) -> []const u8 { + return switch (id) { + TypeId.Bool => ([]const u8)("bool"), // TODO issue #125 + TypeId.Int => ([]const u8)("int"), // TODO issue #125 + TypeId.Float => ([]const u8)("float"), // TODO issue #125 + TypeId.String => ([]const u8)("string"), // TODO issue #125 + TypeId.List => ([]const u8)("list"), // TODO issue #125 + }; + } + + pub fn validateUserInputDidItFail(self: &Builder) -> bool { + // make sure all args are used + var it = self.user_input_options.iterator(); + while (true) { + const entry = it.next() ?? break; + if (!entry.value.used) { + %%io.stderr.printf("Invalid option: -O{}\n\n", entry.key); + self.markInvalidUserInput(); + } + } + + return self.invalid_user_input; + } + }; const CrossTarget = struct { @@ -233,12 +407,14 @@ const Exe = struct { target: Target, linker_script: LinkerScript, link_libs: BufSet, + verbose: bool, + release: bool, - fn deinit(self: &Exe) { + pub fn deinit(self: &Exe) { self.link_libs.deinit(); } - fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) { + pub fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) { self.target = Target.Cross { CrossTarget { .arch = target_arch, @@ -250,17 +426,25 @@ const Exe = struct { /// Exe keeps a reference to script for its lifetime or until this function /// is called again. - fn setLinkerScriptContents(self: &Exe, script: []const u8) { + pub fn setLinkerScriptContents(self: &Exe, script: []const u8) { self.linker_script = LinkerScript.Embed { script }; } - fn setLinkerScriptPath(self: &Exe, path: []const u8) { + pub fn setLinkerScriptPath(self: &Exe, path: []const u8) { self.linker_script = LinkerScript.Path { path }; } - fn linkLibrary(self: &Exe, name: []const u8) { + pub fn linkLibrary(self: &Exe, name: []const u8) { %%self.link_libs.put(name); } + + pub fn setVerbose(self: &Exe, value: bool) { + self.verbose = value; + } + + pub fn setRelease(self: &Exe, value: bool) { + self.release = value; + } }; fn handleErr(err: error) -> noreturn { @@ -401,3 +585,4 @@ fn targetEnvironName(target_environ: Environ) -> []const u8 { Environ.coreclr => ([]const u8)("coreclr"), }; } + diff --git a/std/fmt.zig b/std/fmt.zig index e990867679..19c6e16cf1 100644 --- a/std/fmt.zig +++ b/std/fmt.zig @@ -13,6 +13,8 @@ const State = enum { // TODO put inside format function and make sure the name a Integer, IntegerWidth, Character, + Buf, + BufWidth, }; /// Renders fmt string with args, calling output with slices of bytes. @@ -82,8 +84,21 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool, 'c' => { state = State.Character; }, + 's' => { + state = State.Buf; + }, else => @compileError("Unknown format character: " ++ []u8{c}), }, + State.Buf => switch (c) { + '}' => { + return output(context, args[next_arg]); + }, + '0' ... '9' => { + width_start = i; + state = State.BufWidth; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, State.CloseBrace => switch (c) { '}' => { state = State.Start; @@ -117,6 +132,18 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool, '0' ... '9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, + State.BufWidth => switch (c) { + '}' => { + width = comptime %%parseUnsigned(usize, fmt[width_start...i], 10); + if (!formatBuf(args[next_arg], width, context, output)) + return false; + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => {}, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, State.Character => switch (c) { '}' => { if (!formatAsciiChar(args[next_arg], context, output)) @@ -165,6 +192,23 @@ pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const return output(context, (&c)[0...1]); } +pub fn formatBuf(buf: []const u8, width: usize, + context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool +{ + if (!output(context, buf)) + return false; + + var leftover_padding = if (width > buf.len) (width - buf.len) else return true; + const pad_byte: u8 = ' '; + while (leftover_padding > 0; leftover_padding -= 1) { + if (!output(context, (&pad_byte)[0...1])) + return false; + } + + return true; +} + + pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool { @@ -291,6 +335,34 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 { }; } +const BufPrintContext = struct { + remaining: []u8, +}; + +fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> bool { + mem.copy(u8, context.remaining, bytes); + context.remaining = context.remaining[bytes.len...]; + return true; +} + +pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) { + var context = BufPrintContext { .remaining = buf, }; + _ = format(&context, bufPrintWrite, fmt, args); +} + +pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) -> %[]u8 { + var size: usize = 0; + _ = format(&size, countSize, fmt, args); + const buf = %return allocator.alloc(u8, size); + bufPrint(buf, fmt, args); + return buf; +} + +fn countSize(size: &usize, bytes: []const u8) -> bool { + *size += bytes.len; + return true; +} + test "testBufPrintInt" { var buffer: [max_int_digits]u8 = undefined; const buf = buffer[0...]; diff --git a/std/hash_map.zig b/std/hash_map.zig index 60f4062583..e20787bcd9 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -54,13 +54,15 @@ pub fn HashMap(comptime K: type, comptime V: type, } }; - pub fn init(hm: &Self, allocator: &Allocator) { - hm.entries = []Entry{}; - hm.allocator = allocator; - hm.size = 0; - hm.max_distance_from_start_index = 0; - // it doesn't actually matter what we set this to since we use wrapping integer arithmetic - hm.modification_count = undefined; + pub fn init(allocator: &Allocator) -> Self { + Self { + .entries = []Entry{}, + .allocator = allocator, + .size = 0, + .max_distance_from_start_index = 0, + // it doesn't actually matter what we set this to since we use wrapping integer arithmetic + .modification_count = undefined, + } } pub fn deinit(hm: &Self) { @@ -76,7 +78,8 @@ pub fn HashMap(comptime K: type, comptime V: type, hm.incrementModificationCount(); } - pub fn put(hm: &Self, key: K, value: V) -> %void { + /// Returns the value that was already there. + pub fn put(hm: &Self, key: K, value: &const V) -> %?V { if (hm.entries.len == 0) { %return hm.initCapacity(16); } @@ -89,13 +92,13 @@ pub fn HashMap(comptime K: type, comptime V: type, // dump all of the old elements into the new table for (old_entries) |*old_entry| { if (old_entry.used) { - hm.internalPut(old_entry.key, old_entry.value); + _ = hm.internalPut(old_entry.key, old_entry.value); } } hm.allocator.free(old_entries); } - hm.internalPut(key, value); + return hm.internalPut(key, value); } pub fn get(hm: &Self, key: K) -> ?&Entry { @@ -134,7 +137,7 @@ pub fn HashMap(comptime K: type, comptime V: type, return null; } - pub fn entryIterator(hm: &const Self) -> Iterator { + pub fn iterator(hm: &const Self) -> Iterator { return Iterator { .hm = hm, .count = 0, @@ -158,9 +161,10 @@ pub fn HashMap(comptime K: type, comptime V: type, } } - fn internalPut(hm: &Self, orig_key: K, orig_value: V) { + /// Returns the value that was already there. + fn internalPut(hm: &Self, orig_key: K, orig_value: &const V) -> ?V { var key = orig_key; - var value = orig_value; + var value = *orig_value; const start_index = hm.keyToIndex(key); var roll_over: usize = 0; var distance_from_start_index: usize = 0; @@ -187,7 +191,10 @@ pub fn HashMap(comptime K: type, comptime V: type, continue; } - if (!entry.used) { + var result: ?V = null; + if (entry.used) { + result = entry.value; + } else { // adding an entry. otherwise overwriting old value with // same key hm.size += 1; @@ -200,7 +207,7 @@ pub fn HashMap(comptime K: type, comptime V: type, .key = key, .value = value, }; - return; + return result; } unreachable // put into a full map } @@ -224,15 +231,18 @@ pub fn HashMap(comptime K: type, comptime V: type, } test "basicHashMapTest" { - var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined; - map.init(&debug.global_allocator); + var map = HashMap(i32, i32, hash_i32, eql_i32).init(&debug.global_allocator); defer map.deinit(); - %%map.put(1, 11); - %%map.put(2, 22); - %%map.put(3, 33); - %%map.put(4, 44); - %%map.put(5, 55); + // TODO issue #311 + assert(%%map.put(1, i32(11)) == null); + assert(%%map.put(2, i32(22)) == null); + assert(%%map.put(3, i32(33)) == null); + assert(%%map.put(4, i32(44)) == null); + assert(%%map.put(5, i32(55)) == null); + + assert(??%%map.put(5, i32(66)) == 55); + assert(??%%map.put(5, i32(55)) == 66); assert((??map.get(2)).value == 22); _ = map.remove(2); @@ -243,6 +253,7 @@ test "basicHashMapTest" { fn hash_i32(x: i32) -> u32 { *@ptrcast(&u32, &x) } + fn eql_i32(a: i32, b: i32) -> bool { a == b } diff --git a/std/mem.zig b/std/mem.zig index 16277386b9..fb05eb5fd3 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -208,6 +208,10 @@ pub fn split(s: []const u8, c: u8) -> SplitIterator { } } +pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) -> bool { + return if (needle.len > haystack.len) false else eql(T, haystack[0...needle.len], needle); +} + const SplitIterator = struct { s: []const u8, c: u8, diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 83e4aa7127..1e6628fa99 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -1,26 +1,106 @@ const root = @import("@build"); const std = @import("std"); const io = std.io; +const fmt = std.fmt; const os = std.os; const Builder = std.build.Builder; const mem = std.mem; +const List = std.list.List; error InvalidArgs; pub fn main() -> %void { - if (os.args.count() < 2) { - %%io.stderr.printf("Expected first argument to be path to zig compiler\n"); - return error.InvalidArgs; - } - const zig_exe = os.args.at(1); - const leftover_arg_index = 2; - // TODO use a more general purpose allocator here var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024); defer inc_allocator.deinit(); - var builder = Builder.init(zig_exe, &inc_allocator.allocator); + const allocator = &inc_allocator.allocator; + + var builder = Builder.init(allocator); defer builder.deinit(); + + var maybe_zig_exe: ?[]const u8 = null; + var targets = List([]const u8).init(allocator); + + var arg_i: usize = 1; + while (arg_i < os.args.count(); arg_i += 1) { + const arg = os.args.at(arg_i); + if (mem.startsWith(u8, arg, "-O")) { + const option_contents = arg[2...]; + if (option_contents.len == 0) { + %%io.stderr.printf("Expected option name after '-O'\n\n"); + return usage(&builder, maybe_zig_exe, false, &io.stderr); + } + if (const name_end ?= mem.indexOfScalar(u8, option_contents, '=')) { + const option_name = option_contents[0...name_end]; + const option_value = option_contents[name_end...]; + if (builder.addUserInputOption(option_name, option_value)) + return usage(&builder, maybe_zig_exe, false, &io.stderr); + } else { + if (builder.addUserInputFlag(option_contents)) + return usage(&builder, maybe_zig_exe, false, &io.stderr); + } + } else if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--verbose")) { + builder.verbose = true; + } else if (mem.eql(u8, arg, "--help")) { + return usage(&builder, maybe_zig_exe, false, &io.stdout); + } else { + %%io.stderr.printf("Unrecognized argument: {}\n\n", arg); + return usage(&builder, maybe_zig_exe, false, &io.stderr); + } + } else if (maybe_zig_exe == null) { + maybe_zig_exe = arg; + } else { + %%targets.append(arg); + } + } + + const zig_exe = maybe_zig_exe ?? return usage(&builder, null, false, &io.stderr); + root.build(&builder); - %return builder.make(leftover_arg_index); + + if (builder.validateUserInputDidItFail()) + return usage(&builder, maybe_zig_exe, true, &io.stderr); + + %return builder.make(zig_exe, targets.toSliceConst()); +} + +fn usage(builder: &Builder, maybe_zig_exe: ?[]const u8, already_ran_build: bool, out_stream: &io.OutStream) -> %void { + const zig_exe = maybe_zig_exe ?? { + %%out_stream.printf("Expected first argument to be path to zig compiler\n"); + return error.InvalidArgs; + }; + + // run the build script to collect the options + if (!already_ran_build) { + root.build(builder); + } + + %%out_stream.printf( + \\Usage: {} build [options] + \\ + \\General Options: + \\ --help Print this help and exit. + \\ --verbose Print commands before executing them. + \\ --debug-build-verbose Print verbose debugging information for the build system itself. + \\ + \\Project-Specific Options: + \\ + , zig_exe); + + if (builder.available_options_list.len == 0) { + %%out_stream.printf(" (none)\n"); + } else { + const allocator = builder.allocator; + for (builder.available_options_list.toSliceConst()) |option| { + const name = %%fmt.allocPrint(allocator, + " -O{}=({})", option.name, Builder.typeIdName(option.type_id)); + defer allocator.free(name); + %%out_stream.printf("{s24} {}\n", name, option.description); + } + } + + if (out_stream == &io.stderr) + return error.InvalidArgs; } diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 4767631ce5..d1acfe5dbe 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -138,3 +138,15 @@ fn returnsFalse() -> bool { test "switchOnConstEnumWithVar" { assert(!returnsFalse()); } + +test "switch on type" { + assert(trueIfBoolFalseOtherwise(bool)); + assert(!trueIfBoolFalseOtherwise(i32)); +} + +fn trueIfBoolFalseOtherwise(comptime T: type) -> bool { + switch (T) { + bool => true, + else => false, + } +}