authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-03 18:02:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-03 18:02:01-07:00
log11a06443659154ec53e4deb00956b2f025cd71ca
treefc02b195cf8850f0f1743027055461aca8b4e57f
parentafdb47c32d00db3777352fb745b277b5d0805c69

basic support for building a test target


9 files changed, 249 insertions(+), 58 deletions(-)

CMakeLists.txt+1
......@@ -122,6 +122,7 @@ set(C_HEADERS
122122set(ZIG_STD_SRC
123123 "${CMAKE_SOURCE_DIR}/std/bootstrap.zig"
124124 "${CMAKE_SOURCE_DIR}/std/builtin.zig"
125 "${CMAKE_SOURCE_DIR}/std/test_runner.zig"
125126 "${CMAKE_SOURCE_DIR}/std/std.zig"
126127 "${CMAKE_SOURCE_DIR}/std/syscall.zig"
127128 "${CMAKE_SOURCE_DIR}/std/errno.zig"
src/all_types.hpp+8-7
......@@ -36,11 +36,6 @@ enum OutType {
3636 OutTypeObj,
3737};
3838
39enum CodeGenBuildType {
40 CodeGenBuildTypeDebug,
41 CodeGenBuildTypeRelease,
42};
43
4439struct ConstEnumValue {
4540 uint64_t tag;
4641 ConstExprValue *payload;
......@@ -983,6 +978,7 @@ struct FnTableEntry {
983978 bool is_inline;
984979 bool internal_linkage;
985980 bool is_extern;
981 bool is_test;
986982 uint32_t ref_count; // if this is 0 we don't have to codegen it
987983
988984 // reminder: hash tables must be initialized before use
......@@ -1073,7 +1069,8 @@ struct CodeGen {
10731069 bool link_libc;
10741070 Buf *libc_lib_dir;
10751071 Buf *libc_include_dir;
1076 CodeGenBuildType build_type;
1072 bool is_release_build;
1073 bool is_test_build;
10771074 LLVMTargetMachineRef target_machine;
10781075 LLVMZigDIFile *dummy_di_file;
10791076 bool is_native_target;
......@@ -1087,10 +1084,11 @@ struct CodeGen {
10871084 // there will not be a corresponding fn_defs entry.
10881085 ZigList<FnTableEntry *> fn_protos;
10891086 ZigList<VariableTableEntry *> global_vars;
1090 ZigList<Expr *> global_const_list;
1087 ZigList<AstNode *> global_const_list;
10911088
10921089 OutType out_type;
10931090 FnTableEntry *cur_fn;
1091 FnTableEntry *main_fn;
10941092 LLVMValueRef cur_ret_ptr;
10951093 ZigList<LLVMBasicBlockRef> break_block_stack;
10961094 ZigList<LLVMBasicBlockRef> continue_block_stack;
......@@ -1103,6 +1101,7 @@ struct CodeGen {
11031101 ErrColor err_color;
11041102 ImportTableEntry *root_import;
11051103 ImportTableEntry *bootstrap_import;
1104 ImportTableEntry *test_runner_import;
11061105 LLVMValueRef memcpy_fn_val;
11071106 LLVMValueRef memset_fn_val;
11081107 LLVMValueRef trap_fn_val;
......@@ -1116,6 +1115,8 @@ struct CodeGen {
11161115 const char **clang_argv;
11171116 int clang_argv_len;
11181117 ZigList<const char *> lib_dirs;
1118
1119 uint32_t test_fn_count;
11191120};
11201121
11211122struct VariableTableEntry {
src/analyze.cpp+26-10
......@@ -781,6 +781,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
781781
782782 bool is_cold = false;
783783 bool is_naked = false;
784 bool is_test = false;
784785
785786 if (fn_proto->directives) {
786787 for (int i = 0; i < fn_proto->directives->length; i += 1) {
......@@ -794,6 +795,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
794795 is_naked = true;
795796 } else if (buf_eql_str(attr_name, "cold")) {
796797 is_cold = true;
798 } else if (buf_eql_str(attr_name, "test")) {
799 is_test = true;
800 g->test_fn_count += 1;
797801 } else {
798802 add_node_error(g, directive_node,
799803 buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));
......@@ -813,6 +817,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
813817 is_naked, is_cold);
814818
815819 fn_table_entry->type_entry = fn_type;
820 fn_table_entry->is_test = is_test;
816821
817822 if (fn_type->id == TypeTableEntryIdInvalid) {
818823 fn_proto->skip = true;
......@@ -846,7 +851,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
846851 unsigned scope_line = line_number;
847852 bool is_definition = fn_table_entry->fn_def_node != nullptr;
848853 unsigned flags = 0;
849 bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
854 bool is_optimized = g->is_release_build;
850855 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
851856 import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
852857 import->di_file, line_number,
......@@ -1247,10 +1252,18 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
12471252
12481253 fn_table->put(proto_name, fn_table_entry);
12491254
1250 if (!struct_type &&
1251 g->bootstrap_import &&
1252 import == g->root_import && buf_eql_str(proto_name, "main"))
1253 {
1255 bool is_main_fn = !struct_type && (import == g->root_import) && buf_eql_str(proto_name, "main");
1256 if (is_main_fn) {
1257 g->main_fn = fn_table_entry;
1258
1259 if (g->bootstrap_import && !g->is_test_build) {
1260 g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
1261 }
1262 }
1263 bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main");
1264 if (is_test_main_fn) {
1265 assert(g->bootstrap_import);
1266 assert(g->is_test_build);
12541267 g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
12551268 }
12561269
......@@ -1551,13 +1564,14 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
15511564 }
15521565}
15531566
1554static void add_global_const_expr(CodeGen *g, Expr *expr) {
1567static void add_global_const_expr(CodeGen *g, AstNode *expr_node) {
1568 Expr *expr = get_resolved_expr(expr_node);
15551569 if (expr->const_val.ok &&
15561570 type_has_codegen_value(expr->type_entry) &&
15571571 !expr->has_global_const &&
15581572 type_has_bits(expr->type_entry))
15591573 {
1560 g->global_const_list.append(expr);
1574 g->global_const_list.append(expr_node);
15611575 expr->has_global_const = true;
15621576 }
15631577}
......@@ -1924,7 +1938,7 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn
19241938 *child_node, expected_type, child_types[i]);
19251939 Expr *expr = get_resolved_expr(*child_node);
19261940 expr->type_entry = resolved_type;
1927 add_global_const_expr(g, expr);
1941 add_global_const_expr(g, *child_node);
19281942 }
19291943
19301944 return expected_type;
......@@ -4022,7 +4036,9 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
40224036 if (buf_eql_str(&var_name, "is_big_endian")) {
40234037 return resolve_expr_const_val_as_bool(g, node, g->is_big_endian);
40244038 } else if (buf_eql_str(&var_name, "is_release")) {
4025 return resolve_expr_const_val_as_bool(g, node, g->build_type == CodeGenBuildTypeRelease);
4039 return resolve_expr_const_val_as_bool(g, node, g->is_release_build);
4040 } else if (buf_eql_str(&var_name, "is_test")) {
4041 return resolve_expr_const_val_as_bool(g, node, g->is_test_build);
40264042 } else {
40274043 add_node_error(g, *str_node,
40284044 buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name)));
......@@ -4752,7 +4768,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
47524768 expr->type_entry = return_type;
47534769 node->block_context = context;
47544770
4755 add_global_const_expr(g, expr);
4771 add_global_const_expr(g, node);
47564772
47574773 return resolved_type;
47584774}
src/codegen.cpp+121-19
......@@ -28,7 +28,8 @@ CodeGen *codegen_create(Buf *root_source_dir) {
2828 g->primitive_type_table.init(32);
2929 g->unresolved_top_level_decls.init(32);
3030 g->fn_type_table.init(32);
31 g->build_type = CodeGenBuildTypeDebug;
31 g->is_release_build = false;
32 g->is_test_build = false;
3233 g->root_source_dir = root_source_dir;
3334 g->next_error_index = 1;
3435 g->error_value_count = 1;
......@@ -44,8 +45,12 @@ void codegen_set_clang_argv(CodeGen *g, const char **args, int len) {
4445 g->clang_argv_len = len;
4546}
4647
47void codegen_set_build_type(CodeGen *g, CodeGenBuildType build_type) {
48 g->build_type = build_type;
48void codegen_set_is_release(CodeGen *g, bool is_release_build) {
49 g->is_release_build = is_release_build;
50}
51
52void codegen_set_is_test(CodeGen *g, bool is_test_build) {
53 g->is_test_build = is_test_build;
4954}
5055
5156void codegen_set_is_static(CodeGen *g, bool is_static) {
......@@ -1003,7 +1008,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
10031008 assert(expr_type->id == TypeTableEntryIdErrorUnion);
10041009 TypeTableEntry *child_type = expr_type->data.error.child_type;
10051010
1006 if (g->build_type != CodeGenBuildTypeRelease) {
1011 if (!g->is_release_build) {
10071012 LLVMValueRef err_val;
10081013 if (type_has_bits(child_type)) {
10091014 add_debug_source_node(g, node);
......@@ -1044,7 +1049,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
10441049 assert(expr_type->id == TypeTableEntryIdMaybe);
10451050 TypeTableEntry *child_type = expr_type->data.maybe.child_type;
10461051
1047 if (g->build_type != CodeGenBuildTypeRelease) {
1052 if (!g->is_release_build) {
10481053 add_debug_source_node(g, node);
10491054 LLVMValueRef cond_val;
10501055 if (child_type->id == TypeTableEntryIdPointer ||
......@@ -1977,7 +1982,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
19771982 } else if (type_entry->id == TypeTableEntryIdUnreachable) {
19781983 assert(node->data.container_init_expr.entries.length == 0);
19791984 add_debug_source_node(g, node);
1980 if (g->build_type != CodeGenBuildTypeRelease) {
1985 if (!g->is_release_build) {
19811986 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
19821987 }
19831988 return LLVMBuildUnreachable(g->builder);
......@@ -2233,7 +2238,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
22332238 }
22342239 }
22352240 }
2236 if (!ignore_uninit && g->build_type != CodeGenBuildTypeRelease) {
2241 if (!ignore_uninit && !g->is_release_build) {
22372242 TypeTableEntry *isize = g->builtin_types.entry_isize;
22382243 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
22392244 uint64_t align_bytes = get_memcpy_align(g, variable->type);
......@@ -2645,7 +2650,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
26452650
26462651static void gen_const_globals(CodeGen *g) {
26472652 for (int i = 0; i < g->global_const_list.length; i += 1) {
2648 Expr *expr = g->global_const_list.at(i);
2653 AstNode *expr_node = g->global_const_list.at(i);
2654 Expr *expr = get_resolved_expr(expr_node);
26492655 ConstExprValue *const_val = &expr->const_val;
26502656 assert(const_val->ok);
26512657 TypeTableEntry *type_entry = expr->type_entry;
......@@ -2680,6 +2686,24 @@ static void delete_unused_builtin_fns(CodeGen *g) {
26802686 }
26812687}
26822688
2689static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) {
2690 if (g->is_test_build) {
2691 if (fn_entry->is_test) {
2692 return false;
2693 }
2694 if (fn_entry == g->main_fn) {
2695 return true;
2696 }
2697 return fn_entry->ref_count == 0;
2698 }
2699
2700 if (fn_entry->is_test) {
2701 return true;
2702 }
2703
2704 return fn_entry->ref_count == 0;
2705}
2706
26832707static void do_code_gen(CodeGen *g) {
26842708 assert(!g->errors.length);
26852709
......@@ -2731,12 +2755,19 @@ static void do_code_gen(CodeGen *g) {
27312755 var->value_ref = global_value;
27322756 }
27332757
2758 LLVMValueRef *test_fn_vals = nullptr;
2759 uint32_t next_test_index = 0;
2760 if (g->is_test_build) {
2761 test_fn_vals = allocate<LLVMValueRef>(g->test_fn_count);
2762 }
2763
27342764 // Generate function prototypes
27352765 for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
27362766 FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);
2737 if (fn_table_entry->ref_count == 0) {
2767 if (skip_fn_codegen(g, fn_table_entry)) {
27382768 // huge time saver
27392769 LLVMDeleteFunction(fn_table_entry->fn_value);
2770 fn_table_entry->fn_value = nullptr;
27402771 continue;
27412772 }
27422773
......@@ -2785,12 +2816,44 @@ static void do_code_gen(CodeGen *g) {
27852816 }
27862817 }
27872818
2819 if (fn_table_entry->is_test) {
2820 test_fn_vals[next_test_index] = fn_table_entry->fn_value;
2821 next_test_index += 1;
2822 }
2823 }
2824
2825 // Generate the list of test function pointers.
2826 if (g->is_test_build) {
2827 assert(g->test_fn_count > 0);
2828 assert(next_test_index == g->test_fn_count);
2829
2830 {
2831 LLVMValueRef test_fn_array_val = LLVMConstArray(LLVMTypeOf(test_fn_vals[0]),
2832 test_fn_vals, g->test_fn_count);
2833 LLVMValueRef global_value = LLVMAddGlobal(g->module,
2834 LLVMTypeOf(test_fn_array_val), "zig_test_fn_list");
2835 LLVMSetInitializer(global_value, test_fn_array_val);
2836 LLVMSetLinkage(global_value, LLVMExternalLinkage);
2837 LLVMSetGlobalConstant(global_value, true);
2838 LLVMSetUnnamedAddr(global_value, true);
2839 }
2840
2841 {
2842 LLVMValueRef test_fn_count_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
2843 g->test_fn_count, false);
2844 LLVMValueRef global_value = LLVMAddGlobal(g->module,
2845 LLVMTypeOf(test_fn_count_val), "zig_test_fn_count");
2846 LLVMSetInitializer(global_value, test_fn_count_val);
2847 LLVMSetLinkage(global_value, LLVMExternalLinkage);
2848 LLVMSetGlobalConstant(global_value, true);
2849 LLVMSetUnnamedAddr(global_value, true);
2850 }
27882851 }
27892852
27902853 // Generate function definitions.
27912854 for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
27922855 FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i);
2793 if (fn_table_entry->ref_count == 0) {
2856 if (skip_fn_codegen(g, fn_table_entry)) {
27942857 // huge time saver
27952858 continue;
27962859 }
......@@ -3297,8 +3360,7 @@ static void init(CodeGen *g, Buf *source_path) {
32973360 char *native_cpu = LLVMZigGetHostCPUName();
32983361 char *native_features = LLVMZigGetNativeFeatures();
32993362
3300 LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ?
3301 LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive;
3363 LLVMCodeGenOptLevel opt_level = g->is_release_build ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone;
33023364
33033365 LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC;
33043366
......@@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) {
33213383
33223384
33233385 Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);
3324 bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
3386 bool is_optimized = g->is_release_build;
33253387 const char *flags = "";
33263388 unsigned runtime_version = 0;
33273389 g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(),
......@@ -3611,14 +3673,10 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou
36113673 buf_sprintf("missing export declaration and export type not provided"));
36123674 }
36133675
3614 if (!g->link_libc) {
3676 if (!g->link_libc && !g->is_test_build) {
36153677 if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) {
36163678 g->bootstrap_import = add_special_code(g, "bootstrap.zig");
36173679 }
3618
3619 if (g->out_type == OutTypeExe) {
3620 add_special_code(g, "builtin.zig");
3621 }
36223680 }
36233681
36243682 if (g->verbose) {
......@@ -3677,6 +3735,8 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) {
36773735}
36783736
36793737static void generate_h_file(CodeGen *g) {
3738 assert(!g->is_test_build);
3739
36803740 Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name));
36813741 FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb");
36823742 if (!out_h)
......@@ -3768,8 +3828,38 @@ static const char *get_libc_file(CodeGen *g, const char *file) {
37683828 return buf_ptr(out_buf);
37693829}
37703830
3831static Buf *build_o(CodeGen *parent_gen, const char *oname) {
3832 Buf *source_basename = buf_sprintf("%s.zig", oname);
3833 Buf *std_dir_path = buf_create_from_str(ZIG_STD_DIR);
3834
3835 CodeGen *child_gen = codegen_create(std_dir_path);
3836 codegen_set_is_release(child_gen, parent_gen->is_release_build);
3837
3838 codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
3839 codegen_set_is_static(child_gen, parent_gen->is_static);
3840
3841 codegen_set_out_type(child_gen, OutTypeObj);
3842 codegen_set_out_name(child_gen, buf_create_from_str(oname));
3843
3844 codegen_set_verbose(child_gen, parent_gen->verbose);
3845 codegen_set_errmsg_color(child_gen, parent_gen->err_color);
3846
3847 Buf *full_path = buf_alloc();
3848 os_path_join(std_dir_path, source_basename, full_path);
3849 Buf source_code = BUF_INIT;
3850 if (os_fetch_file_path(full_path, &source_code)) {
3851 zig_panic("unable to fetch file: %s\n", buf_ptr(full_path));
3852 }
3853
3854 codegen_add_root_code(child_gen, std_dir_path, source_basename, &source_code);
3855 Buf *o_out = buf_sprintf("%s.o", oname);
3856 codegen_link(child_gen, buf_ptr(o_out));
3857
3858 return o_out;
3859}
3860
37713861void codegen_link(CodeGen *g, const char *out_file) {
3772 bool is_optimized = (g->build_type == CodeGenBuildTypeRelease);
3862 bool is_optimized = g->is_release_build;
37733863 if (is_optimized) {
37743864 if (g->verbose) {
37753865 fprintf(stderr, "\nOptimization:\n");
......@@ -3788,6 +3878,7 @@ void codegen_link(CodeGen *g, const char *out_file) {
37883878 }
37893879
37903880 if (!out_file) {
3881 assert(g->root_out_name);
37913882 out_file = buf_ptr(g->root_out_name);
37923883 }
37933884
......@@ -3821,6 +3912,7 @@ void codegen_link(CodeGen *g, const char *out_file) {
38213912 return;
38223913 }
38233914
3915
38243916 // invoke `ld`
38253917 ZigList<const char *> args = {0};
38263918 const char *crt1o;
......@@ -3871,6 +3963,16 @@ void codegen_link(CodeGen *g, const char *out_file) {
38713963 args.append(get_libc_file(g, "crtn.o"));
38723964 }
38733965
3966 if (g->is_test_build) {
3967 Buf *test_runner_o_path = build_o(g, "test_runner");
3968 args.append(buf_ptr(test_runner_o_path));
3969 }
3970
3971 if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
3972 Buf *builtin_o_path = build_o(g, "builtin");
3973 args.append(buf_ptr(builtin_o_path));
3974 }
3975
38743976 for (int i = 0; i < g->lib_dirs.length; i += 1) {
38753977 const char *lib_dir = g->lib_dirs.at(i);
38763978 args.append("-L");
src/codegen.hpp+3-1
......@@ -16,7 +16,9 @@
1616CodeGen *codegen_create(Buf *root_source_dir);
1717
1818void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len);
19void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type);
19void codegen_set_is_release(CodeGen *codegen, bool is_release);
20void codegen_set_is_test(CodeGen *codegen, bool is_test);
21
2022void codegen_set_is_static(CodeGen *codegen, bool is_static);
2123void codegen_set_strip(CodeGen *codegen, bool strip);
2224void codegen_set_verbose(CodeGen *codegen, bool verbose);
src/main.cpp+30-5
......@@ -17,6 +17,7 @@ static int usage(const char *arg0) {
1717 fprintf(stderr, "Usage: %s [command] [options]\n"
1818 "Commands:\n"
1919 " build create executable, object, or library from target\n"
20 " test create and run a test build\n"
2021 " version print version number and exit\n"
2122 " parseh convert a c header file to zig extern declarations\n"
2223 "Options:\n"
......@@ -40,6 +41,7 @@ static int usage(const char *arg0) {
4041enum Cmd {
4142 CmdInvalid,
4243 CmdBuild,
44 CmdTest,
4345 CmdVersion,
4446 CmdParseH,
4547};
......@@ -49,7 +51,7 @@ int main(int argc, char **argv) {
4951 Cmd cmd = CmdInvalid;
5052 const char *in_file = nullptr;
5153 const char *out_file = nullptr;
52 bool release = false;
54 bool is_release_build = false;
5355 bool strip = false;
5456 bool is_static = false;
5557 OutType out_type = OutTypeUnknown;
......@@ -67,7 +69,7 @@ int main(int argc, char **argv) {
6769
6870 if (arg[0] == '-') {
6971 if (strcmp(arg, "--release") == 0) {
70 release = true;
72 is_release_build = true;
7173 } else if (strcmp(arg, "--strip") == 0) {
7274 strip = true;
7375 } else if (strcmp(arg, "--static") == 0) {
......@@ -127,6 +129,8 @@ int main(int argc, char **argv) {
127129 cmd = CmdVersion;
128130 } else if (strcmp(arg, "parseh") == 0) {
129131 cmd = CmdParseH;
132 } else if (strcmp(arg, "test") == 0) {
133 cmd = CmdTest;
130134 } else {
131135 fprintf(stderr, "Unrecognized command: %s\n", arg);
132136 return usage(arg0);
......@@ -135,6 +139,7 @@ int main(int argc, char **argv) {
135139 switch (cmd) {
136140 case CmdBuild:
137141 case CmdParseH:
142 case CmdTest:
138143 if (!in_file) {
139144 in_file = arg;
140145 } else {
......@@ -152,6 +157,7 @@ int main(int argc, char **argv) {
152157 switch (cmd) {
153158 case CmdBuild:
154159 case CmdParseH:
160 case CmdTest:
155161 {
156162 if (!in_file)
157163 return usage(arg0);
......@@ -178,14 +184,22 @@ int main(int argc, char **argv) {
178184 }
179185
180186 CodeGen *g = codegen_create(&root_source_dir);
181 codegen_set_build_type(g, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
187 codegen_set_is_release(g, is_release_build);
188 codegen_set_is_test(g, cmd == CmdTest);
189
182190 codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
183191 codegen_set_strip(g, strip);
184192 codegen_set_is_static(g, is_static);
185 if (out_type != OutTypeUnknown)
193 if (out_type != OutTypeUnknown) {
186194 codegen_set_out_type(g, out_type);
187 if (out_name)
195 } else if (cmd == CmdTest) {
196 codegen_set_out_type(g, OutTypeExe);
197 }
198 if (out_name) {
188199 codegen_set_out_name(g, buf_create_from_str(out_name));
200 } else if (cmd == CmdTest) {
201 codegen_set_out_name(g, buf_create_from_str("test"));
202 }
189203 if (libc_lib_dir)
190204 codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir));
191205 if (libc_include_dir)
......@@ -205,6 +219,17 @@ int main(int argc, char **argv) {
205219 codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code);
206220 codegen_render_ast(g, stdout, 4);
207221 return EXIT_SUCCESS;
222 } else if (cmd == CmdTest) {
223 codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
224 codegen_link(g, "./test");
225 ZigList<const char *> args = {0};
226 int return_code;
227 os_spawn_process("./test", args, &return_code);
228 if (return_code != 0) {
229 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
230 fprintf(stderr, "./test\n");
231 }
232 return return_code;
208233 } else {
209234 zig_unreachable();
210235 }
src/os.cpp+14-15
......@@ -17,25 +17,24 @@
1717#include <fcntl.h>
1818#include <limits.h>
1919
20void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) {
20void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code) {
2121 pid_t pid = fork();
2222 if (pid == -1)
2323 zig_panic("fork failed");
24 if (pid != 0)
25 return;
26 if (detached) {
27 if (setsid() == -1)
28 zig_panic("process detach failed");
29 }
30
31 const char **argv = allocate<const char *>(args.length + 2);
32 argv[0] = exe;
33 argv[args.length + 1] = nullptr;
34 for (int i = 0; i < args.length; i += 1) {
35 argv[i + 1] = args.at(i);
24 if (pid == 0) {
25 // child
26 const char **argv = allocate<const char *>(args.length + 2);
27 argv[0] = exe;
28 argv[args.length + 1] = nullptr;
29 for (int i = 0; i < args.length; i += 1) {
30 argv[i + 1] = args.at(i);
31 }
32 execvp(exe, const_cast<char * const *>(argv));
33 zig_panic("execvp failed: %s", strerror(errno));
34 } else {
35 // parent
36 waitpid(pid, return_code, 0);
3637 }
37 execvp(exe, const_cast<char * const *>(argv));
38 zig_panic("execvp failed: %s", strerror(errno));
3938}
4039
4140static int read_all_fd_stream(int fd, Buf *out_buf) {
src/os.hpp+1-1
......@@ -13,7 +13,7 @@
1313
1414#include <stdio.h>
1515
16void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached);
16void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code);
1717void os_exec_process(const char *exe, ZigList<const char *> &args,
1818 int *return_code, Buf *out_stderr, Buf *out_stdout);
1919
std/test_runner.zig created+45
......@@ -0,0 +1,45 @@
1import "std.zig";
2
3/*
4struct TestFn {
5 name: []u8,
6 func: extern fn(),
7}
8
9extern var test_fn_list: []TestFn;
10*/
11
12extern var zig_test_fn_count: isize;
13
14// TODO make this a slice of structs
15extern var zig_test_fn_list: [99999999]extern fn();
16
17pub fn main(args: [][]u8) -> %void {
18 var i : isize = 0;
19 while (i < zig_test_fn_count) {
20 %%stderr.print_str("Test ");
21 // TODO get rid of the isize
22 %%stderr.print_i64(i + isize(1));
23 %%stderr.print_str("/");
24 %%stderr.print_i64(zig_test_fn_count);
25 %%stderr.print_str(" ");
26 /*
27 %%stderr.print_str(test_fn.name);
28 */
29 %%stderr.print_str("...");
30
31/*
32 // TODO support calling function pointers as fields directly
33 const fn_ptr = test_fn.func;
34 fn_ptr();
35 */
36
37 const test_fn = zig_test_fn_list[i];
38 test_fn();
39
40 %%stderr.print_str("OK\n");
41 %%stderr.flush();
42
43 i += 1;
44 }
45}