Commit 7e11ef79d6

Andrew Kelley <superjoe30@gmail.com>
2017-04-27 22:15:41
zig test no longer requires a separate test_runner.o file
See #298
1 parent 7b0542d
doc/style.md
@@ -29,6 +29,9 @@ rules in written English are subject to naming conventions just like any other
 word. Even acronyms that are only 2 letters long are subject to these
 conventions.
 
+These are general rules of thumb; if it makes sense to do something different,
+do what makes sense.
+
 Examples:
 
 ```zig
src/all_types.hpp
@@ -1095,6 +1095,7 @@ struct ImportTableEntry {
     ScopeDecls *decls_scope;
     AstNode *c_import_node;
     bool any_imports_failed;
+    bool scanned;
 
     ZigList<AstNode *> use_decls;
 };
@@ -1398,6 +1399,7 @@ struct CodeGen {
     PackageTableEntry *root_package;
     PackageTableEntry *std_package;
     PackageTableEntry *zigrt_package;
+    PackageTableEntry *test_runner_package;
     Buf *root_out_name;
     bool windows_subsystem_windows;
     bool windows_subsystem_console;
@@ -1451,7 +1453,7 @@ struct CodeGen {
     size_t clang_argv_len;
     ZigList<const char *> lib_dirs;
 
-    uint32_t test_fn_count;
+    ZigList<FnTableEntry *> test_fns;
     TypeTableEntry *test_fn_type;
 
     bool each_lib_rpath;
src/analyze.cpp
@@ -1413,6 +1413,71 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
     zig_unreachable();
 }
 
+TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
+        TypeTableEntry *field_types[], size_t field_count)
+{
+    TypeTableEntry *struct_type = new_type_table_entry(TypeTableEntryIdStruct);
+
+    buf_init_from_str(&struct_type->name, type_name);
+
+    struct_type->data.structure.src_field_count = field_count;
+    struct_type->data.structure.gen_field_count = field_count;
+    struct_type->data.structure.zero_bits_known = true;
+    struct_type->data.structure.complete = true;
+    struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
+
+    ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(field_count);
+    LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count);
+    for (size_t i = 0; i < field_count; i += 1) {
+        element_types[i] = field_types[i]->type_ref;
+
+        TypeStructField *field = &struct_type->data.structure.fields[i];
+        field->name = buf_create_from_str(field_names[i]);
+        field->type_entry = field_types[i];
+        field->src_index = i;
+        field->gen_index = i;
+    }
+
+    struct_type->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), type_name);
+    LLVMStructSetBody(struct_type->type_ref, element_types, field_count, false);
+
+    struct_type->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
+        ZigLLVMTag_DW_structure_type(), type_name,
+        ZigLLVMCompileUnitToScope(g->compile_unit), nullptr, 0);
+
+    for (size_t i = 0; i < field_count; i += 1) {
+        TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
+        TypeTableEntry *field_type = type_struct_field->type_entry;
+        uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref);
+        uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref);
+        uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, i);
+        di_element_types[i] = ZigLLVMCreateDebugMemberType(g->dbuilder,
+                ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name),
+                nullptr, 0,
+                debug_size_in_bits,
+                debug_align_in_bits,
+                debug_offset_in_bits,
+                0, field_type->di_type);
+
+        assert(di_element_types[i]);
+    }
+
+    uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref);
+    uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref);
+    ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
+            ZigLLVMCompileUnitToScope(g->compile_unit),
+            type_name, nullptr, 0,
+            debug_size_in_bits,
+            debug_align_in_bits,
+            0,
+            nullptr, di_element_types, field_count, 0, nullptr, "");
+
+    ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
+    struct_type->di_type = replacement_di_type;
+
+    return struct_type;
+}
+
 static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
     // if you change the logic of this function likely you must make a similar change in
     // parseh.cpp
@@ -1823,7 +1888,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
     }
 }
 
-static TypeTableEntry *get_test_fn_type(CodeGen *g) {
+TypeTableEntry *get_test_fn_type(CodeGen *g) {
     if (g->test_fn_type)
         return g->test_fn_type;
 
@@ -1875,7 +1940,10 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
             if (fn_def_node)
                 g->fn_defs.append(fn_table_entry);
 
-            if (g->have_pub_main && import == g->root_import && scope_is_root_decls(tld_fn->base.parent_scope)) {
+            if (g->have_pub_main && scope_is_root_decls(tld_fn->base.parent_scope) &&
+                ((!g->is_test_build && import == g->root_import) ||
+                (g->is_test_build && import == g->test_runner_import)))
+            {
                 if (buf_eql_str(&fn_table_entry->symbol_name, "main")) {
                     g->main_fn = fn_table_entry;
 
@@ -1909,10 +1977,10 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
         fn_table_entry->type_entry = get_test_fn_type(g);
         fn_table_entry->body_node = source_node->data.test_decl.body;
         fn_table_entry->is_test = true;
-        g->test_fn_count += 1;
 
         g->fn_protos.append(fn_table_entry);
         g->fn_defs.append(fn_table_entry);
+        g->test_fns.append(fn_table_entry);
 
     } else {
         zig_unreachable();
@@ -1926,7 +1994,7 @@ static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
 }
 
 static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
-    if (tld->visib_mod == VisibModExport || (tld->id == TldIdVar && g->is_test_build)) {
+    if (tld->visib_mod == VisibModExport) {
         g->resolve_queue.append(tld);
     }
 
@@ -2932,11 +3000,17 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *a
     return import_entry;
 }
 
+void scan_import(CodeGen *g, ImportTableEntry *import) {
+    if (!import->scanned) {
+        import->scanned = true;
+        scan_decls(g, import->decls_scope, import->root);
+    }
+}
 
 void semantic_analyze(CodeGen *g) {
     for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) {
         ImportTableEntry *import = g->import_queue.at(g->import_queue_index);
-        scan_decls(g, import->decls_scope, import->root);
+        scan_import(g, import);
     }
 
     for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) {
src/analyze.hpp
@@ -33,6 +33,9 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
 TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type);
 TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry);
 TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name);
+TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
+        TypeTableEntry *field_types[], size_t field_count);
+TypeTableEntry *get_test_fn_type(CodeGen *g);
 bool handle_is_ptr(TypeTableEntry *type_entry);
 void find_libc_include_path(CodeGen *g);
 void find_libc_lib_path(CodeGen *g);
@@ -60,6 +63,7 @@ ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
 TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
 bool is_container_ref(TypeTableEntry *type_entry);
 void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
+void scan_import(CodeGen *g, ImportTableEntry *import);
 void preview_use_decl(CodeGen *g, AstNode *node);
 void resolve_use_decl(CodeGen *g, AstNode *node);
 FnTableEntry *scope_fn_entry(Scope *scope);
src/codegen.cpp
@@ -2906,7 +2906,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdDeclRef:
         case IrInstructionIdSwitchVar:
         case IrInstructionIdSetFnRefInline:
-        case IrInstructionIdOffsetOf:        
+        case IrInstructionIdOffsetOf:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -3087,7 +3087,7 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s
     return LLVMConstInBoundsGEP(base_ptr, indices, 2);
 }
 
-static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) { 
+static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
     switch (const_val->special) {
         case ConstValSpecialRuntime:
             zig_unreachable();
@@ -3501,50 +3501,6 @@ static void delete_unused_builtin_fns(CodeGen *g) {
     }
 }
 
-static bool should_skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) {
-    if (g->is_test_build) {
-        if (fn_entry->is_test) {
-            return false;
-        }
-        if (fn_entry == g->main_fn) {
-            return true;
-        }
-        return false;
-    }
-
-    if (fn_entry->is_test) {
-        return true;
-    }
-
-    return false;
-}
-
-static LLVMValueRef gen_test_fn_val(CodeGen *g, FnTableEntry *fn_entry) {
-    // Must match TestFn struct from test_runner.zig
-    Buf *fn_name = &fn_entry->symbol_name;
-    LLVMValueRef str_init = LLVMConstString(buf_ptr(fn_name), (unsigned)buf_len(fn_name), true);
-    LLVMValueRef str_global_val = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
-    LLVMSetInitializer(str_global_val, str_init);
-    LLVMSetLinkage(str_global_val, LLVMPrivateLinkage);
-    LLVMSetGlobalConstant(str_global_val, true);
-    LLVMSetUnnamedAddr(str_global_val, true);
-
-    LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(fn_name), false);
-
-    LLVMTypeRef ptr_type = LLVMPointerType(g->builtin_types.entry_u8->type_ref, 0);
-    LLVMValueRef name_fields[] = {
-        LLVMConstBitCast(str_global_val, ptr_type),
-        len_val,
-    };
-
-    LLVMValueRef name_val = LLVMConstStruct(name_fields, 2, false);
-    LLVMValueRef fields[] = {
-        name_val,
-        fn_llvm_value(g, fn_entry),
-    };
-    return LLVMConstStruct(fields, 2, false);
-}
-
 static void generate_error_name_table(CodeGen *g) {
     if (g->err_name_table != nullptr || !g->generate_error_name_table || g->error_decls.length == 1) {
         return;
@@ -3740,17 +3696,9 @@ static void do_code_gen(CodeGen *g) {
         var->value_ref = global_value;
     }
 
-    LLVMValueRef *test_fn_vals = nullptr;
-    uint32_t next_test_index = 0;
-    if (g->is_test_build) {
-        test_fn_vals = allocate<LLVMValueRef>(g->test_fn_count);
-    }
-
     // Generate function prototypes
     for (size_t fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
         FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);
-        if (should_skip_fn_codegen(g, fn_table_entry))
-            continue;
 
         TypeTableEntry *fn_type = fn_table_entry->type_entry;
         FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
@@ -3797,51 +3745,11 @@ static void do_code_gen(CodeGen *g) {
                 addLLVMArgAttr(fn_val, (unsigned)gen_index, "byval");
             }
         }
-
-        if (fn_table_entry->is_test) {
-            test_fn_vals[next_test_index] = gen_test_fn_val(g, fn_table_entry);
-            next_test_index += 1;
-        }
-    }
-
-    // Generate the list of test function pointers.
-    if (g->is_test_build) {
-        if (g->test_fn_count == 0) {
-            fprintf(stderr, "No tests to run.\n");
-            exit(0);
-        }
-        assert(g->test_fn_count > 0);
-        assert(next_test_index == g->test_fn_count);
-
-        LLVMValueRef test_fn_array_init = LLVMConstArray(LLVMTypeOf(test_fn_vals[0]),
-                test_fn_vals, g->test_fn_count);
-        LLVMValueRef test_fn_array_val = LLVMAddGlobal(g->module,
-                LLVMTypeOf(test_fn_array_init), "");
-        LLVMSetInitializer(test_fn_array_val, test_fn_array_init);
-        LLVMSetLinkage(test_fn_array_val, LLVMInternalLinkage);
-        LLVMSetGlobalConstant(test_fn_array_val, true);
-        LLVMSetUnnamedAddr(test_fn_array_val, true);
-
-        LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, g->test_fn_count, false);
-        LLVMTypeRef ptr_type = LLVMPointerType(LLVMTypeOf(test_fn_vals[0]), 0);
-        LLVMValueRef fields[] = {
-            LLVMConstBitCast(test_fn_array_val, ptr_type),
-            len_val,
-        };
-        LLVMValueRef test_fn_slice_init = LLVMConstStruct(fields, 2, false);
-        LLVMValueRef test_fn_slice_val = LLVMAddGlobal(g->module,
-                LLVMTypeOf(test_fn_slice_init), "zig_test_fn_list");
-        LLVMSetInitializer(test_fn_slice_val, test_fn_slice_init);
-        LLVMSetLinkage(test_fn_slice_val, LLVMExternalLinkage);
-        LLVMSetGlobalConstant(test_fn_slice_val, true);
-        LLVMSetUnnamedAddr(test_fn_slice_val, true);
     }
 
     // Generate function definitions.
     for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
         FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i);
-        if (should_skip_fn_codegen(g, fn_table_entry))
-            continue;
 
         LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
         g->cur_fn = fn_table_entry;
@@ -4737,10 +4645,16 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package
     return add_source_file(g, package, abs_full_path, import_code);
 }
 
-static PackageTableEntry *create_bootstrap_pkg(CodeGen *g) {
+static PackageTableEntry *create_bootstrap_pkg(CodeGen *g, PackageTableEntry *pkg_with_main) {
     PackageTableEntry *package = new_package(buf_ptr(g->zig_std_special_dir), "");
     package->package_table.put(buf_create_from_str("std"), g->std_package);
-    package->package_table.put(buf_create_from_str("@root"), g->root_package);
+    package->package_table.put(buf_create_from_str("@root"), pkg_with_main);
+    return package;
+}
+
+static PackageTableEntry *create_test_runner_pkg(CodeGen *g) {
+    PackageTableEntry *package = new_package(buf_ptr(g->zig_std_special_dir), "test_runner.zig");
+    package->package_table.put(buf_create_from_str("std"), g->std_package);
     return package;
 }
 
@@ -4751,6 +4665,54 @@ static PackageTableEntry *create_zigrt_pkg(CodeGen *g) {
     return package;
 }
 
+static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
+    assert(g->is_test_build);
+
+    if (g->test_fns.length == 0) {
+        fprintf(stderr, "No tests to run.\n");
+        exit(0);
+    }
+
+    TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
+    TypeTableEntry *fn_type = get_test_fn_type(g);
+
+    const char *field_names[] = { "name", "func", };
+    TypeTableEntry *field_types[] = { str_type, fn_type, };
+    TypeTableEntry *struct_type = get_struct_type(g, "ZigTestFn", field_names, field_types, 2);
+
+    ConstExprValue *test_fn_array = allocate<ConstExprValue>(1);
+    test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length);
+    test_fn_array->special = ConstValSpecialStatic;
+    test_fn_array->data.x_array.s_none.elements = allocate<ConstExprValue>(g->test_fns.length);
+
+    for (size_t i = 0; i < g->test_fns.length; i += 1) {
+        FnTableEntry *test_fn_entry = g->test_fns.at(i);
+
+        ConstExprValue *this_val = &test_fn_array->data.x_array.s_none.elements[i];
+        this_val->special = ConstValSpecialStatic;
+        this_val->type = struct_type;
+        this_val->data.x_struct.parent.id = ConstParentIdArray;
+        this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array;
+        this_val->data.x_struct.parent.data.p_array.elem_index = i;
+        this_val->data.x_struct.fields = allocate<ConstExprValue>(2);
+
+        ConstExprValue *name_field = &this_val->data.x_struct.fields[0];
+        ConstExprValue *name_array_val = create_const_str_lit(g, &test_fn_entry->symbol_name);
+        init_const_slice(g, name_field, name_array_val, 0, buf_len(&test_fn_entry->symbol_name), true);
+
+        ConstExprValue *fn_field = &this_val->data.x_struct.fields[1];
+        fn_field->type = fn_type;
+        fn_field->special = ConstValSpecialStatic;
+        fn_field->data.x_fn.fn_entry = test_fn_entry;
+    }
+
+    ConstExprValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true);
+
+    g->compile_vars.put(buf_create_from_str("zig_test_fn_slice"), test_fn_slice);
+    g->test_runner_package = create_test_runner_pkg(g);
+    g->test_runner_import = add_special_code(g, g->test_runner_package, "test_runner.zig");
+}
+
 static void gen_root_source(CodeGen *g) {
     if (buf_len(&g->root_package->root_src_path) == 0)
         return;
@@ -4779,7 +4741,7 @@ static void gen_root_source(CodeGen *g) {
     if (!g->is_test_build && g->zig_target.os != ZigLLVM_UnknownOS && !g->have_c_main &&
         ((g->have_pub_main && g->out_type == OutTypeObj) || g->out_type == OutTypeExe))
     {
-        g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g), "bootstrap.zig");
+        g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap.zig");
     }
     if (!g->omit_zigrt) {
         g->zigrt_package = create_zigrt_pkg(g);
@@ -4793,6 +4755,14 @@ static void gen_root_source(CodeGen *g) {
     if (!g->error_during_imports) {
         semantic_analyze(g);
     }
+    if (g->is_test_build) {
+        create_test_compile_var_and_add_test_runner(g);
+        g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->test_runner_package), "bootstrap.zig");
+
+        if (!g->error_during_imports) {
+            semantic_analyze(g);
+        }
+    }
 
     if (g->errors.length == 0) {
         if (g->verbose) {
src/ir.cpp
@@ -10920,7 +10920,7 @@ static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructi
     }
     ImportTableEntry *target_import = add_source_file(ira->codegen, target_package, abs_full_path, import_code);
 
-    scan_decls(ira->codegen, target_import->decls_scope, target_import->root);
+    scan_import(ira->codegen, target_import);
 
     ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base);
     out_val->data.x_import = target_import;
src/link.cpp
@@ -263,11 +263,6 @@ static void construct_linker_job_elf(LinkJob *lj) {
         lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
     }
 
-    if (g->is_test_build) {
-        Buf *test_runner_o_path = build_o(g, "test_runner");
-        lj->args.append(buf_ptr(test_runner_o_path));
-    }
-
     if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
         Buf *builtin_o_path = build_o(g, "builtin");
         lj->args.append(buf_ptr(builtin_o_path));
@@ -408,11 +403,6 @@ static void construct_linker_job_coff(LinkJob *lj) {
         lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
     }
 
-    if (g->is_test_build) {
-        Buf *test_runner_o_path = build_o(g, "test_runner");
-        lj->args.append(buf_ptr(test_runner_o_path));
-    }
-
     if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
         Buf *builtin_o_path = build_o(g, "builtin");
         lj->args.append(buf_ptr(builtin_o_path));
@@ -674,11 +664,6 @@ static void construct_linker_job_macho(LinkJob *lj) {
         lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
     }
 
-    if (g->is_test_build) {
-        Buf *test_runner_o_path = build_o(g, "test_runner");
-        lj->args.append(buf_ptr(test_runner_o_path));
-    }
-
     for (size_t i = 0; i < g->link_libs.length; i += 1) {
         Buf *link_lib = g->link_libs.at(i);
         if (buf_eql_str(link_lib, "c")) {
src/main.cpp
@@ -558,13 +558,13 @@ int main(int argc, char **argv) {
                 codegen_build(g);
                 codegen_link(g, out_file);
                 if (timing_info)
-                    codegen_print_timing_report(g, stderr);
+                    codegen_print_timing_report(g, stdout);
                 return EXIT_SUCCESS;
             } else if (cmd == CmdParseH) {
                 codegen_parseh(g, in_file_buf);
                 ast_render_decls(g, stdout, 4, g->root_import);
                 if (timing_info)
-                    codegen_print_timing_report(g, stderr);
+                    codegen_print_timing_report(g, stdout);
                 return EXIT_SUCCESS;
             } else if (cmd == CmdTest) {
                 codegen_build(g);
@@ -576,7 +576,7 @@ int main(int argc, char **argv) {
                     fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
                     fprintf(stderr, "./test\n");
                 } else if (timing_info) {
-                    codegen_print_timing_report(g, stderr);
+                    codegen_print_timing_report(g, stdout);
                 }
                 return (term.how == TerminationIdClean) ? term.code : -1;
             } else {
std/special/test_runner.zig
@@ -1,17 +1,11 @@
 const io = @import("std").io;
-
-const TestFn = struct {
-    name: []u8,
-    func: extern fn(),
-};
-
-extern var zig_test_fn_list: []TestFn;
+const test_fn_list = @compileVar("zig_test_fn_slice");
 
 pub fn main() -> %void {
-    for (zig_test_fn_list) |testFn, i| {
-        %%io.stderr.printf("Test {}/{} {}...", i + 1, zig_test_fn_list.len, testFn.name);
+    for (test_fn_list) |test_fn, i| {
+        %%io.stderr.printf("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
 
-        testFn.func();
+        test_fn.func();
 
         %%io.stderr.printf("OK\n");
     }
std/index.zig
@@ -16,3 +16,24 @@ pub const os = @import("os/index.zig");
 pub const rand = @import("rand.zig");
 pub const sort = @import("sort.zig");
 pub const target = @import("target.zig");
+
+test "std" {
+    // run tests from these
+    _ = @import("base64.zig");
+    _ = @import("buffer.zig");
+    _ = @import("build.zig");
+    _ = @import("c/index.zig");
+    _ = @import("cstr.zig");
+    _ = @import("debug.zig");
+    _ = @import("fmt.zig");
+    _ = @import("hash_map.zig");
+    _ = @import("io.zig");
+    _ = @import("list.zig");
+    _ = @import("math.zig");
+    _ = @import("mem.zig");
+    _ = @import("net.zig");
+    _ = @import("os/index.zig");
+    _ = @import("rand.zig");
+    _ = @import("sort.zig");
+    _ = @import("target.zig");
+}