From fdea12b2d9deaea6a90c7b1451df425e84b98099 Mon Sep 17 00:00:00 2001 From: nebulaeonline Date: Sun, 23 Dec 2018 22:44:02 -0500 Subject: msvc subsystem option handling; added uefi os type --- src/main.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 078dfb25f9..469ec448e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -90,9 +90,7 @@ static int print_full_usage(const char *arg0) { " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" " --no-rosegment compromise security to workaround valgrind bug\n" - " -mconsole (windows) --subsystem console to the linker\n" - " -mwindows (windows) --subsystem windows to the linker\n" - " -framework [name] (darwin) link against framework\n" + " --msvc-subsystem [subsystem] (windows/uefi) /SUBSYSTEM: to the linker\n" " -framework [name] (darwin) link against framework\n" " -mios-version-min [ver] (darwin) set iOS deployment target\n" " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" " --ver-major [ver] dynamic library semver major version\n" @@ -395,6 +393,7 @@ int main(int argc, char **argv) { int runtime_args_start = -1; bool no_rosegment_workaround = false; bool system_linker_hack = false; + ZigLLVM_MSVCSubsystemType msvc_subsystem_type = ZigLLVM_MSVC_NONE; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -540,6 +539,32 @@ int main(int argc, char **argv) { verbose_llvm_ir = true; } else if (strcmp(arg, "--verbose-cimport") == 0) { verbose_cimport = true; + } else if (strcmp(arg, "--msvc-subsystem") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Expected 1 argument after --msvc-subsystem\n"); + return print_error_usage(arg0); + } + i += 1; + if (stricmp(argv[i], "CONSOLE") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_CONSOLE; + } else if (stricmp(argv[i], "WINDOWS") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_WINDOWS; + } else if (stricmp(argv[i], "POSIX") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_POSIX; + } else if (stricmp(argv[i], "NATIVE") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_NATIVE; + } else if (stricmp(argv[i], "EFI_APPLICATION") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_EFI_APPLICATION; + } else if (stricmp(argv[i], "EFI_BOOT_SERVICE_DRIVER") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_EFI_BOOT_SERVICE_DRIVER; + } else if (stricmp(argv[i], "EFI_ROM") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_EFI_ROM; + } else if (stricmp(argv[i], "EFI_RUNTIME_DRIVER") == 0) { + msvc_subsystem_type = ZigLLVM_MSVC_EFI_RUNTIME_DRIVER; + } else { + fprintf(stderr, "Unknown format %s for --msvc-subsystem argument\n", argv[i]); + return EXIT_FAILURE; + } } else if (strcmp(arg, "-mwindows") == 0) { mwindows = true; } else if (strcmp(arg, "-mconsole") == 0) { @@ -849,6 +874,8 @@ int main(int argc, char **argv) { buf_out_name = buf_create_from_str("run"); } CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir()); + g->msvc_subsystem = msvc_subsystem_type; + if (disable_pic) { if (out_type != OutTypeLib || !is_static) { fprintf(stderr, "--disable-pic only applies to static libraries"); @@ -909,7 +936,6 @@ int main(int argc, char **argv) { codegen_add_rpath(g, rpath_list.at(i)); } - codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_rdynamic(g, rdynamic); g->no_rosegment_workaround = no_rosegment_workaround; if (mmacosx_version_min && mios_version_min) { -- cgit v1.2.3 From 7dcee99510fe4d69faae67f3aa41cfb47a43045e Mon Sep 17 00:00:00 2001 From: nebulaeonline Date: Sun, 23 Dec 2018 23:59:59 -0500 Subject: fixed stricmp/strcasecmp between windows/posix --- src/main.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 469ec448e8..303e48d750 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,13 @@ #include +#ifdef __GNUC__ +#include +#define STRCASECMP strcasecmp +#else +#define STRCASECMP stricmp +#endif + static int print_error_usage(const char *arg0) { fprintf(stderr, "See `%s help` for detailed usage information\n", arg0); return EXIT_FAILURE; @@ -545,21 +552,21 @@ int main(int argc, char **argv) { return print_error_usage(arg0); } i += 1; - if (stricmp(argv[i], "CONSOLE") == 0) { + if (STRCASECMP(argv[i], "CONSOLE") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_CONSOLE; - } else if (stricmp(argv[i], "WINDOWS") == 0) { + } else if (STRCASECMP(argv[i], "WINDOWS") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_WINDOWS; - } else if (stricmp(argv[i], "POSIX") == 0) { + } else if (STRCASECMP(argv[i], "POSIX") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_POSIX; - } else if (stricmp(argv[i], "NATIVE") == 0) { + } else if (STRCASECMP(argv[i], "NATIVE") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_NATIVE; - } else if (stricmp(argv[i], "EFI_APPLICATION") == 0) { + } else if (STRCASECMP(argv[i], "EFI_APPLICATION") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_EFI_APPLICATION; - } else if (stricmp(argv[i], "EFI_BOOT_SERVICE_DRIVER") == 0) { + } else if (STRCASECMP(argv[i], "EFI_BOOT_SERVICE_DRIVER") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_EFI_BOOT_SERVICE_DRIVER; - } else if (stricmp(argv[i], "EFI_ROM") == 0) { + } else if (STRCASECMP(argv[i], "EFI_ROM") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_EFI_ROM; - } else if (stricmp(argv[i], "EFI_RUNTIME_DRIVER") == 0) { + } else if (STRCASECMP(argv[i], "EFI_RUNTIME_DRIVER") == 0) { msvc_subsystem_type = ZigLLVM_MSVC_EFI_RUNTIME_DRIVER; } else { fprintf(stderr, "Unknown format %s for --msvc-subsystem argument\n", argv[i]); -- cgit v1.2.3 From 28cd337d1ffc547f296f574b82547d6b15253167 Mon Sep 17 00:00:00 2001 From: nebulaeonline Date: Mon, 24 Dec 2018 07:49:15 -0500 Subject: fixed formatting in options display --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 303e48d750..faca9511dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,7 +97,8 @@ static int print_full_usage(const char *arg0) { " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" " --no-rosegment compromise security to workaround valgrind bug\n" - " --msvc-subsystem [subsystem] (windows/uefi) /SUBSYSTEM: to the linker\n" " -framework [name] (darwin) link against framework\n" + " --msvc-subsystem [subsystem] (windows/uefi) /SUBSYSTEM: to the linker\n" + " -framework [name] (darwin) link against framework\n" " -mios-version-min [ver] (darwin) set iOS deployment target\n" " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" " --ver-major [ver] dynamic library semver major version\n" -- cgit v1.2.3 From a918ce26b81b8e51c061d85631aa432a025c1ee2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Dec 2018 15:25:54 -0500 Subject: fixups --- CMakeLists.txt | 2 +- README.md | 40 +++---- src/all_types.hpp | 2 +- src/analyze.cpp | 8 +- src/codegen.cpp | 6 +- src/ir.cpp | 8 +- src/link.cpp | 310 ++++++++++++++++++++++++++------------------------ src/main.cpp | 78 ++++++------- src/target.cpp | 5 +- src/target.hpp | 13 ++- src/translate_c.cpp | 2 +- src/zig_llvm.cpp | 7 +- src/zig_llvm.h | 18 +-- std/os/index.zig | 5 +- std/os/uefi.zig | 2 + std/os/uefi/index.zig | 0 std/special/panic.zig | 6 +- 17 files changed, 253 insertions(+), 259 deletions(-) create mode 100644 std/os/uefi.zig delete mode 100644 std/os/uefi/index.zig (limited to 'src/main.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b31f96562..807077476a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,7 +590,7 @@ set(ZIG_STD_FILES "os/freebsd/x86_64.zig" "os/path.zig" "os/time.zig" - "os/uefi/index.zig" + "os/uefi.zig" "os/windows/advapi32.zig" "os/windows/error.zig" "os/windows/index.zig" diff --git a/README.md b/README.md index c1eec599e5..e9756b404d 100644 --- a/README.md +++ b/README.md @@ -87,26 +87,26 @@ clarity. #### Support Table -| | freestanding | linux | macosx | windows | freebsd | other | -|--------|--------------|--------|--------|---------|---------|--------| -|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 3 | -|i386 | Tier 2 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | -|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | -|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | -|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | -|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | +| | freestanding | linux | macosx | windows | freebsd | UEFI | other | +|--------|--------------|--------|--------|---------|---------|--------|--------| +|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 3 | +|i386 | Tier 2 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | +|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | +|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | +|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | +|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | ## Community diff --git a/src/all_types.hpp b/src/all_types.hpp index 26e9edbab4..2b55f8ee2e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1750,11 +1750,11 @@ struct CodeGen { BuildMode build_mode; OutType out_type; ZigTarget zig_target; + TargetSubsystem subsystem; bool is_static; bool strip_debug_symbols; bool is_test_build; bool is_native_target; - ZigLLVM_MSVCSubsystemType msvc_subsystem; bool linker_rdynamic; bool no_rosegment_workaround; bool each_lib_rpath; diff --git a/src/analyze.cpp b/src/analyze.cpp index 48d473fb37..04d957b626 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3203,22 +3203,20 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLi if (ccc) { if (buf_eql_str(symbol_name, "main") && g->libc_link_lib != nullptr) { g->have_c_main = true; - g->msvc_subsystem = ZigLLVM_MSVC_CONSOLE; + g->subsystem = TargetSubsystemConsole; } else if (buf_eql_str(symbol_name, "WinMain") && g->zig_target.os == OsWindows) { g->have_winmain = true; - g->msvc_subsystem = ZigLLVM_MSVC_WINDOWS; + g->subsystem = TargetSubsystemWindows; } else if (buf_eql_str(symbol_name, "WinMainCRTStartup") && g->zig_target.os == OsWindows) { g->have_winmain_crt_startup = true; - g->msvc_subsystem = ZigLLVM_MSVC_WINDOWS; } else if (buf_eql_str(symbol_name, "DllMainCRTStartup") && g->zig_target.os == OsWindows) { g->have_dllmain_crt_startup = true; - g->msvc_subsystem = ZigLLVM_MSVC_WINDOWS; } } @@ -4377,7 +4375,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *r if (is_pub && ok_cc) { if (buf_eql_str(proto_name, "main")) { g->have_pub_main = true; - g->msvc_subsystem = ZigLLVM_MSVC_CONSOLE; + g->subsystem = TargetSubsystemConsole; } else if (buf_eql_str(proto_name, "panic")) { g->have_pub_panic = true; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 30a4f8c2a4..bbf8ffa340 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7231,7 +7231,7 @@ static void init(CodeGen *g) { } if (g->is_test_build) { - g->msvc_subsystem = ZigLLVM_MSVC_CONSOLE; + g->subsystem = TargetSubsystemConsole; } assert(g->root_out_name); @@ -7513,7 +7513,7 @@ static void gen_root_source(CodeGen *g) { report_errors_and_maybe_exit(g); if (!g->is_test_build && g->zig_target.os != OsFreestanding && - g->zig_target.os != OsZen && g->zig_target.os != OsUefi && + g->zig_target.os != OsUefi && !g->have_c_main && !g->have_winmain && !g->have_winmain_crt_startup && ((g->have_pub_main && g->out_type == OutTypeObj) || g->out_type == OutTypeExe)) { @@ -8070,11 +8070,11 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->zig_target.os); cache_int(ch, g->zig_target.env_type); cache_int(ch, g->zig_target.oformat); + cache_int(ch, g->subsystem); cache_bool(ch, g->is_static); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); cache_bool(ch, g->is_native_target); - cache_int(ch, g->msvc_subsystem); cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->no_rosegment_workaround); cache_bool(ch, g->each_lib_rpath); diff --git a/src/ir.cpp b/src/ir.cpp index a1432c7b11..83960f2eee 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17872,13 +17872,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct if (type_is_invalid(cimport_result->value.type)) return ira->codegen->invalid_instruction; - if (ira->codegen->msvc_subsystem != ZigLLVM_MSVC_EFI_APPLICATION && - ira->codegen->msvc_subsystem != ZigLLVM_MSVC_EFI_BOOT_SERVICE_DRIVER && - ira->codegen->msvc_subsystem != ZigLLVM_MSVC_EFI_ROM && - ira->codegen->msvc_subsystem != ZigLLVM_MSVC_EFI_RUNTIME_DRIVER) { - - find_libc_include_path(ira->codegen); - } + find_libc_include_path(ira->codegen); ImportTableEntry *child_import = allocate(1); child_import->decls_scope = create_decls_scope(ira->codegen, node, nullptr, nullptr, child_import); diff --git a/src/link.cpp b/src/link.cpp index 52ca2f2cd9..593f7f309f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -444,97 +444,20 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si return ZigLLDLink(oformat, args, arg_count, link_diag_callback, diag); } -static void construct_linker_job_coff(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("/ERRORLIMIT:0"); - - if (g->libc_link_lib != nullptr) { - find_libc_lib_path(g); - } - - lj->args.append("-NOLOGO"); - - if (!g->strip_debug_symbols && g->zig_target.os != Os::OsUefi) { - lj->args.append("-DEBUG"); - } - - if (g->out_type == OutTypeExe) { - // TODO compile time stack upper bound detection - lj->args.append("/STACK:16777216"); - } - - coff_append_machine_arg(g, &lj->args); - - // The commented out stuff is from when we linked with MinGW - // Now that we're linking with LLD it remains to be determined - // how to handle --target-environ gnu - // These comments are a clue - - bool is_library = g->out_type == OutTypeLib; - //bool dll = g->out_type == OutTypeLib; - //bool shared = !g->is_static && dll; - //if (g->is_static) { - // lj->args.append("-Bstatic"); - //} else { - // if (dll) { - // lj->args.append("--dll"); - // } else if (shared) { - // lj->args.append("--shared"); - // } - // lj->args.append("-Bdynamic"); - // if (dll || shared) { - // lj->args.append("-e"); - // if (g->zig_target.arch.arch == ZigLLVM_x86) { - // lj->args.append("_DllMainCRTStartup@12"); - // } else { - // lj->args.append("DllMainCRTStartup"); - // } - // lj->args.append("--enable-auto-image-base"); - // } - //} - - - // These are n actual command lines from LINK.EXE UEFI builds (app & driver) to be used as guidance cleaning - // up a bit for building the COFF linker args: - // /OUT:"J:\coding\nebulae\k\x64\Release\k.efi" /LTCG:incremental /Driver /PDB:"J:\coding\nebulae\k\x64\Release\k.pdb" "UefiApplicationEntryPoint.lib" "UefiRuntimeLib.lib" "UefiHiiLib.lib" "UefiHiiServicesLib.lib" "UefiSortLib.lib" "UefiShellLib.lib" "GlueLib.lib" "BaseLib.lib" "BaseDebugPrintErrorLevelLib.lib" "BasePrintLib.lib" "UefiLib.lib" "UefiBootServicesTableLib.lib" "UefiRuntimeServicesTableLib.lib" "UefiDevicePathLibDevicePathProtocol.lib" "UefiDebugLibConOut.lib" "UefiMemoryLib.lib" "UefiMemoryAllocationLib.lib" "BaseSynchronizationLib.lib" "UefiFileHandleLib.lib" /IMPLIB:"J:\coding\nebulae\k\x64\Release\k.lib" /DEBUG:FASTLINK /BASE:"0" /MACHINE:X64 /ENTRY:"EfiMain" /OPT:REF /SAFESEH:NO /SUBSYSTEM:EFI_APPLICATION /MERGE:".rdata=.data" /NOLOGO /ALIGN:32 /NODEFAULTLIB /SECTION:".xdata,D" - // /OUT:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.efi" /LTCG:incremental /Driver /PDB:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.pdb" "UefiDriverEntryPoint.lib" "UefiHiiLib.lib" "UefiHiiServicesLib.lib" "UefiSortLib.lib" "UefiShellLib.lib" "GlueLib.lib" "BaseLib.lib" "BaseDebugPrintErrorLevelLib.lib" "BasePrintLib.lib" "UefiLib.lib" "UefiBootServicesTableLib.lib" "UefiRuntimeServicesTableLib.lib" "UefiDevicePathLibDevicePathProtocol.lib" "UefiDebugLibConOut.lib" "UefiMemoryLib.lib" "UefiMemoryAllocationLib.lib" "BaseSynchronizationLib.lib" "UefiFileHandleLib.lib" /IMPLIB:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.lib" /DEBUG:FASTLINK /BASE:"0" /MACHINE:X64 /ENTRY:"EfiMain" /OPT:REF /SAFESEH:NO /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /MERGE:".rdata=.data" /NOLOGO /ALIGN:32 /NODEFAULTLIB /SECTION:".xdata,D" - - // Sorry for the goto(s) :) - switch (g->msvc_subsystem) { - case ZigLLVM_MSVC_CONSOLE: - lj->args.append("/SUBSYSTEM:console"); - goto building_nt; - case ZigLLVM_MSVC_EFI_APPLICATION: - lj->args.append("/SUBSYSTEM:efi_application"); - goto building_uefi; - case ZigLLVM_MSVC_EFI_BOOT_SERVICE_DRIVER: - lj->args.append("/SUBSYSTEM:efi_boot_service_driver"); - goto building_uefi; - case ZigLLVM_MSVC_EFI_ROM: - lj->args.append("/SUBSYSTEM:efi_rom"); - goto building_uefi; - case ZigLLVM_MSVC_EFI_RUNTIME_DRIVER: - lj->args.append("/SUBSYSTEM:efi_runtime_driver"); - goto building_uefi; - case ZigLLVM_MSVC_NATIVE: - lj->args.append("/SUBSYSTEM:native"); - goto building_nt; - case ZigLLVM_MSVC_POSIX: - lj->args.append("/SUBSYSTEM:posix"); - goto building_nt; - case ZigLLVM_MSVC_WINDOWS: - lj->args.append("/SUBSYSTEM:windows"); - goto building_nt; - case ZigLLVM_MSVC_NONE: - goto continuing_build; - } +static void add_uefi_link_args(LinkJob *lj) { + lj->args.append("/BASE:0"); + lj->args.append("/ENTRY:EfiMain"); + lj->args.append("/OPT:REF"); + lj->args.append("/SAFESEH:NO"); + lj->args.append("/MERGE:.rdata=.data"); + lj->args.append("/ALIGN:32"); + lj->args.append("/NODEFAULTLIB"); + lj->args.append("/SECTION:.xdata,D"); +} -building_uefi: - lj->args.append("/BASE:\"0\" /ENTRY:\"EfiMain\" /OPT:REF /SAFESEH:NO /MERGE:\".rdata=.data\" /ALIGN:32 /NODEFAULTLIB /SECTION:\".xdata,D\""); - goto continuing_build; +static void add_nt_link_args(LinkJob *lj, bool is_library) { + CodeGen *g = lj->codegen; -building_nt: if (lj->link_in_crt) { const char *lib_str = g->is_static ? "lib" : ""; const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : ""; @@ -557,17 +480,6 @@ building_nt: //https://msdn.microsoft.com/en-us/library/bb531344.aspx lj->args.append("legacy_stdio_definitions.lib"); - //if (shared || dll) { - // lj->args.append(get_libc_file(g, "dllcrt2.o")); - //} else { - // if (g->windows_linker_unicode) { - // lj->args.append(get_libc_file(g, "crt2u.o")); - // } else { - // lj->args.append(get_libc_file(g, "crt2.o")); - // } - //} - //lj->args.append(get_libc_static_file(g, "crtbegin.o")); - // msvcrt depends on kernel32 lj->args.append("kernel32.lib"); } else { @@ -580,8 +492,156 @@ building_nt: } } } +} + +// These are n actual command lines from LINK.EXE UEFI builds (app & driver) to be used as guidance cleaning +// up a bit for building the COFF linker args: +// /OUT:"J:\coding\nebulae\k\x64\Release\k.efi" /LTCG:incremental /Driver /PDB:"J:\coding\nebulae\k\x64\Release\k.pdb" "UefiApplicationEntryPoint.lib" "UefiRuntimeLib.lib" "UefiHiiLib.lib" "UefiHiiServicesLib.lib" "UefiSortLib.lib" "UefiShellLib.lib" "GlueLib.lib" "BaseLib.lib" "BaseDebugPrintErrorLevelLib.lib" "BasePrintLib.lib" "UefiLib.lib" "UefiBootServicesTableLib.lib" "UefiRuntimeServicesTableLib.lib" "UefiDevicePathLibDevicePathProtocol.lib" "UefiDebugLibConOut.lib" "UefiMemoryLib.lib" "UefiMemoryAllocationLib.lib" "BaseSynchronizationLib.lib" "UefiFileHandleLib.lib" /IMPLIB:"J:\coding\nebulae\k\x64\Release\k.lib" /DEBUG:FASTLINK /BASE:"0" /MACHINE:X64 /ENTRY:"EfiMain" /OPT:REF /SAFESEH:NO /SUBSYSTEM:EFI_APPLICATION /MERGE:".rdata=.data" /NOLOGO /ALIGN:32 /NODEFAULTLIB /SECTION:".xdata,D" +// /OUT:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.efi" /LTCG:incremental /Driver /PDB:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.pdb" "UefiDriverEntryPoint.lib" "UefiHiiLib.lib" "UefiHiiServicesLib.lib" "UefiSortLib.lib" "UefiShellLib.lib" "GlueLib.lib" "BaseLib.lib" "BaseDebugPrintErrorLevelLib.lib" "BasePrintLib.lib" "UefiLib.lib" "UefiBootServicesTableLib.lib" "UefiRuntimeServicesTableLib.lib" "UefiDevicePathLibDevicePathProtocol.lib" "UefiDebugLibConOut.lib" "UefiMemoryLib.lib" "UefiMemoryAllocationLib.lib" "BaseSynchronizationLib.lib" "UefiFileHandleLib.lib" /IMPLIB:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.lib" /DEBUG:FASTLINK /BASE:"0" /MACHINE:X64 /ENTRY:"EfiMain" /OPT:REF /SAFESEH:NO /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /MERGE:".rdata=.data" /NOLOGO /ALIGN:32 /NODEFAULTLIB /SECTION:".xdata,D" +// This commented out stuff is from when we linked with MinGW +// Now that we're linking with LLD it remains to be determined +// how to handle --target-environ gnu +// These comments are a clue +//bool dll = g->out_type == OutTypeLib; +//bool shared = !g->is_static && dll; +//if (g->is_static) { +// lj->args.append("-Bstatic"); +//} else { +// if (dll) { +// lj->args.append("--dll"); +// } else if (shared) { +// lj->args.append("--shared"); +// } +// lj->args.append("-Bdynamic"); +// if (dll || shared) { +// lj->args.append("-e"); +// if (g->zig_target.arch.arch == ZigLLVM_x86) { +// lj->args.append("_DllMainCRTStartup@12"); +// } else { +// lj->args.append("DllMainCRTStartup"); +// } +// lj->args.append("--enable-auto-image-base"); +// } +//} +//if (shared || dll) { +// lj->args.append(get_libc_file(g, "dllcrt2.o")); +//} else { +// if (g->windows_linker_unicode) { +// lj->args.append(get_libc_file(g, "crt2u.o")); +// } else { +// lj->args.append(get_libc_file(g, "crt2.o")); +// } +//} +//lj->args.append(get_libc_static_file(g, "crtbegin.o")); +//if (g->libc_link_lib != nullptr) { +//if (g->is_static) { +// lj->args.append("--start-group"); +//} + +//lj->args.append("-lmingw32"); + +//lj->args.append("-lgcc"); +//bool is_android = (g->zig_target.env_type == ZigLLVM_Android); +//bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target); +//if (!g->is_static && !is_android) { +// if (!is_cyg_ming) { +// lj->args.append("--as-needed"); +// } +// //lj->args.append("-lgcc_s"); +// if (!is_cyg_ming) { +// lj->args.append("--no-as-needed"); +// } +//} +//if (g->is_static && !is_android) { +// //lj->args.append("-lgcc_eh"); +//} +//if (is_android && !g->is_static) { +// lj->args.append("-ldl"); +//} + +//lj->args.append("-lmoldname"); +//lj->args.append("-lmingwex"); +//lj->args.append("-lmsvcrt"); + + +//if (g->windows_subsystem_windows) { +// lj->args.append("-lgdi32"); +// lj->args.append("-lcomdlg32"); +//} +//lj->args.append("-ladvapi32"); +//lj->args.append("-lshell32"); +//lj->args.append("-luser32"); +//lj->args.append("-lkernel32"); + +//if (g->is_static) { +// lj->args.append("--end-group"); +//} + +//if (lj->link_in_crt) { +// lj->args.append(get_libc_static_file(g, "crtend.o")); +//} +//} + + +static void construct_linker_job_coff(LinkJob *lj) { + CodeGen *g = lj->codegen; + + lj->args.append("/ERRORLIMIT:0"); + + if (g->libc_link_lib != nullptr) { + find_libc_lib_path(g); + } + + lj->args.append("/NOLOGO"); -continuing_build: + if (!g->strip_debug_symbols) { + lj->args.append("/DEBUG"); + } + + if (g->out_type == OutTypeExe) { + // TODO compile time stack upper bound detection + lj->args.append("/STACK:16777216"); + } + + coff_append_machine_arg(g, &lj->args); + + bool is_library = g->out_type == OutTypeLib; + switch (g->subsystem) { + case TargetSubsystemAuto: + break; + case TargetSubsystemConsole: + lj->args.append("/SUBSYSTEM:console"); + add_nt_link_args(lj, is_library); + break; + case TargetSubsystemEfiApplication: + lj->args.append("/SUBSYSTEM:efi_application"); + add_uefi_link_args(lj); + break; + case TargetSubsystemEfiBootServiceDriver: + lj->args.append("/SUBSYSTEM:efi_boot_service_driver"); + add_uefi_link_args(lj); + break; + case TargetSubsystemEfiRom: + lj->args.append("/SUBSYSTEM:efi_rom"); + add_uefi_link_args(lj); + break; + case TargetSubsystemEfiRuntimeDriver: + lj->args.append("/SUBSYSTEM:efi_runtime_driver"); + add_uefi_link_args(lj); + break; + case TargetSubsystemNative: + lj->args.append("/SUBSYSTEM:native"); + add_nt_link_args(lj, is_library); + break; + case TargetSubsystemPosix: + lj->args.append("/SUBSYSTEM:posix"); + add_nt_link_args(lj, is_library); + break; + case TargetSubsystemWindows: + lj->args.append("/SUBSYSTEM:windows"); + add_nt_link_args(lj, is_library); + break; + } lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path)))); @@ -665,54 +725,6 @@ continuing_build: } } - //if (g->libc_link_lib != nullptr) { - //if (g->is_static) { - // lj->args.append("--start-group"); - //} - - //lj->args.append("-lmingw32"); - - //lj->args.append("-lgcc"); - //bool is_android = (g->zig_target.env_type == ZigLLVM_Android); - //bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target); - //if (!g->is_static && !is_android) { - // if (!is_cyg_ming) { - // lj->args.append("--as-needed"); - // } - // //lj->args.append("-lgcc_s"); - // if (!is_cyg_ming) { - // lj->args.append("--no-as-needed"); - // } - //} - //if (g->is_static && !is_android) { - // //lj->args.append("-lgcc_eh"); - //} - //if (is_android && !g->is_static) { - // lj->args.append("-ldl"); - //} - - //lj->args.append("-lmoldname"); - //lj->args.append("-lmingwex"); - //lj->args.append("-lmsvcrt"); - - - //if (g->windows_subsystem_windows) { - // lj->args.append("-lgdi32"); - // lj->args.append("-lcomdlg32"); - //} - //lj->args.append("-ladvapi32"); - //lj->args.append("-lshell32"); - //lj->args.append("-luser32"); - //lj->args.append("-lkernel32"); - - //if (g->is_static) { - // lj->args.append("--end-group"); - //} - - //if (lj->link_in_crt) { - // lj->args.append(get_libc_static_file(g, "crtend.o")); - //} - //} } diff --git a/src/main.cpp b/src/main.cpp index faca9511dc..fd8e3db2fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,13 +16,6 @@ #include -#ifdef __GNUC__ -#include -#define STRCASECMP strcasecmp -#else -#define STRCASECMP stricmp -#endif - static int print_error_usage(const char *arg0) { fprintf(stderr, "See `%s help` for detailed usage information\n", arg0); return EXIT_FAILURE; @@ -97,8 +90,8 @@ static int print_full_usage(const char *arg0) { " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" " --no-rosegment compromise security to workaround valgrind bug\n" - " --msvc-subsystem [subsystem] (windows/uefi) /SUBSYSTEM: to the linker\n" - " -framework [name] (darwin) link against framework\n" + " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" + " -framework [name] (darwin) link against framework\n" " -mios-version-min [ver] (darwin) set iOS deployment target\n" " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" " --ver-major [ver] dynamic library semver major version\n" @@ -377,8 +370,6 @@ int main(int argc, char **argv) { const char *target_arch = nullptr; const char *target_os = nullptr; const char *target_environ = nullptr; - bool mwindows = false; - bool mconsole = false; bool rdynamic = false; const char *mmacosx_version_min = nullptr; const char *mios_version_min = nullptr; @@ -401,7 +392,7 @@ int main(int argc, char **argv) { int runtime_args_start = -1; bool no_rosegment_workaround = false; bool system_linker_hack = false; - ZigLLVM_MSVCSubsystemType msvc_subsystem_type = ZigLLVM_MSVC_NONE; + TargetSubsystem subsystem = TargetSubsystemAuto; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -547,36 +538,6 @@ int main(int argc, char **argv) { verbose_llvm_ir = true; } else if (strcmp(arg, "--verbose-cimport") == 0) { verbose_cimport = true; - } else if (strcmp(arg, "--msvc-subsystem") == 0) { - if (i + 1 >= argc) { - fprintf(stderr, "Expected 1 argument after --msvc-subsystem\n"); - return print_error_usage(arg0); - } - i += 1; - if (STRCASECMP(argv[i], "CONSOLE") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_CONSOLE; - } else if (STRCASECMP(argv[i], "WINDOWS") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_WINDOWS; - } else if (STRCASECMP(argv[i], "POSIX") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_POSIX; - } else if (STRCASECMP(argv[i], "NATIVE") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_NATIVE; - } else if (STRCASECMP(argv[i], "EFI_APPLICATION") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_EFI_APPLICATION; - } else if (STRCASECMP(argv[i], "EFI_BOOT_SERVICE_DRIVER") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_EFI_BOOT_SERVICE_DRIVER; - } else if (STRCASECMP(argv[i], "EFI_ROM") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_EFI_ROM; - } else if (STRCASECMP(argv[i], "EFI_RUNTIME_DRIVER") == 0) { - msvc_subsystem_type = ZigLLVM_MSVC_EFI_RUNTIME_DRIVER; - } else { - fprintf(stderr, "Unknown format %s for --msvc-subsystem argument\n", argv[i]); - return EXIT_FAILURE; - } - } else if (strcmp(arg, "-mwindows") == 0) { - mwindows = true; - } else if (strcmp(arg, "-mconsole") == 0) { - mconsole = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; } else if (strcmp(arg, "--no-rosegment") == 0) { @@ -720,6 +681,37 @@ int main(int argc, char **argv) { ver_patch = atoi(argv[i]); } else if (strcmp(arg, "--test-cmd") == 0) { test_exec_args.append(argv[i]); + } else if (strcmp(arg, "--subsystem") == 0) { + if (strcmp(argv[i], "console") == 0) { + subsystem = TargetSubsystemConsole; + } else if (strcmp(argv[i], "windows") == 0) { + subsystem = TargetSubsystemWindows; + } else if (strcmp(argv[i], "posix") == 0) { + subsystem = TargetSubsystemPosix; + } else if (strcmp(argv[i], "native") == 0) { + subsystem = TargetSubsystemNative; + } else if (strcmp(argv[i], "efi_application") == 0) { + subsystem = TargetSubsystemEfiApplication; + } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) { + subsystem = TargetSubsystemEfiBootServiceDriver; + } else if (strcmp(argv[i], "efi_rom") == 0) { + subsystem = TargetSubsystemEfiRom; + } else if (strcmp(argv[i], "efi_runtime_driver") == 0) { + subsystem = TargetSubsystemEfiRuntimeDriver; + } else { + fprintf(stderr, "invalid: --subsystem %s\n" + "Options are:\n" + " console\n" + " windows\n" + " posix\n" + " native\n" + " efi_application\n" + " efi_boot_service_driver\n" + " efi_rom\n" + " efi_runtime_driver\n" + , argv[i]); + return EXIT_FAILURE; + } } else { fprintf(stderr, "Invalid argument: %s\n", arg); return print_error_usage(arg0); @@ -882,7 +874,7 @@ int main(int argc, char **argv) { buf_out_name = buf_create_from_str("run"); } CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir()); - g->msvc_subsystem = msvc_subsystem_type; + g->subsystem = subsystem; if (disable_pic) { if (out_type != OutTypeLib || !is_static) { diff --git a/src/target.cpp b/src/target.cpp index 61543c38d5..6fea79518c 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -283,6 +283,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) { case OsSolaris: return ZigLLVM_Solaris; case OsWindows: + case OsUefi: return ZigLLVM_Win32; case OsHaiku: return ZigLLVM_Haiku; @@ -316,8 +317,6 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) { return ZigLLVM_Contiki; case OsAMDPAL: return ZigLLVM_AMDPAL; - case OsUefi: - return ZigLLVM_Uefi; } zig_unreachable(); } @@ -809,7 +808,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { } const char *target_o_file_ext(ZigTarget *target) { - if (target->env_type == ZigLLVM_MSVC || (target->os == OsWindows || target->os == OsUefi)) { + if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) { return ".obj"; } else { return ".o"; diff --git a/src/target.hpp b/src/target.hpp index 62cc20711a..a87b12351a 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -54,13 +54,24 @@ enum Os { OsUefi, }; +enum TargetSubsystem { + TargetSubsystemAuto, // Zig should infer the subsystem + TargetSubsystemConsole, + TargetSubsystemWindows, + TargetSubsystemPosix, + TargetSubsystemNative, + TargetSubsystemEfiApplication, + TargetSubsystemEfiBootServiceDriver, + TargetSubsystemEfiRom, + TargetSubsystemEfiRuntimeDriver, +}; + struct ZigTarget { ArchType arch; ZigLLVM_VendorType vendor; Os os; ZigLLVM_EnvironmentType env_type; ZigLLVM_ObjectFormatType oformat; - ZigLLVM_MSVCSubsystemType msvc_subsystem = ZigLLVM_MSVC_NONE; }; enum CIntType { diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 0e56e29810..02fa3b24be 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4749,7 +4749,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); - if (codegen->libc_include_dir) { + if (codegen->libc_include_dir != nullptr) { clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->libc_include_dir)); } diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index bd63b7cbe5..bda8fa0adc 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -690,12 +690,7 @@ const char *ZigLLVMGetVendorTypeName(ZigLLVM_VendorType vendor) { } const char *ZigLLVMGetOSTypeName(ZigLLVM_OSType os) { - switch (os) { - case ZigLLVM_Uefi: - return "windows"; - default: - return (const char*)Triple::getOSTypeName((Triple::OSType)os).bytes_begin(); - } + return (const char*)Triple::getOSTypeName((Triple::OSType)os).bytes_begin(); } const char *ZigLLVMGetEnvironmentTypeName(ZigLLVM_EnvironmentType env_type) { diff --git a/src/zig_llvm.h b/src/zig_llvm.h index bb7cb5c5ff..551a4a7448 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -358,8 +357,8 @@ enum ZigLLVM_OSType { ZigLLVM_Mesa3D, ZigLLVM_Contiki, ZigLLVM_AMDPAL, // AMD PAL Runtime - ZigLLVM_Uefi, - ZigLLVM_LastOSType = ZigLLVM_Uefi + + ZigLLVM_LastOSType = ZigLLVM_AMDPAL }; // Synchronize with target.cpp::environ_list @@ -398,19 +397,6 @@ enum ZigLLVM_ObjectFormatType { ZigLLVM_Wasm, }; -// For MSVC-supported subsystems -enum ZigLLVM_MSVCSubsystemType { - ZigLLVM_MSVC_NONE, - ZigLLVM_MSVC_CONSOLE, - ZigLLVM_MSVC_WINDOWS, - ZigLLVM_MSVC_POSIX, - ZigLLVM_MSVC_NATIVE, - ZigLLVM_MSVC_EFI_APPLICATION, - ZigLLVM_MSVC_EFI_BOOT_SERVICE_DRIVER, - ZigLLVM_MSVC_EFI_ROM, - ZigLLVM_MSVC_EFI_RUNTIME_DRIVER, -}; - ZIG_EXTERN_C const char *ZigLLVMGetArchTypeName(enum ZigLLVM_ArchType arch); ZIG_EXTERN_C const char *ZigLLVMGetSubArchTypeName(enum ZigLLVM_SubArchType sub_arch); ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor); diff --git a/std/os/index.zig b/std/os/index.zig index ce65667157..bb2eb76265 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -18,7 +18,7 @@ test "std.os" { _ = @import("test.zig"); _ = @import("time.zig"); _ = @import("windows/index.zig"); - _ = @import("uefi/index.zig"); + _ = @import("uefi.zig"); _ = @import("get_app_data_dir.zig"); } @@ -27,7 +27,7 @@ pub const darwin = @import("darwin.zig"); pub const linux = @import("linux/index.zig"); pub const freebsd = @import("freebsd/index.zig"); pub const zen = @import("zen.zig"); -pub const uefi = @import("uefi/index.zig"); +pub const uefi = @import("uefi.zig"); pub const posix = switch (builtin.os) { Os.linux => linux, @@ -192,6 +192,7 @@ pub fn abort() noreturn { windows.ExitProcess(3); }, Os.uefi => { + // TODO there's gotta be a better thing to do here than loop forever while (true) {} }, else => @compileError("Unsupported OS"), diff --git a/std/os/uefi.zig b/std/os/uefi.zig new file mode 100644 index 0000000000..8ed60d9c9b --- /dev/null +++ b/std/os/uefi.zig @@ -0,0 +1,2 @@ +// TODO this is where the extern declarations go. For example, see +// inc/efilib.h in gnu-efi-code diff --git a/std/os/uefi/index.zig b/std/os/uefi/index.zig deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/special/panic.zig b/std/special/panic.zig index fe1e020604..bd3ad971e0 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -10,9 +10,13 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn @setCold(true); switch (builtin.os) { // TODO: fix panic in zen. - builtin.Os.freestanding, builtin.Os.zen, builtin.Os.uefi => { + builtin.Os.freestanding, builtin.Os.zen => { while (true) {} }, + builtin.Os.uefi => { + // TODO look into using the debug info and logging helpful messages + std.os.abort(); + }, else => { const first_trace_addr = @ptrToInt(@returnAddress()); std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); -- cgit v1.2.3 From 9b8e23934bc87f1fd6a42cdfdd551212994b6e58 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Feb 2019 17:49:29 -0500 Subject: introduce --single-threaded build option closes #1764 This adds another boolean to the test matrix; hopefully it does not inflate the time too much. std.event.Loop does not work with this option yet. See #1908 --- doc/langref.html.in | 19 +++++++++ src/all_types.hpp | 1 + src/codegen.cpp | 4 ++ src/main.cpp | 6 +++ std/atomic/queue.zig | 42 ++++++++++++------ std/atomic/stack.zig | 82 ++++++++++++++++++++++++------------ std/event/channel.zig | 3 ++ std/event/future.zig | 3 ++ std/event/group.zig | 3 ++ std/event/lock.zig | 3 ++ std/event/loop.zig | 22 ++++++++++ std/event/net.zig | 3 ++ std/event/rwlock.zig | 3 ++ std/mutex.zig | 66 +++++++++++++++++++++-------- std/os/index.zig | 1 + std/os/test.zig | 4 ++ std/statically_initialized_mutex.zig | 21 +++++---- test/tests.zig | 43 +++++++++++-------- 18 files changed, 246 insertions(+), 83 deletions(-) (limited to 'src/main.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index e192dbbf76..144c8571c4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6714,6 +6714,25 @@ pub fn build(b: *Builder) void { {#header_close#} {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} {#header_close#} + + {#header_open|Single Threaded Builds#} +

Zig has a compile option --single-threaded which has the following effects: +

    +
  • {#link|@atomicLoad#} is emitted as a normal load.
  • +
  • {#link|@atomicRmw#} is emitted as a normal memory load, modify, store.
  • +
  • {#link|@fence#} becomes a no-op.
  • +
  • Variables which have Thread Local Storage instead become globals. TODO thread local variables + are not implemented yet.
  • +
  • The overhead of {#link|Coroutines#} becomes equivalent to function call overhead. + TODO: please note this will not be implemented until the upcoming Coroutine Rewrite
  • +
  • The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#} + and therefore various userland APIs which read this variable become more efficient. + For example {#syntax#}std.Mutex{#endsyntax#} becomes + an empty data structure and all of its functions become no-ops.
  • +
+

+ {#header_close#} + {#header_open|Undefined Behavior#}

Zig has many instances of undefined behavior. If undefined behavior is diff --git a/src/all_types.hpp b/src/all_types.hpp index f6fe72891d..3fc6772b31 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1808,6 +1808,7 @@ struct CodeGen { bool is_static; bool strip_debug_symbols; bool is_test_build; + bool is_single_threaded; bool is_native_target; bool linker_rdynamic; bool no_rosegment_workaround; diff --git a/src/codegen.cpp b/src/codegen.cpp index e2576f5335..b73fda59d1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->string_literals_table.init(16); g->type_info_cache.init(32); g->is_test_build = false; + g->is_single_threaded = false; buf_resize(&g->global_asm, 0); for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { @@ -7377,6 +7378,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const endian = %s;\n", endian_str); } buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); + buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ); @@ -7411,6 +7413,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_buf(&cache_hash, compiler_id); cache_int(&cache_hash, g->build_mode); cache_bool(&cache_hash, g->is_test_build); + cache_bool(&cache_hash, g->is_single_threaded); cache_int(&cache_hash, g->zig_target.arch.arch); cache_int(&cache_hash, g->zig_target.arch.sub_arch); cache_int(&cache_hash, g->zig_target.vendor); @@ -8329,6 +8332,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->is_static); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); + cache_bool(ch, g->is_single_threaded); cache_bool(ch, g->is_native_target); cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->no_rosegment_workaround); diff --git a/src/main.cpp b/src/main.cpp index fd8e3db2fa..81f49089be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) { " --release-fast build with optimizations on and safety off\n" " --release-safe build with optimizations on and safety on\n" " --release-small build with size optimizations on and safety off\n" + " --single-threaded source may assume it is only used single-threaded\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" " --target-arch [name] specify target architecture\n" @@ -393,6 +394,7 @@ int main(int argc, char **argv) { bool no_rosegment_workaround = false; bool system_linker_hack = false; TargetSubsystem subsystem = TargetSubsystemAuto; + bool is_single_threaded = false; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -550,6 +552,8 @@ int main(int argc, char **argv) { disable_pic = true; } else if (strcmp(arg, "--system-linker-hack") == 0) { system_linker_hack = true; + } else if (strcmp(arg, "--single-threaded") == 0) { + is_single_threaded = true; } else if (strcmp(arg, "--test-cmd-bin") == 0) { test_exec_args.append(nullptr); } else if (arg[1] == 'L' && arg[2] != 0) { @@ -816,6 +820,7 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuiltin: { CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir()); + g->is_single_threaded = is_single_threaded; Buf *builtin_source = codegen_generate_builtin_source(g); if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); @@ -889,6 +894,7 @@ int main(int argc, char **argv) { codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); codegen_set_is_test(g, cmd == CmdTest); + g->is_single_threaded = is_single_threaded; codegen_set_linker_script(g, linker_script); if (each_lib_rpath) codegen_set_each_lib_rpath(g, each_lib_rpath); diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 1aab4c32de..6c61bcc048 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -170,20 +170,36 @@ test "std.atomic.Queue" { .get_count = 0, }; - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } + if (builtin.single_threaded) { + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startPuts(&context) == 0); + } + } + context.puts_done = 1; + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startGets(&context) == 0); + } + } + } else { + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + } if (context.put_sum != context.get_sum) { std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index b69a93733c..1e4981353b 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder; /// Many reader, many writer, non-allocating, thread-safe /// Uses a spinlock to protect push() and pop() +/// When building in single threaded mode, this is a simple linked list. pub fn Stack(comptime T: type) type { return struct { root: ?*Node, - lock: u8, + lock: @typeOf(lock_init), + + const lock_init = if (builtin.single_threaded) {} else u8(0); pub const Self = @This(); @@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type { pub fn init() Self { return Self{ .root = null, - .lock = 0, + .lock = lock_init, }; } @@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type { } pub fn push(self: *Self, node: *Node) void { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); - - node.next = self.root; - self.root = node; + if (builtin.single_threaded) { + node.next = self.root; + self.root = node; + } else { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + node.next = self.root; + self.root = node; + } } pub fn pop(self: *Self) ?*Node { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); - - const root = self.root orelse return null; - self.root = root.next; - return root; + if (builtin.single_threaded) { + const root = self.root orelse return null; + self.root = root.next; + return root; + } else { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + const root = self.root orelse return null; + self.root = root.next; + return root; + } } pub fn isEmpty(self: *Self) bool { @@ -90,20 +104,36 @@ test "std.atomic.stack" { .get_count = 0, }; - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } + if (builtin.single_threaded) { + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startPuts(&context) == 0); + } + } + context.puts_done = 1; + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startGets(&context) == 0); + } + } + } else { + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + } if (context.put_sum != context.get_sum) { std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); diff --git a/std/event/channel.zig b/std/event/channel.zig index 133ce1c69c..f04ad1604e 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -319,6 +319,9 @@ pub fn Channel(comptime T: type) type { } test "std.event.Channel" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/future.zig b/std/event/future.zig index d61768b198..55ed01046d 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type { } test "std.event.Future" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/group.zig b/std/event/group.zig index 9f2687a5b3..0e9c2d9655 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -121,6 +121,9 @@ pub fn Group(comptime ReturnType: type) type { } test "std.event.Group" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/lock.zig b/std/event/lock.zig index 46e0d13468..01978e1909 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -122,6 +122,9 @@ pub const Lock = struct { }; test "std.event.Lock" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/loop.zig b/std/event/loop.zig index 43965e8293..d5228db604 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -97,6 +97,7 @@ pub const Loop = struct { /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode"); const core_count = try os.cpuCount(allocator); return self.initInternal(allocator, core_count); } @@ -201,6 +202,11 @@ pub const Loop = struct { self.os_data.fs_thread.wait(); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { // writing 8 bytes to an eventfd cannot fail @@ -301,6 +307,11 @@ pub const Loop = struct { self.os_data.fs_thread.wait(); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable; @@ -338,6 +349,11 @@ pub const Loop = struct { self.available_eventfd_resume_nodes.push(eventfd_node); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { var i: usize = 0; @@ -845,6 +861,9 @@ pub const Loop = struct { }; test "std.event.Loop - basic" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -858,6 +877,9 @@ test "std.event.Loop - basic" { } test "std.event.Loop - call" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/net.zig b/std/event/net.zig index 6838704084..9dac6aa566 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File { } test "listen on a port, send bytes, receive bytes" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + if (builtin.os != builtin.Os.linux) { // TODO build abstractions for other operating systems return error.SkipZigTest; diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig index 5d48ea893e..f272ac71ea 100644 --- a/std/event/rwlock.zig +++ b/std/event/rwlock.zig @@ -211,6 +211,9 @@ pub const RwLock = struct { }; test "std.event.RwLock" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/mutex.zig b/std/mutex.zig index 723581cbef..54173fa38a 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -14,7 +14,36 @@ const windows = std.os.windows; /// If you need static initialization, use std.StaticallyInitializedMutex. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf -pub const Mutex = switch(builtin.os) { +/// When an application is built in single threaded release mode, all the functions are +/// no-ops. In single threaded debug mode, there is deadlock detection. +pub const Mutex = if (builtin.single_threaded) + struct { + lock: @typeOf(lock_init), + + const lock_init = if (std.debug.runtime_safety) false else {}; + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + if (std.debug.runtime_safety) { + self.mutex.lock = false; + } + } + }; + pub fn init() Mutex { + return Mutex{ .lock = lock_init }; + } + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + if (std.debug.runtime_safety and self.lock) { + @panic("deadlock detected"); + } + return Held{ .mutex = self }; + } + } +else switch (builtin.os) { builtin.Os.linux => struct { /// 0: unlocked /// 1: locked, no waiters @@ -39,9 +68,7 @@ pub const Mutex = switch(builtin.os) { }; pub fn init() Mutex { - return Mutex { - .lock = 0, - }; + return Mutex{ .lock = 0 }; } pub fn deinit(self: *Mutex) void {} @@ -60,7 +87,7 @@ pub const Mutex = switch(builtin.os) { } c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } - return Held { .mutex = self }; + return Held{ .mutex = self }; } }, // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a @@ -78,21 +105,19 @@ pub const Mutex = switch(builtin.os) { mutex: *Mutex, pub fn release(self: Held) void { - SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock }); } }; pub fn init() Mutex { - return Mutex { - .lock = SpinLock.init(), - }; + return Mutex{ .lock = SpinLock.init() }; } pub fn deinit(self: *Mutex) void {} pub fn acquire(self: *Mutex) Held { _ = self.lock.acquire(); - return Held { .mutex = self }; + return Held{ .mutex = self }; } }, }; @@ -122,15 +147,20 @@ test "std.Mutex" { .data = 0, }; - const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; - for (threads) |*t| { - t.* = try std.os.spawnThread(&context, worker); - } - for (threads) |t| - t.wait(); + if (builtin.single_threaded) { + worker(&context); + std.debug.assertOrPanic(context.data == TestContext.incr_count); + } else { + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, worker); + } + for (threads) |t| + t.wait(); - std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); + } } fn worker(ctx: *TestContext) void { diff --git a/std/os/index.zig b/std/os/index.zig index 78d543583d..75abe3bbde 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{ /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { + if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/ziglang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; diff --git a/std/os/test.zig b/std/os/test.zig index 5142920687..f14cf47786 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void { } test "std.os.Thread.getCurrentId" { + if (builtin.single_threaded) return error.SkipZigTest; + var thread_current_id: os.Thread.Id = undefined; const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); const thread_id = thread.handle(); @@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" { } test "spawn threads" { + if (builtin.single_threaded) return error.SkipZigTest; + var shared_ctx: i32 = 1; const thread1 = try std.os.spawnThread({}, start1); diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig index dd875eeaf9..37582d49c1 100644 --- a/std/statically_initialized_mutex.zig +++ b/std/statically_initialized_mutex.zig @@ -93,13 +93,18 @@ test "std.StaticallyInitializedMutex" { .data = 0, }; - const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; - for (threads) |*t| { - t.* = try std.os.spawnThread(&context, TestContext.worker); - } - for (threads) |t| - t.wait(); + if (builtin.single_threaded) { + TestContext.worker(&context); + std.debug.assertOrPanic(context.data == TestContext.incr_count); + } else { + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, TestContext.worker); + } + for (threads) |t| + t.wait(); - std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); + } } diff --git a/test/tests.zig b/test/tests.zig index 1ca06b4b34..73d4644d18 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -163,25 +163,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); for (modes) |mode| { - for ([]bool{ - false, - true, - }) |link_libc| { - if (link_libc and !is_native) { - // don't assume we have a cross-compiling libc set up - continue; - } - const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare")); - these_tests.setFilter(test_filter); - these_tests.setBuildMode(mode); - if (!is_native) { - these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); - } - if (link_libc) { - these_tests.linkSystemLibrary("c"); + for ([]bool{ false, true }) |link_libc| { + for ([]bool{ false, true }) |single_threaded| { + if (link_libc and !is_native) { + // don't assume we have a cross-compiling libc set up + continue; + } + const these_tests = b.addTest(root_src); + these_tests.setNamePrefix(b.fmt( + "{}-{}-{}-{}-{}-{} ", + name, + @tagName(test_target.os), + @tagName(test_target.arch), + @tagName(mode), + if (link_libc) "c" else "bare", + if (single_threaded) "single" else "multi", + )); + these_tests.setFilter(test_filter); + these_tests.setBuildMode(mode); + if (!is_native) { + these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); + } + if (link_libc) { + these_tests.linkSystemLibrary("c"); + } + step.dependOn(&these_tests.step); } - step.dependOn(&these_tests.step); } } } -- cgit v1.2.3 From d2602b442e7cdea2e74584f9529917d7a53625fd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Feb 2019 14:32:20 -0500 Subject: require running std lib tests coherently this should actually improve CI times a bit too See the description at the top of std/os/startup.zig (deleted in this commit) for a more detailed understanding of what this commit does. --- CMakeLists.txt | 1 - src/codegen.cpp | 18 +++++++++++++----- src/codegen.hpp | 2 +- src/link.cpp | 2 +- src/main.cpp | 12 +++++++++--- std/build.zig | 10 ++++++++++ std/index.zig | 1 - std/os/index.zig | 31 +++++++++++++++++++++---------- std/os/startup.zig | 26 -------------------------- std/special/bootstrap.zig | 14 +++++++------- test/tests.zig | 3 +++ 11 files changed, 65 insertions(+), 55 deletions(-) delete mode 100644 std/os/startup.zig (limited to 'src/main.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index a093bfbfd9..4dd6a1dcfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -587,7 +587,6 @@ set(ZIG_STD_FILES "os/linux/vdso.zig" "os/linux/x86_64.zig" "os/path.zig" - "os/startup.zig" "os/time.zig" "os/uefi.zig" "os/windows/advapi32.zig" diff --git a/src/codegen.cpp b/src/codegen.cpp index d8fc077efc..5e282160d6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = { }; CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, - Buf *zig_lib_dir) + Buf *zig_lib_dir, Buf *override_std_dir) { CodeGen *g = allocate(1); @@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->zig_lib_dir = zig_lib_dir; - g->zig_std_dir = buf_alloc(); - os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); + if (override_std_dir == nullptr) { + g->zig_std_dir = buf_alloc(); + os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); + } else { + g->zig_std_dir = override_std_dir; + } g->zig_c_headers_dir = buf_alloc(); os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); @@ -8356,8 +8360,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) { if (!entry) break; - cache_buf(ch, entry->key); - add_cache_pkg(g, ch, entry->value); + // TODO: I think we need a more sophisticated detection of + // packages we have already seen + if (entry->value != pkg) { + cache_buf(ch, entry->key); + add_cache_pkg(g, ch, entry->value); + } } } diff --git a/src/codegen.hpp b/src/codegen.hpp index 6f1cdfb677..4bd8f2dcca 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -15,7 +15,7 @@ #include CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, - Buf *zig_lib_dir); + Buf *zig_lib_dir, Buf *override_std_dir); void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); diff --git a/src/link.cpp b/src/link.cpp index 593f7f309f..58221a99ea 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) } CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir); + parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir); child_gen->out_h_path = nullptr; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; diff --git a/src/main.cpp b/src/main.cpp index 81f49089be..73a1d75d42 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) { " -dirafter [dir] same as -isystem but do it last\n" " -isystem [dir] add additional search path for other .h files\n" " -mllvm [arg] forward an arg to LLVM's option processing\n" + " --override-std-dir [arg] use an alternate Zig standard library\n" "\n" "Link Options:\n" " --dynamic-linker [path] set the path to ld.so\n" @@ -395,6 +396,7 @@ int main(int argc, char **argv) { bool system_linker_hack = false; TargetSubsystem subsystem = TargetSubsystemAuto; bool is_single_threaded = false; + Buf *override_std_dir = nullptr; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -430,7 +432,8 @@ int main(int argc, char **argv) { Buf *build_runner_path = buf_alloc(); os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path); - CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir()); + CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + override_std_dir); g->enable_time_report = timing_info; buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); codegen_set_out_name(g, buf_create_from_str("build")); @@ -645,6 +648,8 @@ int main(int argc, char **argv) { clang_argv.append(argv[i]); llvm_argv.append(argv[i]); + } else if (strcmp(arg, "--override-std-dir") == 0) { + override_std_dir = buf_create_from_str(argv[i]); } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { lib_dirs.append(argv[i]); } else if (strcmp(arg, "--library") == 0) { @@ -819,7 +824,7 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuiltin: { - CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir()); + CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); g->is_single_threaded = is_single_threaded; Buf *builtin_source = codegen_generate_builtin_source(g); if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { @@ -878,7 +883,8 @@ int main(int argc, char **argv) { if (cmd == CmdRun && buf_out_name == nullptr) { buf_out_name = buf_create_from_str("run"); } - CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir()); + CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), + override_std_dir); g->subsystem = subsystem; if (disable_pic) { diff --git a/std/build.zig b/std/build.zig index 5246d97339..07efcec30d 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1686,6 +1686,7 @@ pub const TestStep = struct { no_rosegment: bool, output_path: ?[]const u8, system_linker_hack: bool, + override_std_dir: ?[]const u8, pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); @@ -1707,6 +1708,7 @@ pub const TestStep = struct { .no_rosegment = false, .output_path = null, .system_linker_hack = false, + .override_std_dir = null, }; } @@ -1737,6 +1739,10 @@ pub const TestStep = struct { self.build_mode = mode; } + pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void { + self.override_std_dir = dir_path; + } + pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { self.output_path = file_path; @@ -1914,6 +1920,10 @@ pub const TestStep = struct { if (self.system_linker_hack) { try zig_args.append("--system-linker-hack"); } + if (self.override_std_dir) |dir| { + try zig_args.append("--override-std-dir"); + try zig_args.append(builder.pathFromRoot(dir)); + } try builder.spawnChild(zig_args.toSliceConst()); } diff --git a/std/index.zig b/std/index.zig index ef3988460f..2a63244004 100644 --- a/std/index.zig +++ b/std/index.zig @@ -45,7 +45,6 @@ pub const unicode = @import("unicode.zig"); pub const zig = @import("zig/index.zig"); pub const lazyInit = @import("lazy_init.zig").lazyInit; -pub const startup = @import("os/startup.zig"); test "std" { // run tests from these diff --git a/std/os/index.zig b/std/os/index.zig index 68b3409757..d17b6f4f40 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -8,8 +8,9 @@ const is_posix = switch (builtin.os) { }; const os = @This(); -// See the comment in startup.zig for why this does not use the `std` global above. -const startup = @import("std").startup; +comptime { + assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir +} test "std.os" { _ = @import("child_process.zig"); @@ -670,11 +671,14 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { } } +pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; +pub var posix_environ_raw: [][*]u8 = undefined; + /// See std.elf for the constants. pub fn linuxGetAuxVal(index: usize) usize { if (builtin.link_libc) { return usize(std.c.getauxval(index)); - } else if (startup.linux_elf_aux_maybe) |auxv| { + } else if (linux_elf_aux_maybe) |auxv| { var i: usize = 0; while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { if (auxv[i].a_type == index) @@ -734,7 +738,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.setMove(key, value); } } else { - for (startup.posix_environ_raw) |ptr| { + for (posix_environ_raw) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const key = ptr[0..line_i]; @@ -756,7 +760,7 @@ test "os.getEnvMap" { /// TODO make this go through libc when we have it pub fn getEnvPosix(key: []const u8) ?[]const u8 { - for (startup.posix_environ_raw) |ptr| { + for (posix_environ_raw) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const this_key = ptr[0..line_i]; @@ -1937,14 +1941,14 @@ pub const ArgIteratorPosix = struct { pub fn init() ArgIteratorPosix { return ArgIteratorPosix{ .index = 0, - .count = startup.posix_argv_raw.len, + .count = raw.len, }; } pub fn next(self: *ArgIteratorPosix) ?[]const u8 { if (self.index == self.count) return null; - const s = startup.posix_argv_raw[self.index]; + const s = raw[self.index]; self.index += 1; return cstr.toSlice(s); } @@ -1955,6 +1959,10 @@ pub const ArgIteratorPosix = struct { self.index += 1; return true; } + + /// This is marked as public but actually it's only meant to be used + /// internally by zig's startup code. + pub var raw: [][*]u8 = undefined; }; pub const ArgIteratorWindows = struct { @@ -3000,6 +3008,9 @@ pub const SpawnThreadError = error{ Unexpected, }; +pub var linux_tls_phdr: ?*std.elf.Phdr = null; +pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is + /// caller must call wait on the returned thread /// fn startFn(@typeOf(context)) T /// where T is u8, noreturn, void, or !void @@ -3109,7 +3120,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread } // Finally, the Thread Local Storage, if any. if (!Thread.use_pthreads) { - if (startup.linux_tls_phdr) |tls_phdr| { + if (linux_tls_phdr) |tls_phdr| { l = mem.alignForward(l, tls_phdr.p_align); tls_start_offset = l; l += tls_phdr.p_memsz; @@ -3153,8 +3164,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; var newtls: usize = undefined; - if (startup.linux_tls_phdr) |tls_phdr| { - @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), startup.linux_tls_img_src, tls_phdr.p_filesz); + if (linux_tls_phdr) |tls_phdr| { + @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz); thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); flags |= posix.CLONE_SETTLS; diff --git a/std/os/startup.zig b/std/os/startup.zig deleted file mode 100644 index c54d274c5d..0000000000 --- a/std/os/startup.zig +++ /dev/null @@ -1,26 +0,0 @@ -// This file contains global variables that are initialized on startup from -// std/special/bootstrap.zig. There are a few things to be aware of here. -// -// First, when building an object or library, and no entry point is defined -// (such as pub fn main), std/special/bootstrap.zig is not included in the -// compilation. And so these global variables will remain set to the values -// you see here. -// -// Second, when using `zig test` to test the standard library, note that -// `zig test` is self-hosted. This means that it uses std/special/bootstrap.zig -// and an @import("std") from the install directory, which is distinct from -// the standard library files that we are directly testing with `zig test`. -// This means that these global variables would not get set. So the workaround -// here is that references to these globals from the standard library must -// use `@import("std").startup` rather than -// `@import("path/to/std/index.zig").startup` (and rather than the file path of -// this file directly). We also put "std" as a reference to itself in the -// standard library package so that this can work. - -const std = @import("../index.zig"); - -pub var linux_tls_phdr: ?*std.elf.Phdr = null; -pub var linux_tls_img_src: [*]const u8 = undefined; // defined when linux_tls_phdr is non-null -pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; -pub var posix_environ_raw: [][*]u8 = undefined; -pub var posix_argv_raw: [][*]u8 = undefined; diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 0e84f67481..97699e0cc5 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -64,7 +64,7 @@ fn posixCallMainAndExit() noreturn { if (builtin.os == builtin.Os.linux) { // Scan auxiliary vector. const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); - std.startup.linux_elf_aux_maybe = auxv; + std.os.linux_elf_aux_maybe = auxv; var i: usize = 0; var at_phdr: usize = 0; var at_phnum: usize = 0; @@ -87,8 +87,8 @@ fn posixCallMainAndExit() noreturn { // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { - std.startup.posix_argv_raw = argv[0..argc]; - std.startup.posix_environ_raw = envp; + std.os.ArgIteratorPosix.raw = argv[0..argc]; + std.os.posix_environ_raw = envp; return callMain(); } @@ -145,15 +145,15 @@ fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917 switch (phdr.p_type) { std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr, - std.elf.PT_TLS => std.startup.linux_tls_phdr = phdr, + std.elf.PT_TLS => std.os.linux_tls_phdr = phdr, else => continue, } } - const tls_phdr = std.startup.linux_tls_phdr orelse return; - std.startup.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr); + const tls_phdr = std.os.linux_tls_phdr orelse return; + std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr); assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough - @memcpy(&main_thread_tls_bytes, std.startup.linux_tls_img_src, tls_phdr.p_filesz); + @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz); tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz; linuxSetThreadArea(@ptrToInt(&tls_end_addr)); } diff --git a/test/tests.zig b/test/tests.zig index 548496fa2f..fc188f5550 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons if (link_libc) { these_tests.linkSystemLibrary("c"); } + if (mem.eql(u8, name, "std")) { + these_tests.overrideStdDir("std"); + } step.dependOn(&these_tests.step); } } -- cgit v1.2.3 From ba56f365c813440b79c1710c6a8b0fd591883e13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 00:42:56 -0500 Subject: bring zig fmt to stage1 --- CMakeLists.txt | 5 + src-self-hosted/main.zig | 6 +- src/main.cpp | 36 +++++- std/special/fmt_runner.zig | 265 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 7 deletions(-) create mode 100644 std/special/fmt_runner.zig (limited to 'src/main.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e80c65dbd..db5f50908c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -652,6 +652,7 @@ set(ZIG_STD_FILES "special/compiler_rt/udivmodti4.zig" "special/compiler_rt/udivti3.zig" "special/compiler_rt/umodti3.zig" + "special/fmt_runner.zig" "special/init-exe/build.zig" "special/init-exe/src/main.zig" "special/init-lib/build.zig" @@ -905,3 +906,7 @@ foreach(file ${ZIG_STD_FILES}) get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY) install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}") endforeach() + +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 64aa729469..42556beaed 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -24,7 +24,7 @@ var stderr_file: os.File = undefined; var stderr: *io.OutStream(os.File.WriteError) = undefined; var stdout: *io.OutStream(os.File.WriteError) = undefined; -const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB +pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB const usage = \\usage: zig [command] [options] @@ -510,7 +510,7 @@ fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { return buildOutputType(allocator, args, Compilation.Kind.Obj); } -const usage_fmt = +pub const usage_fmt = \\usage: zig fmt [file]... \\ \\ Formats the input files and modifies them in-place. @@ -527,7 +527,7 @@ const usage_fmt = \\ ; -const args_fmt_spec = []Flag{ +pub const args_fmt_spec = []Flag{ Flag.Bool("--help"), Flag.Bool("--check"), Flag.Option("--color", []const []const u8{ diff --git a/src/main.cpp b/src/main.cpp index 73a1d75d42..78446f9e98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,8 +21,8 @@ static int print_error_usage(const char *arg0) { return EXIT_FAILURE; } -static int print_full_usage(const char *arg0) { - fprintf(stdout, +static int print_full_usage(const char *arg0, FILE *file, int return_code) { + fprintf(file, "Usage: %s [command] [options]\n" "\n" "Commands:\n" @@ -31,6 +31,7 @@ static int print_full_usage(const char *arg0) { " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" " builtin show the source code of that @import(\"builtin\")\n" + " fmt parse files and render in canonical zig format\n" " help show this usage information\n" " id print the base64-encoded compiler id\n" " init-exe initialize a `zig build` application in the cwd\n" @@ -106,7 +107,7 @@ static int print_full_usage(const char *arg0) { " --test-cmd [arg] specify test execution command one arg at a time\n" " --test-cmd-bin appends test binary path to test cmd args\n" , arg0); - return EXIT_SUCCESS; + return return_code; } static const char *ZIG_ZEN = "\n" @@ -515,6 +516,31 @@ int main(int argc, char **argv) { fprintf(stderr, "\n"); } return (term.how == TerminationIdClean) ? term.code : -1; + } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { + init_all_targets(); + Buf *fmt_runner_path = buf_alloc(); + os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); + CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + nullptr); + g->is_single_threaded = true; + codegen_set_out_name(g, buf_create_from_str("fmt")); + g->enable_cache = true; + + codegen_build_and_link(g); + + ZigList args = {0}; + for (int i = 2; i < argc; i += 1) { + args.append(argv[i]); + } + args.append(nullptr); + const char *exec_path = buf_ptr(&g->output_file_path); + + os_execv(exec_path, args.items); + + args.pop(); + Termination term; + os_spawn_process(exec_path, args, &term); + return term.code; } for (int i = 1; i < argc; i += 1) { @@ -527,6 +553,8 @@ int main(int argc, char **argv) { build_mode = BuildModeSafeRelease; } else if (strcmp(arg, "--release-small") == 0) { build_mode = BuildModeSmallRelease; + } else if (strcmp(arg, "--help") == 0) { + return print_full_usage(arg0, stderr, EXIT_FAILURE); } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { @@ -1080,7 +1108,7 @@ int main(int argc, char **argv) { } } case CmdHelp: - return print_full_usage(arg0); + return print_full_usage(arg0, stdout, EXIT_SUCCESS); case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); return EXIT_SUCCESS; diff --git a/std/special/fmt_runner.zig b/std/special/fmt_runner.zig new file mode 100644 index 0000000000..b6b728f5cf --- /dev/null +++ b/std/special/fmt_runner.zig @@ -0,0 +1,265 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const os = std.os; +const io = std.io; +const mem = std.mem; +const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const Buffer = std.Buffer; +const ast = std.zig.ast; + +const arg = @import("fmt/arg.zig"); +const self_hosted_main = @import("fmt/main.zig"); +const Args = arg.Args; +const Flag = arg.Flag; +const errmsg = @import("fmt/errmsg.zig"); + +var stderr_file: os.File = undefined; +var stderr: *io.OutStream(os.File.WriteError) = undefined; +var stdout: *io.OutStream(os.File.WriteError) = undefined; + +// This brings `zig fmt` to stage 1. +pub fn main() !void { + // Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` is a short-lived, + // one shot program. We don't need to waste time freeing memory and finding places to squish + // bytes into. So we free everything all at once at the very end. + var direct_allocator = std.heap.DirectAllocator.init(); + var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator); + const allocator = &arena.allocator; + + var stdout_file = try std.io.getStdOut(); + var stdout_out_stream = stdout_file.outStream(); + stdout = &stdout_out_stream.stream; + + stderr_file = try std.io.getStdErr(); + var stderr_out_stream = stderr_file.outStream(); + stderr = &stderr_out_stream.stream; + const args = try std.os.argsAlloc(allocator); + + var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args); + defer flags.deinit(); + + if (flags.present("help")) { + try stdout.write(self_hosted_main.usage_fmt); + os.exit(0); + } + + const color = blk: { + if (flags.single("color")) |color_flag| { + if (mem.eql(u8, color_flag, "auto")) { + break :blk errmsg.Color.Auto; + } else if (mem.eql(u8, color_flag, "on")) { + break :blk errmsg.Color.On; + } else if (mem.eql(u8, color_flag, "off")) { + break :blk errmsg.Color.Off; + } else unreachable; + } else { + break :blk errmsg.Color.Auto; + } + }; + + if (flags.present("stdin")) { + if (flags.positionals.len != 0) { + try stderr.write("cannot use --stdin with positional arguments\n"); + os.exit(1); + } + + var stdin_file = try io.getStdIn(); + var stdin = stdin_file.inStream(); + + const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size); + defer allocator.free(source_code); + + var tree = std.zig.parse(allocator, source_code) catch |err| { + try stderr.print("error parsing stdin: {}\n", err); + os.exit(1); + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(allocator, parse_error, &tree, "", stderr_file, color); + } + if (tree.errors.len != 0) { + os.exit(1); + } + if (flags.present("check")) { + const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree); + const code = if (anything_changed) u8(1) else u8(0); + os.exit(code); + } + + _ = try std.zig.render(allocator, stdout, &tree); + return; + } + + if (flags.positionals.len == 0) { + try stderr.write("expected at least one source file argument\n"); + os.exit(1); + } + + if (flags.positionals.len == 0) { + try stderr.write("expected at least one source file argument\n"); + os.exit(1); + } + + var fmt = Fmt{ + .seen = Fmt.SeenMap.init(allocator), + .any_error = false, + .color = color, + .allocator = allocator, + }; + + const check_mode = flags.present("check"); + + for (flags.positionals.toSliceConst()) |file_path| { + try fmtPath(&fmt, file_path, check_mode); + } + if (fmt.any_error) { + os.exit(1); + } +} + +const FmtError = error{ + SystemResources, + OperationAborted, + IoPending, + BrokenPipe, + Unexpected, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + OutOfMemory, + RenameAcrossMountPoints, + ReadOnlyFileSystem, + LinkQuotaExceeded, + FileBusy, +} || os.File.OpenError; + +fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { + const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); + defer fmt.allocator.free(file_path); + + if (try fmt.seen.put(file_path, {})) |_| return; + + const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => { + // TODO make event based (and dir.next()) + var dir = try std.os.Dir.open(fmt.allocator, file_path); + defer dir.close(); + + while (try dir.next()) |entry| { + if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try os.path.join(fmt.allocator, [][]const u8{ file_path, entry.name }); + try fmtPath(fmt, full_path, check_mode); + } + } + return; + }, + else => { + // TODO lock stderr printing + try stderr.print("unable to open '{}': {}\n", file_path, err); + fmt.any_error = true; + return; + }, + }; + defer fmt.allocator.free(source_code); + + var tree = std.zig.parse(fmt.allocator, source_code) catch |err| { + try stderr.print("error parsing file '{}': {}\n", file_path, err); + fmt.any_error = true; + return; + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color); + } + if (tree.errors.len != 0) { + fmt.any_error = true; + return; + } + + if (check_mode) { + const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, &tree); + if (anything_changed) { + try stderr.print("{}\n", file_path); + fmt.any_error = true; + } + } else { + // TODO make this evented + const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); + defer baf.destroy(); + + const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree); + if (anything_changed) { + try stderr.print("{}\n", file_path); + try baf.finish(); + } + } +} + +const Fmt = struct { + seen: SeenMap, + any_error: bool, + color: errmsg.Color, + allocator: *mem.Allocator, + + const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); +}; + +fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree, + path: []const u8, file: os.File, color: errmsg.Color,) !void +{ + const color_on = switch (color) { + errmsg.Color.Auto => file.isTty(), + errmsg.Color.On => true, + errmsg.Color.Off => false, + }; + const lok_token = parse_error.loc(); + const span = errmsg.Span{ + .first = lok_token, + .last = lok_token, + }; + + const first_token = tree.tokens.at(span.first); + const last_token = tree.tokens.at(span.last); + const start_loc = tree.tokenLocationPtr(0, first_token); + const end_loc = tree.tokenLocationPtr(first_token.end, last_token); + + var text_buf = try std.Buffer.initSize(allocator, 0); + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + const text = text_buf.toOwnedSlice(); + + const stream = &file.outStream().stream; + if (!color_on) { + try stream.print( + "{}:{}:{}: error: {}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + text, + ); + return; + } + + try stream.print( + "{}:{}:{}: error: {}\n{}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + text, + tree.source[start_loc.line_start..start_loc.line_end], + ); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.write("\n"); +} -- cgit v1.2.3