Update zig build system to support user defined options
* Fix assertion failure when switching on type. Closes #310 * Update zig build system to support user defined options. See #204 * fmt.format supports {sNNN} to set padding for a buffer arg. * add std.fmt.bufPrint and std.fmt.allocPrint * std.hash_map.HashMap.put returns the previous value * add std.mem.startsWithmaster
parent
d15bcdce69
commit
6fbe1632d0
14
src/ir.cpp
14
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;
|
||||
|
|
|
@ -167,9 +167,8 @@ int main(int argc, char **argv) {
|
|||
ZigList<const char *> 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]);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
239
std/build.zig
239
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"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
72
std/fmt.zig
72
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...];
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue