diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b81669a30..5e342e42e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,6 @@ message("Configuring zig version ${ZIG_VERSION}") set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") -set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp") set(ZIG_WORKAROUND_4799 off CACHE BOOL "workaround for https://github.com/ziglang/zig/issues/4799") set(ZIG_WORKAROUND_POLLY_SO off CACHE STRING "workaround for https://github.com/ziglang/zig/issues/4799") @@ -72,11 +71,6 @@ string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_INCLUDE_DIR_ESCAPED "${ZIG_LIBC_ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) -# Zig no longer has embedded LLD. This option is kept for package maintainers -# so that they don't have to update their scripts in case we ever re-introduce -# LLD to the tree. This option does nothing. -option(ZIG_FORCE_EXTERNAL_LLD "does nothing" OFF) - set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for") set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for") set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") @@ -101,7 +95,7 @@ if(APPLE AND ZIG_WORKAROUND_4799) list(APPEND LLVM_LIBRARIES "-Wl,${CMAKE_PREFIX_PATH}/lib/libPolly.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyPPCG.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyISL.a") endif() -set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp") +set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zigcpp") # Handle multi-config builds and place each into a common lib. The VS generator # for example will append a Debug folder by default if not explicitly specified. @@ -267,53 +261,45 @@ include_directories("${CMAKE_SOURCE_DIR}/deps/dbg-macro") find_package(Threads) -# CMake doesn't let us create an empty executable, so we hang on to this one separately. -set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp") +# This is our shim which will be replaced by stage1.zig. +set(ZIG0_SOURCES + "${CMAKE_SOURCE_DIR}/src/stage1/zig0.cpp" +) -# This is our shim which will be replaced by libstage2 written in Zig. -set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/stage2.cpp") - -if(ZIG_ENABLE_MEM_PROFILE) - set(ZIG_SOURCES_MEM_PROFILE "${CMAKE_SOURCE_DIR}/src/mem_profile.cpp") -endif() - -set(ZIG_SOURCES - "${CMAKE_SOURCE_DIR}/src/analyze.cpp" - "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" - "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp" - "${CMAKE_SOURCE_DIR}/src/bigint.cpp" - "${CMAKE_SOURCE_DIR}/src/buffer.cpp" - "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" - "${CMAKE_SOURCE_DIR}/src/codegen.cpp" - "${CMAKE_SOURCE_DIR}/src/compiler.cpp" - "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp" - "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" - "${CMAKE_SOURCE_DIR}/src/error.cpp" - "${CMAKE_SOURCE_DIR}/src/glibc.cpp" - "${CMAKE_SOURCE_DIR}/src/heap.cpp" - "${CMAKE_SOURCE_DIR}/src/ir.cpp" - "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" - "${CMAKE_SOURCE_DIR}/src/link.cpp" - "${CMAKE_SOURCE_DIR}/src/mem.cpp" - "${CMAKE_SOURCE_DIR}/src/os.cpp" - "${CMAKE_SOURCE_DIR}/src/parser.cpp" - "${CMAKE_SOURCE_DIR}/src/range_set.cpp" - "${CMAKE_SOURCE_DIR}/src/target.cpp" - "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" - "${CMAKE_SOURCE_DIR}/src/util.cpp" - "${CMAKE_SOURCE_DIR}/src/softfloat_ext.cpp" - "${ZIG_SOURCES_MEM_PROFILE}" +set(STAGE1_SOURCES + "${CMAKE_SOURCE_DIR}/src/stage1/analyze.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/ast_render.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/bigfloat.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/bigint.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/buffer.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/codegen.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/dump_analysis.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/errmsg.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/error.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/heap.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/ir.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/ir_print.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/mem.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/os.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/parser.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/range_set.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/stage1.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/target.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/tokenizer.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/util.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/softfloat_ext.cpp" ) set(OPTIMIZED_C_SOURCES - "${CMAKE_SOURCE_DIR}/src/blake2b.c" - "${CMAKE_SOURCE_DIR}/src/parse_f128.c" + "${CMAKE_SOURCE_DIR}/src/stage1/parse_f128.c" ) set(ZIG_CPP_SOURCES + # These are planned to stay even when we are self-hosted. "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp" + # https://github.com/ziglang/zig/issues/6363 "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) @@ -334,7 +320,7 @@ set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std") set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h") set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig") configure_file ( - "${CMAKE_SOURCE_DIR}/src/config.h.in" + "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" "${ZIG_CONFIG_H_OUT}" ) configure_file ( @@ -346,6 +332,7 @@ include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/src" + "${CMAKE_SOURCE_DIR}/src/stage1" ) # These have to go before the -Wno- flags @@ -411,18 +398,19 @@ if(ZIG_TEST_COVERAGE) set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") endif() -add_library(zig_cpp STATIC ${ZIG_CPP_SOURCES}) -set_target_properties(zig_cpp PROPERTIES +add_library(zigcpp STATIC ${ZIG_CPP_SOURCES}) +set_target_properties(zigcpp PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} ) -target_link_libraries(zig_cpp LINK_PUBLIC +target_link_libraries(zigcpp LINK_PUBLIC ${CLANG_LIBRARIES} ${LLD_LIBRARIES} ${LLVM_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} ) if(ZIG_WORKAROUND_POLLY_SO) - target_link_libraries(zig_cpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}") + target_link_libraries(zigcpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}") endif() add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) @@ -430,68 +418,67 @@ set_target_properties(opt_c_util PROPERTIES COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}" ) -add_library(zigcompiler STATIC ${ZIG_SOURCES}) -set_target_properties(zigcompiler PROPERTIES +add_library(zigstage1 STATIC ${STAGE1_SOURCES}) +set_target_properties(zigstage1 PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zigcompiler LINK_PUBLIC - zig_cpp +target_link_libraries(zigstage1 LINK_PUBLIC opt_c_util ${SOFTFLOAT_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} + zigcpp ) if(NOT MSVC) - target_link_libraries(zigcompiler LINK_PUBLIC ${LIBXML2}) + target_link_libraries(zigstage1 LINK_PUBLIC ${LIBXML2}) endif() if(ZIG_DIA_GUIDS_LIB) - target_link_libraries(zigcompiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) + target_link_libraries(zigstage1 LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) endif() if(MSVC OR MINGW) - target_link_libraries(zigcompiler LINK_PUBLIC version) + target_link_libraries(zigstage1 LINK_PUBLIC version) endif() -add_executable(zig0 "${ZIG_MAIN_SRC}" "${ZIG0_SHIM_SRC}") +add_executable(zig0 ${ZIG0_SOURCES}) set_target_properties(zig0 PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig0 zigcompiler) +target_link_libraries(zig0 zigstage1) if(MSVC) - set(LIBSTAGE2 "${CMAKE_BINARY_DIR}/zigstage2.lib") + set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj") else() - set(LIBSTAGE2 "${CMAKE_BINARY_DIR}/libzigstage2.a") + set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.o") endif() if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(LIBSTAGE2_RELEASE_ARG "") + set(ZIG1_RELEASE_ARG "") else() - set(LIBSTAGE2_RELEASE_ARG --release-fast --strip) + set(ZIG1_RELEASE_ARG -OReleaseFast --strip) endif() -set(BUILD_LIBSTAGE2_ARGS "build-lib" - "src-self-hosted/stage2.zig" +set(BUILD_ZIG1_ARGS + "src/stage1.zig" -target "${ZIG_TARGET_TRIPLE}" "-mcpu=${ZIG_TARGET_MCPU}" - --name zigstage2 + --name zig1 --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" - --cache on - --output-dir "${CMAKE_BINARY_DIR}" - ${LIBSTAGE2_RELEASE_ARG} - --bundle-compiler-rt - -fPIC + "-femit-bin=${ZIG1_OBJECT}" + "${ZIG1_RELEASE_ARG}" -lc --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end + --pkg-begin compiler_rt "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig" + --pkg-end ) if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") - add_custom_target(zig_build_libstage2 ALL - COMMAND zig0 ${BUILD_LIBSTAGE2_ARGS} + add_custom_target(zig_build_zig1 ALL + COMMAND zig0 ${BUILD_ZIG1_ARGS} DEPENDS zig0 - BYPRODUCTS "${LIBSTAGE2}" + BYPRODUCTS "${ZIG1_OBJECT}" + COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) set(ZIG_EXECUTABLE "${zig_BINARY_DIR}/zig") @@ -499,26 +486,28 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") set(ZIG_EXECUTABLE "${ZIG_EXECUTABLE}.exe") endif() else() - add_custom_target(zig_build_libstage2 ALL - COMMAND "${ZIG_EXECUTABLE}" ${BUILD_LIBSTAGE2_ARGS} - BYPRODUCTS "${LIBSTAGE2}" + add_custom_target(zig_build_zig1 ALL + COMMAND "${ZIG_EXECUTABLE}" ${BUILD_ZIG1_ARGS} + BYPRODUCTS "${ZIG1_OBJECT}" + COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) endif() -add_executable(zig "${ZIG_MAIN_SRC}") +# cmake won't let us configure an executable without C sources. +add_executable(zig "${CMAKE_SOURCE_DIR}/src/stage1/empty.cpp") set_target_properties(zig PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig zigcompiler "${LIBSTAGE2}") +target_link_libraries(zig "${ZIG1_OBJECT}" zigstage1) if(MSVC) target_link_libraries(zig ntdll.lib) elseif(MINGW) target_link_libraries(zig ntdll) endif() -add_dependencies(zig zig_build_libstage2) +add_dependencies(zig zig_build_zig1) install(TARGETS zig DESTINATION bin) diff --git a/README.md b/README.md index 2f0d65f584..ed31cbb1de 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Note that you can ### Stage 1: Build Zig from C++ Source Code +This step must be repeated when you make changes to any of the C++ source code. + #### Dependencies ##### POSIX @@ -82,6 +84,41 @@ in which case you can try `-DZIG_WORKAROUND_6087=ON`. See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows +### Stage 2: Build Self-Hosted Zig from Zig Source Code + +Now we use the stage1 binary: + +``` +zig build --prefix $(pwd)/stage2 -Denable-llvm +``` + +This produces `stage2/bin/zig` which can be used for testing and development. +Once it is feature complete, it will be used to build stage 3 - the final compiler +binary. + +### Stage 3: Rebuild Self-Hosted Zig Using the Self-Hosted Compiler + +*Note: Stage 2 compiler is not yet able to build Stage 3. Building Stage 3 is +not yet supported.* + +Once the self-hosted compiler can build itself, this will be the actual +compiler binary that we will install to the system. Until then, users should +use stage 1. + +#### Debug / Development Build + +``` +stage2/bin/zig build +``` + +This produces `zig-cache/bin/zig`. + +#### Release / Install Build + +``` +stage2/bin/zig build install -Drelease +``` + ## License The ultimate goal of the Zig project is to serve users. As a first-order diff --git a/build.zig b/build.zig index a6a2d87371..b4d8b71f79 100644 --- a/build.zig +++ b/build.zig @@ -9,6 +9,7 @@ const ArrayList = std.ArrayList; const io = std.io; const fs = std.fs; const InstallDirectoryOptions = std.build.InstallDirectoryOptions; +const assert = std.debug.assert; const zig_version = std.builtin.Version{ .major = 0, .minor = 6, .patch = 0 }; @@ -37,7 +38,7 @@ pub fn build(b: *Builder) !void { const test_step = b.step("test", "Run all the tests"); - var test_stage2 = b.addTest("src-self-hosted/test.zig"); + var test_stage2 = b.addTest("src/test.zig"); test_stage2.setBuildMode(mode); test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig"); @@ -55,70 +56,6 @@ pub fn build(b: *Builder) !void { const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false; const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h"); - if (!only_install_lib_files) { - var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); - exe.setBuildMode(mode); - exe.setTarget(target); - test_step.dependOn(&exe.step); - b.default_step.dependOn(&exe.step); - - if (enable_llvm) { - const config_h_text = if (config_h_path_option) |config_h_path| - try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes) - else - try findAndReadConfigH(b); - - var ctx = parseConfigH(b, config_h_text); - ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); - - try configureStage2(b, exe, ctx); - } - if (!only_install_lib_files) { - exe.install(); - } - const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); - const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; - if (link_libc) { - exe.linkLibC(); - test_stage2.linkLibC(); - } - - const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{}; - const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{}; - - const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); - const version = if (opt_version_string) |version| version else v: { - var code: u8 = undefined; - const version_untrimmed = b.execAllowFail(&[_][]const u8{ - "git", "-C", b.build_root, "name-rev", "HEAD", - "--tags", "--name-only", "--no-undefined", "--always", - }, &code, .Ignore) catch |err| { - std.debug.print( - \\Unable to determine zig version string: {} - \\Provide the zig version string explicitly using the `version-string` build option. - , .{err}); - std.process.exit(1); - }; - const trimmed = mem.trim(u8, version_untrimmed, " \n\r"); - break :v b.fmt("{}.{}.{}+{}", .{ zig_version.major, zig_version.minor, zig_version.patch, trimmed }); - }; - exe.addBuildOption([]const u8, "version", version); - - exe.addBuildOption([]const []const u8, "log_scopes", log_scopes); - exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps); - exe.addBuildOption(bool, "enable_tracy", tracy != null); - if (tracy) |tracy_path| { - const client_cpp = fs.path.join( - b.allocator, - &[_][]const u8{ tracy_path, "TracyClient.cpp" }, - ) catch unreachable; - exe.addIncludeDir(tracy_path); - exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); - exe.linkSystemLibraryName("c++"); - exe.linkLibC(); - } - } - b.installDirectory(InstallDirectoryOptions{ .source_dir = "lib", .install_dir = .Lib, @@ -133,6 +70,95 @@ pub fn build(b: *Builder) !void { }, }); + if (only_install_lib_files) + return; + + const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); + const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm; + + var exe = b.addExecutable("zig", "src/main.zig"); + exe.install(); + exe.setBuildMode(mode); + exe.setTarget(target); + test_step.dependOn(&exe.step); + b.default_step.dependOn(&exe.step); + + exe.addBuildOption(bool, "have_llvm", enable_llvm); + if (enable_llvm) { + const config_h_text = if (config_h_path_option) |config_h_path| + try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes) + else + try findAndReadConfigH(b); + + var ctx = parseConfigH(b, config_h_text); + ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); + + try configureStage2(b, exe, ctx, tracy != null); + } + if (link_libc) { + exe.linkLibC(); + test_stage2.linkLibC(); + } + + const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{}; + const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{}; + + const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); + const version = if (opt_version_string) |version| version else v: { + const version_string = b.fmt("{}.{}.{}", .{ zig_version.major, zig_version.minor, zig_version.patch }); + + var code: u8 = undefined; + const git_sha_untrimmed = b.execAllowFail(&[_][]const u8{ + "git", "-C", b.build_root, "name-rev", "HEAD", + "--tags", "--name-only", "--no-undefined", "--always", + }, &code, .Ignore) catch { + break :v version_string; + }; + const git_sha_trimmed = mem.trim(u8, git_sha_untrimmed, " \n\r"); + // Detect dirty changes. + const diff_untrimmed = b.execAllowFail(&[_][]const u8{ + "git", "-C", b.build_root, "diff", "HEAD", + }, &code, .Ignore) catch |err| { + std.debug.print("Error executing git diff: {}", .{err}); + std.process.exit(1); + }; + const trimmed_diff = mem.trim(u8, diff_untrimmed, " \n\r"); + const dirty_suffix = if (trimmed_diff.len == 0) "" else s: { + const dirty_hash = std.hash.Wyhash.hash(0, trimmed_diff); + break :s b.fmt("dirty{x}", .{@truncate(u32, dirty_hash)}); + }; + + // This will look like e.g. "0.6.0^0" for a tag commit. + if (mem.endsWith(u8, git_sha_trimmed, "^0")) { + const git_ver_string = git_sha_trimmed[0 .. git_sha_trimmed.len - 2]; + if (!mem.eql(u8, git_ver_string, version_string)) { + std.debug.print("Expected git tag '{}', found '{}'", .{ version_string, git_ver_string }); + std.process.exit(1); + } + break :v b.fmt("{}{}", .{ version_string, dirty_suffix }); + } else { + break :v b.fmt("{}+{}{}", .{ version_string, git_sha_trimmed, dirty_suffix }); + } + }; + exe.addBuildOption([]const u8, "version", version); + + exe.addBuildOption([]const []const u8, "log_scopes", log_scopes); + exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps); + exe.addBuildOption(bool, "enable_tracy", tracy != null); + exe.addBuildOption(bool, "is_stage1", false); + if (tracy) |tracy_path| { + const client_cpp = fs.path.join( + b.allocator, + &[_][]const u8{ tracy_path, "TracyClient.cpp" }, + ) catch unreachable; + exe.addIncludeDir(tracy_path); + exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); + if (!enable_llvm) { + exe.linkSystemLibraryName("c++"); + } + exe.linkLibC(); + } + const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false; @@ -140,10 +166,13 @@ pub fn build(b: *Builder) !void { const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); + test_stage2.addBuildOption(bool, "is_stage1", false); + test_stage2.addBuildOption(bool, "have_llvm", enable_llvm); test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir); + test_stage2.addBuildOption([]const u8, "version", version); const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); @@ -182,10 +211,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); - const test_cli = tests.addCliTests(b, test_filter, modes); - const test_cli_step = b.step("test-cli", "Run zig cli tests"); - test_cli_step.dependOn(test_cli); - test_step.dependOn(test_cli); + test_step.dependOn(tests.addCliTests(b, test_filter, modes)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); @@ -241,7 +267,7 @@ fn fileExists(filename: []const u8) !bool { fn addCppLib(b: *Builder, lib_exe_obj: anytype, cmake_binary_dir: []const u8, lib_name: []const u8) void { lib_exe_obj.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ cmake_binary_dir, - "zig_cpp", + "zigcpp", b.fmt("{}{}{}", .{ lib_exe_obj.target.libPrefix(), lib_name, lib_exe_obj.target.staticLibSuffix() }), }) catch unreachable); } @@ -320,21 +346,17 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { return result; } -fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void { +fn configureStage2(b: *Builder, exe: anytype, ctx: Context, need_cpp_includes: bool) !void { exe.addIncludeDir("src"); exe.addIncludeDir(ctx.cmake_binary_dir); - addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); - if (ctx.lld_include_dir.len != 0) { - exe.addIncludeDir(ctx.lld_include_dir); + addCppLib(b, exe, ctx.cmake_binary_dir, "zigcpp"); + assert(ctx.lld_include_dir.len != 0); + exe.addIncludeDir(ctx.lld_include_dir); + { var it = mem.tokenize(ctx.lld_libraries, ";"); while (it.next()) |lib| { exe.addObjectFile(lib); } - } else { - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm"); - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf"); - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff"); - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib"); } { var it = mem.tokenize(ctx.clang_libraries, ";"); @@ -344,42 +366,51 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void { } dependOnLib(b, exe, ctx.llvm); - if (exe.target.getOsTag() == .linux) { - // First we try to static link against gcc libstdc++. If that doesn't work, - // we fall back to -lc++ and cross our fingers. - addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) { - error.RequiredLibraryNotFound => { - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - }; + // Boy, it sure would be nice to simply linkSystemLibrary("c++") and rely on zig's + // ability to provide libc++ right? Well thanks to C++ not having a stable ABI this + // will cause linker errors. It would work in the situation when `zig cc` is used to + // build LLVM, Clang, and LLD, however when depending on them as system libraries, system + // libc++ must be used. + const cross_compile = false; // TODO + if (cross_compile) { + // In this case we assume that zig cc was used to build the LLVM, Clang, LLD dependencies. + exe.linkSystemLibrary("c++"); + } else { + if (exe.target.getOsTag() == .linux) { + // First we try to static link against gcc libstdc++. If that doesn't work, + // we fall back to -lc++ and cross our fingers. + addCxxKnownPath(b, ctx, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { + error.RequiredLibraryNotFound => { + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + }; - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isFreeBSD()) { - try addCxxKnownPath(b, ctx, exe, "libc++.a", null); - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isDarwin()) { - if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) { - // Compiler is GCC. - try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null); exe.linkSystemLibrary("pthread"); - // TODO LLD cannot perform this link. - // See https://github.com/ziglang/zig/issues/1535 - exe.enableSystemLinkerHack(); - } else |err| switch (err) { - error.RequiredLibraryNotFound => { - // System compiler, not gcc. - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, + } else if (exe.target.isFreeBSD()) { + try addCxxKnownPath(b, ctx, exe, "libc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isDarwin()) { + if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "", need_cpp_includes)) { + // Compiler is GCC. + try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + // TODO LLD cannot perform this link. + // See https://github.com/ziglang/zig/issues/1535 + exe.enableSystemLinkerHack(); + } else |err| switch (err) { + error.RequiredLibraryNotFound => { + // System compiler, not gcc. + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + } + } + + if (ctx.dia_guids_lib.len != 0) { + exe.addObjectFile(ctx.dia_guids_lib); } } - - if (ctx.dia_guids_lib.len != 0) { - exe.addObjectFile(ctx.dia_guids_lib); - } - - exe.linkSystemLibrary("c"); } fn addCxxKnownPath( @@ -388,6 +419,7 @@ fn addCxxKnownPath( exe: anytype, objname: []const u8, errtxt: ?[]const u8, + need_cpp_includes: bool, ) !void { const path_padded = try b.exec(&[_][]const u8{ ctx.cxx_compiler, @@ -403,6 +435,16 @@ fn addCxxKnownPath( return error.RequiredLibraryNotFound; } exe.addObjectFile(path_unpadded); + + // TODO a way to integrate with system c++ include files here + // cc -E -Wp,-v -xc++ /dev/null + if (need_cpp_includes) { + // I used these temporarily for testing something but we obviously need a + // more general purpose solution here. + //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0"); + //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0/x86_64-unknown-linux-gnu"); + //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0/backward"); + } } const Context = struct { diff --git a/ci/azure/linux_script b/ci/azure/linux_script index fb4caf18c0..99647ee063 100755 --- a/ci/azure/linux_script +++ b/ci/azure/linux_script @@ -28,22 +28,6 @@ PATH=$PWD/$WASMTIME:$PATH # This will affect the cmake command below. git config core.abbrev 9 -# This patch is a workaround for -# https://bugs.llvm.org/show_bug.cgi?id=44870 / https://github.com/llvm/llvm-project/issues/191 -# It only applies to the apt.llvm.org packages. -patch <<'END_PATCH' ---- CMakeLists.txt -+++ CMakeLists.txt -@@ -369,6 +369,7 @@ target_link_libraries(zig_cpp LINK_PUBLIC - ${CLANG_LIBRARIES} - ${LLD_LIBRARIES} - ${LLVM_LIBRARIES} -+ "-Wl,/usr/lib/llvm-10/lib/LLVMPolly.so" - ) - - add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) -END_PATCH - export CC=gcc-7 export CXX=g++-7 mkdir build diff --git a/ci/azure/windows_mingw_script b/ci/azure/windows_mingw_script index 7883766427..900baebee3 100644 --- a/ci/azure/windows_mingw_script +++ b/ci/azure/windows_mingw_script @@ -7,6 +7,13 @@ pacman --noconfirm --needed -S git base-devel mingw-w64-x86_64-toolchain mingw-w git config core.abbrev 9 +# Git is wrong for autocrlf being enabled by default on Windows. +# git is mangling files on Windows by default. +# This is the second bug I've tracked down to being caused by autocrlf. +git config core.autocrlf false +# Too late; the files are already mangled. +git checkout . + ZIGBUILDDIR="$(pwd)/build" PREFIX="$ZIGBUILDDIR/dist" CMAKEFLAGS="-DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_INSTALL_PREFIX=$PREFIX -DZIG_STATIC=ON" diff --git a/ci/srht/freebsd_script b/ci/srht/freebsd_script index 31aea6c3dc..3d9eb73735 100755 --- a/ci/srht/freebsd_script +++ b/ci/srht/freebsd_script @@ -21,6 +21,11 @@ cd $ZIGDIR # This will affect the cmake command below. git config core.abbrev 9 +# SourceHut reports that it is a terminal that supports escape codes, but it +# is a filthy liar. Here we tell Zig to not try to send any terminal escape +# codes to show progress. +export TERM=dumb + mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PREFIX "-DCMAKE_INSTALL_PREFIX=$(pwd)/release" -DZIG_STATIC=ON diff --git a/doc/docgen.zig b/doc/docgen.zig index af4d2530d0..50523d0948 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -4,7 +4,7 @@ const io = std.io; const fs = std.fs; const process = std.process; const ChildProcess = std.ChildProcess; -const warn = std.debug.warn; +const print = std.debug.print; const mem = std.mem; const testing = std.testing; @@ -215,23 +215,23 @@ const Tokenizer = struct { fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror { const loc = tokenizer.getTokenLocation(token); const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 }; - warn("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); + print("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); if (loc.line_start <= loc.line_end) { - warn("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); + print("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { - warn(" ", .{}); + print(" ", .{}); } } { const caret_count = token.end - token.start; var i: usize = 0; while (i < caret_count) : (i += 1) { - warn("~", .{}); + print("~", .{}); } } - warn("\n", .{}); + print("\n", .{}); } return error.ParseError; } @@ -274,6 +274,7 @@ const Code = struct { link_objects: []const []const u8, target_str: ?[]const u8, link_libc: bool, + disable_cache: bool, const Id = union(enum) { Test, @@ -522,6 +523,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { defer link_objects.deinit(); var target_str: ?[]const u8 = null; var link_libc = false; + var disable_cache = false; const source_token = while (true) { const content_tok = try eatToken(tokenizer, Token.Id.Content); @@ -532,6 +534,8 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { mode = .ReleaseFast; } else if (mem.eql(u8, end_tag_name, "code_release_safe")) { mode = .ReleaseSafe; + } else if (mem.eql(u8, end_tag_name, "code_disable_cache")) { + disable_cache = true; } else if (mem.eql(u8, end_tag_name, "code_link_object")) { _ = try eatToken(tokenizer, Token.Id.Separator); const obj_tok = try eatToken(tokenizer, Token.Id.TagContent); @@ -572,6 +576,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { .link_objects = link_objects.toOwnedSlice(), .target_str = target_str, .link_libc = link_libc, + .disable_cache = disable_cache, }, }); tokenizer.code_node_count += 1; @@ -1032,7 +1037,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }, .Code => |code| { code_progress_index += 1; - warn("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count }); + print("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count }); const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); @@ -1055,30 +1060,17 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); try build_args.appendSlice(&[_][]const u8{ - zig_exe, - "build-exe", - tmp_source_file_name, - "--name", - code.name, - "--color", - "on", - "--cache", - "on", + zig_exe, "build-exe", + "--name", code.name, + "--color", "on", + "--enable-cache", tmp_source_file_name, }); try out.print("
$ zig build-exe {}.zig", .{code.name});
                         switch (code.mode) {
                             .Debug => {},
-                            .ReleaseSafe => {
-                                try build_args.append("--release-safe");
-                                try out.print(" --release-safe", .{});
-                            },
-                            .ReleaseFast => {
-                                try build_args.append("--release-fast");
-                                try out.print(" --release-fast", .{});
-                            },
-                            .ReleaseSmall => {
-                                try build_args.append("--release-small");
-                                try out.print(" --release-small", .{});
+                            else => {
+                                try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+                                try out.print(" -O {s}", .{@tagName(code.mode)});
                             },
                         }
                         for (code.link_objects) |link_object| {
@@ -1087,9 +1079,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                                 allocator,
                                 &[_][]const u8{ tmp_dir_name, name_with_ext },
                             );
-                            try build_args.append("--object");
                             try build_args.append(full_path_object);
-                            try out.print(" --object {}", .{name_with_ext});
+                            try out.print(" {s}", .{name_with_ext});
                         }
                         if (code.link_libc) {
                             try build_args.append("-lc");
@@ -1114,20 +1105,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             switch (result.term) {
                                 .Exited => |exit_code| {
                                     if (exit_code == 0) {
-                                        warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
-                                        for (build_args.items) |arg|
-                                            warn("{} ", .{arg})
-                                        else
-                                            warn("\n", .{});
+                                        print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+                                        dumpArgs(build_args.items);
                                         return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
                                     }
                                 },
                                 else => {
-                                    warn("{}\nThe following command crashed:\n", .{result.stderr});
-                                    for (build_args.items) |arg|
-                                        warn("{} ", .{arg})
-                                    else
-                                        warn("\n", .{});
+                                    print("{}\nThe following command crashed:\n", .{result.stderr});
+                                    dumpArgs(build_args.items);
                                     return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                                 },
                             }
@@ -1174,11 +1159,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             switch (result.term) {
                                 .Exited => |exit_code| {
                                     if (exit_code == 0) {
-                                        warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
-                                        for (run_args) |arg|
-                                            warn("{} ", .{arg})
-                                        else
-                                            warn("\n", .{});
+                                        print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+                                        dumpArgs(run_args);
                                         return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
                                     }
                                 },
@@ -1206,27 +1188,13 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                         var test_args = std.ArrayList([]const u8).init(allocator);
                         defer test_args.deinit();
 
-                        try test_args.appendSlice(&[_][]const u8{
-                            zig_exe,
-                            "test",
-                            tmp_source_file_name,
-                            "--cache",
-                            "on",
-                        });
+                        try test_args.appendSlice(&[_][]const u8{ zig_exe, "test", tmp_source_file_name });
                         try out.print("
$ zig test {}.zig", .{code.name});
                         switch (code.mode) {
                             .Debug => {},
-                            .ReleaseSafe => {
-                                try test_args.append("--release-safe");
-                                try out.print(" --release-safe", .{});
-                            },
-                            .ReleaseFast => {
-                                try test_args.append("--release-fast");
-                                try out.print(" --release-fast", .{});
-                            },
-                            .ReleaseSmall => {
-                                try test_args.append("--release-small");
-                                try out.print(" --release-small", .{});
+                            else => {
+                                try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+                                try out.print(" -O {s}", .{@tagName(code.mode)});
                             },
                         }
                         if (code.link_libc) {
@@ -1252,23 +1220,13 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             "--color",
                             "on",
                             tmp_source_file_name,
-                            "--output-dir",
-                            tmp_dir_name,
                         });
                         try out.print("
$ zig test {}.zig", .{code.name});
                         switch (code.mode) {
                             .Debug => {},
-                            .ReleaseSafe => {
-                                try test_args.append("--release-safe");
-                                try out.print(" --release-safe", .{});
-                            },
-                            .ReleaseFast => {
-                                try test_args.append("--release-fast");
-                                try out.print(" --release-fast", .{});
-                            },
-                            .ReleaseSmall => {
-                                try test_args.append("--release-small");
-                                try out.print(" --release-small", .{});
+                            else => {
+                                try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+                                try out.print(" -O {s}", .{@tagName(code.mode)});
                             },
                         }
                         const result = try ChildProcess.exec(.{
@@ -1280,25 +1238,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                         switch (result.term) {
                             .Exited => |exit_code| {
                                 if (exit_code == 0) {
-                                    warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
-                                    for (test_args.items) |arg|
-                                        warn("{} ", .{arg})
-                                    else
-                                        warn("\n", .{});
+                                    print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+                                    dumpArgs(test_args.items);
                                     return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
                                 }
                             },
                             else => {
-                                warn("{}\nThe following command crashed:\n", .{result.stderr});
-                                for (test_args.items) |arg|
-                                    warn("{} ", .{arg})
-                                else
-                                    warn("\n", .{});
+                                print("{}\nThe following command crashed:\n", .{result.stderr});
+                                dumpArgs(test_args.items);
                                 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                             },
                         }
                         if (mem.indexOf(u8, result.stderr, error_match) == null) {
-                            warn("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
+                            print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
                             return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
                         }
                         const escaped_stderr = try escapeHtml(allocator, result.stderr);
@@ -1314,23 +1266,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             zig_exe,
                             "test",
                             tmp_source_file_name,
-                            "--output-dir",
-                            tmp_dir_name,
                         });
                         var mode_arg: []const u8 = "";
                         switch (code.mode) {
                             .Debug => {},
                             .ReleaseSafe => {
-                                try test_args.append("--release-safe");
-                                mode_arg = " --release-safe";
+                                try test_args.append("-OReleaseSafe");
+                                mode_arg = "-OReleaseSafe";
                             },
                             .ReleaseFast => {
-                                try test_args.append("--release-fast");
-                                mode_arg = " --release-fast";
+                                try test_args.append("-OReleaseFast");
+                                mode_arg = "-OReleaseFast";
                             },
                             .ReleaseSmall => {
-                                try test_args.append("--release-small");
-                                mode_arg = " --release-small";
+                                try test_args.append("-OReleaseSmall");
+                                mode_arg = "-OReleaseSmall";
                             },
                         }
 
@@ -1343,25 +1293,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                         switch (result.term) {
                             .Exited => |exit_code| {
                                 if (exit_code == 0) {
-                                    warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
-                                    for (test_args.items) |arg|
-                                        warn("{} ", .{arg})
-                                    else
-                                        warn("\n", .{});
+                                    print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+                                    dumpArgs(test_args.items);
                                     return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{});
                                 }
                             },
                             else => {
-                                warn("{}\nThe following command crashed:\n", .{result.stderr});
-                                for (test_args.items) |arg|
-                                    warn("{} ", .{arg})
-                                else
-                                    warn("\n", .{});
+                                print("{}\nThe following command crashed:\n", .{result.stderr});
+                                dumpArgs(test_args.items);
                                 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                             },
                         }
                         if (mem.indexOf(u8, result.stderr, error_match) == null) {
-                            warn("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
+                            print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
                             return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});
                         }
                         const escaped_stderr = try escapeHtml(allocator, result.stderr);
@@ -1395,32 +1339,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             "on",
                             "--name",
                             code.name,
-                            "--output-dir",
-                            tmp_dir_name,
+                            try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{
+                                tmp_dir_name, fs.path.sep, name_plus_obj_ext,
+                            }),
                         });
-
                         if (!code.is_inline) {
                             try out.print("
$ zig build-obj {}.zig", .{code.name});
                         }
 
                         switch (code.mode) {
                             .Debug => {},
-                            .ReleaseSafe => {
-                                try build_args.append("--release-safe");
+                            else => {
+                                try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
                                 if (!code.is_inline) {
-                                    try out.print(" --release-safe", .{});
-                                }
-                            },
-                            .ReleaseFast => {
-                                try build_args.append("--release-fast");
-                                if (!code.is_inline) {
-                                    try out.print(" --release-fast", .{});
-                                }
-                            },
-                            .ReleaseSmall => {
-                                try build_args.append("--release-small");
-                                if (!code.is_inline) {
-                                    try out.print(" --release-small", .{});
+                                    try out.print(" -O {s}", .{@tagName(code.mode)});
                                 }
                             },
                         }
@@ -1440,25 +1372,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             switch (result.term) {
                                 .Exited => |exit_code| {
                                     if (exit_code == 0) {
-                                        warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
-                                        for (build_args.items) |arg|
-                                            warn("{} ", .{arg})
-                                        else
-                                            warn("\n", .{});
+                                        print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+                                        dumpArgs(build_args.items);
                                         return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});
                                     }
                                 },
                                 else => {
-                                    warn("{}\nThe following command crashed:\n", .{result.stderr});
-                                    for (build_args.items) |arg|
-                                        warn("{} ", .{arg})
-                                    else
-                                        warn("\n", .{});
+                                    print("{}\nThe following command crashed:\n", .{result.stderr});
+                                    dumpArgs(build_args.items);
                                     return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                                 },
                             }
                             if (mem.indexOf(u8, result.stderr, error_match) == null) {
-                                warn("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
+                                print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
                                 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
                             }
                             const escaped_stderr = try escapeHtml(allocator, result.stderr);
@@ -1472,6 +1398,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                         }
                     },
                     Code.Id.Lib => {
+                        const bin_basename = try std.zig.binNameAlloc(allocator, .{
+                            .root_name = code.name,
+                            .target = std.Target.current,
+                            .output_mode = .Lib,
+                        });
+
                         var test_args = std.ArrayList([]const u8).init(allocator);
                         defer test_args.deinit();
 
@@ -1479,23 +1411,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             zig_exe,
                             "build-lib",
                             tmp_source_file_name,
-                            "--output-dir",
-                            tmp_dir_name,
+                            try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{
+                                tmp_dir_name, fs.path.sep_str, bin_basename,
+                            }),
                         });
                         try out.print("
$ zig build-lib {}.zig", .{code.name});
                         switch (code.mode) {
                             .Debug => {},
-                            .ReleaseSafe => {
-                                try test_args.append("--release-safe");
-                                try out.print(" --release-safe", .{});
-                            },
-                            .ReleaseFast => {
-                                try test_args.append("--release-fast");
-                                try out.print(" --release-fast", .{});
-                            },
-                            .ReleaseSmall => {
-                                try test_args.append("--release-small");
-                                try out.print(" --release-small", .{});
+                            else => {
+                                try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+                                try out.print(" -O {s}", .{@tagName(code.mode)});
                             },
                         }
                         if (code.target_str) |triple| {
@@ -1508,7 +1433,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                         try out.print("\n{}{}
\n", .{ escaped_stderr, escaped_stdout }); }, } - warn("OK\n", .{}); + print("OK\n", .{}); }, } } @@ -1524,20 +1449,14 @@ fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u switch (result.term) { .Exited => |exit_code| { if (exit_code != 0) { - warn("{}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code }); - for (args) |arg| - warn("{} ", .{arg}) - else - warn("\n", .{}); + print("{}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code }); + dumpArgs(args); return error.ChildExitError; } }, else => { - warn("{}\nThe following command crashed:\n", .{result.stderr}); - for (args) |arg| - warn("{} ", .{arg}) - else - warn("\n", .{}); + print("{}\nThe following command crashed:\n", .{result.stderr}); + dumpArgs(args); return error.ChildCrashed; }, } @@ -1545,9 +1464,13 @@ fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u } fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 { - const result = try exec(allocator, env_map, &[_][]const u8{ - zig_exe, - "builtin", - }); + const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" }); return result.stdout; } + +fn dumpArgs(args: []const []const u8) void { + for (args) |arg| + print("{} ", .{arg}) + else + print("\n", .{}); +} diff --git a/doc/langref.html.in b/doc/langref.html.in index 6b8b07e0b3..22faf7fd8f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1078,6 +1078,7 @@ const nan = std.math.nan(f128); but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:

{#code_begin|obj|foo#} {#code_release_fast#} + {#code_disable_cache#} const std = @import("std"); const builtin = std.builtin; const big = @as(f64, 1 << 40); @@ -9881,12 +9882,13 @@ The result is 3
const std = @import("std"); pub fn main() !void { - // TODO a better default allocator that isn't as wasteful! - const args = try std.process.argsAlloc(std.heap.page_allocator); - defer std.process.argsFree(std.heap.page_allocator, args); + var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; + const gpa = &general_purpose_allocator.allocator; + const args = try std.process.argsAlloc(gpa); + defer std.process.argsFree(gpa, args); for (args) |arg, i| { - std.debug.print("{}: {}\n", .{i, arg}); + std.debug.print("{}: {}\n", .{ i, arg }); } } {#code_end#} @@ -11385,8 +11387,9 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyframe / KEYWORD_anytype
  • Incremental improvements.
  • Avoid local maximums.
  • Reduce the amount one must remember.
  • -
  • Minimize energy spent on coding style.
  • -
  • Resource deallocation must succeed.
  • +
  • Focus on code rather than style.
  • +
  • Resource allocation may fail; resource deallocation must succeed.
  • +
  • Memory is a resource.
  • Together we serve the users.
  • {#header_close#} diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index f8c3623ef2..649c1e1055 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -112,12 +112,10 @@ pub fn ArrayHashMap( return self.unmanaged.clearAndFree(self.allocator); } - /// Deprecated. Use `items().len`. pub fn count(self: Self) usize { - return self.items().len; + return self.unmanaged.count(); } - /// Deprecated. Iterate using `items`. pub fn iterator(self: *const Self) Iterator { return Iterator{ .hm = self, @@ -332,6 +330,10 @@ pub fn ArrayHashMapUnmanaged( } } + pub fn count(self: Self) usize { + return self.entries.items.len; + } + /// If key exists this function cannot fail. /// If there is an existing item with `key`, then the result /// `Entry` pointer points to it, and found_existing is true. diff --git a/lib/std/build.zig b/lib/std/build.zig index c0d7f0b8ed..7e3c75bc78 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1384,6 +1384,7 @@ pub const LibExeObjStep = struct { } fn computeOutFileNames(self: *LibExeObjStep) void { + // TODO make this call std.zig.binNameAlloc switch (self.kind) { .Obj => { self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.oFileExt() }); @@ -1699,8 +1700,6 @@ pub const LibExeObjStep = struct { self.main_pkg_path = dir_path; } - pub const setDisableGenH = @compileError("deprecated; set the emit_h field directly"); - pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void { self.libc_file = libc_file; } @@ -1961,10 +1960,10 @@ pub const LibExeObjStep = struct { if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); + var prev_has_extra_flags = false; for (self.link_objects.span()) |link_object| { switch (link_object) { .StaticPath => |static_path| { - try zig_args.append("--object"); try zig_args.append(builder.pathFromRoot(static_path)); }, @@ -1972,12 +1971,10 @@ pub const LibExeObjStep = struct { .Exe => unreachable, .Test => unreachable, .Obj => { - try zig_args.append("--object"); try zig_args.append(other.getOutputPath()); }, .Lib => { if (!other.is_dynamic or self.target.isWindows()) { - try zig_args.append("--object"); try zig_args.append(other.getOutputLibPath()); } else { const full_path_lib = other.getOutputPath(); @@ -1996,13 +1993,26 @@ pub const LibExeObjStep = struct { try zig_args.append(name); }, .AssemblyFile => |asm_file| { - try zig_args.append("--c-source"); + if (prev_has_extra_flags) { + try zig_args.append("-extra-cflags"); + try zig_args.append("--"); + prev_has_extra_flags = false; + } try zig_args.append(asm_file.getPath(builder)); }, .CSourceFile => |c_source_file| { - try zig_args.append("--c-source"); - for (c_source_file.args) |arg| { - try zig_args.append(arg); + if (c_source_file.args.len == 0) { + if (prev_has_extra_flags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_extra_flags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_file.args) |arg| { + try zig_args.append(arg); + } + try zig_args.append("--"); } try zig_args.append(c_source_file.source.getPath(builder)); }, @@ -2078,10 +2088,8 @@ pub const LibExeObjStep = struct { } switch (self.build_mode) { - .Debug => {}, - .ReleaseSafe => zig_args.append("--release-safe") catch unreachable, - .ReleaseFast => zig_args.append("--release-fast") catch unreachable, - .ReleaseSmall => zig_args.append("--release-small") catch unreachable, + .Debug => {}, // Skip since it's the default. + else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable, } try zig_args.append("--cache-dir"); @@ -2092,14 +2100,8 @@ pub const LibExeObjStep = struct { if (self.kind == Kind.Lib and self.is_dynamic) { if (self.version) |version| { - zig_args.append("--ver-major") catch unreachable; - zig_args.append(builder.fmt("{}", .{version.major})) catch unreachable; - - zig_args.append("--ver-minor") catch unreachable; - zig_args.append(builder.fmt("{}", .{version.minor})) catch unreachable; - - zig_args.append("--ver-patch") catch unreachable; - zig_args.append(builder.fmt("{}", .{version.patch})) catch unreachable; + zig_args.append("--version") catch unreachable; + zig_args.append(builder.fmt("{}", .{version})) catch unreachable; } } if (self.is_dynamic) { @@ -2316,8 +2318,7 @@ pub const LibExeObjStep = struct { if (self.kind == Kind.Test) { try builder.spawnChild(zig_args.span()); } else { - try zig_args.append("--cache"); - try zig_args.append("on"); + try zig_args.append("--enable-cache"); const output_dir_nl = try builder.execFromStep(zig_args.span(), &self.step); const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig index 8ca2b87209..87e153066d 100644 --- a/lib/std/build/translate_c.zig +++ b/lib/std/build/translate_c.zig @@ -72,8 +72,7 @@ pub const TranslateCStep = struct { try argv_list.append("translate-c"); try argv_list.append("-lc"); - try argv_list.append("--cache"); - try argv_list.append("on"); + try argv_list.append("--enable-cache"); if (!self.target.isNative()) { try argv_list.append("-target"); diff --git a/lib/std/cache_hash.zig b/lib/std/cache_hash.zig deleted file mode 100644 index 5cd8194e21..0000000000 --- a/lib/std/cache_hash.zig +++ /dev/null @@ -1,726 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2015-2020 Zig Contributors -// This file is part of [zig](https://ziglang.org/), which is MIT licensed. -// The MIT license requires this copyright notice to be included in all copies -// and substantial portions of the software. -const std = @import("std.zig"); -const crypto = std.crypto; -const Hasher = crypto.auth.siphash.SipHash128(1, 3); // provides enough collision resistance for the CacheHash use cases, while being one of our fastest options right now -const fs = std.fs; -const base64 = std.base64; -const ArrayList = std.ArrayList; -const assert = std.debug.assert; -const testing = std.testing; -const mem = std.mem; -const fmt = std.fmt; -const Allocator = std.mem.Allocator; - -const base64_encoder = fs.base64_encoder; -const base64_decoder = fs.base64_decoder; -/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 -const BIN_DIGEST_LEN = 16; -const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); - -const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024; - -pub const File = struct { - path: ?[]const u8, - max_file_size: ?usize, - stat: fs.File.Stat, - bin_digest: [BIN_DIGEST_LEN]u8, - contents: ?[]const u8, - - pub fn deinit(self: *File, allocator: *Allocator) void { - if (self.path) |owned_slice| { - allocator.free(owned_slice); - self.path = null; - } - if (self.contents) |contents| { - allocator.free(contents); - self.contents = null; - } - self.* = undefined; - } -}; - -/// CacheHash manages project-local `zig-cache` directories. -/// This is not a general-purpose cache. -/// It was designed to be fast and simple, not to withstand attacks using specially-crafted input. -pub const CacheHash = struct { - allocator: *Allocator, - hasher_init: Hasher, // initial state, that can be copied - hasher: Hasher, // current state for incremental hashing - manifest_dir: fs.Dir, - manifest_file: ?fs.File, - manifest_dirty: bool, - files: ArrayList(File), - b64_digest: [BASE64_DIGEST_LEN]u8, - - /// Be sure to call release after successful initialization. - pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash { - const hasher_init = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length); - return CacheHash{ - .allocator = allocator, - .hasher_init = hasher_init, - .hasher = hasher_init, - .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), - .manifest_file = null, - .manifest_dirty = false, - .files = ArrayList(File).init(allocator), - .b64_digest = undefined, - }; - } - - /// Record a slice of bytes as an dependency of the process being cached - pub fn addSlice(self: *CacheHash, val: []const u8) void { - assert(self.manifest_file == null); - - self.hasher.update(val); - self.hasher.update(&[_]u8{0}); - } - - /// Convert the input value into bytes and record it as a dependency of the - /// process being cached - pub fn add(self: *CacheHash, val: anytype) void { - assert(self.manifest_file == null); - - const valPtr = switch (@typeInfo(@TypeOf(val))) { - .Int => &val, - .Pointer => val, - else => &val, - }; - - self.addSlice(mem.asBytes(valPtr)); - } - - /// Add a file as a dependency of process being cached. When `CacheHash.hit` is - /// called, the file's contents will be checked to ensure that it matches - /// the contents from previous times. - /// - /// Max file size will be used to determine the amount of space to the file contents - /// are allowed to take up in memory. If max_file_size is null, then the contents - /// will not be loaded into memory. - /// - /// Returns the index of the entry in the `CacheHash.files` ArrayList. You can use it - /// to access the contents of the file after calling `CacheHash.hit()` like so: - /// - /// ``` - /// var file_contents = cache_hash.files.items[file_index].contents.?; - /// ``` - pub fn addFile(self: *CacheHash, file_path: []const u8, max_file_size: ?usize) !usize { - assert(self.manifest_file == null); - - try self.files.ensureCapacity(self.files.items.len + 1); - const resolved_path = try fs.path.resolve(self.allocator, &[_][]const u8{file_path}); - - const idx = self.files.items.len; - self.files.addOneAssumeCapacity().* = .{ - .path = resolved_path, - .contents = null, - .max_file_size = max_file_size, - .stat = undefined, - .bin_digest = undefined, - }; - - self.addSlice(resolved_path); - - return idx; - } - - /// Check the cache to see if the input exists in it. If it exists, a base64 encoding - /// of it's hash will be returned; otherwise, null will be returned. - /// - /// This function will also acquire an exclusive lock to the manifest file. This means - /// that a process holding a CacheHash will block any other process attempting to - /// acquire the lock. - /// - /// The lock on the manifest file is released when `CacheHash.release` is called. - pub fn hit(self: *CacheHash) !?[BASE64_DIGEST_LEN]u8 { - assert(self.manifest_file == null); - - var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; - self.hasher.final(&bin_digest); - - base64_encoder.encode(self.b64_digest[0..], &bin_digest); - - self.hasher = self.hasher_init; - self.hasher.update(&bin_digest); - - const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); - defer self.allocator.free(manifest_file_path); - - if (self.files.items.len != 0) { - self.manifest_file = try self.manifest_dir.createFile(manifest_file_path, .{ - .read = true, - .truncate = false, - .lock = .Exclusive, - }); - } else { - // If there are no file inputs, we check if the manifest file exists instead of - // comparing the hashes on the files used for the cached item - self.manifest_file = self.manifest_dir.openFile(manifest_file_path, .{ - .read = true, - .write = true, - .lock = .Exclusive, - }) catch |err| switch (err) { - error.FileNotFound => { - self.manifest_dirty = true; - self.manifest_file = try self.manifest_dir.createFile(manifest_file_path, .{ - .read = true, - .truncate = false, - .lock = .Exclusive, - }); - return null; - }, - else => |e| return e, - }; - } - - const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.allocator, MANIFEST_FILE_SIZE_MAX); - defer self.allocator.free(file_contents); - - const input_file_count = self.files.items.len; - var any_file_changed = false; - var line_iter = mem.tokenize(file_contents, "\n"); - var idx: usize = 0; - while (line_iter.next()) |line| { - defer idx += 1; - - const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: { - const new = try self.files.addOne(); - new.* = .{ - .path = null, - .contents = null, - .max_file_size = null, - .stat = undefined, - .bin_digest = undefined, - }; - break :blk new; - }; - - var iter = mem.tokenize(line, " "); - const size = iter.next() orelse return error.InvalidFormat; - const inode = iter.next() orelse return error.InvalidFormat; - const mtime_nsec_str = iter.next() orelse return error.InvalidFormat; - const digest_str = iter.next() orelse return error.InvalidFormat; - const file_path = iter.rest(); - - cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; - cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; - cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; - base64_decoder.decode(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; - - if (file_path.len == 0) { - return error.InvalidFormat; - } - if (cache_hash_file.path) |p| { - if (!mem.eql(u8, file_path, p)) { - return error.InvalidFormat; - } - } - - if (cache_hash_file.path == null) { - cache_hash_file.path = try self.allocator.dupe(u8, file_path); - } - - const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch { - return error.CacheUnavailable; - }; - defer this_file.close(); - - const actual_stat = try this_file.stat(); - const size_match = actual_stat.size == cache_hash_file.stat.size; - const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime; - const inode_match = actual_stat.inode == cache_hash_file.stat.inode; - - if (!size_match or !mtime_match or !inode_match) { - self.manifest_dirty = true; - - cache_hash_file.stat = actual_stat; - - if (isProblematicTimestamp(cache_hash_file.stat.mtime)) { - cache_hash_file.stat.mtime = 0; - cache_hash_file.stat.inode = 0; - } - - var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; - try hashFile(this_file, &actual_digest, self.hasher_init); - - if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { - cache_hash_file.bin_digest = actual_digest; - // keep going until we have the input file digests - any_file_changed = true; - } - } - - if (!any_file_changed) { - self.hasher.update(&cache_hash_file.bin_digest); - } - } - - if (any_file_changed) { - // cache miss - // keep the manifest file open - // reset the hash - self.hasher = self.hasher_init; - self.hasher.update(&bin_digest); - - // Remove files not in the initial hash - for (self.files.items[input_file_count..]) |*file| { - file.deinit(self.allocator); - } - self.files.shrink(input_file_count); - - for (self.files.items) |file| { - self.hasher.update(&file.bin_digest); - } - return null; - } - - if (idx < input_file_count) { - self.manifest_dirty = true; - while (idx < input_file_count) : (idx += 1) { - const ch_file = &self.files.items[idx]; - try self.populateFileHash(ch_file); - } - return null; - } - - return self.final(); - } - - fn populateFileHash(self: *CacheHash, ch_file: *File) !void { - const file = try fs.cwd().openFile(ch_file.path.?, .{}); - defer file.close(); - - ch_file.stat = try file.stat(); - - if (isProblematicTimestamp(ch_file.stat.mtime)) { - ch_file.stat.mtime = 0; - ch_file.stat.inode = 0; - } - - if (ch_file.max_file_size) |max_file_size| { - if (ch_file.stat.size > max_file_size) { - return error.FileTooBig; - } - - const contents = try self.allocator.alloc(u8, @intCast(usize, ch_file.stat.size)); - errdefer self.allocator.free(contents); - - // Hash while reading from disk, to keep the contents in the cpu cache while - // doing hashing. - var hasher = self.hasher_init; - var off: usize = 0; - while (true) { - // give me everything you've got, captain - const bytes_read = try file.read(contents[off..]); - if (bytes_read == 0) break; - hasher.update(contents[off..][0..bytes_read]); - off += bytes_read; - } - hasher.final(&ch_file.bin_digest); - - ch_file.contents = contents; - } else { - try hashFile(file, &ch_file.bin_digest, self.hasher_init); - } - - self.hasher.update(&ch_file.bin_digest); - } - - /// Add a file as a dependency of process being cached, after the initial hash has been - /// calculated. This is useful for processes that don't know the all the files that - /// are depended on ahead of time. For example, a source file that can import other files - /// will need to be recompiled if the imported file is changed. - pub fn addFilePostFetch(self: *CacheHash, file_path: []const u8, max_file_size: usize) ![]u8 { - assert(self.manifest_file != null); - - const resolved_path = try fs.path.resolve(self.allocator, &[_][]const u8{file_path}); - errdefer self.allocator.free(resolved_path); - - const new_ch_file = try self.files.addOne(); - new_ch_file.* = .{ - .path = resolved_path, - .max_file_size = max_file_size, - .stat = undefined, - .bin_digest = undefined, - .contents = null, - }; - errdefer self.files.shrink(self.files.items.len - 1); - - try self.populateFileHash(new_ch_file); - - return new_ch_file.contents.?; - } - - /// Add a file as a dependency of process being cached, after the initial hash has been - /// calculated. This is useful for processes that don't know the all the files that - /// are depended on ahead of time. For example, a source file that can import other files - /// will need to be recompiled if the imported file is changed. - pub fn addFilePost(self: *CacheHash, file_path: []const u8) !void { - assert(self.manifest_file != null); - - const resolved_path = try fs.path.resolve(self.allocator, &[_][]const u8{file_path}); - errdefer self.allocator.free(resolved_path); - - const new_ch_file = try self.files.addOne(); - new_ch_file.* = .{ - .path = resolved_path, - .max_file_size = null, - .stat = undefined, - .bin_digest = undefined, - .contents = null, - }; - errdefer self.files.shrink(self.files.items.len - 1); - - try self.populateFileHash(new_ch_file); - } - - /// Returns a base64 encoded hash of the inputs. - pub fn final(self: *CacheHash) [BASE64_DIGEST_LEN]u8 { - assert(self.manifest_file != null); - - // We don't close the manifest file yet, because we want to - // keep it locked until the API user is done using it. - // We also don't write out the manifest yet, because until - // cache_release is called we still might be working on creating - // the artifacts to cache. - - var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; - self.hasher.final(&bin_digest); - - var out_digest: [BASE64_DIGEST_LEN]u8 = undefined; - base64_encoder.encode(&out_digest, &bin_digest); - - return out_digest; - } - - pub fn writeManifest(self: *CacheHash) !void { - assert(self.manifest_file != null); - - var encoded_digest: [BASE64_DIGEST_LEN]u8 = undefined; - var contents = ArrayList(u8).init(self.allocator); - var outStream = contents.outStream(); - defer contents.deinit(); - - for (self.files.items) |file| { - base64_encoder.encode(encoded_digest[0..], &file.bin_digest); - try outStream.print("{} {} {} {} {}\n", .{ file.stat.size, file.stat.inode, file.stat.mtime, encoded_digest[0..], file.path }); - } - - try self.manifest_file.?.pwriteAll(contents.items, 0); - self.manifest_dirty = false; - } - - /// Releases the manifest file and frees any memory the CacheHash was using. - /// `CacheHash.hit` must be called first. - /// - /// Will also attempt to write to the manifest file if the manifest is dirty. - /// Writing to the manifest file can fail, but this function ignores those errors. - /// To detect failures from writing the manifest, one may explicitly call - /// `writeManifest` before `release`. - pub fn release(self: *CacheHash) void { - if (self.manifest_file) |file| { - if (self.manifest_dirty) { - // To handle these errors, API users should call - // writeManifest before release(). - self.writeManifest() catch {}; - } - - file.close(); - } - - for (self.files.items) |*file| { - file.deinit(self.allocator); - } - self.files.deinit(); - self.manifest_dir.close(); - } -}; - -fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void { - var buf: [1024]u8 = undefined; - - var hasher = hasher_init; - while (true) { - const bytes_read = try file.read(&buf); - if (bytes_read == 0) break; - hasher.update(buf[0..bytes_read]); - } - - hasher.final(bin_digest); -} - -/// If the wall clock time, rounded to the same precision as the -/// mtime, is equal to the mtime, then we cannot rely on this mtime -/// yet. We will instead save an mtime value that indicates the hash -/// must be unconditionally computed. -/// This function recognizes the precision of mtime by looking at trailing -/// zero bits of the seconds and nanoseconds. -fn isProblematicTimestamp(fs_clock: i128) bool { - const wall_clock = std.time.nanoTimestamp(); - - // We have to break the nanoseconds into seconds and remainder nanoseconds - // to detect precision of seconds, because looking at the zero bits in base - // 2 would not detect precision of the seconds value. - const fs_sec = @intCast(i64, @divFloor(fs_clock, std.time.ns_per_s)); - const fs_nsec = @intCast(i64, @mod(fs_clock, std.time.ns_per_s)); - var wall_sec = @intCast(i64, @divFloor(wall_clock, std.time.ns_per_s)); - var wall_nsec = @intCast(i64, @mod(wall_clock, std.time.ns_per_s)); - - // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock. - if (fs_nsec == 0) { - wall_nsec = 0; - if (fs_sec == 0) { - wall_sec = 0; - } else { - wall_sec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_sec)); - } - } else { - wall_nsec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_nsec)); - } - return wall_nsec == fs_nsec and wall_sec == fs_sec; -} - -test "cache file and then recall it" { - if (std.Target.current.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - const cwd = fs.cwd(); - - const temp_file = "test.txt"; - const temp_manifest_dir = "temp_manifest_dir"; - - const ts = std.time.nanoTimestamp(); - try cwd.writeFile(temp_file, "Hello, world!\n"); - - while (isProblematicTimestamp(ts)) { - std.time.sleep(1); - } - - var digest1: [BASE64_DIGEST_LEN]u8 = undefined; - var digest2: [BASE64_DIGEST_LEN]u8 = undefined; - - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add(true); - ch.add(@as(u16, 1234)); - ch.add("1234"); - _ = try ch.addFile(temp_file, null); - - // There should be nothing in the cache - testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); - - digest1 = ch.final(); - } - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add(true); - ch.add(@as(u16, 1234)); - ch.add("1234"); - _ = try ch.addFile(temp_file, null); - - // Cache hit! We just "built" the same file - digest2 = (try ch.hit()).?; - } - - testing.expectEqual(digest1, digest2); - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteFile(temp_file); -} - -test "give problematic timestamp" { - var fs_clock = std.time.nanoTimestamp(); - // to make it problematic, we make it only accurate to the second - fs_clock = @divTrunc(fs_clock, std.time.ns_per_s); - fs_clock *= std.time.ns_per_s; - testing.expect(isProblematicTimestamp(fs_clock)); -} - -test "give nonproblematic timestamp" { - testing.expect(!isProblematicTimestamp(std.time.nanoTimestamp() - std.time.ns_per_s)); -} - -test "check that changing a file makes cache fail" { - if (std.Target.current.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - const cwd = fs.cwd(); - - const temp_file = "cache_hash_change_file_test.txt"; - const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; - const original_temp_file_contents = "Hello, world!\n"; - const updated_temp_file_contents = "Hello, world; but updated!\n"; - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteTree(temp_file); - - const ts = std.time.nanoTimestamp(); - try cwd.writeFile(temp_file, original_temp_file_contents); - - while (isProblematicTimestamp(ts)) { - std.time.sleep(1); - } - - var digest1: [BASE64_DIGEST_LEN]u8 = undefined; - var digest2: [BASE64_DIGEST_LEN]u8 = undefined; - - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - const temp_file_idx = try ch.addFile(temp_file, 100); - - // There should be nothing in the cache - testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); - - testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?)); - - digest1 = ch.final(); - } - - try cwd.writeFile(temp_file, updated_temp_file_contents); - - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - const temp_file_idx = try ch.addFile(temp_file, 100); - - // A file that we depend on has been updated, so the cache should not contain an entry for it - testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); - - // The cache system does not keep the contents of re-hashed input files. - testing.expect(ch.files.items[temp_file_idx].contents == null); - - digest2 = ch.final(); - } - - testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteTree(temp_file); -} - -test "no file inputs" { - if (std.Target.current.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - const cwd = fs.cwd(); - const temp_manifest_dir = "no_file_inputs_manifest_dir"; - defer cwd.deleteTree(temp_manifest_dir) catch unreachable; - - var digest1: [BASE64_DIGEST_LEN]u8 = undefined; - var digest2: [BASE64_DIGEST_LEN]u8 = undefined; - - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - - // There should be nothing in the cache - testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); - - digest1 = ch.final(); - } - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - - digest2 = (try ch.hit()).?; - } - - testing.expectEqual(digest1, digest2); -} - -test "CacheHashes with files added after initial hash work" { - if (std.Target.current.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - const cwd = fs.cwd(); - - const temp_file1 = "cache_hash_post_file_test1.txt"; - const temp_file2 = "cache_hash_post_file_test2.txt"; - const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; - - const ts1 = std.time.nanoTimestamp(); - try cwd.writeFile(temp_file1, "Hello, world!\n"); - try cwd.writeFile(temp_file2, "Hello world the second!\n"); - - while (isProblematicTimestamp(ts1)) { - std.time.sleep(1); - } - - var digest1: [BASE64_DIGEST_LEN]u8 = undefined; - var digest2: [BASE64_DIGEST_LEN]u8 = undefined; - var digest3: [BASE64_DIGEST_LEN]u8 = undefined; - - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - _ = try ch.addFile(temp_file1, null); - - // There should be nothing in the cache - testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); - - _ = try ch.addFilePost(temp_file2); - - digest1 = ch.final(); - } - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - _ = try ch.addFile(temp_file1, null); - - digest2 = (try ch.hit()).?; - } - testing.expect(mem.eql(u8, &digest1, &digest2)); - - // Modify the file added after initial hash - const ts2 = std.time.nanoTimestamp(); - try cwd.writeFile(temp_file2, "Hello world the second, updated\n"); - - while (isProblematicTimestamp(ts2)) { - std.time.sleep(1); - } - - { - var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); - defer ch.release(); - - ch.add("1234"); - _ = try ch.addFile(temp_file1, null); - - // A file that we depend on has been updated, so the cache should not contain an entry for it - testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); - - _ = try ch.addFilePost(temp_file2); - - digest3 = ch.final(); - } - - testing.expect(!mem.eql(u8, &digest1, &digest3)); - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteFile(temp_file1); - try cwd.deleteFile(temp_file2); -} diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 9219b05088..e706302ebd 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -213,7 +213,7 @@ pub const ChildProcess = struct { const stdout_in = child.stdout.?.inStream(); const stderr_in = child.stderr.?.inStream(); - // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). + // TODO https://github.com/ziglang/zig/issues/6343 const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes); errdefer args.allocator.free(stdout); const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes); @@ -816,6 +816,13 @@ fn destroyPipe(pipe: [2]os.fd_t) void { // Then the child exits. fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { writeIntFd(fd, @as(ErrInt, @errorToInt(err))) catch {}; + // If we're linking libc, some naughty applications may have registered atexit handlers + // which we really do not want to run in the fork child. I caught LLVM doing this and + // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne, + // "Why'd you have to go and make things so complicated?" + if (std.Target.current.os.tag == .linux) { + std.os.linux.exit(1); // By-pass libc regardless of whether it is linked. + } os.exit(1); } diff --git a/lib/std/log.zig b/lib/std/log.zig index 7b677f698a..0cc2b54452 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -101,14 +101,12 @@ pub const Level = enum { debug, }; -/// The default log level is based on build mode. Note that in ReleaseSmall -/// builds the default level is emerg but no messages will be stored/logged -/// by the default logger to save space. +/// The default log level is based on build mode. pub const default_level: Level = switch (builtin.mode) { .Debug => .debug, .ReleaseSafe => .notice, .ReleaseFast => .err, - .ReleaseSmall => .emerg, + .ReleaseSmall => .err, }; /// The current log level. This is set to root.log_level if present, otherwise @@ -131,11 +129,22 @@ fn log( // On freestanding one must provide a log function; we do not have // any I/O configured. return; - } else if (builtin.mode != .ReleaseSmall) { + } else { + const level_txt = switch (message_level) { + .emerg => "emergency", + .alert => "alert", + .crit => "critical", + .err => "error", + .warn => "warning", + .notice => "notice", + .info => "info", + .debug => "debug", + }; + const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; + const stderr = std.io.getStdErr().writer(); const held = std.debug.getStderrMutex().acquire(); defer held.release(); - const stderr = std.io.getStdErr().writer(); - nosuspend stderr.print(format ++ "\n", args) catch return; + nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; } } } diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 326a73b915..4511acb275 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -231,8 +231,6 @@ fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, compti /// call `free` when done. /// /// For allocating a single item, see `create`. -/// -/// Deprecated; use `allocWithOptions`. pub fn allocSentinel( self: *Allocator, comptime Elem: type, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index a308ee76fc..fab202bf79 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -761,6 +761,7 @@ pub const DeleteFileError = error{ FileNotFound, AccessDenied, NameTooLong, + /// Also known as sharing violation. FileBusy, Unexpected, NotDir, @@ -825,6 +826,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil .INVALID_PARAMETER => unreachable, .FILE_IS_A_DIRECTORY => return error.IsDir, .NOT_A_DIRECTORY => return error.NotDir, + .SHARING_VIOLATION => return error.FileBusy, else => return unexpectedStatus(rc), } } diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index 3ab74a11a2..43d6b96536 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -161,16 +161,16 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) else top_level_step.step.name; - try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description }); + try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description }); } try out_stream.writeAll( \\ \\General Options: - \\ --help Print this help and exit - \\ --verbose Print commands before executing them - \\ --prefix [path] Override default install prefix - \\ --search-prefix [path] Add a path to look for binaries, libraries, headers + \\ --help Print this help and exit + \\ --verbose Print commands before executing them + \\ --prefix [path] Override default install prefix + \\ --search-prefix [path] Add a path to look for binaries, libraries, headers \\ \\Project-Specific Options: \\ @@ -185,7 +185,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void Builder.typeIdName(option.type_id), }); defer allocator.free(name); - try out_stream.print("{s:24} {}\n", .{ name, option.description }); + try out_stream.print("{s:<29} {}\n", .{ name, option.description }); } } diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index b9452b79cc..14a35a3aaa 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -103,6 +103,6 @@ pub fn log( log_err_count += 1; } if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) { - std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args); + std.debug.print("[{}] ({}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args); } } diff --git a/lib/std/start.zig b/lib/std/start.zig index aea31a1531..71940b12ca 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -224,7 +224,7 @@ inline fn initEventLoopAndCallMain() u8 { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.log.err("{}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -270,7 +270,7 @@ pub fn callMain() u8 { }, .ErrorUnion => { const result = root.main() catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.log.err("{}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } diff --git a/lib/std/std.zig b/lib/std/std.zig index e21e428c77..62f7f21f4e 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -47,7 +47,6 @@ pub const base64 = @import("base64.zig"); pub const build = @import("build.zig"); pub const builtin = @import("builtin.zig"); pub const c = @import("c.zig"); -pub const cache_hash = @import("cache_hash.zig"); pub const coff = @import("coff.zig"); pub const compress = @import("compress.zig"); pub const crypto = @import("crypto.zig"); diff --git a/lib/std/target.zig b/lib/std/target.zig index 37425a9a29..99617c6d0e 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -75,6 +75,13 @@ pub const Target = struct { else => return ".so", } } + + pub fn defaultVersionRange(tag: Tag) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag), + }; + } }; /// Based on NTDDI version constants from @@ -290,11 +297,32 @@ pub const Target = struct { } }; - pub fn defaultVersionRange(tag: Tag) Os { - return .{ - .tag = tag, - .version_range = VersionRange.default(tag), - }; + pub const TaggedVersionRange = union(enum) { + none: void, + semver: Version.Range, + linux: LinuxVersionRange, + windows: WindowsVersion.Range, + }; + + /// Provides a tagged union. `Target` does not store the tag because it is + /// redundant with the OS tag; this function abstracts that part away. + pub fn getVersionRange(self: Os) TaggedVersionRange { + switch (self.tag) { + .linux => return TaggedVersionRange{ .linux = self.version_range.linux }, + .windows => return TaggedVersionRange{ .windows = self.version_range.windows }, + + .freebsd, + .macosx, + .ios, + .tvos, + .watchos, + .netbsd, + .openbsd, + .dragonfly, + => return TaggedVersionRange{ .semver = self.version_range.semver }, + + else => return .none, + } } /// Checks if system is guaranteed to be at least `version` or older than `version`. @@ -455,18 +483,9 @@ pub const Target = struct { else => false, }; } - - pub fn oFileExt(abi: Abi) [:0]const u8 { - return switch (abi) { - .msvc => ".obj", - else => ".o", - }; - } }; pub const ObjectFormat = enum { - /// TODO Get rid of this one. - unknown, coff, pe, elf, @@ -1116,8 +1135,18 @@ pub const Target = struct { return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); } + pub fn oFileExt_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { + return ".o.wasm"; + } + switch (abi) { + .msvc => return ".obj", + else => return ".o", + } + } + pub fn oFileExt(self: Target) [:0]const u8 { - return self.abi.oFileExt(); + return oFileExt_cpu_arch_abi(self.cpu.arch, self.abi); } pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 { @@ -1457,6 +1486,27 @@ pub const Target = struct { => return result, } } + + /// Return whether or not the given host target is capable of executing natively executables + /// of the other target. + pub fn canExecBinariesOf(host_target: Target, binary_target: Target) bool { + if (host_target.os.tag != binary_target.os.tag) + return false; + + if (host_target.cpu.arch == binary_target.cpu.arch) + return true; + + if (host_target.cpu.arch == .x86_64 and binary_target.cpu.arch == .i386) + return true; + + if (host_target.cpu.arch == .aarch64 and binary_target.cpu.arch == .arm) + return true; + + if (host_target.cpu.arch == .aarch64_be and binary_target.cpu.arch == .armeb) + return true; + + return false; + } }; test "" { diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 658d31bb82..ccbd1d6324 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -38,7 +38,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void { /// This function is intended to be used only in tests. When the two values are not /// equal, prints diagnostics to stderr to show exactly how they are not equal, /// then aborts. -/// The types must match exactly. +/// `actual` is casted to the type of `expected`. pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { switch (@typeInfo(@TypeOf(actual))) { .NoReturn, diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 1dedce4067..cc1815e8e5 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -64,24 +64,84 @@ pub fn lineDelta(source: []const u8, start: usize, end: usize) isize { return line; } -/// Returns the standard file system basename of a binary generated by the Zig compiler. -pub fn binNameAlloc( - allocator: *std.mem.Allocator, +pub const BinNameOptions = struct { root_name: []const u8, target: std.Target, output_mode: std.builtin.OutputMode, - link_mode: ?std.builtin.LinkMode, -) error{OutOfMemory}![]u8 { - switch (output_mode) { - .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }), - .Lib => { - const suffix = switch (link_mode orelse .Static) { - .Static => target.staticLibSuffix(), - .Dynamic => target.dynamicLibSuffix(), - }; - return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); + link_mode: ?std.builtin.LinkMode = null, + object_format: ?std.Target.ObjectFormat = null, + version: ?std.builtin.Version = null, +}; + +/// Returns the standard file system basename of a binary generated by the Zig compiler. +pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { + const root_name = options.root_name; + const target = options.target; + switch (options.object_format orelse target.getObjectFormat()) { + .coff, .pe => switch (options.output_mode) { + .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }), + .Lib => { + const suffix = switch (options.link_mode orelse .Static) { + .Static => ".lib", + .Dynamic => ".dll", + }; + return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix }); + }, + .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), }, - .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }), + .elf => switch (options.output_mode) { + .Exe => return allocator.dupe(u8, root_name), + .Lib => { + switch (options.link_mode orelse .Static) { + .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ + target.libPrefix(), root_name, + }), + .Dynamic => { + if (options.version) |ver| { + return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{ + target.libPrefix(), root_name, ver.major, ver.minor, ver.patch, + }); + } else { + return std.fmt.allocPrint(allocator, "{s}{s}.so", .{ + target.libPrefix(), root_name, + }); + } + }, + } + }, + .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), + }, + .macho => switch (options.output_mode) { + .Exe => return allocator.dupe(u8, root_name), + .Lib => { + switch (options.link_mode orelse .Static) { + .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ + target.libPrefix(), root_name, + }), + .Dynamic => { + if (options.version) |ver| { + return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{ + target.libPrefix(), root_name, ver.major, ver.minor, ver.patch, + }); + } else { + return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{ + target.libPrefix(), root_name, + }); + } + }, + } + return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix }); + }, + .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), + }, + .wasm => switch (options.output_mode) { + .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }), + .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), + .Lib => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}), + }, + .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}), + .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), + .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), } } diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 75fc5969a5..f1ae3457a5 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -375,7 +375,7 @@ pub const CrossTarget = struct { // `Target.current.os` works when doing `zig build` because Zig generates a build executable using // native OS version range. However this will not be accurate otherwise, and // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. - var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os; + var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange() else Target.current.os; if (self.os_version_min) |min| switch (min) { .none => {}, @@ -466,7 +466,7 @@ pub const CrossTarget = struct { } pub fn oFileExt(self: CrossTarget) [:0]const u8 { - return self.getAbi().oFileExt(); + return Target.oFileExt_cpu_arch_abi(self.getCpuArch(), self.getAbi()); } pub fn exeFileExt(self: CrossTarget) [:0]const u8 { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 37eea36d47..d13d0b22ef 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -203,7 +203,7 @@ pub const NativeTargetInfo = struct { /// deinitialization method. /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { - var os = Target.Os.defaultVersionRange(cross_target.getOsTag()); + var os = cross_target.getOsTag().defaultVersionRange(); if (cross_target.os_tag == null) { switch (Target.current.os.tag) { .linux => { @@ -393,6 +393,12 @@ pub const NativeTargetInfo = struct { if (!native_target_has_ld or have_all_info or os_is_non_native) { return defaultAbiAndDynamicLinker(cpu, os, cross_target); } + if (cross_target.abi) |abi| { + if (abi.isMusl()) { + // musl implies static linking. + return defaultAbiAndDynamicLinker(cpu, os, cross_target); + } + } // The current target's ABI cannot be relied on for this. For example, we may build the zig // compiler for target riscv64-linux-musl and provide a tarball for users to download. // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined diff --git a/src-self-hosted/Package.zig b/src-self-hosted/Package.zig deleted file mode 100644 index 4bf4defbc8..0000000000 --- a/src-self-hosted/Package.zig +++ /dev/null @@ -1,59 +0,0 @@ -pub const Table = std.StringHashMap(*Package); - -/// This should be used for file operations. -root_src_dir: std.fs.Dir, -/// This is for metadata purposes, for example putting into debug information. -root_src_dir_path: []u8, -/// Relative to `root_src_dir` and `root_src_dir_path`. -root_src_path: []u8, -table: Table, - -/// No references to `root_src_dir` and `root_src_path` are kept. -pub fn create( - allocator: *mem.Allocator, - base_dir: std.fs.Dir, - /// Relative to `base_dir`. - root_src_dir: []const u8, - /// Relative to `root_src_dir`. - root_src_path: []const u8, -) !*Package { - const ptr = try allocator.create(Package); - errdefer allocator.destroy(ptr); - const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path); - errdefer allocator.free(root_src_path_dupe); - const root_src_dir_path = try mem.dupe(allocator, u8, root_src_dir); - errdefer allocator.free(root_src_dir_path); - ptr.* = .{ - .root_src_dir = try base_dir.openDir(root_src_dir, .{}), - .root_src_dir_path = root_src_dir_path, - .root_src_path = root_src_path_dupe, - .table = Table.init(allocator), - }; - return ptr; -} - -pub fn destroy(self: *Package) void { - const allocator = self.table.allocator; - self.root_src_dir.close(); - allocator.free(self.root_src_path); - allocator.free(self.root_src_dir_path); - { - var it = self.table.iterator(); - while (it.next()) |kv| { - allocator.free(kv.key); - } - } - self.table.deinit(); - allocator.destroy(self); -} - -pub fn add(self: *Package, name: []const u8, package: *Package) !void { - try self.table.ensureCapacity(self.table.items().len + 1); - const name_dupe = try mem.dupe(self.table.allocator, u8, name); - self.table.putAssumeCapacityNoClobber(name_dupe, package); -} - -const std = @import("std"); -const mem = std.mem; -const assert = std.debug.assert; -const Package = @This(); diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig deleted file mode 100644 index 80f10c8656..0000000000 --- a/src-self-hosted/introspect.zig +++ /dev/null @@ -1,138 +0,0 @@ -//! Introspection and determination of system libraries needed by zig. - -const std = @import("std"); -const mem = std.mem; -const fs = std.fs; -const CacheHash = std.cache_hash.CacheHash; - -/// Caller must free result -pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { - { - const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" }); - errdefer allocator.free(test_zig_dir); - - const test_index_file = try fs.path.join(allocator, &[_][]const u8{ test_zig_dir, "std", "std.zig" }); - defer allocator.free(test_index_file); - - if (fs.cwd().openFile(test_index_file, .{})) |file| { - file.close(); - return test_zig_dir; - } else |err| switch (err) { - error.FileNotFound => { - allocator.free(test_zig_dir); - }, - else => |e| return e, - } - } - - // Also try without "zig" - const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib" }); - errdefer allocator.free(test_zig_dir); - - const test_index_file = try fs.path.join(allocator, &[_][]const u8{ test_zig_dir, "std", "std.zig" }); - defer allocator.free(test_index_file); - - const file = try fs.cwd().openFile(test_index_file, .{}); - file.close(); - - return test_zig_dir; -} - -/// Caller must free result -pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 { - const self_exe_path = try fs.selfExePathAlloc(allocator); - defer allocator.free(self_exe_path); - - var cur_path: []const u8 = self_exe_path; - while (true) { - const test_dir = fs.path.dirname(cur_path) orelse "."; - - if (mem.eql(u8, test_dir, cur_path)) { - break; - } - - return testZigInstallPrefix(allocator, test_dir) catch |err| { - cur_path = test_dir; - continue; - }; - } - - return error.FileNotFound; -} - -pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { - return findZigLibDir(allocator) catch |err| { - std.debug.print( - \\Unable to find zig lib directory: {}. - \\Reinstall Zig or use --zig-install-prefix. - \\ - , .{@errorName(err)}); - - return error.ZigLibDirNotFound; - }; -} - -/// Caller owns returned memory. -pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 { - const appname = "zig"; - - if (std.Target.current.os.tag != .windows) { - if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| { - return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); - } else if (std.os.getenv("HOME")) |home| { - return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); - } - } - - return fs.getAppDataDir(allocator, appname); -} - -pub fn openGlobalCacheDir() !fs.Dir { - var buf: [fs.MAX_PATH_BYTES]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&buf); - const path_name = try resolveGlobalCacheDir(&fba.allocator); - return fs.cwd().makeOpenPath(path_name, .{}); -} - -var compiler_id_mutex = std.Mutex{}; -var compiler_id: [16]u8 = undefined; -var compiler_id_computed = false; - -pub fn resolveCompilerId(gpa: *mem.Allocator) ![16]u8 { - const held = compiler_id_mutex.acquire(); - defer held.release(); - - if (compiler_id_computed) - return compiler_id; - compiler_id_computed = true; - - var cache_dir = try openGlobalCacheDir(); - defer cache_dir.close(); - - var ch = try CacheHash.init(gpa, cache_dir, "exe"); - defer ch.release(); - - const self_exe_path = try fs.selfExePathAlloc(gpa); - defer gpa.free(self_exe_path); - - _ = try ch.addFile(self_exe_path, null); - - if (try ch.hit()) |digest| { - compiler_id = digest[0..16].*; - return compiler_id; - } - - const libs = try std.process.getSelfExeSharedLibPaths(gpa); - defer { - for (libs) |lib| gpa.free(lib); - gpa.free(libs); - } - - for (libs) |lib| { - try ch.addFilePost(lib); - } - - const digest = ch.final(); - compiler_id = digest[0..16].*; - return compiler_id; -} diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig deleted file mode 100644 index fff69a6bbd..0000000000 --- a/src-self-hosted/link.zig +++ /dev/null @@ -1,279 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const Module = @import("Module.zig"); -const fs = std.fs; -const trace = @import("tracy.zig").trace; -const Package = @import("Package.zig"); -const Type = @import("type.zig").Type; -const build_options = @import("build_options"); - -pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; - -pub const Options = struct { - target: std.Target, - output_mode: std.builtin.OutputMode, - link_mode: std.builtin.LinkMode, - object_format: std.builtin.ObjectFormat, - optimize_mode: std.builtin.Mode, - root_name: []const u8, - root_pkg: *const Package, - /// Used for calculating how much space to reserve for symbols in case the binary file - /// does not already have a symbol table. - symbol_count_hint: u64 = 32, - /// Used for calculating how much space to reserve for executable program code in case - /// the binary file deos not already have such a section. - program_code_size_hint: u64 = 256 * 1024, - entry_addr: ?u64 = null, -}; - -pub const File = struct { - tag: Tag, - options: Options, - file: ?fs.File, - allocator: *Allocator, - - pub const LinkBlock = union { - elf: Elf.TextBlock, - coff: Coff.TextBlock, - macho: MachO.TextBlock, - c: void, - wasm: void, - }; - - pub const LinkFn = union { - elf: Elf.SrcFn, - coff: Coff.SrcFn, - macho: MachO.SrcFn, - c: void, - wasm: ?Wasm.FnData, - }; - - /// For DWARF .debug_info. - pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage); - - /// For DWARF .debug_info. - pub const DbgInfoTypeReloc = struct { - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// This is where the .debug_info tag for the type is. - off: u32, - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// List of DW.AT_type / DW.FORM_ref4 that points to the type. - relocs: std.ArrayListUnmanaged(u32), - }; - - /// Attempts incremental linking, if the file already exists. If - /// incremental linking fails, falls back to truncating the file and - /// rewriting it. A malicious file is detected as incremental link failure - /// and does not cause Illegal Behavior. This operation is not atomic. - pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { - switch (options.object_format) { - .unknown => unreachable, - .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options), - .elf => return Elf.openPath(allocator, dir, sub_path, options), - .macho => return MachO.openPath(allocator, dir, sub_path, options), - .wasm => return Wasm.openPath(allocator, dir, sub_path, options), - .c => return C.openPath(allocator, dir, sub_path, options), - .hex => return error.TODOImplementHex, - .raw => return error.TODOImplementRaw, - } - } - - pub fn cast(base: *File, comptime T: type) ?*T { - if (base.tag != T.base_tag) - return null; - - return @fieldParentPtr(T, "base", base); - } - - pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void { - switch (base.tag) { - .coff, .elf, .macho => { - if (base.file != null) return; - base.file = try dir.createFile(sub_path, .{ - .truncate = false, - .read = true, - .mode = determineMode(base.options), - }); - }, - .c, .wasm => {}, - } - } - - pub fn makeExecutable(base: *File) !void { - switch (base.tag) { - .c => unreachable, - .wasm => {}, - else => if (base.file) |f| { - f.close(); - base.file = null; - }, - } - } - - /// May be called before or after updateDeclExports but must be called - /// after allocateDeclIndexes for any given Decl. - pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void { - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl), - .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), - .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), - .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), - } - } - - pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void { - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), - .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), - .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), - .c, .wasm => {}, - } - } - - /// Must be called before any call to updateDecl or updateDeclExports for - /// any given Decl. - pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void { - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), - .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), - .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), - .c, .wasm => {}, - } - } - - pub fn deinit(base: *File) void { - if (base.file) |f| f.close(); - switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).deinit(), - .elf => @fieldParentPtr(Elf, "base", base).deinit(), - .macho => @fieldParentPtr(MachO, "base", base).deinit(), - .c => @fieldParentPtr(C, "base", base).deinit(), - .wasm => @fieldParentPtr(Wasm, "base", base).deinit(), - } - } - - pub fn destroy(base: *File) void { - switch (base.tag) { - .coff => { - const parent = @fieldParentPtr(Coff, "base", base); - parent.deinit(); - base.allocator.destroy(parent); - }, - .elf => { - const parent = @fieldParentPtr(Elf, "base", base); - parent.deinit(); - base.allocator.destroy(parent); - }, - .macho => { - const parent = @fieldParentPtr(MachO, "base", base); - parent.deinit(); - base.allocator.destroy(parent); - }, - .c => { - const parent = @fieldParentPtr(C, "base", base); - parent.deinit(); - base.allocator.destroy(parent); - }, - .wasm => { - const parent = @fieldParentPtr(Wasm, "base", base); - parent.deinit(); - base.allocator.destroy(parent); - }, - } - } - - pub fn flush(base: *File, module: *Module) !void { - const tracy = trace(@src()); - defer tracy.end(); - - try switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).flush(module), - .elf => @fieldParentPtr(Elf, "base", base).flush(module), - .macho => @fieldParentPtr(MachO, "base", base).flush(module), - .c => @fieldParentPtr(C, "base", base).flush(module), - .wasm => @fieldParentPtr(Wasm, "base", base).flush(module), - }; - } - - pub fn freeDecl(base: *File, decl: *Module.Decl) void { - switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl), - .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), - .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), - .c => unreachable, - .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), - } - } - - pub fn errorFlags(base: *File) ErrorFlags { - return switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).error_flags, - .elf => @fieldParentPtr(Elf, "base", base).error_flags, - .macho => @fieldParentPtr(MachO, "base", base).error_flags, - .c => return .{ .no_entry_point_found = false }, - .wasm => return ErrorFlags{}, - }; - } - - /// May be called before or after updateDecl, but must be called after - /// allocateDeclIndexes for any given Decl. - pub fn updateDeclExports( - base: *File, - module: *Module, - decl: *const Module.Decl, - exports: []const *Module.Export, - ) !void { - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports), - .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports), - .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), - .c => return {}, - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), - } - } - - pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 { - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), - .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), - .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), - .c => unreachable, - .wasm => unreachable, - } - } - - pub const Tag = enum { - coff, - elf, - macho, - c, - wasm, - }; - - pub const ErrorFlags = struct { - no_entry_point_found: bool = false, - }; - - pub const C = @import("link/C.zig"); - pub const Coff = @import("link/Coff.zig"); - pub const Elf = @import("link/Elf.zig"); - pub const MachO = @import("link/MachO.zig"); - pub const Wasm = @import("link/Wasm.zig"); -}; - -pub fn determineMode(options: Options) fs.File.Mode { - // On common systems with a 0o022 umask, 0o777 will still result in a file created - // with 0o755 permissions, but it works appropriately if the system is configured - // more leniently. As another data point, C's fopen seems to open files with the - // 666 mode. - const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; - switch (options.output_mode) { - .Lib => return switch (options.link_mode) { - .Dynamic => executable_mode, - .Static => fs.File.default_mode, - }, - .Exe => return executable_mode, - .Obj => return fs.File.default_mode, - } -} diff --git a/src-self-hosted/link/Coff.zig b/src-self-hosted/link/Coff.zig deleted file mode 100644 index 4d1f95e567..0000000000 --- a/src-self-hosted/link/Coff.zig +++ /dev/null @@ -1,792 +0,0 @@ -const Coff = @This(); - -const std = @import("std"); -const log = std.log.scoped(.link); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const fs = std.fs; - -const trace = @import("../tracy.zig").trace; -const Module = @import("../Module.zig"); -const codegen = @import("../codegen.zig"); -const link = @import("../link.zig"); - -const allocation_padding = 4 / 3; -const minimum_text_block_size = 64 * allocation_padding; - -const section_alignment = 4096; -const file_alignment = 512; -const image_base = 0x400_000; -const section_table_size = 2 * 40; -comptime { - std.debug.assert(std.mem.isAligned(image_base, section_alignment)); -} - -pub const base_tag: link.File.Tag = .coff; - -const msdos_stub = @embedFile("msdos-stub.bin"); - -base: link.File, -ptr_width: enum { p32, p64 }, -error_flags: link.File.ErrorFlags = .{}, - -text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, -last_text_block: ?*TextBlock = null, - -/// Section table file pointer. -section_table_offset: u32 = 0, -/// Section data file pointer. -section_data_offset: u32 = 0, -/// Optiona header file pointer. -optional_header_offset: u32 = 0, - -/// Absolute virtual address of the offset table when the executable is loaded in memory. -offset_table_virtual_address: u32 = 0, -/// Current size of the offset table on disk, must be a multiple of `file_alignment` -offset_table_size: u32 = 0, -/// Contains absolute virtual addresses -offset_table: std.ArrayListUnmanaged(u64) = .{}, -/// Free list of offset table indices -offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, - -/// Virtual address of the entry point procedure relative to `image_base` -entry_addr: ?u32 = null, - -/// Absolute virtual address of the text section when the executable is loaded in memory. -text_section_virtual_address: u32 = 0, -/// Current size of the `.text` section on disk, must be a multiple of `file_alignment` -text_section_size: u32 = 0, - -offset_table_size_dirty: bool = false, -text_section_size_dirty: bool = false, -/// This flag is set when the virtual size of the whole image file when loaded in memory has changed -/// and needs to be updated in the optional header. -size_of_image_dirty: bool = false, - -pub const TextBlock = struct { - /// Offset of the code relative to the start of the text section - text_offset: u32, - /// Used size of the text block - size: u32, - /// This field is undefined for symbols with size = 0. - offset_table_index: u32, - /// Points to the previous and next neighbors, based on the `text_offset`. - /// This can be used to find, for example, the capacity of this `TextBlock`. - prev: ?*TextBlock, - next: ?*TextBlock, - - pub const empty = TextBlock{ - .text_offset = 0, - .size = 0, - .offset_table_index = undefined, - .prev = null, - .next = null, - }; - - /// Returns how much room there is to grow in virtual address space. - fn capacity(self: TextBlock) u64 { - if (self.next) |next| { - return next.text_offset - self.text_offset; - } - // This is the last block, the capacity is only limited by the address space. - return std.math.maxInt(u32) - self.text_offset; - } - - fn freeListEligible(self: TextBlock) bool { - // No need to keep a free list node for the last block. - const next = self.next orelse return false; - const cap = next.text_offset - self.text_offset; - const ideal_cap = self.size * allocation_padding; - if (cap <= ideal_cap) return false; - const surplus = cap - ideal_cap; - return surplus >= minimum_text_block_size; - } - - /// Absolute virtual address of the text block when the file is loaded in memory. - fn getVAddr(self: TextBlock, coff: Coff) u32 { - return coff.text_section_virtual_address + self.text_offset; - } -}; - -pub const SrcFn = void; - -pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { - assert(options.object_format == .coff); - - const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); - errdefer file.close(); - - var coff_file = try allocator.create(Coff); - errdefer allocator.destroy(coff_file); - - coff_file.* = openFile(allocator, file, options) catch |err| switch (err) { - error.IncrFailed => try createFile(allocator, file, options), - else => |e| return e, - }; - - return &coff_file.base; -} - -/// Returns error.IncrFailed if incremental update could not be performed. -fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { - switch (options.output_mode) { - .Exe => {}, - .Obj => return error.IncrFailed, - .Lib => return error.IncrFailed, - } - var self: Coff = .{ - .base = .{ - .file = file, - .tag = .coff, - .options = options, - .allocator = allocator, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 32 => .p32, - 64 => .p64, - else => return error.UnsupportedELFArchitecture, - }, - }; - errdefer self.deinit(); - - // TODO implement reading the PE/COFF file - return error.IncrFailed; -} - -/// Truncates the existing file contents and overwrites the contents. -/// Returns an error if `file` is not already open with +read +write +seek abilities. -fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { - // TODO Write object specific relocations, COFF symbol table, then enable object file output. - switch (options.output_mode) { - .Exe => {}, - .Obj => return error.TODOImplementWritingObjFiles, - .Lib => return error.TODOImplementWritingLibFiles, - } - var self: Coff = .{ - .base = .{ - .tag = .coff, - .options = options, - .allocator = allocator, - .file = file, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 32 => .p32, - 64 => .p64, - else => return error.UnsupportedCOFFArchitecture, - }, - }; - errdefer self.deinit(); - - var coff_file_header_offset: u32 = 0; - if (options.output_mode == .Exe) { - // Write the MS-DOS stub and the PE signature - try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0); - coff_file_header_offset = msdos_stub.len + 4; - } - - // COFF file header - const data_directory_count = 0; - var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined; - var index: usize = 0; - - const machine = self.base.options.target.cpu.arch.toCoffMachine(); - if (machine == .Unknown) { - return error.UnsupportedCOFFArchitecture; - } - std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine)); - index += 2; - - // Number of sections (we only use .got, .text) - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); - index += 2; - // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32) - std.mem.set(u8, hdr_data[index..][0..12], 0); - index += 12; - - const optional_header_size = switch (options.output_mode) { - .Exe => data_directory_count * 8 + switch (self.ptr_width) { - .p32 => @as(u16, 96), - .p64 => 112, - }, - else => 0, - }; - - const section_table_offset = coff_file_header_offset + 20 + optional_header_size; - const default_offset_table_size = file_alignment; - const default_size_of_code = 0; - - self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); - const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); - self.offset_table_virtual_address = image_base + section_data_relative_virtual_address; - self.offset_table_size = default_offset_table_size; - self.section_table_offset = section_table_offset; - self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment; - self.text_section_size = default_size_of_code; - - // Size of file when loaded in memory - const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment); - - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); - index += 2; - - // Characteristics - var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary - if (options.output_mode == .Exe) { - characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE; - } - switch (self.ptr_width) { - .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE, - .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE, - } - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics); - index += 2; - - assert(index == 20); - try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset); - - if (options.output_mode == .Exe) { - self.optional_header_offset = coff_file_header_offset + 20; - // Optional header - index = 0; - std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { - .p32 => @as(u16, 0x10b), - .p64 => 0x20b, - }); - index += 2; - - // Linker version (u8 + u8) - std.mem.set(u8, hdr_data[index..][0..2], 0); - index += 2; - - // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32) - std.mem.set(u8, hdr_data[index..][0..20], 0); - index += 20; - - if (self.ptr_width == .p32) { - // Base of data relative to the image base (UNUSED) - std.mem.set(u8, hdr_data[index..][0..4], 0); - index += 4; - - // Image base address - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base); - index += 4; - } else { - // Image base address - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base); - index += 8; - } - - // Section alignment - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment); - index += 4; - // File alignment - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment); - index += 4; - // Required OS version, 6.0 is vista - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); - index += 2; - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); - index += 2; - // Image version - std.mem.set(u8, hdr_data[index..][0..4], 0); - index += 4; - // Required subsystem version, same as OS version - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); - index += 2; - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); - index += 2; - // Reserved zeroes (u32) - std.mem.set(u8, hdr_data[index..][0..4], 0); - index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image); - index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); - index += 4; - // CheckSum (u32) - std.mem.set(u8, hdr_data[index..][0..4], 0); - index += 4; - // Subsystem, TODO: Let users specify the subsystem, always CUI for now - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); - index += 2; - // DLL characteristics - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0); - index += 2; - - switch (self.ptr_width) { - .p32 => { - // Size of stack reserve + commit - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); - index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); - index += 4; - // Size of heap reserve + commit - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); - index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); - index += 4; - }, - .p64 => { - // Size of stack reserve + commit - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); - index += 8; - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); - index += 8; - // Size of heap reserve + commit - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); - index += 8; - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); - index += 8; - }, - } - - // Reserved zeroes - std.mem.set(u8, hdr_data[index..][0..4], 0); - index += 4; - - // Number of data directories - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); - index += 4; - // Initialize data directories to zero - std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0); - index += data_directory_count * 8; - - assert(index == optional_header_size); - } - - // Write section table. - // First, the .got section - hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*; - index += 8; - if (options.output_mode == .Exe) { - // Virtual size (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); - index += 4; - // Virtual address (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base); - index += 4; - } else { - std.mem.set(u8, hdr_data[index..][0..8], 0); - index += 8; - } - // Size of raw data (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); - index += 4; - // File pointer to the start of the section - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); - index += 4; - // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) - std.mem.set(u8, hdr_data[index..][0..12], 0); - index += 12; - // Section flags - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ); - index += 4; - // Then, the .text section - hdr_data[index..][0..8].* = ".text\x00\x00\x00".*; - index += 8; - if (options.output_mode == .Exe) { - // Virtual size (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); - index += 4; - // Virtual address (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base); - index += 4; - } else { - std.mem.set(u8, hdr_data[index..][0..8], 0); - index += 8; - } - // Size of raw data (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); - index += 4; - // File pointer to the start of the section - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size); - index += 4; - // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) - std.mem.set(u8, hdr_data[index..][0..12], 0); - index += 12; - // Section flags - std.mem.writeIntLittle( - u32, - hdr_data[index..][0..4], - std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE, - ); - index += 4; - - assert(index == optional_header_size + section_table_size); - try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset); - try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code); - - return self; -} - -pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { - try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); - - if (self.offset_table_free_list.popOrNull()) |i| { - decl.link.coff.offset_table_index = i; - } else { - decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len); - _ = self.offset_table.addOneAssumeCapacity(); - - const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; - if (self.offset_table.items.len > self.offset_table_size / entry_size) { - self.offset_table_size_dirty = true; - } - } - - self.offset_table.items[decl.link.coff.offset_table_index] = 0; -} - -fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { - const new_block_min_capacity = new_block_size * allocation_padding; - - // We use these to indicate our intention to update metadata, placing the new block, - // and possibly removing a free list node. - // It would be simpler to do it inside the for loop below, but that would cause a - // problem if an error was returned later in the function. So this action - // is actually carried out at the end of the function, when errors are no longer possible. - var block_placement: ?*TextBlock = null; - var free_list_removal: ?usize = null; - - const vaddr = blk: { - var i: usize = 0; - while (i < self.text_block_free_list.items.len) { - const free_block = self.text_block_free_list.items[i]; - - const next_block_text_offset = free_block.text_offset + free_block.capacity(); - const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address; - if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) { - block_placement = free_block; - - const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity; - if (remaining_capacity < minimum_text_block_size) { - free_list_removal = i; - } - - break :blk new_block_text_offset + self.text_section_virtual_address; - } else { - if (!free_block.freeListEligible()) { - _ = self.text_block_free_list.swapRemove(i); - } else { - i += 1; - } - continue; - } - } else if (self.last_text_block) |last| { - const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment); - block_placement = last; - break :blk new_block_vaddr; - } else { - break :blk self.text_section_virtual_address; - } - }; - - const expand_text_section = block_placement == null or block_placement.?.next == null; - if (expand_text_section) { - const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment)); - if (needed_size > self.text_section_size) { - const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment); - const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment); - if (current_text_section_virtual_size != new_text_section_virtual_size) { - self.size_of_image_dirty = true; - // Write new virtual size - var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size); - try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8); - } - - self.text_section_size = needed_size; - self.text_section_size_dirty = true; - } - self.last_text_block = text_block; - } - text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address); - text_block.size = @intCast(u32, new_block_size); - - // This function can also reallocate a text block. - // In this case we need to "unplug" it from its previous location before - // plugging it in to its new location. - if (text_block.prev) |prev| { - prev.next = text_block.next; - } - if (text_block.next) |next| { - next.prev = text_block.prev; - } - - if (block_placement) |big_block| { - text_block.prev = big_block; - text_block.next = big_block.next; - big_block.next = text_block; - } else { - text_block.prev = null; - text_block.next = null; - } - if (free_list_removal) |i| { - _ = self.text_block_free_list.swapRemove(i); - } - return vaddr; -} - -fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { - const block_vaddr = text_block.getVAddr(self.*); - const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr; - const need_realloc = !align_ok or new_block_size > text_block.capacity(); - if (!need_realloc) return @as(u64, block_vaddr); - return self.allocateTextBlock(text_block, new_block_size, alignment); -} - -fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void { - text_block.size = @intCast(u32, new_block_size); - if (text_block.capacity() - text_block.size >= minimum_text_block_size) { - self.text_block_free_list.append(self.base.allocator, text_block) catch {}; - } -} - -fn freeTextBlock(self: *Coff, text_block: *TextBlock) void { - var already_have_free_list_node = false; - { - var i: usize = 0; - // TODO turn text_block_free_list into a hash map - while (i < self.text_block_free_list.items.len) { - if (self.text_block_free_list.items[i] == text_block) { - _ = self.text_block_free_list.swapRemove(i); - continue; - } - if (self.text_block_free_list.items[i] == text_block.prev) { - already_have_free_list_node = true; - } - i += 1; - } - } - if (self.last_text_block == text_block) { - self.last_text_block = text_block.prev; - } - if (text_block.prev) |prev| { - prev.next = text_block.next; - - if (!already_have_free_list_node and prev.freeListEligible()) { - // The free list is heuristics, it doesn't have to be perfect, so we can - // ignore the OOM here. - self.text_block_free_list.append(self.base.allocator, prev) catch {}; - } - } - - if (text_block.next) |next| { - next.prev = text_block.prev; - } -} - -fn writeOffsetTableEntry(self: *Coff, index: usize) !void { - const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; - const endian = self.base.options.target.cpu.arch.endian(); - - const offset_table_start = self.section_data_offset; - if (self.offset_table_size_dirty) { - const current_raw_size = self.offset_table_size; - const new_raw_size = self.offset_table_size * 2; - log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size }); - - // Move the text section to a new place in the executable - const current_text_section_start = self.section_data_offset + current_raw_size; - const new_text_section_start = self.section_data_offset + new_raw_size; - - const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size); - if (amt != self.text_section_size) return error.InputOutput; - - // Write the new raw size in the .got header - var buf: [8]u8 = undefined; - std.mem.writeIntLittle(u32, buf[0..4], new_raw_size); - try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16); - // Write the new .text section file offset in the .text section header - std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start); - try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20); - - const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); - const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment); - // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section - // and the virutal size of the `.got` section - - if (new_virtual_size != current_virtual_size) { - log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size }); - self.size_of_image_dirty = true; - const va_offset = new_virtual_size - current_virtual_size; - - // Write .got virtual size - std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size); - try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8); - - // Write .text new virtual address - self.text_section_virtual_address = self.text_section_virtual_address + va_offset; - std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base); - try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); - - // Fix the VAs in the offset table - for (self.offset_table.items) |*va, idx| { - if (va.* != 0) { - va.* += va_offset; - - switch (entry_size) { - 4 => { - std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian); - try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size); - }, - 8 => { - std.mem.writeInt(u64, &buf, va.*, endian); - try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size); - }, - else => unreachable, - } - } - } - } - self.offset_table_size = new_raw_size; - self.offset_table_size_dirty = false; - } - // Write the new entry - switch (entry_size) { - 4 => { - var buf: [4]u8 = undefined; - std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); - try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); - }, - 8 => { - var buf: [8]u8 = undefined; - std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian); - try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); - }, - else => unreachable, - } -} - -pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { - // TODO COFF/PE debug information - // TODO Implement exports - const tracy = trace(@src()); - defer tracy.end(); - - var code_buffer = std.ArrayList(u8).init(self.base.allocator); - defer code_buffer.deinit(); - - const typed_value = decl.typed_value.most_recent.typed_value; - const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none); - const code = switch (res) { - .externally_managed => |x| x, - .appended => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); - return; - }, - }; - - const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); - const curr_size = decl.link.coff.size; - if (curr_size != 0) { - const capacity = decl.link.coff.capacity(); - const need_realloc = code.len > capacity or - !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); - if (need_realloc) { - const curr_vaddr = self.getDeclVAddr(decl); - const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); - if (vaddr != curr_vaddr) { - log.debug(" (writing new offset table entry)\n", .{}); - self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; - try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); - } - } else if (code.len < curr_size) { - self.shrinkTextBlock(&decl.link.coff, code.len); - } - } else { - const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len }); - errdefer self.freeTextBlock(&decl.link.coff); - self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; - try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); - } - - // Write the code into the file - try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset); - - // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - return self.updateDeclExports(module, decl, decl_exports); -} - -pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { - // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. - self.freeTextBlock(&decl.link.coff); - self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; -} - -pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void { - for (exports) |exp| { - if (exp.options.section) |section_name| { - if (!std.mem.eql(u8, section_name, ".text")) { - try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); - module.failed_exports.putAssumeCapacityNoClobber( - exp, - try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), - ); - continue; - } - } - if (std.mem.eql(u8, exp.options.name, "_start")) { - self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base; - } else { - try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); - module.failed_exports.putAssumeCapacityNoClobber( - exp, - try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}), - ); - continue; - } - } -} - -pub fn flush(self: *Coff, module: *Module) !void { - if (self.text_section_size_dirty) { - // Write the new raw size in the .text header - var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, self.text_section_size); - try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16); - try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size); - self.text_section_size_dirty = false; - } - - if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { - const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment); - var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, new_size_of_image); - try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); - self.size_of_image_dirty = false; - } - - if (self.entry_addr == null and self.base.options.output_mode == .Exe) { - log.debug("flushing. no_entry_point_found = true\n", .{}); - self.error_flags.no_entry_point_found = true; - } else { - log.debug("flushing. no_entry_point_found = false\n", .{}); - self.error_flags.no_entry_point_found = false; - - if (self.base.options.output_mode == .Exe) { - // Write AddressOfEntryPoint - var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, self.entry_addr.?); - try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16); - } - } -} - -pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { - return self.text_section_virtual_address + decl.link.coff.text_offset; -} - -pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { - // TODO Implement this -} - -pub fn deinit(self: *Coff) void { - self.text_block_free_list.deinit(self.base.allocator); - self.offset_table.deinit(self.base.allocator); - self.offset_table_free_list.deinit(self.base.allocator); -} diff --git a/src-self-hosted/link/Wasm.zig b/src-self-hosted/link/Wasm.zig deleted file mode 100644 index d8f172f584..0000000000 --- a/src-self-hosted/link/Wasm.zig +++ /dev/null @@ -1,251 +0,0 @@ -const Wasm = @This(); - -const std = @import("std"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const fs = std.fs; -const leb = std.debug.leb; - -const Module = @import("../Module.zig"); -const codegen = @import("../codegen/wasm.zig"); -const link = @import("../link.zig"); - -/// Various magic numbers defined by the wasm spec -const spec = struct { - const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm - const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1 - - const custom_id = 0; - const types_id = 1; - const imports_id = 2; - const funcs_id = 3; - const tables_id = 4; - const memories_id = 5; - const globals_id = 6; - const exports_id = 7; - const start_id = 8; - const elements_id = 9; - const code_id = 10; - const data_id = 11; -}; - -pub const base_tag = link.File.Tag.wasm; - -pub const FnData = struct { - /// Generated code for the type of the function - functype: std.ArrayListUnmanaged(u8) = .{}, - /// Generated code for the body of the function - code: std.ArrayListUnmanaged(u8) = .{}, - /// Locations in the generated code where function indexes must be filled in. - /// This must be kept ordered by offset. - idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{}, -}; - -base: link.File, - -/// List of all function Decls to be written to the output file. The index of -/// each Decl in this list at the time of writing the binary is used as the -/// function index. -/// TODO: can/should we access some data structure in Module directly? -funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, - -pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { - assert(options.object_format == .wasm); - - // TODO: read the file and keep vaild parts instead of truncating - const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true }); - errdefer file.close(); - - const wasm = try allocator.create(Wasm); - errdefer allocator.destroy(wasm); - - try file.writeAll(&(spec.magic ++ spec.version)); - - wasm.* = .{ - .base = .{ - .tag = .wasm, - .options = options, - .file = file, - .allocator = allocator, - }, - }; - - return &wasm.base; -} - -pub fn deinit(self: *Wasm) void { - for (self.funcs.items) |decl| { - decl.fn_link.wasm.?.functype.deinit(self.base.allocator); - decl.fn_link.wasm.?.code.deinit(self.base.allocator); - decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); - } - self.funcs.deinit(self.base.allocator); -} - -// Generate code for the Decl, storing it in memory to be later written to -// the file on flush(). -pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { - if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn) - return error.TODOImplementNonFnDeclsForWasm; - - if (decl.fn_link.wasm) |*fn_data| { - fn_data.functype.items.len = 0; - fn_data.code.items.len = 0; - fn_data.idx_refs.items.len = 0; - } else { - decl.fn_link.wasm = .{}; - try self.funcs.append(self.base.allocator, decl); - } - const fn_data = &decl.fn_link.wasm.?; - - var managed_functype = fn_data.functype.toManaged(self.base.allocator); - var managed_code = fn_data.code.toManaged(self.base.allocator); - try codegen.genFunctype(&managed_functype, decl); - try codegen.genCode(&managed_code, decl); - fn_data.functype = managed_functype.toUnmanaged(); - fn_data.code = managed_code.toUnmanaged(); -} - -pub fn updateDeclExports( - self: *Wasm, - module: *Module, - decl: *const Module.Decl, - exports: []const *Module.Export, -) !void {} - -pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { - // TODO: remove this assert when non-function Decls are implemented - assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); - _ = self.funcs.swapRemove(self.getFuncidx(decl).?); - decl.fn_link.wasm.?.functype.deinit(self.base.allocator); - decl.fn_link.wasm.?.code.deinit(self.base.allocator); - decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); - decl.fn_link.wasm = null; -} - -pub fn flush(self: *Wasm, module: *Module) !void { - const file = self.base.file.?; - const header_size = 5 + 1; - - // No need to rewrite the magic/version header - try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version))); - try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version))); - - // Type section - { - const header_offset = try reserveVecSectionHeader(file); - for (self.funcs.items) |decl| { - try file.writeAll(decl.fn_link.wasm.?.functype.items); - } - try writeVecSectionHeader( - file, - header_offset, - spec.types_id, - @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), - ); - } - - // Function section - { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); - for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx)); - try writeVecSectionHeader( - file, - header_offset, - spec.funcs_id, - @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), - ); - } - - // Export section - { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); - var count: u32 = 0; - for (module.decl_exports.entries.items) |entry| { - for (entry.value) |exprt| { - // Export name length + name - try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len)); - try writer.writeAll(exprt.options.name); - - switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { - .Fn => { - // Type of the export - try writer.writeByte(0x00); - // Exported function index - try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); - }, - else => return error.TODOImplementNonFnDeclsForWasm, - } - - count += 1; - } - } - try writeVecSectionHeader( - file, - header_offset, - spec.exports_id, - @intCast(u32, (try file.getPos()) - header_offset - header_size), - count, - ); - } - - // Code section - { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); - for (self.funcs.items) |decl| { - const fn_data = &decl.fn_link.wasm.?; - - // Write the already generated code to the file, inserting - // function indexes where required. - var current: u32 = 0; - for (fn_data.idx_refs.items) |idx_ref| { - try writer.writeAll(fn_data.code.items[current..idx_ref.offset]); - current = idx_ref.offset; - // Use a fixed width here to make calculating the code size - // in codegen.wasm.genCode() simpler. - var buf: [5]u8 = undefined; - leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?); - try writer.writeAll(&buf); - } - - try writer.writeAll(fn_data.code.items[current..]); - } - try writeVecSectionHeader( - file, - header_offset, - spec.code_id, - @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), - ); - } -} - -/// Get the current index of a given Decl in the function list -/// TODO: we could maintain a hash map to potentially make this -fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { - return for (self.funcs.items) |func, idx| { - if (func == decl) break @intCast(u32, idx); - } else null; -} - -fn reserveVecSectionHeader(file: fs.File) !u64 { - // section id + fixed leb contents size + fixed leb vector length - const header_size = 1 + 5 + 5; - // TODO: this should be a single lseek(2) call, but fs.File does not - // currently provide a way to do this. - try file.seekBy(header_size); - return (try file.getPos()) - header_size; -} - -fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void { - var buf: [1 + 5 + 5]u8 = undefined; - buf[0] = section; - leb.writeUnsignedFixed(5, buf[1..6], size); - leb.writeUnsignedFixed(5, buf[6..], items); - try file.pwriteAll(&buf, offset); -} diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig deleted file mode 100644 index 4159968252..0000000000 --- a/src-self-hosted/llvm.zig +++ /dev/null @@ -1,293 +0,0 @@ -const c = @import("c.zig"); -const assert = @import("std").debug.assert; - -// we wrap the c module for 3 reasons: -// 1. to avoid accidentally calling the non-thread-safe functions -// 2. patch up some of the types to remove nullability -// 3. some functions have been augmented by zig_llvm.cpp to be more powerful, -// such as ZigLLVMTargetMachineEmitToFile - -pub const AttributeIndex = c_uint; -pub const Bool = c_int; - -pub const Builder = c.LLVMBuilderRef.Child.Child; -pub const Context = c.LLVMContextRef.Child.Child; -pub const Module = c.LLVMModuleRef.Child.Child; -pub const Value = c.LLVMValueRef.Child.Child; -pub const Type = c.LLVMTypeRef.Child.Child; -pub const BasicBlock = c.LLVMBasicBlockRef.Child.Child; -pub const Attribute = c.LLVMAttributeRef.Child.Child; -pub const Target = c.LLVMTargetRef.Child.Child; -pub const TargetMachine = c.LLVMTargetMachineRef.Child.Child; -pub const TargetData = c.LLVMTargetDataRef.Child.Child; -pub const DIBuilder = c.ZigLLVMDIBuilder; -pub const DIFile = c.ZigLLVMDIFile; -pub const DICompileUnit = c.ZigLLVMDICompileUnit; - -pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType; -pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; -pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; -pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; -pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; -pub const ConstAllOnes = c.LLVMConstAllOnes; -pub const ConstArray = c.LLVMConstArray; -pub const ConstBitCast = c.LLVMConstBitCast; -pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision; -pub const ConstNeg = c.LLVMConstNeg; -pub const ConstStructInContext = c.LLVMConstStructInContext; -pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize; -pub const DisposeBuilder = c.LLVMDisposeBuilder; -pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder; -pub const DisposeMessage = c.LLVMDisposeMessage; -pub const DisposeModule = c.LLVMDisposeModule; -pub const DisposeTargetData = c.LLVMDisposeTargetData; -pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine; -pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; -pub const DumpModule = c.LLVMDumpModule; -pub const FP128TypeInContext = c.LLVMFP128TypeInContext; -pub const FloatTypeInContext = c.LLVMFloatTypeInContext; -pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; -pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; -pub const GetUndef = c.LLVMGetUndef; -pub const HalfTypeInContext = c.LLVMHalfTypeInContext; -pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; -pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters; -pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos; -pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs; -pub const InitializeAllTargets = c.LLVMInitializeAllTargets; -pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; -pub const Int128TypeInContext = c.LLVMInt128TypeInContext; -pub const Int16TypeInContext = c.LLVMInt16TypeInContext; -pub const Int1TypeInContext = c.LLVMInt1TypeInContext; -pub const Int32TypeInContext = c.LLVMInt32TypeInContext; -pub const Int64TypeInContext = c.LLVMInt64TypeInContext; -pub const Int8TypeInContext = c.LLVMInt8TypeInContext; -pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext; -pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext; -pub const LabelTypeInContext = c.LLVMLabelTypeInContext; -pub const MDNodeInContext = c.LLVMMDNodeInContext; -pub const MDStringInContext = c.LLVMMDStringInContext; -pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; -pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; -pub const SetAlignment = c.LLVMSetAlignment; -pub const SetDataLayout = c.LLVMSetDataLayout; -pub const SetGlobalConstant = c.LLVMSetGlobalConstant; -pub const SetInitializer = c.LLVMSetInitializer; -pub const SetLinkage = c.LLVMSetLinkage; -pub const SetTarget = c.LLVMSetTarget; -pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr; -pub const SetVolatile = c.LLVMSetVolatile; -pub const StructTypeInContext = c.LLVMStructTypeInContext; -pub const TokenTypeInContext = c.LLVMTokenTypeInContext; -pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; -pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; - -pub const AddGlobal = LLVMAddGlobal; -extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value; - -pub const ConstStringInContext = LLVMConstStringInContext; -extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value; - -pub const ConstInt = LLVMConstInt; -extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value; - -pub const BuildLoad = LLVMBuildLoad; -extern fn LLVMBuildLoad(arg0: *Builder, PointerVal: *Value, Name: [*:0]const u8) ?*Value; - -pub const ConstNull = LLVMConstNull; -extern fn LLVMConstNull(Ty: *Type) ?*Value; - -pub const CreateStringAttribute = LLVMCreateStringAttribute; -extern fn LLVMCreateStringAttribute( - C: *Context, - K: [*]const u8, - KLength: c_uint, - V: [*]const u8, - VLength: c_uint, -) ?*Attribute; - -pub const CreateEnumAttribute = LLVMCreateEnumAttribute; -extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) ?*Attribute; - -pub const AddFunction = LLVMAddFunction; -extern fn LLVMAddFunction(M: *Module, Name: [*:0]const u8, FunctionTy: *Type) ?*Value; - -pub const CreateCompileUnit = ZigLLVMCreateCompileUnit; -extern fn ZigLLVMCreateCompileUnit( - dibuilder: *DIBuilder, - lang: c_uint, - difile: *DIFile, - producer: [*:0]const u8, - is_optimized: bool, - flags: [*:0]const u8, - runtime_version: c_uint, - split_name: [*:0]const u8, - dwo_id: u64, - emit_debug_info: bool, -) ?*DICompileUnit; - -pub const CreateFile = ZigLLVMCreateFile; -extern fn ZigLLVMCreateFile(dibuilder: *DIBuilder, filename: [*:0]const u8, directory: [*:0]const u8) ?*DIFile; - -pub const ArrayType = LLVMArrayType; -extern fn LLVMArrayType(ElementType: *Type, ElementCount: c_uint) ?*Type; - -pub const CreateDIBuilder = ZigLLVMCreateDIBuilder; -extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) ?*DIBuilder; - -pub const PointerType = LLVMPointerType; -extern fn LLVMPointerType(ElementType: *Type, AddressSpace: c_uint) ?*Type; - -pub const CreateBuilderInContext = LLVMCreateBuilderInContext; -extern fn LLVMCreateBuilderInContext(C: *Context) ?*Builder; - -pub const IntTypeInContext = LLVMIntTypeInContext; -extern fn LLVMIntTypeInContext(C: *Context, NumBits: c_uint) ?*Type; - -pub const ModuleCreateWithNameInContext = LLVMModuleCreateWithNameInContext; -extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *Context) ?*Module; - -pub const VoidTypeInContext = LLVMVoidTypeInContext; -extern fn LLVMVoidTypeInContext(C: *Context) ?*Type; - -pub const ContextCreate = LLVMContextCreate; -extern fn LLVMContextCreate() ?*Context; - -pub const ContextDispose = LLVMContextDispose; -extern fn LLVMContextDispose(C: *Context) void; - -pub const CopyStringRepOfTargetData = LLVMCopyStringRepOfTargetData; -extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*:0]u8; - -pub const CreateTargetDataLayout = LLVMCreateTargetDataLayout; -extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData; - -pub const CreateTargetMachine = ZigLLVMCreateTargetMachine; -extern fn ZigLLVMCreateTargetMachine( - T: *Target, - Triple: [*:0]const u8, - CPU: [*:0]const u8, - Features: [*:0]const u8, - Level: CodeGenOptLevel, - Reloc: RelocMode, - CodeModel: CodeModel, - function_sections: bool, -) ?*TargetMachine; - -pub const GetHostCPUName = LLVMGetHostCPUName; -extern fn LLVMGetHostCPUName() ?[*:0]u8; - -pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; -extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8; - -pub const GetElementType = LLVMGetElementType; -extern fn LLVMGetElementType(Ty: *Type) *Type; - -pub const TypeOf = LLVMTypeOf; -extern fn LLVMTypeOf(Val: *Value) *Type; - -pub const BuildStore = LLVMBuildStore; -extern fn LLVMBuildStore(arg0: *Builder, Val: *Value, Ptr: *Value) ?*Value; - -pub const BuildAlloca = LLVMBuildAlloca; -extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*:0]const u8) ?*Value; - -pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; -pub extern fn LLVMConstInBoundsGEP(ConstantVal: *Value, ConstantIndices: [*]*Value, NumIndices: c_uint) ?*Value; - -pub const GetTargetFromTriple = LLVMGetTargetFromTriple; -extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **Target, ErrorMessage: ?*[*:0]u8) Bool; - -pub const VerifyModule = LLVMVerifyModule; -extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*:0]u8) Bool; - -pub const GetInsertBlock = LLVMGetInsertBlock; -extern fn LLVMGetInsertBlock(Builder: *Builder) *BasicBlock; - -pub const FunctionType = LLVMFunctionType; -extern fn LLVMFunctionType( - ReturnType: *Type, - ParamTypes: [*]*Type, - ParamCount: c_uint, - IsVarArg: Bool, -) ?*Type; - -pub const GetParam = LLVMGetParam; -extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value; - -pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext; -extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*:0]const u8) ?*BasicBlock; - -pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd; -extern fn LLVMPositionBuilderAtEnd(Builder: *Builder, Block: *BasicBlock) void; - -pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction; -pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; -pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; -pub const VerifierFailureAction = c.LLVMVerifierFailureAction; - -pub const CodeGenLevelNone = CodeGenOptLevel.LLVMCodeGenLevelNone; -pub const CodeGenLevelLess = CodeGenOptLevel.LLVMCodeGenLevelLess; -pub const CodeGenLevelDefault = CodeGenOptLevel.LLVMCodeGenLevelDefault; -pub const CodeGenLevelAggressive = CodeGenOptLevel.LLVMCodeGenLevelAggressive; -pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel; - -pub const RelocDefault = RelocMode.LLVMRelocDefault; -pub const RelocStatic = RelocMode.LLVMRelocStatic; -pub const RelocPIC = RelocMode.LLVMRelocPIC; -pub const RelocDynamicNoPic = RelocMode.LLVMRelocDynamicNoPic; -pub const RelocMode = c.LLVMRelocMode; - -pub const CodeModelDefault = CodeModel.LLVMCodeModelDefault; -pub const CodeModelJITDefault = CodeModel.LLVMCodeModelJITDefault; -pub const CodeModelSmall = CodeModel.LLVMCodeModelSmall; -pub const CodeModelKernel = CodeModel.LLVMCodeModelKernel; -pub const CodeModelMedium = CodeModel.LLVMCodeModelMedium; -pub const CodeModelLarge = CodeModel.LLVMCodeModelLarge; -pub const CodeModel = c.LLVMCodeModel; - -pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly; -pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; -pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; -pub const EmitOutputType = c.ZigLLVM_EmitOutputType; - -pub const CCallConv = CallConv.LLVMCCallConv; -pub const FastCallConv = CallConv.LLVMFastCallConv; -pub const ColdCallConv = CallConv.LLVMColdCallConv; -pub const WebKitJSCallConv = CallConv.LLVMWebKitJSCallConv; -pub const AnyRegCallConv = CallConv.LLVMAnyRegCallConv; -pub const X86StdcallCallConv = CallConv.LLVMX86StdcallCallConv; -pub const X86FastcallCallConv = CallConv.LLVMX86FastcallCallConv; -pub const CallConv = c.LLVMCallConv; - -pub const CallAttr = extern enum { - Auto, - NeverTail, - NeverInline, - AlwaysTail, - AlwaysInline, -}; - -fn removeNullability(comptime T: type) type { - comptime assert(@typeInfo(T).Pointer.size == .C); - return *T.Child; -} - -pub const BuildRet = LLVMBuildRet; -extern fn LLVMBuildRet(arg0: *Builder, V: ?*Value) ?*Value; - -pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; -extern fn ZigLLVMTargetMachineEmitToFile( - targ_machine_ref: *TargetMachine, - module_ref: *Module, - filename: [*:0]const u8, - output_type: EmitOutputType, - error_message: *[*:0]u8, - is_debug: bool, - is_small: bool, -) bool; - -pub const BuildCall = ZigLLVMBuildCall; -extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: CallConv, fn_inline: CallAttr, Name: [*:0]const u8) ?*Value; - -pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig deleted file mode 100644 index fb20a09f1d..0000000000 --- a/src-self-hosted/main.zig +++ /dev/null @@ -1,927 +0,0 @@ -const std = @import("std"); -const io = std.io; -const fs = std.fs; -const mem = std.mem; -const process = std.process; -const Allocator = mem.Allocator; -const ArrayList = std.ArrayList; -const ast = std.zig.ast; -const Module = @import("Module.zig"); -const link = @import("link.zig"); -const Package = @import("Package.zig"); -const zir = @import("zir.zig"); -const build_options = @import("build_options"); - -pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB - -pub const Color = enum { - Auto, - Off, - On, -}; - -const usage = - \\Usage: zig [command] [options] - \\ - \\Commands: - \\ - \\ build-exe [source] Create executable from source or object files - \\ build-lib [source] Create library from source or object files - \\ build-obj [source] Create object from source or assembly - \\ fmt [source] Parse file and render in canonical zig format - \\ targets List available compilation targets - \\ env Print lib path, std path, compiler id and version - \\ version Print version number and exit - \\ zen Print zen of zig and exit - \\ - \\ -; - -pub fn log( - comptime level: std.log.Level, - comptime scope: @TypeOf(.EnumLiteral), - comptime format: []const u8, - args: anytype, -) void { - // Hide anything more verbose than warn unless it was added with `-Dlog=foo`. - if (@enumToInt(level) > @enumToInt(std.log.level) or - @enumToInt(level) > @enumToInt(std.log.Level.warn)) - { - const scope_name = @tagName(scope); - const ok = comptime for (build_options.log_scopes) |log_scope| { - if (mem.eql(u8, log_scope, scope_name)) - break true; - } else false; - - if (!ok) - return; - } - - const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): "; - - // Print the message to stderr, silently ignoring any errors - std.debug.print(prefix ++ format ++ "\n", args); -} - -var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; - -pub fn main() !void { - const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator; - defer if (!std.builtin.link_libc) { - _ = general_purpose_allocator.deinit(); - }; - var arena_instance = std.heap.ArenaAllocator.init(gpa); - defer arena_instance.deinit(); - const arena = &arena_instance.allocator; - - const args = try process.argsAlloc(arena); - - if (args.len <= 1) { - std.debug.print("expected command argument\n\n{}", .{usage}); - process.exit(1); - } - - const cmd = args[1]; - const cmd_args = args[2..]; - if (mem.eql(u8, cmd, "build-exe")) { - return buildOutputType(gpa, arena, cmd_args, .Exe); - } else if (mem.eql(u8, cmd, "build-lib")) { - return buildOutputType(gpa, arena, cmd_args, .Lib); - } else if (mem.eql(u8, cmd, "build-obj")) { - return buildOutputType(gpa, arena, cmd_args, .Obj); - } else if (mem.eql(u8, cmd, "fmt")) { - return cmdFmt(gpa, cmd_args); - } else if (mem.eql(u8, cmd, "targets")) { - const info = try std.zig.system.NativeTargetInfo.detect(arena, .{}); - const stdout = io.getStdOut().outStream(); - return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); - } else if (mem.eql(u8, cmd, "version")) { - try std.io.getStdOut().writeAll(build_options.version ++ "\n"); - } else if (mem.eql(u8, cmd, "env")) { - try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream()); - } else if (mem.eql(u8, cmd, "zen")) { - try io.getStdOut().writeAll(info_zen); - } else if (mem.eql(u8, cmd, "help")) { - try io.getStdOut().writeAll(usage); - } else { - std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage }); - process.exit(1); - } -} - -const usage_build_generic = - \\Usage: zig build-exe [files] - \\ zig build-lib [files] - \\ zig build-obj [files] - \\ - \\Supported file types: - \\ .zig Zig source code - \\ .zir Zig Intermediate Representation code - \\ (planned) .o ELF object file - \\ (planned) .o MACH-O (macOS) object file - \\ (planned) .obj COFF (Windows) object file - \\ (planned) .lib COFF (Windows) static library - \\ (planned) .a ELF static library - \\ (planned) .so ELF shared object (dynamic link) - \\ (planned) .dll Windows Dynamic Link Library - \\ (planned) .dylib MACH-O (macOS) dynamic library - \\ (planned) .s Target-specific assembly source code - \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions) - \\ (planned) .c C source code (requires LLVM extensions) - \\ (planned) .cpp C++ source code (requires LLVM extensions) - \\ Other C++ extensions: .C .cc .cxx - \\ - \\General Options: - \\ -h, --help Print this help and exit - \\ --watch Enable compiler REPL - \\ --color [auto|off|on] Enable or disable colored error messages - \\ -femit-bin[=path] (default) output machine code - \\ -fno-emit-bin Do not output machine code - \\ - \\Compile Options: - \\ -target [name] -- see the targets command - \\ -mcpu [cpu] Specify target CPU and feature set - \\ --name [name] Override output name - \\ --mode [mode] Set the build mode - \\ Debug (default) optimizations off, safety on - \\ ReleaseFast Optimizations on, safety off - \\ ReleaseSafe Optimizations on, safety on - \\ ReleaseSmall Optimize for small binary, safety off - \\ --dynamic Force output to be dynamically linked - \\ --strip Exclude debug symbols - \\ -ofmt=[mode] Override target object format - \\ elf Executable and Linking Format - \\ c Compile to C source code - \\ wasm WebAssembly - \\ pe Portable Executable (Windows) - \\ coff (planned) Common Object File Format (Windows) - \\ macho (planned) macOS relocatables - \\ hex (planned) Intel IHEX - \\ raw (planned) Dump machine code directly - \\ - \\Link Options: - \\ -l[lib], --library [lib] Link against system library - \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) - \\ --version [ver] Dynamic library semver - \\ - \\Debug Options (Zig Compiler Development): - \\ -ftime-report Print timing diagnostics - \\ --debug-tokenize verbose tokenization - \\ --debug-ast-tree verbose parsing into an AST (tree view) - \\ --debug-ast-fmt verbose parsing into an AST (render source) - \\ --debug-ir verbose Zig IR - \\ --debug-link verbose linking - \\ --debug-codegen verbose machine code generation - \\ -; - -const Emit = union(enum) { - no, - yes_default_path, - yes: []const u8, -}; - -fn buildOutputType( - gpa: *Allocator, - arena: *Allocator, - args: []const []const u8, - output_mode: std.builtin.OutputMode, -) !void { - var color: Color = .Auto; - var build_mode: std.builtin.Mode = .Debug; - var provided_name: ?[]const u8 = null; - var link_mode: ?std.builtin.LinkMode = null; - var root_src_file: ?[]const u8 = null; - var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; - var strip = false; - var watch = false; - var debug_tokenize = false; - var debug_ast_tree = false; - var debug_ast_fmt = false; - var debug_link = false; - var debug_ir = false; - var debug_codegen = false; - var time_report = false; - var emit_bin: Emit = .yes_default_path; - var emit_zir: Emit = .no; - var target_arch_os_abi: []const u8 = "native"; - var target_mcpu: ?[]const u8 = null; - var target_dynamic_linker: ?[]const u8 = null; - var target_ofmt: ?[]const u8 = null; - - var system_libs = std.ArrayList([]const u8).init(gpa); - defer system_libs.deinit(); - - { - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg = args[i]; - if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try io.getStdOut().writeAll(usage_build_generic); - process.exit(0); - } else if (mem.eql(u8, arg, "--color")) { - if (i + 1 >= args.len) { - std.debug.print("expected [auto|on|off] after --color\n", .{}); - process.exit(1); - } - i += 1; - const next_arg = args[i]; - if (mem.eql(u8, next_arg, "auto")) { - color = .Auto; - } else if (mem.eql(u8, next_arg, "on")) { - color = .On; - } else if (mem.eql(u8, next_arg, "off")) { - color = .Off; - } else { - std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); - process.exit(1); - } - } else if (mem.eql(u8, arg, "--mode")) { - if (i + 1 >= args.len) { - std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{}); - process.exit(1); - } - i += 1; - const next_arg = args[i]; - if (mem.eql(u8, next_arg, "Debug")) { - build_mode = .Debug; - } else if (mem.eql(u8, next_arg, "ReleaseSafe")) { - build_mode = .ReleaseSafe; - } else if (mem.eql(u8, next_arg, "ReleaseFast")) { - build_mode = .ReleaseFast; - } else if (mem.eql(u8, next_arg, "ReleaseSmall")) { - build_mode = .ReleaseSmall; - } else { - std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg}); - process.exit(1); - } - } else if (mem.eql(u8, arg, "--name")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after --name\n", .{}); - process.exit(1); - } - i += 1; - provided_name = args[i]; - } else if (mem.eql(u8, arg, "--library")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after --library\n", .{}); - process.exit(1); - } - i += 1; - try system_libs.append(args[i]); - } else if (mem.eql(u8, arg, "--version")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after --version\n", .{}); - process.exit(1); - } - i += 1; - version = std.builtin.Version.parse(args[i]) catch |err| { - std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) }); - process.exit(1); - }; - } else if (mem.eql(u8, arg, "-target")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after -target\n", .{}); - process.exit(1); - } - i += 1; - target_arch_os_abi = args[i]; - } else if (mem.eql(u8, arg, "-mcpu")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after -mcpu\n", .{}); - process.exit(1); - } - i += 1; - target_mcpu = args[i]; - } else if (mem.startsWith(u8, arg, "-ofmt=")) { - target_ofmt = arg["-ofmt=".len..]; - } else if (mem.startsWith(u8, arg, "-mcpu=")) { - target_mcpu = arg["-mcpu=".len..]; - } else if (mem.eql(u8, arg, "--dynamic-linker")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after --dynamic-linker\n", .{}); - process.exit(1); - } - i += 1; - target_dynamic_linker = args[i]; - } else if (mem.eql(u8, arg, "--watch")) { - watch = true; - } else if (mem.eql(u8, arg, "-ftime-report")) { - time_report = true; - } else if (mem.eql(u8, arg, "-femit-bin")) { - emit_bin = .yes_default_path; - } else if (mem.startsWith(u8, arg, "-femit-bin=")) { - emit_bin = .{ .yes = arg["-femit-bin=".len..] }; - } else if (mem.eql(u8, arg, "-fno-emit-bin")) { - emit_bin = .no; - } else if (mem.eql(u8, arg, "-femit-zir")) { - emit_zir = .yes_default_path; - } else if (mem.startsWith(u8, arg, "-femit-zir=")) { - emit_zir = .{ .yes = arg["-femit-zir=".len..] }; - } else if (mem.eql(u8, arg, "-fno-emit-zir")) { - emit_zir = .no; - } else if (mem.eql(u8, arg, "-dynamic")) { - link_mode = .Dynamic; - } else if (mem.eql(u8, arg, "-static")) { - link_mode = .Static; - } else if (mem.eql(u8, arg, "--strip")) { - strip = true; - } else if (mem.eql(u8, arg, "--debug-tokenize")) { - debug_tokenize = true; - } else if (mem.eql(u8, arg, "--debug-ast-tree")) { - debug_ast_tree = true; - } else if (mem.eql(u8, arg, "--debug-ast-fmt")) { - debug_ast_fmt = true; - } else if (mem.eql(u8, arg, "--debug-link")) { - debug_link = true; - } else if (mem.eql(u8, arg, "--debug-ir")) { - debug_ir = true; - } else if (mem.eql(u8, arg, "--debug-codegen")) { - debug_codegen = true; - } else if (mem.startsWith(u8, arg, "-l")) { - try system_libs.append(arg[2..]); - } else { - std.debug.print("unrecognized parameter: '{}'\n", .{arg}); - process.exit(1); - } - } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) { - std.debug.print("assembly files not supported yet\n", .{}); - process.exit(1); - } else if (mem.endsWith(u8, arg, ".o") or - mem.endsWith(u8, arg, ".obj") or - mem.endsWith(u8, arg, ".a") or - mem.endsWith(u8, arg, ".lib")) - { - std.debug.print("object files and static libraries not supported yet\n", .{}); - process.exit(1); - } else if (mem.endsWith(u8, arg, ".c") or - mem.endsWith(u8, arg, ".cpp")) - { - std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet\n", .{}); - process.exit(1); - } else if (mem.endsWith(u8, arg, ".so") or - mem.endsWith(u8, arg, ".dylib") or - mem.endsWith(u8, arg, ".dll")) - { - std.debug.print("linking against dynamic libraries not yet supported\n", .{}); - process.exit(1); - } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) { - if (root_src_file) |other| { - std.debug.print("found another zig file '{}' after root source file '{}'\n", .{ arg, other }); - process.exit(1); - } else { - root_src_file = arg; - } - } else { - std.debug.print("unrecognized file extension of parameter '{}'\n", .{arg}); - } - } - } - - const root_name = if (provided_name) |n| n else blk: { - if (root_src_file) |file| { - const basename = fs.path.basename(file); - var it = mem.split(basename, "."); - break :blk it.next() orelse basename; - } else { - std.debug.print("--name [name] not provided and unable to infer\n", .{}); - process.exit(1); - } - }; - - if (system_libs.items.len != 0) { - std.debug.print("linking against system libraries not yet supported\n", .{}); - process.exit(1); - } - - var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{}; - const cross_target = std.zig.CrossTarget.parse(.{ - .arch_os_abi = target_arch_os_abi, - .cpu_features = target_mcpu, - .dynamic_linker = target_dynamic_linker, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpuModel => { - std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ - diags.cpu_name.?, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allCpuModels()) |cpu| { - std.debug.print(" {}\n", .{cpu.name}); - } - process.exit(1); - }, - error.UnknownCpuFeature => { - std.debug.print( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': - \\ - , .{ - diags.unknown_feature_name, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allFeaturesList()) |feature| { - std.debug.print(" {}: {}\n", .{ feature.name, feature.description }); - } - process.exit(1); - }, - else => |e| return e, - }; - - var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target); - if (target_info.cpu_detection_unimplemented) { - // TODO We want to just use detected_info.target but implementing - // CPU model & feature detection is todo so here we rely on LLVM. - std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{}); - process.exit(1); - } - - const src_path = root_src_file orelse { - std.debug.print("expected at least one file argument", .{}); - process.exit(1); - }; - - const object_format: ?std.Target.ObjectFormat = blk: { - const ofmt = target_ofmt orelse break :blk null; - if (mem.eql(u8, ofmt, "elf")) { - break :blk .elf; - } else if (mem.eql(u8, ofmt, "c")) { - break :blk .c; - } else if (mem.eql(u8, ofmt, "coff")) { - break :blk .coff; - } else if (mem.eql(u8, ofmt, "pe")) { - break :blk .pe; - } else if (mem.eql(u8, ofmt, "macho")) { - break :blk .macho; - } else if (mem.eql(u8, ofmt, "wasm")) { - break :blk .wasm; - } else if (mem.eql(u8, ofmt, "hex")) { - break :blk .hex; - } else if (mem.eql(u8, ofmt, "raw")) { - break :blk .raw; - } else { - std.debug.print("unsupported object format: {}", .{ofmt}); - process.exit(1); - } - }; - - const bin_path = switch (emit_bin) { - .no => { - std.debug.print("-fno-emit-bin not supported yet", .{}); - process.exit(1); - }, - .yes_default_path => if (object_format != null and object_format.? == .c) - try std.fmt.allocPrint(arena, "{}.c", .{root_name}) - else - try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode), - - .yes => |p| p, - }; - - const zir_out_path: ?[]const u8 = switch (emit_zir) { - .no => null, - .yes_default_path => blk: { - if (root_src_file) |rsf| { - if (mem.endsWith(u8, rsf, ".zir")) { - break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name}); - } - } - break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name}); - }, - .yes => |p| p, - }; - - const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path); - defer root_pkg.destroy(); - - var module = try Module.init(gpa, .{ - .root_name = root_name, - .target = target_info.target, - .output_mode = output_mode, - .root_pkg = root_pkg, - .bin_file_dir = fs.cwd(), - .bin_file_path = bin_path, - .link_mode = link_mode, - .object_format = object_format, - .optimize_mode = build_mode, - .keep_source_files_loaded = zir_out_path != null, - }); - defer module.deinit(); - - const stdin = std.io.getStdIn().inStream(); - const stderr = std.io.getStdErr().outStream(); - var repl_buf: [1024]u8 = undefined; - - try updateModule(gpa, &module, zir_out_path); - - while (watch) { - try stderr.print("🦎 ", .{}); - if (output_mode == .Exe) { - try module.makeBinFileExecutable(); - } - if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { - try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); - continue; - }) |line| { - const actual_line = mem.trimRight(u8, line, "\r\n "); - - if (mem.eql(u8, actual_line, "update")) { - if (output_mode == .Exe) { - try module.makeBinFileWritable(); - } - try updateModule(gpa, &module, zir_out_path); - } else if (mem.eql(u8, actual_line, "exit")) { - break; - } else if (mem.eql(u8, actual_line, "help")) { - try stderr.writeAll(repl_help); - } else { - try stderr.print("unknown command: {}\n", .{actual_line}); - } - } else { - break; - } - } -} - -fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void { - var timer = try std.time.Timer.start(); - try module.update(); - const update_nanos = timer.read(); - - var errors = try module.getAllErrorsAlloc(); - defer errors.deinit(module.gpa); - - if (errors.list.len != 0) { - for (errors.list) |full_err_msg| { - std.debug.print("{}:{}:{}: error: {}\n", .{ - full_err_msg.src_path, - full_err_msg.line + 1, - full_err_msg.column + 1, - full_err_msg.msg, - }); - } - } else { - std.log.scoped(.compiler).info("Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms}); - } - - if (zir_out_path) |zop| { - var new_zir_module = try zir.emit(gpa, module.*); - defer new_zir_module.deinit(gpa); - - const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); - defer baf.destroy(); - - try new_zir_module.writeToStream(gpa, baf.stream()); - - try baf.finish(); - } -} - -const repl_help = - \\Commands: - \\ update Detect changes to source files and update output files. - \\ help Print this text - \\ exit Quit this repl - \\ -; - -pub const usage_fmt = - \\usage: zig fmt [file]... - \\ - \\ Formats the input files and modifies them in-place. - \\ Arguments can be files or directories, which are searched - \\ recursively. - \\ - \\Options: - \\ --help Print this help and exit - \\ --color [auto|off|on] Enable or disable colored error messages - \\ --stdin Format code from stdin; output to stdout - \\ --check List non-conforming files and exit with an error - \\ if the list is non-empty - \\ - \\ -; - -const Fmt = struct { - seen: SeenMap, - any_error: bool, - color: Color, - gpa: *Allocator, - out_buffer: std.ArrayList(u8), - - const SeenMap = std.AutoHashMap(fs.File.INode, void); -}; - -pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { - const stderr_file = io.getStdErr(); - var color: Color = .Auto; - var stdin_flag: bool = false; - var check_flag: bool = false; - var input_files = ArrayList([]const u8).init(gpa); - - { - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg = args[i]; - if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "--help")) { - const stdout = io.getStdOut().outStream(); - try stdout.writeAll(usage_fmt); - process.exit(0); - } else if (mem.eql(u8, arg, "--color")) { - if (i + 1 >= args.len) { - std.debug.print("expected [auto|on|off] after --color\n", .{}); - process.exit(1); - } - i += 1; - const next_arg = args[i]; - if (mem.eql(u8, next_arg, "auto")) { - color = .Auto; - } else if (mem.eql(u8, next_arg, "on")) { - color = .On; - } else if (mem.eql(u8, next_arg, "off")) { - color = .Off; - } else { - std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); - process.exit(1); - } - } else if (mem.eql(u8, arg, "--stdin")) { - stdin_flag = true; - } else if (mem.eql(u8, arg, "--check")) { - check_flag = true; - } else { - std.debug.print("unrecognized parameter: '{}'", .{arg}); - process.exit(1); - } - } else { - try input_files.append(arg); - } - } - } - - if (stdin_flag) { - if (input_files.items.len != 0) { - std.debug.print("cannot use --stdin with positional arguments\n", .{}); - process.exit(1); - } - - const stdin = io.getStdIn().inStream(); - - const source_code = try stdin.readAllAlloc(gpa, max_src_size); - defer gpa.free(source_code); - - const tree = std.zig.parse(gpa, source_code) catch |err| { - std.debug.print("error parsing stdin: {}\n", .{err}); - process.exit(1); - }; - defer tree.deinit(); - - for (tree.errors) |parse_error| { - try printErrMsgToFile(gpa, parse_error, tree, "", stderr_file, color); - } - if (tree.errors.len != 0) { - process.exit(1); - } - if (check_flag) { - const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree); - const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); - process.exit(code); - } - - const stdout = io.getStdOut().outStream(); - _ = try std.zig.render(gpa, stdout, tree); - return; - } - - if (input_files.items.len == 0) { - std.debug.print("expected at least one source file argument\n", .{}); - process.exit(1); - } - - var fmt = Fmt{ - .gpa = gpa, - .seen = Fmt.SeenMap.init(gpa), - .any_error = false, - .color = color, - .out_buffer = std.ArrayList(u8).init(gpa), - }; - defer fmt.seen.deinit(); - defer fmt.out_buffer.deinit(); - - for (input_files.span()) |file_path| { - // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. - const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { - std.debug.print("unable to open '{}': {}\n", .{ file_path, err }); - process.exit(1); - }; - defer gpa.free(real_path); - - try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path); - } - if (fmt.any_error) { - process.exit(1); - } -} - -const FmtError = error{ - SystemResources, - OperationAborted, - IoPending, - BrokenPipe, - Unexpected, - WouldBlock, - FileClosed, - DestinationAddressRequired, - DiskQuota, - FileTooBig, - InputOutput, - NoSpaceLeft, - AccessDenied, - OutOfMemory, - RenameAcrossMountPoints, - ReadOnlyFileSystem, - LinkQuotaExceeded, - FileBusy, - EndOfStream, - Unseekable, - NotOpenForWriting, -} || fs.File.OpenError; - -fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { - fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { - error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), - else => { - std.debug.print("unable to format '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }, - }; -} - -fn fmtPathDir( - fmt: *Fmt, - file_path: []const u8, - check_mode: bool, - parent_dir: fs.Dir, - parent_sub_path: []const u8, -) FmtError!void { - var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true }); - defer dir.close(); - - const stat = try dir.stat(); - if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; - - var dir_it = dir.iterate(); - while (try dir_it.next()) |entry| { - const is_dir = entry.kind == .Directory; - if (is_dir or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); - defer fmt.gpa.free(full_path); - - if (is_dir) { - try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); - } else { - fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { - std.debug.print("unable to format '{}': {}\n", .{ full_path, err }); - fmt.any_error = true; - return; - }; - } - } - } -} - -fn fmtPathFile( - fmt: *Fmt, - file_path: []const u8, - check_mode: bool, - dir: fs.Dir, - sub_path: []const u8, -) FmtError!void { - const source_file = try dir.openFile(sub_path, .{}); - var file_closed = false; - errdefer if (!file_closed) source_file.close(); - - const stat = try source_file.stat(); - - if (stat.kind == .Directory) - return error.IsDir; - - const source_code = source_file.readToEndAllocOptions( - fmt.gpa, - max_src_size, - stat.size, - @alignOf(u8), - null, - ) catch |err| switch (err) { - error.ConnectionResetByPeer => unreachable, - error.ConnectionTimedOut => unreachable, - error.NotOpenForReading => unreachable, - else => |e| return e, - }; - source_file.close(); - file_closed = true; - defer fmt.gpa.free(source_code); - - // Add to set after no longer possible to get error.IsDir. - if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; - - const tree = try std.zig.parse(fmt.gpa, source_code); - defer tree.deinit(); - - for (tree.errors) |parse_error| { - try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color); - } - if (tree.errors.len != 0) { - fmt.any_error = true; - return; - } - - if (check_mode) { - const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); - if (anything_changed) { - std.debug.print("{}\n", .{file_path}); - fmt.any_error = true; - } - } else { - // As a heuristic, we make enough capacity for the same as the input source. - try fmt.out_buffer.ensureCapacity(source_code.len); - fmt.out_buffer.items.len = 0; - const writer = fmt.out_buffer.writer(); - const anything_changed = try std.zig.render(fmt.gpa, writer, tree); - if (!anything_changed) - return; // Good thing we didn't waste any file system access on this. - - var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); - defer af.deinit(); - - try af.file.writeAll(fmt.out_buffer.items); - try af.finish(); - std.debug.print("{}\n", .{file_path}); - } -} - -fn printErrMsgToFile( - gpa: *mem.Allocator, - parse_error: ast.Error, - tree: *ast.Tree, - path: []const u8, - file: fs.File, - color: Color, -) !void { - const color_on = switch (color) { - .Auto => file.isTty(), - .On => true, - .Off => false, - }; - const lok_token = parse_error.loc(); - const span_first = lok_token; - const span_last = lok_token; - - const first_token = tree.token_locs[span_first]; - const last_token = tree.token_locs[span_last]; - const start_loc = tree.tokenLocationLoc(0, first_token); - const end_loc = tree.tokenLocationLoc(first_token.end, last_token); - - var text_buf = std.ArrayList(u8).init(gpa); - defer text_buf.deinit(); - const out_stream = text_buf.outStream(); - try parse_error.render(tree.token_ids, out_stream); - const text = text_buf.span(); - - const stream = file.outStream(); - try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); - - if (!color_on) return; - - // Print \r and \t as one space each so that column counts line up - for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| { - try stream.writeByte(switch (byte) { - '\r', '\t' => ' ', - else => byte, - }); - } - try stream.writeByte('\n'); - try stream.writeByteNTimes(' ', start_loc.column); - try stream.writeByteNTimes('~', last_token.end - first_token.start); - try stream.writeByte('\n'); -} - -pub const info_zen = - \\ - \\ * Communicate intent precisely. - \\ * Edge cases matter. - \\ * Favor reading code over writing code. - \\ * Only one obvious way to do things. - \\ * Runtime crashes are better than bugs. - \\ * Compile errors are better than runtime crashes. - \\ * Incremental improvements. - \\ * Avoid local maximums. - \\ * Reduce the amount one must remember. - \\ * Minimize energy spent on coding style. - \\ * Resource deallocation must succeed. - \\ * Together we serve the users. - \\ - \\ -; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig deleted file mode 100644 index ac4d89bb21..0000000000 --- a/src-self-hosted/stage2.zig +++ /dev/null @@ -1,1300 +0,0 @@ -// This is Zig code that is used by both stage1 and stage2. -// The prototypes in src/userland.h must match these definitions. - -const std = @import("std"); -const io = std.io; -const mem = std.mem; -const fs = std.fs; -const process = std.process; -const Allocator = mem.Allocator; -const ArrayList = std.ArrayList; -const ArrayListSentineled = std.ArrayListSentineled; -const Target = std.Target; -const CrossTarget = std.zig.CrossTarget; -const self_hosted_main = @import("main.zig"); -const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer; -const assert = std.debug.assert; -const LibCInstallation = @import("libc_installation.zig").LibCInstallation; - -var stderr_file: fs.File = undefined; -var stderr: fs.File.OutStream = undefined; -var stdout: fs.File.OutStream = undefined; - -comptime { - _ = @import("dep_tokenizer.zig"); -} - -// ABI warning -export fn stage2_zen(ptr: *[*]const u8, len: *usize) void { - const info_zen = @import("main.zig").info_zen; - ptr.* = info_zen; - len.* = info_zen.len; -} - -// ABI warning -export fn stage2_panic(ptr: [*]const u8, len: usize) void { - @panic(ptr[0..len]); -} - -// ABI warning -const Error = extern enum { - None, - OutOfMemory, - InvalidFormat, - SemanticAnalyzeFail, - AccessDenied, - Interrupted, - SystemResources, - FileNotFound, - FileSystem, - FileTooBig, - DivByZero, - Overflow, - PathAlreadyExists, - Unexpected, - ExactDivRemainder, - NegativeDenominator, - ShiftedOutOneBits, - CCompileErrors, - EndOfFile, - IsDir, - NotDir, - UnsupportedOperatingSystem, - SharingViolation, - PipeBusy, - PrimitiveTypeNotFound, - CacheUnavailable, - PathTooLong, - CCompilerCannotFindFile, - NoCCompilerInstalled, - ReadingDepFile, - InvalidDepFile, - MissingArchitecture, - MissingOperatingSystem, - UnknownArchitecture, - UnknownOperatingSystem, - UnknownABI, - InvalidFilename, - DiskQuota, - DiskSpace, - UnexpectedWriteFailure, - UnexpectedSeekFailure, - UnexpectedFileTruncationFailure, - Unimplemented, - OperationAborted, - BrokenPipe, - NoSpaceLeft, - NotLazy, - IsAsync, - ImportOutsidePkgPath, - UnknownCpuModel, - UnknownCpuFeature, - InvalidCpuFeatures, - InvalidLlvmCpuFeaturesFormat, - UnknownApplicationBinaryInterface, - ASTUnitFailure, - BadPathName, - SymLinkLoop, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - NoDevice, - DeviceBusy, - UnableToSpawnCCompiler, - CCompilerExitCode, - CCompilerCrashed, - CCompilerCannotFindHeaders, - LibCRuntimeNotFound, - LibCStdLibHeaderNotFound, - LibCKernel32LibNotFound, - UnsupportedArchitecture, - WindowsSdkNotFound, - UnknownDynamicLinkerPath, - TargetHasNoDynamicLinker, - InvalidAbiVersion, - InvalidOperatingSystemVersion, - UnknownClangOption, - NestedResponseFile, - ZigIsTheCCompiler, - FileBusy, - Locked, -}; - -const FILE = std.c.FILE; -const ast = std.zig.ast; -const translate_c = @import("translate_c.zig"); - -/// Args should have a null terminating last arg. -export fn stage2_translate_c( - out_ast: **ast.Tree, - out_errors_ptr: *[*]translate_c.ClangErrMsg, - out_errors_len: *usize, - args_begin: [*]?[*]const u8, - args_end: [*]?[*]const u8, - resources_path: [*:0]const u8, -) Error { - var errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; - out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { - error.SemanticAnalyzeFail => { - out_errors_ptr.* = errors.ptr; - out_errors_len.* = errors.len; - return .CCompileErrors; - }, - error.ASTUnitFailure => return .ASTUnitFailure, - error.OutOfMemory => return .OutOfMemory, - }; - return .None; -} - -export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void { - translate_c.freeErrors(errors_ptr[0..errors_len]); -} - -export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { - const c_out_stream = std.io.cOutStream(output_file); - _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) { - error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode - error.NotOpenForWriting => unreachable, - error.SystemResources => return .SystemResources, - error.OperationAborted => return .OperationAborted, - error.BrokenPipe => return .BrokenPipe, - error.DiskQuota => return .DiskQuota, - error.FileTooBig => return .FileTooBig, - error.NoSpaceLeft => return .NoSpaceLeft, - error.AccessDenied => return .AccessDenied, - error.OutOfMemory => return .OutOfMemory, - error.Unexpected => return .Unexpected, - error.InputOutput => return .FileSystem, - }; - return .None; -} - -export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int { - if (std.debug.runtime_safety) { - fmtMain(argc, argv) catch unreachable; - } else { - fmtMain(argc, argv) catch |e| { - std.debug.warn("{}\n", .{@errorName(e)}); - return -1; - }; - } - return 0; -} - -fn argvToArrayList(allocator: *Allocator, argc: c_int, argv: [*]const [*:0]const u8) !ArrayList([]const u8) { - var args_list = std.ArrayList([]const u8).init(allocator); - const argc_usize = @intCast(usize, argc); - var arg_i: usize = 0; - while (arg_i < argc_usize) : (arg_i += 1) { - try args_list.append(mem.spanZ(argv[arg_i])); - } - - return args_list; -} - -fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { - const allocator = std.heap.c_allocator; - - var args_list = try argvToArrayList(allocator, argc, argv); - defer args_list.deinit(); - - const args = args_list.span()[2..]; - return self_hosted_main.cmdFmt(allocator, args); -} - -export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer { - const t = std.heap.c_allocator.create(DepTokenizer) catch @panic("failed to create .d tokenizer"); - t.* = DepTokenizer.init(std.heap.c_allocator, input[0..len]); - return stage2_DepTokenizer{ - .handle = t, - }; -} - -export fn stage2_DepTokenizer_deinit(self: *stage2_DepTokenizer) void { - self.handle.deinit(); -} - -export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextResult { - const otoken = self.handle.next() catch { - const textz = std.ArrayListSentineled(u8, 0).init(&self.handle.arena.allocator, self.handle.error_text) catch @panic("failed to create .d tokenizer error text"); - return stage2_DepNextResult{ - .type_id = .error_, - .textz = textz.span().ptr, - }; - }; - const token = otoken orelse { - return stage2_DepNextResult{ - .type_id = .null_, - .textz = undefined, - }; - }; - const textz = std.ArrayListSentineled(u8, 0).init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text"); - return stage2_DepNextResult{ - .type_id = switch (token.id) { - .target => .target, - .prereq => .prereq, - }, - .textz = textz.span().ptr, - }; -} - -const stage2_DepTokenizer = extern struct { - handle: *DepTokenizer, -}; - -const stage2_DepNextResult = extern struct { - type_id: TypeId, - - // when type_id == error --> error text - // when type_id == null --> undefined - // when type_id == target --> target pathname - // when type_id == prereq --> prereq pathname - textz: [*]const u8, - - const TypeId = extern enum { - error_, - null_, - target, - prereq, - }; -}; - -// ABI warning -export fn stage2_attach_segfault_handler() void { - if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) { - std.debug.attachSegfaultHandler(); - } -} - -// ABI warning -export fn stage2_progress_create() *std.Progress { - const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); - ptr.* = std.Progress{}; - return ptr; -} - -// ABI warning -export fn stage2_progress_destroy(progress: *std.Progress) void { - std.heap.c_allocator.destroy(progress); -} - -// ABI warning -export fn stage2_progress_start_root( - progress: *std.Progress, - name_ptr: [*]const u8, - name_len: usize, - estimated_total_items: usize, -) *std.Progress.Node { - return progress.start( - name_ptr[0..name_len], - if (estimated_total_items == 0) null else estimated_total_items, - ) catch @panic("timer unsupported"); -} - -// ABI warning -export fn stage2_progress_disable_tty(progress: *std.Progress) void { - progress.terminal = null; -} - -// ABI warning -export fn stage2_progress_start( - node: *std.Progress.Node, - name_ptr: [*]const u8, - name_len: usize, - estimated_total_items: usize, -) *std.Progress.Node { - const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory"); - child_node.* = node.start( - name_ptr[0..name_len], - if (estimated_total_items == 0) null else estimated_total_items, - ); - child_node.activate(); - return child_node; -} - -// ABI warning -export fn stage2_progress_end(node: *std.Progress.Node) void { - node.end(); - if (&node.context.root != node) { - std.heap.c_allocator.destroy(node); - } -} - -// ABI warning -export fn stage2_progress_complete_one(node: *std.Progress.Node) void { - node.completeOne(); -} - -// ABI warning -export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void { - node.completed_items = done_count; - node.estimated_total_items = total_count; - node.activate(); - node.context.maybeRefresh(); -} - -fn detectNativeCpuWithLLVM( - arch: Target.Cpu.Arch, - llvm_cpu_name_z: ?[*:0]const u8, - llvm_cpu_features_opt: ?[*:0]const u8, -) !Target.Cpu { - var result = Target.Cpu.baseline(arch); - - if (llvm_cpu_name_z) |cpu_name_z| { - const llvm_cpu_name = mem.spanZ(cpu_name_z); - - for (arch.allCpuModels()) |model| { - const this_llvm_name = model.llvm_name orelse continue; - if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { - // Here we use the non-dependencies-populated set, - // so that subtracting features later in this function - // affect the prepopulated set. - result = Target.Cpu{ - .arch = arch, - .model = model, - .features = model.features, - }; - break; - } - } - } - - const all_features = arch.allFeaturesList(); - - if (llvm_cpu_features_opt) |llvm_cpu_features| { - var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ","); - while (it.next()) |decorated_llvm_feat| { - var op: enum { - add, - sub, - } = undefined; - var llvm_feat: []const u8 = undefined; - if (mem.startsWith(u8, decorated_llvm_feat, "+")) { - op = .add; - llvm_feat = decorated_llvm_feat[1..]; - } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) { - op = .sub; - llvm_feat = decorated_llvm_feat[1..]; - } else { - return error.InvalidLlvmCpuFeaturesFormat; - } - for (all_features) |feature, index_usize| { - const this_llvm_name = feature.llvm_name orelse continue; - if (mem.eql(u8, llvm_feat, this_llvm_name)) { - const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); - switch (op) { - .add => result.features.addFeature(index), - .sub => result.features.removeFeature(index), - } - break; - } - } - } - } - - result.features.populateDependencies(all_features); - return result; -} - -export fn stage2_env(argc: c_int, argv: [*]const [*:0]const u8) c_int { - const allocator = std.heap.c_allocator; - - var args_list = argvToArrayList(allocator, argc, argv) catch |err| { - std.debug.print("unable to parse arguments: {}\n", .{@errorName(err)}); - return -1; - }; - defer args_list.deinit(); - - const args = args_list.span()[2..]; - - @import("print_env.zig").cmdEnv(allocator, args, std.io.getStdOut().outStream()) catch |err| { - std.debug.print("unable to print info: {}\n", .{@errorName(err)}); - return -1; - }; - - return 0; -} - -// ABI warning -export fn stage2_cmd_targets( - zig_triple: ?[*:0]const u8, - mcpu: ?[*:0]const u8, - dynamic_linker: ?[*:0]const u8, -) c_int { - cmdTargets(zig_triple, mcpu, dynamic_linker) catch |err| { - std.debug.warn("unable to list targets: {}\n", .{@errorName(err)}); - return -1; - }; - return 0; -} - -fn cmdTargets( - zig_triple_oz: ?[*:0]const u8, - mcpu_oz: ?[*:0]const u8, - dynamic_linker_oz: ?[*:0]const u8, -) !void { - const cross_target = try stage2CrossTarget(zig_triple_oz, mcpu_oz, dynamic_linker_oz); - var dynamic_linker: ?[*:0]u8 = null; - const target = try crossTargetToTarget(cross_target, &dynamic_linker); - return @import("print_targets.zig").cmdTargets( - std.heap.c_allocator, - &[0][]u8{}, - std.io.getStdOut().outStream(), - target, - ); -} - -// ABI warning -export fn stage2_target_parse( - target: *Stage2Target, - zig_triple: ?[*:0]const u8, - mcpu: ?[*:0]const u8, - dynamic_linker: ?[*:0]const u8, -) Error { - stage2TargetParse(target, zig_triple, mcpu, dynamic_linker) catch |err| switch (err) { - error.OutOfMemory => return .OutOfMemory, - error.UnknownArchitecture => return .UnknownArchitecture, - error.UnknownOperatingSystem => return .UnknownOperatingSystem, - error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface, - error.MissingOperatingSystem => return .MissingOperatingSystem, - error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, - error.UnexpectedExtraField => return .SemanticAnalyzeFail, - error.InvalidAbiVersion => return .InvalidAbiVersion, - error.InvalidOperatingSystemVersion => return .InvalidOperatingSystemVersion, - error.FileSystem => return .FileSystem, - error.SymLinkLoop => return .SymLinkLoop, - error.SystemResources => return .SystemResources, - error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, - error.DeviceBusy => return .DeviceBusy, - }; - return .None; -} - -fn stage2CrossTarget( - zig_triple_oz: ?[*:0]const u8, - mcpu_oz: ?[*:0]const u8, - dynamic_linker_oz: ?[*:0]const u8, -) !CrossTarget { - const mcpu = mem.spanZ(mcpu_oz); - const dynamic_linker = mem.spanZ(dynamic_linker_oz); - var diags: CrossTarget.ParseOptions.Diagnostics = .{}; - const target: CrossTarget = CrossTarget.parse(.{ - .arch_os_abi = mem.spanZ(zig_triple_oz) orelse "native", - .cpu_features = mcpu, - .dynamic_linker = dynamic_linker, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpuModel => { - std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ - diags.cpu_name.?, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allCpuModels()) |cpu| { - std.debug.warn(" {}\n", .{cpu.name}); - } - process.exit(1); - }, - error.UnknownCpuFeature => { - std.debug.warn( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': - \\ - , .{ - diags.unknown_feature_name, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allFeaturesList()) |feature| { - std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); - } - process.exit(1); - }, - else => |e| return e, - }; - - return target; -} - -fn stage2TargetParse( - stage1_target: *Stage2Target, - zig_triple_oz: ?[*:0]const u8, - mcpu_oz: ?[*:0]const u8, - dynamic_linker_oz: ?[*:0]const u8, -) !void { - const target = try stage2CrossTarget(zig_triple_oz, mcpu_oz, dynamic_linker_oz); - try stage1_target.fromTarget(target); -} - -// ABI warning -const Stage2LibCInstallation = extern struct { - include_dir: [*]const u8, - include_dir_len: usize, - sys_include_dir: [*]const u8, - sys_include_dir_len: usize, - crt_dir: [*]const u8, - crt_dir_len: usize, - msvc_lib_dir: [*]const u8, - msvc_lib_dir_len: usize, - kernel32_lib_dir: [*]const u8, - kernel32_lib_dir_len: usize, - - fn initFromStage2(self: *Stage2LibCInstallation, libc: LibCInstallation) void { - if (libc.include_dir) |s| { - self.include_dir = s.ptr; - self.include_dir_len = s.len; - } else { - self.include_dir = ""; - self.include_dir_len = 0; - } - if (libc.sys_include_dir) |s| { - self.sys_include_dir = s.ptr; - self.sys_include_dir_len = s.len; - } else { - self.sys_include_dir = ""; - self.sys_include_dir_len = 0; - } - if (libc.crt_dir) |s| { - self.crt_dir = s.ptr; - self.crt_dir_len = s.len; - } else { - self.crt_dir = ""; - self.crt_dir_len = 0; - } - if (libc.msvc_lib_dir) |s| { - self.msvc_lib_dir = s.ptr; - self.msvc_lib_dir_len = s.len; - } else { - self.msvc_lib_dir = ""; - self.msvc_lib_dir_len = 0; - } - if (libc.kernel32_lib_dir) |s| { - self.kernel32_lib_dir = s.ptr; - self.kernel32_lib_dir_len = s.len; - } else { - self.kernel32_lib_dir = ""; - self.kernel32_lib_dir_len = 0; - } - } - - fn toStage2(self: Stage2LibCInstallation) LibCInstallation { - var libc: LibCInstallation = .{}; - if (self.include_dir_len != 0) { - libc.include_dir = self.include_dir[0..self.include_dir_len]; - } - if (self.sys_include_dir_len != 0) { - libc.sys_include_dir = self.sys_include_dir[0..self.sys_include_dir_len]; - } - if (self.crt_dir_len != 0) { - libc.crt_dir = self.crt_dir[0..self.crt_dir_len]; - } - if (self.msvc_lib_dir_len != 0) { - libc.msvc_lib_dir = self.msvc_lib_dir[0..self.msvc_lib_dir_len]; - } - if (self.kernel32_lib_dir_len != 0) { - libc.kernel32_lib_dir = self.kernel32_lib_dir[0..self.kernel32_lib_dir_len]; - } - return libc; - } -}; - -// ABI warning -export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error { - const libc_file = mem.spanZ(libc_file_z); - var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file) catch |err| switch (err) { - error.ParseError => return .SemanticAnalyzeFail, - error.FileTooBig => return .FileTooBig, - error.InputOutput => return .FileSystem, - error.NoSpaceLeft => return .NoSpaceLeft, - error.AccessDenied => return .AccessDenied, - error.BrokenPipe => return .BrokenPipe, - error.SystemResources => return .SystemResources, - error.OperationAborted => return .OperationAborted, - error.WouldBlock => unreachable, - error.NotOpenForReading => unreachable, - error.Unexpected => return .Unexpected, - error.IsDir => return .IsDir, - error.ConnectionResetByPeer => unreachable, - error.ConnectionTimedOut => unreachable, - error.OutOfMemory => return .OutOfMemory, - error.Unseekable => unreachable, - error.SharingViolation => return .SharingViolation, - error.PathAlreadyExists => unreachable, - error.FileNotFound => return .FileNotFound, - error.PipeBusy => return .PipeBusy, - error.NameTooLong => return .PathTooLong, - error.InvalidUtf8 => return .BadPathName, - error.BadPathName => return .BadPathName, - error.SymLinkLoop => return .SymLinkLoop, - error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, - error.NoDevice => return .NoDevice, - error.NotDir => return .NotDir, - error.DeviceBusy => return .DeviceBusy, - error.FileLocksNotSupported => unreachable, - }; - stage1_libc.initFromStage2(libc); - return .None; -} - -// ABI warning -export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error { - var libc = LibCInstallation.findNative(.{ - .allocator = std.heap.c_allocator, - .verbose = true, - }) catch |err| switch (err) { - error.OutOfMemory => return .OutOfMemory, - error.FileSystem => return .FileSystem, - error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler, - error.CCompilerExitCode => return .CCompilerExitCode, - error.CCompilerCrashed => return .CCompilerCrashed, - error.CCompilerCannotFindHeaders => return .CCompilerCannotFindHeaders, - error.LibCRuntimeNotFound => return .LibCRuntimeNotFound, - error.LibCStdLibHeaderNotFound => return .LibCStdLibHeaderNotFound, - error.LibCKernel32LibNotFound => return .LibCKernel32LibNotFound, - error.UnsupportedArchitecture => return .UnsupportedArchitecture, - error.WindowsSdkNotFound => return .WindowsSdkNotFound, - error.ZigIsTheCCompiler => return .ZigIsTheCCompiler, - }; - stage1_libc.initFromStage2(libc); - return .None; -} - -// ABI warning -export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error { - var libc = stage1_libc.toStage2(); - const c_out_stream = std.io.cOutStream(output_file); - libc.render(c_out_stream) catch |err| switch (err) { - error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode - error.NotOpenForWriting => unreachable, - error.SystemResources => return .SystemResources, - error.OperationAborted => return .OperationAborted, - error.BrokenPipe => return .BrokenPipe, - error.DiskQuota => return .DiskQuota, - error.FileTooBig => return .FileTooBig, - error.NoSpaceLeft => return .NoSpaceLeft, - error.AccessDenied => return .AccessDenied, - error.Unexpected => return .Unexpected, - error.InputOutput => return .FileSystem, - }; - return .None; -} - -// ABI warning -const Stage2Target = extern struct { - arch: c_int, - vendor: c_int, - - abi: c_int, - os: c_int, - - is_native_os: bool, - is_native_cpu: bool, - - glibc_or_darwin_version: ?*Stage2SemVer, - - llvm_cpu_name: ?[*:0]const u8, - llvm_cpu_features: ?[*:0]const u8, - cpu_builtin_str: ?[*:0]const u8, - cache_hash: ?[*:0]const u8, - cache_hash_len: usize, - os_builtin_str: ?[*:0]const u8, - - dynamic_linker: ?[*:0]const u8, - standard_dynamic_linker_path: ?[*:0]const u8, - - llvm_cpu_features_asm_ptr: [*]const [*:0]const u8, - llvm_cpu_features_asm_len: usize, - - fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void { - const allocator = std.heap.c_allocator; - - var dynamic_linker: ?[*:0]u8 = null; - const target = try crossTargetToTarget(cross_target, &dynamic_linker); - - var cache_hash = try std.ArrayListSentineled(u8, 0).allocPrint(allocator, "{}\n{}\n", .{ - target.cpu.model.name, - target.cpu.features.asBytes(), - }); - defer cache_hash.deinit(); - - const generic_arch_name = target.cpu.arch.genericName(); - var cpu_builtin_str_buffer = try std.ArrayListSentineled(u8, 0).allocPrint(allocator, - \\Cpu{{ - \\ .arch = .{}, - \\ .model = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ - \\ - , .{ - @tagName(target.cpu.arch), - generic_arch_name, - target.cpu.model.name, - generic_arch_name, - generic_arch_name, - }); - defer cpu_builtin_str_buffer.deinit(); - - var llvm_features_buffer = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0); - defer llvm_features_buffer.deinit(); - - // Unfortunately we have to do the work twice, because Clang does not support - // the same command line parameters for CPU features when assembling code as it does - // when compiling C code. - var asm_features_list = std.ArrayList([*:0]const u8).init(allocator); - defer asm_features_list.deinit(); - - for (target.cpu.arch.allFeaturesList()) |feature, index_usize| { - const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); - const is_enabled = target.cpu.features.isEnabled(index); - - if (feature.llvm_name) |llvm_name| { - const plus_or_minus = "-+"[@boolToInt(is_enabled)]; - try llvm_features_buffer.append(plus_or_minus); - try llvm_features_buffer.appendSlice(llvm_name); - try llvm_features_buffer.appendSlice(","); - } - - if (is_enabled) { - // TODO some kind of "zig identifier escape" function rather than - // unconditionally using @"" syntax - try cpu_builtin_str_buffer.appendSlice(" .@\""); - try cpu_builtin_str_buffer.appendSlice(feature.name); - try cpu_builtin_str_buffer.appendSlice("\",\n"); - } - } - - switch (target.cpu.arch) { - .riscv32, .riscv64 => { - if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { - try asm_features_list.append("-mrelax"); - } else { - try asm_features_list.append("-mno-relax"); - } - }, - else => { - // TODO - // Argh, why doesn't the assembler accept the list of CPU features?! - // I don't see a way to do this other than hard coding everything. - }, - } - - try cpu_builtin_str_buffer.appendSlice( - \\ }), - \\}; - \\ - ); - - assert(mem.endsWith(u8, llvm_features_buffer.span(), ",")); - llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); - - var os_builtin_str_buffer = try std.ArrayListSentineled(u8, 0).allocPrint(allocator, - \\Os{{ - \\ .tag = .{}, - \\ .version_range = .{{ - , .{@tagName(target.os.tag)}); - defer os_builtin_str_buffer.deinit(); - - // We'll re-use the OS version range builtin string for the cache hash. - const os_builtin_str_ver_start_index = os_builtin_str_buffer.len(); - - @setEvalBranchQuota(2000); - switch (target.os.tag) { - .freestanding, - .ananas, - .cloudabi, - .dragonfly, - .fuchsia, - .ios, - .kfreebsd, - .lv2, - .solaris, - .haiku, - .minix, - .rtems, - .nacl, - .cnk, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .elfiamcu, - .tvos, - .watchos, - .mesa3d, - .contiki, - .amdpal, - .hermit, - .hurd, - .wasi, - .emscripten, - .uefi, - .other, - => try os_builtin_str_buffer.appendSlice(" .none = {} }\n"), - - .freebsd, - .macosx, - .netbsd, - .openbsd, - => try os_builtin_str_buffer.outStream().print( - \\ .semver = .{{ - \\ .min = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ .max = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ }}}}, - \\ - , .{ - target.os.version_range.semver.min.major, - target.os.version_range.semver.min.minor, - target.os.version_range.semver.min.patch, - - target.os.version_range.semver.max.major, - target.os.version_range.semver.max.minor, - target.os.version_range.semver.max.patch, - }), - - .linux => try os_builtin_str_buffer.outStream().print( - \\ .linux = .{{ - \\ .range = .{{ - \\ .min = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ .max = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ }}, - \\ .glibc = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ }}}}, - \\ - , .{ - target.os.version_range.linux.range.min.major, - target.os.version_range.linux.range.min.minor, - target.os.version_range.linux.range.min.patch, - - target.os.version_range.linux.range.max.major, - target.os.version_range.linux.range.max.minor, - target.os.version_range.linux.range.max.patch, - - target.os.version_range.linux.glibc.major, - target.os.version_range.linux.glibc.minor, - target.os.version_range.linux.glibc.patch, - }), - - .windows => try os_builtin_str_buffer.outStream().print( - \\ .windows = .{{ - \\ .min = {s}, - \\ .max = {s}, - \\ }}}}, - \\ - , .{ - target.os.version_range.windows.min, - target.os.version_range.windows.max, - }), - } - try os_builtin_str_buffer.appendSlice("};\n"); - - try cache_hash.appendSlice( - os_builtin_str_buffer.span()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()], - ); - - const glibc_or_darwin_version = blk: { - if (target.isGnuLibC()) { - const stage1_glibc = try std.heap.c_allocator.create(Stage2SemVer); - const stage2_glibc = target.os.version_range.linux.glibc; - stage1_glibc.* = .{ - .major = stage2_glibc.major, - .minor = stage2_glibc.minor, - .patch = stage2_glibc.patch, - }; - break :blk stage1_glibc; - } else if (target.isDarwin()) { - const stage1_semver = try std.heap.c_allocator.create(Stage2SemVer); - const stage2_semver = target.os.version_range.semver.min; - stage1_semver.* = .{ - .major = stage2_semver.major, - .minor = stage2_semver.minor, - .patch = stage2_semver.patch, - }; - break :blk stage1_semver; - } else { - break :blk null; - } - }; - - const std_dl = target.standardDynamicLinkerPath(); - const std_dl_z = if (std_dl.get()) |dl| - (try mem.dupeZ(std.heap.c_allocator, u8, dl)).ptr - else - null; - - const cache_hash_slice = cache_hash.toOwnedSlice(); - const asm_features = asm_features_list.toOwnedSlice(); - self.* = .{ - .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch - .vendor = 0, - .os = @enumToInt(target.os.tag), - .abi = @enumToInt(target.abi), - .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null, - .llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr, - .llvm_cpu_features_asm_ptr = asm_features.ptr, - .llvm_cpu_features_asm_len = asm_features.len, - .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr, - .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, - .cache_hash = cache_hash_slice.ptr, - .cache_hash_len = cache_hash_slice.len, - .is_native_os = cross_target.isNativeOs(), - .is_native_cpu = cross_target.isNativeCpu(), - .glibc_or_darwin_version = glibc_or_darwin_version, - .dynamic_linker = dynamic_linker, - .standard_dynamic_linker_path = std_dl_z, - }; - } -}; - -fn enumInt(comptime Enum: type, int: c_int) Enum { - return @intToEnum(Enum, @intCast(@TagType(Enum), int)); -} - -fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { - var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target); - if (info.cpu_detection_unimplemented) { - // TODO We want to just use detected_info.target but implementing - // CPU model & feature detection is todo so here we rely on LLVM. - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - const arch = std.Target.current.cpu.arch; - info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); - cross_target.updateCpuFeatures(&info.target.cpu.features); - info.target.cpu.arch = cross_target.getCpuArch(); - } - if (info.dynamic_linker.get()) |dl| { - dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); - } else { - dynamic_linker_ptr.* = null; - } - return info.target; -} - -// ABI warning -const Stage2SemVer = extern struct { - major: u32, - minor: u32, - patch: u32, -}; - -// ABI warning -const Stage2NativePaths = extern struct { - include_dirs_ptr: [*][*:0]u8, - include_dirs_len: usize, - lib_dirs_ptr: [*][*:0]u8, - lib_dirs_len: usize, - rpaths_ptr: [*][*:0]u8, - rpaths_len: usize, - warnings_ptr: [*][*:0]u8, - warnings_len: usize, -}; -// ABI warning -export fn stage2_detect_native_paths(stage1_paths: *Stage2NativePaths) Error { - stage2DetectNativePaths(stage1_paths) catch |err| switch (err) { - error.OutOfMemory => return .OutOfMemory, - }; - return .None; -} - -fn stage2DetectNativePaths(stage1_paths: *Stage2NativePaths) !void { - var paths = try std.zig.system.NativePaths.detect(std.heap.c_allocator); - errdefer paths.deinit(); - - try convertSlice(paths.include_dirs.span(), &stage1_paths.include_dirs_ptr, &stage1_paths.include_dirs_len); - try convertSlice(paths.lib_dirs.span(), &stage1_paths.lib_dirs_ptr, &stage1_paths.lib_dirs_len); - try convertSlice(paths.rpaths.span(), &stage1_paths.rpaths_ptr, &stage1_paths.rpaths_len); - try convertSlice(paths.warnings.span(), &stage1_paths.warnings_ptr, &stage1_paths.warnings_len); -} - -fn convertSlice(slice: [][:0]u8, ptr: *[*][*:0]u8, len: *usize) !void { - len.* = slice.len; - const new_slice = try std.heap.c_allocator.alloc([*:0]u8, slice.len); - for (slice) |item, i| { - new_slice[i] = item.ptr; - } - ptr.* = new_slice.ptr; -} - -const clang_args = @import("clang_options.zig").list; - -// ABI warning -pub const ClangArgIterator = extern struct { - has_next: bool, - zig_equivalent: ZigEquivalent, - only_arg: [*:0]const u8, - second_arg: [*:0]const u8, - other_args_ptr: [*]const [*:0]const u8, - other_args_len: usize, - argv_ptr: [*]const [*:0]const u8, - argv_len: usize, - next_index: usize, - root_args: ?*Args, - - // ABI warning - pub const ZigEquivalent = extern enum { - target, - o, - c, - other, - positional, - l, - ignore, - driver_punt, - pic, - no_pic, - nostdlib, - nostdlib_cpp, - shared, - rdynamic, - wl, - pp_or_asm, - optimize, - debug, - sanitize, - linker_script, - verbose_cmds, - for_linker, - linker_input_z, - lib_dir, - mcpu, - dep_file, - framework_dir, - framework, - nostdlibinc, - }; - - const Args = struct { - next_index: usize, - argv_ptr: [*]const [*:0]const u8, - argv_len: usize, - }; - - pub fn init(argv: []const [*:0]const u8) ClangArgIterator { - return .{ - .next_index = 2, // `zig cc foo` this points to `foo` - .has_next = argv.len > 2, - .zig_equivalent = undefined, - .only_arg = undefined, - .second_arg = undefined, - .other_args_ptr = undefined, - .other_args_len = undefined, - .argv_ptr = argv.ptr, - .argv_len = argv.len, - .root_args = null, - }; - } - - pub fn next(self: *ClangArgIterator) !void { - assert(self.has_next); - assert(self.next_index < self.argv_len); - // In this state we know that the parameter we are looking at is a root parameter - // rather than an argument to a parameter. - self.other_args_ptr = self.argv_ptr + self.next_index; - self.other_args_len = 1; // We adjust this value below when necessary. - var arg = mem.span(self.argv_ptr[self.next_index]); - self.incrementArgIndex(); - - if (mem.startsWith(u8, arg, "@")) { - if (self.root_args != null) return error.NestedResponseFile; - - // This is a "compiler response file". We must parse the file and treat its - // contents as command line parameters. - const allocator = std.heap.c_allocator; - const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit - const resp_file_path = arg[1..]; - const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { - std.debug.warn("unable to read response file '{}': {}\n", .{ resp_file_path, @errorName(err) }); - process.exit(1); - }; - defer allocator.free(resp_contents); - // TODO is there a specification for this file format? Let's find it and make this parsing more robust - // at the very least I'm guessing this needs to handle quotes and `#` comments. - var it = mem.tokenize(resp_contents, " \t\r\n"); - var resp_arg_list = std.ArrayList([*:0]const u8).init(allocator); - defer resp_arg_list.deinit(); - { - errdefer { - for (resp_arg_list.span()) |item| { - allocator.free(mem.span(item)); - } - } - while (it.next()) |token| { - const dupe_token = try mem.dupeZ(allocator, u8, token); - errdefer allocator.free(dupe_token); - try resp_arg_list.append(dupe_token); - } - const args = try allocator.create(Args); - errdefer allocator.destroy(args); - args.* = .{ - .next_index = self.next_index, - .argv_ptr = self.argv_ptr, - .argv_len = self.argv_len, - }; - self.root_args = args; - } - const resp_arg_slice = resp_arg_list.toOwnedSlice(); - self.next_index = 0; - self.argv_ptr = resp_arg_slice.ptr; - self.argv_len = resp_arg_slice.len; - - if (resp_arg_slice.len == 0) { - self.resolveRespFileArgs(); - return; - } - - self.has_next = true; - self.other_args_ptr = self.argv_ptr + self.next_index; - self.other_args_len = 1; // We adjust this value below when necessary. - arg = mem.span(self.argv_ptr[self.next_index]); - self.incrementArgIndex(); - } - if (!mem.startsWith(u8, arg, "-")) { - self.zig_equivalent = .positional; - self.only_arg = arg.ptr; - return; - } - - find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) { - .flag => { - const prefix_len = clang_arg.matchEql(arg); - if (prefix_len > 0) { - self.zig_equivalent = clang_arg.zig_equivalent; - self.only_arg = arg.ptr + prefix_len; - - break :find_clang_arg; - } - }, - .joined, .comma_joined => { - // joined example: --target=foo - // comma_joined example: -Wl,-soname,libsoundio.so.2 - const prefix_len = clang_arg.matchStartsWith(arg); - if (prefix_len != 0) { - self.zig_equivalent = clang_arg.zig_equivalent; - self.only_arg = arg.ptr + prefix_len; // This will skip over the "--target=" part. - - break :find_clang_arg; - } - }, - .joined_or_separate => { - // Examples: `-lfoo`, `-l foo` - const prefix_len = clang_arg.matchStartsWith(arg); - if (prefix_len == arg.len) { - if (self.next_index >= self.argv_len) { - std.debug.warn("Expected parameter after '{}'\n", .{arg}); - process.exit(1); - } - self.only_arg = self.argv_ptr[self.next_index]; - self.incrementArgIndex(); - self.other_args_len += 1; - self.zig_equivalent = clang_arg.zig_equivalent; - - break :find_clang_arg; - } else if (prefix_len != 0) { - self.zig_equivalent = clang_arg.zig_equivalent; - self.only_arg = arg.ptr + prefix_len; - - break :find_clang_arg; - } - }, - .joined_and_separate => { - // Example: `-Xopenmp-target=riscv64-linux-unknown foo` - const prefix_len = clang_arg.matchStartsWith(arg); - if (prefix_len != 0) { - self.only_arg = arg.ptr + prefix_len; - if (self.next_index >= self.argv_len) { - std.debug.warn("Expected parameter after '{}'\n", .{arg}); - process.exit(1); - } - self.second_arg = self.argv_ptr[self.next_index]; - self.incrementArgIndex(); - self.other_args_len += 1; - self.zig_equivalent = clang_arg.zig_equivalent; - break :find_clang_arg; - } - }, - .separate => if (clang_arg.matchEql(arg) > 0) { - if (self.next_index >= self.argv_len) { - std.debug.warn("Expected parameter after '{}'\n", .{arg}); - process.exit(1); - } - self.only_arg = self.argv_ptr[self.next_index]; - self.incrementArgIndex(); - self.other_args_len += 1; - self.zig_equivalent = clang_arg.zig_equivalent; - break :find_clang_arg; - }, - .remaining_args_joined => { - const prefix_len = clang_arg.matchStartsWith(arg); - if (prefix_len != 0) { - @panic("TODO"); - } - }, - .multi_arg => if (clang_arg.matchEql(arg) > 0) { - @panic("TODO"); - }, - } - else { - std.debug.warn("Unknown Clang option: '{}'\n", .{arg}); - process.exit(1); - } - } - - fn incrementArgIndex(self: *ClangArgIterator) void { - self.next_index += 1; - self.resolveRespFileArgs(); - } - - fn resolveRespFileArgs(self: *ClangArgIterator) void { - const allocator = std.heap.c_allocator; - if (self.next_index >= self.argv_len) { - if (self.root_args) |root_args| { - self.next_index = root_args.next_index; - self.argv_ptr = root_args.argv_ptr; - self.argv_len = root_args.argv_len; - - allocator.destroy(root_args); - self.root_args = null; - } - if (self.next_index >= self.argv_len) { - self.has_next = false; - } - } - } -}; - -export fn stage2_clang_arg_iterator( - result: *ClangArgIterator, - argc: usize, - argv: [*]const [*:0]const u8, -) void { - result.* = ClangArgIterator.init(argv[0..argc]); -} - -export fn stage2_clang_arg_next(it: *ClangArgIterator) Error { - it.next() catch |err| switch (err) { - error.NestedResponseFile => return .NestedResponseFile, - error.OutOfMemory => return .OutOfMemory, - }; - return .None; -} - -export const stage2_is_zig0 = false; diff --git a/src/Cache.zig b/src/Cache.zig new file mode 100644 index 0000000000..dff6f7e38e --- /dev/null +++ b/src/Cache.zig @@ -0,0 +1,911 @@ +gpa: *Allocator, +manifest_dir: fs.Dir, +hash: HashHelper = .{}, + +const Cache = @This(); +const std = @import("std"); +const crypto = std.crypto; +const fs = std.fs; +const assert = std.debug.assert; +const testing = std.testing; +const mem = std.mem; +const fmt = std.fmt; +const Allocator = std.mem.Allocator; + +/// Be sure to call `Manifest.deinit` after successful initialization. +pub fn obtain(cache: *const Cache) Manifest { + return Manifest{ + .cache = cache, + .hash = cache.hash, + .manifest_file = null, + .manifest_dirty = false, + .hex_digest = undefined, + }; +} + +/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 +pub const bin_digest_len = 16; +pub const hex_digest_len = bin_digest_len * 2; + +const manifest_file_size_max = 50 * 1024 * 1024; + +/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it +/// provides enough collision resistance for the Manifest use cases, while being one of our +/// fastest options right now. +pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); + +/// Initial state, that can be copied. +pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length); + +pub const File = struct { + path: ?[]const u8, + max_file_size: ?usize, + stat: fs.File.Stat, + bin_digest: [bin_digest_len]u8, + contents: ?[]const u8, + + pub fn deinit(self: *File, allocator: *Allocator) void { + if (self.path) |owned_slice| { + allocator.free(owned_slice); + self.path = null; + } + if (self.contents) |contents| { + allocator.free(contents); + self.contents = null; + } + self.* = undefined; + } +}; + +pub const HashHelper = struct { + hasher: Hasher = hasher_init, + + /// Record a slice of bytes as an dependency of the process being cached + pub fn addBytes(hh: *HashHelper, bytes: []const u8) void { + hh.hasher.update(mem.asBytes(&bytes.len)); + hh.hasher.update(bytes); + } + + pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void { + hh.add(optional_bytes != null); + hh.addBytes(optional_bytes orelse return); + } + + pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void { + hh.add(list_of_bytes.len); + for (list_of_bytes) |bytes| hh.addBytes(bytes); + } + + pub fn addStringSet(hh: *HashHelper, hm: std.StringArrayHashMapUnmanaged(void)) void { + const entries = hm.items(); + hh.add(entries.len); + for (entries) |entry| { + hh.addBytes(entry.key); + } + } + + /// Convert the input value into bytes and record it as a dependency of the process being cached. + pub fn add(hh: *HashHelper, x: anytype) void { + switch (@TypeOf(x)) { + std.builtin.Version => { + hh.add(x.major); + hh.add(x.minor); + hh.add(x.patch); + }, + std.Target.Os.TaggedVersionRange => { + switch (x) { + .linux => |linux| { + hh.add(linux.range.min); + hh.add(linux.range.max); + hh.add(linux.glibc); + }, + .windows => |windows| { + hh.add(windows.min); + hh.add(windows.max); + }, + .semver => |semver| { + hh.add(semver.min); + hh.add(semver.max); + }, + .none => {}, + } + }, + else => switch (@typeInfo(@TypeOf(x))) { + .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)), + else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))), + }, + } + } + + pub fn addOptional(hh: *HashHelper, optional: anytype) void { + hh.add(optional != null); + hh.add(optional orelse return); + } + + /// Returns a hex encoded hash of the inputs, without modifying state. + pub fn peek(hh: HashHelper) [hex_digest_len]u8 { + var copy = hh; + return copy.final(); + } + + pub fn peekBin(hh: HashHelper) [bin_digest_len]u8 { + var copy = hh; + var bin_digest: [bin_digest_len]u8 = undefined; + copy.hasher.final(&bin_digest); + return bin_digest; + } + + /// Returns a hex encoded hash of the inputs, mutating the state of the hasher. + pub fn final(hh: *HashHelper) [hex_digest_len]u8 { + var bin_digest: [bin_digest_len]u8 = undefined; + hh.hasher.final(&bin_digest); + + var out_digest: [hex_digest_len]u8 = undefined; + _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable; + return out_digest; + } +}; + +pub const Lock = struct { + manifest_file: fs.File, + + pub fn release(lock: *Lock) void { + lock.manifest_file.close(); + lock.* = undefined; + } +}; + +/// Manifest manages project-local `zig-cache` directories. +/// This is not a general-purpose cache. +/// It is designed to be fast and simple, not to withstand attacks using specially-crafted input. +pub const Manifest = struct { + cache: *const Cache, + /// Current state for incremental hashing. + hash: HashHelper, + manifest_file: ?fs.File, + manifest_dirty: bool, + files: std.ArrayListUnmanaged(File) = .{}, + hex_digest: [hex_digest_len]u8, + + /// Add a file as a dependency of process being cached. When `hit` is + /// called, the file's contents will be checked to ensure that it matches + /// the contents from previous times. + /// + /// Max file size will be used to determine the amount of space to the file contents + /// are allowed to take up in memory. If max_file_size is null, then the contents + /// will not be loaded into memory. + /// + /// Returns the index of the entry in the `files` array list. You can use it + /// to access the contents of the file after calling `hit()` like so: + /// + /// ``` + /// var file_contents = cache_hash.files.items[file_index].contents.?; + /// ``` + pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize { + assert(self.manifest_file == null); + + try self.files.ensureCapacity(self.cache.gpa, self.files.items.len + 1); + const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path}); + + const idx = self.files.items.len; + self.files.addOneAssumeCapacity().* = .{ + .path = resolved_path, + .contents = null, + .max_file_size = max_file_size, + .stat = undefined, + .bin_digest = undefined, + }; + + self.hash.addBytes(resolved_path); + + return idx; + } + + pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { + self.hash.add(optional_file_path != null); + const file_path = optional_file_path orelse return; + _ = try self.addFile(file_path, null); + } + + pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void { + self.hash.add(list_of_files.len); + for (list_of_files) |file_path| { + _ = try self.addFile(file_path, null); + } + } + + /// Check the cache to see if the input exists in it. If it exists, returns `true`. + /// A hex encoding of its hash is available by calling `final`. + /// + /// This function will also acquire an exclusive lock to the manifest file. This means + /// that a process holding a Manifest will block any other process attempting to + /// acquire the lock. + /// + /// The lock on the manifest file is released when `deinit` is called. As another + /// option, one may call `toOwnedLock` to obtain a smaller object which can represent + /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called. + pub fn hit(self: *Manifest) !bool { + assert(self.manifest_file == null); + + const ext = ".txt"; + var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined; + + var bin_digest: [bin_digest_len]u8 = undefined; + self.hash.hasher.final(&bin_digest); + + _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable; + + self.hash.hasher = hasher_init; + self.hash.hasher.update(&bin_digest); + + mem.copy(u8, &manifest_file_path, &self.hex_digest); + manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*; + + if (self.files.items.len != 0) { + self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{ + .read = true, + .truncate = false, + .lock = .Exclusive, + }); + } else { + // If there are no file inputs, we check if the manifest file exists instead of + // comparing the hashes on the files used for the cached item + self.manifest_file = self.cache.manifest_dir.openFile(&manifest_file_path, .{ + .read = true, + .write = true, + .lock = .Exclusive, + }) catch |err| switch (err) { + error.FileNotFound => { + self.manifest_dirty = true; + self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{ + .read = true, + .truncate = false, + .lock = .Exclusive, + }); + return false; + }, + else => |e| return e, + }; + } + + const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.cache.gpa, manifest_file_size_max); + defer self.cache.gpa.free(file_contents); + + const input_file_count = self.files.items.len; + var any_file_changed = false; + var line_iter = mem.tokenize(file_contents, "\n"); + var idx: usize = 0; + while (line_iter.next()) |line| { + defer idx += 1; + + const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: { + const new = try self.files.addOne(self.cache.gpa); + new.* = .{ + .path = null, + .contents = null, + .max_file_size = null, + .stat = undefined, + .bin_digest = undefined, + }; + break :blk new; + }; + + var iter = mem.tokenize(line, " "); + const size = iter.next() orelse return error.InvalidFormat; + const inode = iter.next() orelse return error.InvalidFormat; + const mtime_nsec_str = iter.next() orelse return error.InvalidFormat; + const digest_str = iter.next() orelse return error.InvalidFormat; + const file_path = iter.rest(); + + cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; + cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; + cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; + std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; + + if (file_path.len == 0) { + return error.InvalidFormat; + } + if (cache_hash_file.path) |p| { + if (!mem.eql(u8, file_path, p)) { + return error.InvalidFormat; + } + } + + if (cache_hash_file.path == null) { + cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path); + } + + const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch { + return error.CacheUnavailable; + }; + defer this_file.close(); + + const actual_stat = try this_file.stat(); + const size_match = actual_stat.size == cache_hash_file.stat.size; + const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime; + const inode_match = actual_stat.inode == cache_hash_file.stat.inode; + + if (!size_match or !mtime_match or !inode_match) { + self.manifest_dirty = true; + + cache_hash_file.stat = actual_stat; + + if (isProblematicTimestamp(cache_hash_file.stat.mtime)) { + cache_hash_file.stat.mtime = 0; + cache_hash_file.stat.inode = 0; + } + + var actual_digest: [bin_digest_len]u8 = undefined; + try hashFile(this_file, &actual_digest); + + if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { + cache_hash_file.bin_digest = actual_digest; + // keep going until we have the input file digests + any_file_changed = true; + } + } + + if (!any_file_changed) { + self.hash.hasher.update(&cache_hash_file.bin_digest); + } + } + + if (any_file_changed) { + // cache miss + // keep the manifest file open + self.unhit(bin_digest, input_file_count); + return false; + } + + if (idx < input_file_count) { + self.manifest_dirty = true; + while (idx < input_file_count) : (idx += 1) { + const ch_file = &self.files.items[idx]; + try self.populateFileHash(ch_file); + } + return false; + } + + return true; + } + + pub fn unhit(self: *Manifest, bin_digest: [bin_digest_len]u8, input_file_count: usize) void { + // Reset the hash. + self.hash.hasher = hasher_init; + self.hash.hasher.update(&bin_digest); + + // Remove files not in the initial hash. + for (self.files.items[input_file_count..]) |*file| { + file.deinit(self.cache.gpa); + } + self.files.shrinkRetainingCapacity(input_file_count); + + for (self.files.items) |file| { + self.hash.hasher.update(&file.bin_digest); + } + } + + fn populateFileHash(self: *Manifest, ch_file: *File) !void { + const file = try fs.cwd().openFile(ch_file.path.?, .{}); + defer file.close(); + + ch_file.stat = try file.stat(); + + if (isProblematicTimestamp(ch_file.stat.mtime)) { + ch_file.stat.mtime = 0; + ch_file.stat.inode = 0; + } + + if (ch_file.max_file_size) |max_file_size| { + if (ch_file.stat.size > max_file_size) { + return error.FileTooBig; + } + + const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size)); + errdefer self.cache.gpa.free(contents); + + // Hash while reading from disk, to keep the contents in the cpu cache while + // doing hashing. + var hasher = hasher_init; + var off: usize = 0; + while (true) { + // give me everything you've got, captain + const bytes_read = try file.read(contents[off..]); + if (bytes_read == 0) break; + hasher.update(contents[off..][0..bytes_read]); + off += bytes_read; + } + hasher.final(&ch_file.bin_digest); + + ch_file.contents = contents; + } else { + try hashFile(file, &ch_file.bin_digest); + } + + self.hash.hasher.update(&ch_file.bin_digest); + } + + /// Add a file as a dependency of process being cached, after the initial hash has been + /// calculated. This is useful for processes that don't know the all the files that + /// are depended on ahead of time. For example, a source file that can import other files + /// will need to be recompiled if the imported file is changed. + pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 { + assert(self.manifest_file != null); + + const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path}); + errdefer self.cache.gpa.free(resolved_path); + + const new_ch_file = try self.files.addOne(self.cache.gpa); + new_ch_file.* = .{ + .path = resolved_path, + .max_file_size = max_file_size, + .stat = undefined, + .bin_digest = undefined, + .contents = null, + }; + errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); + + try self.populateFileHash(new_ch_file); + + return new_ch_file.contents.?; + } + + /// Add a file as a dependency of process being cached, after the initial hash has been + /// calculated. This is useful for processes that don't know the all the files that + /// are depended on ahead of time. For example, a source file that can import other files + /// will need to be recompiled if the imported file is changed. + pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { + assert(self.manifest_file != null); + + const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path}); + errdefer self.cache.gpa.free(resolved_path); + + const new_ch_file = try self.files.addOne(self.cache.gpa); + new_ch_file.* = .{ + .path = resolved_path, + .max_file_size = null, + .stat = undefined, + .bin_digest = undefined, + .contents = null, + }; + errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); + + try self.populateFileHash(new_ch_file); + } + + pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void { + assert(self.manifest_file != null); + + const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max); + defer self.cache.gpa.free(dep_file_contents); + + var error_buf = std.ArrayList(u8).init(self.cache.gpa); + defer error_buf.deinit(); + + var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents }; + + // Skip first token: target. + switch (it.next() orelse return) { // Empty dep file OK. + .target, .target_must_resolve, .prereq => {}, + else => |err| { + try err.printError(error_buf.writer()); + std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items }); + return error.InvalidDepFile; + }, + } + // Process 0+ preqreqs. + // Clang is invoked in single-source mode so we never get more targets. + while (true) { + switch (it.next() orelse return) { + .target, .target_must_resolve => return, + .prereq => |bytes| try self.addFilePost(bytes), + else => |err| { + try err.printError(error_buf.writer()); + std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items }); + return error.InvalidDepFile; + }, + } + } + } + + /// Returns a hex encoded hash of the inputs. + pub fn final(self: *Manifest) [hex_digest_len]u8 { + assert(self.manifest_file != null); + + // We don't close the manifest file yet, because we want to + // keep it locked until the API user is done using it. + // We also don't write out the manifest yet, because until + // cache_release is called we still might be working on creating + // the artifacts to cache. + + var bin_digest: [bin_digest_len]u8 = undefined; + self.hash.hasher.final(&bin_digest); + + var out_digest: [hex_digest_len]u8 = undefined; + _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable; + + return out_digest; + } + + pub fn writeManifest(self: *Manifest) !void { + const manifest_file = self.manifest_file.?; + if (!self.manifest_dirty) return; + + var contents = std.ArrayList(u8).init(self.cache.gpa); + defer contents.deinit(); + + const writer = contents.writer(); + var encoded_digest: [hex_digest_len]u8 = undefined; + + for (self.files.items) |file| { + _ = std.fmt.bufPrint(&encoded_digest, "{x}", .{file.bin_digest}) catch unreachable; + try writer.print("{d} {d} {d} {s} {s}\n", .{ + file.stat.size, + file.stat.inode, + file.stat.mtime, + &encoded_digest, + file.path, + }); + } + + try manifest_file.setEndPos(contents.items.len); + try manifest_file.pwriteAll(contents.items, 0); + self.manifest_dirty = false; + } + + /// Obtain only the data needed to maintain a lock on the manifest file. + /// The `Manifest` remains safe to deinit. + /// Don't forget to call `writeManifest` before this! + pub fn toOwnedLock(self: *Manifest) Lock { + const manifest_file = self.manifest_file.?; + self.manifest_file = null; + return Lock{ .manifest_file = manifest_file }; + } + + /// Releases the manifest file and frees any memory the Manifest was using. + /// `Manifest.hit` must be called first. + /// Don't forget to call `writeManifest` before this! + pub fn deinit(self: *Manifest) void { + if (self.manifest_file) |file| { + file.close(); + } + for (self.files.items) |*file| { + file.deinit(self.cache.gpa); + } + self.files.deinit(self.cache.gpa); + } +}; + +fn hashFile(file: fs.File, bin_digest: []u8) !void { + var buf: [1024]u8 = undefined; + + var hasher = hasher_init; + while (true) { + const bytes_read = try file.read(&buf); + if (bytes_read == 0) break; + hasher.update(buf[0..bytes_read]); + } + + hasher.final(bin_digest); +} + +/// If the wall clock time, rounded to the same precision as the +/// mtime, is equal to the mtime, then we cannot rely on this mtime +/// yet. We will instead save an mtime value that indicates the hash +/// must be unconditionally computed. +/// This function recognizes the precision of mtime by looking at trailing +/// zero bits of the seconds and nanoseconds. +fn isProblematicTimestamp(fs_clock: i128) bool { + const wall_clock = std.time.nanoTimestamp(); + + // We have to break the nanoseconds into seconds and remainder nanoseconds + // to detect precision of seconds, because looking at the zero bits in base + // 2 would not detect precision of the seconds value. + const fs_sec = @intCast(i64, @divFloor(fs_clock, std.time.ns_per_s)); + const fs_nsec = @intCast(i64, @mod(fs_clock, std.time.ns_per_s)); + var wall_sec = @intCast(i64, @divFloor(wall_clock, std.time.ns_per_s)); + var wall_nsec = @intCast(i64, @mod(wall_clock, std.time.ns_per_s)); + + // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock. + if (fs_nsec == 0) { + wall_nsec = 0; + if (fs_sec == 0) { + wall_sec = 0; + } else { + wall_sec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_sec)); + } + } else { + wall_nsec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_nsec)); + } + return wall_nsec == fs_nsec and wall_sec == fs_sec; +} + +test "cache file and then recall it" { + if (std.Target.current.os.tag == .wasi) { + // https://github.com/ziglang/zig/issues/5437 + return error.SkipZigTest; + } + const cwd = fs.cwd(); + + const temp_file = "test.txt"; + const temp_manifest_dir = "temp_manifest_dir"; + + const ts = std.time.nanoTimestamp(); + try cwd.writeFile(temp_file, "Hello, world!\n"); + + while (isProblematicTimestamp(ts)) { + std.time.sleep(1); + } + + var digest1: [hex_digest_len]u8 = undefined; + var digest2: [hex_digest_len]u8 = undefined; + + { + var cache = Cache{ + .gpa = testing.allocator, + .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + }; + defer cache.manifest_dir.close(); + + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.add(true); + ch.hash.add(@as(u16, 1234)); + ch.hash.addBytes("1234"); + _ = try ch.addFile(temp_file, null); + + // There should be nothing in the cache + testing.expectEqual(false, try ch.hit()); + + digest1 = ch.final(); + try ch.writeManifest(); + } + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.add(true); + ch.hash.add(@as(u16, 1234)); + ch.hash.addBytes("1234"); + _ = try ch.addFile(temp_file, null); + + // Cache hit! We just "built" the same file + testing.expect(try ch.hit()); + digest2 = ch.final(); + + try ch.writeManifest(); + } + + testing.expectEqual(digest1, digest2); + } + + try cwd.deleteTree(temp_manifest_dir); + try cwd.deleteFile(temp_file); +} + +test "give problematic timestamp" { + var fs_clock = std.time.nanoTimestamp(); + // to make it problematic, we make it only accurate to the second + fs_clock = @divTrunc(fs_clock, std.time.ns_per_s); + fs_clock *= std.time.ns_per_s; + testing.expect(isProblematicTimestamp(fs_clock)); +} + +test "give nonproblematic timestamp" { + testing.expect(!isProblematicTimestamp(std.time.nanoTimestamp() - std.time.ns_per_s)); +} + +test "check that changing a file makes cache fail" { + if (std.Target.current.os.tag == .wasi) { + // https://github.com/ziglang/zig/issues/5437 + return error.SkipZigTest; + } + const cwd = fs.cwd(); + + const temp_file = "cache_hash_change_file_test.txt"; + const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; + const original_temp_file_contents = "Hello, world!\n"; + const updated_temp_file_contents = "Hello, world; but updated!\n"; + + try cwd.deleteTree(temp_manifest_dir); + try cwd.deleteTree(temp_file); + + const ts = std.time.nanoTimestamp(); + try cwd.writeFile(temp_file, original_temp_file_contents); + + while (isProblematicTimestamp(ts)) { + std.time.sleep(1); + } + + var digest1: [hex_digest_len]u8 = undefined; + var digest2: [hex_digest_len]u8 = undefined; + + { + var cache = Cache{ + .gpa = testing.allocator, + .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + }; + defer cache.manifest_dir.close(); + + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + const temp_file_idx = try ch.addFile(temp_file, 100); + + // There should be nothing in the cache + testing.expectEqual(false, try ch.hit()); + + testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?)); + + digest1 = ch.final(); + + try ch.writeManifest(); + } + + try cwd.writeFile(temp_file, updated_temp_file_contents); + + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + const temp_file_idx = try ch.addFile(temp_file, 100); + + // A file that we depend on has been updated, so the cache should not contain an entry for it + testing.expectEqual(false, try ch.hit()); + + // The cache system does not keep the contents of re-hashed input files. + testing.expect(ch.files.items[temp_file_idx].contents == null); + + digest2 = ch.final(); + + try ch.writeManifest(); + } + + testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); + } + + try cwd.deleteTree(temp_manifest_dir); + try cwd.deleteTree(temp_file); +} + +test "no file inputs" { + if (std.Target.current.os.tag == .wasi) { + // https://github.com/ziglang/zig/issues/5437 + return error.SkipZigTest; + } + const cwd = fs.cwd(); + const temp_manifest_dir = "no_file_inputs_manifest_dir"; + defer cwd.deleteTree(temp_manifest_dir) catch {}; + + var digest1: [hex_digest_len]u8 = undefined; + var digest2: [hex_digest_len]u8 = undefined; + + var cache = Cache{ + .gpa = testing.allocator, + .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + }; + defer cache.manifest_dir.close(); + + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + + // There should be nothing in the cache + testing.expectEqual(false, try ch.hit()); + + digest1 = ch.final(); + + try ch.writeManifest(); + } + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + + testing.expect(try ch.hit()); + digest2 = ch.final(); + try ch.writeManifest(); + } + + testing.expectEqual(digest1, digest2); +} + +test "Manifest with files added after initial hash work" { + if (std.Target.current.os.tag == .wasi) { + // https://github.com/ziglang/zig/issues/5437 + return error.SkipZigTest; + } + const cwd = fs.cwd(); + + const temp_file1 = "cache_hash_post_file_test1.txt"; + const temp_file2 = "cache_hash_post_file_test2.txt"; + const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; + + const ts1 = std.time.nanoTimestamp(); + try cwd.writeFile(temp_file1, "Hello, world!\n"); + try cwd.writeFile(temp_file2, "Hello world the second!\n"); + + while (isProblematicTimestamp(ts1)) { + std.time.sleep(1); + } + + var digest1: [hex_digest_len]u8 = undefined; + var digest2: [hex_digest_len]u8 = undefined; + var digest3: [hex_digest_len]u8 = undefined; + + { + var cache = Cache{ + .gpa = testing.allocator, + .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + }; + defer cache.manifest_dir.close(); + + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + _ = try ch.addFile(temp_file1, null); + + // There should be nothing in the cache + testing.expectEqual(false, try ch.hit()); + + _ = try ch.addFilePost(temp_file2); + + digest1 = ch.final(); + try ch.writeManifest(); + } + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + _ = try ch.addFile(temp_file1, null); + + testing.expect(try ch.hit()); + digest2 = ch.final(); + + try ch.writeManifest(); + } + testing.expect(mem.eql(u8, &digest1, &digest2)); + + // Modify the file added after initial hash + const ts2 = std.time.nanoTimestamp(); + try cwd.writeFile(temp_file2, "Hello world the second, updated\n"); + + while (isProblematicTimestamp(ts2)) { + std.time.sleep(1); + } + + { + var ch = cache.obtain(); + defer ch.deinit(); + + ch.hash.addBytes("1234"); + _ = try ch.addFile(temp_file1, null); + + // A file that we depend on has been updated, so the cache should not contain an entry for it + testing.expectEqual(false, try ch.hit()); + + _ = try ch.addFilePost(temp_file2); + + digest3 = ch.final(); + + try ch.writeManifest(); + } + + testing.expect(!mem.eql(u8, &digest1, &digest3)); + } + + try cwd.deleteTree(temp_manifest_dir); + try cwd.deleteFile(temp_file1); + try cwd.deleteFile(temp_file2); +} diff --git a/src/Compilation.zig b/src/Compilation.zig new file mode 100644 index 0000000000..623635a6b0 --- /dev/null +++ b/src/Compilation.zig @@ -0,0 +1,2882 @@ +const Compilation = @This(); + +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const log = std.log.scoped(.compilation); +const Target = std.Target; + +const Value = @import("value.zig").Value; +const target_util = @import("target.zig"); +const Package = @import("Package.zig"); +const link = @import("link.zig"); +const trace = @import("tracy.zig").trace; +const liveness = @import("liveness.zig"); +const build_options = @import("build_options"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const glibc = @import("glibc.zig"); +const musl = @import("musl.zig"); +const mingw = @import("mingw.zig"); +const libunwind = @import("libunwind.zig"); +const libcxx = @import("libcxx.zig"); +const fatal = @import("main.zig").fatal; +const Module = @import("Module.zig"); +const Cache = @import("Cache.zig"); +const stage1 = @import("stage1.zig"); +const translate_c = @import("translate_c.zig"); + +/// General-purpose allocator. Used for both temporary and long-term storage. +gpa: *Allocator, +/// Arena-allocated memory used during initialization. Should be untouched until deinit. +arena_state: std.heap.ArenaAllocator.State, +bin_file: *link.File, +c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, +stage1_lock: ?Cache.Lock = null, +stage1_cache_manifest: *Cache.Manifest = undefined, + +link_error_flags: link.File.ErrorFlags = .{}, + +work_queue: std.fifo.LinearFifo(Job, .Dynamic), + +/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. +failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{}, + +keep_source_files_loaded: bool, +use_clang: bool, +sanitize_c: bool, +/// When this is `true` it means invoking clang as a sub-process is expected to inherit +/// stdin, stdout, stderr, and if it returns non success, to forward the exit code. +/// Otherwise we attempt to parse the error messages and expose them via the Compilation API. +/// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`. +clang_passthrough_mode: bool, +clang_preprocessor_mode: ClangPreprocessorMode, +/// Whether to print clang argvs to stdout. +verbose_cc: bool, +verbose_tokenize: bool, +verbose_ast: bool, +verbose_ir: bool, +verbose_llvm_ir: bool, +verbose_cimport: bool, +verbose_llvm_cpu_features: bool, +disable_c_depfile: bool, +time_report: bool, +stack_report: bool, + +c_source_files: []const CSourceFile, +clang_argv: []const []const u8, +cache_parent: *Cache, +/// Path to own executable for invoking `zig clang`. +self_exe_path: ?[]const u8, +zig_lib_directory: Directory, +local_cache_directory: Directory, +global_cache_directory: Directory, +libc_include_dir_list: []const []const u8, +rand: *std.rand.Random, + +/// Populated when we build the libc++ static library. A Job to build this is placed in the queue +/// and resolved before calling linker.flush(). +libcxx_static_lib: ?CRTFile = null, +/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue +/// and resolved before calling linker.flush(). +libcxxabi_static_lib: ?CRTFile = null, +/// Populated when we build the libunwind static library. A Job to build this is placed in the queue +/// and resolved before calling linker.flush(). +libunwind_static_lib: ?CRTFile = null, +/// Populated when we build the libc static library. A Job to build this is placed in the queue +/// and resolved before calling linker.flush(). +libc_static_lib: ?CRTFile = null, +/// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue +/// and resolved before calling linker.flush(). +compiler_rt_static_lib: ?CRTFile = null, + +glibc_so_files: ?glibc.BuiltSharedObjects = null, + +/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, +/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. +/// The key is the basename, and the value is the absolute path to the completed build artifact. +crt_files: std.StringHashMapUnmanaged(CRTFile) = .{}, + +/// Keeping track of this possibly open resource so we can close it later. +owned_link_dir: ?std.fs.Dir, + +/// This is for stage1 and should be deleted upon completion of self-hosting. +/// Don't use this for anything other than stage1 compatibility. +color: @import("main.zig").Color = .Auto, + +test_filter: ?[]const u8, +test_name_prefix: ?[]const u8, +test_evented_io: bool, + +emit_h: ?EmitLoc, +emit_asm: ?EmitLoc, +emit_llvm_ir: ?EmitLoc, +emit_analysis: ?EmitLoc, +emit_docs: ?EmitLoc, + +pub const InnerError = Module.InnerError; + +pub const CRTFile = struct { + lock: Cache.Lock, + full_object_path: []const u8, + + fn deinit(self: *CRTFile, gpa: *Allocator) void { + self.lock.release(); + gpa.free(self.full_object_path); + self.* = undefined; + } +}; + +/// For passing to a C compiler. +pub const CSourceFile = struct { + src_path: []const u8, + extra_flags: []const []const u8 = &[0][]const u8{}, +}; + +const Job = union(enum) { + /// Write the machine code for a Decl to the output file. + codegen_decl: *Module.Decl, + /// The Decl needs to be analyzed and possibly export itself. + /// It may have already be analyzed, or it may have been determined + /// to be outdated; in this case perform semantic analysis again. + analyze_decl: *Module.Decl, + /// The source file containing the Decl has been updated, and so the + /// Decl may need its line number information updated in the debug info. + update_line_number: *Module.Decl, + /// Invoke the Clang compiler to create an object file, which gets linked + /// with the Compilation. + c_object: *CObject, + + /// one of the glibc static objects + glibc_crt_file: glibc.CRTFile, + /// all of the glibc shared objects + glibc_shared_objects, + /// one of the musl static objects + musl_crt_file: musl.CRTFile, + /// one of the mingw-w64 static objects + mingw_crt_file: mingw.CRTFile, + /// libunwind.a, usually needed when linking libc + libunwind: void, + libcxx: void, + libcxxabi: void, + /// needed when producing a dynamic library or executable + libcompiler_rt: void, + /// needed when not linking libc and using LLVM for code generation because it generates + /// calls to, for example, memcpy and memset. + zig_libc: void, + + /// Generate builtin.zig source code and write it into the correct place. + generate_builtin_zig: void, + /// Use stage1 C++ code to compile zig code into an object file. + stage1_module: void, + + /// The value is the index into `link.File.Options.system_libs`. + windows_import_lib: usize, +}; + +pub const CObject = struct { + /// Relative to cwd. Owned by arena. + src: CSourceFile, + status: union(enum) { + new, + success: struct { + /// The outputted result. Owned by gpa. + object_path: []u8, + /// This is a file system lock on the cache hash manifest representing this + /// object. It prevents other invocations of the Zig compiler from interfering + /// with this object until released. + lock: Cache.Lock, + }, + /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects. + failure, + }, + + /// Returns if there was failure. + pub fn clearStatus(self: *CObject, gpa: *Allocator) bool { + switch (self.status) { + .new => return false, + .failure => { + self.status = .new; + return true; + }, + .success => |*success| { + gpa.free(success.object_path); + success.lock.release(); + self.status = .new; + return false; + }, + } + } + + pub fn destroy(self: *CObject, gpa: *Allocator) void { + _ = self.clearStatus(gpa); + gpa.destroy(self); + } +}; + +pub const AllErrors = struct { + arena: std.heap.ArenaAllocator.State, + list: []const Message, + + pub const Message = struct { + src_path: []const u8, + line: usize, + column: usize, + byte_offset: usize, + msg: []const u8, + + pub fn renderToStdErr(self: Message) void { + std.debug.print("{}:{}:{}: error: {}\n", .{ + self.src_path, + self.line + 1, + self.column + 1, + self.msg, + }); + } + }; + + pub fn deinit(self: *AllErrors, gpa: *Allocator) void { + self.arena.promote(gpa).deinit(); + } + + fn add( + arena: *std.heap.ArenaAllocator, + errors: *std.ArrayList(Message), + sub_file_path: []const u8, + source: []const u8, + simple_err_msg: ErrorMsg, + ) !void { + const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); + try errors.append(.{ + .src_path = try arena.allocator.dupe(u8, sub_file_path), + .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), + .byte_offset = simple_err_msg.byte_offset, + .line = loc.line, + .column = loc.column, + }); + } +}; + +pub const Directory = struct { + /// This field is redundant for operations that can act on the open directory handle + /// directly, but it is needed when passing the directory to a child process. + /// `null` means cwd. + path: ?[]const u8, + handle: std.fs.Dir, + + pub fn join(self: Directory, allocator: *Allocator, paths: []const []const u8) ![]u8 { + if (self.path) |p| { + // TODO clean way to do this with only 1 allocation + const part2 = try std.fs.path.join(allocator, paths); + defer allocator.free(part2); + return std.fs.path.join(allocator, &[_][]const u8{ p, part2 }); + } else { + return std.fs.path.join(allocator, paths); + } + } +}; + +pub const EmitLoc = struct { + /// If this is `null` it means the file will be output to the cache directory. + /// When provided, both the open file handle and the path name must outlive the `Compilation`. + directory: ?Compilation.Directory, + /// This may not have sub-directories in it. + basename: []const u8, +}; + +pub const ClangPreprocessorMode = enum { + no, + /// This means we are doing `zig cc -E -o `. + yes, + /// This means we are doing `zig cc -E`. + stdout, +}; + +pub const InitOptions = struct { + zig_lib_directory: Directory, + local_cache_directory: Directory, + global_cache_directory: Directory, + target: Target, + root_name: []const u8, + root_pkg: ?*Package, + output_mode: std.builtin.OutputMode, + rand: *std.rand.Random, + dynamic_linker: ?[]const u8 = null, + /// `null` means to not emit a binary file. + emit_bin: ?EmitLoc, + /// `null` means to not emit a C header file. + emit_h: ?EmitLoc = null, + /// `null` means to not emit assembly. + emit_asm: ?EmitLoc = null, + /// `null` means to not emit LLVM IR. + emit_llvm_ir: ?EmitLoc = null, + /// `null` means to not emit semantic analysis JSON. + emit_analysis: ?EmitLoc = null, + /// `null` means to not emit docs. + emit_docs: ?EmitLoc = null, + link_mode: ?std.builtin.LinkMode = null, + dll_export_fns: ?bool = false, + /// Normally when using LLD to link, Zig uses a file named "lld.id" in the + /// same directory as the output binary which contains the hash of the link + /// operation, allowing Zig to skip linking when the hash would be unchanged. + /// In the case that the output binary is being emitted into a directory which + /// is externally modified - essentially anything other than zig-cache - then + /// this flag would be set to disable this machinery to avoid false positives. + disable_lld_caching: bool = false, + object_format: ?std.builtin.ObjectFormat = null, + optimize_mode: std.builtin.Mode = .Debug, + keep_source_files_loaded: bool = false, + clang_argv: []const []const u8 = &[0][]const u8{}, + lld_argv: []const []const u8 = &[0][]const u8{}, + lib_dirs: []const []const u8 = &[0][]const u8{}, + rpath_list: []const []const u8 = &[0][]const u8{}, + c_source_files: []const CSourceFile = &[0]CSourceFile{}, + link_objects: []const []const u8 = &[0][]const u8{}, + framework_dirs: []const []const u8 = &[0][]const u8{}, + frameworks: []const []const u8 = &[0][]const u8{}, + system_libs: []const []const u8 = &[0][]const u8{}, + link_libc: bool = false, + link_libcpp: bool = false, + want_pic: ?bool = null, + want_sanitize_c: ?bool = null, + want_stack_check: ?bool = null, + want_valgrind: ?bool = null, + use_llvm: ?bool = null, + use_lld: ?bool = null, + use_clang: ?bool = null, + rdynamic: bool = false, + strip: bool = false, + single_threaded: bool = false, + function_sections: bool = false, + is_native_os: bool, + time_report: bool = false, + stack_report: bool = false, + link_eh_frame_hdr: bool = false, + linker_script: ?[]const u8 = null, + version_script: ?[]const u8 = null, + override_soname: ?[]const u8 = null, + linker_gc_sections: ?bool = null, + linker_allow_shlib_undefined: ?bool = null, + linker_bind_global_refs_locally: ?bool = null, + each_lib_rpath: ?bool = null, + disable_c_depfile: bool = false, + linker_z_nodelete: bool = false, + linker_z_defs: bool = false, + clang_passthrough_mode: bool = false, + verbose_cc: bool = false, + verbose_link: bool = false, + verbose_tokenize: bool = false, + verbose_ast: bool = false, + verbose_ir: bool = false, + verbose_llvm_ir: bool = false, + verbose_cimport: bool = false, + verbose_llvm_cpu_features: bool = false, + is_test: bool = false, + test_evented_io: bool = false, + is_compiler_rt_or_libc: bool = false, + parent_compilation_link_libc: bool = false, + stack_size_override: ?u64 = null, + self_exe_path: ?[]const u8 = null, + version: ?std.builtin.Version = null, + libc_installation: ?*const LibCInstallation = null, + machine_code_model: std.builtin.CodeModel = .default, + clang_preprocessor_mode: ClangPreprocessorMode = .no, + /// This is for stage1 and should be deleted upon completion of self-hosting. + color: @import("main.zig").Color = .Auto, + test_filter: ?[]const u8 = null, + test_name_prefix: ?[]const u8 = null, + subsystem: ?std.Target.SubSystem = null, +}; + +pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { + const is_dyn_lib = switch (options.output_mode) { + .Obj, .Exe => false, + .Lib => (options.link_mode orelse .Static) == .Dynamic, + }; + const is_exe_or_dyn_lib = switch (options.output_mode) { + .Obj => false, + .Lib => is_dyn_lib, + .Exe => true, + }; + const comp: *Compilation = comp: { + // For allocations that have the same lifetime as Compilation. This arena is used only during this + // initialization and then is freed in deinit(). + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + errdefer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + // We put the `Compilation` itself in the arena. Freeing the arena will free the module. + // It's initialized later after we prepare the initialization options. + const comp = try arena.create(Compilation); + const root_name = try arena.dupe(u8, options.root_name); + + const ofmt = options.object_format orelse options.target.getObjectFormat(); + + // Make a decision on whether to use LLVM or our own backend. + const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { + // If we have no zig code to compile, no need for LLVM. + if (options.root_pkg == null) + break :blk false; + + // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend + // to compile zig code. + if (build_options.is_stage1) + break :blk true; + + // We would want to prefer LLVM for release builds when it is available, however + // we don't have an LLVM backend yet :) + // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. + break :blk false; + }; + if (!use_llvm and options.machine_code_model != .default) { + return error.MachineCodeModelNotSupported; + } + + // Make a decision on whether to use LLD or our own linker. + const use_lld = if (options.use_lld) |explicit| explicit else blk: { + if (!build_options.have_llvm) + break :blk false; + + if (ofmt == .c) + break :blk false; + + // Our linker can't handle objects or most advanced options yet. + if (options.link_objects.len != 0 or + options.c_source_files.len != 0 or + options.frameworks.len != 0 or + options.system_libs.len != 0 or + options.link_libc or options.link_libcpp or + options.link_eh_frame_hdr or + options.output_mode == .Lib or + options.lld_argv.len != 0 or + options.linker_script != null or options.version_script != null) + { + break :blk true; + } + + if (use_llvm) { + // If stage1 generates an object file, self-hosted linker is not + // yet sophisticated enough to handle that. + break :blk options.root_pkg != null; + } + + break :blk false; + }; + + const link_libc = options.link_libc or target_util.osRequiresLibC(options.target); + + const must_dynamic_link = dl: { + if (target_util.cannotDynamicLink(options.target)) + break :dl false; + if (is_exe_or_dyn_lib and link_libc and + (options.target.isGnuLibC() or target_util.osRequiresLibC(options.target))) + { + break :dl true; + } + if (options.system_libs.len != 0) + break :dl true; + + break :dl false; + }; + const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static; + const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: { + if (lm == .Static and must_dynamic_link) { + return error.UnableToStaticLink; + } + break :blk lm; + } else default_link_mode; + + const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib; + + const libc_dirs = try detectLibCIncludeDirs( + arena, + options.zig_lib_directory.path.?, + options.target, + options.is_native_os, + link_libc, + options.libc_installation, + ); + + const must_pic: bool = b: { + if (target_util.requiresPIC(options.target, link_libc)) + break :b true; + break :b link_mode == .Dynamic; + }; + const pic = if (options.want_pic) |explicit| pic: { + if (!explicit and must_pic) { + return error.TargetRequiresPIC; + } + break :pic explicit; + } else must_pic; + + // Make a decision on whether to use Clang for translate-c and compiling C files. + const use_clang = if (options.use_clang) |explicit| explicit else blk: { + if (build_options.have_llvm) { + // Can't use it if we don't have it! + break :blk false; + } + // It's not planned to do our own translate-c or C compilation. + break :blk true; + }; + + const is_safe_mode = switch (options.optimize_mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }; + + const sanitize_c = options.want_sanitize_c orelse is_safe_mode; + + const stack_check: bool = b: { + if (!target_util.supportsStackProbing(options.target)) + break :b false; + break :b options.want_stack_check orelse is_safe_mode; + }; + + const valgrind: bool = b: { + if (!target_util.hasValgrindSupport(options.target)) + break :b false; + break :b options.want_valgrind orelse (options.optimize_mode == .Debug); + }; + + const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target); + + const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: { + var buf = std.ArrayList(u8).init(arena); + for (options.target.cpu.arch.allFeaturesList()) |feature, index_usize| { + const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); + const is_enabled = options.target.cpu.features.isEnabled(index); + + if (feature.llvm_name) |llvm_name| { + const plus_or_minus = "-+"[@boolToInt(is_enabled)]; + try buf.ensureCapacity(buf.items.len + 2 + llvm_name.len); + buf.appendAssumeCapacity(plus_or_minus); + buf.appendSliceAssumeCapacity(llvm_name); + buf.appendSliceAssumeCapacity(","); + } + } + assert(mem.endsWith(u8, buf.items, ",")); + buf.items[buf.items.len - 1] = 0; + buf.shrink(buf.items.len); + break :blk buf.items[0 .. buf.items.len - 1 :0].ptr; + } else null; + + const strip = options.strip or !target_util.hasDebugInfo(options.target); + + // We put everything into the cache hash that *cannot be modified during an incremental update*. + // For example, one cannot change the target between updates, but one can change source files, + // so the target goes into the cache hash, but source files do not. This is so that we can + // find the same binary and incrementally update it even if there are modified source files. + // We do this even if outputting to the current directory because we need somewhere to store + // incremental compilation metadata. + const cache = try arena.create(Cache); + cache.* = .{ + .gpa = gpa, + .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}), + }; + errdefer cache.manifest_dir.close(); + + // This is shared hasher state common to zig source and all C source files. + cache.hash.addBytes(build_options.version); + cache.hash.addBytes(options.zig_lib_directory.path orelse "."); + cache.hash.add(options.optimize_mode); + cache.hash.add(options.target.cpu.arch); + cache.hash.addBytes(options.target.cpu.model.name); + cache.hash.add(options.target.cpu.features.ints); + cache.hash.add(options.target.os.tag); + cache.hash.add(options.is_native_os); + cache.hash.add(options.target.abi); + cache.hash.add(ofmt); + cache.hash.add(pic); + cache.hash.add(stack_check); + cache.hash.add(link_mode); + cache.hash.add(options.function_sections); + cache.hash.add(strip); + cache.hash.add(link_libc); + cache.hash.add(options.link_libcpp); + cache.hash.add(options.output_mode); + cache.hash.add(options.machine_code_model); + cache.hash.add(options.emit_bin != null); + // TODO audit this and make sure everything is in it + + const module: ?*Module = if (options.root_pkg) |root_pkg| blk: { + // Options that are specific to zig source files, that cannot be + // modified between incremental updates. + var hash = cache.hash; + + // Here we put the root source file path name, but *not* with addFile. We want the + // hash to be the same regardless of the contents of the source file, because + // incremental compilation will handle it, but we do want to namespace different + // source file names because they are likely different compilations and therefore this + // would be likely to cause cache hits. + hash.addBytes(root_pkg.root_src_path); + hash.addOptionalBytes(root_pkg.root_src_directory.path); + hash.add(valgrind); + hash.add(single_threaded); + hash.add(options.target.os.getVersionRange()); + hash.add(dll_export_fns); + hash.add(options.is_test); + hash.add(options.is_compiler_rt_or_libc); + hash.add(options.parent_compilation_link_libc); + + const digest = hash.final(); + const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); + errdefer artifact_dir.close(); + const zig_cache_artifact_directory: Directory = .{ + .handle = artifact_dir, + .path = if (options.local_cache_directory.path) |p| + try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir }) + else + artifact_sub_dir, + }; + + // TODO when we implement serialization and deserialization of incremental compilation metadata, + // this is where we would load it. We have open a handle to the directory where + // the output either already is, or will be. + // However we currently do not have serialization of such metadata, so for now + // we set up an empty Module that does the entire compilation fresh. + + const root_scope = rs: { + if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { + const root_scope = try gpa.create(Module.Scope.File); + root_scope.* = .{ + .sub_file_path = root_pkg.root_src_path, + .source = .{ .unloaded = {} }, + .contents = .{ .not_available = {} }, + .status = .never_loaded, + .root_container = .{ + .file_scope = root_scope, + .decls = .{}, + }, + }; + break :rs &root_scope.base; + } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { + const root_scope = try gpa.create(Module.Scope.ZIRModule); + root_scope.* = .{ + .sub_file_path = root_pkg.root_src_path, + .source = .{ .unloaded = {} }, + .contents = .{ .not_available = {} }, + .status = .never_loaded, + .decls = .{}, + }; + break :rs &root_scope.base; + } else { + unreachable; + } + }; + + const module = try arena.create(Module); + module.* = .{ + .gpa = gpa, + .comp = comp, + .root_pkg = root_pkg, + .root_scope = root_scope, + .zig_cache_artifact_directory = zig_cache_artifact_directory, + }; + break :blk module; + } else null; + errdefer if (module) |zm| zm.deinit(); + + const error_return_tracing = !strip and switch (options.optimize_mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }; + + // For resource management purposes. + var owned_link_dir: ?std.fs.Dir = null; + errdefer if (owned_link_dir) |*dir| dir.close(); + + const bin_file_emit: ?link.Emit = blk: { + const emit_bin = options.emit_bin orelse break :blk null; + if (emit_bin.directory) |directory| { + break :blk link.Emit{ + .directory = directory, + .sub_path = emit_bin.basename, + }; + } + if (module) |zm| { + break :blk link.Emit{ + .directory = zm.zig_cache_artifact_directory, + .sub_path = emit_bin.basename, + }; + } + // We could use the cache hash as is no problem, however, we increase + // the likelihood of cache hits by adding the first C source file + // path name (not contents) to the hash. This way if the user is compiling + // foo.c and bar.c as separate compilations, they get different cache + // directories. + var hash = cache.hash; + if (options.c_source_files.len >= 1) { + hash.addBytes(options.c_source_files[0].src_path); + } else if (options.link_objects.len >= 1) { + hash.addBytes(options.link_objects[0]); + } + + const digest = hash.final(); + const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); + owned_link_dir = artifact_dir; + const link_artifact_directory: Directory = .{ + .handle = artifact_dir, + .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), + }; + break :blk link.Emit{ + .directory = link_artifact_directory, + .sub_path = emit_bin.basename, + }; + }; + + if (!use_llvm and options.emit_h != null) { + fatal("TODO implement support for -femit-h in the self-hosted backend", .{}); + } + + var system_libs: std.StringArrayHashMapUnmanaged(void) = .{}; + errdefer system_libs.deinit(gpa); + try system_libs.ensureCapacity(gpa, options.system_libs.len); + for (options.system_libs) |lib_name| { + system_libs.putAssumeCapacity(lib_name, {}); + } + + const bin_file = try link.File.openPath(gpa, .{ + .emit = bin_file_emit, + .root_name = root_name, + .module = module, + .target = options.target, + .dynamic_linker = options.dynamic_linker, + .output_mode = options.output_mode, + .link_mode = link_mode, + .object_format = ofmt, + .optimize_mode = options.optimize_mode, + .use_lld = use_lld, + .use_llvm = use_llvm, + .link_libc = link_libc, + .link_libcpp = options.link_libcpp, + .objects = options.link_objects, + .frameworks = options.frameworks, + .framework_dirs = options.framework_dirs, + .system_libs = system_libs, + .lib_dirs = options.lib_dirs, + .rpath_list = options.rpath_list, + .strip = strip, + .is_native_os = options.is_native_os, + .function_sections = options.function_sections, + .allow_shlib_undefined = options.linker_allow_shlib_undefined, + .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, + .z_nodelete = options.linker_z_nodelete, + .z_defs = options.linker_z_defs, + .stack_size_override = options.stack_size_override, + .linker_script = options.linker_script, + .version_script = options.version_script, + .gc_sections = options.linker_gc_sections, + .eh_frame_hdr = options.link_eh_frame_hdr, + .rdynamic = options.rdynamic, + .extra_lld_args = options.lld_argv, + .override_soname = options.override_soname, + .version = options.version, + .libc_installation = libc_dirs.libc_installation, + .pic = pic, + .valgrind = valgrind, + .stack_check = stack_check, + .single_threaded = single_threaded, + .verbose_link = options.verbose_link, + .machine_code_model = options.machine_code_model, + .dll_export_fns = dll_export_fns, + .error_return_tracing = error_return_tracing, + .llvm_cpu_features = llvm_cpu_features, + .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc, + .parent_compilation_link_libc = options.parent_compilation_link_libc, + .each_lib_rpath = options.each_lib_rpath orelse false, + .disable_lld_caching = options.disable_lld_caching, + .subsystem = options.subsystem, + .is_test = options.is_test, + }); + errdefer bin_file.destroy(); + comp.* = .{ + .gpa = gpa, + .arena_state = arena_allocator.state, + .zig_lib_directory = options.zig_lib_directory, + .local_cache_directory = options.local_cache_directory, + .global_cache_directory = options.global_cache_directory, + .bin_file = bin_file, + .emit_h = options.emit_h, + .emit_asm = options.emit_asm, + .emit_llvm_ir = options.emit_llvm_ir, + .emit_analysis = options.emit_analysis, + .emit_docs = options.emit_docs, + .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), + .keep_source_files_loaded = options.keep_source_files_loaded, + .use_clang = use_clang, + .clang_argv = options.clang_argv, + .c_source_files = options.c_source_files, + .cache_parent = cache, + .self_exe_path = options.self_exe_path, + .libc_include_dir_list = libc_dirs.libc_include_dir_list, + .sanitize_c = sanitize_c, + .rand = options.rand, + .clang_passthrough_mode = options.clang_passthrough_mode, + .clang_preprocessor_mode = options.clang_preprocessor_mode, + .verbose_cc = options.verbose_cc, + .verbose_tokenize = options.verbose_tokenize, + .verbose_ast = options.verbose_ast, + .verbose_ir = options.verbose_ir, + .verbose_llvm_ir = options.verbose_llvm_ir, + .verbose_cimport = options.verbose_cimport, + .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, + .disable_c_depfile = options.disable_c_depfile, + .owned_link_dir = owned_link_dir, + .color = options.color, + .time_report = options.time_report, + .stack_report = options.stack_report, + .test_filter = options.test_filter, + .test_name_prefix = options.test_name_prefix, + .test_evented_io = options.test_evented_io, + }; + break :comp comp; + }; + errdefer comp.destroy(); + + if (comp.bin_file.options.module) |mod| { + try comp.work_queue.writeItem(.{ .generate_builtin_zig = {} }); + } + + // Add a `CObject` for each `c_source_files`. + try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len); + for (options.c_source_files) |c_source_file| { + const c_object = try gpa.create(CObject); + errdefer gpa.destroy(c_object); + + c_object.* = .{ + .status = .{ .new = {} }, + .src = c_source_file, + }; + comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); + } + + if (comp.bin_file.options.emit != null and !comp.bin_file.options.is_compiler_rt_or_libc) { + // If we need to build glibc for the target, add work items for it. + // We go through the work queue so that building can be done in parallel. + if (comp.wantBuildGLibCFromSource()) { + try comp.addBuildingGLibCJobs(); + } + if (comp.wantBuildMuslFromSource()) { + try comp.work_queue.ensureUnusedCapacity(5); + if (target_util.libc_needs_crti_crtn(comp.getTarget())) { + comp.work_queue.writeAssumeCapacity(&[_]Job{ + .{ .musl_crt_file = .crti_o }, + .{ .musl_crt_file = .crtn_o }, + }); + } + comp.work_queue.writeAssumeCapacity(&[_]Job{ + .{ .musl_crt_file = .crt1_o }, + .{ .musl_crt_file = .scrt1_o }, + .{ .musl_crt_file = .libc_a }, + }); + } + if (comp.wantBuildMinGWFromSource()) { + const static_lib_jobs = [_]Job{ + .{ .mingw_crt_file = .mingw32_lib }, + .{ .mingw_crt_file = .msvcrt_os_lib }, + .{ .mingw_crt_file = .mingwex_lib }, + .{ .mingw_crt_file = .uuid_lib }, + }; + const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; + try comp.work_queue.ensureUnusedCapacity(static_lib_jobs.len + 1); + comp.work_queue.writeAssumeCapacity(&static_lib_jobs); + comp.work_queue.writeItemAssumeCapacity(crt_job); + + // When linking mingw-w64 there are some import libs we always need. + for (mingw.always_link_libs) |name| { + try comp.bin_file.options.system_libs.put(comp.gpa, name, .{}); + } + } + // Generate Windows import libs. + if (comp.getTarget().os.tag == .windows) { + const count = comp.bin_file.options.system_libs.count(); + try comp.work_queue.ensureUnusedCapacity(count); + var i: usize = 0; + while (i < count) : (i += 1) { + comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i }); + } + } + if (comp.wantBuildLibUnwindFromSource()) { + try comp.work_queue.writeItem(.{ .libunwind = {} }); + } + if (build_options.have_llvm and comp.bin_file.options.output_mode != .Obj and + comp.bin_file.options.link_libcpp) + { + try comp.work_queue.writeItem(.libcxx); + try comp.work_queue.writeItem(.libcxxabi); + } + if (is_exe_or_dyn_lib and build_options.is_stage1) { + try comp.work_queue.writeItem(.{ .libcompiler_rt = {} }); + if (!comp.bin_file.options.link_libc) { + try comp.work_queue.writeItem(.{ .zig_libc = {} }); + } + } + } + + if (build_options.is_stage1 and comp.bin_file.options.use_llvm) { + try comp.work_queue.writeItem(.{ .stage1_module = {} }); + } + + return comp; +} + +fn releaseStage1Lock(comp: *Compilation) void { + if (comp.stage1_lock) |*lock| { + lock.release(); + comp.stage1_lock = null; + } +} + +pub fn destroy(self: *Compilation) void { + const optional_module = self.bin_file.options.module; + self.bin_file.destroy(); + if (optional_module) |module| module.deinit(); + + self.releaseStage1Lock(); + + const gpa = self.gpa; + self.work_queue.deinit(); + + { + var it = self.crt_files.iterator(); + while (it.next()) |entry| { + gpa.free(entry.key); + entry.value.deinit(gpa); + } + self.crt_files.deinit(gpa); + } + + if (self.libunwind_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } + if (self.libcxx_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } + if (self.libcxxabi_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } + if (self.compiler_rt_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } + if (self.libc_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } + + for (self.c_object_table.items()) |entry| { + entry.key.destroy(gpa); + } + self.c_object_table.deinit(gpa); + + for (self.failed_c_objects.items()) |entry| { + entry.value.destroy(gpa); + } + self.failed_c_objects.deinit(gpa); + + self.cache_parent.manifest_dir.close(); + if (self.owned_link_dir) |*dir| dir.close(); + + // This destroys `self`. + self.arena_state.promote(gpa).deinit(); +} + +pub fn getTarget(self: Compilation) Target { + return self.bin_file.options.target; +} + +/// Detect changes to source files, perform semantic analysis, and update the output files. +pub fn update(self: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + // For compiling C objects, we rely on the cache hash system to avoid duplicating work. + // Add a Job for each C object. + try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len); + for (self.c_object_table.items()) |entry| { + self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key }); + } + + const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm; + if (!use_stage1) { + if (self.bin_file.options.module) |module| { + module.generation += 1; + + // TODO Detect which source files changed. + // Until then we simulate a full cache miss. Source files could have been loaded for any reason; + // to force a refresh we unload now. + if (module.root_scope.cast(Module.Scope.File)) |zig_file| { + zig_file.unload(module.gpa); + module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { + error.AnalysisFail => { + assert(self.totalErrorCount() != 0); + }, + else => |e| return e, + }; + } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { + zir_module.unload(module.gpa); + module.analyzeRootZIRModule(zir_module) catch |err| switch (err) { + error.AnalysisFail => { + assert(self.totalErrorCount() != 0); + }, + else => |e| return e, + }; + } + } + } + + try self.performAllTheWork(); + + if (!use_stage1) { + if (self.bin_file.options.module) |module| { + // Process the deletion set. + while (module.deletion_set.popOrNull()) |decl| { + if (decl.dependants.items().len != 0) { + decl.deletion_flag = false; + continue; + } + try module.deleteDecl(decl); + } + } + } + + if (self.totalErrorCount() != 0) { + // Skip flushing. + self.link_error_flags = .{}; + return; + } + + // This is needed before reading the error flags. + try self.bin_file.flush(self); + + self.link_error_flags = self.bin_file.errorFlags(); + + // If there are any errors, we anticipate the source files being loaded + // to report error messages. Otherwise we unload all source files to save memory. + if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { + if (self.bin_file.options.module) |module| { + module.root_scope.unload(self.gpa); + } + } +} + +/// Having the file open for writing is problematic as far as executing the +/// binary is concerned. This will remove the write flag, or close the file, +/// or whatever is needed so that it can be executed. +/// After this, one must call` makeFileWritable` before calling `update`. +pub fn makeBinFileExecutable(self: *Compilation) !void { + return self.bin_file.makeExecutable(); +} + +pub fn makeBinFileWritable(self: *Compilation) !void { + return self.bin_file.makeWritable(); +} + +pub fn totalErrorCount(self: *Compilation) usize { + var total: usize = self.failed_c_objects.items().len; + + if (self.bin_file.options.module) |module| { + total += module.failed_decls.items().len + + module.failed_exports.items().len + + module.failed_files.items().len; + } + + // The "no entry point found" error only counts if there are no other errors. + if (total == 0) { + return @boolToInt(self.link_error_flags.no_entry_point_found); + } + + return total; +} + +pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { + var arena = std.heap.ArenaAllocator.init(self.gpa); + errdefer arena.deinit(); + + var errors = std.ArrayList(AllErrors.Message).init(self.gpa); + defer errors.deinit(); + + for (self.failed_c_objects.items()) |entry| { + const c_object = entry.key; + const err_msg = entry.value; + try AllErrors.add(&arena, &errors, c_object.src.src_path, "", err_msg.*); + } + if (self.bin_file.options.module) |module| { + for (module.failed_files.items()) |entry| { + const scope = entry.key; + const err_msg = entry.value; + const source = try scope.getSource(module); + try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); + } + for (module.failed_decls.items()) |entry| { + const decl = entry.key; + const err_msg = entry.value; + const source = try decl.scope.getSource(module); + try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); + } + for (module.failed_exports.items()) |entry| { + const decl = entry.key.owner_decl; + const err_msg = entry.value; + const source = try decl.scope.getSource(module); + try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); + } + } + + if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { + const global_err_src_path = blk: { + if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path; + if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; + if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; + break :blk "(no file)"; + }; + try errors.append(.{ + .src_path = global_err_src_path, + .line = 0, + .column = 0, + .byte_offset = 0, + .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), + }); + } + + assert(errors.items.len == self.totalErrorCount()); + + return AllErrors{ + .list = try arena.allocator.dupe(AllErrors.Message, errors.items), + .arena = arena.state, + }; +} + +pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { + while (self.work_queue.readItem()) |work_item| switch (work_item) { + .codegen_decl => |decl| switch (decl.analysis) { + .unreferenced => unreachable, + .in_progress => unreachable, + .outdated => unreachable, + + .sema_failure, + .codegen_failure, + .dependency_failure, + .sema_failure_retryable, + => continue, + + .complete, .codegen_failure_retryable => { + const module = self.bin_file.options.module.?; + if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { + switch (payload.func.analysis) { + .queued => module.analyzeFnBody(decl, payload.func) catch |err| switch (err) { + error.AnalysisFail => { + assert(payload.func.analysis != .in_progress); + continue; + }, + error.OutOfMemory => return error.OutOfMemory, + }, + .in_progress => unreachable, + .sema_failure, .dependency_failure => continue, + .success => {}, + } + // Here we tack on additional allocations to the Decl's arena. The allocations are + // lifetime annotations in the ZIR. + var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); + defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; + log.debug("analyze liveness of {}\n", .{decl.name}); + try liveness.analyze(module.gpa, &decl_arena.allocator, payload.func.analysis.success); + } + + assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); + + self.bin_file.updateDecl(module, decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .dependency_failure; + }, + else => { + try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); + module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.gpa, + decl.src(), + "unable to codegen: {}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + }, + }; + }, + }, + .analyze_decl => |decl| { + const module = self.bin_file.options.module.?; + module.ensureDeclAnalyzed(decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => continue, + }; + }, + .update_line_number => |decl| { + const module = self.bin_file.options.module.?; + self.bin_file.updateDeclLineNumber(module, decl) catch |err| { + try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); + module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.gpa, + decl.src(), + "unable to update line number: {}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + }; + }, + .c_object => |c_object| { + self.updateCObject(c_object) catch |err| switch (err) { + error.AnalysisFail => continue, + else => { + try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); + self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( + self.gpa, + 0, + "unable to build C object: {}", + .{@errorName(err)}, + )); + c_object.status = .{ .failure = {} }; + }, + }; + }, + .glibc_crt_file => |crt_file| { + glibc.buildCRTFile(self, crt_file) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build glibc CRT file: {}", .{@errorName(err)}); + }; + }, + .glibc_shared_objects => { + glibc.buildSharedObjects(self) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build glibc shared objects: {}", .{@errorName(err)}); + }; + }, + .musl_crt_file => |crt_file| { + musl.buildCRTFile(self, crt_file) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build musl CRT file: {}", .{@errorName(err)}); + }; + }, + .mingw_crt_file => |crt_file| { + mingw.buildCRTFile(self, crt_file) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)}); + }; + }, + .windows_import_lib => |index| { + const link_lib = self.bin_file.options.system_libs.items()[index].key; + mingw.buildImportLib(self, link_lib) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to generate DLL import .lib file: {}", .{@errorName(err)}); + }; + }, + .libunwind => { + libunwind.buildStaticLib(self) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build libunwind: {}", .{@errorName(err)}); + }; + }, + .libcxx => { + libcxx.buildLibCXX(self) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build libcxx: {}", .{@errorName(err)}); + }; + }, + .libcxxabi => { + libcxx.buildLibCXXABI(self) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build libcxxabi: {}", .{@errorName(err)}); + }; + }, + .libcompiler_rt => { + self.buildStaticLibFromZig("compiler_rt.zig", &self.compiler_rt_static_lib) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build compiler_rt: {}", .{@errorName(err)}); + }; + }, + .zig_libc => { + self.buildStaticLibFromZig("c.zig", &self.libc_static_lib) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)}); + }; + }, + .generate_builtin_zig => { + // This Job is only queued up if there is a zig module. + self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to update builtin.zig file: {}", .{@errorName(err)}); + }; + }, + .stage1_module => { + if (!build_options.is_stage1) + unreachable; + + self.updateStage1Module() catch |err| { + fatal("unable to build stage1 zig object: {}", .{@errorName(err)}); + }; + }, + }; +} + +pub fn obtainCObjectCacheManifest(comp: *Compilation) Cache.Manifest { + var man = comp.cache_parent.obtain(); + + // Only things that need to be added on top of the base hash, and only things + // that apply both to @cImport and compiling C objects. No linking stuff here! + // Also nothing that applies only to compiling .zig code. + man.hash.add(comp.sanitize_c); + man.hash.addListOfBytes(comp.clang_argv); + man.hash.add(comp.bin_file.options.link_libcpp); + man.hash.addListOfBytes(comp.libc_include_dir_list); + + return man; +} + +test "cImport" { + _ = cImport; +} + +const CImportResult = struct { + out_zig_path: []u8, + errors: []translate_c.ClangErrMsg, +}; + +/// Caller owns returned memory. +/// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked +/// a bit when we want to start using it from self-hosted. +pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { + if (!build_options.have_llvm) + return error.ZigCompilerNotBuiltWithLLVMExtensions; + + const tracy = trace(@src()); + defer tracy.end(); + + const cimport_zig_basename = "cimport.zig"; + + var man = comp.obtainCObjectCacheManifest(); + defer man.deinit(); + + man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects + man.hash.addBytes(c_src); + + // If the previous invocation resulted in clang errors, we will see a hit + // here with 0 files in the manifest, in which case it is actually a miss. + // We need to "unhit" in this case, to keep the digests matching. + const prev_hash_state = man.hash.peekBin(); + const actual_hit = hit: { + const is_hit = try man.hit(); + if (man.files.items.len == 0) { + man.unhit(prev_hash_state, 0); + break :hit false; + } + break :hit true; + }; + const digest = if (!actual_hit) digest: { + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const tmp_digest = man.hash.peek(); + const tmp_dir_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &tmp_digest }); + var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); + defer zig_cache_tmp_dir.close(); + const cimport_basename = "cimport.h"; + const out_h_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ + tmp_dir_sub_path, cimport_basename, + }); + const out_dep_path = try std.fmt.allocPrint(arena, "{}.d", .{out_h_path}); + + try zig_cache_tmp_dir.writeFile(cimport_basename, c_src); + if (comp.verbose_cimport) { + log.info("C import source: {}", .{out_h_path}); + } + + var argv = std.ArrayList([]const u8).init(comp.gpa); + defer argv.deinit(); + + try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path); + + try argv.append(out_h_path); + + if (comp.verbose_cc) { + dump_argv(argv.items); + } + + // Convert to null terminated args. + const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); + new_argv_with_sentinel[argv.items.len] = null; + const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); + const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); + var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; + const tree = translate_c.translate( + comp.gpa, + new_argv.ptr, + new_argv.ptr + new_argv.len, + &clang_errors, + c_headers_dir_path_z, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ASTUnitFailure => { + log.warn("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}); + return error.ASTUnitFailure; + }, + error.SemanticAnalyzeFail => { + return CImportResult{ + .out_zig_path = "", + .errors = clang_errors, + }; + }, + }; + defer tree.deinit(); + + if (comp.verbose_cimport) { + log.info("C import .d file: {}", .{out_dep_path}); + } + + const dep_basename = std.fs.path.basename(out_dep_path); + try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); + + const digest = man.final(); + const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); + defer o_dir.close(); + + var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{}); + defer out_zig_file.close(); + + var bos = std.io.bufferedOutStream(out_zig_file.writer()); + _ = try std.zig.render(comp.gpa, bos.writer(), tree); + try bos.flush(); + + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest for C import: {}", .{@errorName(err)}); + }; + + break :digest digest; + } else man.final(); + + const out_zig_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ + "o", &digest, cimport_zig_basename, + }); + if (comp.verbose_cimport) { + log.info("C import output: {}\n", .{out_zig_path}); + } + return CImportResult{ + .out_zig_path = out_zig_path, + .errors = &[0]translate_c.ClangErrMsg{}, + }; +} + +fn updateCObject(comp: *Compilation, c_object: *CObject) !void { + if (!build_options.have_llvm) { + return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{}); + } + const self_exe_path = comp.self_exe_path orelse + return comp.failCObj(c_object, "clang compilation disabled", .{}); + + const tracy = trace(@src()); + defer tracy.end(); + + if (c_object.clearStatus(comp.gpa)) { + // There was previous failure. + comp.failed_c_objects.removeAssertDiscard(c_object); + } + + var man = comp.obtainCObjectCacheManifest(); + defer man.deinit(); + + man.hash.add(comp.clang_preprocessor_mode); + + _ = try man.addFile(c_object.src.src_path, null); + { + // Hash the extra flags, with special care to call addFile for file parameters. + // TODO this logic can likely be improved by utilizing clang_options_data.zig. + const file_args = [_][]const u8{"-include"}; + var arg_i: usize = 0; + while (arg_i < c_object.src.extra_flags.len) : (arg_i += 1) { + const arg = c_object.src.extra_flags[arg_i]; + man.hash.addBytes(arg); + for (file_args) |file_arg| { + if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.src.extra_flags.len) { + arg_i += 1; + _ = try man.addFile(c_object.src.extra_flags[arg_i], null); + } + } + } + } + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const c_source_basename = std.fs.path.basename(c_object.src.src_path); + // Special case when doing build-obj for just one C file. When there are more than one object + // file and building an object we need to link them together, but with just one it should go + // directly to the output file. + const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and + comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; + const o_basename_noext = if (direct_o) + comp.bin_file.options.root_name + else + mem.split(c_source_basename, ".").next().?; + const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, comp.getTarget().oFileExt() }); + + const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { + var argv = std.ArrayList([]const u8).init(comp.gpa); + defer argv.deinit(); + + // We can't know the digest until we do the C compiler invocation, so we need a temporary filename. + const out_obj_path = try comp.tmpFilePath(arena, o_basename); + var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); + defer zig_cache_tmp_dir.close(); + + try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" }); + + const ext = classifyFileExt(c_object.src.src_path); + const out_dep_path: ?[]const u8 = if (comp.disable_c_depfile or !ext.clangSupportsDepFile()) + null + else + try std.fmt.allocPrint(arena, "{}.d", .{out_obj_path}); + try comp.addCCArgs(arena, &argv, ext, out_dep_path); + + try argv.ensureCapacity(argv.items.len + 3); + switch (comp.clang_preprocessor_mode) { + .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), + .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), + .stdout => argv.appendAssumeCapacity("-E"), + } + + try argv.append(c_object.src.src_path); + try argv.appendSlice(c_object.src.extra_flags); + + if (comp.verbose_cc) { + dump_argv(argv.items); + } + + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); + + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = child.spawnAndWait() catch |err| { + return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO https://github.com/ziglang/zig/issues/6342 + std.process.exit(1); + } + if (comp.clang_preprocessor_mode == .stdout) + std.process.exit(0); + }, + else => std.process.exit(1), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stdout_reader = child.stdout.?.reader(); + const stderr_reader = child.stderr.?.reader(); + + // TODO https://github.com/ziglang/zig/issues/6343 + const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse clang stderr and turn it into an error message + // and then call failCObjWithOwnedErrorMsg + log.err("clang failed with stderr: {}", .{stderr}); + return comp.failCObj(c_object, "clang exited with code {}", .{code}); + } + }, + else => { + log.err("clang terminated with stderr: {}", .{stderr}); + return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); + }, + } + } + + if (out_dep_path) |dep_file_path| { + const dep_basename = std.fs.path.basename(dep_file_path); + // Add the files depended on to the cache system. + try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); + // Just to save disk space, we delete the file because it is never needed again. + zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { + log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + }; + } + + // Rename into place. + const digest = man.final(); + const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); + defer o_dir.close(); + const tmp_basename = std.fs.path.basename(out_obj_path); + try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename); + + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src.src_path, @errorName(err) }); + }; + break :blk digest; + }; + + c_object.status = .{ + .success = .{ + .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ + "o", &digest, o_basename, + }), + .lock = man.toOwnedLock(), + }, + }; +} + +pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { + const s = std.fs.path.sep_str; + const rand_int = comp.rand.int(u64); + if (comp.local_cache_directory.path) |p| { + return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); + } else { + return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); + } +} + +pub fn addTranslateCCArgs( + comp: *Compilation, + arena: *Allocator, + argv: *std.ArrayList([]const u8), + ext: FileExt, + out_dep_path: ?[]const u8, +) !void { + try argv.appendSlice(&[_][]const u8{ "-x", "c" }); + try comp.addCCArgs(arena, argv, ext, out_dep_path); + // This gives us access to preprocessing entities, presumably at the cost of performance. + try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" }); +} + +/// Add common C compiler args between translate-c and C object compilation. +pub fn addCCArgs( + comp: *Compilation, + arena: *Allocator, + argv: *std.ArrayList([]const u8), + ext: FileExt, + out_dep_path: ?[]const u8, +) !void { + const target = comp.getTarget(); + + if (ext == .cpp) { + try argv.append("-nostdinc++"); + } + + // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode + // we want Clang to infer it, and in normal mode we always want it off, which will be true since + // clang will detect stderr as a pipe rather than a terminal. + if (!comp.clang_passthrough_mode) { + // Make stderr more easily parseable. + try argv.append("-fno-caret-diagnostics"); + } + + if (comp.bin_file.options.function_sections) { + try argv.append("-ffunction-sections"); + } + + try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2); + for (comp.bin_file.options.framework_dirs) |framework_dir| { + argv.appendAssumeCapacity("-iframework"); + argv.appendAssumeCapacity(framework_dir); + } + + if (comp.bin_file.options.link_libcpp) { + const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ + comp.zig_lib_directory.path.?, "libcxx", "include", + }); + const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{ + comp.zig_lib_directory.path.?, "libcxxabi", "include", + }); + + try argv.append("-isystem"); + try argv.append(libcxx_include_path); + + try argv.append("-isystem"); + try argv.append(libcxxabi_include_path); + + if (target.abi.isMusl()) { + try argv.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + } + + const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); + try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); + + switch (ext) { + .c, .cpp, .h => { + try argv.appendSlice(&[_][]const u8{ + "-nostdinc", + "-fno-spell-checking", + }); + + // According to Rich Felker libc headers are supposed to go before C language headers. + // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics + // and other compiler specific items. + const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" }); + try argv.append("-isystem"); + try argv.append(c_headers_dir); + + for (comp.libc_include_dir_list) |include_dir| { + try argv.append("-isystem"); + try argv.append(include_dir); + } + + if (target.cpu.model.llvm_name) |llvm_name| { + try argv.appendSlice(&[_][]const u8{ + "-Xclang", "-target-cpu", "-Xclang", llvm_name, + }); + } + + // It would be really nice if there was a more compact way to communicate this info to Clang. + const all_features_list = target.cpu.arch.allFeaturesList(); + try argv.ensureCapacity(argv.items.len + all_features_list.len * 4); + for (all_features_list) |feature, index_usize| { + const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize); + const is_enabled = target.cpu.features.isEnabled(index); + + if (feature.llvm_name) |llvm_name| { + argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" }); + const plus_or_minus = "-+"[@boolToInt(is_enabled)]; + const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); + argv.appendAssumeCapacity(arg); + } + } + const mcmodel = comp.bin_file.options.machine_code_model; + if (mcmodel != .default) { + try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)})); + } + + // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning. + // So for this target, we disable this warning. + if (target.os.tag == .windows and target.abi.isGnu()) { + try argv.append("-Wno-pragma-pack"); + } + + if (!comp.bin_file.options.strip) { + try argv.append("-g"); + } + + if (comp.haveFramePointer()) { + try argv.append("-fno-omit-frame-pointer"); + } else { + try argv.append("-fomit-frame-pointer"); + } + + if (comp.sanitize_c) { + try argv.append("-fsanitize=undefined"); + try argv.append("-fsanitize-trap=undefined"); + } + + switch (comp.bin_file.options.optimize_mode) { + .Debug => { + // windows c runtime requires -D_DEBUG if using debug libraries + try argv.append("-D_DEBUG"); + try argv.append("-Og"); + + if (comp.bin_file.options.link_libc) { + try argv.append("-fstack-protector-strong"); + try argv.append("--param"); + try argv.append("ssp-buffer-size=4"); + } else { + try argv.append("-fno-stack-protector"); + } + }, + .ReleaseSafe => { + // See the comment in the BuildModeFastRelease case for why we pass -O2 rather + // than -O3 here. + try argv.append("-O2"); + if (comp.bin_file.options.link_libc) { + try argv.append("-D_FORTIFY_SOURCE=2"); + try argv.append("-fstack-protector-strong"); + try argv.append("--param"); + try argv.append("ssp-buffer-size=4"); + } else { + try argv.append("-fno-stack-protector"); + } + }, + .ReleaseFast => { + try argv.append("-DNDEBUG"); + // Here we pass -O2 rather than -O3 because, although we do the equivalent of + // -O3 in Zig code, the justification for the difference here is that Zig + // has better detection and prevention of undefined behavior, so -O3 is safer for + // Zig code than it is for C code. Also, C programmers are used to their code + // running in -O2 and thus the -O3 path has been tested less. + try argv.append("-O2"); + try argv.append("-fno-stack-protector"); + }, + .ReleaseSmall => { + try argv.append("-DNDEBUG"); + try argv.append("-Os"); + try argv.append("-fno-stack-protector"); + }, + } + + if (target_util.supports_fpic(target) and comp.bin_file.options.pic) { + try argv.append("-fPIC"); + } + }, + .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig, .zir => {}, + } + if (out_dep_path) |p| { + try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); + } + // Argh, why doesn't the assembler accept the list of CPU features?! + // I don't see a way to do this other than hard coding everything. + switch (target.cpu.arch) { + .riscv32, .riscv64 => { + if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { + try argv.append("-mrelax"); + } else { + try argv.append("-mno-relax"); + } + }, + else => { + // TODO + }, + } + + if (target.os.tag == .freestanding) { + try argv.append("-ffreestanding"); + } + + try argv.appendSlice(comp.clang_argv); +} + +fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError { + @setCold(true); + const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args); + return comp.failCObjWithOwnedErrorMsg(c_object, err_msg); +} + +fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError { + { + errdefer err_msg.destroy(comp.gpa); + try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1); + } + comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg); + c_object.status = .failure; + return error.AnalysisFail; +} + +pub const ErrorMsg = struct { + byte_offset: usize, + msg: []const u8, + + pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg { + const self = try gpa.create(ErrorMsg); + errdefer gpa.destroy(self); + self.* = try init(gpa, byte_offset, format, args); + return self; + } + + /// Assumes the ErrorMsg struct and msg were both allocated with allocator. + pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { + self.deinit(gpa); + gpa.destroy(self); + } + + pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg { + return ErrorMsg{ + .byte_offset = byte_offset, + .msg = try std.fmt.allocPrint(gpa, format, args), + }; + } + + pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { + gpa.free(self.msg); + self.* = undefined; + } +}; + +pub const FileExt = enum { + c, + cpp, + h, + ll, + bc, + assembly, + shared_library, + object, + static_library, + zig, + zir, + unknown, + + pub fn clangSupportsDepFile(ext: FileExt) bool { + return switch (ext) { + .c, .cpp, .h => true, + + .ll, + .bc, + .assembly, + .shared_library, + .object, + .static_library, + .zig, + .zir, + .unknown, + => false, + }; + } +}; + +pub fn hasObjectExt(filename: []const u8) bool { + return mem.endsWith(u8, filename, ".o") or mem.endsWith(u8, filename, ".obj"); +} + +pub fn hasStaticLibraryExt(filename: []const u8) bool { + return mem.endsWith(u8, filename, ".a") or mem.endsWith(u8, filename, ".lib"); +} + +pub fn hasCExt(filename: []const u8) bool { + return mem.endsWith(u8, filename, ".c"); +} + +pub fn hasCppExt(filename: []const u8) bool { + return mem.endsWith(u8, filename, ".C") or + mem.endsWith(u8, filename, ".cc") or + mem.endsWith(u8, filename, ".cpp") or + mem.endsWith(u8, filename, ".cxx"); +} + +pub fn hasAsmExt(filename: []const u8) bool { + return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); +} + +pub fn hasSharedLibraryExt(filename: []const u8) bool { + if (mem.endsWith(u8, filename, ".so") or + mem.endsWith(u8, filename, ".dll") or + mem.endsWith(u8, filename, ".dylib")) + { + return true; + } + // Look for .so.X, .so.X.Y, .so.X.Y.Z + var it = mem.split(filename, "."); + _ = it.next().?; + var so_txt = it.next() orelse return false; + while (!mem.eql(u8, so_txt, "so")) { + so_txt = it.next() orelse return false; + } + const n1 = it.next() orelse return false; + const n2 = it.next(); + const n3 = it.next(); + + _ = std.fmt.parseInt(u32, n1, 10) catch return false; + if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false; + if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false; + if (it.next() != null) return false; + + return true; +} + +pub fn classifyFileExt(filename: []const u8) FileExt { + if (hasCExt(filename)) { + return .c; + } else if (hasCppExt(filename)) { + return .cpp; + } else if (mem.endsWith(u8, filename, ".ll")) { + return .ll; + } else if (mem.endsWith(u8, filename, ".bc")) { + return .bc; + } else if (hasAsmExt(filename)) { + return .assembly; + } else if (mem.endsWith(u8, filename, ".h")) { + return .h; + } else if (mem.endsWith(u8, filename, ".zig")) { + return .zig; + } else if (mem.endsWith(u8, filename, ".zir")) { + return .zir; + } else if (hasSharedLibraryExt(filename)) { + return .shared_library; + } else if (hasStaticLibraryExt(filename)) { + return .static_library; + } else if (hasObjectExt(filename)) { + return .object; + } else { + return .unknown; + } +} + +test "classifyFileExt" { + std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc")); + std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim")); + std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so")); + std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1")); + std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2")); + std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3")); + std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~")); + std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); + std.testing.expectEqual(FileExt.zir, classifyFileExt("foo.zir")); +} + +fn haveFramePointer(comp: *Compilation) bool { + // If you complicate this logic make sure you update the parent cache hash. + // Right now it's not in the cache hash because the value depends on optimize_mode + // and strip which are both already part of the hash. + return switch (comp.bin_file.options.optimize_mode) { + .Debug, .ReleaseSafe => !comp.bin_file.options.strip, + .ReleaseSmall, .ReleaseFast => false, + }; +} + +const LibCDirs = struct { + libc_include_dir_list: []const []const u8, + libc_installation: ?*const LibCInstallation, +}; + +fn detectLibCIncludeDirs( + arena: *Allocator, + zig_lib_dir: []const u8, + target: Target, + is_native_os: bool, + link_libc: bool, + libc_installation: ?*const LibCInstallation, +) !LibCDirs { + if (!link_libc) { + return LibCDirs{ + .libc_include_dir_list = &[0][]u8{}, + .libc_installation = null, + }; + } + + if (libc_installation) |lci| { + return detectLibCFromLibCInstallation(arena, target, lci); + } + + if (target_util.canBuildLibC(target)) { + const generic_name = target_util.libCGenericName(target); + // Some architectures are handled by the same set of headers. + const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch); + const os_name = @tagName(target.os.tag); + // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. + const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); + const s = std.fs.path.sep_str; + const arch_include_dir = try std.fmt.allocPrint( + arena, + "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", + .{ zig_lib_dir, arch_name, os_name, abi_name }, + ); + const generic_include_dir = try std.fmt.allocPrint( + arena, + "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}", + .{ zig_lib_dir, generic_name }, + ); + const arch_os_include_dir = try std.fmt.allocPrint( + arena, + "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any", + .{ zig_lib_dir, @tagName(target.cpu.arch), os_name }, + ); + const generic_os_include_dir = try std.fmt.allocPrint( + arena, + "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any", + .{ zig_lib_dir, os_name }, + ); + + const list = try arena.alloc([]const u8, 4); + list[0] = arch_include_dir; + list[1] = generic_include_dir; + list[2] = arch_os_include_dir; + list[3] = generic_os_include_dir; + return LibCDirs{ + .libc_include_dir_list = list, + .libc_installation = null, + }; + } + + if (is_native_os) { + const libc = try arena.create(LibCInstallation); + libc.* = try LibCInstallation.findNative(.{ .allocator = arena }); + return detectLibCFromLibCInstallation(arena, target, libc); + } + + return LibCDirs{ + .libc_include_dir_list = &[0][]u8{}, + .libc_installation = null, + }; +} + +fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { + var list = std.ArrayList([]const u8).init(arena); + try list.ensureCapacity(4); + + list.appendAssumeCapacity(lci.include_dir.?); + + const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?); + if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?); + + if (target.os.tag == .windows) { + if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| { + const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" }); + list.appendAssumeCapacity(um_dir); + + const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" }); + list.appendAssumeCapacity(shared_dir); + } + } + return LibCDirs{ + .libc_include_dir_list = list.items, + .libc_installation = lci, + }; +} + +pub fn get_libc_crt_file(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { + if (comp.wantBuildGLibCFromSource() or + comp.wantBuildMuslFromSource() or + comp.wantBuildMinGWFromSource()) + { + return comp.crt_files.get(basename).?.full_object_path; + } + const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable; + const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; + const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); + return full_path; +} + +fn addBuildingGLibCJobs(comp: *Compilation) !void { + try comp.work_queue.write(&[_]Job{ + .{ .glibc_crt_file = .crti_o }, + .{ .glibc_crt_file = .crtn_o }, + .{ .glibc_crt_file = .scrt1_o }, + .{ .glibc_crt_file = .libc_nonshared_a }, + .{ .glibc_shared_objects = {} }, + }); +} + +fn wantBuildLibCFromSource(comp: Compilation) bool { + const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) { + .Obj => false, + .Lib => comp.bin_file.options.link_mode == .Dynamic, + .Exe => true, + }; + return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and + comp.bin_file.options.libc_installation == null; +} + +fn wantBuildGLibCFromSource(comp: Compilation) bool { + return comp.wantBuildLibCFromSource() and comp.getTarget().isGnuLibC(); +} + +fn wantBuildMuslFromSource(comp: Compilation) bool { + return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl(); +} + +fn wantBuildMinGWFromSource(comp: Compilation) bool { + return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW(); +} + +fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { + const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) { + .Obj => false, + .Lib => comp.bin_file.options.link_mode == .Dynamic, + .Exe => true, + }; + return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and + comp.bin_file.options.libc_installation == null and + target_util.libcNeedsLibUnwind(comp.getTarget()); +} + +fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const source = try comp.generateBuiltinZigSource(comp.gpa); + defer comp.gpa.free(source); + try mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source); +} + +pub fn dump_argv(argv: []const []const u8) void { + for (argv[0 .. argv.len - 1]) |arg| { + std.debug.print("{} ", .{arg}); + } + std.debug.print("{}\n", .{argv[argv.len - 1]}); +} + +pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 { + const tracy = trace(@src()); + defer tracy.end(); + + var buffer = std.ArrayList(u8).init(allocator); + defer buffer.deinit(); + + const target = comp.getTarget(); + const generic_arch_name = target.cpu.arch.genericName(); + + @setEvalBranchQuota(4000); + try buffer.writer().print( + \\usingnamespace @import("std").builtin; + \\/// Deprecated + \\pub const arch = Target.current.cpu.arch; + \\/// Deprecated + \\pub const endian = Target.current.cpu.arch.endian(); + \\pub const output_mode = OutputMode.{}; + \\pub const link_mode = LinkMode.{}; + \\pub const is_test = {}; + \\pub const single_threaded = {}; + \\pub const abi = Abi.{}; + \\pub const cpu: Cpu = Cpu{{ + \\ .arch = .{}, + \\ .model = &Target.{}.cpu.{}, + \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ + , .{ + @tagName(comp.bin_file.options.output_mode), + @tagName(comp.bin_file.options.link_mode), + comp.bin_file.options.is_test, + comp.bin_file.options.single_threaded, + @tagName(target.abi), + @tagName(target.cpu.arch), + generic_arch_name, + target.cpu.model.name, + generic_arch_name, + generic_arch_name, + }); + + for (target.cpu.arch.allFeaturesList()) |feature, index_usize| { + const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize); + const is_enabled = target.cpu.features.isEnabled(index); + if (is_enabled) { + // TODO some kind of "zig identifier escape" function rather than + // unconditionally using @"" syntax + try buffer.appendSlice(" .@\""); + try buffer.appendSlice(feature.name); + try buffer.appendSlice("\",\n"); + } + } + + try buffer.writer().print( + \\ }}), + \\}}; + \\pub const os = Os{{ + \\ .tag = .{}, + \\ .version_range = .{{ + , + .{@tagName(target.os.tag)}, + ); + + switch (target.os.getVersionRange()) { + .none => try buffer.appendSlice(" .none = {} }\n"), + .semver => |semver| try buffer.outStream().print( + \\ .semver = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + semver.min.major, + semver.min.minor, + semver.min.patch, + + semver.max.major, + semver.max.minor, + semver.max.patch, + }), + .linux => |linux| try buffer.outStream().print( + \\ .linux = .{{ + \\ .range = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}, + \\ .glibc = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + linux.range.min.major, + linux.range.min.minor, + linux.range.min.patch, + + linux.range.max.major, + linux.range.max.minor, + linux.range.max.patch, + + linux.glibc.major, + linux.glibc.minor, + linux.glibc.patch, + }), + .windows => |windows| try buffer.outStream().print( + \\ .windows = .{{ + \\ .min = {s}, + \\ .max = {s}, + \\ }}}}, + \\ + , + .{ windows.min, windows.max }, + ), + } + try buffer.appendSlice("};\n"); + + // This is so that compiler_rt and libc.zig libraries know whether they + // will eventually be linked with libc. They make different decisions + // about what to export depending on whether another libc will be linked + // in. For example, compiler_rt will not export the __chkstk symbol if it + // knows libc will provide it, and likewise c.zig will not export memcpy. + const link_libc = comp.bin_file.options.link_libc or + (comp.bin_file.options.is_compiler_rt_or_libc and comp.bin_file.options.parent_compilation_link_libc); + + try buffer.writer().print( + \\pub const object_format = ObjectFormat.{}; + \\pub const mode = Mode.{}; + \\pub const link_libc = {}; + \\pub const link_libcpp = {}; + \\pub const have_error_return_tracing = {}; + \\pub const valgrind_support = {}; + \\pub const position_independent_code = {}; + \\pub const strip_debug_info = {}; + \\pub const code_model = CodeModel.{}; + \\ + , .{ + @tagName(comp.bin_file.options.object_format), + @tagName(comp.bin_file.options.optimize_mode), + link_libc, + comp.bin_file.options.link_libcpp, + comp.bin_file.options.error_return_tracing, + comp.bin_file.options.valgrind, + comp.bin_file.options.pic, + comp.bin_file.options.strip, + @tagName(comp.bin_file.options.machine_code_model), + }); + + if (comp.bin_file.options.is_test) { + try buffer.appendSlice( + \\pub var test_functions: []TestFn = undefined; // overwritten later + \\ + ); + if (comp.test_evented_io) { + try buffer.appendSlice( + \\pub const test_io_mode = .evented; + \\ + ); + } else { + try buffer.appendSlice( + \\pub const test_io_mode = .blocking; + \\ + ); + } + } + + return buffer.toOwnedSlice(); +} + +pub fn updateSubCompilation(sub_compilation: *Compilation) !void { + try sub_compilation.update(); + + // Look for compilation errors in this sub_compilation + var errors = try sub_compilation.getAllErrorsAlloc(); + defer errors.deinit(sub_compilation.gpa); + + if (errors.list.len != 0) { + for (errors.list) |full_err_msg| { + log.err("{}:{}:{}: {}\n", .{ + full_err_msg.src_path, + full_err_msg.line + 1, + full_err_msg.column + 1, + full_err_msg.msg, + }); + } + return error.BuildingLibCObjectFailed; + } +} + +fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const special_sub = "std" ++ std.fs.path.sep_str ++ "special"; + const special_path = try comp.zig_lib_directory.join(comp.gpa, &[_][]const u8{special_sub}); + defer comp.gpa.free(special_path); + + var special_dir = try comp.zig_lib_directory.handle.openDir(special_sub, .{}); + defer special_dir.close(); + + var root_pkg: Package = .{ + .root_src_directory = .{ + .path = special_path, + .handle = special_dir, + }, + .root_src_path = src_basename, + }; + const root_name = mem.split(src_basename, ".").next().?; + const target = comp.getTarget(); + const output_mode: std.builtin.OutputMode = if (target.cpu.arch.isWasm()) .Obj else .Lib; + const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + }); + defer comp.gpa.free(bin_basename); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = bin_basename, + }; + const optimize_mode: std.builtin.Mode = blk: { + if (comp.bin_file.options.is_test) + break :blk comp.bin_file.options.optimize_mode; + switch (comp.bin_file.options.optimize_mode) { + .Debug, .ReleaseFast, .ReleaseSafe => break :blk .ReleaseFast, + .ReleaseSmall => break :blk .ReleaseSmall, + } + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .global_cache_directory = comp.global_cache_directory, + .local_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = &root_pkg, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = optimize_mode, + .link_mode = .Static, + .function_sections = true, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .is_compiler_rt_or_libc = true, + .parent_compilation_link_libc = comp.bin_file.options.link_libc, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(out.* == null); + out.* = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} + +fn updateStage1Module(comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + // Here we use the legacy stage1 C++ compiler to compile Zig code. + const mod = comp.bin_file.options.module.?; + const directory = mod.zig_cache_artifact_directory; // Just an alias to make it shorter to type. + const main_zig_file = try mod.root_pkg.root_src_directory.join(arena, &[_][]const u8{ + mod.root_pkg.root_src_path, + }); + const zig_lib_dir = comp.zig_lib_directory.path.?; + const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"}); + const target = comp.getTarget(); + const id_symlink_basename = "stage1.id"; + const libs_txt_basename = "libs.txt"; + + // We are about to obtain this lock, so here we give other processes a chance first. + comp.releaseStage1Lock(); + + // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation, + // so we input all the zig source files into the cache hash system. We're going to keep + // the artifact directory the same, however, so we take the same strategy as linking + // does where we have a file which specifies the hash of the output directory so that we can + // skip the expensive compilation step if the hash matches. + var man = comp.cache_parent.obtain(); + defer man.deinit(); + + _ = try man.addFile(main_zig_file, null); + man.hash.add(comp.bin_file.options.valgrind); + man.hash.add(comp.bin_file.options.single_threaded); + man.hash.add(target.os.getVersionRange()); + man.hash.add(comp.bin_file.options.dll_export_fns); + man.hash.add(comp.bin_file.options.function_sections); + man.hash.add(comp.bin_file.options.is_test); + man.hash.add(comp.bin_file.options.emit != null); + man.hash.add(comp.emit_h != null); + man.hash.add(comp.emit_asm != null); + man.hash.add(comp.emit_llvm_ir != null); + man.hash.add(comp.emit_analysis != null); + man.hash.add(comp.emit_docs != null); + + // Capture the state in case we come back from this branch where the hash doesn't match. + const prev_hash_state = man.hash.peekBin(); + const input_file_count = man.files.items.len; + + if (try man.hit()) { + const digest = man.final(); + + // We use an extra hex-encoded byte here to store some flags. + var prev_digest_buf: [digest.len + 2]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (prev_digest.len >= digest.len + 2) hit: { + if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) + break :hit; + + log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest }); + var flags_bytes: [1]u8 = undefined; + _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { + log.warn("bad cache stage1 digest: '{s}'", .{prev_digest}); + break :hit; + }; + + if (directory.handle.readFileAlloc(comp.gpa, libs_txt_basename, 10 * 1024 * 1024)) |libs_txt| { + var it = mem.tokenize(libs_txt, "\n"); + while (it.next()) |lib_name| { + try comp.stage1AddLinkLib(lib_name); + } + } else |err| switch (err) { + error.FileNotFound => {}, // That's OK, it just means 0 libs. + else => { + log.warn("unable to read cached list of link libs: {s}", .{@errorName(err)}); + break :hit; + }, + } + comp.stage1_lock = man.toOwnedLock(); + mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); + return; + } + log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest }); + man.unhit(prev_hash_state, input_file_count); + } + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + + const stage2_target = try arena.create(stage1.Stage2Target); + stage2_target.* = .{ + .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch + .os = @enumToInt(target.os.tag), + .abi = @enumToInt(target.abi), + .is_native_os = comp.bin_file.options.is_native_os, + .is_native_cpu = false, // Only true when bootstrapping the compiler. + .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null, + .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?, + }; + var progress: std.Progress = .{}; + var main_progress_node = try progress.start("", null); + defer main_progress_node.end(); + if (comp.color == .Off) progress.terminal = null; + + comp.stage1_cache_manifest = &man; + + const main_pkg_path = mod.root_pkg.root_src_directory.path orelse ""; + + const stage1_module = stage1.create( + @enumToInt(comp.bin_file.options.optimize_mode), + main_pkg_path.ptr, + main_pkg_path.len, + main_zig_file.ptr, + main_zig_file.len, + zig_lib_dir.ptr, + zig_lib_dir.len, + stage2_target, + comp.bin_file.options.is_test, + ) orelse return error.OutOfMemory; + + const emit_bin_path = if (comp.bin_file.options.emit != null) blk: { + const bin_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = comp.bin_file.options.root_name, + .target = target, + .output_mode = .Obj, + }); + break :blk try directory.join(arena, &[_][]const u8{bin_basename}); + } else ""; + if (comp.emit_h != null) { + log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{}); + } + const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory); + const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory); + const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory); + const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory); + const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory); + const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null); + const test_filter = comp.test_filter orelse ""[0..0]; + const test_name_prefix = comp.test_name_prefix orelse ""[0..0]; + const subsystem = if (comp.bin_file.options.subsystem) |s| + @intToEnum(stage1.TargetSubsystem, @enumToInt(s)) + else + stage1.TargetSubsystem.Auto; + stage1_module.* = .{ + .root_name_ptr = comp.bin_file.options.root_name.ptr, + .root_name_len = comp.bin_file.options.root_name.len, + .emit_o_ptr = emit_bin_path.ptr, + .emit_o_len = emit_bin_path.len, + .emit_h_ptr = emit_h_path.ptr, + .emit_h_len = emit_h_path.len, + .emit_asm_ptr = emit_asm_path.ptr, + .emit_asm_len = emit_asm_path.len, + .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr, + .emit_llvm_ir_len = emit_llvm_ir_path.len, + .emit_analysis_json_ptr = emit_analysis_path.ptr, + .emit_analysis_json_len = emit_analysis_path.len, + .emit_docs_ptr = emit_docs_path.ptr, + .emit_docs_len = emit_docs_path.len, + .builtin_zig_path_ptr = builtin_zig_path.ptr, + .builtin_zig_path_len = builtin_zig_path.len, + .test_filter_ptr = test_filter.ptr, + .test_filter_len = test_filter.len, + .test_name_prefix_ptr = test_name_prefix.ptr, + .test_name_prefix_len = test_name_prefix.len, + .userdata = @ptrToInt(comp), + .root_pkg = stage1_pkg, + .code_model = @enumToInt(comp.bin_file.options.machine_code_model), + .subsystem = subsystem, + .err_color = @enumToInt(comp.color), + .pic = comp.bin_file.options.pic, + .link_libc = comp.bin_file.options.link_libc, + .link_libcpp = comp.bin_file.options.link_libcpp, + .strip = comp.bin_file.options.strip, + .is_single_threaded = comp.bin_file.options.single_threaded, + .dll_export_fns = comp.bin_file.options.dll_export_fns, + .link_mode_dynamic = comp.bin_file.options.link_mode == .Dynamic, + .valgrind_enabled = comp.bin_file.options.valgrind, + .function_sections = comp.bin_file.options.function_sections, + .enable_stack_probing = comp.bin_file.options.stack_check, + .enable_time_report = comp.time_report, + .enable_stack_report = comp.stack_report, + .test_is_evented = comp.test_evented_io, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .main_progress_node = main_progress_node, + .have_c_main = false, + .have_winmain = false, + .have_wwinmain = false, + .have_winmain_crt_startup = false, + .have_wwinmain_crt_startup = false, + .have_dllmain_crt_startup = false, + }; + + const inferred_lib_start_index = comp.bin_file.options.system_libs.count(); + stage1_module.build_object(); + + if (comp.bin_file.options.system_libs.count() > inferred_lib_start_index) { + // We need to save the inferred link libs to the cache, otherwise if we get a cache hit + // next time we will be missing these libs. + var libs_txt = std.ArrayList(u8).init(arena); + for (comp.bin_file.options.system_libs.items()[inferred_lib_start_index..]) |entry| { + try libs_txt.writer().print("{s}\n", .{entry.key}); + } + try directory.handle.writeFile(libs_txt_basename, libs_txt.items); + } + + mod.stage1_flags = .{ + .have_c_main = stage1_module.have_c_main, + .have_winmain = stage1_module.have_winmain, + .have_wwinmain = stage1_module.have_wwinmain, + .have_winmain_crt_startup = stage1_module.have_winmain_crt_startup, + .have_wwinmain_crt_startup = stage1_module.have_wwinmain_crt_startup, + .have_dllmain_crt_startup = stage1_module.have_dllmain_crt_startup, + }; + + stage1_module.destroy(); + + const digest = man.final(); + + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); + log.debug("stage1 {} final digest={} flags={x}", .{ + mod.root_pkg.root_src_path, digest, stage1_flags_byte, + }); + var digest_plus_flags: [digest.len + 2]u8 = undefined; + digest_plus_flags[0..digest.len].* = digest; + assert(std.fmt.formatIntBuf(digest_plus_flags[digest.len..], stage1_flags_byte, 16, false, .{ + .width = 2, + .fill = '0', + }) == 2); + log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{ + digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup, + }); + directory.handle.symLink(&digest_plus_flags, id_symlink_basename, .{}) catch |err| { + log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + comp.stage1_lock = man.toOwnedLock(); +} + +fn stage1LocPath(arena: *Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 { + const loc = opt_loc orelse return ""; + const directory = loc.directory orelse cache_directory; + return directory.join(arena, &[_][]const u8{loc.basename}); +} + +fn createStage1Pkg( + arena: *Allocator, + name: []const u8, + pkg: *Package, + parent_pkg: ?*stage1.Pkg, +) error{OutOfMemory}!*stage1.Pkg { + const child_pkg = try arena.create(stage1.Pkg); + + const pkg_children = blk: { + var children = std.ArrayList(*stage1.Pkg).init(arena); + var it = pkg.table.iterator(); + while (it.next()) |entry| { + try children.append(try createStage1Pkg(arena, entry.key, entry.value, child_pkg)); + } + break :blk children.items; + }; + + const src_path = try pkg.root_src_directory.join(arena, &[_][]const u8{pkg.root_src_path}); + + child_pkg.* = .{ + .name_ptr = name.ptr, + .name_len = name.len, + .path_ptr = src_path.ptr, + .path_len = src_path.len, + .children_ptr = pkg_children.ptr, + .children_len = pkg_children.len, + .parent = parent_pkg, + }; + return child_pkg; +} + +pub fn build_crt_file( + comp: *Compilation, + root_name: []const u8, + output_mode: std.builtin.OutputMode, + c_source_files: []const Compilation.CSourceFile, +) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(comp.gpa, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + }); + errdefer comp.gpa.free(basename); + + // TODO: This is extracted into a local variable to work around a stage1 miscompilation. + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = c_source_files, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .is_compiler_rt_or_libc = true, + .parent_compilation_link_libc = comp.bin_file.options.link_libc, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); + + comp.crt_files.putAssumeCapacityNoClobber(basename, .{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), + .lock = sub_compilation.bin_file.toOwnedLock(), + }); +} + +pub fn stage1AddLinkLib(comp: *Compilation, lib_name: []const u8) !void { + // This happens when an `extern "foo"` function is referenced by the stage1 backend. + // If we haven't seen this library yet and we're targeting Windows, we need to queue up + // a work item to produce the DLL import library for this. + const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name); + if (!gop.found_existing and comp.getTarget().os.tag == .windows) { + try comp.work_queue.writeItem(.{ + .windows_import_lib = comp.bin_file.options.system_libs.count() - 1, + }); + } +} diff --git a/src-self-hosted/dep_tokenizer.zig b/src/DepTokenizer.zig similarity index 51% rename from src-self-hosted/dep_tokenizer.zig rename to src/DepTokenizer.zig index 20324cbf0c..cc2211a1aa 100644 --- a/src-self-hosted/dep_tokenizer.zig +++ b/src/DepTokenizer.zig @@ -1,361 +1,405 @@ +const Tokenizer = @This(); + +index: usize = 0, +bytes: []const u8, +state: State = .lhs, + const std = @import("std"); const testing = std.testing; +const assert = std.debug.assert; -pub const Tokenizer = struct { - arena: std.heap.ArenaAllocator, - index: usize, - bytes: []const u8, - error_text: []const u8, - state: State, - - pub fn init(allocator: *std.mem.Allocator, bytes: []const u8) Tokenizer { - return Tokenizer{ - .arena = std.heap.ArenaAllocator.init(allocator), - .index = 0, - .bytes = bytes, - .error_text = "", - .state = State{ .lhs = {} }, - }; - } - - pub fn deinit(self: *Tokenizer) void { - self.arena.deinit(); - } - - pub fn next(self: *Tokenizer) Error!?Token { - while (self.index < self.bytes.len) { - const char = self.bytes[self.index]; - while (true) { - switch (self.state) { - .lhs => switch (char) { - '\t', '\n', '\r', ' ' => { - // silently ignore whitespace - break; // advance - }, - else => { - self.state = State{ .target = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) }; - }, - }, - .target => |*target| switch (char) { - '\t', '\n', '\r', ' ' => { - return self.errorIllegalChar(self.index, char, "invalid target", .{}); - }, - '$' => { - self.state = State{ .target_dollar_sign = target.* }; - break; // advance - }, - '\\' => { - self.state = State{ .target_reverse_solidus = target.* }; - break; // advance - }, - ':' => { - self.state = State{ .target_colon = target.* }; - break; // advance - }, - else => { - try target.append(char); - break; // advance - }, - }, - .target_reverse_solidus => |*target| switch (char) { - '\t', '\n', '\r' => { - return self.errorIllegalChar(self.index, char, "bad target escape", .{}); - }, - ' ', '#', '\\' => { - try target.append(char); - self.state = State{ .target = target.* }; - break; // advance - }, - '$' => { - try target.appendSlice(self.bytes[self.index - 1 .. self.index]); - self.state = State{ .target_dollar_sign = target.* }; - break; // advance - }, - else => { - try target.appendSlice(self.bytes[self.index - 1 .. self.index + 1]); - self.state = State{ .target = target.* }; - break; // advance - }, - }, - .target_dollar_sign => |*target| switch (char) { - '$' => { - try target.append(char); - self.state = State{ .target = target.* }; - break; // advance - }, - else => { - return self.errorIllegalChar(self.index, char, "expecting '$'", .{}); - }, - }, - .target_colon => |*target| switch (char) { - '\n', '\r' => { - const bytes = target.span(); - if (bytes.len != 0) { - self.state = State{ .lhs = {} }; - return Token{ .id = .target, .bytes = bytes }; - } - // silently ignore null target - self.state = State{ .lhs = {} }; - continue; - }, - '\\' => { - self.state = State{ .target_colon_reverse_solidus = target.* }; - break; // advance - }, - else => { - const bytes = target.span(); - if (bytes.len != 0) { - self.state = State{ .rhs = {} }; - return Token{ .id = .target, .bytes = bytes }; - } - // silently ignore null target - self.state = State{ .lhs = {} }; - continue; - }, - }, - .target_colon_reverse_solidus => |*target| switch (char) { - '\n', '\r' => { - const bytes = target.span(); - if (bytes.len != 0) { - self.state = State{ .lhs = {} }; - return Token{ .id = .target, .bytes = bytes }; - } - // silently ignore null target - self.state = State{ .lhs = {} }; - continue; - }, - else => { - try target.appendSlice(self.bytes[self.index - 2 .. self.index + 1]); - self.state = State{ .target = target.* }; - break; - }, - }, - .rhs => switch (char) { - '\t', ' ' => { - // silently ignore horizontal whitespace - break; // advance - }, - '\n', '\r' => { - self.state = State{ .lhs = {} }; - continue; - }, - '\\' => { - self.state = State{ .rhs_continuation = {} }; - break; // advance - }, - '"' => { - self.state = State{ .prereq_quote = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) }; - break; // advance - }, - else => { - self.state = State{ .prereq = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) }; - }, - }, - .rhs_continuation => switch (char) { - '\n' => { - self.state = State{ .rhs = {} }; - break; // advance - }, - '\r' => { - self.state = State{ .rhs_continuation_linefeed = {} }; - break; // advance - }, - else => { - return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{}); - }, - }, - .rhs_continuation_linefeed => switch (char) { - '\n' => { - self.state = State{ .rhs = {} }; - break; // advance - }, - else => { - return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{}); - }, - }, - .prereq_quote => |*prereq| switch (char) { - '"' => { - const bytes = prereq.span(); - self.index += 1; - self.state = State{ .rhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; - }, - else => { - try prereq.append(char); - break; // advance - }, - }, - .prereq => |*prereq| switch (char) { - '\t', ' ' => { - const bytes = prereq.span(); - self.state = State{ .rhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; - }, - '\n', '\r' => { - const bytes = prereq.span(); - self.state = State{ .lhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; - }, - '\\' => { - self.state = State{ .prereq_continuation = prereq.* }; - break; // advance - }, - else => { - try prereq.append(char); - break; // advance - }, - }, - .prereq_continuation => |*prereq| switch (char) { - '\n' => { - const bytes = prereq.span(); - self.index += 1; - self.state = State{ .rhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; - }, - '\r' => { - self.state = State{ .prereq_continuation_linefeed = prereq.* }; - break; // advance - }, - else => { - // not continuation - try prereq.appendSlice(self.bytes[self.index - 1 .. self.index + 1]); - self.state = State{ .prereq = prereq.* }; - break; // advance - }, - }, - .prereq_continuation_linefeed => |prereq| switch (char) { - '\n' => { - const bytes = prereq.span(); - self.index += 1; - self.state = State{ .rhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; - }, - else => { - return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{}); - }, - }, - } - } - self.index += 1; +pub fn next(self: *Tokenizer) ?Token { + var start = self.index; + var must_resolve = false; + while (self.index < self.bytes.len) { + const char = self.bytes[self.index]; + switch (self.state) { + .lhs => switch (char) { + '\t', '\n', '\r', ' ' => { + // silently ignore whitespace + self.index += 1; + }, + else => { + start = self.index; + self.state = .target; + }, + }, + .target => switch (char) { + '\t', '\n', '\r', ' ' => { + return errorIllegalChar(.invalid_target, self.index, char); + }, + '$' => { + self.state = .target_dollar_sign; + self.index += 1; + }, + '\\' => { + self.state = .target_reverse_solidus; + self.index += 1; + }, + ':' => { + self.state = .target_colon; + self.index += 1; + }, + else => { + self.index += 1; + }, + }, + .target_reverse_solidus => switch (char) { + '\t', '\n', '\r' => { + return errorIllegalChar(.bad_target_escape, self.index, char); + }, + ' ', '#', '\\' => { + must_resolve = true; + self.state = .target; + self.index += 1; + }, + '$' => { + self.state = .target_dollar_sign; + self.index += 1; + }, + else => { + self.state = .target; + self.index += 1; + }, + }, + .target_dollar_sign => switch (char) { + '$' => { + must_resolve = true; + self.state = .target; + self.index += 1; + }, + else => { + return errorIllegalChar(.expected_dollar_sign, self.index, char); + }, + }, + .target_colon => switch (char) { + '\n', '\r' => { + const bytes = self.bytes[start .. self.index - 1]; + if (bytes.len != 0) { + self.state = .lhs; + return finishTarget(must_resolve, bytes); + } + // silently ignore null target + self.state = .lhs; + }, + '\\' => { + self.state = .target_colon_reverse_solidus; + self.index += 1; + }, + else => { + const bytes = self.bytes[start .. self.index - 1]; + if (bytes.len != 0) { + self.state = .rhs; + return finishTarget(must_resolve, bytes); + } + // silently ignore null target + self.state = .lhs; + }, + }, + .target_colon_reverse_solidus => switch (char) { + '\n', '\r' => { + const bytes = self.bytes[start .. self.index - 2]; + if (bytes.len != 0) { + self.state = .lhs; + return finishTarget(must_resolve, bytes); + } + // silently ignore null target + self.state = .lhs; + }, + else => { + self.state = .target; + }, + }, + .rhs => switch (char) { + '\t', ' ' => { + // silently ignore horizontal whitespace + self.index += 1; + }, + '\n', '\r' => { + self.state = .lhs; + }, + '\\' => { + self.state = .rhs_continuation; + self.index += 1; + }, + '"' => { + self.state = .prereq_quote; + self.index += 1; + start = self.index; + }, + else => { + start = self.index; + self.state = .prereq; + }, + }, + .rhs_continuation => switch (char) { + '\n' => { + self.state = .rhs; + self.index += 1; + }, + '\r' => { + self.state = .rhs_continuation_linefeed; + self.index += 1; + }, + else => { + return errorIllegalChar(.continuation_eol, self.index, char); + }, + }, + .rhs_continuation_linefeed => switch (char) { + '\n' => { + self.state = .rhs; + self.index += 1; + }, + else => { + return errorIllegalChar(.continuation_eol, self.index, char); + }, + }, + .prereq_quote => switch (char) { + '"' => { + self.index += 1; + self.state = .rhs; + return Token{ .prereq = self.bytes[start .. self.index - 1] }; + }, + else => { + self.index += 1; + }, + }, + .prereq => switch (char) { + '\t', ' ' => { + self.state = .rhs; + return Token{ .prereq = self.bytes[start..self.index] }; + }, + '\n', '\r' => { + self.state = .lhs; + return Token{ .prereq = self.bytes[start..self.index] }; + }, + '\\' => { + self.state = .prereq_continuation; + self.index += 1; + }, + else => { + self.index += 1; + }, + }, + .prereq_continuation => switch (char) { + '\n' => { + self.index += 1; + self.state = .rhs; + return Token{ .prereq = self.bytes[start .. self.index - 2] }; + }, + '\r' => { + self.state = .prereq_continuation_linefeed; + self.index += 1; + }, + else => { + // not continuation + self.state = .prereq; + self.index += 1; + }, + }, + .prereq_continuation_linefeed => switch (char) { + '\n' => { + self.index += 1; + self.state = .rhs; + return Token{ .prereq = self.bytes[start .. self.index - 1] }; + }, + else => { + return errorIllegalChar(.continuation_eol, self.index, char); + }, + }, } - - // eof, handle maybe incomplete token - if (self.index == 0) return null; - const idx = self.index - 1; + } else { switch (self.state) { .lhs, .rhs, .rhs_continuation, .rhs_continuation_linefeed, - => {}, - .target => |target| { - return self.errorPosition(idx, target.span(), "incomplete target", .{}); + => return null, + .target => { + return errorPosition(.incomplete_target, start, self.bytes[start..]); }, .target_reverse_solidus, .target_dollar_sign, => { - const index = self.index - 1; - return self.errorIllegalChar(idx, self.bytes[idx], "incomplete escape", .{}); + const idx = self.index - 1; + return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]); }, - .target_colon => |target| { - const bytes = target.span(); + .target_colon => { + const bytes = self.bytes[start .. self.index - 1]; if (bytes.len != 0) { self.index += 1; - self.state = State{ .rhs = {} }; - return Token{ .id = .target, .bytes = bytes }; + self.state = .rhs; + return finishTarget(must_resolve, bytes); } // silently ignore null target - self.state = State{ .lhs = {} }; + self.state = .lhs; + return null; }, - .target_colon_reverse_solidus => |target| { - const bytes = target.span(); + .target_colon_reverse_solidus => { + const bytes = self.bytes[start .. self.index - 2]; if (bytes.len != 0) { self.index += 1; - self.state = State{ .rhs = {} }; - return Token{ .id = .target, .bytes = bytes }; + self.state = .rhs; + return finishTarget(must_resolve, bytes); } // silently ignore null target - self.state = State{ .lhs = {} }; + self.state = .lhs; + return null; }, - .prereq_quote => |prereq| { - return self.errorPosition(idx, prereq.span(), "incomplete quoted prerequisite", .{}); + .prereq_quote => { + return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]); }, - .prereq => |prereq| { - const bytes = prereq.span(); - self.state = State{ .lhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; + .prereq => { + self.state = .lhs; + return Token{ .prereq = self.bytes[start..] }; }, - .prereq_continuation => |prereq| { - const bytes = prereq.span(); - self.state = State{ .lhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; + .prereq_continuation => { + self.state = .lhs; + return Token{ .prereq = self.bytes[start .. self.index - 1] }; }, - .prereq_continuation_linefeed => |prereq| { - const bytes = prereq.span(); - self.state = State{ .lhs = {} }; - return Token{ .id = .prereq, .bytes = bytes }; + .prereq_continuation_linefeed => { + self.state = .lhs; + return Token{ .prereq = self.bytes[start .. self.index - 2] }; }, } - return null; } + unreachable; +} - fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: anytype) Error { - self.error_text = try std.fmt.allocPrintZ(&self.arena.allocator, fmt, args); - return Error.InvalidInput; - } +fn errorPosition(comptime id: @TagType(Token), index: usize, bytes: []const u8) Token { + return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes }); +} - fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: anytype) Error { - var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0); - try buffer.outStream().print(fmt, args); - try buffer.appendSlice(" '"); - var out = makeOutput(std.ArrayListSentineled(u8, 0).appendSlice, &buffer); - try printCharValues(&out, bytes); - try buffer.appendSlice("'"); - try buffer.outStream().print(" at position {}", .{position - (bytes.len - 1)}); - self.error_text = buffer.span(); - return Error.InvalidInput; - } +fn errorIllegalChar(comptime id: @TagType(Token), index: usize, char: u8) Token { + return @unionInit(Token, @tagName(id), .{ .index = index, .char = char }); +} - fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: anytype) Error { - var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0); - try buffer.appendSlice("illegal char "); - try printUnderstandableChar(&buffer, char); - try buffer.outStream().print(" at position {}", .{position}); - if (fmt.len != 0) try buffer.outStream().print(": " ++ fmt, args); - self.error_text = buffer.span(); - return Error.InvalidInput; - } +fn finishTarget(must_resolve: bool, bytes: []const u8) Token { + return if (must_resolve) + .{ .target_must_resolve = bytes } + else + .{ .target = bytes }; +} - const Error = error{ - OutOfMemory, - InvalidInput, +const State = enum { + lhs, + target, + target_reverse_solidus, + target_dollar_sign, + target_colon, + target_colon_reverse_solidus, + rhs, + rhs_continuation, + rhs_continuation_linefeed, + prereq_quote, + prereq, + prereq_continuation, + prereq_continuation_linefeed, +}; + +pub const Token = union(enum) { + target: []const u8, + target_must_resolve: []const u8, + prereq: []const u8, + + incomplete_quoted_prerequisite: IndexAndBytes, + incomplete_target: IndexAndBytes, + + invalid_target: IndexAndChar, + bad_target_escape: IndexAndChar, + expected_dollar_sign: IndexAndChar, + continuation_eol: IndexAndChar, + incomplete_escape: IndexAndChar, + + pub const IndexAndChar = struct { + index: usize, + char: u8, }; - const State = union(enum) { - lhs: void, - target: std.ArrayListSentineled(u8, 0), - target_reverse_solidus: std.ArrayListSentineled(u8, 0), - target_dollar_sign: std.ArrayListSentineled(u8, 0), - target_colon: std.ArrayListSentineled(u8, 0), - target_colon_reverse_solidus: std.ArrayListSentineled(u8, 0), - rhs: void, - rhs_continuation: void, - rhs_continuation_linefeed: void, - prereq_quote: std.ArrayListSentineled(u8, 0), - prereq: std.ArrayListSentineled(u8, 0), - prereq_continuation: std.ArrayListSentineled(u8, 0), - prereq_continuation_linefeed: std.ArrayListSentineled(u8, 0), - }; - - const Token = struct { - id: ID, + pub const IndexAndBytes = struct { + index: usize, bytes: []const u8, - - const ID = enum { - target, - prereq, - }; }; + + /// Resolve escapes in target. Only valid with .target_must_resolve. + pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void { + const bytes = self.target_must_resolve; // resolve called on incorrect token + + var state: enum { start, escape, dollar } = .start; + for (bytes) |c| { + switch (state) { + .start => { + switch (c) { + '\\' => state = .escape, + '$' => state = .dollar, + else => try writer.writeByte(c), + } + }, + .escape => { + switch (c) { + ' ', '#', '\\' => {}, + '$' => { + try writer.writeByte('\\'); + state = .dollar; + continue; + }, + else => try writer.writeByte('\\'), + } + try writer.writeByte(c); + state = .start; + }, + .dollar => { + try writer.writeByte('$'); + switch (c) { + '$' => {}, + else => try writer.writeByte(c), + } + state = .start; + }, + } + } + } + + pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void { + switch (self) { + .target, .target_must_resolve, .prereq => unreachable, // not an error + .incomplete_quoted_prerequisite, + .incomplete_target, + => |index_and_bytes| { + try writer.print("{} '", .{self.errStr()}); + if (self == .incomplete_target) { + const tmp = Token{ .target_must_resolve = index_and_bytes.bytes }; + try tmp.resolve(writer); + } else { + try printCharValues(writer, index_and_bytes.bytes); + } + try writer.print("' at position {}", .{index_and_bytes.index}); + }, + .invalid_target, + .bad_target_escape, + .expected_dollar_sign, + .continuation_eol, + .incomplete_escape, + => |index_and_char| { + try writer.writeAll("illegal char "); + try printUnderstandableChar(writer, index_and_char.char); + try writer.print(" at position {}: {}", .{ index_and_char.index, self.errStr() }); + }, + } + } + + fn errStr(self: Token) []const u8 { + return switch (self) { + .target, .target_must_resolve, .prereq => unreachable, // not an error + .incomplete_quoted_prerequisite => "incomplete quoted prerequisite", + .incomplete_target => "incomplete target", + .invalid_target => "invalid target", + .bad_target_escape => "bad target escape", + .expected_dollar_sign => "expecting '$'", + .continuation_eol => "continuation expecting end-of-line", + .incomplete_escape => "incomplete escape", + }; + } }; test "empty file" { @@ -750,16 +794,16 @@ test "error incomplete target" { ); try depTokenizer("\\ foo.o", - \\ERROR: incomplete target ' foo.o' at position 1 + \\ERROR: incomplete target ' foo.o' at position 0 ); try depTokenizer("\\#foo.o", - \\ERROR: incomplete target '#foo.o' at position 1 + \\ERROR: incomplete target '#foo.o' at position 0 ); try depTokenizer("\\\\foo.o", - \\ERROR: incomplete target '\foo.o' at position 1 + \\ERROR: incomplete target '\foo.o' at position 0 ); try depTokenizer("$$foo.o", - \\ERROR: incomplete target '$foo.o' at position 1 + \\ERROR: incomplete target '$foo.o' at position 0 ); } @@ -836,33 +880,40 @@ test "error prereq - continuation expecting end-of-line" { // - tokenize input, emit textual representation, and compare to expect fn depTokenizer(input: []const u8, expect: []const u8) !void { - var arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator); + var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); const arena = &arena_allocator.allocator; defer arena_allocator.deinit(); - var it = Tokenizer.init(arena, input); + var it: Tokenizer = .{ .bytes = input }; var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0); + var resolve_buf = std.ArrayList(u8).init(arena); var i: usize = 0; - while (true) { - const r = it.next() catch |err| { - switch (err) { - Tokenizer.Error.InvalidInput => { - if (i != 0) try buffer.appendSlice("\n"); - try buffer.appendSlice("ERROR: "); - try buffer.appendSlice(it.error_text); - }, - else => return err, - } - break; - }; - const token = r orelse break; + while (it.next()) |token| { if (i != 0) try buffer.appendSlice("\n"); - try buffer.appendSlice(@tagName(token.id)); - try buffer.appendSlice(" = {"); - for (token.bytes) |b| { - try buffer.append(printable_char_tab[b]); + switch (token) { + .target, .prereq => |bytes| { + try buffer.appendSlice(@tagName(token)); + try buffer.appendSlice(" = {"); + for (bytes) |b| { + try buffer.append(printable_char_tab[b]); + } + try buffer.appendSlice("}"); + }, + .target_must_resolve => { + try buffer.appendSlice("target = {"); + try token.resolve(resolve_buf.writer()); + for (resolve_buf.items) |b| { + try buffer.append(printable_char_tab[b]); + } + resolve_buf.items.len = 0; + try buffer.appendSlice("}"); + }, + else => { + try buffer.appendSlice("ERROR: "); + try token.printError(buffer.outStream()); + break; + }, } - try buffer.appendSlice("}"); i += 1; } const got: []const u8 = buffer.span(); @@ -872,13 +923,13 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void { return; } - var out = makeOutput(std.fs.File.write, try std.io.getStdErr()); + const out = std.io.getStdErr().writer(); - try out.write("\n"); - try printSection(&out, "<<<< input", input); - try printSection(&out, "==== expect", expect); - try printSection(&out, ">>>> got", got); - try printRuler(&out); + try out.writeAll("\n"); + try printSection(out, "<<<< input", input); + try printSection(out, "==== expect", expect); + try printSection(out, ">>>> got", got); + try printRuler(out); testing.expect(false); } @@ -887,29 +938,29 @@ fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void { try printLabel(out, label, bytes); try hexDump(out, bytes); try printRuler(out); - try out.write(bytes); - try out.write("\n"); + try out.writeAll(bytes); + try out.writeAll("\n"); } fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void { var buf: [80]u8 = undefined; var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); - try out.write(text); + try out.writeAll(text); var i: usize = text.len; const end = 79; while (i < 79) : (i += 1) { - try out.write([_]u8{label[0]}); + try out.writeAll(&[_]u8{label[0]}); } - try out.write("\n"); + try out.writeAll("\n"); } fn printRuler(out: anytype) !void { var i: usize = 0; const end = 79; while (i < 79) : (i += 1) { - try out.write("-"); + try out.writeAll("-"); } - try out.write("\n"); + try out.writeAll("\n"); } fn hexDump(out: anytype, bytes: []const u8) !void { @@ -924,116 +975,90 @@ fn hexDump(out: anytype, bytes: []const u8) !void { const n = bytes.len & 0x0f; if (n > 0) { try printDecValue(out, offset, 8); - try out.write(":"); - try out.write(" "); + try out.writeAll(":"); + try out.writeAll(" "); var end1 = std.math.min(offset + n, offset + 8); for (bytes[offset..end1]) |b| { - try out.write(" "); + try out.writeAll(" "); try printHexValue(out, b, 2); } var end2 = offset + n; if (end2 > end1) { - try out.write(" "); + try out.writeAll(" "); for (bytes[end1..end2]) |b| { - try out.write(" "); + try out.writeAll(" "); try printHexValue(out, b, 2); } } const short = 16 - n; var i: usize = 0; while (i < short) : (i += 1) { - try out.write(" "); + try out.writeAll(" "); } if (end2 > end1) { - try out.write(" |"); + try out.writeAll(" |"); } else { - try out.write(" |"); + try out.writeAll(" |"); } try printCharValues(out, bytes[offset..end2]); - try out.write("|\n"); + try out.writeAll("|\n"); offset += n; } try printDecValue(out, offset, 8); - try out.write(":"); - try out.write("\n"); + try out.writeAll(":"); + try out.writeAll("\n"); } fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void { try printDecValue(out, offset, 8); - try out.write(":"); - try out.write(" "); + try out.writeAll(":"); + try out.writeAll(" "); for (bytes[0..8]) |b| { - try out.write(" "); + try out.writeAll(" "); try printHexValue(out, b, 2); } - try out.write(" "); + try out.writeAll(" "); for (bytes[8..16]) |b| { - try out.write(" "); + try out.writeAll(" "); try printHexValue(out, b, 2); } - try out.write(" |"); + try out.writeAll(" |"); try printCharValues(out, bytes); - try out.write("|\n"); + try out.writeAll("|\n"); } fn printDecValue(out: anytype, value: u64, width: u8) !void { var buffer: [20]u8 = undefined; - const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width); - try out.write(buffer[0..len]); + const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, .{ .width = width, .fill = '0' }); + try out.writeAll(buffer[0..len]); } fn printHexValue(out: anytype, value: u64, width: u8) !void { var buffer: [16]u8 = undefined; - const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width); - try out.write(buffer[0..len]); + const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, .{ .width = width, .fill = '0' }); + try out.writeAll(buffer[0..len]); } fn printCharValues(out: anytype, bytes: []const u8) !void { for (bytes) |b| { - try out.write(&[_]u8{printable_char_tab[b]}); + try out.writeAll(&[_]u8{printable_char_tab[b]}); } } -fn printUnderstandableChar(buffer: *std.ArrayListSentineled(u8, 0), char: u8) !void { +fn printUnderstandableChar(out: anytype, char: u8) !void { if (!std.ascii.isPrint(char) or char == ' ') { - try buffer.outStream().print("\\x{X:2}", .{char}); + try out.print("\\x{X:0>2}", .{char}); } else { - try buffer.appendSlice("'"); - try buffer.append(printable_char_tab[char]); - try buffer.appendSlice("'"); + try out.print("'{c}'", .{printable_char_tab[char]}); } } // zig fmt: off -const printable_char_tab: []const u8 = +const printable_char_tab: [256]u8 = ( "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++ "................................................................" ++ - "................................................................"; -// zig fmt: on -comptime { - std.debug.assert(printable_char_tab.len == 256); -} + "................................................................" +).*; -// Make an output var that wraps a context and output function. -// output: must be a function that takes a `self` idiom parameter -// and a bytes parameter -// context: must be that self -fn makeOutput(comptime output: anytype, context: anytype) Output(output, @TypeOf(context)) { - return Output(output, @TypeOf(context)){ - .context = context, - }; -} - -fn Output(comptime output_func: anytype, comptime Context: type) type { - return struct { - context: Context, - - pub const output = output_func; - - fn write(self: @This(), bytes: []const u8) !void { - try output_func(self.context, bytes); - } - }; -} diff --git a/src-self-hosted/Module.zig b/src/Module.zig similarity index 87% rename from src-self-hosted/Module.zig rename to src/Module.zig index dc48ae23e7..4fcf72f4ff 100644 --- a/src-self-hosted/Module.zig +++ b/src/Module.zig @@ -1,4 +1,6 @@ +const Module = @This(); const std = @import("std"); +const Compilation = @import("Compilation.zig"); const mem = std.mem; const Allocator = std.mem.Allocator; const ArrayListUnmanaged = std.ArrayListUnmanaged; @@ -14,25 +16,24 @@ const Package = @import("Package.zig"); const link = @import("link.zig"); const ir = @import("ir.zig"); const zir = @import("zir.zig"); -const Module = @This(); const Inst = ir.Inst; const Body = ir.Body; const ast = std.zig.ast; const trace = @import("tracy.zig").trace; -const liveness = @import("liveness.zig"); const astgen = @import("astgen.zig"); const zir_sema = @import("zir_sema.zig"); /// General-purpose allocator. Used for both temporary and long-term storage. gpa: *Allocator, -/// Pointer to externally managed resource. +comp: *Compilation, + +/// Where our incremental compilation metadata serialization will go. +zig_cache_artifact_directory: Compilation.Directory, +/// Pointer to externally managed resource. `null` if there is no zig file being compiled. root_pkg: *Package, /// Module owns this resource. /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. root_scope: *Scope, -bin_file: *link.File, -bin_file_dir: std.fs.Dir, -bin_file_path: []const u8, /// It's rare for a decl to be exported, so we save memory by having a sparse map of /// Decl pointers to details about them being exported. /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. @@ -47,55 +48,42 @@ symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{}, export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, /// Maps fully qualified namespaced names to the Decl struct for them. decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, - -link_error_flags: link.File.ErrorFlags = .{}, - -work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), - /// We optimize memory usage for a compilation with no compile errors by storing the /// error messages and mapping outside of `Decl`. -/// The ErrorMsg memory is owned by the decl, using Module's allocator. +/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. /// Note that a Decl can succeed but the Fn it represents can fail. In this case, /// a Decl can have a failed_decls entry but have analysis status of success. -failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, +failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, /// Using a map here for consistency with the other fields here. -/// The ErrorMsg memory is owned by the `Scope`, using Module's allocator. -failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{}, +/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. +failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, /// Using a map here for consistency with the other fields here. -/// The ErrorMsg memory is owned by the `Export`, using Module's allocator. -failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, +/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. +failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{}, + +next_anon_name_index: usize = 0, + +/// Candidates for deletion. After a semantic analysis update completes, this list +/// contains Decls that need to be deleted if they end up having no references to them. +deletion_set: ArrayListUnmanaged(*Decl) = .{}, + +/// Error tags and their values, tag names are duped with mod.gpa. +global_error_set: std.StringHashMapUnmanaged(u16) = .{}, /// Incrementing integer used to compare against the corresponding Decl /// field to determine whether a Decl's status applies to an ongoing update, or a /// previous analysis. generation: u32 = 0, -next_anon_name_index: usize = 0, - -/// Candidates for deletion. After a semantic analysis update completes, this list -/// contains Decls that need to be deleted if they end up having no references to them. -deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, - -/// Owned by Module. -root_name: []u8, -keep_source_files_loaded: bool, - -/// Error tags and their values, tag names are duped with mod.gpa. -global_error_set: std.StringHashMapUnmanaged(u16) = .{}, - -pub const InnerError = error{ OutOfMemory, AnalysisFail }; - -const WorkItem = union(enum) { - /// Write the machine code for a Decl to the output file. - codegen_decl: *Decl, - /// The Decl needs to be analyzed and possibly export itself. - /// It may have already be analyzed, or it may have been determined - /// to be outdated; in this case perform semantic analysis again. - analyze_decl: *Decl, - /// The source file containing the Decl has been updated, and so the - /// Decl may need its line number information updated in the debug info. - update_line_number: *Decl, -}; +stage1_flags: packed struct { + have_winmain: bool = false, + have_wwinmain: bool = false, + have_winmain_crt_startup: bool = false, + have_wwinmain_crt_startup: bool = false, + have_dllmain_crt_startup: bool = false, + have_c_main: bool = false, + reserved: u2 = 0, +} = .{}, pub const Export = struct { options: std.builtin.ExportOptions, @@ -622,7 +610,7 @@ pub const Scope = struct { pub fn getSource(self: *File, module: *Module) ![:0]const u8 { switch (self.source) { .unloaded => { - const source = try module.root_pkg.root_src_dir.readFileAllocOptions( + const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( module.gpa, self.sub_file_path, std.math.maxInt(u32), @@ -720,7 +708,7 @@ pub const Scope = struct { pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { switch (self.source) { .unloaded => { - const source = try module.root_pkg.root_src_dir.readFileAllocOptions( + const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( module.gpa, self.sub_file_path, std.math.maxInt(u32), @@ -818,117 +806,14 @@ pub const Scope = struct { }; }; -pub const AllErrors = struct { - arena: std.heap.ArenaAllocator.State, - list: []const Message, - - pub const Message = struct { - src_path: []const u8, - line: usize, - column: usize, - byte_offset: usize, - msg: []const u8, - }; - - pub fn deinit(self: *AllErrors, gpa: *Allocator) void { - self.arena.promote(gpa).deinit(); - } - - fn add( - arena: *std.heap.ArenaAllocator, - errors: *std.ArrayList(Message), - sub_file_path: []const u8, - source: []const u8, - simple_err_msg: ErrorMsg, - ) !void { - const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); - try errors.append(.{ - .src_path = try arena.allocator.dupe(u8, sub_file_path), - .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), - .byte_offset = simple_err_msg.byte_offset, - .line = loc.line, - .column = loc.column, - }); - } -}; - -pub const InitOptions = struct { - target: std.Target, - root_name: []const u8, - root_pkg: *Package, - output_mode: std.builtin.OutputMode, - bin_file_dir: ?std.fs.Dir = null, - bin_file_path: []const u8, - link_mode: ?std.builtin.LinkMode = null, - object_format: ?std.builtin.ObjectFormat = null, - optimize_mode: std.builtin.Mode = .Debug, - keep_source_files_loaded: bool = false, -}; - -pub fn init(gpa: *Allocator, options: InitOptions) !Module { - const root_name = try gpa.dupe(u8, options.root_name); - errdefer gpa.free(root_name); - - const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); - const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{ - .root_name = root_name, - .root_pkg = options.root_pkg, - .target = options.target, - .output_mode = options.output_mode, - .link_mode = options.link_mode orelse .Static, - .object_format = options.object_format orelse options.target.getObjectFormat(), - .optimize_mode = options.optimize_mode, - }); - errdefer bin_file.destroy(); - - const root_scope = blk: { - if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) { - const root_scope = try gpa.create(Scope.File); - root_scope.* = .{ - .sub_file_path = options.root_pkg.root_src_path, - .source = .{ .unloaded = {} }, - .contents = .{ .not_available = {} }, - .status = .never_loaded, - .root_container = .{ - .file_scope = root_scope, - .decls = .{}, - }, - }; - break :blk &root_scope.base; - } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { - const root_scope = try gpa.create(Scope.ZIRModule); - root_scope.* = .{ - .sub_file_path = options.root_pkg.root_src_path, - .source = .{ .unloaded = {} }, - .contents = .{ .not_available = {} }, - .status = .never_loaded, - .decls = .{}, - }; - break :blk &root_scope.base; - } else { - unreachable; - } - }; - - return Module{ - .gpa = gpa, - .root_name = root_name, - .root_pkg = options.root_pkg, - .root_scope = root_scope, - .bin_file_dir = bin_file_dir, - .bin_file_path = options.bin_file_path, - .bin_file = bin_file, - .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), - .keep_source_files_loaded = options.keep_source_files_loaded, - }; -} +pub const InnerError = error{ OutOfMemory, AnalysisFail }; pub fn deinit(self: *Module) void { - self.bin_file.destroy(); const gpa = self.gpa; - self.gpa.free(self.root_name); + + self.zig_cache_artifact_directory.handle.close(); + self.deletion_set.deinit(gpa); - self.work_queue.deinit(); for (self.decl_table.items()) |entry| { entry.value.destroy(gpa); @@ -969,7 +854,6 @@ pub fn deinit(self: *Module) void { gpa.free(entry.key); } self.global_error_set.deinit(gpa); - self.* = undefined; } fn freeExportList(gpa: *Allocator, export_list: []*Export) void { @@ -980,204 +864,6 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void { gpa.free(export_list); } -pub fn target(self: Module) std.Target { - return self.bin_file.options.target; -} - -pub fn optimizeMode(self: Module) std.builtin.Mode { - return self.bin_file.options.optimize_mode; -} - -/// Detect changes to source files, perform semantic analysis, and update the output files. -pub fn update(self: *Module) !void { - const tracy = trace(@src()); - defer tracy.end(); - - self.generation += 1; - - // TODO Use the cache hash file system to detect which source files changed. - // Until then we simulate a full cache miss. Source files could have been loaded for any reason; - // to force a refresh we unload now. - if (self.root_scope.cast(Scope.File)) |zig_file| { - zig_file.unload(self.gpa); - self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { - error.AnalysisFail => { - assert(self.totalErrorCount() != 0); - }, - else => |e| return e, - }; - } else if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| { - zir_module.unload(self.gpa); - self.analyzeRootZIRModule(zir_module) catch |err| switch (err) { - error.AnalysisFail => { - assert(self.totalErrorCount() != 0); - }, - else => |e| return e, - }; - } - - try self.performAllTheWork(); - - // Process the deletion set. - while (self.deletion_set.popOrNull()) |decl| { - if (decl.dependants.items().len != 0) { - decl.deletion_flag = false; - continue; - } - try self.deleteDecl(decl); - } - - // This is needed before reading the error flags. - try self.bin_file.flush(self); - - self.link_error_flags = self.bin_file.errorFlags(); - - // If there are any errors, we anticipate the source files being loaded - // to report error messages. Otherwise we unload all source files to save memory. - if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { - self.root_scope.unload(self.gpa); - } -} - -/// Having the file open for writing is problematic as far as executing the -/// binary is concerned. This will remove the write flag, or close the file, -/// or whatever is needed so that it can be executed. -/// After this, one must call` makeFileWritable` before calling `update`. -pub fn makeBinFileExecutable(self: *Module) !void { - return self.bin_file.makeExecutable(); -} - -pub fn makeBinFileWritable(self: *Module) !void { - return self.bin_file.makeWritable(self.bin_file_dir, self.bin_file_path); -} - -pub fn totalErrorCount(self: *Module) usize { - const total = self.failed_decls.items().len + - self.failed_files.items().len + - self.failed_exports.items().len; - return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; -} - -pub fn getAllErrorsAlloc(self: *Module) !AllErrors { - var arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer arena.deinit(); - - var errors = std.ArrayList(AllErrors.Message).init(self.gpa); - defer errors.deinit(); - - for (self.failed_files.items()) |entry| { - const scope = entry.key; - const err_msg = entry.value; - const source = try scope.getSource(self); - try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); - } - for (self.failed_decls.items()) |entry| { - const decl = entry.key; - const err_msg = entry.value; - const source = try decl.scope.getSource(self); - try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); - } - for (self.failed_exports.items()) |entry| { - const decl = entry.key.owner_decl; - const err_msg = entry.value; - const source = try decl.scope.getSource(self); - try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); - } - - if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { - try errors.append(.{ - .src_path = self.root_pkg.root_src_path, - .line = 0, - .column = 0, - .byte_offset = 0, - .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), - }); - } - - assert(errors.items.len == self.totalErrorCount()); - - return AllErrors{ - .list = try arena.allocator.dupe(AllErrors.Message, errors.items), - .arena = arena.state, - }; -} - -pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { - while (self.work_queue.readItem()) |work_item| switch (work_item) { - .codegen_decl => |decl| switch (decl.analysis) { - .unreferenced => unreachable, - .in_progress => unreachable, - .outdated => unreachable, - - .sema_failure, - .codegen_failure, - .dependency_failure, - .sema_failure_retryable, - => continue, - - .complete, .codegen_failure_retryable => { - if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { - switch (payload.func.analysis) { - .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) { - error.AnalysisFail => { - assert(payload.func.analysis != .in_progress); - continue; - }, - error.OutOfMemory => return error.OutOfMemory, - }, - .in_progress => unreachable, - .sema_failure, .dependency_failure => continue, - .success => {}, - } - // Here we tack on additional allocations to the Decl's arena. The allocations are - // lifetime annotations in the ZIR. - var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa); - defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; - log.debug("analyze liveness of {}\n", .{decl.name}); - try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success); - } - - assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); - - self.bin_file.updateDecl(self, decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .dependency_failure; - }, - else => { - try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); - self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( - self.gpa, - decl.src(), - "unable to codegen: {}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - }, - }; - }, - }, - .analyze_decl => |decl| { - self.ensureDeclAnalyzed(decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => continue, - }; - }, - .update_line_number => |decl| { - self.bin_file.updateDeclLineNumber(self, decl) catch |err| { - try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); - self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( - self.gpa, - decl.src(), - "unable to update line number: {}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - }; - }, - }; -} - pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -1227,7 +913,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { error.AnalysisFail => return error.AnalysisFail, else => { try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); - self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create( self.gpa, decl.src(), "unable to analyze: {}", @@ -1457,10 +1143,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { // We don't fully codegen the decl until later, but we do need to reserve a global // offset table index for it. This allows us to codegen decls out of dependency order, // increasing how many computations can be done in parallel. - try self.bin_file.allocateDeclIndexes(decl); - try self.work_queue.writeItem(.{ .codegen_decl = decl }); + try self.comp.bin_file.allocateDeclIndexes(decl); + try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); } else if (prev_type_has_bits) { - self.bin_file.freeDecl(decl); + self.comp.bin_file.freeDecl(decl); } if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { @@ -1708,7 +1394,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { if (zir_module.error_msg) |src_err_msg| { self.failed_files.putAssumeCapacityNoClobber( &root_scope.base, - try ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), + try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), ); root_scope.status = .unloaded_parse_failure; return error.AnalysisFail; @@ -1752,7 +1438,7 @@ fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { defer msg.deinit(); try parse_err.render(tree.token_ids, msg.outStream()); - const err_msg = try self.gpa.create(ErrorMsg); + const err_msg = try self.gpa.create(Compilation.ErrorMsg); err_msg.* = .{ .msg = msg.toOwnedSlice(), .byte_offset = tree.token_locs[parse_err.loc()].start, @@ -1776,7 +1462,7 @@ fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { } } -fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { +pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1785,7 +1471,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { const tree = try self.getAstTree(container_scope); const decls = tree.root_node.decls(); - try self.work_queue.ensureUnusedCapacity(decls.len); + try self.comp.work_queue.ensureUnusedCapacity(decls.len); try container_scope.decls.ensureCapacity(self.gpa, decls.len); // Keep track of the decls that we expect to see in this file so that @@ -1814,21 +1500,21 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { decl.src_index = decl_i; if (deleted_decls.remove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name}); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else { if (!srcHashEql(decl.contents_hash, contents_hash)) { try self.markOutdatedDecl(decl); decl.contents_hash = contents_hash; - } else switch (self.bin_file.tag) { + } else switch (self.comp.bin_file.tag) { .coff => { // TODO Implement for COFF }, .elf => if (decl.fn_link.elf.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); + self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, .macho => { // TODO Implement for MachO @@ -1841,7 +1527,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { container_scope.decls.putAssumeCapacity(new_decl, {}); if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { if (tree.token_ids[maybe_export_token] == .Keyword_export) { - self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); } } } @@ -1856,7 +1542,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { decl.src_index = decl_i; if (deleted_decls.remove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else if (!srcHashEql(decl.contents_hash, contents_hash)) { @@ -1868,7 +1554,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { container_scope.decls.putAssumeCapacity(new_decl, {}); if (var_decl.getExternExportToken()) |maybe_export_token| { if (tree.token_ids[maybe_export_token] == .Keyword_export) { - self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); } } } @@ -1882,7 +1568,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); container_scope.decls.putAssumeCapacity(new_decl, {}); - self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); } else if (src_decl.castTag(.ContainerField)) |container_field| { log.err("TODO: analyze container field", .{}); } else if (src_decl.castTag(.TestDecl)) |test_decl| { @@ -1901,12 +1587,12 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { } } -fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { +pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { // We may be analyzing it for the first time, or this may be // an incremental update. This code handles both cases. const src_module = try self.getSrcModule(root_scope); - try self.work_queue.ensureUnusedCapacity(src_module.decls.len); + try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len); try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len); var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa); @@ -1954,7 +1640,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { } } -fn deleteDecl(self: *Module, decl: *Decl) !void { +pub fn deleteDecl(self: *Module, decl: *Decl) !void { try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); // Remove from the namespace it resides in. In the case of an anonymous Decl it will @@ -1988,7 +1674,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void { entry.value.destroy(self.gpa); } self.deleteDeclExports(decl); - self.bin_file.freeDecl(decl); + self.comp.bin_file.freeDecl(decl); decl.destroy(self.gpa); } @@ -2016,7 +1702,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { self.decl_exports.removeAssertDiscard(exp.exported_decl); } } - if (self.bin_file.cast(link.File.Elf)) |elf| { + if (self.comp.bin_file.cast(link.File.Elf)) |elf| { elf.deleteExport(exp.link); } if (self.failed_exports.remove(exp)) |entry| { @@ -2029,7 +1715,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { self.gpa.free(kv.value); } -fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { +pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2060,7 +1746,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { fn markOutdatedDecl(self: *Module, decl: *Decl) !void { log.debug("mark {} outdated\n", .{decl.name}); - try self.work_queue.writeItem(.{ .analyze_decl = decl }); + try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); if (self.failed_decls.remove(decl)) |entry| { entry.value.destroy(self.gpa); } @@ -2082,14 +1768,14 @@ fn allocateNewDecl( .analysis = .unreferenced, .deletion_flag = false, .contents_hash = contents_hash, - .link = switch (self.bin_file.tag) { + .link = switch (self.comp.bin_file.tag) { .coff => .{ .coff = link.File.Coff.TextBlock.empty }, .elf => .{ .elf = link.File.Elf.TextBlock.empty }, .macho => .{ .macho = link.File.MachO.TextBlock.empty }, .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, }, - .fn_link = switch (self.bin_file.tag) { + .fn_link = switch (self.comp.bin_file.tag) { .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.SrcFn.empty }, .macho => .{ .macho = link.File.MachO.SrcFn.empty }, @@ -2206,7 +1892,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n if (self.symbol_exports.get(symbol_name)) |_| { try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); - self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( + self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( self.gpa, src, "exported symbol collision: {}", @@ -2218,11 +1904,11 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n } try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export); - self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { + self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); - self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( + self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( self.gpa, src, "unable to export: {}", @@ -2502,8 +2188,8 @@ pub fn createAnonymousDecl( // We should be able to further improve the compiler to not omit Decls which are only referenced at // compile-time and not runtime. if (typed_value.ty.hasCodeGenBits()) { - try self.bin_file.allocateDeclIndexes(new_decl); - try self.work_queue.writeItem(.{ .codegen_decl = new_decl }); + try self.comp.bin_file.allocateDeclIndexes(new_decl); + try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl }); } return new_decl; @@ -2756,7 +2442,7 @@ pub fn cmpNumeric( } else if (rhs_ty_tag == .ComptimeFloat) { break :x lhs.ty; } - if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) { + if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) { break :x lhs.ty; } else { break :x rhs.ty; @@ -2815,7 +2501,7 @@ pub fn cmpNumeric( } else if (lhs_is_float) { dest_float_type = lhs.ty; } else { - const int_info = lhs.ty.intInfo(self.target()); + const int_info = lhs.ty.intInfo(self.getTarget()); lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); } @@ -2850,7 +2536,7 @@ pub fn cmpNumeric( } else if (rhs_is_float) { dest_float_type = rhs.ty; } else { - const int_info = rhs.ty.intInfo(self.target()); + const int_info = rhs.ty.intInfo(self.getTarget()); rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); } @@ -2915,13 +2601,13 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty next_inst.ty.isInt() and prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt()) { - if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) { + if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) { prev_inst = next_inst; } continue; } if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { - if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) { + if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) { prev_inst = next_inst; } continue; @@ -2989,10 +2675,10 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { assert(inst.value() == null); // handled above - const src_info = inst.ty.intInfo(self.target()); - const dst_info = dest_type.intInfo(self.target()); + const src_info = inst.ty.intInfo(self.getTarget()); + const dst_info = dest_type.intInfo(self.getTarget()); if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or - // small enough unsigned ints can get casted to large enough signed ints + // small enough unsigned ints can get casted to large enough signed ints (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) { const b = try self.requireRuntimeBlock(scope, inst.src); @@ -3004,8 +2690,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { assert(inst.value() == null); // handled above - const src_bits = inst.ty.floatBits(self.target()); - const dst_bits = dest_type.floatBits(self.target()); + const src_bits = inst.ty.floatBits(self.getTarget()); + const dst_bits = dest_type.floatBits(self.getTarget()); if (dst_bits >= src_bits) { const b = try self.requireRuntimeBlock(scope, inst.src); return self.addUnOp(b, inst.src, dest_type, .floatcast, inst); @@ -3027,14 +2713,14 @@ pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?* } return self.fail(scope, inst.src, "TODO float to int", .{}); } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { - if (!val.intFitsInType(dest_type, self.target())) { + if (!val.intFitsInType(dest_type, self.getTarget())) { return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); } return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); } } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { - const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) { + const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) { error.Overflow => return self.fail( scope, inst.src, @@ -3087,7 +2773,7 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { @setCold(true); - const err_msg = try ErrorMsg.create(self.gpa, src, format, args); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); return self.failWithOwnedErrorMsg(scope, src, err_msg); } @@ -3115,7 +2801,7 @@ pub fn failNode( return self.fail(scope, src, format, args); } -fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { +fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError { { errdefer err_msg.destroy(self.gpa); try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); @@ -3181,36 +2867,6 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult return .no_match; } -pub const ErrorMsg = struct { - byte_offset: usize, - msg: []const u8, - - pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg { - const self = try gpa.create(ErrorMsg); - errdefer gpa.destroy(self); - self.* = try init(gpa, byte_offset, format, args); - return self; - } - - /// Assumes the ErrorMsg struct and msg were both allocated with allocator. - pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { - self.deinit(gpa); - gpa.destroy(self); - } - - pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg { - return ErrorMsg{ - .byte_offset = byte_offset, - .msg = try std.fmt.allocPrint(gpa, format, args), - }; - } - - pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { - gpa.free(self.msg); - self.* = undefined; - } -}; - fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { return @bitCast(u128, a) == @bitCast(u128, b); } @@ -3274,7 +2930,7 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { var bit_count = switch (float_type.tag()) { .comptime_float => 128, - else => float_type.floatBits(self.target()), + else => float_type.floatBits(self.getTarget()), }; const allocator = scope.arena(); @@ -3308,7 +2964,7 @@ pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { var bit_count = switch (float_type.tag()) { .comptime_float => 128, - else => float_type.floatBits(self.target()), + else => float_type.floatBits(self.getTarget()), }; const allocator = scope.arena(); @@ -3579,3 +3235,11 @@ pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: Pani _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); } + +pub fn getTarget(self: Module) Target { + return self.comp.bin_file.options.target; +} + +pub fn optimizeMode(self: Module) std.builtin.Mode { + return self.comp.bin_file.options.optimize_mode; +} diff --git a/src/Package.zig b/src/Package.zig new file mode 100644 index 0000000000..8a0e89f883 --- /dev/null +++ b/src/Package.zig @@ -0,0 +1,62 @@ +pub const Table = std.StringHashMapUnmanaged(*Package); + +root_src_directory: Compilation.Directory, +/// Relative to `root_src_directory`. May contain path separators. +root_src_path: []const u8, +table: Table = .{}, +parent: ?*Package = null, + +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const Package = @This(); +const Compilation = @import("Compilation.zig"); + +/// No references to `root_src_dir` and `root_src_path` are kept. +pub fn create( + gpa: *Allocator, + base_directory: Compilation.Directory, + /// Relative to `base_directory`. + root_src_dir: []const u8, + /// Relative to `root_src_dir`. + root_src_path: []const u8, +) !*Package { + const ptr = try gpa.create(Package); + errdefer gpa.destroy(ptr); + + const root_src_dir_path = try base_directory.join(gpa, &[_][]const u8{root_src_dir}); + errdefer gpa.free(root_src_dir_path); + + const root_src_path_dupe = try mem.dupe(gpa, u8, root_src_path); + errdefer gpa.free(root_src_path_dupe); + + ptr.* = .{ + .root_src_directory = .{ + .path = root_src_dir_path, + .handle = try base_directory.handle.openDir(root_src_dir, .{}), + }, + .root_src_path = root_src_path_dupe, + }; + return ptr; +} + +pub fn destroy(pkg: *Package, gpa: *Allocator) void { + pkg.root_src_directory.handle.close(); + gpa.free(pkg.root_src_path); + if (pkg.root_src_directory.path) |p| gpa.free(p); + { + var it = pkg.table.iterator(); + while (it.next()) |kv| { + gpa.free(kv.key); + } + } + pkg.table.deinit(gpa); + gpa.destroy(pkg); +} + +pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void { + try pkg.table.ensureCapacity(gpa, pkg.table.items().len + 1); + const name_dupe = try mem.dupe(gpa, u8, name); + pkg.table.putAssumeCapacityNoClobber(name_dupe, package); +} diff --git a/src-self-hosted/TypedValue.zig b/src/TypedValue.zig similarity index 100% rename from src-self-hosted/TypedValue.zig rename to src/TypedValue.zig diff --git a/src-self-hosted/astgen.zig b/src/astgen.zig similarity index 100% rename from src-self-hosted/astgen.zig rename to src/astgen.zig diff --git a/src/blake2.h b/src/blake2.h deleted file mode 100644 index 6420c5367a..0000000000 --- a/src/blake2.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_H -#define BLAKE2_H - -#include -#include - -#if defined(_MSC_VER) -#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) -#else -#define BLAKE2_PACKED(x) x __attribute__((packed)) -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - - enum blake2s_constant - { - BLAKE2S_BLOCKBYTES = 64, - BLAKE2S_OUTBYTES = 32, - BLAKE2S_KEYBYTES = 32, - BLAKE2S_SALTBYTES = 8, - BLAKE2S_PERSONALBYTES = 8 - }; - - enum blake2b_constant - { - BLAKE2B_BLOCKBYTES = 128, - BLAKE2B_OUTBYTES = 64, - BLAKE2B_KEYBYTES = 64, - BLAKE2B_SALTBYTES = 16, - BLAKE2B_PERSONALBYTES = 16 - }; - - typedef struct blake2s_state__ - { - uint32_t h[8]; - uint32_t t[2]; - uint32_t f[2]; - uint8_t buf[BLAKE2S_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2s_state; - - typedef struct blake2b_state__ - { - uint64_t h[8]; - uint64_t t[2]; - uint64_t f[2]; - uint8_t buf[BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2b_state; - - typedef struct blake2sp_state__ - { - blake2s_state S[8][1]; - blake2s_state R[1]; - uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; - size_t buflen; - size_t outlen; - } blake2sp_state; - - typedef struct blake2bp_state__ - { - blake2b_state S[4][1]; - blake2b_state R[1]; - uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - } blake2bp_state; - - - BLAKE2_PACKED(struct blake2s_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint16_t xof_length; /* 14 */ - uint8_t node_depth; /* 15 */ - uint8_t inner_length; /* 16 */ - /* uint8_t reserved[0]; */ - uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ - uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ - }); - - typedef struct blake2s_param__ blake2s_param; - - BLAKE2_PACKED(struct blake2b_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint32_t xof_length; /* 16 */ - uint8_t node_depth; /* 17 */ - uint8_t inner_length; /* 18 */ - uint8_t reserved[14]; /* 32 */ - uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ - uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ - }); - - typedef struct blake2b_param__ blake2b_param; - - typedef struct blake2xs_state__ - { - blake2s_state S[1]; - blake2s_param P[1]; - } blake2xs_state; - - typedef struct blake2xb_state__ - { - blake2b_state S[1]; - blake2b_param P[1]; - } blake2xb_state; - - /* Padded structs result in a compile-time error */ - enum { - BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), - BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) - }; - - /* Streaming API */ - int blake2s_init( blake2s_state *S, size_t outlen ); - int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); - int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); - int blake2s_final( blake2s_state *S, void *out, size_t outlen ); - - int blake2b_init( blake2b_state *S, size_t outlen ); - int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); - int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); - int blake2b_final( blake2b_state *S, void *out, size_t outlen ); - - int blake2sp_init( blake2sp_state *S, size_t outlen ); - int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); - int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); - - int blake2bp_init( blake2bp_state *S, size_t outlen ); - int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); - int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); - - /* Variable output length API */ - int blake2xs_init( blake2xs_state *S, const size_t outlen ); - int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); - int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); - - int blake2xb_init( blake2xb_state *S, const size_t outlen ); - int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); - int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); - - /* Simple API */ - int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - /* This is simply an alias for blake2b */ - int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - -#if defined(__cplusplus) -} -#endif - -#endif - diff --git a/src/blake2b.c b/src/blake2b.c deleted file mode 100644 index 600f951b9b..0000000000 --- a/src/blake2b.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_IMPL_H -#define BLAKE2_IMPL_H - -#include -#include - -#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) - #if defined(_MSC_VER) - #define BLAKE2_INLINE __inline - #elif defined(__GNUC__) - #define BLAKE2_INLINE __inline__ - #else - #define BLAKE2_INLINE - #endif -#else - #define BLAKE2_INLINE inline -#endif - -static BLAKE2_INLINE uint32_t load32( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint32_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8) | - (( uint32_t )( p[2] ) << 16) | - (( uint32_t )( p[3] ) << 24) ; -#endif -} - -static BLAKE2_INLINE uint64_t load64( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint64_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) | - (( uint64_t )( p[6] ) << 48) | - (( uint64_t )( p[7] ) << 56) ; -#endif -} - -static BLAKE2_INLINE uint16_t load16( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint16_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return ( uint16_t )((( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8)); -#endif -} - -static BLAKE2_INLINE void store16( void *dst, uint16_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - *p++ = ( uint8_t )w; w >>= 8; - *p++ = ( uint8_t )w; -#endif -} - -static BLAKE2_INLINE void store32( void *dst, uint32_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); -#endif -} - -static BLAKE2_INLINE void store64( void *dst, uint64_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); - p[6] = (uint8_t)(w >> 48); - p[7] = (uint8_t)(w >> 56); -#endif -} - -static BLAKE2_INLINE uint64_t load48( const void *src ) -{ - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) ; -} - -static BLAKE2_INLINE void store48( void *dst, uint64_t w ) -{ - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); -} - -static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 32 - c ) ); -} - -static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 64 - c ) ); -} - -/* prevents compiler optimizing out memset() */ -static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) -{ - static void *(*const volatile memset_v)(void *, int, size_t) = &memset; - memset_v(v, 0, n); -} - -#endif - -static const uint64_t blake2b_IV[8] = -{ - 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL -}; - -static const uint8_t blake2b_sigma[12][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } -}; - - -static void blake2b_set_lastnode( blake2b_state *S ) -{ - S->f[1] = (uint64_t)-1; -} - -/* Some helper functions, not necessarily useful */ -static int blake2b_is_lastblock( const blake2b_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2b_set_lastblock( blake2b_state *S ) -{ - if( S->last_node ) blake2b_set_lastnode( S ); - - S->f[0] = (uint64_t)-1; -} - -static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) -{ - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); -} - -static void blake2b_init0( blake2b_state *S ) -{ - size_t i; - memset( S, 0, sizeof( blake2b_state ) ); - - for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; -} - -/* init xors IV with input parameter block */ -int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) -{ - const uint8_t *p = ( const uint8_t * )( P ); - size_t i; - - blake2b_init0( S ); - - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); - - S->outlen = P->digest_length; - return 0; -} - - - -int blake2b_init( blake2b_state *S, size_t outlen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2b_init_param( S, P ); -} - - -int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2b_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2b_sigma[r][2*i+0]]; \ - d = rotr64(d ^ a, 32); \ - c = c + d; \ - b = rotr64(b ^ c, 24); \ - a = a + b + m[blake2b_sigma[r][2*i+1]]; \ - d = rotr64(d ^ a, 16); \ - c = c + d; \ - b = rotr64(b ^ c, 63); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) -{ - uint64_t m[16]; - uint64_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load64( block + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2b_IV[0]; - v[ 9] = blake2b_IV[1]; - v[10] = blake2b_IV[2]; - v[11] = blake2b_IV[3]; - v[12] = blake2b_IV[4] ^ S->t[0]; - v[13] = blake2b_IV[5] ^ S->t[1]; - v[14] = blake2b_IV[6] ^ S->f[0]; - v[15] = blake2b_IV[7] ^ S->f[1]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - ROUND( 10 ); - ROUND( 11 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } -} - -#undef G -#undef ROUND - -int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2B_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); - blake2b_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2B_BLOCKBYTES) { - blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); - blake2b_compress( S, in ); - in += BLAKE2B_BLOCKBYTES; - inlen -= BLAKE2B_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - -int blake2b_final( blake2b_state *S, void *out, size_t outlen ) -{ - uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; - size_t i; - - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2b_is_lastblock( S ) ) - return -1; - - blake2b_increment_counter( S, S->buflen ); - blake2b_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ - blake2b_compress( S, S->buf ); - - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); - - memcpy( out, buffer, S->outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; -} - -/* inlen, at least, should be uint64_t. Others can be size_t. */ -int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2b_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - if( keylen > 0 ) - { - if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2b_init( S, outlen ) < 0 ) return -1; - } - - blake2b_update( S, ( const uint8_t * )in, inlen ); - blake2b_final( S, out, outlen ); - return 0; -} - -int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { - return blake2b(out, outlen, in, inlen, key, keylen); -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2B_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2b_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2b_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif - diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp deleted file mode 100644 index c12d8f29ef..0000000000 --- a/src/cache_hash.cpp +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "stage2.h" -#include "cache_hash.hpp" -#include "all_types.hpp" -#include "buffer.hpp" -#include "os.hpp" - -#include - -void cache_init(CacheHash *ch, Buf *manifest_dir) { - int rc = blake2b_init(&ch->blake, 48); - assert(rc == 0); - ch->files = {}; - ch->manifest_dir = manifest_dir; - ch->manifest_file_path = nullptr; - ch->manifest_dirty = false; - ch->force_check_manifest = false; - ch->b64_digest = BUF_INIT; -} - -void cache_mem(CacheHash *ch, const char *ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - assert(ptr != nullptr); - blake2b_update(&ch->blake, ptr, len); -} - -void cache_slice(CacheHash *ch, Slice slice) { - // mix the length into the hash so that two juxtaposed cached slices can't collide - cache_usize(ch, slice.len); - cache_mem(ch, slice.ptr, slice.len); -} - -void cache_str(CacheHash *ch, const char *ptr) { - // + 1 to include the null byte - cache_mem(ch, ptr, strlen(ptr) + 1); -} - -void cache_int(CacheHash *ch, int x) { - assert(ch->manifest_file_path == nullptr); - // + 1 to include the null byte - uint8_t buf[sizeof(int) + 1]; - memcpy(buf, &x, sizeof(int)); - buf[sizeof(int)] = 0; - blake2b_update(&ch->blake, buf, sizeof(int) + 1); -} - -void cache_usize(CacheHash *ch, size_t x) { - assert(ch->manifest_file_path == nullptr); - // + 1 to include the null byte - uint8_t buf[sizeof(size_t) + 1]; - memcpy(buf, &x, sizeof(size_t)); - buf[sizeof(size_t)] = 0; - blake2b_update(&ch->blake, buf, sizeof(size_t) + 1); -} - -void cache_bool(CacheHash *ch, bool x) { - assert(ch->manifest_file_path == nullptr); - blake2b_update(&ch->blake, &x, 1); -} - -void cache_buf(CacheHash *ch, Buf *buf) { - assert(ch->manifest_file_path == nullptr); - assert(buf != nullptr); - // + 1 to include the null byte - blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1); -} - -void cache_buf_opt(CacheHash *ch, Buf *buf) { - assert(ch->manifest_file_path == nullptr); - if (buf == nullptr) { - cache_str(ch, ""); - cache_str(ch, ""); - } else { - cache_buf(ch, buf); - } -} - -void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - for (size_t i = 0; i < len; i += 1) { - LinkLib *lib = ptr[i]; - if (lib->provided_explicitly) { - cache_buf(ch, lib->name); - } - } - cache_str(ch, ""); -} - -void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - for (size_t i = 0; i < len; i += 1) { - Buf *buf = ptr[i]; - cache_buf(ch, buf); - } - cache_str(ch, ""); -} - -void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - - for (size_t i = 0; i < len; i += 1) { - Buf *buf = ptr[i]; - cache_file(ch, buf); - } - cache_str(ch, ""); -} - -void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - - for (size_t i = 0; i < len; i += 1) { - const char *s = ptr[i]; - cache_str(ch, s); - } - cache_str(ch, ""); -} - -void cache_file(CacheHash *ch, Buf *file_path) { - assert(ch->manifest_file_path == nullptr); - assert(file_path != nullptr); - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(&file_path, 1); - CacheHashFile *chf = ch->files.add_one(); - chf->path = resolved_path; - cache_buf(ch, resolved_path); -} - -void cache_file_opt(CacheHash *ch, Buf *file_path) { - assert(ch->manifest_file_path == nullptr); - if (file_path == nullptr) { - cache_str(ch, ""); - cache_str(ch, ""); - } else { - cache_file(ch, file_path); - } -} - -// Ported from std/base64.zig -static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -static void base64_encode(Slice dest, Slice source) { - size_t dest_len = ((source.len + 2) / 3) * 4; - assert(dest.len == dest_len); - - size_t i = 0; - size_t out_index = 0; - for (; i + 2 < source.len; i += 3) { - dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f]; - out_index += 1; - - dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)]; - out_index += 1; - - dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)]; - out_index += 1; - - dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f]; - out_index += 1; - } - - // Assert that we never need pad characters. - assert(i == source.len); -} - -// Ported from std/base64.zig -static Error base64_decode(Slice dest, Slice source) { - if (source.len % 4 != 0) - return ErrorInvalidFormat; - if (dest.len != (source.len / 4) * 3) - return ErrorInvalidFormat; - - // In Zig this is comptime computed. In C++ it's not worth it to do that. - uint8_t char_to_index[256]; - bool char_in_alphabet[256] = {0}; - for (size_t i = 0; i < 64; i += 1) { - uint8_t c = base64_fs_alphabet[i]; - assert(!char_in_alphabet[c]); - char_in_alphabet[c] = true; - char_to_index[c] = i; - } - - size_t src_cursor = 0; - size_t dest_cursor = 0; - - for (;src_cursor < source.len; src_cursor += 4) { - if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat; - if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat; - if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat; - if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat; - dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4); - dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2); - dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]); - dest_cursor += 3; - } - - assert(src_cursor == source.len); - assert(dest_cursor == dest.len); - return ErrorNone; -} - -static Error hash_file(uint8_t *digest, OsFile handle, Buf *contents) { - Error err; - - if (contents) { - buf_resize(contents, 0); - } - - blake2b_state blake; - int rc = blake2b_init(&blake, 48); - assert(rc == 0); - - for (;;) { - uint8_t buf[4096]; - size_t amt = 4096; - if ((err = os_file_read(handle, buf, &amt))) - return err; - if (amt == 0) { - rc = blake2b_final(&blake, digest, 48); - assert(rc == 0); - return ErrorNone; - } - blake2b_update(&blake, buf, amt); - if (contents) { - buf_append_mem(contents, (char*)buf, amt); - } - } -} - -// If the wall clock time, rounded to the same precision as the -// mtime, is equal to the mtime, then we cannot rely on this mtime -// yet. We will instead save an mtime value that indicates the hash -// must be unconditionally computed. -static bool is_problematic_timestamp(const OsTimeStamp *fs_clock) { - OsTimeStamp wall_clock = os_timestamp_calendar(); - // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock. - if (fs_clock->nsec == 0) { - wall_clock.nsec = 0; - if (fs_clock->sec == 0) { - wall_clock.sec = 0; - } else { - wall_clock.sec &= (-1ull) << ctzll(fs_clock->sec); - } - } else { - wall_clock.nsec &= (-1ull) << ctzll(fs_clock->nsec); - } - return wall_clock.nsec == fs_clock->nsec && wall_clock.sec == fs_clock->sec; -} - -static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents) { - Error err; - - assert(chf->path != nullptr); - - OsFile this_file; - if ((err = os_file_open_r(chf->path, &this_file, &chf->attr))) - return err; - - if (is_problematic_timestamp(&chf->attr.mtime)) { - chf->attr.mtime.sec = 0; - chf->attr.mtime.nsec = 0; - chf->attr.inode = 0; - } - - if ((err = hash_file(chf->bin_digest, this_file, contents))) { - os_file_close(&this_file); - return err; - } - os_file_close(&this_file); - - blake2b_update(&ch->blake, chf->bin_digest, 48); - - return ErrorNone; -} - -Error cache_hit(CacheHash *ch, Buf *out_digest) { - Error err; - - uint8_t bin_digest[48]; - int rc = blake2b_final(&ch->blake, bin_digest, 48); - assert(rc == 0); - - buf_resize(&ch->b64_digest, 64); - base64_encode(buf_to_slice(&ch->b64_digest), {bin_digest, 48}); - - if (ch->files.length == 0 && !ch->force_check_manifest) { - buf_resize(out_digest, 64); - base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); - return ErrorNone; - } - - rc = blake2b_init(&ch->blake, 48); - assert(rc == 0); - blake2b_update(&ch->blake, bin_digest, 48); - - ch->manifest_file_path = buf_alloc(); - os_path_join(ch->manifest_dir, &ch->b64_digest, ch->manifest_file_path); - - buf_append_str(ch->manifest_file_path, ".txt"); - - if ((err = os_make_path(ch->manifest_dir))) - return err; - - if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file))) - return err; - - Buf line_buf = BUF_INIT; - buf_resize(&line_buf, 512); - if ((err = os_file_read_all(ch->manifest_file, &line_buf))) { - os_file_close(&ch->manifest_file); - return err; - } - - size_t input_file_count = ch->files.length; - bool any_file_changed = false; - Error return_code = ErrorNone; - size_t file_i = 0; - SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n")); - for (;; file_i += 1) { - Optional> opt_line = SplitIterator_next(&line_it); - - CacheHashFile *chf; - if (file_i < input_file_count) { - chf = &ch->files.at(file_i); - } else if (any_file_changed) { - // cache miss. - // keep the manifest file open with the rw lock - // reset the hash - rc = blake2b_init(&ch->blake, 48); - assert(rc == 0); - blake2b_update(&ch->blake, bin_digest, 48); - ch->files.resize(input_file_count); - // bring the hash up to the input file hashes - for (file_i = 0; file_i < input_file_count; file_i += 1) { - blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48); - } - // caller can notice that out_digest is unmodified. - return return_code; - } else if (!opt_line.is_some) { - break; - } else { - chf = ch->files.add_one(); - chf->path = nullptr; - } - - if (!opt_line.is_some) - break; - - SplitIterator it = memSplit(opt_line.value, str(" ")); - - Optional> opt_inode = SplitIterator_next(&it); - if (!opt_inode.is_some) { - return_code = ErrorInvalidFormat; - break; - } - chf->attr.inode = strtoull((const char *)opt_inode.value.ptr, nullptr, 10); - - Optional> opt_mtime_sec = SplitIterator_next(&it); - if (!opt_mtime_sec.is_some) { - return_code = ErrorInvalidFormat; - break; - } - chf->attr.mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10); - - Optional> opt_mtime_nsec = SplitIterator_next(&it); - if (!opt_mtime_nsec.is_some) { - return_code = ErrorInvalidFormat; - break; - } - chf->attr.mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10); - - Optional> opt_digest = SplitIterator_next(&it); - if (!opt_digest.is_some) { - return_code = ErrorInvalidFormat; - break; - } - if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) { - return_code = ErrorInvalidFormat; - break; - } - - Slice file_path = SplitIterator_rest(&it); - if (file_path.len == 0) { - return_code = ErrorInvalidFormat; - break; - } - Buf *this_path = buf_create_from_slice(file_path); - if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) { - return_code = ErrorInvalidFormat; - break; - } - chf->path = this_path; - - // if the mtime matches we can trust the digest - OsFile this_file; - OsFileAttr actual_attr; - if ((err = os_file_open_r(chf->path, &this_file, &actual_attr))) { - fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err)); - os_file_close(&ch->manifest_file); - return ErrorCacheUnavailable; - } - if (chf->attr.mtime.sec == actual_attr.mtime.sec && - chf->attr.mtime.nsec == actual_attr.mtime.nsec && - chf->attr.inode == actual_attr.inode) - { - os_file_close(&this_file); - } else { - // we have to recompute the digest. - // later we'll rewrite the manifest with the new mtime/digest values - ch->manifest_dirty = true; - chf->attr = actual_attr; - - if (is_problematic_timestamp(&actual_attr.mtime)) { - chf->attr.mtime.sec = 0; - chf->attr.mtime.nsec = 0; - chf->attr.inode = 0; - } - - uint8_t actual_digest[48]; - if ((err = hash_file(actual_digest, this_file, nullptr))) { - os_file_close(&this_file); - os_file_close(&ch->manifest_file); - return err; - } - os_file_close(&this_file); - if (memcmp(chf->bin_digest, actual_digest, 48) != 0) { - memcpy(chf->bin_digest, actual_digest, 48); - // keep going until we have the input file digests - any_file_changed = true; - } - } - if (!any_file_changed) { - blake2b_update(&ch->blake, chf->bin_digest, 48); - } - } - if (file_i < input_file_count || file_i == 0 || return_code != ErrorNone) { - // manifest file is empty or missing entries, so this is a cache miss - ch->manifest_dirty = true; - for (; file_i < input_file_count; file_i += 1) { - CacheHashFile *chf = &ch->files.at(file_i); - if ((err = populate_file_hash(ch, chf, nullptr))) { - fprintf(stderr, "Unable to hash %s: %s\n", buf_ptr(chf->path), err_str(err)); - os_file_close(&ch->manifest_file); - return ErrorCacheUnavailable; - } - } - if (return_code != ErrorNone && return_code != ErrorInvalidFormat) { - os_file_close(&ch->manifest_file); - } - return return_code; - } - // Cache Hit - return cache_final(ch, out_digest); -} - -Error cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents) { - Error err; - - assert(ch->manifest_file_path != nullptr); - CacheHashFile *chf = ch->files.add_one(); - chf->path = resolved_path; - if ((err = populate_file_hash(ch, chf, contents))) { - os_file_close(&ch->manifest_file); - return err; - } - - return ErrorNone; -} - -Error cache_add_file(CacheHash *ch, Buf *path) { - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(&path, 1); - return cache_add_file_fetch(ch, resolved_path, nullptr); -} - -Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) { - Error err; - Buf *contents = buf_alloc(); - if ((err = os_fetch_file_path(dep_file_path, contents))) { - if (err == ErrorFileNotFound) - return err; - if (verbose) { - fprintf(stderr, "%s: unable to read .d file: %s\n", err_str(err), buf_ptr(dep_file_path)); - } - return ErrorReadingDepFile; - } - auto it = stage2_DepTokenizer_init(buf_ptr(contents), buf_len(contents)); - // skip first token: target - { - auto result = stage2_DepTokenizer_next(&it); - switch (result.type_id) { - case stage2_DepNextResult::error: - if (verbose) { - fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path)); - } - err = ErrorInvalidDepFile; - goto finish; - case stage2_DepNextResult::null: - err = ErrorNone; - goto finish; - case stage2_DepNextResult::target: - case stage2_DepNextResult::prereq: - err = ErrorNone; - break; - } - } - // Process 0+ preqreqs. - // clang is invoked in single-source mode so we never get more targets. - for (;;) { - auto result = stage2_DepTokenizer_next(&it); - switch (result.type_id) { - case stage2_DepNextResult::error: - if (verbose) { - fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path)); - } - err = ErrorInvalidDepFile; - goto finish; - case stage2_DepNextResult::null: - case stage2_DepNextResult::target: - err = ErrorNone; - goto finish; - case stage2_DepNextResult::prereq: - break; - } - auto textbuf = buf_alloc(); - buf_init_from_str(textbuf, result.textz); - if ((err = cache_add_file(ch, textbuf))) { - if (verbose) { - fprintf(stderr, "unable to add %s to cache: %s\n", result.textz, err_str(err)); - fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path)); - } - goto finish; - } - } - - finish: - stage2_DepTokenizer_deinit(&it); - return err; -} - -static Error write_manifest_file(CacheHash *ch) { - Error err; - Buf contents = BUF_INIT; - buf_resize(&contents, 0); - uint8_t encoded_digest[65]; - encoded_digest[64] = 0; - for (size_t i = 0; i < ch->files.length; i += 1) { - CacheHashFile *chf = &ch->files.at(i); - base64_encode({encoded_digest, 64}, {chf->bin_digest, 48}); - buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n", - chf->attr.inode, chf->attr.mtime.sec, chf->attr.mtime.nsec, encoded_digest, buf_ptr(chf->path)); - } - if ((err = os_file_overwrite(ch->manifest_file, &contents))) - return err; - - return ErrorNone; -} - -Error cache_final(CacheHash *ch, Buf *out_digest) { - assert(ch->manifest_file_path != nullptr); - - // We don't close the manifest file yet, because we want to - // keep it locked until the API user is done using it. - // We also don't write out the manifest yet, because until - // cache_release is called we still might be working on creating - // the artifacts to cache. - - uint8_t bin_digest[48]; - int rc = blake2b_final(&ch->blake, bin_digest, 48); - assert(rc == 0); - buf_resize(out_digest, 64); - base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); - - return ErrorNone; -} - -void cache_release(CacheHash *ch) { - assert(ch->manifest_file_path != nullptr); - - Error err; - - if (ch->manifest_dirty) { - if ((err = write_manifest_file(ch))) { - fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n", - buf_ptr(ch->manifest_file_path), err_str(err)); - } - } - - os_file_close(&ch->manifest_file); -} - diff --git a/src/cache_hash.hpp b/src/cache_hash.hpp deleted file mode 100644 index ba2434076a..0000000000 --- a/src/cache_hash.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_CACHE_HASH_HPP -#define ZIG_CACHE_HASH_HPP - -#include "blake2.h" -#include "os.hpp" - -struct LinkLib; - -struct CacheHashFile { - Buf *path; - OsFileAttr attr; - uint8_t bin_digest[48]; - Buf *contents; -}; - -struct CacheHash { - blake2b_state blake; - ZigList files; - Buf *manifest_dir; - Buf *manifest_file_path; - Buf b64_digest; - OsFile manifest_file; - bool manifest_dirty; - bool force_check_manifest; -}; - -// Always call this first to set up. -void cache_init(CacheHash *ch, Buf *manifest_dir); - -// Next, use the hash population functions to add the initial parameters. -void cache_mem(CacheHash *ch, const char *ptr, size_t len); -void cache_slice(CacheHash *ch, Slice slice); -void cache_str(CacheHash *ch, const char *ptr); -void cache_int(CacheHash *ch, int x); -void cache_bool(CacheHash *ch, bool x); -void cache_usize(CacheHash *ch, size_t x); -void cache_buf(CacheHash *ch, Buf *buf); -void cache_buf_opt(CacheHash *ch, Buf *buf); -void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len); -void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len); -void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len); -void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len); -void cache_file(CacheHash *ch, Buf *path); -void cache_file_opt(CacheHash *ch, Buf *path); - -// Then call cache_hit when you're ready to see if you can skip the next step. -// out_b64_digest will be left unchanged if it was a cache miss. -// If you got a cache hit, the next step is cache_release. -// From this point on, there is a lock on the input params. Release -// the lock with cache_release. -// Set force_check_manifest if you plan to add files later, but have not -// added any files before calling cache_hit. CacheHash::b64_digest becomes -// available for use after this call, even in the case of a miss, and it -// is a hash of the input parameters only. -// If this function returns ErrorInvalidFormat, that error may be treated -// as a cache miss. -Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest); - -// If you did not get a cache hit, call this function for every file -// that is depended on, and then finish with cache_final. -Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path); -// This opens a file created by -MD -MF args to Clang -Error ATTRIBUTE_MUST_USE cache_add_dep_file(CacheHash *ch, Buf *path, bool verbose); - -// This variant of cache_add_file returns the file contents. -// Also the file path argument must be already resolved. -Error ATTRIBUTE_MUST_USE cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents); - -// out_b64_digest will be the same thing that cache_hit returns if you got a cache hit -Error ATTRIBUTE_MUST_USE cache_final(CacheHash *ch, Buf *out_b64_digest); - -// Until this function is called, no one will be able to get a lock on your input params. -void cache_release(CacheHash *ch); - - -#endif diff --git a/src-self-hosted/clang.zig b/src/clang.zig similarity index 100% rename from src-self-hosted/clang.zig rename to src/clang.zig diff --git a/src-self-hosted/clang_options.zig b/src/clang_options.zig similarity index 93% rename from src-self-hosted/clang_options.zig rename to src/clang_options.zig index 1b70c71dac..42bfecb746 100644 --- a/src-self-hosted/clang_options.zig +++ b/src/clang_options.zig @@ -7,9 +7,7 @@ pub const CliArg = struct { name: []const u8, syntax: Syntax, - /// TODO we're going to want to change this when we start shipping self-hosted because this causes - /// all the functions in stage2.zig to get exported. - zig_equivalent: @import("stage2.zig").ClangArgIterator.ZigEquivalent, + zig_equivalent: @import("main.zig").ClangArgIterator.ZigEquivalent, /// Prefixed by "-" pd1: bool = false, diff --git a/src-self-hosted/clang_options_data.zig b/src/clang_options_data.zig similarity index 99% rename from src-self-hosted/clang_options_data.zig rename to src/clang_options_data.zig index 889737bdac..bd1237bc00 100644 --- a/src-self-hosted/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -7,7 +7,7 @@ flagpd1("CC"), .{ .name = "E", .syntax = .flag, - .zig_equivalent = .pp_or_asm, + .zig_equivalent = .preprocess_only, .pd1 = true, .pd2 = false, .psl = false, @@ -95,7 +95,7 @@ flagpd1("Qy"), .{ .name = "S", .syntax = .flag, - .zig_equivalent = .pp_or_asm, + .zig_equivalent = .asm_only, .pd1 = true, .pd2 = false, .psl = false, @@ -196,7 +196,7 @@ sepd1("Zlinker-input"), .{ .name = "E", .syntax = .flag, - .zig_equivalent = .pp_or_asm, + .zig_equivalent = .preprocess_only, .pd1 = true, .pd2 = false, .psl = true, @@ -1477,7 +1477,7 @@ flagpsl("MT"), .{ .name = "assemble", .syntax = .flag, - .zig_equivalent = .pp_or_asm, + .zig_equivalent = .asm_only, .pd1 = false, .pd2 = true, .psl = false, @@ -1805,7 +1805,7 @@ flagpsl("MT"), .{ .name = "preprocess", .syntax = .flag, - .zig_equivalent = .pp_or_asm, + .zig_equivalent = .preprocess_only, .pd1 = false, .pd2 = true, .psl = false, @@ -3406,6 +3406,8 @@ flagpd1("mlong-double-128"), flagpd1("mlong-double-64"), flagpd1("mlong-double-80"), flagpd1("mlongcall"), +flagpd1("mlvi-cfi"), +flagpd1("mlvi-hardening"), flagpd1("mlwp"), flagpd1("mlzcnt"), flagpd1("mmadd4"), @@ -3499,6 +3501,8 @@ flagpd1("mno-ldc1-sdc1"), flagpd1("mno-local-sdata"), flagpd1("mno-long-calls"), flagpd1("mno-longcall"), +flagpd1("mno-lvi-cfi"), +flagpd1("mno-lvi-hardening"), flagpd1("mno-lwp"), flagpd1("mno-lzcnt"), flagpd1("mno-madd4"), diff --git a/src/codegen.hpp b/src/codegen.hpp deleted file mode 100644 index 3139071d52..0000000000 --- a/src/codegen.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_CODEGEN_HPP -#define ZIG_CODEGEN_HPP - -#include "parser.hpp" -#include "errmsg.hpp" -#include "target.hpp" -#include "stage2.h" - -#include - -CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, - OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, - Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node); - -CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node); - -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); -void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath); - -void codegen_set_strip(CodeGen *codegen, bool strip); -void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color); -void codegen_set_out_name(CodeGen *codegen, Buf *out_name); -void codegen_add_lib_dir(CodeGen *codegen, const char *dir); -void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); -LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); -void codegen_add_framework(CodeGen *codegen, const char *name); -void codegen_add_rpath(CodeGen *codegen, const char *name); -void codegen_set_rdynamic(CodeGen *g, bool rdynamic); -void codegen_set_linker_script(CodeGen *g, const char *linker_script); -void codegen_set_test_filter(CodeGen *g, Buf *filter); -void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); -void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch); -void codegen_add_time_event(CodeGen *g, const char *name); -void codegen_print_timing_report(CodeGen *g, FILE *f); -void codegen_link(CodeGen *g); -void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node); -void codegen_build_and_link(CodeGen *g); - -ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, - const char *pkg_path); -void codegen_add_assembly(CodeGen *g, Buf *path); -void codegen_add_object(CodeGen *g, Buf *object_path); - -void codegen_translate_c(CodeGen *g, Buf *full_path); - -Buf *codegen_generate_builtin_source(CodeGen *g); - -TargetSubsystem detect_subsystem(CodeGen *g); - -void codegen_release_caches(CodeGen *codegen); -bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); -bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); - -ATTRIBUTE_NORETURN -void codegen_report_errors_and_exit(CodeGen *g); - -void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node); - -#endif diff --git a/src-self-hosted/codegen.zig b/src/codegen.zig similarity index 99% rename from src-self-hosted/codegen.zig rename to src/codegen.zig index 9405a5f72c..a1d3cc2fc4 100644 --- a/src-self-hosted/codegen.zig +++ b/src/codegen.zig @@ -8,7 +8,8 @@ const Value = @import("value.zig").Value; const TypedValue = @import("TypedValue.zig"); const link = @import("link.zig"); const Module = @import("Module.zig"); -const ErrorMsg = Module.ErrorMsg; +const Compilation = @import("Compilation.zig"); +const ErrorMsg = Compilation.ErrorMsg; const Target = std.Target; const Allocator = mem.Allocator; const trace = @import("tracy.zig").trace; @@ -50,7 +51,7 @@ pub const Result = union(enum) { appended: void, /// The value is available externally, `code` is unused. externally_managed: []const u8, - fail: *Module.ErrorMsg, + fail: *ErrorMsg, }; pub const GenerateSymbolError = error{ diff --git a/src-self-hosted/codegen/arm.zig b/src/codegen/arm.zig similarity index 100% rename from src-self-hosted/codegen/arm.zig rename to src/codegen/arm.zig diff --git a/src-self-hosted/codegen/c.zig b/src/codegen/c.zig similarity index 100% rename from src-self-hosted/codegen/c.zig rename to src/codegen/c.zig diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig new file mode 100644 index 0000000000..01fa0baf02 --- /dev/null +++ b/src/codegen/llvm.zig @@ -0,0 +1,125 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 { + const llvm_arch = switch (target.cpu.arch) { + .arm => "arm", + .armeb => "armeb", + .aarch64 => "aarch64", + .aarch64_be => "aarch64_be", + .aarch64_32 => "aarch64_32", + .arc => "arc", + .avr => "avr", + .bpfel => "bpfel", + .bpfeb => "bpfeb", + .hexagon => "hexagon", + .mips => "mips", + .mipsel => "mipsel", + .mips64 => "mips64", + .mips64el => "mips64el", + .msp430 => "msp430", + .powerpc => "powerpc", + .powerpc64 => "powerpc64", + .powerpc64le => "powerpc64le", + .r600 => "r600", + .amdgcn => "amdgcn", + .riscv32 => "riscv32", + .riscv64 => "riscv64", + .sparc => "sparc", + .sparcv9 => "sparcv9", + .sparcel => "sparcel", + .s390x => "s390x", + .tce => "tce", + .tcele => "tcele", + .thumb => "thumb", + .thumbeb => "thumbeb", + .i386 => "i386", + .x86_64 => "x86_64", + .xcore => "xcore", + .nvptx => "nvptx", + .nvptx64 => "nvptx64", + .le32 => "le32", + .le64 => "le64", + .amdil => "amdil", + .amdil64 => "amdil64", + .hsail => "hsail", + .hsail64 => "hsail64", + .spir => "spir", + .spir64 => "spir64", + .kalimba => "kalimba", + .shave => "shave", + .lanai => "lanai", + .wasm32 => "wasm32", + .wasm64 => "wasm64", + .renderscript32 => "renderscript32", + .renderscript64 => "renderscript64", + .ve => "ve", + .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, + }; + // TODO Add a sub-arch for some architectures depending on CPU features. + + const llvm_os = switch (target.os.tag) { + .freestanding => "unknown", + .ananas => "ananas", + .cloudabi => "cloudabi", + .dragonfly => "dragonfly", + .freebsd => "freebsd", + .fuchsia => "fuchsia", + .ios => "ios", + .kfreebsd => "kfreebsd", + .linux => "linux", + .lv2 => "lv2", + .macosx => "macosx", + .netbsd => "netbsd", + .openbsd => "openbsd", + .solaris => "solaris", + .windows => "windows", + .haiku => "haiku", + .minix => "minix", + .rtems => "rtems", + .nacl => "nacl", + .cnk => "cnk", + .aix => "aix", + .cuda => "cuda", + .nvcl => "nvcl", + .amdhsa => "amdhsa", + .ps4 => "ps4", + .elfiamcu => "elfiamcu", + .tvos => "tvos", + .watchos => "watchos", + .mesa3d => "mesa3d", + .contiki => "contiki", + .amdpal => "amdpal", + .hermit => "hermit", + .hurd => "hurd", + .wasi => "wasi", + .emscripten => "emscripten", + .uefi => "windows", + .other => "unknown", + }; + + const llvm_abi = switch (target.abi) { + .none => "unknown", + .gnu => "gnu", + .gnuabin32 => "gnuabin32", + .gnuabi64 => "gnuabi64", + .gnueabi => "gnueabi", + .gnueabihf => "gnueabihf", + .gnux32 => "gnux32", + .code16 => "code16", + .eabi => "eabi", + .eabihf => "eabihf", + .android => "android", + .musl => "musl", + .musleabi => "musleabi", + .musleabihf => "musleabihf", + .msvc => "msvc", + .itanium => "itanium", + .cygnus => "cygnus", + .coreclr => "coreclr", + .simulator => "simulator", + .macabi => "macabi", + }; + + return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi }); +} diff --git a/src-self-hosted/codegen/riscv64.zig b/src/codegen/riscv64.zig similarity index 100% rename from src-self-hosted/codegen/riscv64.zig rename to src/codegen/riscv64.zig diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src/codegen/spu-mk2.zig similarity index 100% rename from src-self-hosted/codegen/spu-mk2.zig rename to src/codegen/spu-mk2.zig diff --git a/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src/codegen/spu-mk2/interpreter.zig similarity index 100% rename from src-self-hosted/codegen/spu-mk2/interpreter.zig rename to src/codegen/spu-mk2/interpreter.zig diff --git a/src-self-hosted/codegen/wasm.zig b/src/codegen/wasm.zig similarity index 98% rename from src-self-hosted/codegen/wasm.zig rename to src/codegen/wasm.zig index e55e904934..4ea8838409 100644 --- a/src-self-hosted/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -5,7 +5,8 @@ const assert = std.debug.assert; const leb = std.debug.leb; const mem = std.mem; -const Decl = @import("../Module.zig").Decl; +const Module = @import("../Module.zig"); +const Decl = Module.Decl; const Inst = @import("../ir.zig").Inst; const Type = @import("../type.zig").Type; const Value = @import("../value.zig").Value; diff --git a/src-self-hosted/codegen/x86.zig b/src/codegen/x86.zig similarity index 100% rename from src-self-hosted/codegen/x86.zig rename to src/codegen/x86.zig diff --git a/src-self-hosted/codegen/x86_64.zig b/src/codegen/x86_64.zig similarity index 100% rename from src-self-hosted/codegen/x86_64.zig rename to src/codegen/x86_64.zig diff --git a/src/compiler.cpp b/src/compiler.cpp deleted file mode 100644 index 6c477a1506..0000000000 --- a/src/compiler.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "cache_hash.hpp" -#include "os.hpp" -#include "compiler.hpp" - -#include - -Error get_compiler_id(Buf **result) { - static Buf saved_compiler_id = BUF_INIT; - - if (saved_compiler_id.list.length != 0) { - *result = &saved_compiler_id; - return ErrorNone; - } - - Error err; - Buf *manifest_dir = buf_alloc(); - os_path_join(get_global_cache_dir(), buf_create_from_str("exe"), manifest_dir); - - CacheHash cache_hash; - CacheHash *ch = &cache_hash; - cache_init(ch, manifest_dir); - Buf self_exe_path = BUF_INIT; - if ((err = os_self_exe_path(&self_exe_path))) - return err; - - cache_file(ch, &self_exe_path); - - buf_resize(&saved_compiler_id, 0); - if ((err = cache_hit(ch, &saved_compiler_id))) { - if (err != ErrorInvalidFormat) - return err; - } - if (buf_len(&saved_compiler_id) != 0) { - cache_release(ch); - *result = &saved_compiler_id; - return ErrorNone; - } - ZigList lib_paths = {}; - if ((err = os_self_exe_shared_libs(lib_paths))) - return err; - #if defined(ZIG_OS_DARWIN) - // only add the self exe path on mac os - Buf *lib_path = lib_paths.at(0); - if ((err = cache_add_file(ch, lib_path))) - return err; - #else - for (size_t i = 0; i < lib_paths.length; i += 1) { - Buf *lib_path = lib_paths.at(i); - if ((err = cache_add_file(ch, lib_path))) - return err; - } - #endif - - if ((err = cache_final(ch, &saved_compiler_id))) - return err; - - cache_release(ch); - - *result = &saved_compiler_id; - return ErrorNone; -} - -static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { - { - Buf *test_zig_dir = buf_sprintf("%s" OS_SEP "lib" OS_SEP "zig", buf_ptr(test_path)); - Buf *test_index_file = buf_sprintf("%s" OS_SEP "std" OS_SEP "std.zig", buf_ptr(test_zig_dir)); - int err; - bool exists; - if ((err = os_file_exists(test_index_file, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_buf(out_zig_lib_dir, test_zig_dir); - return true; - } - } - - // Also try without "zig" - { - Buf *test_zig_dir = buf_sprintf("%s" OS_SEP "lib", buf_ptr(test_path)); - Buf *test_index_file = buf_sprintf("%s" OS_SEP "std" OS_SEP "std.zig", buf_ptr(test_zig_dir)); - int err; - bool exists; - if ((err = os_file_exists(test_index_file, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_buf(out_zig_lib_dir, test_zig_dir); - return true; - } - } - - return false; -} - -static int find_zig_lib_dir(Buf *out_path) { - int err; - - Buf self_exe_path = BUF_INIT; - buf_resize(&self_exe_path, 0); - if (!(err = os_self_exe_path(&self_exe_path))) { - Buf *cur_path = &self_exe_path; - - for (;;) { - Buf *test_dir = buf_alloc(); - os_path_dirname(cur_path, test_dir); - - if (buf_eql_buf(test_dir, cur_path)) { - break; - } - - if (test_zig_install_prefix(test_dir, out_path)) { - return 0; - } - - cur_path = test_dir; - } - } - - return ErrorFileNotFound; -} - -Buf *get_zig_lib_dir(void) { - static Buf saved_lib_dir = BUF_INIT; - if (saved_lib_dir.list.length != 0) - return &saved_lib_dir; - buf_resize(&saved_lib_dir, 0); - - int err; - if ((err = find_zig_lib_dir(&saved_lib_dir))) { - fprintf(stderr, "Unable to find zig lib directory\n"); - exit(EXIT_FAILURE); - } - return &saved_lib_dir; -} - -Buf *get_zig_std_dir(Buf *zig_lib_dir) { - static Buf saved_std_dir = BUF_INIT; - if (saved_std_dir.list.length != 0) - return &saved_std_dir; - buf_resize(&saved_std_dir, 0); - - os_path_join(zig_lib_dir, buf_create_from_str("std"), &saved_std_dir); - - return &saved_std_dir; -} - -Buf *get_zig_special_dir(Buf *zig_lib_dir) { - static Buf saved_special_dir = BUF_INIT; - if (saved_special_dir.list.length != 0) - return &saved_special_dir; - buf_resize(&saved_special_dir, 0); - - os_path_join(get_zig_std_dir(zig_lib_dir), buf_sprintf("special"), &saved_special_dir); - - return &saved_special_dir; -} - -Buf *get_global_cache_dir(void) { - static Buf saved_global_cache_dir = BUF_INIT; - if (saved_global_cache_dir.list.length != 0) - return &saved_global_cache_dir; - buf_resize(&saved_global_cache_dir, 0); - - Buf app_data_dir = BUF_INIT; - Error err; - if ((err = os_get_app_data_dir(&app_data_dir, "zig"))) { - fprintf(stderr, "Unable to get application data dir: %s\n", err_str(err)); - exit(1); - } - os_path_join(&app_data_dir, buf_create_from_str("stage1"), &saved_global_cache_dir); - buf_deinit(&app_data_dir); - return &saved_global_cache_dir; -} - -FileExt classify_file_ext(const char *filename_ptr, size_t filename_len) { - if (mem_ends_with_str(filename_ptr, filename_len, ".c")) { - return FileExtC; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".C") || - mem_ends_with_str(filename_ptr, filename_len, ".cc") || - mem_ends_with_str(filename_ptr, filename_len, ".cpp") || - mem_ends_with_str(filename_ptr, filename_len, ".cxx")) - { - return FileExtCpp; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".ll")) { - return FileExtLLVMIr; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".bc")) { - return FileExtLLVMBitCode; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".s") || - mem_ends_with_str(filename_ptr, filename_len, ".S")) - { - return FileExtAsm; - } - // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z - return FileExtUnknown; -} diff --git a/src/compiler.hpp b/src/compiler.hpp deleted file mode 100644 index ae2e6e9c5e..0000000000 --- a/src/compiler.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_COMPILER_HPP -#define ZIG_COMPILER_HPP - -#include "all_types.hpp" - -Error get_compiler_id(Buf **result); - -Buf *get_zig_lib_dir(void); -Buf *get_zig_special_dir(Buf *zig_lib_dir); -Buf *get_zig_std_dir(Buf *zig_lib_dir); - -Buf *get_global_cache_dir(void); - - -FileExt classify_file_ext(const char *filename_ptr, size_t filename_len); - -#endif diff --git a/src/config.zig.in b/src/config.zig.in index ccb618df2d..9e574bc1e8 100644 --- a/src/config.zig.in +++ b/src/config.zig.in @@ -1,3 +1,6 @@ +pub const have_llvm = true; pub const version: []const u8 = "@ZIG_VERSION@"; pub const log_scopes: []const []const u8 = &[_][]const u8{}; +pub const zir_dumps: []const []const u8 = &[_][]const u8{}; pub const enable_tracy = false; +pub const is_stage1 = true; diff --git a/src/glibc.cpp b/src/glibc.cpp deleted file mode 100644 index 62f5604ba7..0000000000 --- a/src/glibc.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "glibc.hpp" -#include "compiler.hpp" -#include "cache_hash.hpp" -#include "codegen.hpp" - -static const ZigGLibCLib glibc_libs[] = { - {"c", 6}, - {"m", 6}, - {"pthread", 0}, - {"dl", 2}, - {"rt", 1}, - {"ld", 2}, - {"util", 1}, -}; - -Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) { - Error err; - - ZigGLibCAbi *glibc_abi = heap::c_allocator.create(); - glibc_abi->vers_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "vers.txt", buf_ptr(zig_lib_dir)); - glibc_abi->fns_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "fns.txt", buf_ptr(zig_lib_dir)); - glibc_abi->abi_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "abi.txt", buf_ptr(zig_lib_dir)); - glibc_abi->version_table.init(16); - - Buf *vers_txt_contents = buf_alloc(); - if ((err = os_fetch_file_path(glibc_abi->vers_txt_path, vers_txt_contents))) { - if (verbose) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->vers_txt_path), err_str(err)); - } - return err; - } - Buf *fns_txt_contents = buf_alloc(); - if ((err = os_fetch_file_path(glibc_abi->fns_txt_path, fns_txt_contents))) { - if (verbose) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->fns_txt_path), err_str(err)); - } - return err; - } - Buf *abi_txt_contents = buf_alloc(); - if ((err = os_fetch_file_path(glibc_abi->abi_txt_path, abi_txt_contents))) { - if (verbose) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->abi_txt_path), err_str(err)); - } - return err; - } - - { - SplitIterator it = memSplit(buf_to_slice(vers_txt_contents), str("\r\n")); - for (;;) { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) break; - Buf *ver_buf = buf_create_from_slice(opt_component.value); - Stage2SemVer *this_ver = glibc_abi->all_versions.add_one(); - if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) { - if (verbose) { - fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err)); - } - return err; - } - } - } - { - SplitIterator it = memSplit(buf_to_slice(fns_txt_contents), str("\r\n")); - for (;;) { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) break; - SplitIterator line_it = memSplit(opt_component.value, str(" ")); - Optional> opt_fn_name = SplitIterator_next(&line_it); - if (!opt_fn_name.is_some) { - if (verbose) { - fprintf(stderr, "%s: Expected function name\n", buf_ptr(glibc_abi->fns_txt_path)); - } - return ErrorInvalidFormat; - } - Optional> opt_lib_name = SplitIterator_next(&line_it); - if (!opt_lib_name.is_some) { - if (verbose) { - fprintf(stderr, "%s: Expected lib name\n", buf_ptr(glibc_abi->fns_txt_path)); - } - return ErrorInvalidFormat; - } - - Buf *this_fn_name = buf_create_from_slice(opt_fn_name.value); - Buf *this_lib_name = buf_create_from_slice(opt_lib_name.value); - glibc_abi->all_functions.append({ this_fn_name, glibc_lib_find(buf_ptr(this_lib_name)) }); - } - } - { - SplitIterator it = memSplit(buf_to_slice(abi_txt_contents), str("\r\n")); - ZigGLibCVerList *ver_list_base = nullptr; - int line_num = 0; - for (;;) { - if (ver_list_base == nullptr) { - line_num += 1; - Optional> opt_line = SplitIterator_next_separate(&it); - if (!opt_line.is_some) break; - - ver_list_base = heap::c_allocator.allocate(glibc_abi->all_functions.length); - SplitIterator line_it = memSplit(opt_line.value, str(" ")); - for (;;) { - ZigTarget *target = heap::c_allocator.create(); - Optional> opt_target = SplitIterator_next(&line_it); - if (!opt_target.is_some) break; - - SplitIterator component_it = memSplit(opt_target.value, str("-")); - Optional> opt_arch = SplitIterator_next(&component_it); - assert(opt_arch.is_some); - Optional> opt_os = SplitIterator_next(&component_it); - assert(opt_os.is_some); // it's always "linux" so we ignore it - Optional> opt_abi = SplitIterator_next(&component_it); - assert(opt_abi.is_some); - - - err = target_parse_arch(&target->arch, (char*)opt_arch.value.ptr, opt_arch.value.len); - assert(err == ErrorNone); - - target->os = OsLinux; - - err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len); - if (err != ErrorNone) { - fprintf(stderr, "Error parsing %s:%d: %s\n", buf_ptr(glibc_abi->abi_txt_path), - line_num, err_str(err)); - fprintf(stderr, "arch: '%.*s', os: '%.*s', abi: '%.*s'\n", - (int)opt_arch.value.len, (const char*)opt_arch.value.ptr, - (int)opt_os.value.len, (const char*)opt_os.value.ptr, - (int)opt_abi.value.len, (const char*)opt_abi.value.ptr); - fprintf(stderr, "parsed from target: '%.*s'\n", - (int)opt_target.value.len, (const char*)opt_target.value.ptr); - fprintf(stderr, "parsed from line:\n%.*s\n", (int)opt_line.value.len, opt_line.value.ptr); - fprintf(stderr, "Zig installation appears to be corrupted.\n"); - exit(1); - } - - glibc_abi->version_table.put(target, ver_list_base); - } - continue; - } - for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) { - ZigGLibCVerList *ver_list = &ver_list_base[fn_i]; - line_num += 1; - Optional> opt_line = SplitIterator_next_separate(&it); - assert(opt_line.is_some); - - SplitIterator line_it = memSplit(opt_line.value, str(" ")); - for (;;) { - Optional> opt_ver = SplitIterator_next(&line_it); - if (!opt_ver.is_some) break; - assert(ver_list->len < 8); // increase the array len in the type - - unsigned long ver_index = strtoul(buf_ptr(buf_create_from_slice(opt_ver.value)), nullptr, 10); - assert(ver_index < 255); // use a bigger integer in the type - ver_list->versions[ver_list->len] = ver_index; - ver_list->len += 1; - } - } - ver_list_base = nullptr; - } - } - - *out_result = glibc_abi; - return ErrorNone; -} - -Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, - Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node) -{ - Error err; - - Buf *cache_dir = get_global_cache_dir(); - CacheHash *cache_hash = heap::c_allocator.create(); - Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); - cache_init(cache_hash, manifest_dir); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - if (verbose) { - fprintf(stderr, "unable to get compiler id: %s\n", err_str(err)); - } - return err; - } - cache_buf(cache_hash, compiler_id); - cache_int(cache_hash, target->arch); - cache_int(cache_hash, target->abi); - cache_int(cache_hash, target->glibc_or_darwin_version->major); - cache_int(cache_hash, target->glibc_or_darwin_version->minor); - cache_int(cache_hash, target->glibc_or_darwin_version->patch); - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(cache_hash, &digest))) { - // Treat an invalid format error as a cache miss. - if (err != ErrorInvalidFormat) - return err; - } - // We should always get a cache hit because there are no - // files in the input hash. - assert(buf_len(&digest) != 0); - - Buf *dummy_dir = buf_alloc(); - os_path_join(manifest_dir, &digest, dummy_dir); - - if ((err = os_make_path(dummy_dir))) - return err; - - Buf *test_if_exists_path = buf_alloc(); - os_path_join(dummy_dir, buf_create_from_str("ok"), test_if_exists_path); - - bool hit; - if ((err = os_file_exists(test_if_exists_path, &hit))) - return err; - - if (hit) { - *out_dir = dummy_dir; - return ErrorNone; - } - - - ZigGLibCVerList *ver_list_base = glibc_abi->version_table.get(target); - - uint8_t target_ver_index = 0; - for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) { - const Stage2SemVer *this_ver = &glibc_abi->all_versions.at(target_ver_index); - if (this_ver->major == target->glibc_or_darwin_version->major && - this_ver->minor == target->glibc_or_darwin_version->minor && - this_ver->patch == target->glibc_or_darwin_version->patch) - { - break; - } - } - if (target_ver_index == glibc_abi->all_versions.length) { - if (verbose) { - fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n", - target->glibc_or_darwin_version->major, - target->glibc_or_darwin_version->minor, - target->glibc_or_darwin_version->patch); - } - return ErrorUnknownABI; - } - - Buf *map_file_path = buf_sprintf("%s" OS_SEP "all.map", buf_ptr(dummy_dir)); - Buf *map_contents = buf_alloc(); - - for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) { - const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_i); - if (ver->patch == 0) { - buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor); - } else { - buf_appendf(map_contents, "GLIBC_%d.%d.%d { };\n", ver->major, ver->minor, ver->patch); - } - } - - if ((err = os_write_file(map_file_path, map_contents))) { - if (verbose) { - fprintf(stderr, "unable to write %s: %s", buf_ptr(map_file_path), err_str(err)); - } - return err; - } - - - for (size_t lib_i = 0; lib_i < array_length(glibc_libs); lib_i += 1) { - const ZigGLibCLib *lib = &glibc_libs[lib_i]; - Buf *zig_file_path = buf_sprintf("%s" OS_SEP "%s.zig", buf_ptr(dummy_dir), lib->name); - Buf *zig_body = buf_alloc(); - Buf *zig_footer = buf_alloc(); - - buf_appendf(zig_body, "comptime {\n"); - buf_appendf(zig_body, " asm (\n"); - - for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) { - const ZigGLibCFn *libc_fn = &glibc_abi->all_functions.at(fn_i); - if (libc_fn->lib != lib) continue; - ZigGLibCVerList *ver_list = &ver_list_base[fn_i]; - // Pick the default symbol version: - // - If there are no versions, don't emit it - // - Take the greatest one <= than the target one - // - If none of them is <= than the - // specified one don't pick any default version - if (ver_list->len == 0) continue; - uint8_t chosen_def_ver_index = 255; - for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) { - uint8_t ver_index = ver_list->versions[ver_i]; - if ((chosen_def_ver_index == 255 || ver_index > chosen_def_ver_index) && - target_ver_index >= ver_index) - { - chosen_def_ver_index = ver_index; - } - } - for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) { - uint8_t ver_index = ver_list->versions[ver_i]; - - Buf *stub_name; - const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_index); - const char *sym_name = buf_ptr(libc_fn->name); - if (ver->patch == 0) { - stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor); - } else { - stub_name = buf_sprintf("%s_%d_%d_%d", sym_name, ver->major, ver->minor, ver->patch); - } - - buf_appendf(zig_footer, "export fn %s() void {}\n", buf_ptr(stub_name)); - - // Default symbol version definition vs normal symbol version definition - const char *at_sign_str = (chosen_def_ver_index != 255 && - ver_index == chosen_def_ver_index) ? "@@" : "@"; - if (ver->patch == 0) { - buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d\n", - buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor); - } else { - buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d.%d\n", - buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor, ver->patch); - } - // Hide the stub to keep the symbol table clean - buf_appendf(zig_body, " \\\\ .hidden %s\n", buf_ptr(stub_name)); - } - } - - buf_appendf(zig_body, " );\n"); - buf_appendf(zig_body, "}\n"); - buf_append_buf(zig_body, zig_footer); - - if ((err = os_write_file(zig_file_path, zig_body))) { - if (verbose) { - fprintf(stderr, "unable to write %s: %s", buf_ptr(zig_file_path), err_str(err)); - } - return err; - } - - bool is_ld = (strcmp(lib->name, "ld") == 0); - - CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node); - codegen_set_lib_version(child_gen, true, lib->sover, 0, 0); - child_gen->is_dynamic = true; - child_gen->is_dummy_so = true; - child_gen->version_script_path = map_file_path; - child_gen->enable_cache = false; - child_gen->output_dir = dummy_dir; - if (is_ld) { - assert(g->zig_target->standard_dynamic_linker_path != nullptr); - Buf *ld_basename = buf_alloc(); - os_path_split(buf_create_from_str(g->zig_target->standard_dynamic_linker_path), - nullptr, ld_basename); - child_gen->override_soname = ld_basename; - } - codegen_build_and_link(child_gen); - } - - if ((err = os_write_file(test_if_exists_path, buf_alloc()))) { - if (verbose) { - fprintf(stderr, "unable to write %s: %s", buf_ptr(test_if_exists_path), err_str(err)); - } - return err; - } - *out_dir = dummy_dir; - return ErrorNone; -} - -uint32_t hash_glibc_target(const ZigTarget *x) { - return x->arch * (uint32_t)3250106448 + - x->os * (uint32_t)542534372 + - x->abi * (uint32_t)59162639; -} - -bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) { - return a->arch == b->arch && - a->os == b->os && - a->abi == b->abi; -} - -size_t glibc_lib_count(void) { - return array_length(glibc_libs); -} - -const ZigGLibCLib *glibc_lib_enum(size_t index) { - assert(index < array_length(glibc_libs)); - return &glibc_libs[index]; -} - -const ZigGLibCLib *glibc_lib_find(const char *name) { - for (size_t i = 0; i < array_length(glibc_libs); i += 1) { - if (strcmp(glibc_libs[i].name, name) == 0) { - return &glibc_libs[i]; - } - } - return nullptr; -} diff --git a/src/glibc.hpp b/src/glibc.hpp deleted file mode 100644 index c04dcb4629..0000000000 --- a/src/glibc.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_GLIBC_HPP -#define ZIG_GLIBC_HPP - -#include "all_types.hpp" - -struct ZigGLibCLib { - const char *name; - uint8_t sover; -}; - -struct ZigGLibCFn { - Buf *name; - const ZigGLibCLib *lib; -}; - -struct ZigGLibCVerList { - uint8_t versions[8]; // 8 is just the max number, we know statically it's big enough - uint8_t len; -}; - -uint32_t hash_glibc_target(const ZigTarget *x); -bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b); - -struct ZigGLibCAbi { - Buf *abi_txt_path; - Buf *vers_txt_path; - Buf *fns_txt_path; - ZigList all_versions; - ZigList all_functions; - // The value is a pointer to all_functions.length items and each item is an index - // into all_functions. - HashMap version_table; -}; - -Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose); -Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, - Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node); - -size_t glibc_lib_count(void); -const ZigGLibCLib *glibc_lib_enum(size_t index); -const ZigGLibCLib *glibc_lib_find(const char *name); - -#endif diff --git a/src/glibc.zig b/src/glibc.zig new file mode 100644 index 0000000000..1860726f93 --- /dev/null +++ b/src/glibc.zig @@ -0,0 +1,956 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; +const Cache = @import("Cache.zig"); +const Package = @import("Package.zig"); + +pub const Lib = struct { + name: []const u8, + sover: u8, +}; + +pub const Fn = struct { + name: []const u8, + lib: *const Lib, +}; + +pub const VerList = struct { + /// 7 is just the max number, we know statically it's big enough. + versions: [7]u8, + len: u8, +}; + +pub const ABI = struct { + all_versions: []const std.builtin.Version, + all_functions: []const Fn, + /// The value is a pointer to all_functions.len items and each item is an index into all_functions. + version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList), + arena_state: std.heap.ArenaAllocator.State, + + pub fn destroy(abi: *ABI, gpa: *Allocator) void { + abi.version_table.deinit(gpa); + abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too. + } +}; + +pub const libs = [_]Lib{ + .{ .name = "c", .sover = 6 }, + .{ .name = "m", .sover = 6 }, + .{ .name = "pthread", .sover = 0 }, + .{ .name = "dl", .sover = 2 }, + .{ .name = "rt", .sover = 1 }, + .{ .name = "ld", .sover = 2 }, + .{ .name = "util", .sover = 1 }, +}; + +pub const LoadMetaDataError = error{ + /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data. + ZigInstallationCorrupt, + OutOfMemory, +}; + +/// This function will emit a log error when there is a problem with the zig installation and then return +/// `error.ZigInstallationCorrupt`. +pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + errdefer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + var all_versions = std.ArrayListUnmanaged(std.builtin.Version){}; + var all_functions = std.ArrayListUnmanaged(Fn){}; + var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){}; + errdefer version_table.deinit(gpa); + + var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| { + std.log.err("unable to open glibc dir: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }; + defer glibc_dir.close(); + + const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked. + const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + std.log.err("unable to read vers.txt: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }, + }; + defer gpa.free(vers_txt_contents); + + // Arena allocated because the result contains references to function names. + const fns_txt_contents = glibc_dir.readFileAlloc(arena, "fns.txt", max_txt_size) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + std.log.err("unable to read fns.txt: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }, + }; + + const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + std.log.err("unable to read abi.txt: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }, + }; + defer gpa.free(abi_txt_contents); + + { + var it = mem.tokenize(vers_txt_contents, "\r\n"); + var line_i: usize = 1; + while (it.next()) |line| : (line_i += 1) { + const prefix = "GLIBC_"; + if (!mem.startsWith(u8, line, prefix)) { + std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i}); + return error.ZigInstallationCorrupt; + } + const adjusted_line = line[prefix.len..]; + const ver = std.builtin.Version.parse(adjusted_line) catch |err| { + std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) }); + return error.ZigInstallationCorrupt; + }; + try all_versions.append(arena, ver); + } + } + { + var file_it = mem.tokenize(fns_txt_contents, "\r\n"); + var line_i: usize = 1; + while (file_it.next()) |line| : (line_i += 1) { + var line_it = mem.tokenize(line, " "); + const fn_name = line_it.next() orelse { + std.log.err("fns.txt:{}: expected function name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const lib_name = line_it.next() orelse { + std.log.err("fns.txt:{}: expected library name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const lib = findLib(lib_name) orelse { + std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name }); + return error.ZigInstallationCorrupt; + }; + try all_functions.append(arena, .{ + .name = fn_name, + .lib = lib, + }); + } + } + { + var file_it = mem.split(abi_txt_contents, "\n"); + var line_i: usize = 0; + while (true) { + const ver_list_base: []VerList = blk: { + const line = file_it.next() orelse break; + if (line.len == 0) break; + line_i += 1; + const ver_list_base = try arena.alloc(VerList, all_functions.items.len); + var line_it = mem.tokenize(line, " "); + while (line_it.next()) |target_string| { + var component_it = mem.tokenize(target_string, "-"); + const arch_name = component_it.next() orelse { + std.log.err("abi.txt:{}: expected arch name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const os_name = component_it.next() orelse { + std.log.err("abi.txt:{}: expected OS name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const abi_name = component_it.next() orelse { + std.log.err("abi.txt:{}: expected ABI name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse { + std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name }); + return error.ZigInstallationCorrupt; + }; + if (!mem.eql(u8, os_name, "linux")) { + std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name }); + return error.ZigInstallationCorrupt; + } + const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse { + std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name }); + return error.ZigInstallationCorrupt; + }; + + const triple = target_util.ArchOsAbi{ + .arch = arch_tag, + .os = .linux, + .abi = abi_tag, + }; + try version_table.put(gpa, triple, ver_list_base.ptr); + } + break :blk ver_list_base; + }; + for (ver_list_base) |*ver_list| { + const line = file_it.next() orelse { + std.log.err("abi.txt:{}: missing version number line", .{line_i}); + return error.ZigInstallationCorrupt; + }; + line_i += 1; + + ver_list.* = .{ + .versions = undefined, + .len = 0, + }; + var line_it = mem.tokenize(line, " "); + while (line_it.next()) |version_index_string| { + if (ver_list.len >= ver_list.versions.len) { + // If this happens with legit data, increase the array len in the type. + std.log.err("abi.txt:{}: too many versions", .{line_i}); + return error.ZigInstallationCorrupt; + } + const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| { + // If this happens with legit data, increase the size of the integer type in the struct. + std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) }); + return error.ZigInstallationCorrupt; + }; + + ver_list.versions[ver_list.len] = version_index; + ver_list.len += 1; + } + } + } + } + + const abi = try arena.create(ABI); + abi.* = .{ + .all_versions = all_versions.items, + .all_functions = all_functions.items, + .version_table = version_table, + .arena_state = arena_allocator.state, + }; + return abi; +} + +fn findLib(name: []const u8) ?*const Lib { + for (libs) |*lib| { + if (mem.eql(u8, lib.name, name)) { + return lib; + } + } + return null; +} + +pub const CRTFile = enum { + crti_o, + crtn_o, + scrt1_o, + libc_nonshared_a, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crti_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crti.S"), + .extra_flags = args.items, + }, + }); + }, + .crtn_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-DMODULE_NAME=libc", + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crtn.S"), + .extra_flags = args.items, + }, + }); + }, + .scrt1_o => { + const start_os: Compilation.CSourceFile = blk: { + var args = std.ArrayList([]const u8).init(arena); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DPIC", + "-DSHARED", + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + break :blk .{ + .src_path = try start_asm_path(comp, arena, "start.S"), + .extra_flags = args.items, + }; + }; + const abi_note_o: Compilation.CSourceFile = blk: { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-I", + try lib_path(comp, arena, lib_libc_glibc ++ "csu"), + }); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-DMODULE_NAME=libc", + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + break :blk .{ + .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"), + .extra_flags = args.items, + }; + }; + return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o }); + }, + .libc_nonshared_a => { + const deps = [_][]const u8{ + lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "atexit.c", + lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "at_quick_exit.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknod.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknodat.c", + lib_libc_glibc ++ "nptl" ++ path.sep_str ++ "pthread_atfork.c", + lib_libc_glibc ++ "debug" ++ path.sep_str ++ "stack_chk_fail_local.c", + }; + + var c_source_files: [deps.len + 1]Compilation.CSourceFile = undefined; + + c_source_files[0] = blk: { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-fgnu89-inline", + "-g", + "-O2", + "-fmerge-all-constants", + "-fno-stack-protector", + "-fmath-errno", + "-fno-stack-protector", + "-I", + try lib_path(comp, arena, lib_libc_glibc ++ "csu"), + }); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-DSTACK_PROTECTOR_LEVEL=0", + "-fPIC", + "-fno-stack-protector", + "-ftls-model=initial-exec", + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DPIC", + "-DLIBC_NONSHARED=1", + "-DTOP_NAMESPACE=glibc", + }); + break :blk .{ + .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "elf-init.c"), + .extra_flags = args.items, + }; + }; + + for (deps) |dep, i| { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-fgnu89-inline", + "-g", + "-O2", + "-fmerge-all-constants", + "-fno-stack-protector", + "-fmath-errno", + "-ftls-model=initial-exec", + "-Wno-ignored-attributes", + }); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DPIC", + "-DLIBC_NONSHARED=1", + "-DTOP_NAMESPACE=glibc", + }); + c_source_files[i + 1] = .{ + .src_path = try lib_path(comp, arena, dep), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("c_nonshared", .Lib, &c_source_files); + }, + } +} + +fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { + const arch = comp.getTarget().cpu.arch; + const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; + const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; + const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; + const is_64 = arch.ptrBitWidth() == 64; + + const s = path.sep_str; + + var result = std.ArrayList(u8).init(arena); + try result.appendSlice(comp.zig_lib_directory.path.?); + try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s); + if (is_sparc) { + if (is_64) { + try result.appendSlice("sparc" ++ s ++ "sparc64"); + } else { + try result.appendSlice("sparc" ++ s ++ "sparc32"); + } + } else if (arch.isARM()) { + try result.appendSlice("arm"); + } else if (arch.isMIPS()) { + try result.appendSlice("mips"); + } else if (arch == .x86_64) { + try result.appendSlice("x86_64"); + } else if (arch == .i386) { + try result.appendSlice("i386"); + } else if (is_aarch64) { + try result.appendSlice("aarch64"); + } else if (arch.isRISCV()) { + try result.appendSlice("riscv"); + } else if (is_ppc) { + if (is_64) { + try result.appendSlice("powerpc" ++ s ++ "powerpc64"); + } else { + try result.appendSlice("powerpc" ++ s ++ "powerpc32"); + } + } + + try result.appendSlice(s); + try result.appendSlice(basename); + return result.items; +} + +fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void { + const target = comp.getTarget(); + const arch = target.cpu.arch; + const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl"; + const glibc = try lib_path(comp, arena, lib_libc ++ "glibc"); + + const s = path.sep_str; + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include")); + + if (target.os.tag == .linux) { + try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux")); + } + + if (opt_nptl) |nptl| { + try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps")); + } + + if (target.os.tag == .linux) { + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ + "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ + "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include")); + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ + "unix" ++ s ++ "sysv" ++ s ++ "linux")); + } + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl })); + } + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv")); + + try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); + + try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic")); + + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" })); + + try args.append("-I"); + try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{ + comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi), + })); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); + + try args.append("-I"); + try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{ + comp.zig_lib_directory.path.?, @tagName(arch), + })); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any")); +} + +fn add_include_dirs_arch( + arena: *Allocator, + args: *std.ArrayList([]const u8), + arch: std.Target.Cpu.Arch, + opt_nptl: ?[]const u8, + dir: []const u8, +) error{OutOfMemory}!void { + const is_x86 = arch == .i386 or arch == .x86_64; + const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; + const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; + const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; + const is_64 = arch.ptrBitWidth() == 64; + + const s = path.sep_str; + + if (is_x86) { + if (arch == .x86_64) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" })); + } + } else if (arch == .i386) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" })); + } + } + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" })); + } + } else if (arch.isARM()) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" })); + } + } else if (arch.isMIPS()) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl })); + } else { + if (is_64) { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" })); + } + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" })); + } + } else if (is_sparc) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl })); + } else { + if (is_64) { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" })); + } + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" })); + } + } else if (is_aarch64) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" })); + } + } else if (is_ppc) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl })); + } else { + if (is_64) { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" })); + } + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" })); + } + } else if (arch.isRISCV()) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" })); + } + } +} + +fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 { + return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path }); +} + +const lib_libc = "libc" ++ path.sep_str; +const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str; + +fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 { + return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path }); +} + +pub const BuiltSharedObjects = struct { + lock: Cache.Lock, + dir_path: []u8, + + pub fn deinit(self: *BuiltSharedObjects, gpa: *Allocator) void { + self.lock.release(); + gpa.free(self.dir_path); + self.* = undefined; + } +}; + +const all_map_basename = "all.map"; + +// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. +// zig fmt: off + +pub fn buildSharedObjects(comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const target = comp.getTarget(); + const target_version = target.os.version_range.linux.glibc; + + // Use the global cache directory. + var cache_parent: Cache = .{ + .gpa = comp.gpa, + .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), + }; + defer cache_parent.manifest_dir.close(); + + var cache = cache_parent.obtain(); + defer cache.deinit(); + cache.hash.addBytes(build_options.version); + cache.hash.addBytes(comp.zig_lib_directory.path orelse "."); + cache.hash.add(target.cpu.arch); + cache.hash.add(target.abi); + cache.hash.add(target_version); + + const hit = try cache.hit(); + const digest = cache.final(); + const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest }); + + // Even if we get a hit, it doesn't guarantee that we finished the job last time. + // We use the presence of an "ok" file to determine if it is a true hit. + + var o_directory: Compilation.Directory = .{ + .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}), + .path = try path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }), + }; + defer o_directory.handle.close(); + + const ok_basename = "ok"; + const actual_hit = if (hit) blk: { + o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) { + error.FileNotFound => break :blk false, + else => |e| return e, + }; + break :blk true; + } else false; + + if (!actual_hit) { + const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle); + defer metadata.destroy(comp.gpa); + + const ver_list_base = metadata.version_table.get(.{ + .arch = target.cpu.arch, + .os = target.os.tag, + .abi = target.abi, + }) orelse return error.GLibCUnavailableForThisTarget; + const target_ver_index = for (metadata.all_versions) |ver, i| { + switch (ver.order(target_version)) { + .eq => break i, + .lt => continue, + .gt => { + // TODO Expose via compile error mechanism instead of log. + std.log.warn("invalid target glibc version: {}", .{target_version}); + return error.InvalidTargetGLibCVersion; + }, + } + } else blk: { + const latest_index = metadata.all_versions.len - 1; + std.log.warn("zig cannot build new glibc version {}; providing instead {}", .{ + target_version, metadata.all_versions[latest_index], + }); + break :blk latest_index; + }; + { + var map_contents = std.ArrayList(u8).init(arena); + for (metadata.all_versions) |ver| { + if (ver.patch == 0) { + try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor }); + } else { + try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch }); + } + } + try o_directory.handle.writeFile(all_map_basename, map_contents.items); + map_contents.deinit(); // The most recent allocation of an arena can be freed :) + } + var zig_body = std.ArrayList(u8).init(comp.gpa); + defer zig_body.deinit(); + for (libs) |*lib| { + zig_body.shrinkRetainingCapacity(0); + + for (metadata.all_functions) |*libc_fn, fn_i| { + if (libc_fn.lib != lib) continue; + + const ver_list = ver_list_base[fn_i]; + // Pick the default symbol version: + // - If there are no versions, don't emit it + // - Take the greatest one <= than the target one + // - If none of them is <= than the + // specified one don't pick any default version + if (ver_list.len == 0) continue; + var chosen_def_ver_index: u8 = 255; + { + var ver_i: u8 = 0; + while (ver_i < ver_list.len) : (ver_i += 1) { + const ver_index = ver_list.versions[ver_i]; + if ((chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) and + target_ver_index >= ver_index) + { + chosen_def_ver_index = ver_index; + } + } + } + { + var ver_i: u8 = 0; + while (ver_i < ver_list.len) : (ver_i += 1) { + // Example: + // .globl _Exit_2_2_5 + // .type _Exit_2_2_5, @function; + // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5 + // .hidden _Exit_2_2_5 + // _Exit_2_2_5: + const ver_index = ver_list.versions[ver_i]; + const ver = metadata.all_versions[ver_index]; + const sym_name = libc_fn.name; + // Default symbol version definition vs normal symbol version definition + const want_two_ats = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index; + const at_sign_str = "@@"[0 .. @boolToInt(want_two_ats) + @as(usize, 1)]; + + if (ver.patch == 0) { + const sym_plus_ver = try std.fmt.allocPrint( + arena, "{s}_{d}_{d}", + .{sym_name, ver.major, ver.minor}, + ); + try zig_body.writer().print( + \\.globl {s} + \\.type {s}, @function; + \\.symver {s}, {s}{s}GLIBC_{d}.{d} + \\.hidden {s} + \\{s}: + \\ + , .{ + sym_plus_ver, + sym_plus_ver, + sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, + sym_plus_ver, + sym_plus_ver, + }); + } else { + const sym_plus_ver = try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}", + .{sym_name, ver.major, ver.minor, ver.patch}, + ); + try zig_body.writer().print( + \\.globl {s} + \\.type {s}, @function; + \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d} + \\.hidden {s} + \\{s}: + \\ + , .{ + sym_plus_ver, + sym_plus_ver, + sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, ver.patch, + sym_plus_ver, + sym_plus_ver, + }); + } + } + } + } + + var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc. + const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; + try o_directory.handle.writeFile(asm_file_basename, zig_body.items); + + try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib); + } + // No need to write the manifest because there are no file inputs associated with this cache hash. + // However we do need to write the ok file now. + if (o_directory.handle.createFile(ok_basename, .{})) |file| { + file.close(); + } else |err| { + std.log.warn("glibc shared objects: failed to mark completion: {}", .{@errorName(err)}); + } + } + + assert(comp.glibc_so_files == null); + comp.glibc_so_files = BuiltSharedObjects{ + .lock = cache.toOwnedLock(), + .dir_path = try path.join(comp.gpa, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }), + }; +} + +// zig fmt: on + +fn buildSharedLib( + comp: *Compilation, + arena: *Allocator, + zig_cache_directory: Compilation.Directory, + bin_directory: Compilation.Directory, + asm_file_basename: []const u8, + lib: *const Lib, +) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const emit_bin = Compilation.EmitLoc{ + .directory = bin_directory, + .basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }), + }; + const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; + const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); + const override_soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null; + const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename }); + const c_source_files = [1]Compilation.CSourceFile{ + .{ + .src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }), + }, + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = zig_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = comp.getTarget(), + .root_name = lib.name, + .root_pkg = null, + .output_mode = .Lib, + .link_mode = .Dynamic, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = false, + .self_exe_path = comp.self_exe_path, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .version = version, + .version_script = map_file_path, + .override_soname = override_soname, + .c_source_files = &c_source_files, + .is_compiler_rt_or_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); +} diff --git a/src/install_files.h b/src/install_files.h deleted file mode 100644 index 8e7431145e..0000000000 --- a/src/install_files.h +++ /dev/null @@ -1,1907 +0,0 @@ -#ifndef ZIG_INSTALL_FILES_H -#define ZIG_INSTALL_FILES_H -static const char *ZIG_MUSL_SRC_FILES[] = { -"musl/src/aio/aio.c", -"musl/src/aio/aio_suspend.c", -"musl/src/aio/lio_listio.c", -"musl/src/complex/__cexp.c", -"musl/src/complex/__cexpf.c", -"musl/src/complex/cabs.c", -"musl/src/complex/cabsf.c", -"musl/src/complex/cabsl.c", -"musl/src/complex/cacos.c", -"musl/src/complex/cacosf.c", -"musl/src/complex/cacosh.c", -"musl/src/complex/cacoshf.c", -"musl/src/complex/cacoshl.c", -"musl/src/complex/cacosl.c", -"musl/src/complex/carg.c", -"musl/src/complex/cargf.c", -"musl/src/complex/cargl.c", -"musl/src/complex/casin.c", -"musl/src/complex/casinf.c", -"musl/src/complex/casinh.c", -"musl/src/complex/casinhf.c", -"musl/src/complex/casinhl.c", -"musl/src/complex/casinl.c", -"musl/src/complex/catan.c", -"musl/src/complex/catanf.c", -"musl/src/complex/catanh.c", -"musl/src/complex/catanhf.c", -"musl/src/complex/catanhl.c", -"musl/src/complex/catanl.c", -"musl/src/complex/ccos.c", -"musl/src/complex/ccosf.c", -"musl/src/complex/ccosh.c", -"musl/src/complex/ccoshf.c", -"musl/src/complex/ccoshl.c", -"musl/src/complex/ccosl.c", -"musl/src/complex/cexp.c", -"musl/src/complex/cexpf.c", -"musl/src/complex/cexpl.c", -"musl/src/complex/cimag.c", -"musl/src/complex/cimagf.c", -"musl/src/complex/cimagl.c", -"musl/src/complex/clog.c", -"musl/src/complex/clogf.c", -"musl/src/complex/clogl.c", -"musl/src/complex/conj.c", -"musl/src/complex/conjf.c", -"musl/src/complex/conjl.c", -"musl/src/complex/cpow.c", -"musl/src/complex/cpowf.c", -"musl/src/complex/cpowl.c", -"musl/src/complex/cproj.c", -"musl/src/complex/cprojf.c", -"musl/src/complex/cprojl.c", -"musl/src/complex/creal.c", -"musl/src/complex/crealf.c", -"musl/src/complex/creall.c", -"musl/src/complex/csin.c", -"musl/src/complex/csinf.c", -"musl/src/complex/csinh.c", -"musl/src/complex/csinhf.c", -"musl/src/complex/csinhl.c", -"musl/src/complex/csinl.c", -"musl/src/complex/csqrt.c", -"musl/src/complex/csqrtf.c", -"musl/src/complex/csqrtl.c", -"musl/src/complex/ctan.c", -"musl/src/complex/ctanf.c", -"musl/src/complex/ctanh.c", -"musl/src/complex/ctanhf.c", -"musl/src/complex/ctanhl.c", -"musl/src/complex/ctanl.c", -"musl/src/conf/confstr.c", -"musl/src/conf/fpathconf.c", -"musl/src/conf/legacy.c", -"musl/src/conf/pathconf.c", -"musl/src/conf/sysconf.c", -"musl/src/crypt/crypt.c", -"musl/src/crypt/crypt_blowfish.c", -"musl/src/crypt/crypt_des.c", -"musl/src/crypt/crypt_md5.c", -"musl/src/crypt/crypt_r.c", -"musl/src/crypt/crypt_sha256.c", -"musl/src/crypt/crypt_sha512.c", -"musl/src/crypt/encrypt.c", -"musl/src/ctype/__ctype_b_loc.c", -"musl/src/ctype/__ctype_get_mb_cur_max.c", -"musl/src/ctype/__ctype_tolower_loc.c", -"musl/src/ctype/__ctype_toupper_loc.c", -"musl/src/ctype/isalnum.c", -"musl/src/ctype/isalpha.c", -"musl/src/ctype/isascii.c", -"musl/src/ctype/isblank.c", -"musl/src/ctype/iscntrl.c", -"musl/src/ctype/isdigit.c", -"musl/src/ctype/isgraph.c", -"musl/src/ctype/islower.c", -"musl/src/ctype/isprint.c", -"musl/src/ctype/ispunct.c", -"musl/src/ctype/isspace.c", -"musl/src/ctype/isupper.c", -"musl/src/ctype/iswalnum.c", -"musl/src/ctype/iswalpha.c", -"musl/src/ctype/iswblank.c", -"musl/src/ctype/iswcntrl.c", -"musl/src/ctype/iswctype.c", -"musl/src/ctype/iswdigit.c", -"musl/src/ctype/iswgraph.c", -"musl/src/ctype/iswlower.c", -"musl/src/ctype/iswprint.c", -"musl/src/ctype/iswpunct.c", -"musl/src/ctype/iswspace.c", -"musl/src/ctype/iswupper.c", -"musl/src/ctype/iswxdigit.c", -"musl/src/ctype/isxdigit.c", -"musl/src/ctype/toascii.c", -"musl/src/ctype/tolower.c", -"musl/src/ctype/toupper.c", -"musl/src/ctype/towctrans.c", -"musl/src/ctype/wcswidth.c", -"musl/src/ctype/wctrans.c", -"musl/src/ctype/wcwidth.c", -"musl/src/dirent/alphasort.c", -"musl/src/dirent/closedir.c", -"musl/src/dirent/dirfd.c", -"musl/src/dirent/fdopendir.c", -"musl/src/dirent/opendir.c", -"musl/src/dirent/readdir.c", -"musl/src/dirent/readdir_r.c", -"musl/src/dirent/rewinddir.c", -"musl/src/dirent/scandir.c", -"musl/src/dirent/seekdir.c", -"musl/src/dirent/telldir.c", -"musl/src/dirent/versionsort.c", -"musl/src/env/__environ.c", -"musl/src/env/__init_tls.c", -"musl/src/env/__libc_start_main.c", -"musl/src/env/__reset_tls.c", -"musl/src/env/__stack_chk_fail.c", -"musl/src/env/clearenv.c", -"musl/src/env/getenv.c", -"musl/src/env/putenv.c", -"musl/src/env/secure_getenv.c", -"musl/src/env/setenv.c", -"musl/src/env/unsetenv.c", -"musl/src/errno/__errno_location.c", -"musl/src/errno/strerror.c", -"musl/src/exit/_Exit.c", -"musl/src/exit/abort.c", -"musl/src/exit/arm/__aeabi_atexit.c", -"musl/src/exit/assert.c", -"musl/src/exit/at_quick_exit.c", -"musl/src/exit/atexit.c", -"musl/src/exit/exit.c", -"musl/src/exit/quick_exit.c", -"musl/src/fcntl/creat.c", -"musl/src/fcntl/fcntl.c", -"musl/src/fcntl/open.c", -"musl/src/fcntl/openat.c", -"musl/src/fcntl/posix_fadvise.c", -"musl/src/fcntl/posix_fallocate.c", -"musl/src/fenv/__flt_rounds.c", -"musl/src/fenv/aarch64/fenv.s", -"musl/src/fenv/arm/fenv-hf.S", -"musl/src/fenv/arm/fenv.c", -"musl/src/fenv/fegetexceptflag.c", -"musl/src/fenv/feholdexcept.c", -"musl/src/fenv/fenv.c", -"musl/src/fenv/fesetexceptflag.c", -"musl/src/fenv/fesetround.c", -"musl/src/fenv/feupdateenv.c", -"musl/src/fenv/i386/fenv.s", -"musl/src/fenv/m68k/fenv.c", -"musl/src/fenv/mips/fenv-sf.c", -"musl/src/fenv/mips/fenv.S", -"musl/src/fenv/mips64/fenv-sf.c", -"musl/src/fenv/mips64/fenv.S", -"musl/src/fenv/mipsn32/fenv-sf.c", -"musl/src/fenv/mipsn32/fenv.S", -"musl/src/fenv/powerpc/fenv-sf.c", -"musl/src/fenv/powerpc/fenv.S", -"musl/src/fenv/powerpc64/fenv.c", -"musl/src/fenv/riscv64/fenv-sf.c", -"musl/src/fenv/riscv64/fenv.S", -"musl/src/fenv/s390x/fenv.c", -"musl/src/fenv/sh/fenv-nofpu.c", -"musl/src/fenv/sh/fenv.S", -"musl/src/fenv/x32/fenv.s", -"musl/src/fenv/x86_64/fenv.s", -"musl/src/internal/defsysinfo.c", -"musl/src/internal/floatscan.c", -"musl/src/internal/i386/defsysinfo.s", -"musl/src/internal/intscan.c", -"musl/src/internal/libc.c", -"musl/src/internal/procfdname.c", -"musl/src/internal/sh/__shcall.c", -"musl/src/internal/shgetc.c", -"musl/src/internal/syscall_ret.c", -"musl/src/internal/vdso.c", -"musl/src/internal/version.c", -"musl/src/ipc/ftok.c", -"musl/src/ipc/msgctl.c", -"musl/src/ipc/msgget.c", -"musl/src/ipc/msgrcv.c", -"musl/src/ipc/msgsnd.c", -"musl/src/ipc/semctl.c", -"musl/src/ipc/semget.c", -"musl/src/ipc/semop.c", -"musl/src/ipc/semtimedop.c", -"musl/src/ipc/shmat.c", -"musl/src/ipc/shmctl.c", -"musl/src/ipc/shmdt.c", -"musl/src/ipc/shmget.c", -"musl/src/ldso/__dlsym.c", -"musl/src/ldso/aarch64/dlsym.s", -"musl/src/ldso/aarch64/tlsdesc.s", -"musl/src/ldso/arm/dlsym.s", -"musl/src/ldso/arm/dlsym_time64.S", -"musl/src/ldso/arm/find_exidx.c", -"musl/src/ldso/arm/tlsdesc.S", -"musl/src/ldso/dl_iterate_phdr.c", -"musl/src/ldso/dladdr.c", -"musl/src/ldso/dlclose.c", -"musl/src/ldso/dlerror.c", -"musl/src/ldso/dlinfo.c", -"musl/src/ldso/dlopen.c", -"musl/src/ldso/dlsym.c", -"musl/src/ldso/i386/dlsym.s", -"musl/src/ldso/i386/dlsym_time64.S", -"musl/src/ldso/i386/tlsdesc.s", -"musl/src/ldso/m68k/dlsym.s", -"musl/src/ldso/m68k/dlsym_time64.S", -"musl/src/ldso/microblaze/dlsym.s", -"musl/src/ldso/microblaze/dlsym_time64.S", -"musl/src/ldso/mips/dlsym.s", -"musl/src/ldso/mips/dlsym_time64.S", -"musl/src/ldso/mips64/dlsym.s", -"musl/src/ldso/mipsn32/dlsym.s", -"musl/src/ldso/mipsn32/dlsym_time64.S", -"musl/src/ldso/or1k/dlsym.s", -"musl/src/ldso/or1k/dlsym_time64.S", -"musl/src/ldso/powerpc/dlsym.s", -"musl/src/ldso/powerpc/dlsym_time64.S", -"musl/src/ldso/powerpc64/dlsym.s", -"musl/src/ldso/riscv64/dlsym.s", -"musl/src/ldso/s390x/dlsym.s", -"musl/src/ldso/sh/dlsym.s", -"musl/src/ldso/sh/dlsym_time64.S", -"musl/src/ldso/tlsdesc.c", -"musl/src/ldso/x32/dlsym.s", -"musl/src/ldso/x86_64/dlsym.s", -"musl/src/ldso/x86_64/tlsdesc.s", -"musl/src/legacy/cuserid.c", -"musl/src/legacy/daemon.c", -"musl/src/legacy/err.c", -"musl/src/legacy/euidaccess.c", -"musl/src/legacy/ftw.c", -"musl/src/legacy/futimes.c", -"musl/src/legacy/getdtablesize.c", -"musl/src/legacy/getloadavg.c", -"musl/src/legacy/getpagesize.c", -"musl/src/legacy/getpass.c", -"musl/src/legacy/getusershell.c", -"musl/src/legacy/isastream.c", -"musl/src/legacy/lutimes.c", -"musl/src/legacy/ulimit.c", -"musl/src/legacy/utmpx.c", -"musl/src/legacy/valloc.c", -"musl/src/linux/adjtime.c", -"musl/src/linux/adjtimex.c", -"musl/src/linux/arch_prctl.c", -"musl/src/linux/brk.c", -"musl/src/linux/cache.c", -"musl/src/linux/cap.c", -"musl/src/linux/chroot.c", -"musl/src/linux/clock_adjtime.c", -"musl/src/linux/clone.c", -"musl/src/linux/copy_file_range.c", -"musl/src/linux/epoll.c", -"musl/src/linux/eventfd.c", -"musl/src/linux/fallocate.c", -"musl/src/linux/fanotify.c", -"musl/src/linux/flock.c", -"musl/src/linux/getdents.c", -"musl/src/linux/getrandom.c", -"musl/src/linux/inotify.c", -"musl/src/linux/ioperm.c", -"musl/src/linux/iopl.c", -"musl/src/linux/klogctl.c", -"musl/src/linux/membarrier.c", -"musl/src/linux/memfd_create.c", -"musl/src/linux/mlock2.c", -"musl/src/linux/module.c", -"musl/src/linux/mount.c", -"musl/src/linux/name_to_handle_at.c", -"musl/src/linux/open_by_handle_at.c", -"musl/src/linux/personality.c", -"musl/src/linux/pivot_root.c", -"musl/src/linux/ppoll.c", -"musl/src/linux/prctl.c", -"musl/src/linux/prlimit.c", -"musl/src/linux/process_vm.c", -"musl/src/linux/ptrace.c", -"musl/src/linux/quotactl.c", -"musl/src/linux/readahead.c", -"musl/src/linux/reboot.c", -"musl/src/linux/remap_file_pages.c", -"musl/src/linux/sbrk.c", -"musl/src/linux/sendfile.c", -"musl/src/linux/setfsgid.c", -"musl/src/linux/setfsuid.c", -"musl/src/linux/setgroups.c", -"musl/src/linux/sethostname.c", -"musl/src/linux/setns.c", -"musl/src/linux/settimeofday.c", -"musl/src/linux/signalfd.c", -"musl/src/linux/splice.c", -"musl/src/linux/stime.c", -"musl/src/linux/swap.c", -"musl/src/linux/sync_file_range.c", -"musl/src/linux/syncfs.c", -"musl/src/linux/sysinfo.c", -"musl/src/linux/tee.c", -"musl/src/linux/timerfd.c", -"musl/src/linux/unshare.c", -"musl/src/linux/utimes.c", -"musl/src/linux/vhangup.c", -"musl/src/linux/vmsplice.c", -"musl/src/linux/wait3.c", -"musl/src/linux/wait4.c", -"musl/src/linux/x32/sysinfo.c", -"musl/src/linux/xattr.c", -"musl/src/locale/__lctrans.c", -"musl/src/locale/__mo_lookup.c", -"musl/src/locale/bind_textdomain_codeset.c", -"musl/src/locale/c_locale.c", -"musl/src/locale/catclose.c", -"musl/src/locale/catgets.c", -"musl/src/locale/catopen.c", -"musl/src/locale/dcngettext.c", -"musl/src/locale/duplocale.c", -"musl/src/locale/freelocale.c", -"musl/src/locale/iconv.c", -"musl/src/locale/iconv_close.c", -"musl/src/locale/langinfo.c", -"musl/src/locale/locale_map.c", -"musl/src/locale/localeconv.c", -"musl/src/locale/newlocale.c", -"musl/src/locale/pleval.c", -"musl/src/locale/setlocale.c", -"musl/src/locale/strcoll.c", -"musl/src/locale/strfmon.c", -"musl/src/locale/strxfrm.c", -"musl/src/locale/textdomain.c", -"musl/src/locale/uselocale.c", -"musl/src/locale/wcscoll.c", -"musl/src/locale/wcsxfrm.c", -"musl/src/malloc/aligned_alloc.c", -"musl/src/malloc/expand_heap.c", -"musl/src/malloc/lite_malloc.c", -"musl/src/malloc/malloc.c", -"musl/src/malloc/malloc_usable_size.c", -"musl/src/malloc/memalign.c", -"musl/src/malloc/posix_memalign.c", -"musl/src/math/__cos.c", -"musl/src/math/__cosdf.c", -"musl/src/math/__cosl.c", -"musl/src/math/__expo2.c", -"musl/src/math/__expo2f.c", -"musl/src/math/__fpclassify.c", -"musl/src/math/__fpclassifyf.c", -"musl/src/math/__fpclassifyl.c", -"musl/src/math/__invtrigl.c", -"musl/src/math/__math_divzero.c", -"musl/src/math/__math_divzerof.c", -"musl/src/math/__math_invalid.c", -"musl/src/math/__math_invalidf.c", -"musl/src/math/__math_oflow.c", -"musl/src/math/__math_oflowf.c", -"musl/src/math/__math_uflow.c", -"musl/src/math/__math_uflowf.c", -"musl/src/math/__math_xflow.c", -"musl/src/math/__math_xflowf.c", -"musl/src/math/__polevll.c", -"musl/src/math/__rem_pio2.c", -"musl/src/math/__rem_pio2_large.c", -"musl/src/math/__rem_pio2f.c", -"musl/src/math/__rem_pio2l.c", -"musl/src/math/__signbit.c", -"musl/src/math/__signbitf.c", -"musl/src/math/__signbitl.c", -"musl/src/math/__sin.c", -"musl/src/math/__sindf.c", -"musl/src/math/__sinl.c", -"musl/src/math/__tan.c", -"musl/src/math/__tandf.c", -"musl/src/math/__tanl.c", -"musl/src/math/aarch64/ceil.c", -"musl/src/math/aarch64/ceilf.c", -"musl/src/math/aarch64/fabs.c", -"musl/src/math/aarch64/fabsf.c", -"musl/src/math/aarch64/floor.c", -"musl/src/math/aarch64/floorf.c", -"musl/src/math/aarch64/fma.c", -"musl/src/math/aarch64/fmaf.c", -"musl/src/math/aarch64/fmax.c", -"musl/src/math/aarch64/fmaxf.c", -"musl/src/math/aarch64/fmin.c", -"musl/src/math/aarch64/fminf.c", -"musl/src/math/aarch64/llrint.c", -"musl/src/math/aarch64/llrintf.c", -"musl/src/math/aarch64/llround.c", -"musl/src/math/aarch64/llroundf.c", -"musl/src/math/aarch64/lrint.c", -"musl/src/math/aarch64/lrintf.c", -"musl/src/math/aarch64/lround.c", -"musl/src/math/aarch64/lroundf.c", -"musl/src/math/aarch64/nearbyint.c", -"musl/src/math/aarch64/nearbyintf.c", -"musl/src/math/aarch64/rint.c", -"musl/src/math/aarch64/rintf.c", -"musl/src/math/aarch64/round.c", -"musl/src/math/aarch64/roundf.c", -"musl/src/math/aarch64/sqrt.c", -"musl/src/math/aarch64/sqrtf.c", -"musl/src/math/aarch64/trunc.c", -"musl/src/math/aarch64/truncf.c", -"musl/src/math/acos.c", -"musl/src/math/acosf.c", -"musl/src/math/acosh.c", -"musl/src/math/acoshf.c", -"musl/src/math/acoshl.c", -"musl/src/math/acosl.c", -"musl/src/math/arm/fabs.c", -"musl/src/math/arm/fabsf.c", -"musl/src/math/arm/fma.c", -"musl/src/math/arm/fmaf.c", -"musl/src/math/arm/sqrt.c", -"musl/src/math/arm/sqrtf.c", -"musl/src/math/asin.c", -"musl/src/math/asinf.c", -"musl/src/math/asinh.c", -"musl/src/math/asinhf.c", -"musl/src/math/asinhl.c", -"musl/src/math/asinl.c", -"musl/src/math/atan.c", -"musl/src/math/atan2.c", -"musl/src/math/atan2f.c", -"musl/src/math/atan2l.c", -"musl/src/math/atanf.c", -"musl/src/math/atanh.c", -"musl/src/math/atanhf.c", -"musl/src/math/atanhl.c", -"musl/src/math/atanl.c", -"musl/src/math/cbrt.c", -"musl/src/math/cbrtf.c", -"musl/src/math/cbrtl.c", -"musl/src/math/ceil.c", -"musl/src/math/ceilf.c", -"musl/src/math/ceill.c", -"musl/src/math/copysign.c", -"musl/src/math/copysignf.c", -"musl/src/math/copysignl.c", -"musl/src/math/cos.c", -"musl/src/math/cosf.c", -"musl/src/math/cosh.c", -"musl/src/math/coshf.c", -"musl/src/math/coshl.c", -"musl/src/math/cosl.c", -"musl/src/math/erf.c", -"musl/src/math/erff.c", -"musl/src/math/erfl.c", -"musl/src/math/exp.c", -"musl/src/math/exp10.c", -"musl/src/math/exp10f.c", -"musl/src/math/exp10l.c", -"musl/src/math/exp2.c", -"musl/src/math/exp2f.c", -"musl/src/math/exp2f_data.c", -"musl/src/math/exp2l.c", -"musl/src/math/exp_data.c", -"musl/src/math/expf.c", -"musl/src/math/expl.c", -"musl/src/math/expm1.c", -"musl/src/math/expm1f.c", -"musl/src/math/expm1l.c", -"musl/src/math/fabs.c", -"musl/src/math/fabsf.c", -"musl/src/math/fabsl.c", -"musl/src/math/fdim.c", -"musl/src/math/fdimf.c", -"musl/src/math/fdiml.c", -"musl/src/math/finite.c", -"musl/src/math/finitef.c", -"musl/src/math/floor.c", -"musl/src/math/floorf.c", -"musl/src/math/floorl.c", -"musl/src/math/fma.c", -"musl/src/math/fmaf.c", -"musl/src/math/fmal.c", -"musl/src/math/fmax.c", -"musl/src/math/fmaxf.c", -"musl/src/math/fmaxl.c", -"musl/src/math/fmin.c", -"musl/src/math/fminf.c", -"musl/src/math/fminl.c", -"musl/src/math/fmod.c", -"musl/src/math/fmodf.c", -"musl/src/math/fmodl.c", -"musl/src/math/frexp.c", -"musl/src/math/frexpf.c", -"musl/src/math/frexpl.c", -"musl/src/math/hypot.c", -"musl/src/math/hypotf.c", -"musl/src/math/hypotl.c", -"musl/src/math/i386/__invtrigl.s", -"musl/src/math/i386/acos.s", -"musl/src/math/i386/acosf.s", -"musl/src/math/i386/acosl.s", -"musl/src/math/i386/asin.s", -"musl/src/math/i386/asinf.s", -"musl/src/math/i386/asinl.s", -"musl/src/math/i386/atan.s", -"musl/src/math/i386/atan2.s", -"musl/src/math/i386/atan2f.s", -"musl/src/math/i386/atan2l.s", -"musl/src/math/i386/atanf.s", -"musl/src/math/i386/atanl.s", -"musl/src/math/i386/ceil.s", -"musl/src/math/i386/ceilf.s", -"musl/src/math/i386/ceill.s", -"musl/src/math/i386/exp2l.s", -"musl/src/math/i386/exp_ld.s", -"musl/src/math/i386/expl.s", -"musl/src/math/i386/expm1l.s", -"musl/src/math/i386/fabs.s", -"musl/src/math/i386/fabsf.s", -"musl/src/math/i386/fabsl.s", -"musl/src/math/i386/floor.s", -"musl/src/math/i386/floorf.s", -"musl/src/math/i386/floorl.s", -"musl/src/math/i386/fmod.s", -"musl/src/math/i386/fmodf.s", -"musl/src/math/i386/fmodl.s", -"musl/src/math/i386/hypot.s", -"musl/src/math/i386/hypotf.s", -"musl/src/math/i386/ldexp.s", -"musl/src/math/i386/ldexpf.s", -"musl/src/math/i386/ldexpl.s", -"musl/src/math/i386/llrint.s", -"musl/src/math/i386/llrintf.s", -"musl/src/math/i386/llrintl.s", -"musl/src/math/i386/log.s", -"musl/src/math/i386/log10.s", -"musl/src/math/i386/log10f.s", -"musl/src/math/i386/log10l.s", -"musl/src/math/i386/log1p.s", -"musl/src/math/i386/log1pf.s", -"musl/src/math/i386/log1pl.s", -"musl/src/math/i386/log2.s", -"musl/src/math/i386/log2f.s", -"musl/src/math/i386/log2l.s", -"musl/src/math/i386/logf.s", -"musl/src/math/i386/logl.s", -"musl/src/math/i386/lrint.s", -"musl/src/math/i386/lrintf.s", -"musl/src/math/i386/lrintl.s", -"musl/src/math/i386/remainder.s", -"musl/src/math/i386/remainderf.s", -"musl/src/math/i386/remainderl.s", -"musl/src/math/i386/remquo.s", -"musl/src/math/i386/remquof.s", -"musl/src/math/i386/remquol.s", -"musl/src/math/i386/rint.s", -"musl/src/math/i386/rintf.s", -"musl/src/math/i386/rintl.s", -"musl/src/math/i386/scalbln.s", -"musl/src/math/i386/scalblnf.s", -"musl/src/math/i386/scalblnl.s", -"musl/src/math/i386/scalbn.s", -"musl/src/math/i386/scalbnf.s", -"musl/src/math/i386/scalbnl.s", -"musl/src/math/i386/sqrt.s", -"musl/src/math/i386/sqrtf.s", -"musl/src/math/i386/sqrtl.s", -"musl/src/math/i386/trunc.s", -"musl/src/math/i386/truncf.s", -"musl/src/math/i386/truncl.s", -"musl/src/math/ilogb.c", -"musl/src/math/ilogbf.c", -"musl/src/math/ilogbl.c", -"musl/src/math/j0.c", -"musl/src/math/j0f.c", -"musl/src/math/j1.c", -"musl/src/math/j1f.c", -"musl/src/math/jn.c", -"musl/src/math/jnf.c", -"musl/src/math/ldexp.c", -"musl/src/math/ldexpf.c", -"musl/src/math/ldexpl.c", -"musl/src/math/lgamma.c", -"musl/src/math/lgamma_r.c", -"musl/src/math/lgammaf.c", -"musl/src/math/lgammaf_r.c", -"musl/src/math/lgammal.c", -"musl/src/math/llrint.c", -"musl/src/math/llrintf.c", -"musl/src/math/llrintl.c", -"musl/src/math/llround.c", -"musl/src/math/llroundf.c", -"musl/src/math/llroundl.c", -"musl/src/math/log.c", -"musl/src/math/log10.c", -"musl/src/math/log10f.c", -"musl/src/math/log10l.c", -"musl/src/math/log1p.c", -"musl/src/math/log1pf.c", -"musl/src/math/log1pl.c", -"musl/src/math/log2.c", -"musl/src/math/log2_data.c", -"musl/src/math/log2f.c", -"musl/src/math/log2f_data.c", -"musl/src/math/log2l.c", -"musl/src/math/log_data.c", -"musl/src/math/logb.c", -"musl/src/math/logbf.c", -"musl/src/math/logbl.c", -"musl/src/math/logf.c", -"musl/src/math/logf_data.c", -"musl/src/math/logl.c", -"musl/src/math/lrint.c", -"musl/src/math/lrintf.c", -"musl/src/math/lrintl.c", -"musl/src/math/lround.c", -"musl/src/math/lroundf.c", -"musl/src/math/lroundl.c", -"musl/src/math/mips/fabs.c", -"musl/src/math/mips/fabsf.c", -"musl/src/math/mips/sqrt.c", -"musl/src/math/mips/sqrtf.c", -"musl/src/math/modf.c", -"musl/src/math/modff.c", -"musl/src/math/modfl.c", -"musl/src/math/nan.c", -"musl/src/math/nanf.c", -"musl/src/math/nanl.c", -"musl/src/math/nearbyint.c", -"musl/src/math/nearbyintf.c", -"musl/src/math/nearbyintl.c", -"musl/src/math/nextafter.c", -"musl/src/math/nextafterf.c", -"musl/src/math/nextafterl.c", -"musl/src/math/nexttoward.c", -"musl/src/math/nexttowardf.c", -"musl/src/math/nexttowardl.c", -"musl/src/math/pow.c", -"musl/src/math/pow_data.c", -"musl/src/math/powerpc/fabs.c", -"musl/src/math/powerpc/fabsf.c", -"musl/src/math/powerpc/fma.c", -"musl/src/math/powerpc/fmaf.c", -"musl/src/math/powerpc/sqrt.c", -"musl/src/math/powerpc/sqrtf.c", -"musl/src/math/powerpc64/ceil.c", -"musl/src/math/powerpc64/ceilf.c", -"musl/src/math/powerpc64/fabs.c", -"musl/src/math/powerpc64/fabsf.c", -"musl/src/math/powerpc64/floor.c", -"musl/src/math/powerpc64/floorf.c", -"musl/src/math/powerpc64/fma.c", -"musl/src/math/powerpc64/fmaf.c", -"musl/src/math/powerpc64/fmax.c", -"musl/src/math/powerpc64/fmaxf.c", -"musl/src/math/powerpc64/fmin.c", -"musl/src/math/powerpc64/fminf.c", -"musl/src/math/powerpc64/lrint.c", -"musl/src/math/powerpc64/lrintf.c", -"musl/src/math/powerpc64/lround.c", -"musl/src/math/powerpc64/lroundf.c", -"musl/src/math/powerpc64/round.c", -"musl/src/math/powerpc64/roundf.c", -"musl/src/math/powerpc64/sqrt.c", -"musl/src/math/powerpc64/sqrtf.c", -"musl/src/math/powerpc64/trunc.c", -"musl/src/math/powerpc64/truncf.c", -"musl/src/math/powf.c", -"musl/src/math/powf_data.c", -"musl/src/math/powl.c", -"musl/src/math/remainder.c", -"musl/src/math/remainderf.c", -"musl/src/math/remainderl.c", -"musl/src/math/remquo.c", -"musl/src/math/remquof.c", -"musl/src/math/remquol.c", -"musl/src/math/rint.c", -"musl/src/math/rintf.c", -"musl/src/math/rintl.c", -"musl/src/math/riscv64/copysign.c", -"musl/src/math/riscv64/copysignf.c", -"musl/src/math/riscv64/fabs.c", -"musl/src/math/riscv64/fabsf.c", -"musl/src/math/riscv64/fma.c", -"musl/src/math/riscv64/fmaf.c", -"musl/src/math/riscv64/fmax.c", -"musl/src/math/riscv64/fmaxf.c", -"musl/src/math/riscv64/fmin.c", -"musl/src/math/riscv64/fminf.c", -"musl/src/math/riscv64/sqrt.c", -"musl/src/math/riscv64/sqrtf.c", -"musl/src/math/round.c", -"musl/src/math/roundf.c", -"musl/src/math/roundl.c", -"musl/src/math/s390x/ceil.c", -"musl/src/math/s390x/ceilf.c", -"musl/src/math/s390x/ceill.c", -"musl/src/math/s390x/fabs.c", -"musl/src/math/s390x/fabsf.c", -"musl/src/math/s390x/fabsl.c", -"musl/src/math/s390x/floor.c", -"musl/src/math/s390x/floorf.c", -"musl/src/math/s390x/floorl.c", -"musl/src/math/s390x/fma.c", -"musl/src/math/s390x/fmaf.c", -"musl/src/math/s390x/nearbyint.c", -"musl/src/math/s390x/nearbyintf.c", -"musl/src/math/s390x/nearbyintl.c", -"musl/src/math/s390x/rint.c", -"musl/src/math/s390x/rintf.c", -"musl/src/math/s390x/rintl.c", -"musl/src/math/s390x/round.c", -"musl/src/math/s390x/roundf.c", -"musl/src/math/s390x/roundl.c", -"musl/src/math/s390x/sqrt.c", -"musl/src/math/s390x/sqrtf.c", -"musl/src/math/s390x/sqrtl.c", -"musl/src/math/s390x/trunc.c", -"musl/src/math/s390x/truncf.c", -"musl/src/math/s390x/truncl.c", -"musl/src/math/scalb.c", -"musl/src/math/scalbf.c", -"musl/src/math/scalbln.c", -"musl/src/math/scalblnf.c", -"musl/src/math/scalblnl.c", -"musl/src/math/scalbn.c", -"musl/src/math/scalbnf.c", -"musl/src/math/scalbnl.c", -"musl/src/math/signgam.c", -"musl/src/math/significand.c", -"musl/src/math/significandf.c", -"musl/src/math/sin.c", -"musl/src/math/sincos.c", -"musl/src/math/sincosf.c", -"musl/src/math/sincosl.c", -"musl/src/math/sinf.c", -"musl/src/math/sinh.c", -"musl/src/math/sinhf.c", -"musl/src/math/sinhl.c", -"musl/src/math/sinl.c", -"musl/src/math/sqrt.c", -"musl/src/math/sqrtf.c", -"musl/src/math/sqrtl.c", -"musl/src/math/tan.c", -"musl/src/math/tanf.c", -"musl/src/math/tanh.c", -"musl/src/math/tanhf.c", -"musl/src/math/tanhl.c", -"musl/src/math/tanl.c", -"musl/src/math/tgamma.c", -"musl/src/math/tgammaf.c", -"musl/src/math/tgammal.c", -"musl/src/math/trunc.c", -"musl/src/math/truncf.c", -"musl/src/math/truncl.c", -"musl/src/math/x32/__invtrigl.s", -"musl/src/math/x32/acosl.s", -"musl/src/math/x32/asinl.s", -"musl/src/math/x32/atan2l.s", -"musl/src/math/x32/atanl.s", -"musl/src/math/x32/ceill.s", -"musl/src/math/x32/exp2l.s", -"musl/src/math/x32/expl.s", -"musl/src/math/x32/expm1l.s", -"musl/src/math/x32/fabs.s", -"musl/src/math/x32/fabsf.s", -"musl/src/math/x32/fabsl.s", -"musl/src/math/x32/floorl.s", -"musl/src/math/x32/fma.c", -"musl/src/math/x32/fmaf.c", -"musl/src/math/x32/fmodl.s", -"musl/src/math/x32/llrint.s", -"musl/src/math/x32/llrintf.s", -"musl/src/math/x32/llrintl.s", -"musl/src/math/x32/log10l.s", -"musl/src/math/x32/log1pl.s", -"musl/src/math/x32/log2l.s", -"musl/src/math/x32/logl.s", -"musl/src/math/x32/lrint.s", -"musl/src/math/x32/lrintf.s", -"musl/src/math/x32/lrintl.s", -"musl/src/math/x32/remainderl.s", -"musl/src/math/x32/rintl.s", -"musl/src/math/x32/sqrt.s", -"musl/src/math/x32/sqrtf.s", -"musl/src/math/x32/sqrtl.s", -"musl/src/math/x32/truncl.s", -"musl/src/math/x86_64/__invtrigl.s", -"musl/src/math/x86_64/acosl.s", -"musl/src/math/x86_64/asinl.s", -"musl/src/math/x86_64/atan2l.s", -"musl/src/math/x86_64/atanl.s", -"musl/src/math/x86_64/ceill.s", -"musl/src/math/x86_64/exp2l.s", -"musl/src/math/x86_64/expl.s", -"musl/src/math/x86_64/expm1l.s", -"musl/src/math/x86_64/fabs.s", -"musl/src/math/x86_64/fabsf.s", -"musl/src/math/x86_64/fabsl.s", -"musl/src/math/x86_64/floorl.s", -"musl/src/math/x86_64/fma.c", -"musl/src/math/x86_64/fmaf.c", -"musl/src/math/x86_64/fmodl.s", -"musl/src/math/x86_64/llrint.s", -"musl/src/math/x86_64/llrintf.s", -"musl/src/math/x86_64/llrintl.s", -"musl/src/math/x86_64/log10l.s", -"musl/src/math/x86_64/log1pl.s", -"musl/src/math/x86_64/log2l.s", -"musl/src/math/x86_64/logl.s", -"musl/src/math/x86_64/lrint.s", -"musl/src/math/x86_64/lrintf.s", -"musl/src/math/x86_64/lrintl.s", -"musl/src/math/x86_64/remainderl.s", -"musl/src/math/x86_64/rintl.s", -"musl/src/math/x86_64/sqrt.s", -"musl/src/math/x86_64/sqrtf.s", -"musl/src/math/x86_64/sqrtl.s", -"musl/src/math/x86_64/truncl.s", -"musl/src/misc/a64l.c", -"musl/src/misc/basename.c", -"musl/src/misc/dirname.c", -"musl/src/misc/ffs.c", -"musl/src/misc/ffsl.c", -"musl/src/misc/ffsll.c", -"musl/src/misc/fmtmsg.c", -"musl/src/misc/forkpty.c", -"musl/src/misc/get_current_dir_name.c", -"musl/src/misc/getauxval.c", -"musl/src/misc/getdomainname.c", -"musl/src/misc/getentropy.c", -"musl/src/misc/gethostid.c", -"musl/src/misc/getopt.c", -"musl/src/misc/getopt_long.c", -"musl/src/misc/getpriority.c", -"musl/src/misc/getresgid.c", -"musl/src/misc/getresuid.c", -"musl/src/misc/getrlimit.c", -"musl/src/misc/getrusage.c", -"musl/src/misc/getsubopt.c", -"musl/src/misc/initgroups.c", -"musl/src/misc/ioctl.c", -"musl/src/misc/issetugid.c", -"musl/src/misc/lockf.c", -"musl/src/misc/login_tty.c", -"musl/src/misc/mntent.c", -"musl/src/misc/nftw.c", -"musl/src/misc/openpty.c", -"musl/src/misc/ptsname.c", -"musl/src/misc/pty.c", -"musl/src/misc/realpath.c", -"musl/src/misc/setdomainname.c", -"musl/src/misc/setpriority.c", -"musl/src/misc/setrlimit.c", -"musl/src/misc/syscall.c", -"musl/src/misc/syslog.c", -"musl/src/misc/uname.c", -"musl/src/misc/wordexp.c", -"musl/src/mman/madvise.c", -"musl/src/mman/mincore.c", -"musl/src/mman/mlock.c", -"musl/src/mman/mlockall.c", -"musl/src/mman/mmap.c", -"musl/src/mman/mprotect.c", -"musl/src/mman/mremap.c", -"musl/src/mman/msync.c", -"musl/src/mman/munlock.c", -"musl/src/mman/munlockall.c", -"musl/src/mman/munmap.c", -"musl/src/mman/posix_madvise.c", -"musl/src/mman/shm_open.c", -"musl/src/mq/mq_close.c", -"musl/src/mq/mq_getattr.c", -"musl/src/mq/mq_notify.c", -"musl/src/mq/mq_open.c", -"musl/src/mq/mq_receive.c", -"musl/src/mq/mq_send.c", -"musl/src/mq/mq_setattr.c", -"musl/src/mq/mq_timedreceive.c", -"musl/src/mq/mq_timedsend.c", -"musl/src/mq/mq_unlink.c", -"musl/src/multibyte/btowc.c", -"musl/src/multibyte/c16rtomb.c", -"musl/src/multibyte/c32rtomb.c", -"musl/src/multibyte/internal.c", -"musl/src/multibyte/mblen.c", -"musl/src/multibyte/mbrlen.c", -"musl/src/multibyte/mbrtoc16.c", -"musl/src/multibyte/mbrtoc32.c", -"musl/src/multibyte/mbrtowc.c", -"musl/src/multibyte/mbsinit.c", -"musl/src/multibyte/mbsnrtowcs.c", -"musl/src/multibyte/mbsrtowcs.c", -"musl/src/multibyte/mbstowcs.c", -"musl/src/multibyte/mbtowc.c", -"musl/src/multibyte/wcrtomb.c", -"musl/src/multibyte/wcsnrtombs.c", -"musl/src/multibyte/wcsrtombs.c", -"musl/src/multibyte/wcstombs.c", -"musl/src/multibyte/wctob.c", -"musl/src/multibyte/wctomb.c", -"musl/src/network/accept.c", -"musl/src/network/accept4.c", -"musl/src/network/bind.c", -"musl/src/network/connect.c", -"musl/src/network/dn_comp.c", -"musl/src/network/dn_expand.c", -"musl/src/network/dn_skipname.c", -"musl/src/network/dns_parse.c", -"musl/src/network/ent.c", -"musl/src/network/ether.c", -"musl/src/network/freeaddrinfo.c", -"musl/src/network/gai_strerror.c", -"musl/src/network/getaddrinfo.c", -"musl/src/network/gethostbyaddr.c", -"musl/src/network/gethostbyaddr_r.c", -"musl/src/network/gethostbyname.c", -"musl/src/network/gethostbyname2.c", -"musl/src/network/gethostbyname2_r.c", -"musl/src/network/gethostbyname_r.c", -"musl/src/network/getifaddrs.c", -"musl/src/network/getnameinfo.c", -"musl/src/network/getpeername.c", -"musl/src/network/getservbyname.c", -"musl/src/network/getservbyname_r.c", -"musl/src/network/getservbyport.c", -"musl/src/network/getservbyport_r.c", -"musl/src/network/getsockname.c", -"musl/src/network/getsockopt.c", -"musl/src/network/h_errno.c", -"musl/src/network/herror.c", -"musl/src/network/hstrerror.c", -"musl/src/network/htonl.c", -"musl/src/network/htons.c", -"musl/src/network/if_freenameindex.c", -"musl/src/network/if_indextoname.c", -"musl/src/network/if_nameindex.c", -"musl/src/network/if_nametoindex.c", -"musl/src/network/in6addr_any.c", -"musl/src/network/in6addr_loopback.c", -"musl/src/network/inet_addr.c", -"musl/src/network/inet_aton.c", -"musl/src/network/inet_legacy.c", -"musl/src/network/inet_ntoa.c", -"musl/src/network/inet_ntop.c", -"musl/src/network/inet_pton.c", -"musl/src/network/listen.c", -"musl/src/network/lookup_ipliteral.c", -"musl/src/network/lookup_name.c", -"musl/src/network/lookup_serv.c", -"musl/src/network/netlink.c", -"musl/src/network/netname.c", -"musl/src/network/ns_parse.c", -"musl/src/network/ntohl.c", -"musl/src/network/ntohs.c", -"musl/src/network/proto.c", -"musl/src/network/recv.c", -"musl/src/network/recvfrom.c", -"musl/src/network/recvmmsg.c", -"musl/src/network/recvmsg.c", -"musl/src/network/res_init.c", -"musl/src/network/res_mkquery.c", -"musl/src/network/res_msend.c", -"musl/src/network/res_query.c", -"musl/src/network/res_querydomain.c", -"musl/src/network/res_send.c", -"musl/src/network/res_state.c", -"musl/src/network/resolvconf.c", -"musl/src/network/send.c", -"musl/src/network/sendmmsg.c", -"musl/src/network/sendmsg.c", -"musl/src/network/sendto.c", -"musl/src/network/serv.c", -"musl/src/network/setsockopt.c", -"musl/src/network/shutdown.c", -"musl/src/network/sockatmark.c", -"musl/src/network/socket.c", -"musl/src/network/socketpair.c", -"musl/src/passwd/fgetgrent.c", -"musl/src/passwd/fgetpwent.c", -"musl/src/passwd/fgetspent.c", -"musl/src/passwd/getgr_a.c", -"musl/src/passwd/getgr_r.c", -"musl/src/passwd/getgrent.c", -"musl/src/passwd/getgrent_a.c", -"musl/src/passwd/getgrouplist.c", -"musl/src/passwd/getpw_a.c", -"musl/src/passwd/getpw_r.c", -"musl/src/passwd/getpwent.c", -"musl/src/passwd/getpwent_a.c", -"musl/src/passwd/getspent.c", -"musl/src/passwd/getspnam.c", -"musl/src/passwd/getspnam_r.c", -"musl/src/passwd/lckpwdf.c", -"musl/src/passwd/nscd_query.c", -"musl/src/passwd/putgrent.c", -"musl/src/passwd/putpwent.c", -"musl/src/passwd/putspent.c", -"musl/src/prng/__rand48_step.c", -"musl/src/prng/__seed48.c", -"musl/src/prng/drand48.c", -"musl/src/prng/lcong48.c", -"musl/src/prng/lrand48.c", -"musl/src/prng/mrand48.c", -"musl/src/prng/rand.c", -"musl/src/prng/rand_r.c", -"musl/src/prng/random.c", -"musl/src/prng/seed48.c", -"musl/src/prng/srand48.c", -"musl/src/process/arm/vfork.s", -"musl/src/process/execl.c", -"musl/src/process/execle.c", -"musl/src/process/execlp.c", -"musl/src/process/execv.c", -"musl/src/process/execve.c", -"musl/src/process/execvp.c", -"musl/src/process/fexecve.c", -"musl/src/process/fork.c", -"musl/src/process/i386/vfork.s", -"musl/src/process/posix_spawn.c", -"musl/src/process/posix_spawn_file_actions_addchdir.c", -"musl/src/process/posix_spawn_file_actions_addclose.c", -"musl/src/process/posix_spawn_file_actions_adddup2.c", -"musl/src/process/posix_spawn_file_actions_addfchdir.c", -"musl/src/process/posix_spawn_file_actions_addopen.c", -"musl/src/process/posix_spawn_file_actions_destroy.c", -"musl/src/process/posix_spawn_file_actions_init.c", -"musl/src/process/posix_spawnattr_destroy.c", -"musl/src/process/posix_spawnattr_getflags.c", -"musl/src/process/posix_spawnattr_getpgroup.c", -"musl/src/process/posix_spawnattr_getsigdefault.c", -"musl/src/process/posix_spawnattr_getsigmask.c", -"musl/src/process/posix_spawnattr_init.c", -"musl/src/process/posix_spawnattr_sched.c", -"musl/src/process/posix_spawnattr_setflags.c", -"musl/src/process/posix_spawnattr_setpgroup.c", -"musl/src/process/posix_spawnattr_setsigdefault.c", -"musl/src/process/posix_spawnattr_setsigmask.c", -"musl/src/process/posix_spawnp.c", -"musl/src/process/s390x/vfork.s", -"musl/src/process/sh/vfork.s", -"musl/src/process/system.c", -"musl/src/process/vfork.c", -"musl/src/process/wait.c", -"musl/src/process/waitid.c", -"musl/src/process/waitpid.c", -"musl/src/process/x32/vfork.s", -"musl/src/process/x86_64/vfork.s", -"musl/src/regex/fnmatch.c", -"musl/src/regex/glob.c", -"musl/src/regex/regcomp.c", -"musl/src/regex/regerror.c", -"musl/src/regex/regexec.c", -"musl/src/regex/tre-mem.c", -"musl/src/sched/affinity.c", -"musl/src/sched/sched_cpucount.c", -"musl/src/sched/sched_get_priority_max.c", -"musl/src/sched/sched_getcpu.c", -"musl/src/sched/sched_getparam.c", -"musl/src/sched/sched_getscheduler.c", -"musl/src/sched/sched_rr_get_interval.c", -"musl/src/sched/sched_setparam.c", -"musl/src/sched/sched_setscheduler.c", -"musl/src/sched/sched_yield.c", -"musl/src/search/hsearch.c", -"musl/src/search/insque.c", -"musl/src/search/lsearch.c", -"musl/src/search/tdelete.c", -"musl/src/search/tdestroy.c", -"musl/src/search/tfind.c", -"musl/src/search/tsearch.c", -"musl/src/search/twalk.c", -"musl/src/select/poll.c", -"musl/src/select/pselect.c", -"musl/src/select/select.c", -"musl/src/setjmp/aarch64/longjmp.s", -"musl/src/setjmp/aarch64/setjmp.s", -"musl/src/setjmp/arm/longjmp.S", -"musl/src/setjmp/arm/setjmp.S", -"musl/src/setjmp/i386/longjmp.s", -"musl/src/setjmp/i386/setjmp.s", -"musl/src/setjmp/longjmp.c", -"musl/src/setjmp/m68k/longjmp.s", -"musl/src/setjmp/m68k/setjmp.s", -"musl/src/setjmp/microblaze/longjmp.s", -"musl/src/setjmp/microblaze/setjmp.s", -"musl/src/setjmp/mips/longjmp.S", -"musl/src/setjmp/mips/setjmp.S", -"musl/src/setjmp/mips64/longjmp.S", -"musl/src/setjmp/mips64/setjmp.S", -"musl/src/setjmp/mipsn32/longjmp.S", -"musl/src/setjmp/mipsn32/setjmp.S", -"musl/src/setjmp/or1k/longjmp.s", -"musl/src/setjmp/or1k/setjmp.s", -"musl/src/setjmp/powerpc/longjmp.S", -"musl/src/setjmp/powerpc/setjmp.S", -"musl/src/setjmp/powerpc64/longjmp.s", -"musl/src/setjmp/powerpc64/setjmp.s", -"musl/src/setjmp/riscv64/longjmp.S", -"musl/src/setjmp/riscv64/setjmp.S", -"musl/src/setjmp/s390x/longjmp.s", -"musl/src/setjmp/s390x/setjmp.s", -"musl/src/setjmp/setjmp.c", -"musl/src/setjmp/sh/longjmp.S", -"musl/src/setjmp/sh/setjmp.S", -"musl/src/setjmp/x32/longjmp.s", -"musl/src/setjmp/x32/setjmp.s", -"musl/src/setjmp/x86_64/longjmp.s", -"musl/src/setjmp/x86_64/setjmp.s", -"musl/src/signal/aarch64/restore.s", -"musl/src/signal/aarch64/sigsetjmp.s", -"musl/src/signal/arm/restore.s", -"musl/src/signal/arm/sigsetjmp.s", -"musl/src/signal/block.c", -"musl/src/signal/getitimer.c", -"musl/src/signal/i386/restore.s", -"musl/src/signal/i386/sigsetjmp.s", -"musl/src/signal/kill.c", -"musl/src/signal/killpg.c", -"musl/src/signal/m68k/sigsetjmp.s", -"musl/src/signal/microblaze/restore.s", -"musl/src/signal/microblaze/sigsetjmp.s", -"musl/src/signal/mips/restore.s", -"musl/src/signal/mips/sigsetjmp.s", -"musl/src/signal/mips64/restore.s", -"musl/src/signal/mips64/sigsetjmp.s", -"musl/src/signal/mipsn32/restore.s", -"musl/src/signal/mipsn32/sigsetjmp.s", -"musl/src/signal/or1k/sigsetjmp.s", -"musl/src/signal/powerpc/restore.s", -"musl/src/signal/powerpc/sigsetjmp.s", -"musl/src/signal/powerpc64/restore.s", -"musl/src/signal/powerpc64/sigsetjmp.s", -"musl/src/signal/psiginfo.c", -"musl/src/signal/psignal.c", -"musl/src/signal/raise.c", -"musl/src/signal/restore.c", -"musl/src/signal/riscv64/restore.s", -"musl/src/signal/riscv64/sigsetjmp.s", -"musl/src/signal/s390x/restore.s", -"musl/src/signal/s390x/sigsetjmp.s", -"musl/src/signal/setitimer.c", -"musl/src/signal/sh/restore.s", -"musl/src/signal/sh/sigsetjmp.s", -"musl/src/signal/sigaction.c", -"musl/src/signal/sigaddset.c", -"musl/src/signal/sigaltstack.c", -"musl/src/signal/sigandset.c", -"musl/src/signal/sigdelset.c", -"musl/src/signal/sigemptyset.c", -"musl/src/signal/sigfillset.c", -"musl/src/signal/sighold.c", -"musl/src/signal/sigignore.c", -"musl/src/signal/siginterrupt.c", -"musl/src/signal/sigisemptyset.c", -"musl/src/signal/sigismember.c", -"musl/src/signal/siglongjmp.c", -"musl/src/signal/signal.c", -"musl/src/signal/sigorset.c", -"musl/src/signal/sigpause.c", -"musl/src/signal/sigpending.c", -"musl/src/signal/sigprocmask.c", -"musl/src/signal/sigqueue.c", -"musl/src/signal/sigrelse.c", -"musl/src/signal/sigrtmax.c", -"musl/src/signal/sigrtmin.c", -"musl/src/signal/sigset.c", -"musl/src/signal/sigsetjmp.c", -"musl/src/signal/sigsetjmp_tail.c", -"musl/src/signal/sigsuspend.c", -"musl/src/signal/sigtimedwait.c", -"musl/src/signal/sigwait.c", -"musl/src/signal/sigwaitinfo.c", -"musl/src/signal/x32/getitimer.c", -"musl/src/signal/x32/restore.s", -"musl/src/signal/x32/setitimer.c", -"musl/src/signal/x32/sigsetjmp.s", -"musl/src/signal/x86_64/restore.s", -"musl/src/signal/x86_64/sigsetjmp.s", -"musl/src/stat/__xstat.c", -"musl/src/stat/chmod.c", -"musl/src/stat/fchmod.c", -"musl/src/stat/fchmodat.c", -"musl/src/stat/fstat.c", -"musl/src/stat/fstatat.c", -"musl/src/stat/futimens.c", -"musl/src/stat/futimesat.c", -"musl/src/stat/lchmod.c", -"musl/src/stat/lstat.c", -"musl/src/stat/mkdir.c", -"musl/src/stat/mkdirat.c", -"musl/src/stat/mkfifo.c", -"musl/src/stat/mkfifoat.c", -"musl/src/stat/mknod.c", -"musl/src/stat/mknodat.c", -"musl/src/stat/stat.c", -"musl/src/stat/statvfs.c", -"musl/src/stat/umask.c", -"musl/src/stat/utimensat.c", -"musl/src/stdio/__fclose_ca.c", -"musl/src/stdio/__fdopen.c", -"musl/src/stdio/__fmodeflags.c", -"musl/src/stdio/__fopen_rb_ca.c", -"musl/src/stdio/__lockfile.c", -"musl/src/stdio/__overflow.c", -"musl/src/stdio/__stdio_close.c", -"musl/src/stdio/__stdio_exit.c", -"musl/src/stdio/__stdio_read.c", -"musl/src/stdio/__stdio_seek.c", -"musl/src/stdio/__stdio_write.c", -"musl/src/stdio/__stdout_write.c", -"musl/src/stdio/__string_read.c", -"musl/src/stdio/__toread.c", -"musl/src/stdio/__towrite.c", -"musl/src/stdio/__uflow.c", -"musl/src/stdio/asprintf.c", -"musl/src/stdio/clearerr.c", -"musl/src/stdio/dprintf.c", -"musl/src/stdio/ext.c", -"musl/src/stdio/ext2.c", -"musl/src/stdio/fclose.c", -"musl/src/stdio/feof.c", -"musl/src/stdio/ferror.c", -"musl/src/stdio/fflush.c", -"musl/src/stdio/fgetc.c", -"musl/src/stdio/fgetln.c", -"musl/src/stdio/fgetpos.c", -"musl/src/stdio/fgets.c", -"musl/src/stdio/fgetwc.c", -"musl/src/stdio/fgetws.c", -"musl/src/stdio/fileno.c", -"musl/src/stdio/flockfile.c", -"musl/src/stdio/fmemopen.c", -"musl/src/stdio/fopen.c", -"musl/src/stdio/fopencookie.c", -"musl/src/stdio/fprintf.c", -"musl/src/stdio/fputc.c", -"musl/src/stdio/fputs.c", -"musl/src/stdio/fputwc.c", -"musl/src/stdio/fputws.c", -"musl/src/stdio/fread.c", -"musl/src/stdio/freopen.c", -"musl/src/stdio/fscanf.c", -"musl/src/stdio/fseek.c", -"musl/src/stdio/fsetpos.c", -"musl/src/stdio/ftell.c", -"musl/src/stdio/ftrylockfile.c", -"musl/src/stdio/funlockfile.c", -"musl/src/stdio/fwide.c", -"musl/src/stdio/fwprintf.c", -"musl/src/stdio/fwrite.c", -"musl/src/stdio/fwscanf.c", -"musl/src/stdio/getc.c", -"musl/src/stdio/getc_unlocked.c", -"musl/src/stdio/getchar.c", -"musl/src/stdio/getchar_unlocked.c", -"musl/src/stdio/getdelim.c", -"musl/src/stdio/getline.c", -"musl/src/stdio/gets.c", -"musl/src/stdio/getw.c", -"musl/src/stdio/getwc.c", -"musl/src/stdio/getwchar.c", -"musl/src/stdio/ofl.c", -"musl/src/stdio/ofl_add.c", -"musl/src/stdio/open_memstream.c", -"musl/src/stdio/open_wmemstream.c", -"musl/src/stdio/pclose.c", -"musl/src/stdio/perror.c", -"musl/src/stdio/popen.c", -"musl/src/stdio/printf.c", -"musl/src/stdio/putc.c", -"musl/src/stdio/putc_unlocked.c", -"musl/src/stdio/putchar.c", -"musl/src/stdio/putchar_unlocked.c", -"musl/src/stdio/puts.c", -"musl/src/stdio/putw.c", -"musl/src/stdio/putwc.c", -"musl/src/stdio/putwchar.c", -"musl/src/stdio/remove.c", -"musl/src/stdio/rename.c", -"musl/src/stdio/rewind.c", -"musl/src/stdio/scanf.c", -"musl/src/stdio/setbuf.c", -"musl/src/stdio/setbuffer.c", -"musl/src/stdio/setlinebuf.c", -"musl/src/stdio/setvbuf.c", -"musl/src/stdio/snprintf.c", -"musl/src/stdio/sprintf.c", -"musl/src/stdio/sscanf.c", -"musl/src/stdio/stderr.c", -"musl/src/stdio/stdin.c", -"musl/src/stdio/stdout.c", -"musl/src/stdio/swprintf.c", -"musl/src/stdio/swscanf.c", -"musl/src/stdio/tempnam.c", -"musl/src/stdio/tmpfile.c", -"musl/src/stdio/tmpnam.c", -"musl/src/stdio/ungetc.c", -"musl/src/stdio/ungetwc.c", -"musl/src/stdio/vasprintf.c", -"musl/src/stdio/vdprintf.c", -"musl/src/stdio/vfprintf.c", -"musl/src/stdio/vfscanf.c", -"musl/src/stdio/vfwprintf.c", -"musl/src/stdio/vfwscanf.c", -"musl/src/stdio/vprintf.c", -"musl/src/stdio/vscanf.c", -"musl/src/stdio/vsnprintf.c", -"musl/src/stdio/vsprintf.c", -"musl/src/stdio/vsscanf.c", -"musl/src/stdio/vswprintf.c", -"musl/src/stdio/vswscanf.c", -"musl/src/stdio/vwprintf.c", -"musl/src/stdio/vwscanf.c", -"musl/src/stdio/wprintf.c", -"musl/src/stdio/wscanf.c", -"musl/src/stdlib/abs.c", -"musl/src/stdlib/atof.c", -"musl/src/stdlib/atoi.c", -"musl/src/stdlib/atol.c", -"musl/src/stdlib/atoll.c", -"musl/src/stdlib/bsearch.c", -"musl/src/stdlib/div.c", -"musl/src/stdlib/ecvt.c", -"musl/src/stdlib/fcvt.c", -"musl/src/stdlib/gcvt.c", -"musl/src/stdlib/imaxabs.c", -"musl/src/stdlib/imaxdiv.c", -"musl/src/stdlib/labs.c", -"musl/src/stdlib/ldiv.c", -"musl/src/stdlib/llabs.c", -"musl/src/stdlib/lldiv.c", -"musl/src/stdlib/qsort.c", -"musl/src/stdlib/strtod.c", -"musl/src/stdlib/strtol.c", -"musl/src/stdlib/wcstod.c", -"musl/src/stdlib/wcstol.c", -"musl/src/string/arm/__aeabi_memcpy.s", -"musl/src/string/arm/__aeabi_memset.s", -"musl/src/string/arm/memcpy.c", -"musl/src/string/arm/memcpy_le.S", -"musl/src/string/bcmp.c", -"musl/src/string/bcopy.c", -"musl/src/string/bzero.c", -"musl/src/string/explicit_bzero.c", -"musl/src/string/i386/memcpy.s", -"musl/src/string/i386/memmove.s", -"musl/src/string/i386/memset.s", -"musl/src/string/index.c", -"musl/src/string/memccpy.c", -"musl/src/string/memchr.c", -"musl/src/string/memcmp.c", -"musl/src/string/memcpy.c", -"musl/src/string/memmem.c", -"musl/src/string/memmove.c", -"musl/src/string/mempcpy.c", -"musl/src/string/memrchr.c", -"musl/src/string/memset.c", -"musl/src/string/rindex.c", -"musl/src/string/stpcpy.c", -"musl/src/string/stpncpy.c", -"musl/src/string/strcasecmp.c", -"musl/src/string/strcasestr.c", -"musl/src/string/strcat.c", -"musl/src/string/strchr.c", -"musl/src/string/strchrnul.c", -"musl/src/string/strcmp.c", -"musl/src/string/strcpy.c", -"musl/src/string/strcspn.c", -"musl/src/string/strdup.c", -"musl/src/string/strerror_r.c", -"musl/src/string/strlcat.c", -"musl/src/string/strlcpy.c", -"musl/src/string/strlen.c", -"musl/src/string/strncasecmp.c", -"musl/src/string/strncat.c", -"musl/src/string/strncmp.c", -"musl/src/string/strncpy.c", -"musl/src/string/strndup.c", -"musl/src/string/strnlen.c", -"musl/src/string/strpbrk.c", -"musl/src/string/strrchr.c", -"musl/src/string/strsep.c", -"musl/src/string/strsignal.c", -"musl/src/string/strspn.c", -"musl/src/string/strstr.c", -"musl/src/string/strtok.c", -"musl/src/string/strtok_r.c", -"musl/src/string/strverscmp.c", -"musl/src/string/swab.c", -"musl/src/string/wcpcpy.c", -"musl/src/string/wcpncpy.c", -"musl/src/string/wcscasecmp.c", -"musl/src/string/wcscasecmp_l.c", -"musl/src/string/wcscat.c", -"musl/src/string/wcschr.c", -"musl/src/string/wcscmp.c", -"musl/src/string/wcscpy.c", -"musl/src/string/wcscspn.c", -"musl/src/string/wcsdup.c", -"musl/src/string/wcslen.c", -"musl/src/string/wcsncasecmp.c", -"musl/src/string/wcsncasecmp_l.c", -"musl/src/string/wcsncat.c", -"musl/src/string/wcsncmp.c", -"musl/src/string/wcsncpy.c", -"musl/src/string/wcsnlen.c", -"musl/src/string/wcspbrk.c", -"musl/src/string/wcsrchr.c", -"musl/src/string/wcsspn.c", -"musl/src/string/wcsstr.c", -"musl/src/string/wcstok.c", -"musl/src/string/wcswcs.c", -"musl/src/string/wmemchr.c", -"musl/src/string/wmemcmp.c", -"musl/src/string/wmemcpy.c", -"musl/src/string/wmemmove.c", -"musl/src/string/wmemset.c", -"musl/src/string/x86_64/memcpy.s", -"musl/src/string/x86_64/memmove.s", -"musl/src/string/x86_64/memset.s", -"musl/src/temp/__randname.c", -"musl/src/temp/mkdtemp.c", -"musl/src/temp/mkostemp.c", -"musl/src/temp/mkostemps.c", -"musl/src/temp/mkstemp.c", -"musl/src/temp/mkstemps.c", -"musl/src/temp/mktemp.c", -"musl/src/termios/cfgetospeed.c", -"musl/src/termios/cfmakeraw.c", -"musl/src/termios/cfsetospeed.c", -"musl/src/termios/tcdrain.c", -"musl/src/termios/tcflow.c", -"musl/src/termios/tcflush.c", -"musl/src/termios/tcgetattr.c", -"musl/src/termios/tcgetsid.c", -"musl/src/termios/tcsendbreak.c", -"musl/src/termios/tcsetattr.c", -"musl/src/thread/__lock.c", -"musl/src/thread/__set_thread_area.c", -"musl/src/thread/__syscall_cp.c", -"musl/src/thread/__timedwait.c", -"musl/src/thread/__tls_get_addr.c", -"musl/src/thread/__unmapself.c", -"musl/src/thread/__wait.c", -"musl/src/thread/aarch64/__set_thread_area.s", -"musl/src/thread/aarch64/__unmapself.s", -"musl/src/thread/aarch64/clone.s", -"musl/src/thread/aarch64/syscall_cp.s", -"musl/src/thread/arm/__aeabi_read_tp.s", -"musl/src/thread/arm/__set_thread_area.c", -"musl/src/thread/arm/__unmapself.s", -"musl/src/thread/arm/atomics.s", -"musl/src/thread/arm/clone.s", -"musl/src/thread/arm/syscall_cp.s", -"musl/src/thread/call_once.c", -"musl/src/thread/clone.c", -"musl/src/thread/cnd_broadcast.c", -"musl/src/thread/cnd_destroy.c", -"musl/src/thread/cnd_init.c", -"musl/src/thread/cnd_signal.c", -"musl/src/thread/cnd_timedwait.c", -"musl/src/thread/cnd_wait.c", -"musl/src/thread/default_attr.c", -"musl/src/thread/i386/__set_thread_area.s", -"musl/src/thread/i386/__unmapself.s", -"musl/src/thread/i386/clone.s", -"musl/src/thread/i386/syscall_cp.s", -"musl/src/thread/i386/tls.s", -"musl/src/thread/lock_ptc.c", -"musl/src/thread/m68k/__m68k_read_tp.s", -"musl/src/thread/m68k/clone.s", -"musl/src/thread/m68k/syscall_cp.s", -"musl/src/thread/microblaze/__set_thread_area.s", -"musl/src/thread/microblaze/__unmapself.s", -"musl/src/thread/microblaze/clone.s", -"musl/src/thread/microblaze/syscall_cp.s", -"musl/src/thread/mips/__unmapself.s", -"musl/src/thread/mips/clone.s", -"musl/src/thread/mips/syscall_cp.s", -"musl/src/thread/mips64/__unmapself.s", -"musl/src/thread/mips64/clone.s", -"musl/src/thread/mips64/syscall_cp.s", -"musl/src/thread/mipsn32/__unmapself.s", -"musl/src/thread/mipsn32/clone.s", -"musl/src/thread/mipsn32/syscall_cp.s", -"musl/src/thread/mtx_destroy.c", -"musl/src/thread/mtx_init.c", -"musl/src/thread/mtx_lock.c", -"musl/src/thread/mtx_timedlock.c", -"musl/src/thread/mtx_trylock.c", -"musl/src/thread/mtx_unlock.c", -"musl/src/thread/or1k/__set_thread_area.s", -"musl/src/thread/or1k/__unmapself.s", -"musl/src/thread/or1k/clone.s", -"musl/src/thread/or1k/syscall_cp.s", -"musl/src/thread/powerpc/__set_thread_area.s", -"musl/src/thread/powerpc/__unmapself.s", -"musl/src/thread/powerpc/clone.s", -"musl/src/thread/powerpc/syscall_cp.s", -"musl/src/thread/powerpc64/__set_thread_area.s", -"musl/src/thread/powerpc64/__unmapself.s", -"musl/src/thread/powerpc64/clone.s", -"musl/src/thread/powerpc64/syscall_cp.s", -"musl/src/thread/pthread_atfork.c", -"musl/src/thread/pthread_attr_destroy.c", -"musl/src/thread/pthread_attr_get.c", -"musl/src/thread/pthread_attr_init.c", -"musl/src/thread/pthread_attr_setdetachstate.c", -"musl/src/thread/pthread_attr_setguardsize.c", -"musl/src/thread/pthread_attr_setinheritsched.c", -"musl/src/thread/pthread_attr_setschedparam.c", -"musl/src/thread/pthread_attr_setschedpolicy.c", -"musl/src/thread/pthread_attr_setscope.c", -"musl/src/thread/pthread_attr_setstack.c", -"musl/src/thread/pthread_attr_setstacksize.c", -"musl/src/thread/pthread_barrier_destroy.c", -"musl/src/thread/pthread_barrier_init.c", -"musl/src/thread/pthread_barrier_wait.c", -"musl/src/thread/pthread_barrierattr_destroy.c", -"musl/src/thread/pthread_barrierattr_init.c", -"musl/src/thread/pthread_barrierattr_setpshared.c", -"musl/src/thread/pthread_cancel.c", -"musl/src/thread/pthread_cleanup_push.c", -"musl/src/thread/pthread_cond_broadcast.c", -"musl/src/thread/pthread_cond_destroy.c", -"musl/src/thread/pthread_cond_init.c", -"musl/src/thread/pthread_cond_signal.c", -"musl/src/thread/pthread_cond_timedwait.c", -"musl/src/thread/pthread_cond_wait.c", -"musl/src/thread/pthread_condattr_destroy.c", -"musl/src/thread/pthread_condattr_init.c", -"musl/src/thread/pthread_condattr_setclock.c", -"musl/src/thread/pthread_condattr_setpshared.c", -"musl/src/thread/pthread_create.c", -"musl/src/thread/pthread_detach.c", -"musl/src/thread/pthread_equal.c", -"musl/src/thread/pthread_getattr_np.c", -"musl/src/thread/pthread_getconcurrency.c", -"musl/src/thread/pthread_getcpuclockid.c", -"musl/src/thread/pthread_getschedparam.c", -"musl/src/thread/pthread_getspecific.c", -"musl/src/thread/pthread_join.c", -"musl/src/thread/pthread_key_create.c", -"musl/src/thread/pthread_kill.c", -"musl/src/thread/pthread_mutex_consistent.c", -"musl/src/thread/pthread_mutex_destroy.c", -"musl/src/thread/pthread_mutex_getprioceiling.c", -"musl/src/thread/pthread_mutex_init.c", -"musl/src/thread/pthread_mutex_lock.c", -"musl/src/thread/pthread_mutex_setprioceiling.c", -"musl/src/thread/pthread_mutex_timedlock.c", -"musl/src/thread/pthread_mutex_trylock.c", -"musl/src/thread/pthread_mutex_unlock.c", -"musl/src/thread/pthread_mutexattr_destroy.c", -"musl/src/thread/pthread_mutexattr_init.c", -"musl/src/thread/pthread_mutexattr_setprotocol.c", -"musl/src/thread/pthread_mutexattr_setpshared.c", -"musl/src/thread/pthread_mutexattr_setrobust.c", -"musl/src/thread/pthread_mutexattr_settype.c", -"musl/src/thread/pthread_once.c", -"musl/src/thread/pthread_rwlock_destroy.c", -"musl/src/thread/pthread_rwlock_init.c", -"musl/src/thread/pthread_rwlock_rdlock.c", -"musl/src/thread/pthread_rwlock_timedrdlock.c", -"musl/src/thread/pthread_rwlock_timedwrlock.c", -"musl/src/thread/pthread_rwlock_tryrdlock.c", -"musl/src/thread/pthread_rwlock_trywrlock.c", -"musl/src/thread/pthread_rwlock_unlock.c", -"musl/src/thread/pthread_rwlock_wrlock.c", -"musl/src/thread/pthread_rwlockattr_destroy.c", -"musl/src/thread/pthread_rwlockattr_init.c", -"musl/src/thread/pthread_rwlockattr_setpshared.c", -"musl/src/thread/pthread_self.c", -"musl/src/thread/pthread_setattr_default_np.c", -"musl/src/thread/pthread_setcancelstate.c", -"musl/src/thread/pthread_setcanceltype.c", -"musl/src/thread/pthread_setconcurrency.c", -"musl/src/thread/pthread_setname_np.c", -"musl/src/thread/pthread_setschedparam.c", -"musl/src/thread/pthread_setschedprio.c", -"musl/src/thread/pthread_setspecific.c", -"musl/src/thread/pthread_sigmask.c", -"musl/src/thread/pthread_spin_destroy.c", -"musl/src/thread/pthread_spin_init.c", -"musl/src/thread/pthread_spin_lock.c", -"musl/src/thread/pthread_spin_trylock.c", -"musl/src/thread/pthread_spin_unlock.c", -"musl/src/thread/pthread_testcancel.c", -"musl/src/thread/riscv64/__set_thread_area.s", -"musl/src/thread/riscv64/__unmapself.s", -"musl/src/thread/riscv64/clone.s", -"musl/src/thread/riscv64/syscall_cp.s", -"musl/src/thread/s390x/__set_thread_area.s", -"musl/src/thread/s390x/__tls_get_offset.s", -"musl/src/thread/s390x/__unmapself.s", -"musl/src/thread/s390x/clone.s", -"musl/src/thread/s390x/syscall_cp.s", -"musl/src/thread/sem_destroy.c", -"musl/src/thread/sem_getvalue.c", -"musl/src/thread/sem_init.c", -"musl/src/thread/sem_open.c", -"musl/src/thread/sem_post.c", -"musl/src/thread/sem_timedwait.c", -"musl/src/thread/sem_trywait.c", -"musl/src/thread/sem_unlink.c", -"musl/src/thread/sem_wait.c", -"musl/src/thread/sh/__set_thread_area.c", -"musl/src/thread/sh/__unmapself.c", -"musl/src/thread/sh/__unmapself_mmu.s", -"musl/src/thread/sh/atomics.s", -"musl/src/thread/sh/clone.s", -"musl/src/thread/sh/syscall_cp.s", -"musl/src/thread/synccall.c", -"musl/src/thread/syscall_cp.c", -"musl/src/thread/thrd_create.c", -"musl/src/thread/thrd_exit.c", -"musl/src/thread/thrd_join.c", -"musl/src/thread/thrd_sleep.c", -"musl/src/thread/thrd_yield.c", -"musl/src/thread/tls.c", -"musl/src/thread/tss_create.c", -"musl/src/thread/tss_delete.c", -"musl/src/thread/tss_set.c", -"musl/src/thread/vmlock.c", -"musl/src/thread/x32/__set_thread_area.s", -"musl/src/thread/x32/__unmapself.s", -"musl/src/thread/x32/clone.s", -"musl/src/thread/x32/syscall_cp.s", -"musl/src/thread/x86_64/__set_thread_area.s", -"musl/src/thread/x86_64/__unmapself.s", -"musl/src/thread/x86_64/clone.s", -"musl/src/thread/x86_64/syscall_cp.s", -"musl/src/time/__map_file.c", -"musl/src/time/__month_to_secs.c", -"musl/src/time/__secs_to_tm.c", -"musl/src/time/__tm_to_secs.c", -"musl/src/time/__tz.c", -"musl/src/time/__year_to_secs.c", -"musl/src/time/asctime.c", -"musl/src/time/asctime_r.c", -"musl/src/time/clock.c", -"musl/src/time/clock_getcpuclockid.c", -"musl/src/time/clock_getres.c", -"musl/src/time/clock_gettime.c", -"musl/src/time/clock_nanosleep.c", -"musl/src/time/clock_settime.c", -"musl/src/time/ctime.c", -"musl/src/time/ctime_r.c", -"musl/src/time/difftime.c", -"musl/src/time/ftime.c", -"musl/src/time/getdate.c", -"musl/src/time/gettimeofday.c", -"musl/src/time/gmtime.c", -"musl/src/time/gmtime_r.c", -"musl/src/time/localtime.c", -"musl/src/time/localtime_r.c", -"musl/src/time/mktime.c", -"musl/src/time/nanosleep.c", -"musl/src/time/strftime.c", -"musl/src/time/strptime.c", -"musl/src/time/time.c", -"musl/src/time/timegm.c", -"musl/src/time/timer_create.c", -"musl/src/time/timer_delete.c", -"musl/src/time/timer_getoverrun.c", -"musl/src/time/timer_gettime.c", -"musl/src/time/timer_settime.c", -"musl/src/time/times.c", -"musl/src/time/timespec_get.c", -"musl/src/time/utime.c", -"musl/src/time/wcsftime.c", -"musl/src/unistd/_exit.c", -"musl/src/unistd/access.c", -"musl/src/unistd/acct.c", -"musl/src/unistd/alarm.c", -"musl/src/unistd/chdir.c", -"musl/src/unistd/chown.c", -"musl/src/unistd/close.c", -"musl/src/unistd/ctermid.c", -"musl/src/unistd/dup.c", -"musl/src/unistd/dup2.c", -"musl/src/unistd/dup3.c", -"musl/src/unistd/faccessat.c", -"musl/src/unistd/fchdir.c", -"musl/src/unistd/fchown.c", -"musl/src/unistd/fchownat.c", -"musl/src/unistd/fdatasync.c", -"musl/src/unistd/fsync.c", -"musl/src/unistd/ftruncate.c", -"musl/src/unistd/getcwd.c", -"musl/src/unistd/getegid.c", -"musl/src/unistd/geteuid.c", -"musl/src/unistd/getgid.c", -"musl/src/unistd/getgroups.c", -"musl/src/unistd/gethostname.c", -"musl/src/unistd/getlogin.c", -"musl/src/unistd/getlogin_r.c", -"musl/src/unistd/getpgid.c", -"musl/src/unistd/getpgrp.c", -"musl/src/unistd/getpid.c", -"musl/src/unistd/getppid.c", -"musl/src/unistd/getsid.c", -"musl/src/unistd/getuid.c", -"musl/src/unistd/isatty.c", -"musl/src/unistd/lchown.c", -"musl/src/unistd/link.c", -"musl/src/unistd/linkat.c", -"musl/src/unistd/lseek.c", -"musl/src/unistd/mips/pipe.s", -"musl/src/unistd/mips64/pipe.s", -"musl/src/unistd/mipsn32/lseek.c", -"musl/src/unistd/mipsn32/pipe.s", -"musl/src/unistd/nice.c", -"musl/src/unistd/pause.c", -"musl/src/unistd/pipe.c", -"musl/src/unistd/pipe2.c", -"musl/src/unistd/posix_close.c", -"musl/src/unistd/pread.c", -"musl/src/unistd/preadv.c", -"musl/src/unistd/pwrite.c", -"musl/src/unistd/pwritev.c", -"musl/src/unistd/read.c", -"musl/src/unistd/readlink.c", -"musl/src/unistd/readlinkat.c", -"musl/src/unistd/readv.c", -"musl/src/unistd/renameat.c", -"musl/src/unistd/rmdir.c", -"musl/src/unistd/setegid.c", -"musl/src/unistd/seteuid.c", -"musl/src/unistd/setgid.c", -"musl/src/unistd/setpgid.c", -"musl/src/unistd/setpgrp.c", -"musl/src/unistd/setregid.c", -"musl/src/unistd/setresgid.c", -"musl/src/unistd/setresuid.c", -"musl/src/unistd/setreuid.c", -"musl/src/unistd/setsid.c", -"musl/src/unistd/setuid.c", -"musl/src/unistd/setxid.c", -"musl/src/unistd/sh/pipe.s", -"musl/src/unistd/sleep.c", -"musl/src/unistd/symlink.c", -"musl/src/unistd/symlinkat.c", -"musl/src/unistd/sync.c", -"musl/src/unistd/tcgetpgrp.c", -"musl/src/unistd/tcsetpgrp.c", -"musl/src/unistd/truncate.c", -"musl/src/unistd/ttyname.c", -"musl/src/unistd/ttyname_r.c", -"musl/src/unistd/ualarm.c", -"musl/src/unistd/unlink.c", -"musl/src/unistd/unlinkat.c", -"musl/src/unistd/usleep.c", -"musl/src/unistd/write.c", -"musl/src/unistd/writev.c", -"musl/src/unistd/x32/lseek.c", -}; -static const char *ZIG_MUSL_COMPAT_TIME32_FILES[] = { -"musl/compat/time32/__xstat.c", -"musl/compat/time32/adjtime32.c", -"musl/compat/time32/adjtimex_time32.c", -"musl/compat/time32/aio_suspend_time32.c", -"musl/compat/time32/clock_adjtime32.c", -"musl/compat/time32/clock_getres_time32.c", -"musl/compat/time32/clock_gettime32.c", -"musl/compat/time32/clock_nanosleep_time32.c", -"musl/compat/time32/clock_settime32.c", -"musl/compat/time32/cnd_timedwait_time32.c", -"musl/compat/time32/ctime32.c", -"musl/compat/time32/ctime32_r.c", -"musl/compat/time32/difftime32.c", -"musl/compat/time32/fstat_time32.c", -"musl/compat/time32/fstatat_time32.c", -"musl/compat/time32/ftime32.c", -"musl/compat/time32/futimens_time32.c", -"musl/compat/time32/futimes_time32.c", -"musl/compat/time32/futimesat_time32.c", -"musl/compat/time32/getitimer_time32.c", -"musl/compat/time32/getrusage_time32.c", -"musl/compat/time32/gettimeofday_time32.c", -"musl/compat/time32/gmtime32.c", -"musl/compat/time32/gmtime32_r.c", -"musl/compat/time32/localtime32.c", -"musl/compat/time32/localtime32_r.c", -"musl/compat/time32/lstat_time32.c", -"musl/compat/time32/lutimes_time32.c", -"musl/compat/time32/mktime32.c", -"musl/compat/time32/mq_timedreceive_time32.c", -"musl/compat/time32/mq_timedsend_time32.c", -"musl/compat/time32/mtx_timedlock_time32.c", -"musl/compat/time32/nanosleep_time32.c", -"musl/compat/time32/ppoll_time32.c", -"musl/compat/time32/pselect_time32.c", -"musl/compat/time32/pthread_cond_timedwait_time32.c", -"musl/compat/time32/pthread_mutex_timedlock_time32.c", -"musl/compat/time32/pthread_rwlock_timedrdlock_time32.c", -"musl/compat/time32/pthread_rwlock_timedwrlock_time32.c", -"musl/compat/time32/pthread_timedjoin_np_time32.c", -"musl/compat/time32/recvmmsg_time32.c", -"musl/compat/time32/sched_rr_get_interval_time32.c", -"musl/compat/time32/select_time32.c", -"musl/compat/time32/sem_timedwait_time32.c", -"musl/compat/time32/semtimedop_time32.c", -"musl/compat/time32/setitimer_time32.c", -"musl/compat/time32/settimeofday_time32.c", -"musl/compat/time32/sigtimedwait_time32.c", -"musl/compat/time32/stat_time32.c", -"musl/compat/time32/stime32.c", -"musl/compat/time32/thrd_sleep_time32.c", -"musl/compat/time32/time32.c", -"musl/compat/time32/time32gm.c", -"musl/compat/time32/timer_gettime32.c", -"musl/compat/time32/timer_settime32.c", -"musl/compat/time32/timerfd_gettime32.c", -"musl/compat/time32/timerfd_settime32.c", -"musl/compat/time32/timespec_get_time32.c", -"musl/compat/time32/utime_time32.c", -"musl/compat/time32/utimensat_time32.c", -"musl/compat/time32/utimes_time32.c", -"musl/compat/time32/wait3_time32.c", -"musl/compat/time32/wait4_time32.c", -}; -static const char *ZIG_LIBCXXABI_FILES[] = { -"src/abort_message.cpp", -"src/cxa_aux_runtime.cpp", -"src/cxa_default_handlers.cpp", -"src/cxa_demangle.cpp", -"src/cxa_exception.cpp", -"src/cxa_exception_storage.cpp", -"src/cxa_guard.cpp", -"src/cxa_handlers.cpp", -"src/cxa_noexception.cpp", -"src/cxa_personality.cpp", -"src/cxa_thread_atexit.cpp", -"src/cxa_unexpected.cpp", -"src/cxa_vector.cpp", -"src/cxa_virtual.cpp", -"src/fallback_malloc.cpp", -"src/private_typeinfo.cpp", -"src/stdlib_exception.cpp", -"src/stdlib_stdexcept.cpp", -"src/stdlib_typeinfo.cpp", -}; -static const char *ZIG_LIBCXX_FILES[] = { -"src/algorithm.cpp", -"src/any.cpp", -"src/bind.cpp", -"src/charconv.cpp", -"src/chrono.cpp", -"src/condition_variable.cpp", -"src/condition_variable_destructor.cpp", -"src/debug.cpp", -"src/exception.cpp", -"src/experimental/memory_resource.cpp", -"src/filesystem/directory_iterator.cpp", -"src/filesystem/operations.cpp", -"src/functional.cpp", -"src/future.cpp", -"src/hash.cpp", -"src/ios.cpp", -"src/iostream.cpp", -"src/locale.cpp", -"src/memory.cpp", -"src/mutex.cpp", -"src/mutex_destructor.cpp", -"src/new.cpp", -"src/optional.cpp", -"src/random.cpp", -"src/regex.cpp", -"src/shared_mutex.cpp", -"src/stdexcept.cpp", -"src/string.cpp", -"src/strstream.cpp", -"src/support/solaris/xlocale.cpp", -"src/support/win32/locale_win32.cpp", -"src/support/win32/support.cpp", -"src/support/win32/thread_win32.cpp", -"src/system_error.cpp", -"src/thread.cpp", -"src/typeinfo.cpp", -"src/utility.cpp", -"src/valarray.cpp", -"src/variant.cpp", -"src/vector.cpp", -}; -#endif diff --git a/src/introspect.zig b/src/introspect.zig new file mode 100644 index 0000000000..b75bf8f4b8 --- /dev/null +++ b/src/introspect.zig @@ -0,0 +1,75 @@ +const std = @import("std"); +const mem = std.mem; +const fs = std.fs; +const Compilation = @import("Compilation.zig"); + +/// Returns the sub_path that worked, or `null` if none did. +/// The path of the returned Directory is relative to `base`. +/// The handle of the returned Directory is open. +fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory { + const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig"; + + zig_dir: { + // Try lib/zig/std/std.zig + const lib_zig = "lib" ++ fs.path.sep_str ++ "zig"; + var test_zig_dir = base_dir.openDir(lib_zig, .{}) catch break :zig_dir; + const file = test_zig_dir.openFile(test_index_file, .{}) catch { + test_zig_dir.close(); + break :zig_dir; + }; + file.close(); + return Compilation.Directory{ .handle = test_zig_dir, .path = lib_zig }; + } + + // Try lib/std/std.zig + var test_zig_dir = base_dir.openDir("lib", .{}) catch return null; + const file = test_zig_dir.openFile(test_index_file, .{}) catch { + test_zig_dir.close(); + return null; + }; + file.close(); + return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" }; +} + +/// Both the directory handle and the path are newly allocated resources which the caller now owns. +pub fn findZigLibDir(gpa: *mem.Allocator) !Compilation.Directory { + const self_exe_path = try fs.selfExePathAlloc(gpa); + defer gpa.free(self_exe_path); + + return findZigLibDirFromSelfExe(gpa, self_exe_path); +} + +/// Both the directory handle and the path are newly allocated resources which the caller now owns. +pub fn findZigLibDirFromSelfExe( + allocator: *mem.Allocator, + self_exe_path: []const u8, +) error{ OutOfMemory, FileNotFound }!Compilation.Directory { + const cwd = fs.cwd(); + var cur_path: []const u8 = self_exe_path; + while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { + var base_dir = cwd.openDir(dirname, .{}) catch continue; + defer base_dir.close(); + + const sub_directory = testZigInstallPrefix(base_dir) orelse continue; + return Compilation.Directory{ + .handle = sub_directory.handle, + .path = try fs.path.join(allocator, &[_][]const u8{ dirname, sub_directory.path.? }), + }; + } + return error.FileNotFound; +} + +/// Caller owns returned memory. +pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 { + const appname = "zig"; + + if (std.Target.current.os.tag != .windows) { + if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| { + return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); + } else if (std.os.getenv("HOME")) |home| { + return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); + } + } + + return fs.getAppDataDir(allocator, appname); +} diff --git a/src-self-hosted/ir.zig b/src/ir.zig similarity index 100% rename from src-self-hosted/ir.zig rename to src/ir.zig diff --git a/src-self-hosted/libc_installation.zig b/src/libc_installation.zig similarity index 99% rename from src-self-hosted/libc_installation.zig rename to src/libc_installation.zig index fa2ef30ccd..535892ce74 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src/libc_installation.zig @@ -4,6 +4,7 @@ const Target = std.Target; const fs = std.fs; const Allocator = std.mem.Allocator; const Batch = std.event.Batch; +const build_options = @import("build_options"); const is_darwin = Target.current.isDarwin(); const is_windows = Target.current.os.tag == .windows; @@ -13,6 +14,8 @@ const log = std.log.scoped(.libc_installation); usingnamespace @import("windows_sdk.zig"); +// TODO https://github.com/ziglang/zig/issues/6345 + /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { include_dir: ?[]const u8 = null, @@ -168,6 +171,8 @@ pub const LibCInstallation = struct { var self: LibCInstallation = .{}; if (is_windows) { + if (!build_options.have_llvm) + return error.WindowsSdkNotFound; var sdk: *ZigWindowsSDK = undefined; switch (zig_find_windows_sdk(&sdk)) { .None => { diff --git a/src/libcxx.zig b/src/libcxx.zig new file mode 100644 index 0000000000..19987082aa --- /dev/null +++ b/src/libcxx.zig @@ -0,0 +1,315 @@ +const std = @import("std"); +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; + +const libcxxabi_files = [_][]const u8{ + "src/abort_message.cpp", + "src/cxa_aux_runtime.cpp", + "src/cxa_default_handlers.cpp", + "src/cxa_demangle.cpp", + "src/cxa_exception.cpp", + "src/cxa_exception_storage.cpp", + "src/cxa_guard.cpp", + "src/cxa_handlers.cpp", + "src/cxa_noexception.cpp", + "src/cxa_personality.cpp", + "src/cxa_thread_atexit.cpp", + "src/cxa_unexpected.cpp", + "src/cxa_vector.cpp", + "src/cxa_virtual.cpp", + "src/fallback_malloc.cpp", + "src/private_typeinfo.cpp", + "src/stdlib_exception.cpp", + "src/stdlib_stdexcept.cpp", + "src/stdlib_typeinfo.cpp", +}; + +const libcxx_files = [_][]const u8{ + "src/algorithm.cpp", + "src/any.cpp", + "src/bind.cpp", + "src/charconv.cpp", + "src/chrono.cpp", + "src/condition_variable.cpp", + "src/condition_variable_destructor.cpp", + "src/debug.cpp", + "src/exception.cpp", + "src/experimental/memory_resource.cpp", + "src/filesystem/directory_iterator.cpp", + "src/filesystem/operations.cpp", + "src/functional.cpp", + "src/future.cpp", + "src/hash.cpp", + "src/ios.cpp", + "src/iostream.cpp", + "src/locale.cpp", + "src/memory.cpp", + "src/mutex.cpp", + "src/mutex_destructor.cpp", + "src/new.cpp", + "src/optional.cpp", + "src/random.cpp", + "src/regex.cpp", + "src/shared_mutex.cpp", + "src/stdexcept.cpp", + "src/string.cpp", + "src/strstream.cpp", + "src/support/solaris/xlocale.cpp", + "src/support/win32/locale_win32.cpp", + "src/support/win32/support.cpp", + "src/support/win32/thread_win32.cpp", + "src/system_error.cpp", + "src/thread.cpp", + "src/typeinfo.cpp", + "src/utility.cpp", + "src/valarray.cpp", + "src/variant.cpp", + "src/vector.cpp", +}; + +pub fn buildLibCXX(comp: *Compilation) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const root_name = "c++"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + .link_mode = link_mode, + }); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + + const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); + const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + try c_source_files.ensureCapacity(libcxx_files.len); + + for (libcxx_files) |cxx_src| { + var cflags = std.ArrayList([]const u8).init(arena); + + if (target.os.tag == .windows) { + // Filesystem stuff isn't supported on Windows. + if (std.mem.startsWith(u8, cxx_src, "src/filesystem/")) + continue; + } else { + if (std.mem.startsWith(u8, cxx_src, "src/support/win32/")) + continue; + } + + try cflags.append("-DNDEBUG"); + try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); + try cflags.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); + try cflags.append("-DLIBCXX_BUILDING_LIBCXXABI"); + try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target.abi.isMusl()) { + try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + + try cflags.append("-I"); + try cflags.append(cxx_include_path); + + try cflags.append("-I"); + try cflags.append(cxxabi_include_path); + + try cflags.append("-O3"); + try cflags.append("-DNDEBUG"); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-nostdinc++"); + try cflags.append("-fvisibility-inlines-hidden"); + try cflags.append("-std=c++14"); + try cflags.append("-Wno-user-defined-literals"); + + c_source_files.appendAssumeCapacity(.{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), + .extra_flags = cflags.items, + }); + } + + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = c_source_files.items, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libcxx_static_lib == null); + comp.libcxx_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( + comp.gpa, + &[_][]const u8{basename}, + ), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} + +pub fn buildLibCXXABI(comp: *Compilation) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const root_name = "c++abi"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + .link_mode = link_mode, + }); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + + const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); + const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); + + var c_source_files: [libcxxabi_files.len]Compilation.CSourceFile = undefined; + for (libcxxabi_files) |cxxabi_src, i| { + var cflags = std.ArrayList([]const u8).init(arena); + + try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); + try cflags.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE"); + try cflags.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); + try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY"); + try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target.abi.isMusl()) { + try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + + try cflags.append("-I"); + try cflags.append(cxxabi_include_path); + + try cflags.append("-I"); + try cflags.append(cxx_include_path); + + try cflags.append("-O3"); + try cflags.append("-DNDEBUG"); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-nostdinc++"); + try cflags.append("-fstrict-aliasing"); + try cflags.append("-funwind-tables"); + try cflags.append("-D_DEBUG"); + try cflags.append("-UNDEBUG"); + try cflags.append("-std=c++11"); + + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), + .extra_flags = cflags.items, + }; + } + + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = &c_source_files, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libcxxabi_static_lib == null); + comp.libcxxabi_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( + comp.gpa, + &[_][]const u8{basename}, + ), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} diff --git a/src/libunwind.zig b/src/libunwind.zig new file mode 100644 index 0000000000..d47eed40dd --- /dev/null +++ b/src/libunwind.zig @@ -0,0 +1,135 @@ +const std = @import("std"); +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; + +pub fn buildStaticLib(comp: *Compilation) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const root_name = "unwind"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + .link_mode = link_mode, + }); + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + const unwind_src_list = [_][]const u8{ + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S", + }; + var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined; + for (unwind_src_list) |unwind_src, i| { + var cflags = std.ArrayList([]const u8).init(arena); + + switch (Compilation.classifyFileExt(unwind_src)) { + .c => { + try cflags.append("-std=c99"); + }, + .cpp => { + try cflags.appendSlice(&[_][]const u8{ + "-fno-rtti", + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }), + }); + }, + .assembly => {}, + else => unreachable, // You can see the entire list of files just above. + } + try cflags.append("-I"); + try cflags.append(try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libunwind", "include" })); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-Wa,--noexecstack"); + + // This is intentionally always defined because the macro definition means, should it only + // build for the target specified by compiler defines. Since we pass -target the compiler + // defines will be correct. + try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); + + if (comp.bin_file.options.optimize_mode == .Debug) { + try cflags.append("-D_DEBUG"); + } + if (comp.bin_file.options.single_threaded) { + try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS"); + } + try cflags.append("-Wno-bitwise-conditional-parentheses"); + + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}), + .extra_flags = cflags.items, + }; + } + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = &c_source_files, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libunwind_static_lib == null); + comp.libunwind_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( + comp.gpa, + &[_][]const u8{basename}, + ), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} diff --git a/src/link.cpp b/src/link.cpp deleted file mode 100644 index b54fdf6b93..0000000000 --- a/src/link.cpp +++ /dev/null @@ -1,2984 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "os.hpp" -#include "config.h" -#include "codegen.hpp" -#include "analyze.hpp" -#include "compiler.hpp" -#include "install_files.h" -#include "glibc.hpp" - -static const char *msvcrt_common_src[] = { - "misc" OS_SEP "_create_locale.c", - "misc" OS_SEP "_free_locale.c", - "misc" OS_SEP "onexit_table.c", - "misc" OS_SEP "register_tls_atexit.c", - "stdio" OS_SEP "acrt_iob_func.c", - "misc" OS_SEP "_configthreadlocale.c", - "misc" OS_SEP "_get_current_locale.c", - "misc" OS_SEP "invalid_parameter_handler.c", - "misc" OS_SEP "output_format.c", - "misc" OS_SEP "purecall.c", - "secapi" OS_SEP "_access_s.c", - "secapi" OS_SEP "_cgets_s.c", - "secapi" OS_SEP "_cgetws_s.c", - "secapi" OS_SEP "_chsize_s.c", - "secapi" OS_SEP "_controlfp_s.c", - "secapi" OS_SEP "_cprintf_s.c", - "secapi" OS_SEP "_cprintf_s_l.c", - "secapi" OS_SEP "_ctime32_s.c", - "secapi" OS_SEP "_ctime64_s.c", - "secapi" OS_SEP "_cwprintf_s.c", - "secapi" OS_SEP "_cwprintf_s_l.c", - "secapi" OS_SEP "_gmtime32_s.c", - "secapi" OS_SEP "_gmtime64_s.c", - "secapi" OS_SEP "_localtime32_s.c", - "secapi" OS_SEP "_localtime64_s.c", - "secapi" OS_SEP "_mktemp_s.c", - "secapi" OS_SEP "_sopen_s.c", - "secapi" OS_SEP "_strdate_s.c", - "secapi" OS_SEP "_strtime_s.c", - "secapi" OS_SEP "_umask_s.c", - "secapi" OS_SEP "_vcprintf_s.c", - "secapi" OS_SEP "_vcprintf_s_l.c", - "secapi" OS_SEP "_vcwprintf_s.c", - "secapi" OS_SEP "_vcwprintf_s_l.c", - "secapi" OS_SEP "_vscprintf_p.c", - "secapi" OS_SEP "_vscwprintf_p.c", - "secapi" OS_SEP "_vswprintf_p.c", - "secapi" OS_SEP "_waccess_s.c", - "secapi" OS_SEP "_wasctime_s.c", - "secapi" OS_SEP "_wctime32_s.c", - "secapi" OS_SEP "_wctime64_s.c", - "secapi" OS_SEP "_wstrtime_s.c", - "secapi" OS_SEP "_wmktemp_s.c", - "secapi" OS_SEP "_wstrdate_s.c", - "secapi" OS_SEP "asctime_s.c", - "secapi" OS_SEP "memcpy_s.c", - "secapi" OS_SEP "memmove_s.c", - "secapi" OS_SEP "rand_s.c", - "secapi" OS_SEP "sprintf_s.c", - "secapi" OS_SEP "strerror_s.c", - "secapi" OS_SEP "vsprintf_s.c", - "secapi" OS_SEP "wmemcpy_s.c", - "secapi" OS_SEP "wmemmove_s.c", - "stdio" OS_SEP "mingw_lock.c", -}; - -static const char *msvcrt_i386_src[] = { - "misc" OS_SEP "lc_locale_func.c", - "misc" OS_SEP "___mb_cur_max_func.c", -}; - -static const char *msvcrt_other_src[] = { - "misc" OS_SEP "__p___argv.c", - "misc" OS_SEP "__p__acmdln.c", - "misc" OS_SEP "__p__fmode.c", - "misc" OS_SEP "__p__wcmdln.c", -}; - -static const char *mingwex_generic_src[] = { - "complex" OS_SEP "_cabs.c", - "complex" OS_SEP "cabs.c", - "complex" OS_SEP "cabsf.c", - "complex" OS_SEP "cabsl.c", - "complex" OS_SEP "cacos.c", - "complex" OS_SEP "cacosf.c", - "complex" OS_SEP "cacosl.c", - "complex" OS_SEP "carg.c", - "complex" OS_SEP "cargf.c", - "complex" OS_SEP "cargl.c", - "complex" OS_SEP "casin.c", - "complex" OS_SEP "casinf.c", - "complex" OS_SEP "casinl.c", - "complex" OS_SEP "catan.c", - "complex" OS_SEP "catanf.c", - "complex" OS_SEP "catanl.c", - "complex" OS_SEP "ccos.c", - "complex" OS_SEP "ccosf.c", - "complex" OS_SEP "ccosl.c", - "complex" OS_SEP "cexp.c", - "complex" OS_SEP "cexpf.c", - "complex" OS_SEP "cexpl.c", - "complex" OS_SEP "cimag.c", - "complex" OS_SEP "cimagf.c", - "complex" OS_SEP "cimagl.c", - "complex" OS_SEP "clog.c", - "complex" OS_SEP "clog10.c", - "complex" OS_SEP "clog10f.c", - "complex" OS_SEP "clog10l.c", - "complex" OS_SEP "clogf.c", - "complex" OS_SEP "clogl.c", - "complex" OS_SEP "conj.c", - "complex" OS_SEP "conjf.c", - "complex" OS_SEP "conjl.c", - "complex" OS_SEP "cpow.c", - "complex" OS_SEP "cpowf.c", - "complex" OS_SEP "cpowl.c", - "complex" OS_SEP "cproj.c", - "complex" OS_SEP "cprojf.c", - "complex" OS_SEP "cprojl.c", - "complex" OS_SEP "creal.c", - "complex" OS_SEP "crealf.c", - "complex" OS_SEP "creall.c", - "complex" OS_SEP "csin.c", - "complex" OS_SEP "csinf.c", - "complex" OS_SEP "csinl.c", - "complex" OS_SEP "csqrt.c", - "complex" OS_SEP "csqrtf.c", - "complex" OS_SEP "csqrtl.c", - "complex" OS_SEP "ctan.c", - "complex" OS_SEP "ctanf.c", - "complex" OS_SEP "ctanl.c", - "crt" OS_SEP "dllentry.c", - "crt" OS_SEP "dllmain.c", - "gdtoa" OS_SEP "arithchk.c", - "gdtoa" OS_SEP "dmisc.c", - "gdtoa" OS_SEP "dtoa.c", - "gdtoa" OS_SEP "g__fmt.c", - "gdtoa" OS_SEP "g_dfmt.c", - "gdtoa" OS_SEP "g_ffmt.c", - "gdtoa" OS_SEP "g_xfmt.c", - "gdtoa" OS_SEP "gdtoa.c", - "gdtoa" OS_SEP "gethex.c", - "gdtoa" OS_SEP "gmisc.c", - "gdtoa" OS_SEP "hd_init.c", - "gdtoa" OS_SEP "hexnan.c", - "gdtoa" OS_SEP "misc.c", - "gdtoa" OS_SEP "qnan.c", - "gdtoa" OS_SEP "smisc.c", - "gdtoa" OS_SEP "strtodg.c", - "gdtoa" OS_SEP "strtodnrp.c", - "gdtoa" OS_SEP "strtof.c", - "gdtoa" OS_SEP "strtopx.c", - "gdtoa" OS_SEP "sum.c", - "gdtoa" OS_SEP "ulp.c", - "math" OS_SEP "abs64.c", - "math" OS_SEP "cbrt.c", - "math" OS_SEP "cbrtf.c", - "math" OS_SEP "cbrtl.c", - "math" OS_SEP "cephes_emath.c", - "math" OS_SEP "copysign.c", - "math" OS_SEP "copysignf.c", - "math" OS_SEP "coshf.c", - "math" OS_SEP "coshl.c", - "math" OS_SEP "erfl.c", - "math" OS_SEP "expf.c", - "math" OS_SEP "fabs.c", - "math" OS_SEP "fabsf.c", - "math" OS_SEP "fabsl.c", - "math" OS_SEP "fdim.c", - "math" OS_SEP "fdimf.c", - "math" OS_SEP "fdiml.c", - "math" OS_SEP "fma.c", - "math" OS_SEP "fmaf.c", - "math" OS_SEP "fmal.c", - "math" OS_SEP "fmax.c", - "math" OS_SEP "fmaxf.c", - "math" OS_SEP "fmaxl.c", - "math" OS_SEP "fmin.c", - "math" OS_SEP "fminf.c", - "math" OS_SEP "fminl.c", - "math" OS_SEP "fp_consts.c", - "math" OS_SEP "fp_constsf.c", - "math" OS_SEP "fp_constsl.c", - "math" OS_SEP "fpclassify.c", - "math" OS_SEP "fpclassifyf.c", - "math" OS_SEP "fpclassifyl.c", - "math" OS_SEP "frexpf.c", - "math" OS_SEP "hypot.c", - "math" OS_SEP "hypotf.c", - "math" OS_SEP "hypotl.c", - "math" OS_SEP "isnan.c", - "math" OS_SEP "isnanf.c", - "math" OS_SEP "isnanl.c", - "math" OS_SEP "ldexpf.c", - "math" OS_SEP "lgamma.c", - "math" OS_SEP "lgammaf.c", - "math" OS_SEP "lgammal.c", - "math" OS_SEP "llrint.c", - "math" OS_SEP "llrintf.c", - "math" OS_SEP "llrintl.c", - "math" OS_SEP "llround.c", - "math" OS_SEP "llroundf.c", - "math" OS_SEP "llroundl.c", - "math" OS_SEP "log10f.c", - "math" OS_SEP "logf.c", - "math" OS_SEP "lrint.c", - "math" OS_SEP "lrintf.c", - "math" OS_SEP "lrintl.c", - "math" OS_SEP "lround.c", - "math" OS_SEP "lroundf.c", - "math" OS_SEP "lroundl.c", - "math" OS_SEP "modf.c", - "math" OS_SEP "modff.c", - "math" OS_SEP "modfl.c", - "math" OS_SEP "nextafterf.c", - "math" OS_SEP "nextafterl.c", - "math" OS_SEP "nexttoward.c", - "math" OS_SEP "nexttowardf.c", - "math" OS_SEP "powf.c", - "math" OS_SEP "powi.c", - "math" OS_SEP "powif.c", - "math" OS_SEP "powil.c", - "math" OS_SEP "rint.c", - "math" OS_SEP "rintf.c", - "math" OS_SEP "rintl.c", - "math" OS_SEP "round.c", - "math" OS_SEP "roundf.c", - "math" OS_SEP "roundl.c", - "math" OS_SEP "s_erf.c", - "math" OS_SEP "sf_erf.c", - "math" OS_SEP "signbit.c", - "math" OS_SEP "signbitf.c", - "math" OS_SEP "signbitl.c", - "math" OS_SEP "signgam.c", - "math" OS_SEP "sinhf.c", - "math" OS_SEP "sinhl.c", - "math" OS_SEP "sqrt.c", - "math" OS_SEP "sqrtf.c", - "math" OS_SEP "sqrtl.c", - "math" OS_SEP "tanhf.c", - "math" OS_SEP "tanhl.c", - "math" OS_SEP "tgamma.c", - "math" OS_SEP "tgammaf.c", - "math" OS_SEP "tgammal.c", - "math" OS_SEP "truncl.c", - "misc" OS_SEP "alarm.c", - "misc" OS_SEP "basename.c", - "misc" OS_SEP "btowc.c", - "misc" OS_SEP "delay-f.c", - "misc" OS_SEP "delay-n.c", - "misc" OS_SEP "delayimp.c", - "misc" OS_SEP "dirent.c", - "misc" OS_SEP "dirname.c", - "misc" OS_SEP "feclearexcept.c", - "misc" OS_SEP "fegetenv.c", - "misc" OS_SEP "fegetexceptflag.c", - "misc" OS_SEP "fegetround.c", - "misc" OS_SEP "feholdexcept.c", - "misc" OS_SEP "feraiseexcept.c", - "misc" OS_SEP "fesetenv.c", - "misc" OS_SEP "fesetexceptflag.c", - "misc" OS_SEP "fesetround.c", - "misc" OS_SEP "fetestexcept.c", - "misc" OS_SEP "feupdateenv.c", - "misc" OS_SEP "ftruncate.c", - "misc" OS_SEP "ftw.c", - "misc" OS_SEP "ftw64.c", - "misc" OS_SEP "fwide.c", - "misc" OS_SEP "getlogin.c", - "misc" OS_SEP "getopt.c", - "misc" OS_SEP "gettimeofday.c", - "misc" OS_SEP "imaxabs.c", - "misc" OS_SEP "imaxdiv.c", - "misc" OS_SEP "isblank.c", - "misc" OS_SEP "iswblank.c", - "misc" OS_SEP "mbrtowc.c", - "misc" OS_SEP "mbsinit.c", - "misc" OS_SEP "mempcpy.c", - "misc" OS_SEP "mingw-aligned-malloc.c", - "misc" OS_SEP "mingw-fseek.c", - "misc" OS_SEP "mingw_getsp.S", - "misc" OS_SEP "mingw_matherr.c", - "misc" OS_SEP "mingw_mbwc_convert.c", - "misc" OS_SEP "mingw_usleep.c", - "misc" OS_SEP "mingw_wcstod.c", - "misc" OS_SEP "mingw_wcstof.c", - "misc" OS_SEP "mingw_wcstold.c", - "misc" OS_SEP "mkstemp.c", - "misc" OS_SEP "seterrno.c", - "misc" OS_SEP "sleep.c", - "misc" OS_SEP "strnlen.c", - "misc" OS_SEP "strsafe.c", - "misc" OS_SEP "strtoimax.c", - "misc" OS_SEP "strtold.c", - "misc" OS_SEP "strtoumax.c", - "misc" OS_SEP "tdelete.c", - "misc" OS_SEP "tfind.c", - "misc" OS_SEP "tsearch.c", - "misc" OS_SEP "twalk.c", - "misc" OS_SEP "uchar_c16rtomb.c", - "misc" OS_SEP "uchar_c32rtomb.c", - "misc" OS_SEP "uchar_mbrtoc16.c", - "misc" OS_SEP "uchar_mbrtoc32.c", - "misc" OS_SEP "wassert.c", - "misc" OS_SEP "wcrtomb.c", - "misc" OS_SEP "wcsnlen.c", - "misc" OS_SEP "wcstof.c", - "misc" OS_SEP "wcstoimax.c", - "misc" OS_SEP "wcstold.c", - "misc" OS_SEP "wcstoumax.c", - "misc" OS_SEP "wctob.c", - "misc" OS_SEP "wctrans.c", - "misc" OS_SEP "wctype.c", - "misc" OS_SEP "wdirent.c", - "misc" OS_SEP "winbs_uint64.c", - "misc" OS_SEP "winbs_ulong.c", - "misc" OS_SEP "winbs_ushort.c", - "misc" OS_SEP "wmemchr.c", - "misc" OS_SEP "wmemcmp.c", - "misc" OS_SEP "wmemcpy.c", - "misc" OS_SEP "wmemmove.c", - "misc" OS_SEP "wmempcpy.c", - "misc" OS_SEP "wmemset.c", - "stdio" OS_SEP "_Exit.c", - "stdio" OS_SEP "_findfirst64i32.c", - "stdio" OS_SEP "_findnext64i32.c", - "stdio" OS_SEP "_fstat.c", - "stdio" OS_SEP "_fstat64i32.c", - "stdio" OS_SEP "_ftime.c", - "stdio" OS_SEP "_getc_nolock.c", - "stdio" OS_SEP "_getwc_nolock.c", - "stdio" OS_SEP "_putc_nolock.c", - "stdio" OS_SEP "_putwc_nolock.c", - "stdio" OS_SEP "_stat.c", - "stdio" OS_SEP "_stat64i32.c", - "stdio" OS_SEP "_wfindfirst64i32.c", - "stdio" OS_SEP "_wfindnext64i32.c", - "stdio" OS_SEP "_wstat.c", - "stdio" OS_SEP "_wstat64i32.c", - "stdio" OS_SEP "asprintf.c", - "stdio" OS_SEP "atoll.c", - "stdio" OS_SEP "fgetpos64.c", - "stdio" OS_SEP "fopen64.c", - "stdio" OS_SEP "fseeko32.c", - "stdio" OS_SEP "fseeko64.c", - "stdio" OS_SEP "fsetpos64.c", - "stdio" OS_SEP "ftello.c", - "stdio" OS_SEP "ftello64.c", - "stdio" OS_SEP "ftruncate64.c", - "stdio" OS_SEP "lltoa.c", - "stdio" OS_SEP "lltow.c", - "stdio" OS_SEP "lseek64.c", - "stdio" OS_SEP "mingw_asprintf.c", - "stdio" OS_SEP "mingw_fprintf.c", - "stdio" OS_SEP "mingw_fprintfw.c", - "stdio" OS_SEP "mingw_fscanf.c", - "stdio" OS_SEP "mingw_fwscanf.c", - "stdio" OS_SEP "mingw_pformat.c", - "stdio" OS_SEP "mingw_pformatw.c", - "stdio" OS_SEP "mingw_printf.c", - "stdio" OS_SEP "mingw_printfw.c", - "stdio" OS_SEP "mingw_scanf.c", - "stdio" OS_SEP "mingw_snprintf.c", - "stdio" OS_SEP "mingw_snprintfw.c", - "stdio" OS_SEP "mingw_sprintf.c", - "stdio" OS_SEP "mingw_sprintfw.c", - "stdio" OS_SEP "mingw_sscanf.c", - "stdio" OS_SEP "mingw_swscanf.c", - "stdio" OS_SEP "mingw_vasprintf.c", - "stdio" OS_SEP "mingw_vfprintf.c", - "stdio" OS_SEP "mingw_vfprintfw.c", - "stdio" OS_SEP "mingw_vfscanf.c", - "stdio" OS_SEP "mingw_vprintf.c", - "stdio" OS_SEP "mingw_vprintfw.c", - "stdio" OS_SEP "mingw_vsnprintf.c", - "stdio" OS_SEP "mingw_vsnprintfw.c", - "stdio" OS_SEP "mingw_vsprintf.c", - "stdio" OS_SEP "mingw_vsprintfw.c", - "stdio" OS_SEP "mingw_wscanf.c", - "stdio" OS_SEP "mingw_wvfscanf.c", - "stdio" OS_SEP "scanf.S", - "stdio" OS_SEP "snprintf.c", - "stdio" OS_SEP "snwprintf.c", - "stdio" OS_SEP "strtof.c", - "stdio" OS_SEP "strtok_r.c", - "stdio" OS_SEP "truncate.c", - "stdio" OS_SEP "ulltoa.c", - "stdio" OS_SEP "ulltow.c", - "stdio" OS_SEP "vasprintf.c", - "stdio" OS_SEP "vfscanf.c", - "stdio" OS_SEP "vfscanf2.S", - "stdio" OS_SEP "vfwscanf.c", - "stdio" OS_SEP "vfwscanf2.S", - "stdio" OS_SEP "vscanf.c", - "stdio" OS_SEP "vscanf2.S", - "stdio" OS_SEP "vsnprintf.c", - "stdio" OS_SEP "vsnwprintf.c", - "stdio" OS_SEP "vsscanf.c", - "stdio" OS_SEP "vsscanf2.S", - "stdio" OS_SEP "vswscanf.c", - "stdio" OS_SEP "vswscanf2.S", - "stdio" OS_SEP "vwscanf.c", - "stdio" OS_SEP "vwscanf2.S", - "stdio" OS_SEP "wtoll.c", -}; - -static const char *mingwex_x86_src[] = { - "math" OS_SEP "x86" OS_SEP "acosf.c", - "math" OS_SEP "x86" OS_SEP "acosh.c", - "math" OS_SEP "x86" OS_SEP "acoshf.c", - "math" OS_SEP "x86" OS_SEP "acoshl.c", - "math" OS_SEP "x86" OS_SEP "acosl.c", - "math" OS_SEP "x86" OS_SEP "asinf.c", - "math" OS_SEP "x86" OS_SEP "asinh.c", - "math" OS_SEP "x86" OS_SEP "asinhf.c", - "math" OS_SEP "x86" OS_SEP "asinhl.c", - "math" OS_SEP "x86" OS_SEP "asinl.c", - "math" OS_SEP "x86" OS_SEP "atan2.c", - "math" OS_SEP "x86" OS_SEP "atan2f.c", - "math" OS_SEP "x86" OS_SEP "atan2l.c", - "math" OS_SEP "x86" OS_SEP "atanf.c", - "math" OS_SEP "x86" OS_SEP "atanh.c", - "math" OS_SEP "x86" OS_SEP "atanhf.c", - "math" OS_SEP "x86" OS_SEP "atanhl.c", - "math" OS_SEP "x86" OS_SEP "atanl.c", - "math" OS_SEP "x86" OS_SEP "ceilf.S", - "math" OS_SEP "x86" OS_SEP "ceill.S", - "math" OS_SEP "x86" OS_SEP "ceil.S", - "math" OS_SEP "x86" OS_SEP "_chgsignl.S", - "math" OS_SEP "x86" OS_SEP "copysignl.S", - "math" OS_SEP "x86" OS_SEP "cos.c", - "math" OS_SEP "x86" OS_SEP "cosf.c", - "math" OS_SEP "x86" OS_SEP "cosl.c", - "math" OS_SEP "x86" OS_SEP "cosl_internal.S", - "math" OS_SEP "x86" OS_SEP "cossin.c", - "math" OS_SEP "x86" OS_SEP "exp2f.S", - "math" OS_SEP "x86" OS_SEP "exp2l.S", - "math" OS_SEP "x86" OS_SEP "exp2.S", - "math" OS_SEP "x86" OS_SEP "exp.c", - "math" OS_SEP "x86" OS_SEP "expl.c", - "math" OS_SEP "x86" OS_SEP "expm1.c", - "math" OS_SEP "x86" OS_SEP "expm1f.c", - "math" OS_SEP "x86" OS_SEP "expm1l.c", - "math" OS_SEP "x86" OS_SEP "floorf.S", - "math" OS_SEP "x86" OS_SEP "floorl.S", - "math" OS_SEP "x86" OS_SEP "floor.S", - "math" OS_SEP "x86" OS_SEP "fmod.c", - "math" OS_SEP "x86" OS_SEP "fmodf.c", - "math" OS_SEP "x86" OS_SEP "fmodl.c", - "math" OS_SEP "x86" OS_SEP "fucom.c", - "math" OS_SEP "x86" OS_SEP "ilogbf.S", - "math" OS_SEP "x86" OS_SEP "ilogbl.S", - "math" OS_SEP "x86" OS_SEP "ilogb.S", - "math" OS_SEP "x86" OS_SEP "internal_logl.S", - "math" OS_SEP "x86" OS_SEP "ldexp.c", - "math" OS_SEP "x86" OS_SEP "ldexpl.c", - "math" OS_SEP "x86" OS_SEP "log10l.S", - "math" OS_SEP "x86" OS_SEP "log1pf.S", - "math" OS_SEP "x86" OS_SEP "log1pl.S", - "math" OS_SEP "x86" OS_SEP "log1p.S", - "math" OS_SEP "x86" OS_SEP "log2f.S", - "math" OS_SEP "x86" OS_SEP "log2l.S", - "math" OS_SEP "x86" OS_SEP "log2.S", - "math" OS_SEP "x86" OS_SEP "logb.c", - "math" OS_SEP "x86" OS_SEP "logbf.c", - "math" OS_SEP "x86" OS_SEP "logbl.c", - "math" OS_SEP "x86" OS_SEP "log.c", - "math" OS_SEP "x86" OS_SEP "logl.c", - "math" OS_SEP "x86" OS_SEP "nearbyintf.S", - "math" OS_SEP "x86" OS_SEP "nearbyintl.S", - "math" OS_SEP "x86" OS_SEP "nearbyint.S", - "math" OS_SEP "x86" OS_SEP "pow.c", - "math" OS_SEP "x86" OS_SEP "powl.c", - "math" OS_SEP "x86" OS_SEP "remainderf.S", - "math" OS_SEP "x86" OS_SEP "remainderl.S", - "math" OS_SEP "x86" OS_SEP "remainder.S", - "math" OS_SEP "x86" OS_SEP "remquof.S", - "math" OS_SEP "x86" OS_SEP "remquol.S", - "math" OS_SEP "x86" OS_SEP "remquo.S", - "math" OS_SEP "x86" OS_SEP "scalbnf.S", - "math" OS_SEP "x86" OS_SEP "scalbnl.S", - "math" OS_SEP "x86" OS_SEP "scalbn.S", - "math" OS_SEP "x86" OS_SEP "sin.c", - "math" OS_SEP "x86" OS_SEP "sinf.c", - "math" OS_SEP "x86" OS_SEP "sinl.c", - "math" OS_SEP "x86" OS_SEP "sinl_internal.S", - "math" OS_SEP "x86" OS_SEP "tanf.c", - "math" OS_SEP "x86" OS_SEP "tanl.S", - "math" OS_SEP "x86" OS_SEP "truncf.S", - "math" OS_SEP "x86" OS_SEP "trunc.S", -}; - -static const char *mingwex_arm32_src[] = { - "math" OS_SEP "arm" OS_SEP "_chgsignl.S", - "math" OS_SEP "arm" OS_SEP "exp2.c", - "math" OS_SEP "arm" OS_SEP "nearbyint.S", - "math" OS_SEP "arm" OS_SEP "nearbyintf.S", - "math" OS_SEP "arm" OS_SEP "nearbyintl.S", - "math" OS_SEP "arm" OS_SEP "trunc.S", - "math" OS_SEP "arm" OS_SEP "truncf.S", -}; - -static const char *mingwex_arm64_src[] = { - "math" OS_SEP "arm64" OS_SEP "_chgsignl.S", - "math" OS_SEP "arm64" OS_SEP "exp2f.S", - "math" OS_SEP "arm64" OS_SEP "exp2.S", - "math" OS_SEP "arm64" OS_SEP "nearbyintf.S", - "math" OS_SEP "arm64" OS_SEP "nearbyintl.S", - "math" OS_SEP "arm64" OS_SEP "nearbyint.S", - "math" OS_SEP "arm64" OS_SEP "truncf.S", - "math" OS_SEP "arm64" OS_SEP "trunc.S", -}; - -static const char *mingw_uuid_src[] = { - "libsrc/ativscp-uuid.c", - "libsrc/atsmedia-uuid.c", - "libsrc/bth-uuid.c", - "libsrc/cguid-uuid.c", - "libsrc/comcat-uuid.c", - "libsrc/devguid.c", - "libsrc/docobj-uuid.c", - "libsrc/dxva-uuid.c", - "libsrc/exdisp-uuid.c", - "libsrc/extras-uuid.c", - "libsrc/fwp-uuid.c", - "libsrc/guid_nul.c", - "libsrc/hlguids-uuid.c", - "libsrc/hlink-uuid.c", - "libsrc/mlang-uuid.c", - "libsrc/msctf-uuid.c", - "libsrc/mshtmhst-uuid.c", - "libsrc/mshtml-uuid.c", - "libsrc/msxml-uuid.c", - "libsrc/netcon-uuid.c", - "libsrc/ntddkbd-uuid.c", - "libsrc/ntddmou-uuid.c", - "libsrc/ntddpar-uuid.c", - "libsrc/ntddscsi-uuid.c", - "libsrc/ntddser-uuid.c", - "libsrc/ntddstor-uuid.c", - "libsrc/ntddvdeo-uuid.c", - "libsrc/oaidl-uuid.c", - "libsrc/objidl-uuid.c", - "libsrc/objsafe-uuid.c", - "libsrc/ocidl-uuid.c", - "libsrc/oleacc-uuid.c", - "libsrc/olectlid-uuid.c", - "libsrc/oleidl-uuid.c", - "libsrc/power-uuid.c", - "libsrc/powrprof-uuid.c", - "libsrc/uianimation-uuid.c", - "libsrc/usbcamdi-uuid.c", - "libsrc/usbiodef-uuid.c", - "libsrc/uuid.c", - "libsrc/vds-uuid.c", - "libsrc/virtdisk-uuid.c", - "libsrc/wia-uuid.c", -}; - -struct MinGWDef { - const char *name; - bool always_link; -}; -static const MinGWDef mingw_def_list[] = { - {"advapi32",true}, - {"bcrypt", false}, - {"comctl32",false}, - {"comdlg32",false}, - {"crypt32", false}, - {"cryptnet",false}, - {"gdi32", false}, - {"imm32", false}, - {"kernel32",true}, - {"lz32", false}, - {"mpr", false}, - {"msvcrt", true}, - {"mswsock", false}, - {"ncrypt", false}, - {"netapi32",false}, - {"ntdll", true}, - {"ole32", false}, - {"oleaut32",false}, - {"opengl32",false}, - {"psapi", false}, - {"rpcns4", false}, - {"rpcrt4", false}, - {"scarddlg",false}, - {"setupapi",false}, - {"shell32", true}, - {"shlwapi", false}, - {"urlmon", false}, - {"user32", true}, - {"version", false}, - {"winmm", false}, - {"winscard",false}, - {"winspool",false}, - {"wintrust",false}, - {"ws2_32", false}, -}; - -struct LinkJob { - CodeGen *codegen; - ZigList args; - bool link_in_crt; - HashMap rpath_table; - Stage2ProgressNode *build_dep_prog_node; -}; - -static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file, - Stage2ProgressNode *progress_node) -{ - CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node); - child_gen->root_out_name = buf_create_from_str(name); - ZigList c_source_files = {0}; - c_source_files.append(c_file); - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static const char *path_from_zig_lib(CodeGen *g, const char *dir, const char *subpath) { - Buf *dir1 = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str(dir), dir1); - Buf *result = buf_alloc(); - os_path_join(dir1, buf_create_from_str(subpath), result); - return buf_ptr(result); -} - -static const char *path_from_libc(CodeGen *g, const char *subpath) { - return path_from_zig_lib(g, "libc", subpath); -} - -static const char *path_from_libunwind(CodeGen *g, const char *subpath) { - return path_from_zig_lib(g, "libunwind", subpath); -} - -static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "unwind", progress_node); - LinkLib *new_link_lib = codegen_add_link_lib(child_gen, buf_create_from_str("c")); - new_link_lib->provided_explicitly = false; - enum SrcKind { - SrcCpp, - SrcC, - SrcAsm, - }; - static const struct { - const char *path; - SrcKind kind; - } unwind_src[] = { - {"src" OS_SEP "libunwind.cpp", SrcCpp}, - {"src" OS_SEP "Unwind-EHABI.cpp", SrcCpp}, - {"src" OS_SEP "Unwind-seh.cpp", SrcCpp}, - - {"src" OS_SEP "UnwindLevel1.c", SrcC}, - {"src" OS_SEP "UnwindLevel1-gcc-ext.c", SrcC}, - {"src" OS_SEP "Unwind-sjlj.c", SrcC}, - - {"src" OS_SEP "UnwindRegistersRestore.S", SrcAsm}, - {"src" OS_SEP "UnwindRegistersSave.S", SrcAsm}, - }; - ZigList c_source_files = {0}; - for (size_t i = 0; i < array_length(unwind_src); i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libunwind(parent, unwind_src[i].path); - switch (unwind_src[i].kind) { - case SrcC: - c_file->args.append("-std=c99"); - break; - case SrcCpp: - c_file->args.append("-fno-rtti"); - c_file->args.append("-I"); - c_file->args.append(path_from_zig_lib(parent, "libcxx", "include")); - break; - case SrcAsm: - break; - } - c_file->args.append("-I"); - c_file->args.append(path_from_libunwind(parent, "include")); - if (target_supports_fpic(parent->zig_target)) { - c_file->args.append("-fPIC"); - } - c_file->args.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS"); - c_file->args.append("-Wa,--noexecstack"); - - // This is intentionally always defined because the macro definition means, should it only - // build for the target specified by compiler defines. Since we pass -target the compiler - // defines will be correct. - c_file->args.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); - - if (parent->build_mode == BuildModeDebug) { - c_file->args.append("-D_DEBUG"); - } - if (parent->is_single_threaded) { - c_file->args.append("-D_LIBUNWIND_HAS_NO_THREADS"); - } - c_file->args.append("-Wno-bitwise-conditional-parentheses"); - c_source_files.append(c_file); - } - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static void mingw_add_cc_args(CodeGen *parent, CFile *c_file) { - c_file->args.append("-DHAVE_CONFIG_H"); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "include", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-isystem"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "any-windows-any", - buf_ptr(parent->zig_lib_dir)))); - - if (target_is_arm(parent->zig_target) && - target_arch_pointer_bit_width(parent->zig_target->arch) == 32) - { - c_file->args.append("-mfpu=vfp"); - } - - c_file->args.append("-std=gnu11"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); -} - -static void glibc_add_include_dirs_arch(CFile *c_file, ZigLLVM_ArchType arch, const char *nptl, const char *dir) { - bool is_x86 = arch == ZigLLVM_x86 || arch == ZigLLVM_x86_64; - bool is_aarch64 = arch == ZigLLVM_aarch64 || arch == ZigLLVM_aarch64_be; - bool is_mips = arch == ZigLLVM_mips || arch == ZigLLVM_mipsel || - arch == ZigLLVM_mips64el || arch == ZigLLVM_mips64; - bool is_arm = arch == ZigLLVM_arm || arch == ZigLLVM_armeb; - bool is_ppc = arch == ZigLLVM_ppc || arch == ZigLLVM_ppc64 || arch == ZigLLVM_ppc64le; - bool is_riscv = arch == ZigLLVM_riscv32 || arch == ZigLLVM_riscv64; - bool is_sparc = arch == ZigLLVM_sparc || arch == ZigLLVM_sparcel || arch == ZigLLVM_sparcv9; - bool is_64 = target_arch_pointer_bit_width(arch) == 64; - - if (is_x86) { - if (arch == ZigLLVM_x86_64) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86_64" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86_64", dir))); - } - } else if (arch == ZigLLVM_x86) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "i386" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "i386", dir))); - } - } - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86", dir))); - } - } else if (is_arm) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "arm" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "arm", dir))); - } - } else if (is_mips) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "%s", dir, nptl))); - } else { - if (is_64) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "mips64", dir))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "mips32", dir))); - } - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips", dir))); - } - } else if (is_sparc) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "%s", dir, nptl))); - } else { - if (is_64) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "sparc64", dir))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "sparc32", dir))); - } - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc", dir))); - } - } else if (is_aarch64) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "aarch64" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "aarch64", dir))); - } - } else if (is_ppc) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "%s", dir, nptl))); - } else { - if (is_64) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "powerpc64", dir))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "powerpc32", dir))); - } - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc", dir))); - } - } else if (is_riscv) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "riscv" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "riscv", dir))); - } - } -} - -static void glibc_add_include_dirs(CodeGen *parent, CFile *c_file) { - ZigLLVM_ArchType arch = parent->zig_target->arch; - const char *nptl = (parent->zig_target->os == OsLinux) ? "nptl" : "htl"; - const char *glibc = path_from_libc(parent, "glibc"); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "include", glibc))); - - if (parent->zig_target->os == OsLinux) { - glibc_add_include_dirs_arch(c_file, arch, nullptr, - path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix" OS_SEP "sysv" OS_SEP "linux")); - } - - if (nptl != nullptr) { - glibc_add_include_dirs_arch(c_file, arch, nptl, path_from_libc(parent, "glibc" OS_SEP "sysdeps")); - } - - if (parent->zig_target->os == OsLinux) { - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP - "unix" OS_SEP "sysv" OS_SEP "linux" OS_SEP "generic")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP - "unix" OS_SEP "sysv" OS_SEP "linux" OS_SEP "include")); - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP - "unix" OS_SEP "sysv" OS_SEP "linux")); - } - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sysdeps" OS_SEP "%s", glibc, nptl))); - } - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "pthread")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix" OS_SEP "sysv")); - - glibc_add_include_dirs_arch(c_file, arch, nullptr, - path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix")); - - glibc_add_include_dirs_arch(c_file, arch, nullptr, path_from_libc(parent, "glibc" OS_SEP "sysdeps")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "generic")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc")); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-%s", - buf_ptr(parent->zig_lib_dir), target_arch_name(parent->zig_target->arch), - target_os_name(parent->zig_target->os), target_abi_name(parent->zig_target->abi)))); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "generic-glibc")); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-linux-any", - buf_ptr(parent->zig_lib_dir), target_arch_name(parent->zig_target->arch)))); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-linux-any")); -} - -static const char *glibc_start_asm_path(CodeGen *parent, const char *file) { - ZigLLVM_ArchType arch = parent->zig_target->arch; - bool is_aarch64 = arch == ZigLLVM_aarch64 || arch == ZigLLVM_aarch64_be; - bool is_mips = arch == ZigLLVM_mips || arch == ZigLLVM_mipsel || - arch == ZigLLVM_mips64el || arch == ZigLLVM_mips64; - bool is_arm = arch == ZigLLVM_arm || arch == ZigLLVM_armeb; - bool is_ppc = arch == ZigLLVM_ppc || arch == ZigLLVM_ppc64 || arch == ZigLLVM_ppc64le; - bool is_riscv = arch == ZigLLVM_riscv32 || arch == ZigLLVM_riscv64; - bool is_sparc = arch == ZigLLVM_sparc || arch == ZigLLVM_sparcel || arch == ZigLLVM_sparcv9; - bool is_64 = target_arch_pointer_bit_width(arch) == 64; - - Buf result = BUF_INIT; - buf_resize(&result, 0); - buf_append_buf(&result, parent->zig_lib_dir); - buf_append_str(&result, OS_SEP "libc" OS_SEP "glibc" OS_SEP "sysdeps" OS_SEP); - if (is_sparc) { - if (is_64) { - buf_append_str(&result, "sparc" OS_SEP "sparc64"); - } else { - buf_append_str(&result, "sparc" OS_SEP "sparc32"); - } - } else if (is_arm) { - buf_append_str(&result, "arm"); - } else if (is_mips) { - buf_append_str(&result, "mips"); - } else if (arch == ZigLLVM_x86_64) { - buf_append_str(&result, "x86_64"); - } else if (arch == ZigLLVM_x86) { - buf_append_str(&result, "i386"); - } else if (is_aarch64) { - buf_append_str(&result, "aarch64"); - } else if (is_riscv) { - buf_append_str(&result, "riscv"); - } else if (is_ppc) { - if (is_64) { - buf_append_str(&result, "powerpc" OS_SEP "powerpc64"); - } else { - buf_append_str(&result, "powerpc" OS_SEP "powerpc32"); - } - } - - buf_append_str(&result, OS_SEP); - buf_append_str(&result, file); - return buf_ptr(&result); -} - -static const char *musl_start_asm_path(CodeGen *parent, const char *file) { - Buf *result = buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "crt" OS_SEP "%s" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), target_arch_musl_name(parent->zig_target->arch), file); - return buf_ptr(result); -} - -static void musl_add_cc_args(CodeGen *parent, CFile *c_file, bool want_O3) { - c_file->args.append("-std=c99"); - c_file->args.append("-ffreestanding"); - // Musl adds these args to builds with gcc but clang does not support them. - //c_file->args.append("-fexcess-precision=standard"); - //c_file->args.append("-frounding-math"); - c_file->args.append("-Wa,--noexecstack"); - c_file->args.append("-D_XOPEN_SOURCE=700"); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "arch" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), target_arch_musl_name(parent->zig_target->arch)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "arch" OS_SEP "generic", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "src" OS_SEP "include", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "src" OS_SEP "internal", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "include", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf( - "%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-musl", - buf_ptr(parent->zig_lib_dir), - target_arch_musl_name(parent->zig_target->arch), - target_os_name(parent->zig_target->os)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "generic-musl", - buf_ptr(parent->zig_lib_dir)))); - - if (want_O3) - c_file->args.append("-O3"); - else - c_file->args.append("-Os"); - - c_file->args.append("-fomit-frame-pointer"); - c_file->args.append("-fno-unwind-tables"); - c_file->args.append("-fno-asynchronous-unwind-tables"); - c_file->args.append("-ffunction-sections"); - c_file->args.append("-fdata-sections"); -} - -static const char *musl_arch_names[] = { - "aarch64", - "arm", - "generic", - "i386", - "m68k", - "microblaze", - "mips", - "mips64", - "mipsn32", - "or1k", - "powerpc", - "powerpc64", - "riscv64", - "s390x", - "sh", - "x32", - "x86_64", -}; - -static bool is_musl_arch_name(const char *name) { - for (size_t i = 0; i < array_length(musl_arch_names); i += 1) { - if (strcmp(name, musl_arch_names[i]) == 0) - return true; - } - return false; -} - -enum MuslSrc { - MuslSrcAsm, - MuslSrcNormal, - MuslSrcO3, -}; - -static void add_musl_src_file(HashMap &source_table, - const char *file_path) -{ - Buf *src_file = buf_create_from_str(file_path); - - MuslSrc src_kind; - if (buf_ends_with_str(src_file, ".c")) { - bool want_O3 = buf_starts_with_str(src_file, "musl/src/malloc/") || - buf_starts_with_str(src_file, "musl/src/string/") || - buf_starts_with_str(src_file, "musl/src/internal/"); - src_kind = want_O3 ? MuslSrcO3 : MuslSrcNormal; - } else if (buf_ends_with_str(src_file, ".s") || buf_ends_with_str(src_file, ".S")) { - src_kind = MuslSrcAsm; - } else { - zig_unreachable(); - } - if (ZIG_OS_SEP_CHAR != '/') { - buf_replace(src_file, '/', ZIG_OS_SEP_CHAR); - } - source_table.put_unique(src_file, src_kind); -} - -static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c", progress_node); - - // When there is a src//foo.* then it should substitute for src/foo.* - // Even a .s file can substitute for a .c file. - - const char *target_musl_arch_name = target_arch_musl_name(parent->zig_target->arch); - - HashMap source_table = {}; - source_table.init(2000); - - for (size_t i = 0; i < array_length(ZIG_MUSL_SRC_FILES); i += 1) { - add_musl_src_file(source_table, ZIG_MUSL_SRC_FILES[i]); - } - - static const char *time32_compat_arch_list[] = {"arm", "i386", "mips", "powerpc"}; - for (size_t arch_i = 0; arch_i < array_length(time32_compat_arch_list); arch_i += 1) { - if (strcmp(target_musl_arch_name, time32_compat_arch_list[arch_i]) == 0) { - for (size_t i = 0; i < array_length(ZIG_MUSL_COMPAT_TIME32_FILES); i += 1) { - add_musl_src_file(source_table, ZIG_MUSL_COMPAT_TIME32_FILES[i]); - } - } - } - - - ZigList c_source_files = {0}; - - Buf dirname = BUF_INIT; - Buf basename = BUF_INIT; - Buf noextbasename = BUF_INIT; - Buf dirbasename = BUF_INIT; - Buf before_arch_dir = BUF_INIT; - - auto source_it = source_table.entry_iterator(); - for (;;) { - auto *entry = source_it.next(); - if (!entry) break; - - Buf *src_file = entry->key; - MuslSrc src_kind = entry->value; - - os_path_split(src_file, &dirname, &basename); - os_path_extname(&basename, &noextbasename, nullptr); - os_path_split(&dirname, &before_arch_dir, &dirbasename); - - bool is_arch_specific = false; - // Architecture-specific implementations are under a / folder. - if (is_musl_arch_name(buf_ptr(&dirbasename))) { - // Not the architecture we're compiling for. - if (strcmp(buf_ptr(&dirbasename), target_musl_arch_name) != 0) - continue; - is_arch_specific = true; - } - - if (!is_arch_specific) { - Buf override_path = BUF_INIT; - - // Look for an arch specific override. - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.s", - buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename)); - if (source_table.maybe_get(&override_path) != nullptr) - continue; - - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.S", - buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename)); - if (source_table.maybe_get(&override_path) != nullptr) - continue; - - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.c", - buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename)); - if (source_table.maybe_get(&override_path) != nullptr) - continue; - } - - Buf *full_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), buf_ptr(src_file)); - - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(full_path); - - musl_add_cc_args(parent, c_file, src_kind == MuslSrcO3); - c_file->args.append("-Qunused-arguments"); - c_file->args.append("-w"); // disable all warnings - - c_source_files.append(c_file); - } - - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static const char *build_libcxxabi(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++abi", progress_node); - codegen_add_link_lib(child_gen, buf_create_from_str("c")); - - ZigList c_source_files = {0}; - - const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - - for (size_t i = 0; i < array_length(ZIG_LIBCXXABI_FILES); i += 1) { - const char *rel_src_path = ZIG_LIBCXXABI_FILES[i]; - - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), rel_src_path)); - - c_file->args.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); - c_file->args.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE"); - c_file->args.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); - c_file->args.append("-D_LIBCXXABI_BUILDING_LIBRARY"); - c_file->args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); - c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - - if (target_abi_is_musl(parent->zig_target->abi)) { - c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC"); - } - - c_file->args.append("-I"); - c_file->args.append(cxxabi_include_path); - - c_file->args.append("-I"); - c_file->args.append(cxx_include_path); - - c_file->args.append("-O3"); - c_file->args.append("-DNDEBUG"); - if (target_supports_fpic(parent->zig_target)) { - c_file->args.append("-fPIC"); - } - c_file->args.append("-nostdinc++"); - c_file->args.append("-fstrict-aliasing"); - c_file->args.append("-funwind-tables"); - c_file->args.append("-D_DEBUG"); - c_file->args.append("-UNDEBUG"); - c_file->args.append("-std=c++11"); - - c_source_files.append(c_file); - } - - - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++", progress_node); - codegen_add_link_lib(child_gen, buf_create_from_str("c")); - - ZigList c_source_files = {0}; - - const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - - for (size_t i = 0; i < array_length(ZIG_LIBCXX_FILES); i += 1) { - const char *rel_src_path = ZIG_LIBCXX_FILES[i]; - - Buf *src_path_buf = buf_create_from_str(rel_src_path); - if (parent->zig_target->os == OsWindows) { - // filesystem stuff isn't supported on Windows - if (buf_starts_with_str(src_path_buf, "src/filesystem/")) { - continue; - } - } else { - if (buf_starts_with_str(src_path_buf, "src/support/win32/")) { - continue; - } - } - - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), rel_src_path)); - - c_file->args.append("-DNDEBUG"); - c_file->args.append("-D_LIBCPP_BUILDING_LIBRARY"); - c_file->args.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); - c_file->args.append("-DLIBCXX_BUILDING_LIBCXXABI"); - c_file->args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); - c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - - if (target_abi_is_musl(parent->zig_target->abi)) { - c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC"); - } - - c_file->args.append("-I"); - c_file->args.append(cxx_include_path); - - c_file->args.append("-I"); - c_file->args.append(cxxabi_include_path); - - c_file->args.append("-O3"); - c_file->args.append("-DNDEBUG"); - if (target_supports_fpic(parent->zig_target)) { - c_file->args.append("-fPIC"); - } - c_file->args.append("-nostdinc++"); - c_file->args.append("-fvisibility-inlines-hidden"); - c_file->args.append("-std=c++14"); - c_file->args.append("-Wno-user-defined-literals"); - - c_source_files.append(c_file); - } - - - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), src_path)); - c_file->args.append("-DHAVE_CONFIG_H"); - c_file->args.append("-D__LIBMSVCRT__"); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - c_file->args.append("-g"); - c_file->args.append("-O2"); - - child_gen->c_source_files.append(c_file); -} - -static void add_mingwex_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), src_path)); - c_file->args.append("-DHAVE_CONFIG_H"); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - child_gen->c_source_files.append(c_file); -} - -static void add_mingw_uuid_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), src_path)); - c_file->args.append("-DHAVE_CONFIG_H"); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - child_gen->c_source_files.append(c_file); -} - -static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2ProgressNode *progress_node) { - if (parent->libc == nullptr && parent->zig_target->os == OsWindows) { - if (strcmp(file, "crt2.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf( - "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtexe.c", buf_ptr(parent->zig_lib_dir))); - mingw_add_cc_args(parent, c_file); - c_file->args.append("-U__CRTDLL__"); - c_file->args.append("-D__MSVCRT__"); - // Uncomment these 3 things for crtu - //c_file->args.append("-DUNICODE"); - //c_file->args.append("-D_UNICODE"); - //c_file->args.append("-DWPRFLAG=1"); - return build_libc_object(parent, "crt2", c_file, progress_node); - } else if (strcmp(file, "dllcrt2.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf( - "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtdll.c", buf_ptr(parent->zig_lib_dir))); - mingw_add_cc_args(parent, c_file); - c_file->args.append("-U__CRTDLL__"); - c_file->args.append("-D__MSVCRT__"); - return build_libc_object(parent, "dllcrt2", c_file, progress_node); - } else if (strcmp(file, "mingw32.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingw32", progress_node); - - static const char *deps[] = { - "mingw" OS_SEP "crt" OS_SEP "crt0_c.c", - "mingw" OS_SEP "crt" OS_SEP "dll_argv.c", - "mingw" OS_SEP "crt" OS_SEP "gccmain.c", - "mingw" OS_SEP "crt" OS_SEP "natstart.c", - "mingw" OS_SEP "crt" OS_SEP "pseudo-reloc-list.c", - "mingw" OS_SEP "crt" OS_SEP "wildcard.c", - "mingw" OS_SEP "crt" OS_SEP "charmax.c", - "mingw" OS_SEP "crt" OS_SEP "crt0_w.c", - "mingw" OS_SEP "crt" OS_SEP "dllargv.c", - "mingw" OS_SEP "crt" OS_SEP "gs_support.c", - "mingw" OS_SEP "crt" OS_SEP "_newmode.c", - "mingw" OS_SEP "crt" OS_SEP "tlssup.c", - "mingw" OS_SEP "crt" OS_SEP "xncommod.c", - "mingw" OS_SEP "crt" OS_SEP "cinitexe.c", - "mingw" OS_SEP "crt" OS_SEP "merr.c", - "mingw" OS_SEP "crt" OS_SEP "usermatherr.c", - "mingw" OS_SEP "crt" OS_SEP "pesect.c", - "mingw" OS_SEP "crt" OS_SEP "udllargc.c", - "mingw" OS_SEP "crt" OS_SEP "xthdloc.c", - "mingw" OS_SEP "crt" OS_SEP "CRT_fp10.c", - "mingw" OS_SEP "crt" OS_SEP "mingw_helpers.c", - "mingw" OS_SEP "crt" OS_SEP "pseudo-reloc.c", - "mingw" OS_SEP "crt" OS_SEP "udll_argv.c", - "mingw" OS_SEP "crt" OS_SEP "xtxtmode.c", - "mingw" OS_SEP "crt" OS_SEP "crt_handler.c", - "mingw" OS_SEP "crt" OS_SEP "tlsthrd.c", - "mingw" OS_SEP "crt" OS_SEP "tlsmthread.c", - "mingw" OS_SEP "crt" OS_SEP "tlsmcrt.c", - "mingw" OS_SEP "crt" OS_SEP "cxa_atexit.c", - }; - for (size_t i = 0; i < array_length(deps); i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, deps[i]); - c_file->args.append("-DHAVE_CONFIG_H"); - c_file->args.append("-D_SYSCRT=1"); - c_file->args.append("-DCRTDLL=1"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - - child_gen->c_source_files.append(c_file); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "msvcrt-os.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "msvcrt-os", progress_node); - - for (size_t i = 0; i < array_length(msvcrt_common_src); i += 1) { - add_msvcrt_os_dep(parent, child_gen, msvcrt_common_src[i]); - } - if (parent->zig_target->arch == ZigLLVM_x86) { - for (size_t i = 0; i < array_length(msvcrt_i386_src); i += 1) { - add_msvcrt_os_dep(parent, child_gen, msvcrt_i386_src[i]); - } - } else { - for (size_t i = 0; i < array_length(msvcrt_other_src); i += 1) { - add_msvcrt_os_dep(parent, child_gen, msvcrt_other_src[i]); - } - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "mingwex.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingwex", progress_node); - - for (size_t i = 0; i < array_length(mingwex_generic_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_generic_src[i]); - } - if (parent->zig_target->arch == ZigLLVM_x86 || parent->zig_target->arch == ZigLLVM_x86_64) { - for (size_t i = 0; i < array_length(mingwex_x86_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_x86_src[i]); - } - } else if (target_is_arm(parent->zig_target)) { - if (target_arch_pointer_bit_width(parent->zig_target->arch) == 32) { - for (size_t i = 0; i < array_length(mingwex_arm32_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_arm32_src[i]); - } - } else { - for (size_t i = 0; i < array_length(mingwex_arm64_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_arm64_src[i]); - } - } - } else { - zig_unreachable(); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "uuid.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "uuid", progress_node); - for (size_t i = 0; i < array_length(mingw_uuid_src); i += 1) { - add_mingw_uuid_dep(parent, child_gen, mingw_uuid_src[i]); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else { - zig_unreachable(); - } - } else if (parent->libc == nullptr && target_is_glibc(parent->zig_target)) { - if (strcmp(file, "crti.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = glibc_start_asm_path(parent, "crti.S"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "crti", c_file, progress_node); - } else if (strcmp(file, "crtn.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = glibc_start_asm_path(parent, "crtn.S"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "crtn", c_file, progress_node); - } else if (strcmp(file, "start.os") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = glibc_start_asm_path(parent, "start.S"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DPIC"); - c_file->args.append("-DSHARED"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "start", c_file, progress_node); - } else if (strcmp(file, "abi-note.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "abi-note.S"); - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu")); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "abi-note", c_file, progress_node); - } else if (strcmp(file, "Scrt1.o") == 0) { - const char *start_os = get_libc_crt_file(parent, "start.os", progress_node); - const char *abi_note_o = get_libc_crt_file(parent, "abi-note.o", progress_node); - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeObj, nullptr, "Scrt1", progress_node); - codegen_add_object(child_gen, buf_create_from_str(start_os)); - codegen_add_object(child_gen, buf_create_from_str(abi_note_o)); - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "libc_nonshared.a") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c_nonshared", progress_node); - { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "elf-init.c"); - c_file->args.append("-std=gnu11"); - c_file->args.append("-fgnu89-inline"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - c_file->args.append("-fmerge-all-constants"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-fmath-errno"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu")); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-DSTACK_PROTECTOR_LEVEL=0"); - c_file->args.append("-fPIC"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-ftls-model=initial-exec"); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DPIC"); - c_file->args.append("-DLIBC_NONSHARED=1"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - codegen_add_object(child_gen, buf_create_from_str( - build_libc_object(parent, "elf-init", c_file, progress_node))); - } - static const struct { - const char *name; - const char *path; - } deps[] = { - {"atexit", "glibc" OS_SEP "stdlib" OS_SEP "atexit.c"}, - {"at_quick_exit", "glibc" OS_SEP "stdlib" OS_SEP "at_quick_exit.c"}, - {"stat", "glibc" OS_SEP "io" OS_SEP "stat.c"}, - {"fstat", "glibc" OS_SEP "io" OS_SEP "fstat.c"}, - {"lstat", "glibc" OS_SEP "io" OS_SEP "lstat.c"}, - {"stat64", "glibc" OS_SEP "io" OS_SEP "stat64.c"}, - {"fstat64", "glibc" OS_SEP "io" OS_SEP "fstat64.c"}, - {"lstat64", "glibc" OS_SEP "io" OS_SEP "lstat64.c"}, - {"fstatat", "glibc" OS_SEP "io" OS_SEP "fstatat.c"}, - {"fstatat64", "glibc" OS_SEP "io" OS_SEP "fstatat64.c"}, - {"mknod", "glibc" OS_SEP "io" OS_SEP "mknod.c"}, - {"mknodat", "glibc" OS_SEP "io" OS_SEP "mknodat.c"}, - {"pthread_atfork", "glibc" OS_SEP "nptl" OS_SEP "pthread_atfork.c"}, - {"stack_chk_fail_local", "glibc" OS_SEP "debug" OS_SEP "stack_chk_fail_local.c"}, - }; - for (size_t i = 0; i < array_length(deps); i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, deps[i].path); - c_file->args.append("-std=gnu11"); - c_file->args.append("-fgnu89-inline"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - c_file->args.append("-fmerge-all-constants"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-fmath-errno"); - c_file->args.append("-ftls-model=initial-exec"); - c_file->args.append("-Wno-ignored-attributes"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DPIC"); - c_file->args.append("-DLIBC_NONSHARED=1"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - codegen_add_object(child_gen, buf_create_from_str( - build_libc_object(parent, deps[i].name, c_file, progress_node))); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else { - zig_unreachable(); - } - } else if (parent->libc == nullptr && target_is_musl(parent->zig_target)) { - if (strcmp(file, "crti.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = musl_start_asm_path(parent, "crti.s"); - musl_add_cc_args(parent, c_file, false); - c_file->args.append("-Qunused-arguments"); - return build_libc_object(parent, "crti", c_file, progress_node); - } else if (strcmp(file, "crtn.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = musl_start_asm_path(parent, "crtn.s"); - c_file->args.append("-Qunused-arguments"); - musl_add_cc_args(parent, c_file, false); - return build_libc_object(parent, "crtn", c_file, progress_node); - } else if (strcmp(file, "crt1.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "crt1.c"); - musl_add_cc_args(parent, c_file, false); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-DCRT"); - return build_libc_object(parent, "crt1", c_file, progress_node); - } else if (strcmp(file, "Scrt1.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "Scrt1.c"); - musl_add_cc_args(parent, c_file, false); - c_file->args.append("-fPIC"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-DCRT"); - return build_libc_object(parent, "Scrt1", c_file, progress_node); - } else { - zig_unreachable(); - } - } else { - assert(parent->libc != nullptr); - Buf *out_buf = buf_alloc(); - os_path_join(buf_create_from_mem(parent->libc->crt_dir, parent->libc->crt_dir_len), - buf_create_from_str(file), out_buf); - return buf_ptr(out_buf); - } -} - -static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type, - Stage2ProgressNode *progress_node) -{ - CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type, parent_gen->libc, aname, - progress_node); - - // This is so that compiler_rt and libc.zig libraries know whether they - // will eventually be linked with libc. They make different decisions - // about what to export depending on whether libc is linked. - if (parent_gen->libc_link_lib != nullptr) { - LinkLib *new_link_lib = codegen_add_link_lib(child_gen, parent_gen->libc_link_lib->name); - new_link_lib->provided_explicitly = parent_gen->libc_link_lib->provided_explicitly; - } - - // Override the inherited build mode parameter - if (!parent_gen->is_test_build) { - switch (parent_gen->build_mode) { - case BuildModeDebug: - case BuildModeFastRelease: - case BuildModeSafeRelease: - child_gen->build_mode = BuildModeFastRelease; - break; - case BuildModeSmallRelease: - break; - } - } - - child_gen->function_sections = true; - child_gen->want_stack_check = WantStackCheckDisabled; - - codegen_build_and_link(child_gen); - return &child_gen->bin_file_output_path; -} - -static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) { - Buf *full_path = buf_alloc(); - os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path); - - return build_a_raw(parent_gen, "compiler_rt", full_path, child_out_type, progress_node); -} - -static Buf *build_c(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) { - Buf *full_path = buf_alloc(); - os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("c.zig"), full_path); - - return build_a_raw(parent_gen, "c", full_path, child_out_type, progress_node); -} - -static const char *get_darwin_arch_string(const ZigTarget *t) { - switch (t->arch) { - case ZigLLVM_aarch64: - return "arm64"; - case ZigLLVM_thumb: - case ZigLLVM_arm: - return "arm"; - case ZigLLVM_ppc: - return "ppc"; - case ZigLLVM_ppc64: - return "ppc64"; - case ZigLLVM_ppc64le: - return "ppc64le"; - default: - return ZigLLVMGetArchTypeName(t->arch); - } -} - - -static const char *getLDMOption(const ZigTarget *t) { - switch (t->arch) { - case ZigLLVM_x86: - return "elf_i386"; - case ZigLLVM_aarch64: - return "aarch64linux"; - case ZigLLVM_aarch64_be: - return "aarch64_be_linux"; - case ZigLLVM_arm: - case ZigLLVM_thumb: - return "armelf_linux_eabi"; - case ZigLLVM_armeb: - case ZigLLVM_thumbeb: - return "armebelf_linux_eabi"; - case ZigLLVM_ppc: - return "elf32ppclinux"; - case ZigLLVM_ppc64: - return "elf64ppc"; - case ZigLLVM_ppc64le: - return "elf64lppc"; - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - return "elf32_sparc"; - case ZigLLVM_sparcv9: - return "elf64_sparc"; - case ZigLLVM_mips: - return "elf32btsmip"; - case ZigLLVM_mipsel: - return "elf32ltsmip"; - return "elf64btsmip"; - case ZigLLVM_mips64el: - return "elf64ltsmip"; - case ZigLLVM_systemz: - return "elf64_s390"; - case ZigLLVM_x86_64: - if (t->abi == ZigLLVM_GNUX32) { - return "elf32_x86_64"; - } - // Any target elf will use the freebsd osabi if suffixed with "_fbsd". - if (t->os == OsFreeBSD) { - return "elf_x86_64_fbsd"; - } - return "elf_x86_64"; - case ZigLLVM_riscv32: - return "elf32lriscv"; - case ZigLLVM_riscv64: - return "elf64lriscv"; - default: - zig_unreachable(); - } -} - -static void add_rpath(LinkJob *lj, Buf *rpath) { - if (lj->rpath_table.maybe_get(rpath) != nullptr) - return; - - lj->args.append("-rpath"); - lj->args.append(buf_ptr(rpath)); - - lj->rpath_table.put(rpath, true); -} - -static void add_glibc_libs(LinkJob *lj) { - Error err; - ZigGLibCAbi *glibc_abi; - if ((err = glibc_load_metadata(&glibc_abi, lj->codegen->zig_lib_dir, true))) { - fprintf(stderr, "%s\n", err_str(err)); - exit(1); - } - - Buf *artifact_dir; - if ((err = glibc_build_dummies_and_maps(lj->codegen, glibc_abi, lj->codegen->zig_target, - &artifact_dir, true, lj->build_dep_prog_node))) - { - fprintf(stderr, "%s\n", err_str(err)); - exit(1); - } - - size_t lib_count = glibc_lib_count(); - for (size_t i = 0; i < lib_count; i += 1) { - const ZigGLibCLib *lib = glibc_lib_enum(i); - Buf *so_path = buf_sprintf("%s" OS_SEP "lib%s.so.%d.0.0", buf_ptr(artifact_dir), lib->name, lib->sover); - lj->args.append(buf_ptr(so_path)); - } -} - -static void construct_linker_job_elf(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("-error-limit=0"); - - if (g->out_type == OutTypeExe) { - lj->args.append("-z"); - size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override; - lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size))); - } - - if (g->linker_script) { - lj->args.append("-T"); - lj->args.append(g->linker_script); - } - - switch (g->linker_gc_sections) { - case OptionalBoolNull: - if (g->out_type != OutTypeObj) { - lj->args.append("--gc-sections"); - } - break; - case OptionalBoolTrue: - lj->args.append("--gc-sections"); - break; - case OptionalBoolFalse: - break; - } - - if (g->link_eh_frame_hdr) { - lj->args.append("--eh-frame-hdr"); - } - - if (g->linker_rdynamic) { - lj->args.append("--export-dynamic"); - } - - if (g->linker_optimization != nullptr) { - lj->args.append(buf_ptr(g->linker_optimization)); - } - - if (g->linker_z_nodelete) { - lj->args.append("-z"); - lj->args.append("nodelete"); - } - if (g->linker_z_defs) { - lj->args.append("-z"); - lj->args.append("defs"); - } - - lj->args.append("-m"); - lj->args.append(getLDMOption(g->zig_target)); - - bool is_lib = g->out_type == OutTypeLib; - bool is_dyn_lib = g->is_dynamic && is_lib; - if (!g->have_dynamic_link) { - if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb || - g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb) - { - lj->args.append("-Bstatic"); - } else { - lj->args.append("-static"); - } - } else if (is_dyn_lib) { - lj->args.append("-shared"); - } - - if (target_requires_pie(g->zig_target) && g->out_type == OutTypeExe) { - lj->args.append("-pie"); - } - - assert(buf_len(&g->bin_file_output_path) != 0); - lj->args.append("-o"); - lj->args.append(buf_ptr(&g->bin_file_output_path)); - - if (lj->link_in_crt) { - const char *crt1o; - if (g->zig_target->os == OsNetBSD) { - crt1o = "crt0.o"; - } else if (target_is_android(g->zig_target)) { - if (g->have_dynamic_link) { - crt1o = "crtbegin_dynamic.o"; - } else { - crt1o = "crtbegin_static.o"; - } - } else if (!g->have_dynamic_link) { - crt1o = "crt1.o"; - } else { - crt1o = "Scrt1.o"; - } - lj->args.append(get_libc_crt_file(g, crt1o, lj->build_dep_prog_node)); - if (target_libc_needs_crti_crtn(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crti.o", lj->build_dep_prog_node)); - } - } - - for (size_t i = 0; i < g->rpath_list.length; i += 1) { - Buf *rpath = g->rpath_list.at(i); - add_rpath(lj, rpath); - } - if (g->each_lib_rpath) { - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *link_lib = g->link_libs_list.at(i); - if (buf_eql_str(link_lib->name, "c")) { - continue; - } - bool does_exist; - Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); - if (os_file_exists(test_path, &does_exist) != ErrorNone) { - zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); - } - if (does_exist) { - add_rpath(lj, buf_create_from_str(lib_dir)); - break; - } - } - } - } - - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - lj->args.append("-L"); - lj->args.append(lib_dir); - } - - if (g->libc_link_lib != nullptr) { - if (g->libc != nullptr) { - lj->args.append("-L"); - lj->args.append(buf_ptr(buf_create_from_mem(g->libc->crt_dir, g->libc->crt_dir_len))); - } - - if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) { - assert(g->zig_target->dynamic_linker != nullptr); - lj->args.append("-dynamic-linker"); - lj->args.append(g->zig_target->dynamic_linker); - } - } - - if (is_dyn_lib) { - Buf *soname = (g->override_soname == nullptr) ? - buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major) : - g->override_soname; - lj->args.append("-soname"); - lj->args.append(buf_ptr(soname)); - - if (g->version_script_path != nullptr) { - lj->args.append("-version-script"); - lj->args.append(buf_ptr(g->version_script_path)); - } - } - - // .o files - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) { - if (g->libc_link_lib == nullptr) { - Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(libc_a_path)); - } - - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } - - // libraries - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *link_lib = g->link_libs_list.at(i); - if (buf_eql_str(link_lib->name, "c")) { - // libc is linked specially - continue; - } - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libc++ is linked specially - continue; - } - if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // these libraries are always linked below when targeting glibc - continue; - } - Buf *arg; - if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || - buf_ends_with_str(link_lib->name, ".so")) - { - arg = link_lib->name; - } else { - arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); - } - lj->args.append(buf_ptr(arg)); - } - - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - } - - // libc dep - if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) { - if (g->libc != nullptr) { - if (!g->have_dynamic_link) { - lj->args.append("--start-group"); - lj->args.append("-lc"); - lj->args.append("-lm"); - lj->args.append("--end-group"); - } else { - lj->args.append("-lc"); - lj->args.append("-lm"); - } - - if (g->zig_target->os == OsFreeBSD || - g->zig_target->os == OsNetBSD) - { - lj->args.append("-lpthread"); - } - } else if (target_is_glibc(g->zig_target)) { - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - add_glibc_libs(lj); - lj->args.append(get_libc_crt_file(g, "libc_nonshared.a", lj->build_dep_prog_node)); - } else if (target_is_musl(g->zig_target)) { - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - lj->args.append(build_musl(g, lj->build_dep_prog_node)); - } else if (g->libcpp_link_lib != nullptr) { - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - } else { - zig_unreachable(); - } - } - - // crt end - if (lj->link_in_crt) { - if (target_is_android(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crtend_android.o", lj->build_dep_prog_node)); - } else if (target_libc_needs_crti_crtn(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crtn.o", lj->build_dep_prog_node)); - } - } - - switch (g->linker_allow_shlib_undefined) { - case OptionalBoolNull: - if (!g->zig_target->is_native_os) { - lj->args.append("--allow-shlib-undefined"); - } - break; - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("--allow-shlib-undefined"); - break; - } - switch (g->linker_bind_global_refs_locally) { - case OptionalBoolNull: - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-Bsymbolic"); - break; - } -} - -static void construct_linker_job_wasm(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("-error-limit=0"); - // Increase the default stack size to a more reasonable value of 1MB instead of - // the default of 1 Wasm page being 64KB, unless overriden by the user. - size_t stack_size = (g->stack_size_override == 0) ? 1048576 : g->stack_size_override; - lj->args.append("-z"); - lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size))); - - // put stack before globals so that stack overflow results in segfault immediately before corrupting globals - // see https://github.com/ziglang/zig/issues/4496 - lj->args.append("--stack-first"); - - if (g->out_type != OutTypeExe) { - lj->args.append("--no-entry"); // So lld doesn't look for _start. - - // If there are any C source files we cannot rely on individual exports. - if (g->c_source_files.length != 0) { - lj->args.append("--export-all"); - } else { - auto export_it = g->exported_symbol_names.entry_iterator(); - decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr; - while ((curr_entry = export_it.next()) != nullptr) { - Buf *arg = buf_sprintf("--export=%s", buf_ptr(curr_entry->key)); - lj->args.append(buf_ptr(arg)); - } - } - } - lj->args.append("--allow-undefined"); - lj->args.append("-o"); - lj->args.append(buf_ptr(&g->bin_file_output_path)); - - // .o files - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - if (g->out_type != OutTypeObj) { - Buf *libc_o_path = build_c(g, OutTypeObj, lj->build_dep_prog_node); - lj->args.append(buf_ptr(libc_o_path)); - - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } -} - -static void coff_append_machine_arg(CodeGen *g, ZigList *list) { - if (g->zig_target->arch == ZigLLVM_x86) { - list->append("-MACHINE:X86"); - } else if (g->zig_target->arch == ZigLLVM_x86_64) { - list->append("-MACHINE:X64"); - } else if (target_is_arm(g->zig_target)) { - if (target_arch_pointer_bit_width(g->zig_target->arch) == 32) { - list->append("-MACHINE:ARM"); - } else { - list->append("-MACHINE:ARM64"); - } - } -} - -static void link_diag_callback(void *context, const char *ptr, size_t len) { - Buf *diag = reinterpret_cast(context); - buf_append_mem(diag, ptr, len); -} - -static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, - Buf *diag) -{ - Buf *stdout_diag = buf_alloc(); - buf_resize(diag, 0); - bool result = ZigLLDLink(oformat, args, arg_count, link_diag_callback, stdout_diag, diag); - buf_destroy(stdout_diag); - return result; -} - -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"); -} - -static void add_msvc_link_args(LinkJob *lj, bool is_library) { - CodeGen *g = lj->codegen; - - bool is_dynamic = g->is_dynamic; - const char *lib_str = is_dynamic ? "" : "lib"; - const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : ""; - - if (!is_dynamic) { - Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str); - lj->args.append(buf_ptr(cmt_lib_name)); - } else { - Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str); - lj->args.append(buf_ptr(msvcrt_lib_name)); - } - - Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str); - lj->args.append(buf_ptr(vcruntime_lib_name)); - - Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str); - lj->args.append(buf_ptr(crt_lib_name)); - - //Visual C++ 2015 Conformance Changes - //https://msdn.microsoft.com/en-us/library/bb531344.aspx - lj->args.append("legacy_stdio_definitions.lib"); - - // msvcrt depends on kernel32 and ntdll - lj->args.append("kernel32.lib"); - lj->args.append("ntdll.lib"); -} - -static void print_zig_cc_cmd(ZigList *args) { - for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) { - const char *space_str = (arg_i == 0) ? "" : " "; - fprintf(stderr, "%s%s", space_str, args->at(arg_i)); - } - fprintf(stderr, "\n"); -} - -static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_file) { - Error err; - - Buf *self_exe_path = buf_alloc(); - if ((err = os_self_exe_path(self_exe_path))) { - fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err)); - exit(1); - } - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - fprintf(stderr, "Unable to get compiler id: %s\n", err_str(err)); - exit(1); - } - - Buf *cache_dir = get_global_cache_dir(); - Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(cache_dir)); - Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); - - Buf *def_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "def-include", - buf_ptr(parent->zig_lib_dir)); - - CacheHash *cache_hash = heap::c_allocator.create(); - cache_init(cache_hash, manifest_dir); - - cache_buf(cache_hash, compiler_id); - cache_file(cache_hash, def_in_file); - cache_buf(cache_hash, def_include_dir); - cache_int(cache_hash, parent->zig_target->arch); - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(cache_hash, &digest))) { - if (err != ErrorInvalidFormat) { - if (err == ErrorCacheUnavailable) { - // already printed error - } else { - fprintf(stderr, "unable to check cache when processing .def.in file: %s\n", err_str(err)); - } - exit(1); - } - } - - Buf *artifact_dir; - Buf *lib_final_path; - Buf *final_lib_basename = buf_sprintf("%s.lib", name); - - bool is_cache_miss = (buf_len(&digest) == 0); - if (is_cache_miss) { - if ((err = cache_final(cache_hash, &digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - if ((err = os_make_path(artifact_dir))) { - fprintf(stderr, "Unable to create output directory '%s': %s", - buf_ptr(artifact_dir), err_str(err)); - exit(1); - } - Buf *final_def_basename = buf_sprintf("%s.def", name); - Buf *def_final_path = buf_alloc(); - os_path_join(artifact_dir, final_def_basename, def_final_path); - - ZigList args = {}; - args.append(buf_ptr(self_exe_path)); - args.append("clang"); - args.append("-x"); - args.append("c"); - args.append(buf_ptr(def_in_file)); - args.append("-Wp,-w"); - args.append("-undef"); - args.append("-P"); - args.append("-I"); - args.append(buf_ptr(def_include_dir)); - if (target_is_arm(parent->zig_target)) { - if (target_arch_pointer_bit_width(parent->zig_target->arch) == 32) { - args.append("-DDEF_ARM32"); - } else { - args.append("-DDEF_ARM64"); - } - } else if (parent->zig_target->arch == ZigLLVM_x86) { - args.append("-DDEF_I386"); - } else if (parent->zig_target->arch == ZigLLVM_x86_64) { - args.append("-DDEF_X64"); - } else { - zig_unreachable(); - } - args.append("-E"); - args.append("-o"); - args.append(buf_ptr(def_final_path)); - - if (parent->verbose_cc) { - print_zig_cc_cmd(&args); - } - Termination term; - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nThe following command failed:\n"); - print_zig_cc_cmd(&args); - exit(1); - } - - lib_final_path = buf_alloc(); - os_path_join(artifact_dir, final_lib_basename, lib_final_path); - - if (ZigLLVMWriteImportLibrary(buf_ptr(def_final_path), - parent->zig_target->arch, - buf_ptr(lib_final_path), - /* kill_at */ true)) - { - zig_panic("link: could not emit %s", buf_ptr(lib_final_path)); - } - } else { - // cache hit - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - lib_final_path = buf_alloc(); - os_path_join(artifact_dir, final_lib_basename, lib_final_path); - } - parent->caches_to_release.append(cache_hash); - - return buf_ptr(lib_final_path); -} - -static bool is_linking_system_lib(CodeGen *g, const char *name) { - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (buf_eql_str(link_lib->name, name)) { - return true; - } - } - return false; -} - -static Error find_mingw_lib_def(LinkJob *lj, const char *name, Buf *out_path) { - CodeGen *g = lj->codegen; - Buf override_path = BUF_INIT; - Error err; - - char const *lib_path = nullptr; - if (g->zig_target->arch == ZigLLVM_x86) { - lib_path = "lib32"; - } else if (g->zig_target->arch == ZigLLVM_x86_64) { - lib_path = "lib64"; - } else if (target_is_arm(g->zig_target)) { - const bool is_32 = target_arch_pointer_bit_width(g->zig_target->arch) == 32; - lib_path = is_32 ? "libarm32" : "libarm64"; - } else { - zig_unreachable(); - } - - // Try the archtecture-specific path first - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), lib_path, name); - - bool does_exist; - if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { - return err; - } - - if (!does_exist) { - // Try the generic version - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), name); - - if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { - return err; - } - } - - if (!does_exist) { - // Try the generic version and preprocess it - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def.in", buf_ptr(g->zig_lib_dir), name); - - if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { - return err; - } - } - - if (!does_exist) { - return ErrorFileNotFound; - } - - buf_init_from_buf(out_path, &override_path); - return ErrorNone; -} - -static void add_mingw_link_args(LinkJob *lj, bool is_library) { - CodeGen *g = lj->codegen; - - lj->args.append("-lldmingw"); - - bool is_dll = g->out_type == OutTypeLib && g->is_dynamic; - - if (g->zig_target->arch == ZigLLVM_x86) { - lj->args.append("-ALTERNATENAME:__image_base__=___ImageBase"); - } else { - lj->args.append("-ALTERNATENAME:__image_base__=__ImageBase"); - } - - if (is_dll) { - lj->args.append(get_libc_crt_file(g, "dllcrt2.o", lj->build_dep_prog_node)); - } else { - lj->args.append(get_libc_crt_file(g, "crt2.o", lj->build_dep_prog_node)); - } - - lj->args.append(get_libc_crt_file(g, "mingw32.lib", lj->build_dep_prog_node)); - lj->args.append(get_libc_crt_file(g, "mingwex.lib", lj->build_dep_prog_node)); - lj->args.append(get_libc_crt_file(g, "msvcrt-os.lib", lj->build_dep_prog_node)); - - for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) { - const char *name = mingw_def_list[def_i].name; - const bool always_link = mingw_def_list[def_i].always_link; - - if (always_link || is_linking_system_lib(g, name)) { - Buf lib_path = BUF_INIT; - Error err = find_mingw_lib_def(lj, name, &lib_path); - - if (err == ErrorFileNotFound) { - zig_panic("link: could not find .def file to build %s\n", name); - } else if (err != ErrorNone) { - zig_panic("link: unable to check if .def file for %s exists: %s", - name, err_str(err)); - } - - lj->args.append(get_def_lib(g, name, &lib_path)); - } - } -} - -static void add_win_link_args(LinkJob *lj, bool is_library, bool *have_windows_dll_import_libs) { - if (lj->link_in_crt) { - if (target_abi_is_gnu(lj->codegen->zig_target->abi)) { - *have_windows_dll_import_libs = true; - add_mingw_link_args(lj, is_library); - } else { - add_msvc_link_args(lj, is_library); - } - } else { - lj->args.append("-NODEFAULTLIB"); - if (!is_library) { - if (lj->codegen->have_winmain) { - lj->args.append("-ENTRY:WinMain"); - } else if (lj->codegen->have_wwinmain) { - lj->args.append("-ENTRY:wWinMain"); - } else if (lj->codegen->have_wwinmain_crt_startup) { - lj->args.append("-ENTRY:wWinMainCRTStartup"); - } else { - lj->args.append("-ENTRY:WinMainCRTStartup"); - } - } - } -} - -static bool is_mingw_link_lib(Buf *name) { - for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) { - if (buf_eql_str_ignore_case(name, mingw_def_list[def_i].name)) { - return true; - } - } - return false; -} -static void construct_linker_job_coff(LinkJob *lj) { - Error err; - CodeGen *g = lj->codegen; - - lj->args.append("-ERRORLIMIT:0"); - - lj->args.append("-NOLOGO"); - - if (!g->strip_debug_symbols) { - lj->args.append("-DEBUG"); - } - - if (g->out_type == OutTypeExe) { - // TODO compile time stack upper bound detection - size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override; - lj->args.append(buf_ptr(buf_sprintf("-STACK:%" ZIG_PRI_usize, stack_size))); - } - - coff_append_machine_arg(g, &lj->args); - - bool is_library = g->out_type == OutTypeLib; - if (is_library && g->is_dynamic) { - lj->args.append("-DLL"); - } - - lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->bin_file_output_path)))); - - if (g->libc_link_lib != nullptr && g->libc != nullptr) { - Buf *buff0 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff0, g->libc->crt_dir, g->libc->crt_dir_len); - lj->args.append(buf_ptr(buff0)); - - if (target_abi_is_gnu(g->zig_target->abi)) { - Buf *buff1 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff1, g->libc->sys_include_dir, g->libc->sys_include_dir_len); - lj->args.append(buf_ptr(buff1)); - - Buf *buff2 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff2, g->libc->include_dir, g->libc->include_dir_len); - lj->args.append(buf_ptr(buff2)); - } else { - Buf *buff1 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff1, g->libc->msvc_lib_dir, g->libc->msvc_lib_dir_len); - lj->args.append(buf_ptr(buff1)); - - Buf *buff2 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff2, g->libc->kernel32_lib_dir, g->libc->kernel32_lib_dir_len); - lj->args.append(buf_ptr(buff2)); - } - } - - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); - } - - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - bool have_windows_dll_import_libs = false; - switch (detect_subsystem(g)) { - case TargetSubsystemAuto: - if (g->zig_target->os == OsUefi) { - add_uefi_link_args(lj); - } else { - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - } - break; - case TargetSubsystemConsole: - lj->args.append("-SUBSYSTEM:console"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - 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_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - case TargetSubsystemPosix: - lj->args.append("-SUBSYSTEM:posix"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - case TargetSubsystemWindows: - lj->args.append("-SUBSYSTEM:windows"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - } - - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - } - - if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) { - if (g->libc_link_lib == nullptr && !g->is_dummy_so) { - Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(libc_a_path)); - } - - // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } - - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (buf_eql_str(link_lib->name, "c")) { - continue; - } - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libc++ is linked specially - continue; - } - if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // these libraries are always linked below when targeting glibc - continue; - } - bool is_sys_lib = is_mingw_link_lib(link_lib->name); - if (have_windows_dll_import_libs && is_sys_lib) { - continue; - } - // If we're linking in the CRT or the libs are provided explictly we don't want to generate def/libs - if ((lj->link_in_crt && is_sys_lib) || link_lib->provided_explicitly) { - if (target_abi_is_gnu(lj->codegen->zig_target->abi)) { - if (buf_eql_str(link_lib->name, "uuid")) { - // mingw-w64 provides this lib - lj->args.append(get_libc_crt_file(g, "uuid.lib", lj->build_dep_prog_node)); - } else { - Buf* lib_name = buf_sprintf("lib%s.a", buf_ptr(link_lib->name)); - lj->args.append(buf_ptr(lib_name)); - } - } else { - Buf* lib_name = buf_sprintf("%s.lib", buf_ptr(link_lib->name)); - lj->args.append(buf_ptr(lib_name)); - } - continue; - } - - // This library may be a system one and we may have a suitable .lib file - - // Normalize the library name to lower case, the FS may be - // case-sensitive - char *name = strdup(buf_ptr(link_lib->name)); - assert(name != nullptr); - for (char *ch = name; *ch; ++ch) *ch = tolower(*ch); - - Buf lib_path = BUF_INIT; - err = find_mingw_lib_def(lj, name, &lib_path); - - if (err == ErrorFileNotFound) { - zig_panic("link: could not find .def file to build %s\n", name); - } else if (err != ErrorNone) { - zig_panic("link: unable to check if .def file for %s exists: %s", - name, err_str(err)); - } - - lj->args.append(get_def_lib(g, name, &lib_path)); - - mem::os::free(name); - } -} - -static void construct_linker_job_macho(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("-error-limit"); - lj->args.append("0"); - lj->args.append("-demangle"); - - switch (g->linker_gc_sections) { - case OptionalBoolNull: - // TODO why do we not follow the same logic of elf here? - break; - case OptionalBoolTrue: - lj->args.append("--gc-sections"); - break; - case OptionalBoolFalse: - break; - } - - if (g->linker_rdynamic) { - lj->args.append("-export_dynamic"); - } - - if (g->linker_optimization != nullptr) { - lj->args.append(buf_ptr(g->linker_optimization)); - } - - if (g->linker_z_nodelete) { - lj->args.append("-z"); - lj->args.append("nodelete"); - } - if (g->linker_z_defs) { - lj->args.append("-z"); - lj->args.append("defs"); - } - - bool is_lib = g->out_type == OutTypeLib; - bool is_dyn_lib = g->is_dynamic && is_lib; - if (is_lib && !g->is_dynamic) { - lj->args.append("-static"); - } else { - lj->args.append("-dynamic"); - } - - if (is_dyn_lib) { - lj->args.append("-dylib"); - - Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); - lj->args.append("-compatibility_version"); - lj->args.append(buf_ptr(compat_vers)); - - Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, - g->version_major, g->version_minor, g->version_patch); - lj->args.append("-current_version"); - lj->args.append(buf_ptr(cur_vers)); - - // TODO getting an error when running an executable when doing this rpath thing - //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", - // buf_ptr(g->root_out_name), g->version_major); - //lj->args.append("-install_name"); - //lj->args.append(buf_ptr(dylib_install_name)); - - assert(buf_len(&g->bin_file_output_path) != 0); - } - - lj->args.append("-arch"); - lj->args.append(get_darwin_arch_string(g->zig_target)); - - if (g->zig_target->glibc_or_darwin_version != nullptr) { - if (g->zig_target->os == OsMacOSX) { - lj->args.append("-macosx_version_min"); - } else if (g->zig_target->os == OsIOS) { - if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64) { - lj->args.append("-ios_simulator_version_min"); - } else { - lj->args.append("-iphoneos_version_min"); - } - } - - Buf *version_string = buf_sprintf("%d.%d.%d", - g->zig_target->glibc_or_darwin_version->major, - g->zig_target->glibc_or_darwin_version->minor, - g->zig_target->glibc_or_darwin_version->patch); - lj->args.append(buf_ptr(version_string)); - - lj->args.append("-sdk_version"); - lj->args.append(buf_ptr(version_string)); - } else if (stage2_is_zig0 && g->zig_target->os == OsMacOSX) { - // running `zig0`; `-pie` requires versions >= 10.5; select 10.13 - lj->args.append("-macosx_version_min"); - lj->args.append("10.13"); - lj->args.append("-sdk_version"); - lj->args.append("10.13"); - } - - if (g->out_type == OutTypeExe) { - lj->args.append("-pie"); - } - - lj->args.append("-o"); - lj->args.append(buf_ptr(&g->bin_file_output_path)); - - for (size_t i = 0; i < g->rpath_list.length; i += 1) { - Buf *rpath = g->rpath_list.at(i); - add_rpath(lj, rpath); - } - if (is_dyn_lib) { - add_rpath(lj, &g->bin_file_output_path); - } - - if (is_dyn_lib) { - if (g->system_linker_hack) { - lj->args.append("-headerpad_max_install_names"); - } - } - - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - lj->args.append("-L"); - lj->args.append(lib_dir); - } - - // .o files - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce - if (g->out_type == OutTypeExe || is_dyn_lib) { - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } - - // libraries - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (buf_eql_str(link_lib->name, "c")) { - // libc is linked specially - continue; - } - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libc++ is linked specially - continue; - } - if (g->zig_target->is_native_os && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libSystem is linked specially - continue; - } - - Buf *arg; - if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || - buf_ends_with_str(link_lib->name, ".dylib")) - { - arg = link_lib->name; - } else { - arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); - } - lj->args.append(buf_ptr(arg)); - } - - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - } - - // libc dep - if (g->zig_target->is_native_os || stage2_is_zig0) { - // on Darwin, libSystem has libc in it, but also you have to use it - // to make syscalls because the syscall numbers are not documented - // and change between versions. - // so we always link against libSystem - lj->args.append("-lSystem"); - } - - for (size_t i = 0; i < g->framework_dirs.length; i += 1) { - const char *framework_dir = g->framework_dirs.at(i); - lj->args.append("-F"); - lj->args.append(framework_dir); - } - - for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { - lj->args.append("-framework"); - lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); - } - - switch (g->linker_allow_shlib_undefined) { - case OptionalBoolNull: - if (!g->zig_target->is_native_os && !stage2_is_zig0) { - // TODO https://github.com/ziglang/zig/issues/5059 - lj->args.append("-undefined"); - lj->args.append("dynamic_lookup"); - } - break; - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-undefined"); - lj->args.append("dynamic_lookup"); - break; - } - switch (g->linker_bind_global_refs_locally) { - case OptionalBoolNull: - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-Bsymbolic"); - break; - } -} - -static void construct_linker_job(LinkJob *lj) { - switch (target_object_format(lj->codegen->zig_target)) { - case ZigLLVM_UnknownObjectFormat: - case ZigLLVM_XCOFF: - zig_unreachable(); - - case ZigLLVM_COFF: - return construct_linker_job_coff(lj); - case ZigLLVM_ELF: - return construct_linker_job_elf(lj); - case ZigLLVM_MachO: - return construct_linker_job_macho(lj); - case ZigLLVM_Wasm: - return construct_linker_job_wasm(lj); - } -} - -void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node) { - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, progress_node); - g->link_objects.append(compiler_rt_o_path); -} - -void codegen_link(CodeGen *g) { - codegen_add_time_event(g, "Build Dependencies"); - LinkJob lj = {0}; - - { - const char *progress_name = "Build Dependencies"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - lj.build_dep_prog_node = g->sub_progress_node; - } - - - // even though we're calling LLD as a library it thinks the first - // argument is its own exe name - lj.args.append("lld"); - - lj.rpath_table.init(4); - lj.codegen = g; - - if (g->out_type == OutTypeObj) { - lj.args.append("-r"); - } - - if (g->out_type == OutTypeLib && !g->is_dynamic && !target_is_wasm(g->zig_target)) { - ZigList file_names = {}; - for (size_t i = 0; i < g->link_objects.length; i += 1) { - file_names.append(buf_ptr(g->link_objects.at(i))); - } - ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target->os); - codegen_add_time_event(g, "LLVM Link"); - { - const char *progress_name = "Link"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - if (g->verbose_link) { - fprintf(stderr, "ar rcs %s", buf_ptr(&g->bin_file_output_path)); - for (size_t i = 0; i < file_names.length; i += 1) { - fprintf(stderr, " %s", file_names.at(i)); - } - fprintf(stderr, "\n"); - } - if (ZigLLVMWriteArchive(buf_ptr(&g->bin_file_output_path), file_names.items, file_names.length, os_type)) { - fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->bin_file_output_path)); - exit(1); - } - return; - } - - lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); - - construct_linker_job(&lj); - - if (g->verbose_link) { - for (size_t i = 0; i < lj.args.length; i += 1) { - const char *space = (i != 0) ? " " : ""; - fprintf(stderr, "%s%s", space, lj.args.at(i)); - } - fprintf(stderr, "\n"); - } - - Buf diag = BUF_INIT; - - codegen_add_time_event(g, "LLVM Link"); - { - const char *progress_name = "Link"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - if (g->system_linker_hack && g->zig_target->os == OsMacOSX) { - Termination term; - ZigList args = {}; - args.append("ld"); - for (size_t i = 1; i < lj.args.length; i += 1) { - args.append(lj.args.at(i)); - } - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - exit(1); - } - } else if (!zig_lld_link(target_object_format(g->zig_target), lj.args.items, lj.args.length, &diag)) { - fprintf(stderr, "%s\n", buf_ptr(&diag)); - exit(1); - } -} - diff --git a/src/link.zig b/src/link.zig new file mode 100644 index 0000000000..4d28c8a1a7 --- /dev/null +++ b/src/link.zig @@ -0,0 +1,549 @@ +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const fs = std.fs; +const log = std.log.scoped(.link); +const assert = std.debug.assert; + +const Compilation = @import("Compilation.zig"); +const Module = @import("Module.zig"); +const trace = @import("tracy.zig").trace; +const Package = @import("Package.zig"); +const Type = @import("type.zig").Type; +const Cache = @import("Cache.zig"); +const build_options = @import("build_options"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; + +pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; + +pub const Emit = struct { + /// Where the output will go. + directory: Compilation.Directory, + /// Path to the output file, relative to `directory`. + sub_path: []const u8, +}; + +pub const Options = struct { + /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called, + /// it will have already been null-checked. + emit: ?Emit, + target: std.Target, + output_mode: std.builtin.OutputMode, + link_mode: std.builtin.LinkMode, + object_format: std.builtin.ObjectFormat, + optimize_mode: std.builtin.Mode, + machine_code_model: std.builtin.CodeModel, + root_name: []const u8, + /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. + module: ?*Module, + dynamic_linker: ?[]const u8, + /// Used for calculating how much space to reserve for symbols in case the binary file + /// does not already have a symbol table. + symbol_count_hint: u64 = 32, + /// Used for calculating how much space to reserve for executable program code in case + /// the binary file does not already have such a section. + program_code_size_hint: u64 = 256 * 1024, + entry_addr: ?u64 = null, + stack_size_override: ?u64, + /// Set to `true` to omit debug info. + strip: bool, + /// If this is true then this link code is responsible for outputting an object + /// file and then using LLD to link it together with the link options and other objects. + /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. + use_lld: bool, + /// If this is true then this link code is responsible for making an LLVM IR Module, + /// outputting it to an object file, and then linking that together with link options and + /// other objects. + /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. + use_llvm: bool, + link_libc: bool, + link_libcpp: bool, + function_sections: bool, + eh_frame_hdr: bool, + rdynamic: bool, + z_nodelete: bool, + z_defs: bool, + bind_global_refs_locally: bool, + is_native_os: bool, + pic: bool, + valgrind: bool, + stack_check: bool, + single_threaded: bool, + verbose_link: bool, + dll_export_fns: bool, + error_return_tracing: bool, + is_compiler_rt_or_libc: bool, + parent_compilation_link_libc: bool, + each_lib_rpath: bool, + disable_lld_caching: bool, + is_test: bool, + gc_sections: ?bool = null, + allow_shlib_undefined: ?bool, + subsystem: ?std.Target.SubSystem, + linker_script: ?[]const u8, + version_script: ?[]const u8, + override_soname: ?[]const u8, + llvm_cpu_features: ?[*:0]const u8, + /// Extra args passed directly to LLD. Ignored when not linking with LLD. + extra_lld_args: []const []const u8, + + objects: []const []const u8, + framework_dirs: []const []const u8, + frameworks: []const []const u8, + system_libs: std.StringArrayHashMapUnmanaged(void), + lib_dirs: []const []const u8, + rpath_list: []const []const u8, + + version: ?std.builtin.Version, + libc_installation: ?*const LibCInstallation, + + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { + return if (options.use_lld) .Obj else options.output_mode; + } +}; + +pub const File = struct { + tag: Tag, + options: Options, + file: ?fs.File, + allocator: *Allocator, + /// When linking with LLD, this linker code will output an object file only at + /// this location, and then this path can be placed on the LLD linker line. + intermediary_basename: ?[]const u8 = null, + + /// Prevents other processes from clobbering files in the output directory + /// of this linking operation. + lock: ?Cache.Lock = null, + + pub const LinkBlock = union { + elf: Elf.TextBlock, + coff: Coff.TextBlock, + macho: MachO.TextBlock, + c: void, + wasm: void, + }; + + pub const LinkFn = union { + elf: Elf.SrcFn, + coff: Coff.SrcFn, + macho: MachO.SrcFn, + c: void, + wasm: ?Wasm.FnData, + }; + + /// For DWARF .debug_info. + pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage); + + /// For DWARF .debug_info. + pub const DbgInfoTypeReloc = struct { + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// This is where the .debug_info tag for the type is. + off: u32, + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// List of DW.AT_type / DW.FORM_ref4 that points to the type. + relocs: std.ArrayListUnmanaged(u32), + }; + + /// Attempts incremental linking, if the file already exists. If + /// incremental linking fails, falls back to truncating the file and + /// rewriting it. A malicious file is detected as incremental link failure + /// and does not cause Illegal Behavior. This operation is not atomic. + pub fn openPath(allocator: *Allocator, options: Options) !*File { + const use_stage1 = build_options.is_stage1 and options.use_llvm; + if (use_stage1 or options.emit == null) { + return switch (options.object_format) { + .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, + .elf => &(try Elf.createEmpty(allocator, options)).base, + .macho => &(try MachO.createEmpty(allocator, options)).base, + .wasm => &(try Wasm.createEmpty(allocator, options)).base, + .c => unreachable, // Reported error earlier. + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + } + const emit = options.emit.?; + const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm + const sub_path = if (use_lld) blk: { + if (options.module == null) { + // No point in opening a file, we would not write anything to it. Initialize with empty. + return switch (options.object_format) { + .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, + .elf => &(try Elf.createEmpty(allocator, options)).base, + .macho => &(try MachO.createEmpty(allocator, options)).base, + .wasm => &(try Wasm.createEmpty(allocator, options)).base, + .c => unreachable, // Reported error earlier. + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + } + // Open a temporary object file, not the final output file because we want to link with LLD. + break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() }); + } else emit.sub_path; + errdefer if (use_lld) allocator.free(sub_path); + + const file: *File = switch (options.object_format) { + .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base, + .elf => &(try Elf.openPath(allocator, sub_path, options)).base, + .macho => &(try MachO.openPath(allocator, sub_path, options)).base, + .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, + .c => &(try C.openPath(allocator, sub_path, options)).base, + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + + if (use_lld) { + file.intermediary_basename = sub_path; + } + + return file; + } + + pub fn cast(base: *File, comptime T: type) ?*T { + if (base.tag != T.base_tag) + return null; + + return @fieldParentPtr(T, "base", base); + } + + pub fn makeWritable(base: *File) !void { + switch (base.tag) { + .coff, .elf, .macho => { + if (base.file != null) return; + const emit = base.options.emit orelse return; + base.file = try emit.directory.handle.createFile(emit.sub_path, .{ + .truncate = false, + .read = true, + .mode = determineMode(base.options), + }); + }, + .c, .wasm => {}, + } + } + + pub fn makeExecutable(base: *File) !void { + switch (base.tag) { + .coff, .elf, .macho => if (base.file) |f| { + if (base.intermediary_basename != null) { + // The file we have open is not the final file that we want to + // make executable, so we don't have to close it. + return; + } + f.close(); + base.file = null; + }, + .c, .wasm => {}, + } + } + + /// May be called before or after updateDeclExports but must be called + /// after allocateDeclIndexes for any given Decl. + pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl), + .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), + .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), + .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), + } + } + + pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), + .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), + .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), + .c, .wasm => {}, + } + } + + /// Must be called before any call to updateDecl or updateDeclExports for + /// any given Decl. + pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), + .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), + .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), + .c, .wasm => {}, + } + } + + pub fn releaseLock(self: *File) void { + if (self.lock) |*lock| { + lock.release(); + self.lock = null; + } + } + + pub fn toOwnedLock(self: *File) Cache.Lock { + const lock = self.lock.?; + self.lock = null; + return lock; + } + + pub fn destroy(base: *File) void { + base.releaseLock(); + if (base.file) |f| f.close(); + if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path); + switch (base.tag) { + .coff => { + const parent = @fieldParentPtr(Coff, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .elf => { + const parent = @fieldParentPtr(Elf, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .macho => { + const parent = @fieldParentPtr(MachO, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .c => { + const parent = @fieldParentPtr(C, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .wasm => { + const parent = @fieldParentPtr(Wasm, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + } + } + + /// Commit pending changes and write headers. Takes into account final output mode + /// and `use_lld`, not only `effectiveOutputMode`. + pub fn flush(base: *File, comp: *Compilation) !void { + const emit = base.options.emit orelse return; // -fno-emit-bin + + if (comp.clang_preprocessor_mode == .yes) { + // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) + // Until then, we do `lld -r -o output.o input.o` even though the output is the same + // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file + // to the final location. See also the corresponding TODO in Coff linking. + const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path}); + defer comp.gpa.free(full_out_path); + assert(comp.c_object_table.count() == 1); + const the_entry = comp.c_object_table.items()[0]; + const cached_pp_file_path = the_entry.key.status.success.object_path; + try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{}); + return; + } + const use_lld = build_options.have_llvm and base.options.use_lld; + if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static and + !base.options.target.isWasm()) + { + return base.linkAsArchive(comp); + } + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).flush(comp), + .elf => return @fieldParentPtr(Elf, "base", base).flush(comp), + .macho => return @fieldParentPtr(MachO, "base", base).flush(comp), + .c => return @fieldParentPtr(C, "base", base).flush(comp), + .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), + } + } + + /// Commit pending changes and write headers. Works based on `effectiveOutputMode` + /// rather than final output mode. + pub fn flushModule(base: *File, comp: *Compilation) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp), + .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp), + .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp), + .c => return @fieldParentPtr(C, "base", base).flushModule(comp), + .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), + } + } + + pub fn freeDecl(base: *File, decl: *Module.Decl) void { + switch (base.tag) { + .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl), + .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), + .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), + .c => unreachable, + .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), + } + } + + pub fn errorFlags(base: *File) ErrorFlags { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).error_flags, + .elf => return @fieldParentPtr(Elf, "base", base).error_flags, + .macho => return @fieldParentPtr(MachO, "base", base).error_flags, + .c => return .{ .no_entry_point_found = false }, + .wasm => return ErrorFlags{}, + } + } + + /// May be called before or after updateDecl, but must be called after + /// allocateDeclIndexes for any given Decl. + pub fn updateDeclExports( + base: *File, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, + ) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports), + .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports), + .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), + .c => return {}, + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), + } + } + + pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), + .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), + .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), + .c => unreachable, + .wasm => unreachable, + } + } + + fn linkAsArchive(base: *File, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = base.options.root_name, + .target = base.options.target, + .output_mode = .Obj, + }); + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + try base.flushModule(comp); + const obj_basename = base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + // This function follows the same pattern as link.Elf.linkWithLLD so if you want some + // insight as to what's going on here you can read that function body which is more + // well-commented. + + const id_symlink_basename = "llvm-ar.id"; + + base.releaseLock(); + + var ch = comp.cache_parent.obtain(); + defer ch.deinit(); + + try ch.addListOfFiles(base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try ch.addFile(entry.key.status.success.object_path, null); + } + try ch.addOptionalFile(module_obj_path); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try ch.hit(); + const digest = ch.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| b: { + log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + break :b prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("archive digest={} match - skipping invocation", .{digest}); + base.lock = ch.toOwnedLock(); + return; + } + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + + var object_files = std.ArrayList([*:0]const u8).init(base.allocator); + defer object_files.deinit(); + + try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 1); + for (base.options.objects) |obj_path| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path)); + } + for (comp.c_object_table.items()) |entry| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, entry.key.status.success.object_path)); + } + if (module_obj_path) |p| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); + } + + const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path}); + const full_out_path_z = try arena.dupeZ(u8, full_out_path); + + if (base.options.verbose_link) { + std.debug.print("ar rcs {}", .{full_out_path_z}); + for (object_files.items) |arg| { + std.debug.print(" {}", .{arg}); + } + std.debug.print("\n", .{}); + } + + const llvm = @import("llvm.zig"); + const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag); + const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type); + if (bad) return error.UnableToWriteArchive; + + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)}); + }; + + ch.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)}); + }; + + base.lock = ch.toOwnedLock(); + } + + pub const Tag = enum { + coff, + elf, + macho, + c, + wasm, + }; + + pub const ErrorFlags = struct { + no_entry_point_found: bool = false, + }; + + pub const C = @import("link/C.zig"); + pub const Coff = @import("link/Coff.zig"); + pub const Elf = @import("link/Elf.zig"); + pub const MachO = @import("link/MachO.zig"); + pub const Wasm = @import("link/Wasm.zig"); +}; + +pub fn determineMode(options: Options) fs.File.Mode { + // On common systems with a 0o022 umask, 0o777 will still result in a file created + // with 0o755 permissions, but it works appropriately if the system is configured + // more leniently. As another data point, C's fopen seems to open files with the + // 666 mode. + const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; + switch (options.effectiveOutputMode()) { + .Lib => return switch (options.link_mode) { + .Dynamic => executable_mode, + .Static => fs.File.default_mode, + }, + .Exe => return executable_mode, + .Obj => return fs.File.default_mode, + } +} diff --git a/src-self-hosted/link/C.zig b/src/link/C.zig similarity index 75% rename from src-self-hosted/link/C.zig rename to src/link/C.zig index 69eabd1f8b..467e10998e 100644 --- a/src-self-hosted/link/C.zig +++ b/src/link/C.zig @@ -3,9 +3,11 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); const fs = std.fs; const codegen = @import("../codegen/c.zig"); const link = @import("../link.zig"); +const trace = @import("../tracy.zig").trace; const File = link.File; const C = @This(); @@ -20,12 +22,15 @@ main: std.ArrayList(u8), called: std.StringHashMap(void), need_stddef: bool = false, need_stdint: bool = false, -error_msg: *Module.ErrorMsg = undefined, +error_msg: *Compilation.ErrorMsg = undefined, -pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { assert(options.object_format == .c); - const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) }); + if (options.use_llvm) return error.LLVMHasNoCBackend; + if (options.use_lld) return error.LLDHasNoCBackend; + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) }); errdefer file.close(); var c_file = try allocator.create(C); @@ -44,11 +49,11 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option .called = std.StringHashMap(void).init(allocator), }; - return &c_file.base; + return c_file; } pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { - self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args); + self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args); return error.AnalysisFail; } @@ -68,7 +73,14 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { }; } -pub fn flush(self: *C, module: *Module) !void { +pub fn flush(self: *C, comp: *Compilation) !void { + return self.flushModule(comp); +} + +pub fn flushModule(self: *C, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + const writer = self.base.file.?.writer(); try writer.writeAll(@embedFile("cbe.h")); var includes = false; diff --git a/src/link/Coff.zig b/src/link/Coff.zig new file mode 100644 index 0000000000..3f462582e7 --- /dev/null +++ b/src/link/Coff.zig @@ -0,0 +1,1220 @@ +const Coff = @This(); + +const std = @import("std"); +const log = std.log.scoped(.link); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const fs = std.fs; +const allocPrint = std.fmt.allocPrint; +const mem = std.mem; + +const trace = @import("../tracy.zig").trace; +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); +const link = @import("../link.zig"); +const build_options = @import("build_options"); +const Cache = @import("../Cache.zig"); +const mingw = @import("../mingw.zig"); + +const allocation_padding = 4 / 3; +const minimum_text_block_size = 64 * allocation_padding; + +const section_alignment = 4096; +const file_alignment = 512; +const image_base = 0x400_000; +const section_table_size = 2 * 40; +comptime { + assert(mem.isAligned(image_base, section_alignment)); +} + +pub const base_tag: link.File.Tag = .coff; + +const msdos_stub = @embedFile("msdos-stub.bin"); + +base: link.File, +ptr_width: PtrWidth, +error_flags: link.File.ErrorFlags = .{}, + +text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, +last_text_block: ?*TextBlock = null, + +/// Section table file pointer. +section_table_offset: u32 = 0, +/// Section data file pointer. +section_data_offset: u32 = 0, +/// Optiona header file pointer. +optional_header_offset: u32 = 0, + +/// Absolute virtual address of the offset table when the executable is loaded in memory. +offset_table_virtual_address: u32 = 0, +/// Current size of the offset table on disk, must be a multiple of `file_alignment` +offset_table_size: u32 = 0, +/// Contains absolute virtual addresses +offset_table: std.ArrayListUnmanaged(u64) = .{}, +/// Free list of offset table indices +offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, + +/// Virtual address of the entry point procedure relative to `image_base` +entry_addr: ?u32 = null, + +/// Absolute virtual address of the text section when the executable is loaded in memory. +text_section_virtual_address: u32 = 0, +/// Current size of the `.text` section on disk, must be a multiple of `file_alignment` +text_section_size: u32 = 0, + +offset_table_size_dirty: bool = false, +text_section_size_dirty: bool = false, +/// This flag is set when the virtual size of the whole image file when loaded in memory has changed +/// and needs to be updated in the optional header. +size_of_image_dirty: bool = false, + +pub const PtrWidth = enum { p32, p64 }; + +pub const TextBlock = struct { + /// Offset of the code relative to the start of the text section + text_offset: u32, + /// Used size of the text block + size: u32, + /// This field is undefined for symbols with size = 0. + offset_table_index: u32, + /// Points to the previous and next neighbors, based on the `text_offset`. + /// This can be used to find, for example, the capacity of this `TextBlock`. + prev: ?*TextBlock, + next: ?*TextBlock, + + pub const empty = TextBlock{ + .text_offset = 0, + .size = 0, + .offset_table_index = undefined, + .prev = null, + .next = null, + }; + + /// Returns how much room there is to grow in virtual address space. + fn capacity(self: TextBlock) u64 { + if (self.next) |next| { + return next.text_offset - self.text_offset; + } + // This is the last block, the capacity is only limited by the address space. + return std.math.maxInt(u32) - self.text_offset; + } + + fn freeListEligible(self: TextBlock) bool { + // No need to keep a free list node for the last block. + const next = self.next orelse return false; + const cap = next.text_offset - self.text_offset; + const ideal_cap = self.size * allocation_padding; + if (cap <= ideal_cap) return false; + const surplus = cap - ideal_cap; + return surplus >= minimum_text_block_size; + } + + /// Absolute virtual address of the text block when the file is loaded in memory. + fn getVAddr(self: TextBlock, coff: Coff) u32 { + return coff.text_section_virtual_address + self.text_offset; + } +}; + +pub const SrcFn = void; + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { + assert(options.object_format == .coff); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO + if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); + errdefer file.close(); + + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); + + self.base.file = file; + + // TODO Write object specific relocations, COFF symbol table, then enable object file output. + switch (options.output_mode) { + .Exe => {}, + .Obj => return error.TODOImplementWritingObjFiles, + .Lib => return error.TODOImplementWritingLibFiles, + } + + var coff_file_header_offset: u32 = 0; + if (options.output_mode == .Exe) { + // Write the MS-DOS stub and the PE signature + try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0); + coff_file_header_offset = msdos_stub.len + 4; + } + + // COFF file header + const data_directory_count = 0; + var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined; + var index: usize = 0; + + const machine = self.base.options.target.cpu.arch.toCoffMachine(); + if (machine == .Unknown) { + return error.UnsupportedCOFFArchitecture; + } + mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine)); + index += 2; + + // Number of sections (we only use .got, .text) + mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); + index += 2; + // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32) + mem.set(u8, hdr_data[index..][0..12], 0); + index += 12; + + const optional_header_size = switch (options.output_mode) { + .Exe => data_directory_count * 8 + switch (self.ptr_width) { + .p32 => @as(u16, 96), + .p64 => 112, + }, + else => 0, + }; + + const section_table_offset = coff_file_header_offset + 20 + optional_header_size; + const default_offset_table_size = file_alignment; + const default_size_of_code = 0; + + self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); + const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); + self.offset_table_virtual_address = image_base + section_data_relative_virtual_address; + self.offset_table_size = default_offset_table_size; + self.section_table_offset = section_table_offset; + self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment; + self.text_section_size = default_size_of_code; + + // Size of file when loaded in memory + const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment); + + mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); + index += 2; + + // Characteristics + var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary + if (options.output_mode == .Exe) { + characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE; + } + switch (self.ptr_width) { + .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE, + .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE, + } + mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics); + index += 2; + + assert(index == 20); + try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset); + + if (options.output_mode == .Exe) { + self.optional_header_offset = coff_file_header_offset + 20; + // Optional header + index = 0; + mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { + .p32 => @as(u16, 0x10b), + .p64 => 0x20b, + }); + index += 2; + + // Linker version (u8 + u8) + mem.set(u8, hdr_data[index..][0..2], 0); + index += 2; + + // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32) + mem.set(u8, hdr_data[index..][0..20], 0); + index += 20; + + if (self.ptr_width == .p32) { + // Base of data relative to the image base (UNUSED) + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + + // Image base address + mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base); + index += 4; + } else { + // Image base address + mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base); + index += 8; + } + + // Section alignment + mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment); + index += 4; + // File alignment + mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment); + index += 4; + // Required OS version, 6.0 is vista + mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); + index += 2; + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); + index += 2; + // Image version + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + // Required subsystem version, same as OS version + mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); + index += 2; + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); + index += 2; + // Reserved zeroes (u32) + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); + index += 4; + // CheckSum (u32) + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + // Subsystem, TODO: Let users specify the subsystem, always CUI for now + mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); + index += 2; + // DLL characteristics + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0); + index += 2; + + switch (self.ptr_width) { + .p32 => { + // Size of stack reserve + commit + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); + index += 4; + // Size of heap reserve + commit + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); + index += 4; + }, + .p64 => { + // Size of stack reserve + commit + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); + index += 8; + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); + index += 8; + // Size of heap reserve + commit + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); + index += 8; + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); + index += 8; + }, + } + + // Reserved zeroes + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + + // Number of data directories + mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); + index += 4; + // Initialize data directories to zero + mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0); + index += data_directory_count * 8; + + assert(index == optional_header_size); + } + + // Write section table. + // First, the .got section + hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*; + index += 8; + if (options.output_mode == .Exe) { + // Virtual size (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); + index += 4; + // Virtual address (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base); + index += 4; + } else { + mem.set(u8, hdr_data[index..][0..8], 0); + index += 8; + } + // Size of raw data (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); + index += 4; + // File pointer to the start of the section + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); + index += 4; + // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) + mem.set(u8, hdr_data[index..][0..12], 0); + index += 12; + // Section flags + mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ); + index += 4; + // Then, the .text section + hdr_data[index..][0..8].* = ".text\x00\x00\x00".*; + index += 8; + if (options.output_mode == .Exe) { + // Virtual size (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); + index += 4; + // Virtual address (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base); + index += 4; + } else { + mem.set(u8, hdr_data[index..][0..8], 0); + index += 8; + } + // Size of raw data (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); + index += 4; + // File pointer to the start of the section + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size); + index += 4; + // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) + mem.set(u8, hdr_data[index..][0..12], 0); + index += 12; + // Section flags + mem.writeIntLittle( + u32, + hdr_data[index..][0..4], + std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE, + ); + index += 4; + + assert(index == optional_header_size + section_table_size); + try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset); + try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code); + + return self; +} + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0...32 => .p32, + 33...64 => .p64, + else => return error.UnsupportedCOFFArchitecture, + }; + const self = try gpa.create(Coff); + self.* = .{ + .base = .{ + .tag = .coff, + .options = options, + .allocator = gpa, + .file = null, + }, + .ptr_width = ptr_width, + }; + return self; +} + +pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { + try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); + + if (self.offset_table_free_list.popOrNull()) |i| { + decl.link.coff.offset_table_index = i; + } else { + decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len); + _ = self.offset_table.addOneAssumeCapacity(); + + const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; + if (self.offset_table.items.len > self.offset_table_size / entry_size) { + self.offset_table_size_dirty = true; + } + } + + self.offset_table.items[decl.link.coff.offset_table_index] = 0; +} + +fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { + const new_block_min_capacity = new_block_size * allocation_padding; + + // We use these to indicate our intention to update metadata, placing the new block, + // and possibly removing a free list node. + // It would be simpler to do it inside the for loop below, but that would cause a + // problem if an error was returned later in the function. So this action + // is actually carried out at the end of the function, when errors are no longer possible. + var block_placement: ?*TextBlock = null; + var free_list_removal: ?usize = null; + + const vaddr = blk: { + var i: usize = 0; + while (i < self.text_block_free_list.items.len) { + const free_block = self.text_block_free_list.items[i]; + + const next_block_text_offset = free_block.text_offset + free_block.capacity(); + const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address; + if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) { + block_placement = free_block; + + const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity; + if (remaining_capacity < minimum_text_block_size) { + free_list_removal = i; + } + + break :blk new_block_text_offset + self.text_section_virtual_address; + } else { + if (!free_block.freeListEligible()) { + _ = self.text_block_free_list.swapRemove(i); + } else { + i += 1; + } + continue; + } + } else if (self.last_text_block) |last| { + const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment); + block_placement = last; + break :blk new_block_vaddr; + } else { + break :blk self.text_section_virtual_address; + } + }; + + const expand_text_section = block_placement == null or block_placement.?.next == null; + if (expand_text_section) { + const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment)); + if (needed_size > self.text_section_size) { + const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment); + const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment); + if (current_text_section_virtual_size != new_text_section_virtual_size) { + self.size_of_image_dirty = true; + // Write new virtual size + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, new_text_section_virtual_size); + try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8); + } + + self.text_section_size = needed_size; + self.text_section_size_dirty = true; + } + self.last_text_block = text_block; + } + text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address); + text_block.size = @intCast(u32, new_block_size); + + // This function can also reallocate a text block. + // In this case we need to "unplug" it from its previous location before + // plugging it in to its new location. + if (text_block.prev) |prev| { + prev.next = text_block.next; + } + if (text_block.next) |next| { + next.prev = text_block.prev; + } + + if (block_placement) |big_block| { + text_block.prev = big_block; + text_block.next = big_block.next; + big_block.next = text_block; + } else { + text_block.prev = null; + text_block.next = null; + } + if (free_list_removal) |i| { + _ = self.text_block_free_list.swapRemove(i); + } + return vaddr; +} + +fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { + const block_vaddr = text_block.getVAddr(self.*); + const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr; + const need_realloc = !align_ok or new_block_size > text_block.capacity(); + if (!need_realloc) return @as(u64, block_vaddr); + return self.allocateTextBlock(text_block, new_block_size, alignment); +} + +fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void { + text_block.size = @intCast(u32, new_block_size); + if (text_block.capacity() - text_block.size >= minimum_text_block_size) { + self.text_block_free_list.append(self.base.allocator, text_block) catch {}; + } +} + +fn freeTextBlock(self: *Coff, text_block: *TextBlock) void { + var already_have_free_list_node = false; + { + var i: usize = 0; + // TODO turn text_block_free_list into a hash map + while (i < self.text_block_free_list.items.len) { + if (self.text_block_free_list.items[i] == text_block) { + _ = self.text_block_free_list.swapRemove(i); + continue; + } + if (self.text_block_free_list.items[i] == text_block.prev) { + already_have_free_list_node = true; + } + i += 1; + } + } + if (self.last_text_block == text_block) { + self.last_text_block = text_block.prev; + } + if (text_block.prev) |prev| { + prev.next = text_block.next; + + if (!already_have_free_list_node and prev.freeListEligible()) { + // The free list is heuristics, it doesn't have to be perfect, so we can + // ignore the OOM here. + self.text_block_free_list.append(self.base.allocator, prev) catch {}; + } + } + + if (text_block.next) |next| { + next.prev = text_block.prev; + } +} + +fn writeOffsetTableEntry(self: *Coff, index: usize) !void { + const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; + const endian = self.base.options.target.cpu.arch.endian(); + + const offset_table_start = self.section_data_offset; + if (self.offset_table_size_dirty) { + const current_raw_size = self.offset_table_size; + const new_raw_size = self.offset_table_size * 2; + log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size }); + + // Move the text section to a new place in the executable + const current_text_section_start = self.section_data_offset + current_raw_size; + const new_text_section_start = self.section_data_offset + new_raw_size; + + const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size); + if (amt != self.text_section_size) return error.InputOutput; + + // Write the new raw size in the .got header + var buf: [8]u8 = undefined; + mem.writeIntLittle(u32, buf[0..4], new_raw_size); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16); + // Write the new .text section file offset in the .text section header + mem.writeIntLittle(u32, buf[0..4], new_text_section_start); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20); + + const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); + const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment); + // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section + // and the virutal size of the `.got` section + + if (new_virtual_size != current_virtual_size) { + log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size }); + self.size_of_image_dirty = true; + const va_offset = new_virtual_size - current_virtual_size; + + // Write .got virtual size + mem.writeIntLittle(u32, buf[0..4], new_virtual_size); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8); + + // Write .text new virtual address + self.text_section_virtual_address = self.text_section_virtual_address + va_offset; + mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); + + // Fix the VAs in the offset table + for (self.offset_table.items) |*va, idx| { + if (va.* != 0) { + va.* += va_offset; + + switch (entry_size) { + 4 => { + mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian); + try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size); + }, + 8 => { + mem.writeInt(u64, &buf, va.*, endian); + try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size); + }, + else => unreachable, + } + } + } + } + self.offset_table_size = new_raw_size; + self.offset_table_size_dirty = false; + } + // Write the new entry + switch (entry_size) { + 4 => { + var buf: [4]u8 = undefined; + mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); + try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); + }, + 8 => { + var buf: [8]u8 = undefined; + mem.writeInt(u64, &buf, self.offset_table.items[index], endian); + try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); + }, + else => unreachable, + } +} + +pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { + // TODO COFF/PE debug information + // TODO Implement exports + const tracy = trace(@src()); + defer tracy.end(); + + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + + const typed_value = decl.typed_value.most_recent.typed_value; + const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return; + }, + }; + + const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); + const curr_size = decl.link.coff.size; + if (curr_size != 0) { + const capacity = decl.link.coff.capacity(); + const need_realloc = code.len > capacity or + !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); + if (need_realloc) { + const curr_vaddr = self.getDeclVAddr(decl); + const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); + log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); + if (vaddr != curr_vaddr) { + log.debug(" (writing new offset table entry)\n", .{}); + self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; + try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); + } + } else if (code.len < curr_size) { + self.shrinkTextBlock(&decl.link.coff, code.len); + } + } else { + const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); + log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); + errdefer self.freeTextBlock(&decl.link.coff); + self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; + try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); + } + + // Write the code into the file + try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset); + + // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. + const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl, decl_exports); +} + +pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { + // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. + self.freeTextBlock(&decl.link.coff); + self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; +} + +pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void { + for (exports) |exp| { + if (exp.options.section) |section_name| { + if (!mem.eql(u8, section_name, ".text")) { + try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); + module.failed_exports.putAssumeCapacityNoClobber( + exp, + try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), + ); + continue; + } + } + if (mem.eql(u8, exp.options.name, "_start")) { + self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base; + } else { + try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); + module.failed_exports.putAssumeCapacityNoClobber( + exp, + try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}), + ); + continue; + } + } +} + +pub fn flush(self: *Coff, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *Coff, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + if (self.text_section_size_dirty) { + // Write the new raw size in the .text header + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, self.text_section_size); + try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16); + try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size); + self.text_section_size_dirty = false; + } + + if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { + const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment); + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, new_size_of_image); + try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); + self.size_of_image_dirty = false; + } + + if (self.entry_addr == null and self.base.options.output_mode == .Exe) { + log.debug("flushing. no_entry_point_found = true\n", .{}); + self.error_flags.no_entry_point_found = true; + } else { + log.debug("flushing. no_entry_point_found = false\n", .{}); + self.error_flags.no_entry_point_found = false; + + if (self.base.options.output_mode == .Exe) { + // Write AddressOfEntryPoint + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, self.entry_addr.?); + try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16); + } + } +} + +fn linkWithLLD(self: *Coff, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; + const target = self.base.options.target; + + // See link/Elf.zig for comments on how this mechanism works. + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + self.base.releaseLock(); + + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + man.hash.addOptional(self.base.options.stack_size_override); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + if (self.base.options.link_libc) { + man.hash.add(self.base.options.libc_installation != null); + if (self.base.options.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + if (target.abi == .msvc) { + man.hash.addBytes(libc_installation.msvc_lib_dir.?); + man.hash.addBytes(libc_installation.kernel32_lib_dir.?); + } + } + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.addOptional(self.base.options.subsystem); + man.hash.add(self.base.options.is_test); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("COFF LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + + if (self.base.options.output_mode == .Obj) { + // LLD's COFF driver does not support the equvialent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (self.base.options.objects.len != 0) + break :blk self.base.options.objects[0]; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.items()[0].key.status.success.object_path; + + if (module_obj_path) |p| + break :blk p; + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + // This can happen when using --enable-cache and using the stage1 backend. In this case + // we can skip the file copy. + if (!mem.eql(u8, the_object_path, full_out_path)) { + try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); + } + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + + try argv.append("-ERRORLIMIT:0"); + try argv.append("-NOLOGO"); + if (!self.base.options.strip) { + try argv.append("-DEBUG"); + } + if (self.base.options.output_mode == .Exe) { + const stack_size = self.base.options.stack_size_override orelse 16777216; + try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); + } + + if (target.cpu.arch == .i386) { + try argv.append("-MACHINE:X86"); + } else if (target.cpu.arch == .x86_64) { + try argv.append("-MACHINE:X64"); + } else if (target.cpu.arch.isARM()) { + if (target.cpu.arch.ptrBitWidth() == 32) { + try argv.append("-MACHINE:ARM"); + } else { + try argv.append("-MACHINE:ARM64"); + } + } + + if (is_dyn_lib) { + try argv.append("-DLL"); + } + + try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); + + if (self.base.options.link_libc) { + if (self.base.options.libc_installation) |libc_installation| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); + + if (target.abi == .msvc) { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); + } + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); + } + + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + const resolved_subsystem: ?std.Target.SubSystem = blk: { + if (self.base.options.subsystem) |explicit| break :blk explicit; + switch (target.os.tag) { + .windows => { + if (self.base.options.module) |module| { + if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) + break :blk null; + if (module.stage1_flags.have_c_main or self.base.options.is_test or + module.stage1_flags.have_winmain_crt_startup or + module.stage1_flags.have_wwinmain_crt_startup) + { + break :blk .Console; + } + if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain) + break :blk .Windows; + } + }, + .uefi => break :blk .EfiApplication, + else => {}, + } + break :blk null; + }; + const Mode = enum { uefi, win32 }; + const mode: Mode = mode: { + if (resolved_subsystem) |subsystem| switch (subsystem) { + .Console => { + try argv.append("-SUBSYSTEM:console"); + break :mode .win32; + }, + .EfiApplication => { + try argv.append("-SUBSYSTEM:efi_application"); + break :mode .uefi; + }, + .EfiBootServiceDriver => { + try argv.append("-SUBSYSTEM:efi_boot_service_driver"); + break :mode .uefi; + }, + .EfiRom => { + try argv.append("-SUBSYSTEM:efi_rom"); + break :mode .uefi; + }, + .EfiRuntimeDriver => { + try argv.append("-SUBSYSTEM:efi_runtime_driver"); + break :mode .uefi; + }, + .Native => { + try argv.append("-SUBSYSTEM:native"); + break :mode .win32; + }, + .Posix => { + try argv.append("-SUBSYSTEM:posix"); + break :mode .win32; + }, + .Windows => { + try argv.append("-SUBSYSTEM:windows"); + break :mode .win32; + }, + } else if (target.os.tag == .uefi) { + break :mode .uefi; + } else { + break :mode .win32; + } + }; + + switch (mode) { + .uefi => try argv.appendSlice(&[_][]const u8{ + "-BASE:0", + "-ENTRY:EfiMain", + "-OPT:REF", + "-SAFESEH:NO", + "-MERGE:.rdata=.data", + "-ALIGN:32", + "-NODEFAULTLIB", + "-SECTION:.xdata,D", + }), + .win32 => { + if (link_in_crt) { + if (target.abi.isGnu()) { + try argv.append("-lldmingw"); + + if (target.cpu.arch == .i386) { + try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); + } else { + try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); + } + + if (is_dyn_lib) { + try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.o")); + } else { + try argv.append(try comp.get_libc_crt_file(arena, "crt2.o")); + } + + try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); + try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib")); + try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib")); + + for (mingw.always_link_libs) |name| { + if (!self.base.options.system_libs.contains(name)) { + const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); + try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); + } + } + } else { + const lib_str = switch (self.base.options.link_mode) { + .Dynamic => "", + .Static => "lib", + }; + const d_str = switch (self.base.options.optimize_mode) { + .Debug => "d", + else => "", + }; + switch (self.base.options.link_mode) { + .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), + .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), + } + + try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str })); + try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str })); + + //Visual C++ 2015 Conformance Changes + //https://msdn.microsoft.com/en-us/library/bb531344.aspx + try argv.append("legacy_stdio_definitions.lib"); + + // msvcrt depends on kernel32 and ntdll + try argv.append("kernel32.lib"); + try argv.append("ntdll.lib"); + } + } else { + try argv.append("-NODEFAULTLIB"); + if (!is_lib) { + if (self.base.options.module) |module| { + if (module.stage1_flags.have_winmain) { + try argv.append("-ENTRY:WinMain"); + } else if (module.stage1_flags.have_wwinmain) { + try argv.append("-ENTRY:wWinMain"); + } else if (module.stage1_flags.have_wwinmain_crt_startup) { + try argv.append("-ENTRY:wWinMainCRTStartup"); + } else { + try argv.append("-ENTRY:WinMainCRTStartup"); + } + } else { + try argv.append("-ENTRY:WinMainCRTStartup"); + } + } + } + }, + } + + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } + + // compiler-rt and libc + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + // MSVC compiler_rt is missing some stuff, so we build it unconditionally but + // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + for (self.base.options.system_libs.items()) |entry| { + const lib_basename = try allocPrint(arena, "{s}.lib", .{entry.key}); + if (comp.crt_files.get(lib_basename)) |crt_file| { + try argv.append(crt_file.full_object_path); + } else { + try argv.append(lib_basename); + } + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .coff = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .coff = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .COFF, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + coff: *Coff, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { + return self.text_section_virtual_address + decl.link.coff.text_offset; +} + +pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { + // TODO Implement this +} + +pub fn deinit(self: *Coff) void { + self.text_block_free_list.deinit(self.base.allocator); + self.offset_table.deinit(self.base.allocator); + self.offset_table_free_list.deinit(self.base.allocator); +} diff --git a/src-self-hosted/link/Elf.zig b/src/link/Elf.zig similarity index 83% rename from src-self-hosted/link/Elf.zig rename to src/link/Elf.zig index e5acde947c..38b9b1acca 100644 --- a/src-self-hosted/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,23 +1,29 @@ +const Elf = @This(); + const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; -const ir = @import("../ir.zig"); -const Module = @import("../Module.zig"); const fs = std.fs; const elf = std.elf; -const codegen = @import("../codegen.zig"); const log = std.log.scoped(.link); const DW = std.dwarf; -const trace = @import("../tracy.zig").trace; const leb128 = std.debug.leb; + +const ir = @import("../ir.zig"); +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); +const trace = @import("../tracy.zig").trace; const Package = @import("../Package.zig"); const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; const link = @import("../link.zig"); const File = link.File; -const Elf = @This(); const build_options = @import("build_options"); +const target_util = @import("../target.zig"); +const glibc = @import("../glibc.zig"); +const Cache = @import("../Cache.zig"); const default_entry_addr = 0x8000000; @@ -28,7 +34,7 @@ pub const base_tag: File.Tag = .elf; base: File, -ptr_width: enum { p32, p64 }, +ptr_width: PtrWidth, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. @@ -130,6 +136,8 @@ const alloc_den = 3; const minimum_text_block_size = 64; const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; +pub const PtrWidth = enum { p32, p64 }; + pub const TextBlock = struct { /// Each decl always gets a local symbol with the fully qualified name. /// The vaddr and size are found here directly. @@ -216,74 +224,23 @@ pub const SrcFn = struct { }; }; -pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf { assert(options.object_format == .elf); - const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); + if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); errdefer file.close(); - var elf_file = try allocator.create(Elf); - errdefer allocator.destroy(elf_file); + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); - elf_file.* = openFile(allocator, file, options) catch |err| switch (err) { - error.IncrFailed => try createFile(allocator, file, options), - else => |e| return e, - }; - - return &elf_file.base; -} - -/// Returns error.IncrFailed if incremental update could not be performed. -fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { - switch (options.output_mode) { - .Exe => {}, - .Obj => {}, - .Lib => return error.IncrFailed, - } - var self: Elf = .{ - .base = .{ - .file = file, - .tag = .elf, - .options = options, - .allocator = allocator, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 0 ... 32 => .p32, - 33 ... 64 => .p64, - else => return error.UnsupportedELFArchitecture, - }, - }; - errdefer self.deinit(); - - // TODO implement reading the elf file - return error.IncrFailed; - //try self.populateMissingMetadata(); - //return self; -} - -/// Truncates the existing file contents and overwrites the contents. -/// Returns an error if `file` is not already open with +read +write +seek abilities. -fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { - switch (options.output_mode) { - .Exe => {}, - .Obj => {}, - .Lib => return error.TODOImplementWritingLibFiles, - } - var self: Elf = .{ - .base = .{ - .tag = .elf, - .options = options, - .allocator = allocator, - .file = file, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 0 ... 32 => .p32, - 33 ... 64 => .p64, - else => return error.UnsupportedELFArchitecture, - }, - .shdr_table_dirty = true, - }; - errdefer self.deinit(); + self.base.file = file; + self.shdr_table_dirty = true; // Index 0 is always a null symbol. try self.local_symbols.append(allocator, .{ @@ -314,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf return self; } +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0 ... 32 => .p32, + 33 ... 64 => .p64, + else => return error.UnsupportedELFArchitecture, + }; + const self = try gpa.create(Elf); + self.* = .{ + .base = .{ + .tag = .elf, + .options = options, + .allocator = gpa, + .file = null, + }, + .ptr_width = ptr_width, + }; + return self; +} + pub fn deinit(self: *Elf) void { self.sections.deinit(self.base.allocator); self.program_headers.deinit(self.base.allocator); @@ -738,8 +714,26 @@ pub const abbrev_base_type = 4; pub const abbrev_pad1 = 5; pub const abbrev_parameter = 6; -/// Commit pending changes and write headers. -pub fn flush(self: *Elf, module: *Module) !void { +pub fn flush(self: *Elf, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *Elf, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the + // Zig source code. + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); const ptr_width_bytes: u8 = self.ptrWidthBytes(); @@ -861,8 +855,8 @@ pub fn flush(self: *Elf, module: *Module) !void { }, } // Write the form for the compile unit, which must match the abbrev table above. - const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); - const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); + const name_strp = try self.makeDebugString(module.root_pkg.root_src_path); + const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse "."); const producer_strp = try self.makeDebugString(link.producer_string); // Currently only one compilation unit is supported, so the address range is simply // identical to the main program header virtual address and memory size. @@ -1031,7 +1025,7 @@ pub fn flush(self: *Elf, module: *Module) !void { 0, // include_directories (none except the compilation unit cwd) }); // file_names[0] - di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name + di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name di_buf.appendSliceAssumeCapacity(&[_]u8{ 0, // null byte for the relative path name 0, // directory_index @@ -1195,7 +1189,7 @@ pub fn flush(self: *Elf, module: *Module) !void { } self.shdr_table_dirty = false; } - if (self.entry_addr == null and self.base.options.output_mode == .Exe) { + if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { log.debug("flushing. no_entry_point_found = true\n", .{}); self.error_flags.no_entry_point_found = true; } else { @@ -1216,6 +1210,449 @@ pub fn flush(self: *Elf, module: *Module) !void { assert(!self.debug_strtab_dirty); } +fn linkWithLLD(self: *Elf, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_obj = self.base.options.output_mode == .Obj; + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const have_dynamic_linker = self.base.options.link_libc and + self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; + const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; + const target = self.base.options.target; + const gc_sections = self.base.options.gc_sections orelse !is_obj; + const stack_size = self.base.options.stack_size_override orelse 16777216; + const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; + + // Here we want to determine whether we can save time by not invoking LLD when the + // output is unchanged. None of the linker options or the object files that are being + // linked are in the hash that namespaces the directory we are outputting to. Therefore, + // we must hash those now, and the resulting digest will form the "id" of the linking + // job we are about to perform. + // After a successful link, we store the id in the metadata of a symlink named "id.txt" in + // the artifact directory. So, now, we check if this symlink exists, and if it matches + // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + self.base.releaseLock(); + + try man.addOptionalFile(self.base.options.linker_script); + try man.addOptionalFile(self.base.options.version_script); + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + // We can skip hashing libc and libc++ components that we are in charge of building from Zig + // installation sources because they are always a product of the compiler version + target information. + man.hash.add(stack_size); + man.hash.add(gc_sections); + man.hash.add(self.base.options.eh_frame_hdr); + man.hash.add(self.base.options.rdynamic); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.addListOfBytes(self.base.options.rpath_list); + man.hash.add(self.base.options.each_lib_rpath); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + man.hash.add(self.base.options.z_nodelete); + man.hash.add(self.base.options.z_defs); + if (self.base.options.link_libc) { + man.hash.add(self.base.options.libc_installation != null); + if (self.base.options.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + } + if (have_dynamic_linker) { + man.hash.addOptionalBytes(self.base.options.dynamic_linker); + } + } + if (is_dyn_lib) { + man.hash.addOptionalBytes(self.base.options.override_soname); + man.hash.addOptional(self.base.options.version); + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.add(allow_shlib_undefined); + man.hash.add(self.base.options.bind_global_refs_locally); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)}); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("ELF LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest}); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + if (is_obj) { + try argv.append("-r"); + } + + try argv.append("-error-limit=0"); + + if (self.base.options.output_mode == .Exe) { + try argv.append("-z"); + try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size})); + } + + if (self.base.options.linker_script) |linker_script| { + try argv.append("-T"); + try argv.append(linker_script); + } + + if (gc_sections) { + try argv.append("--gc-sections"); + } + + if (self.base.options.eh_frame_hdr) { + try argv.append("--eh-frame-hdr"); + } + + if (self.base.options.rdynamic) { + try argv.append("--export-dynamic"); + } + + try argv.appendSlice(self.base.options.extra_lld_args); + + if (self.base.options.z_nodelete) { + try argv.append("-z"); + try argv.append("nodelete"); + } + if (self.base.options.z_defs) { + try argv.append("-z"); + try argv.append("defs"); + } + + if (getLDMOption(target)) |ldm| { + // Any target ELF will use the freebsd osabi if suffixed with "_fbsd". + const arg = if (target.os.tag == .freebsd) + try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm}) + else + ldm; + try argv.append("-m"); + try argv.append(arg); + } + + if (self.base.options.link_mode == .Static) { + if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) { + try argv.append("-Bstatic"); + } else { + try argv.append("-static"); + } + } else if (is_dyn_lib) { + try argv.append("-shared"); + } + + if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { + try argv.append("-pie"); + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + try argv.append("-o"); + try argv.append(full_out_path); + + if (link_in_crt) { + const crt1o: []const u8 = o: { + if (target.os.tag == .netbsd) { + break :o "crt0.o"; + } else if (target.isAndroid()) { + if (self.base.options.link_mode == .Dynamic) { + break :o "crtbegin_dynamic.o"; + } else { + break :o "crtbegin_static.o"; + } + } else if (self.base.options.link_mode == .Static) { + break :o "crt1.o"; + } else { + break :o "Scrt1.o"; + } + }; + try argv.append(try comp.get_libc_crt_file(arena, crt1o)); + if (target_util.libc_needs_crti_crtn(target)) { + try argv.append(try comp.get_libc_crt_file(arena, "crti.o")); + } + } + + // rpaths + var rpath_table = std.StringHashMap(void).init(self.base.allocator); + defer rpath_table.deinit(); + for (self.base.options.rpath_list) |rpath| { + if ((try rpath_table.fetchPut(rpath, {})) == null) { + try argv.append("-rpath"); + try argv.append(rpath); + } + } + if (self.base.options.each_lib_rpath) { + var test_path = std.ArrayList(u8).init(self.base.allocator); + defer test_path.deinit(); + for (self.base.options.lib_dirs) |lib_dir_path| { + for (self.base.options.system_libs.items()) |link_lib| { + test_path.shrinkRetainingCapacity(0); + const sep = fs.path.sep_str; + try test_path.writer().print("{}" ++ sep ++ "lib{}.so", .{ lib_dir_path, link_lib }); + fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) { + try argv.append("-rpath"); + try argv.append(lib_dir_path); + } + } + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append("-L"); + try argv.append(lib_dir); + } + + if (self.base.options.link_libc) { + if (self.base.options.libc_installation) |libc_installation| { + try argv.append("-L"); + try argv.append(libc_installation.crt_dir.?); + } + + if (have_dynamic_linker) { + if (self.base.options.dynamic_linker) |dynamic_linker| { + try argv.append("-dynamic-linker"); + try argv.append(dynamic_linker); + } + } + } + + if (is_dyn_lib) { + const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver| + try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major}) + else + try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name}); + try argv.append("-soname"); + try argv.append(soname); + + if (self.base.options.version_script) |version_script| { + try argv.append("-version-script"); + try argv.append(version_script); + } + } + + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + // compiler-rt and libc + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + // Shared libraries. + const system_libs = self.base.options.system_libs.items(); + try argv.ensureCapacity(argv.items.len + system_libs.len); + for (system_libs) |entry| { + const link_lib = entry.key; + // By this time, we depend on these libs being dynamically linked libraries and not static libraries + // (the check for that needs to be earlier), but they could be full paths to .so files, in which + // case we want to avoid prepending "-l". + const ext = Compilation.classifyFileExt(link_lib); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + argv.appendAssumeCapacity(arg); + } + + if (!is_obj) { + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } + + // libc dep + if (self.base.options.link_libc) { + if (self.base.options.libc_installation != null) { + if (self.base.options.link_mode == .Static) { + try argv.append("--start-group"); + try argv.append("-lc"); + try argv.append("-lm"); + try argv.append("--end-group"); + } else { + try argv.append("-lc"); + try argv.append("-lm"); + } + + if (target.os.tag == .freebsd or target.os.tag == .netbsd) { + try argv.append("-lpthread"); + } + } else if (target.isGnuLibC()) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + for (glibc.libs) |lib| { + const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ + comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, + }); + try argv.append(lib_path); + } + try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + } else if (target.isMusl()) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); + } else if (self.base.options.link_libcpp) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } else { + unreachable; // Compiler was supposed to emit an error for not being able to provide libc. + } + } + } + + // crt end + if (link_in_crt) { + if (target.isAndroid()) { + try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o")); + } else if (target_util.libc_needs_crti_crtn(target)) { + try argv.append(try comp.get_libc_crt_file(arena, "crtn.o")); + } + } + + if (allow_shlib_undefined) { + try argv.append("--allow-shlib-undefined"); + } + + if (self.base.options.bind_global_refs_locally) { + try argv.append("-Bsymbolic"); + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + // Oh, snapplesauce! We need null terminated argv. + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .elf = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .elf = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) }); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + elf: *Elf, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { const target_endian = self.base.options.target.cpu.arch.endian(); switch (self.ptr_width) { @@ -1255,7 +1692,7 @@ fn writeElfHeader(self: *Elf) !void { assert(index == 16); - const elf_type = switch (self.base.options.output_mode) { + const elf_type = switch (self.base.options.effectiveOutputMode()) { .Exe => elf.ET.EXEC, .Obj => elf.ET.REL, .Lib => switch (self.base.options.link_mode) { @@ -2104,7 +2541,7 @@ pub fn updateDeclExports( try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), + try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), ); continue; } @@ -2122,7 +2559,7 @@ pub fn updateDeclExports( try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), + try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), ); continue; }, @@ -2426,12 +2863,13 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { const file_name_entry_format_count = 1; const directory_count = 1; const file_name_count = 1; + const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + directory_count * 8 + file_name_count * 8 + // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. - self.base.options.root_pkg.root_src_dir_path.len + - self.base.options.root_pkg.root_src_path.len); + root_src_dir_path_len + + self.base.options.module.?.root_pkg.root_src_path.len); } fn dbgInfoNeededHeaderBytes(self: Elf) u32 { @@ -2630,3 +3068,33 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { .sh_entsize = @intCast(u32, shdr.sh_entsize), }; } + +fn getLDMOption(target: std.Target) ?[]const u8 { + switch (target.cpu.arch) { + .i386 => return "elf_i386", + .aarch64 => return "aarch64linux", + .aarch64_be => return "aarch64_be_linux", + .arm, .thumb => return "armelf_linux_eabi", + .armeb, .thumbeb => return "armebelf_linux_eabi", + .powerpc => return "elf32ppclinux", + .powerpc64 => return "elf64ppc", + .powerpc64le => return "elf64lppc", + .sparc, .sparcel => return "elf32_sparc", + .sparcv9 => return "elf64_sparc", + .mips => return "elf32btsmip", + .mipsel => return "elf32ltsmip", + .mips64 => return "elf64btsmip", + .mips64el => return "elf64ltsmip", + .s390x => return "elf64_s390", + .x86_64 => { + if (target.abi == .gnux32) { + return "elf32_x86_64"; + } else { + return "elf_x86_64"; + } + }, + .riscv32 => return "elf32lriscv", + .riscv64 => return "elf64lriscv", + else => return null, + } +} diff --git a/src-self-hosted/link/MachO.zig b/src/link/MachO.zig similarity index 59% rename from src-self-hosted/link/MachO.zig rename to src/link/MachO.zig index 13932e514a..961b64a840 100644 --- a/src-self-hosted/link/MachO.zig +++ b/src/link/MachO.zig @@ -9,12 +9,16 @@ const macho = std.macho; const codegen = @import("../codegen.zig"); const math = std.math; const mem = std.mem; + const trace = @import("../tracy.zig").trace; const Type = @import("../type.zig").Type; - +const build_options = @import("build_options"); const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); const link = @import("../link.zig"); const File = link.File; +const Cache = @import("../Cache.zig"); +const target_util = @import("../target.zig"); pub const base_tag: File.Tag = File.Tag.macho; @@ -134,71 +138,64 @@ pub const SrcFn = struct { pub const empty = SrcFn{}; }; -pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO { assert(options.object_format == .macho); - const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO + if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); errdefer file.close(); - var macho_file = try allocator.create(MachO); - errdefer allocator.destroy(macho_file); + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); - macho_file.* = openFile(allocator, file, options) catch |err| switch (err) { - error.IncrFailed => try createFile(allocator, file, options), - else => |e| return e, - }; + self.base.file = file; - return &macho_file.base; -} - -/// Returns error.IncrFailed if incremental update could not be performed. -fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { - switch (options.output_mode) { - .Exe => {}, - .Obj => {}, - .Lib => return error.IncrFailed, - } - var self: MachO = .{ - .base = .{ - .file = file, - .tag = .macho, - .options = options, - .allocator = allocator, - }, - }; - errdefer self.deinit(); - - // TODO implement reading the macho file - return error.IncrFailed; - //try self.populateMissingMetadata(); - //return self; -} - -/// Truncates the existing file contents and overwrites the contents. -/// Returns an error if `file` is not already open with +read +write +seek abilities. -fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { switch (options.output_mode) { .Exe => {}, .Obj => {}, .Lib => return error.TODOImplementWritingLibFiles, } - var self: MachO = .{ - .base = .{ - .file = file, - .tag = .macho, - .options = options, - .allocator = allocator, - }, - }; - errdefer self.deinit(); - try self.populateMissingMetadata(); return self; } -pub fn flush(self: *MachO, module: *Module) !void { +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { + const self = try gpa.create(MachO); + self.* = .{ + .base = .{ + .tag = .macho, + .options = options, + .allocator = gpa, + .file = null, + }, + }; + return self; +} + +pub fn flush(self: *MachO, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *MachO, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + switch (self.base.options.output_mode) { .Exe => { var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); @@ -291,6 +288,384 @@ pub fn flush(self: *MachO, module: *Module) !void { } } +fn linkWithLLD(self: *MachO, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const target = self.base.options.target; + const stack_size = self.base.options.stack_size_override orelse 16777216; + const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; + + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + self.base.releaseLock(); + + try man.addOptionalFile(self.base.options.linker_script); + try man.addOptionalFile(self.base.options.version_script); + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + // We can skip hashing libc and libc++ components that we are in charge of building from Zig + // installation sources because they are always a product of the compiler version + target information. + man.hash.add(stack_size); + man.hash.add(self.base.options.rdynamic); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.addListOfBytes(self.base.options.framework_dirs); + man.hash.addListOfBytes(self.base.options.frameworks); + man.hash.addListOfBytes(self.base.options.rpath_list); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + man.hash.add(self.base.options.z_nodelete); + man.hash.add(self.base.options.z_defs); + if (is_dyn_lib) { + man.hash.addOptional(self.base.options.version); + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.add(allow_shlib_undefined); + man.hash.add(self.base.options.bind_global_refs_locally); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("MachO LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("MachO LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("MachO LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + + if (self.base.options.output_mode == .Obj) { + // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (self.base.options.objects.len != 0) + break :blk self.base.options.objects[0]; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.items()[0].key.status.success.object_path; + + if (module_obj_path) |p| + break :blk p; + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + // This can happen when using --enable-cache and using the stage1 backend. In this case + // we can skip the file copy. + if (!mem.eql(u8, the_object_path, full_out_path)) { + try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); + } + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + + try argv.append("-error-limit"); + try argv.append("0"); + + try argv.append("-demangle"); + + if (self.base.options.rdynamic) { + try argv.append("--export-dynamic"); + } + + try argv.appendSlice(self.base.options.extra_lld_args); + + if (self.base.options.z_nodelete) { + try argv.append("-z"); + try argv.append("nodelete"); + } + if (self.base.options.z_defs) { + try argv.append("-z"); + try argv.append("defs"); + } + + if (is_dyn_lib) { + try argv.append("-static"); + } else { + try argv.append("-dynamic"); + } + + if (is_dyn_lib) { + try argv.append("-dylib"); + + if (self.base.options.version) |ver| { + const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major}); + try argv.append("-compatibility_version"); + try argv.append(compat_vers); + + const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); + try argv.append("-current_version"); + try argv.append(cur_vers); + } + + // TODO getting an error when running an executable when doing this rpath thing + //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major); + //try argv.append("-install_name"); + //try argv.append(buf_ptr(dylib_install_name)); + } + + try argv.append("-arch"); + try argv.append(darwinArchString(target.cpu.arch)); + + switch (target.os.tag) { + .macosx => { + try argv.append("-macosx_version_min"); + }, + .ios, .tvos, .watchos => switch (target.cpu.arch) { + .i386, .x86_64 => { + try argv.append("-ios_simulator_version_min"); + }, + else => { + try argv.append("-iphoneos_version_min"); + }, + }, + else => unreachable, + } + const ver = target.os.version_range.semver.min; + const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); + try argv.append(version_string); + + try argv.append("-sdk_version"); + try argv.append(version_string); + + if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { + try argv.append("-pie"); + } + + try argv.append("-o"); + try argv.append(full_out_path); + + // rpaths + var rpath_table = std.StringHashMap(void).init(self.base.allocator); + defer rpath_table.deinit(); + for (self.base.options.rpath_list) |rpath| { + if ((try rpath_table.fetchPut(rpath, {})) == null) { + try argv.append("-rpath"); + try argv.append(rpath); + } + } + if (is_dyn_lib) { + if ((try rpath_table.fetchPut(full_out_path, {})) == null) { + try argv.append("-rpath"); + try argv.append(full_out_path); + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append("-L"); + try argv.append(lib_dir); + } + + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + if (module_obj_path) |p| { + try argv.append(p); + } + + // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + // Shared libraries. + const system_libs = self.base.options.system_libs.items(); + try argv.ensureCapacity(argv.items.len + system_libs.len); + for (system_libs) |entry| { + const link_lib = entry.key; + // By this time, we depend on these libs being dynamically linked libraries and not static libraries + // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which + // case we want to avoid prepending "-l". + const ext = Compilation.classifyFileExt(link_lib); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + argv.appendAssumeCapacity(arg); + } + + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } + + // On Darwin, libSystem has libc in it, but also you have to use it + // to make syscalls because the syscall numbers are not documented + // and change between versions. So we always link against libSystem. + // LLD craps out if you do -lSystem cross compiling, so until that + // codebase gets some love from the new maintainers we're left with + // this dirty hack. + if (self.base.options.is_native_os) { + try argv.append("-lSystem"); + } + + for (self.base.options.framework_dirs) |framework_dir| { + try argv.append("-F"); + try argv.append(framework_dir); + } + for (self.base.options.frameworks) |framework| { + try argv.append("-framework"); + try argv.append(framework); + } + + if (allow_shlib_undefined) { + try argv.append("-undefined"); + try argv.append("dynamic_lookup"); + } + if (self.base.options.bind_global_refs_locally) { + try argv.append("-Bsymbolic"); + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .MachO, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + macho: *MachO, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { + return switch (arch) { + .aarch64, .aarch64_be, .aarch64_32 => "arm64", + .thumb, .arm => "arm", + .thumbeb, .armeb => "armeb", + .powerpc => "ppc", + .powerpc64 => "ppc64", + .powerpc64le => "ppc64le", + else => @tagName(arch), + }; +} + pub fn deinit(self: *MachO) void { self.offset_table.deinit(self.base.allocator); self.string_table.deinit(self.base.allocator); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig new file mode 100644 index 0000000000..3f879a3b32 --- /dev/null +++ b/src/link/Wasm.zig @@ -0,0 +1,479 @@ +const Wasm = @This(); + +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const fs = std.fs; +const leb = std.debug.leb; +const log = std.log.scoped(.link); + +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen/wasm.zig"); +const link = @import("../link.zig"); +const trace = @import("../tracy.zig").trace; +const build_options = @import("build_options"); +const Cache = @import("../Cache.zig"); + +/// Various magic numbers defined by the wasm spec +const spec = struct { + const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm + const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1 + + const custom_id = 0; + const types_id = 1; + const imports_id = 2; + const funcs_id = 3; + const tables_id = 4; + const memories_id = 5; + const globals_id = 6; + const exports_id = 7; + const start_id = 8; + const elements_id = 9; + const code_id = 10; + const data_id = 11; +}; + +pub const base_tag = link.File.Tag.wasm; + +pub const FnData = struct { + /// Generated code for the type of the function + functype: std.ArrayListUnmanaged(u8) = .{}, + /// Generated code for the body of the function + code: std.ArrayListUnmanaged(u8) = .{}, + /// Locations in the generated code where function indexes must be filled in. + /// This must be kept ordered by offset. + idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{}, +}; + +base: link.File, + +/// List of all function Decls to be written to the output file. The index of +/// each Decl in this list at the time of writing the binary is used as the +/// function index. +/// TODO: can/should we access some data structure in Module directly? +funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { + assert(options.object_format == .wasm); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO + if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO + + // TODO: read the file and keep vaild parts instead of truncating + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); + errdefer file.close(); + + const wasm = try createEmpty(allocator, options); + errdefer wasm.base.destroy(); + + wasm.base.file = file; + + try file.writeAll(&(spec.magic ++ spec.version)); + + return wasm; +} + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { + const wasm = try gpa.create(Wasm); + wasm.* = .{ + .base = .{ + .tag = .wasm, + .options = options, + .file = null, + .allocator = gpa, + }, + }; + return wasm; +} + +pub fn deinit(self: *Wasm) void { + for (self.funcs.items) |decl| { + decl.fn_link.wasm.?.functype.deinit(self.base.allocator); + decl.fn_link.wasm.?.code.deinit(self.base.allocator); + decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); + } + self.funcs.deinit(self.base.allocator); +} + +// Generate code for the Decl, storing it in memory to be later written to +// the file on flush(). +pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { + if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn) + return error.TODOImplementNonFnDeclsForWasm; + + if (decl.fn_link.wasm) |*fn_data| { + fn_data.functype.items.len = 0; + fn_data.code.items.len = 0; + fn_data.idx_refs.items.len = 0; + } else { + decl.fn_link.wasm = .{}; + try self.funcs.append(self.base.allocator, decl); + } + const fn_data = &decl.fn_link.wasm.?; + + var managed_functype = fn_data.functype.toManaged(self.base.allocator); + var managed_code = fn_data.code.toManaged(self.base.allocator); + try codegen.genFunctype(&managed_functype, decl); + try codegen.genCode(&managed_code, decl); + fn_data.functype = managed_functype.toUnmanaged(); + fn_data.code = managed_code.toUnmanaged(); +} + +pub fn updateDeclExports( + self: *Wasm, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, +) !void {} + +pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { + // TODO: remove this assert when non-function Decls are implemented + assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); + _ = self.funcs.swapRemove(self.getFuncidx(decl).?); + decl.fn_link.wasm.?.functype.deinit(self.base.allocator); + decl.fn_link.wasm.?.code.deinit(self.base.allocator); + decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); + decl.fn_link.wasm = null; +} + +pub fn flush(self: *Wasm, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *Wasm, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const file = self.base.file.?; + const header_size = 5 + 1; + + // No need to rewrite the magic/version header + try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version))); + try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version))); + + // Type section + { + const header_offset = try reserveVecSectionHeader(file); + for (self.funcs.items) |decl| { + try file.writeAll(decl.fn_link.wasm.?.functype.items); + } + try writeVecSectionHeader( + file, + header_offset, + spec.types_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.funcs.items.len), + ); + } + + // Function section + { + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx)); + try writeVecSectionHeader( + file, + header_offset, + spec.funcs_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.funcs.items.len), + ); + } + + // Export section + if (self.base.options.module) |module| { + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + var count: u32 = 0; + for (module.decl_exports.entries.items) |entry| { + for (entry.value) |exprt| { + // Export name length + name + try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len)); + try writer.writeAll(exprt.options.name); + + switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { + .Fn => { + // Type of the export + try writer.writeByte(0x00); + // Exported function index + try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); + }, + else => return error.TODOImplementNonFnDeclsForWasm, + } + + count += 1; + } + } + try writeVecSectionHeader( + file, + header_offset, + spec.exports_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + count, + ); + } + + // Code section + { + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + for (self.funcs.items) |decl| { + const fn_data = &decl.fn_link.wasm.?; + + // Write the already generated code to the file, inserting + // function indexes where required. + var current: u32 = 0; + for (fn_data.idx_refs.items) |idx_ref| { + try writer.writeAll(fn_data.code.items[current..idx_ref.offset]); + current = idx_ref.offset; + // Use a fixed width here to make calculating the code size + // in codegen.wasm.genCode() simpler. + var buf: [5]u8 = undefined; + leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?); + try writer.writeAll(&buf); + } + + try writer.writeAll(fn_data.code.items[current..]); + } + try writeVecSectionHeader( + file, + header_offset, + spec.code_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.funcs.items.len), + ); + } +} + +fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const target = self.base.options.target; + + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + self.base.releaseLock(); + + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + man.hash.addOptional(self.base.options.stack_size_override); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("WASM LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("WASM LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("WASM LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const is_obj = self.base.options.output_mode == .Obj; + + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + if (is_obj) { + try argv.append("-r"); + } + + try argv.append("-error-limit=0"); + + if (self.base.options.output_mode == .Exe) { + // Increase the default stack size to a more reasonable value of 1MB instead of + // the default of 1 Wasm page being 64KB, unless overriden by the user. + try argv.append("-z"); + const stack_size = self.base.options.stack_size_override orelse 1048576; + const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); + try argv.append(arg); + + // Put stack before globals so that stack overflow results in segfault immediately + // before corrupting globals. See https://github.com/ziglang/zig/issues/4496 + try argv.append("--stack-first"); + } else { + try argv.append("--no-entry"); // So lld doesn't look for _start. + try argv.append("--export-all"); + } + try argv.appendSlice(&[_][]const u8{ + "--allow-undefined", + "-o", + try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}), + }); + + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + if (module_obj_path) |p| { + try argv.append(p); + } + + if (self.base.options.output_mode == .Exe and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .wasm = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .wasm = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .Wasm, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + wasm: *Wasm, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +/// Get the current index of a given Decl in the function list +/// TODO: we could maintain a hash map to potentially make this +fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { + return for (self.funcs.items) |func, idx| { + if (func == decl) break @intCast(u32, idx); + } else null; +} + +fn reserveVecSectionHeader(file: fs.File) !u64 { + // section id + fixed leb contents size + fixed leb vector length + const header_size = 1 + 5 + 5; + // TODO: this should be a single lseek(2) call, but fs.File does not + // currently provide a way to do this. + try file.seekBy(header_size); + return (try file.getPos()) - header_size; +} + +fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void { + var buf: [1 + 5 + 5]u8 = undefined; + buf[0] = section; + leb.writeUnsignedFixed(5, buf[1..6], size); + leb.writeUnsignedFixed(5, buf[6..], items); + try file.pwriteAll(&buf, offset); +} diff --git a/src-self-hosted/link/cbe.h b/src/link/cbe.h similarity index 100% rename from src-self-hosted/link/cbe.h rename to src/link/cbe.h diff --git a/src-self-hosted/link/msdos-stub.bin b/src/link/msdos-stub.bin similarity index 100% rename from src-self-hosted/link/msdos-stub.bin rename to src/link/msdos-stub.bin diff --git a/src-self-hosted/liveness.zig b/src/liveness.zig similarity index 100% rename from src-self-hosted/liveness.zig rename to src/liveness.zig diff --git a/src/llvm.zig b/src/llvm.zig new file mode 100644 index 0000000000..3aebf46b81 --- /dev/null +++ b/src/llvm.zig @@ -0,0 +1,140 @@ +//! We do this instead of @cImport because the self-hosted compiler is easier +//! to bootstrap if it does not depend on translate-c. + +pub const Link = ZigLLDLink; +extern fn ZigLLDLink( + oformat: ObjectFormatType, + args: [*:null]const ?[*:0]const u8, + arg_count: usize, + append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void, + context_stdout: usize, + context_stderr: usize, +) bool; + +pub const ObjectFormatType = extern enum(c_int) { + Unknown, + COFF, + ELF, + MachO, + Wasm, + XCOFF, +}; + +pub const GetHostCPUName = LLVMGetHostCPUName; +extern fn LLVMGetHostCPUName() ?[*:0]u8; + +pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; +extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8; + +pub const WriteArchive = ZigLLVMWriteArchive; +extern fn ZigLLVMWriteArchive( + archive_name: [*:0]const u8, + file_names_ptr: [*]const [*:0]const u8, + file_names_len: usize, + os_type: OSType, +) bool; + +pub const OSType = extern enum(c_int) { + UnknownOS = 0, + Ananas = 1, + CloudABI = 2, + Darwin = 3, + DragonFly = 4, + FreeBSD = 5, + Fuchsia = 6, + IOS = 7, + KFreeBSD = 8, + Linux = 9, + Lv2 = 10, + MacOSX = 11, + NetBSD = 12, + OpenBSD = 13, + Solaris = 14, + Win32 = 15, + Haiku = 16, + Minix = 17, + RTEMS = 18, + NaCl = 19, + CNK = 20, + AIX = 21, + CUDA = 22, + NVCL = 23, + AMDHSA = 24, + PS4 = 25, + ELFIAMCU = 26, + TvOS = 27, + WatchOS = 28, + Mesa3D = 29, + Contiki = 30, + AMDPAL = 31, + HermitCore = 32, + Hurd = 33, + WASI = 34, + Emscripten = 35, +}; + +pub const ArchType = extern enum(c_int) { + UnknownArch = 0, + arm = 1, + armeb = 2, + aarch64 = 3, + aarch64_be = 4, + aarch64_32 = 5, + arc = 6, + avr = 7, + bpfel = 8, + bpfeb = 9, + hexagon = 10, + mips = 11, + mipsel = 12, + mips64 = 13, + mips64el = 14, + msp430 = 15, + ppc = 16, + ppc64 = 17, + ppc64le = 18, + r600 = 19, + amdgcn = 20, + riscv32 = 21, + riscv64 = 22, + sparc = 23, + sparcv9 = 24, + sparcel = 25, + systemz = 26, + tce = 27, + tcele = 28, + thumb = 29, + thumbeb = 30, + x86 = 31, + x86_64 = 32, + xcore = 33, + nvptx = 34, + nvptx64 = 35, + le32 = 36, + le64 = 37, + amdil = 38, + amdil64 = 39, + hsail = 40, + hsail64 = 41, + spir = 42, + spir64 = 43, + kalimba = 44, + shave = 45, + lanai = 46, + wasm32 = 47, + wasm64 = 48, + renderscript32 = 49, + renderscript64 = 50, + ve = 51, +}; + +pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions; +extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void; + +pub const WriteImportLibrary = ZigLLVMWriteImportLibrary; +extern fn ZigLLVMWriteImportLibrary( + def_path: [*:0]const u8, + arch: ArchType, + output_lib_path: [*c]const u8, + kill_at: bool, +) bool; diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 348321598c..0000000000 --- a/src/main.cpp +++ /dev/null @@ -1,1878 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "ast_render.hpp" -#include "buffer.hpp" -#include "codegen.hpp" -#include "compiler.hpp" -#include "config.h" -#include "error.hpp" -#include "heap.hpp" -#include "os.hpp" -#include "target.hpp" -#include "stage2.h" -#include "glibc.hpp" -#include "dump_analysis.hpp" -#include "mem_profile.hpp" - -#include - -static int print_error_usage(const char *arg0) { - fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0); - return EXIT_FAILURE; -} - -static int print_full_usage(const char *arg0, FILE *file, int return_code) { - fprintf(file, - "Usage: %s [command] [options]\n" - "\n" - "Commands:\n" - " build build project from build.zig\n" - " build-exe [source] create executable from source or object files\n" - " 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 @import(\"builtin\")\n" - " cc use Zig as a drop-in C compiler\n" - " c++ use Zig as a drop-in C++ compiler\n" - " env print lib path, std path, compiler id and version\n" - " fmt parse files and render in canonical zig format\n" - " id print the base64-encoded compiler id\n" - " init-exe initialize a `zig build` application in the cwd\n" - " init-lib initialize a `zig build` library in the cwd\n" - " libc [paths_file] Display native libc paths file or validate one\n" - " run [source] [-- [args]] create executable and run immediately\n" - " translate-c [source] convert c code to zig code\n" - " targets list available compilation targets\n" - " test [source] create and run a test build\n" - " version print version number and exit\n" - " zen print zen of zig and exit\n" - "\n" - "Compile Options:\n" - " --c-source [options] [file] compile C source code\n" - " --cache-dir [path] override the local cache directory\n" - " --cache [auto|off|on] build in cache, print output path to stdout\n" - " --color [auto|off|on] enable or disable colored error messages\n" - " --disable-valgrind omit valgrind client requests in debug builds\n" - " --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n" - " --enable-valgrind include valgrind client requests release builds\n" - " -fstack-check enable stack probing in unsafe builds\n" - " -fno-stack-check disable stack probing in safe builds\n" - " -fsanitize-c enable C undefined behavior detection in unsafe builds\n" - " -fno-sanitize-c disable C undefined behavior detection in safe builds\n" - " --emit [asm|bin|llvm-ir] (deprecated) emit a specific file format as compilation output\n" - " -fPIC enable Position Independent Code\n" - " -fno-PIC disable Position Independent Code\n" - " -ftime-report print timing diagnostics\n" - " -fstack-report print stack size diagnostics\n" - " -fmem-report print memory usage diagnostics\n" - " -fdump-analysis write analysis.json file with type information\n" - " -femit-docs create a docs/ dir with html documentation\n" - " -fno-emit-docs do not produce docs/ dir with html documentation\n" - " -femit-bin (default) output machine code\n" - " -fno-emit-bin do not output machine code\n" - " -femit-asm output .s (assembly code)\n" - " -fno-emit-asm (default) do not output .s (assembly code)\n" - " -femit-llvm-ir produce a .ll file with LLVM IR\n" - " -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n" - " -femit-h generate a C header file (.h)\n" - " -fno-emit-h (default) do not generate a C header file (.h)\n" - " --libc [file] Provide a file which specifies libc paths\n" - " --name [name] override output name\n" - " --output-dir [dir] override output directory (defaults to cwd)\n" - " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" - " --pkg-end pop current pkg\n" - " --main-pkg-path set the directory of the root package\n" - " --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" - " -dynamic create a shared library (.so; .dll; .dylib)\n" - " --strip exclude debug symbols\n" - " -target [name] -- see the targets command\n" - " --verbose-tokenize enable compiler debug output for tokenization\n" - " --verbose-ast enable compiler debug output for AST parsing\n" - " --verbose-link enable compiler debug output for linking\n" - " --verbose-ir enable compiler debug output for Zig IR\n" - " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" - " --verbose-cimport enable compiler debug output for C imports\n" - " --verbose-cc enable compiler debug output for C compilation\n" - " --verbose-llvm-cpu-features enable compiler debug output for LLVM CPU features\n" - " -dirafter [dir] add directory to AFTER include search path\n" - " -isystem [dir] add directory to SYSTEM include search path\n" - " -I[dir] add directory to include search path\n" - " -mllvm [arg] (unsupported) forward an arg to LLVM's option processing\n" - " --override-lib-dir [arg] override path to Zig lib directory\n" - " -ffunction-sections places each function in a separate section\n" - " -D[macro]=[value] define C [macro] to [value] (1 if [value] omitted)\n" - " -mcpu [cpu] specify target CPU and feature set\n" - " -code-model [default|tiny| set target code model\n" - " small|kernel|\n" - " medium|large]\n" - "\n" - "Link Options:\n" - " --bundle-compiler-rt for static libraries, include compiler-rt symbols\n" - " --dynamic-linker [path] set the path to ld.so\n" - " --each-lib-rpath add rpath for each used dynamic library\n" - " --library [lib] link against lib\n" - " --forbid-library [lib] make it an error to link against lib\n" - " --library-path [dir] add a directory to the library search path\n" - " --linker-script [path] use a custom linker script\n" - " --version-script [path] provide a version .map file\n" - " --object [obj] add object file to build\n" - " -L[dir] alias for --library-path\n" - " -l[lib] alias for --library\n" - " -rdynamic add all symbols to the dynamic symbol table\n" - " -rpath [path] add directory to the runtime library search path\n" - " --stack [size] (linux, windows, Wasm) override default stack size\n" - " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" - " -F[dir] (darwin) add search path for frameworks\n" - " -framework [name] (darwin) link against framework\n" - " --ver-major [ver] dynamic library semver major version\n" - " --ver-minor [ver] dynamic library semver minor version\n" - " --ver-patch [ver] dynamic library semver patch version\n" - " -Bsymbolic bind global references locally\n" - "\n" - "Test Options:\n" - " --test-filter [text] skip tests that do not match filter\n" - " --test-name-prefix [text] add prefix to all tests\n" - " --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" - " --test-evented-io runs the test in evented I/O mode\n" - , arg0); - return return_code; -} - -static int print_libc_usage(const char *arg0, FILE *file, int return_code) { - fprintf(file, - "Usage: %s libc\n" - "\n" - "Detect the native libc installation and print the resulting paths to stdout.\n" - "You can save this into a file and then edit the paths to create a cross\n" - "compilation libc kit. Then you can pass `--libc [file]` for Zig to use it.\n" - "\n" - "When compiling natively and no `--libc` argument provided, Zig will create\n" - "`%s/native_libc.txt`\n" - "so that it does not have to detect libc on every invocation. You can remove\n" - "this file to have Zig re-detect the native libc.\n" - "\n\n" - "Usage: %s libc [file]\n" - "\n" - "Parse a libc installation text file and validate it.\n" - , arg0, buf_ptr(get_global_cache_dir()), arg0); - return return_code; -} - -enum Cmd { - CmdNone, - CmdBuild, - CmdBuiltin, - CmdRun, - CmdTargets, - CmdTest, - CmdTranslateC, - CmdVersion, - CmdZen, - CmdLibC, -}; - -static const char *default_zig_cache_name = "zig-cache"; - -struct CliPkg { - const char *name; - const char *path; - ZigList children; - CliPkg *parent; -}; - -static void add_package(CodeGen *g, CliPkg *cli_pkg, ZigPackage *pkg) { - for (size_t i = 0; i < cli_pkg->children.length; i += 1) { - CliPkg *child_cli_pkg = cli_pkg->children.at(i); - - Buf *dirname = buf_alloc(); - Buf *basename = buf_alloc(); - os_path_split(buf_create_from_str(child_cli_pkg->path), dirname, basename); - - ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename), - buf_ptr(buf_sprintf("%s.%s", buf_ptr(&pkg->pkg_path), child_cli_pkg->name))); - auto entry = pkg->package_table.put_unique(buf_create_from_str(child_cli_pkg->name), child_pkg); - if (entry) { - ZigPackage *existing_pkg = entry->value; - Buf *full_path = buf_alloc(); - os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path); - fprintf(stderr, "Unable to add package '%s'->'%s': already exists as '%s'\n", - child_cli_pkg->name, child_cli_pkg->path, buf_ptr(full_path)); - exit(EXIT_FAILURE); - } - - add_package(g, child_cli_pkg, child_pkg); - } -} - -enum CacheOpt { - CacheOptAuto, - CacheOptOn, - CacheOptOff, -}; - -static bool get_cache_opt(CacheOpt opt, bool default_value) { - switch (opt) { - case CacheOptAuto: - return default_value; - case CacheOptOn: - return true; - case CacheOptOff: - return false; - } - zig_unreachable(); -} - -static int zig_error_no_build_file(void) { - fprintf(stderr, - "No 'build.zig' file found, in the current directory or any parent directories.\n" - "Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,\n" - "or see `zig --help` for more options.\n" - ); - return EXIT_FAILURE; -} - -static bool str_starts_with(const char *s1, const char *s2) { - size_t s2_len = strlen(s2); - if (strlen(s1) < s2_len) { - return false; - } - return memcmp(s1, s2, s2_len) == 0; -} - -extern "C" int ZigClang_main(int argc, char **argv); - -#ifdef ZIG_ENABLE_MEM_PROFILE -bool mem_report = false; -#endif - -int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) { - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - } - return exit_code; -} - -static int main0(int argc, char **argv) { - char *arg0 = argv[0]; - Error err; - - if (argc >= 2 && (strcmp(argv[1], "clang") == 0 || - strcmp(argv[1], "-cc1") == 0 || strcmp(argv[1], "-cc1as") == 0)) - { - return ZigClang_main(argc, argv); - } - - if (argc == 2 && strcmp(argv[1], "id") == 0) { - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); - return EXIT_FAILURE; - } - printf("%s\n", buf_ptr(compiler_id)); - return EXIT_SUCCESS; - } - - enum InitKind { - InitKindNone, - InitKindExe, - InitKindLib, - }; - InitKind init_kind = InitKindNone; - if (argc >= 2) { - const char *init_cmd = argv[1]; - if (strcmp(init_cmd, "init-exe") == 0) { - init_kind = InitKindExe; - } else if (strcmp(init_cmd, "init-lib") == 0) { - init_kind = InitKindLib; - } - if (init_kind != InitKindNone) { - if (argc >= 3) { - fprintf(stderr, "Unexpected extra argument: %s\n", argv[2]); - return print_error_usage(arg0); - } - Buf *cmd_template_path = buf_alloc(); - os_path_join(get_zig_special_dir(get_zig_lib_dir()), buf_create_from_str(init_cmd), cmd_template_path); - Buf *build_zig_path = buf_alloc(); - os_path_join(cmd_template_path, buf_create_from_str("build.zig"), build_zig_path); - Buf *src_dir_path = buf_alloc(); - os_path_join(cmd_template_path, buf_create_from_str("src"), src_dir_path); - Buf *main_zig_path = buf_alloc(); - os_path_join(src_dir_path, buf_create_from_str("main.zig"), main_zig_path); - - Buf *cwd = buf_alloc(); - if ((err = os_get_cwd(cwd))) { - fprintf(stderr, "Unable to get cwd: %s\n", err_str(err)); - return EXIT_FAILURE; - } - Buf *cwd_basename = buf_alloc(); - os_path_split(cwd, nullptr, cwd_basename); - - Buf *build_zig_contents = buf_alloc(); - if ((err = os_fetch_file_path(build_zig_path, build_zig_contents))) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(build_zig_path), err_str(err)); - return EXIT_FAILURE; - } - Buf *modified_build_zig_contents = buf_alloc(); - for (size_t i = 0; i < buf_len(build_zig_contents); i += 1) { - char c = buf_ptr(build_zig_contents)[i]; - if (c == '$') { - buf_append_buf(modified_build_zig_contents, cwd_basename); - } else { - buf_append_char(modified_build_zig_contents, c); - } - } - - Buf *main_zig_contents = buf_alloc(); - if ((err = os_fetch_file_path(main_zig_path, main_zig_contents))) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(main_zig_path), err_str(err)); - return EXIT_FAILURE; - } - - Buf *out_build_zig_path = buf_create_from_str("build.zig"); - Buf *out_src_dir_path = buf_create_from_str("src"); - Buf *out_main_zig_path = buf_alloc(); - os_path_join(out_src_dir_path, buf_create_from_str("main.zig"), out_main_zig_path); - - bool already_exists; - if ((err = os_file_exists(out_build_zig_path, &already_exists))) { - fprintf(stderr, "Unable test existence of %s: %s\n", buf_ptr(out_build_zig_path), err_str(err)); - return EXIT_FAILURE; - } - if (already_exists) { - fprintf(stderr, "This file would be overwritten: %s\n", buf_ptr(out_build_zig_path)); - return EXIT_FAILURE; - } - - if ((err = os_make_dir(out_src_dir_path))) { - fprintf(stderr, "Unable to make directory: %s: %s\n", buf_ptr(out_src_dir_path), err_str(err)); - return EXIT_FAILURE; - } - if ((err = os_write_file(out_build_zig_path, modified_build_zig_contents))) { - fprintf(stderr, "Unable to write file: %s: %s\n", buf_ptr(out_build_zig_path), err_str(err)); - return EXIT_FAILURE; - } - if ((err = os_write_file(out_main_zig_path, main_zig_contents))) { - fprintf(stderr, "Unable to write file: %s: %s\n", buf_ptr(out_main_zig_path), err_str(err)); - return EXIT_FAILURE; - } - fprintf(stderr, "Created %s\n", buf_ptr(out_build_zig_path)); - fprintf(stderr, "Created %s\n", buf_ptr(out_main_zig_path)); - if (init_kind == InitKindExe) { - fprintf(stderr, "\nNext, try `zig build --help` or `zig build run`\n"); - } else if (init_kind == InitKindLib) { - fprintf(stderr, "\nNext, try `zig build --help` or `zig build test`\n"); - } else { - zig_unreachable(); - } - - return EXIT_SUCCESS; - } - } - - Cmd cmd = CmdNone; - const char *in_file = nullptr; - Buf *output_dir = nullptr; - bool strip = false; - bool is_dynamic = false; - OutType out_type = OutTypeUnknown; - const char *out_name = nullptr; - bool verbose_tokenize = false; - bool verbose_ast = false; - bool verbose_link = false; - bool verbose_ir = false; - bool verbose_llvm_ir = false; - bool verbose_cimport = false; - bool verbose_cc = false; - bool verbose_llvm_cpu_features = false; - bool link_eh_frame_hdr = false; - ErrColor color = ErrColorAuto; - CacheOpt enable_cache = CacheOptAuto; - const char *dynamic_linker = nullptr; - const char *libc_txt = nullptr; - ZigList clang_argv = {0}; - ZigList lib_dirs = {0}; - ZigList link_libs = {0}; - ZigList forbidden_link_libs = {0}; - ZigList framework_dirs = {0}; - ZigList frameworks = {0}; - bool have_libc = false; - bool have_libcpp = false; - const char *target_string = nullptr; - bool rdynamic = false; - const char *linker_script = nullptr; - Buf *version_script = nullptr; - ZigList rpath_list = {0}; - bool each_lib_rpath = false; - ZigList objects = {0}; - ZigList c_source_files = {0}; - const char *test_filter = nullptr; - const char *test_name_prefix = nullptr; - bool test_evented_io = false; - bool is_versioned = false; - size_t ver_major = 0; - size_t ver_minor = 0; - size_t ver_patch = 0; - bool timing_info = false; - bool stack_report = false; - bool enable_dump_analysis = false; - bool enable_doc_generation = false; - bool emit_bin = true; - const char *emit_bin_override_path = nullptr; - bool emit_asm = false; - bool emit_llvm_ir = false; - bool emit_h = false; - const char *cache_dir = nullptr; - CliPkg *cur_pkg = heap::c_allocator.create(); - BuildMode build_mode = BuildModeDebug; - ZigList test_exec_args = {0}; - int runtime_args_start = -1; - bool system_linker_hack = false; - TargetSubsystem subsystem = TargetSubsystemAuto; - bool want_single_threaded = false; - bool bundle_compiler_rt = false; - Buf *override_lib_dir = nullptr; - Buf *main_pkg_path = nullptr; - ValgrindSupport valgrind_support = ValgrindSupportAuto; - WantPIC want_pic = WantPICAuto; - WantStackCheck want_stack_check = WantStackCheckAuto; - WantCSanitize want_sanitize_c = WantCSanitizeAuto; - bool function_sections = false; - const char *mcpu = nullptr; - CodeModel code_model = CodeModelDefault; - const char *override_soname = nullptr; - bool only_pp_or_asm = false; - bool ensure_libc_on_non_freestanding = false; - bool ensure_libcpp_on_non_freestanding = false; - bool disable_c_depfile = false; - bool want_native_include_dirs = false; - Buf *linker_optimization = nullptr; - OptionalBool linker_gc_sections = OptionalBoolNull; - OptionalBool linker_allow_shlib_undefined = OptionalBoolNull; - OptionalBool linker_bind_global_refs_locally = OptionalBoolNull; - bool linker_z_nodelete = false; - bool linker_z_defs = false; - size_t stack_size_override = 0; - - ZigList llvm_argv = {0}; - llvm_argv.append("zig (LLVM option parsing)"); - - if (argc >= 2 && strcmp(argv[1], "build") == 0) { - Buf zig_exe_path_buf = BUF_INIT; - if ((err = os_self_exe_path(&zig_exe_path_buf))) { - fprintf(stderr, "Unable to determine path to zig's own executable\n"); - return EXIT_FAILURE; - } - const char *zig_exe_path = buf_ptr(&zig_exe_path_buf); - const char *build_file = nullptr; - - init_all_targets(); - - ZigList args = {0}; - args.append(NULL); // placeholder - args.append(zig_exe_path); - args.append(NULL); // placeholder - args.append(NULL); // placeholder - for (int i = 2; i < argc; i += 1) { - if (strcmp(argv[i], "--help") == 0) { - args.append(argv[i]); - } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) { - build_file = argv[i + 1]; - i += 1; - } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) { - cache_dir = argv[i + 1]; - i += 1; - } else if (i + 1 < argc && strcmp(argv[i], "--override-lib-dir") == 0) { - override_lib_dir = buf_create_from_str(argv[i + 1]); - i += 1; - - args.append("--override-lib-dir"); - args.append(buf_ptr(override_lib_dir)); - } else { - args.append(argv[i]); - } - } - - Buf *zig_lib_dir = (override_lib_dir == nullptr) ? get_zig_lib_dir() : override_lib_dir; - - Buf *build_runner_path = buf_alloc(); - os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path); - - ZigTarget target; - if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) { - fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); - return EXIT_FAILURE; - } - - Buf *build_file_buf = buf_create_from_str((build_file != nullptr) ? build_file : "build.zig"); - Buf build_file_abs = os_path_resolve(&build_file_buf, 1); - Buf build_file_basename = BUF_INIT; - Buf build_file_dirname = BUF_INIT; - os_path_split(&build_file_abs, &build_file_dirname, &build_file_basename); - - for (;;) { - bool build_file_exists; - if ((err = os_file_exists(&build_file_abs, &build_file_exists))) { - fprintf(stderr, "unable to check existence of '%s': %s\n", buf_ptr(&build_file_abs), err_str(err)); - return 1; - } - if (build_file_exists) - break; - - if (build_file != nullptr) { - // they asked for a specific build file path. only look for that one - return zig_error_no_build_file(); - } - - Buf *next_dir = buf_alloc(); - os_path_dirname(&build_file_dirname, next_dir); - if (buf_eql_buf(&build_file_dirname, next_dir)) { - // no more parent directories to search, give up - return zig_error_no_build_file(); - } - os_path_join(next_dir, &build_file_basename, &build_file_abs); - buf_init_from_buf(&build_file_dirname, next_dir); - } - - Buf full_cache_dir = BUF_INIT; - if (cache_dir == nullptr) { - os_path_join(&build_file_dirname, buf_create_from_str(default_zig_cache_name), &full_cache_dir); - } else { - Buf *cache_dir_buf = buf_create_from_str(cache_dir); - full_cache_dir = os_path_resolve(&cache_dir_buf, 1); - } - Stage2ProgressNode *root_progress_node = stage2_progress_start_root(stage2_progress_create(), "", 0, 0); - - CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe, - BuildModeDebug, override_lib_dir, nullptr, &full_cache_dir, false, root_progress_node); - g->valgrind_support = valgrind_support; - g->enable_time_report = timing_info; - codegen_set_out_name(g, buf_create_from_str("build")); - - args.items[2] = buf_ptr(&build_file_dirname); - args.items[3] = buf_ptr(&full_cache_dir); - - ZigPackage *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname), - buf_ptr(&build_file_basename), "std.special"); - g->main_pkg->package_table.put(buf_create_from_str("@build"), build_pkg); - g->enable_cache = get_cache_opt(enable_cache, true); - codegen_build_and_link(g); - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - root_progress_node = nullptr; - } - - Termination term; - args.items[0] = buf_ptr(&g->bin_file_output_path); - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nBuild failed. The following command failed:\n"); - const char *prefix = ""; - for (size_t i = 0; i < args.length; i += 1) { - fprintf(stderr, "%s%s", prefix, args.at(i)); - prefix = " "; - } - fprintf(stderr, "\n"); - } - return (term.how == TerminationIdClean) ? term.code : -1; - } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { - return stage2_fmt(argc, argv); - } else if (argc >= 2 && strcmp(argv[1], "env") == 0) { - return stage2_env(argc, argv); - } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) { - emit_h = false; - strip = true; - ensure_libc_on_non_freestanding = true; - ensure_libcpp_on_non_freestanding = (strcmp(argv[1], "c++") == 0); - want_native_include_dirs = true; - - bool c_arg = false; - Stage2ClangArgIterator it; - stage2_clang_arg_iterator(&it, argc, argv); - bool is_shared_lib = false; - ZigList linker_args = {}; - while (it.has_next) { - if ((err = stage2_clang_arg_next(&it))) { - fprintf(stderr, "unable to parse command line parameters: %s\n", err_str(err)); - return EXIT_FAILURE; - } - switch (it.kind) { - case Stage2ClangArgTarget: // example: -target riscv64-linux-unknown - target_string = it.only_arg; - break; - case Stage2ClangArgO: // -o - emit_bin_override_path = it.only_arg; - enable_cache = CacheOptOn; - break; - case Stage2ClangArgC: // -c - c_arg = true; - break; - case Stage2ClangArgOther: - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - break; - case Stage2ClangArgPositional: { - FileExt file_ext = classify_file_ext(it.only_arg, strlen(it.only_arg)); - switch (file_ext) { - case FileExtAsm: - case FileExtC: - case FileExtCpp: - case FileExtLLVMIr: - case FileExtLLVMBitCode: - case FileExtHeader: { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = it.only_arg; - c_source_files.append(c_file); - break; - } - case FileExtUnknown: - objects.append(it.only_arg); - break; - } - break; - } - case Stage2ClangArgL: // -l - if (strcmp(it.only_arg, "c") == 0) { - have_libc = true; - link_libs.append("c"); - } else if (strcmp(it.only_arg, "c++") == 0 || - strcmp(it.only_arg, "stdc++") == 0) - { - have_libcpp = true; - link_libs.append("c++"); - } else { - link_libs.append(it.only_arg); - } - break; - case Stage2ClangArgIgnore: - break; - case Stage2ClangArgDriverPunt: - // Never mind what we're doing, just pass the args directly. For example --help. - return ZigClang_main(argc, argv); - case Stage2ClangArgPIC: - want_pic = WantPICEnabled; - break; - case Stage2ClangArgNoPIC: - want_pic = WantPICDisabled; - break; - case Stage2ClangArgNoStdLib: - ensure_libc_on_non_freestanding = false; - break; - case Stage2ClangArgNoStdLibCpp: - ensure_libcpp_on_non_freestanding = false; - break; - case Stage2ClangArgShared: - is_dynamic = true; - is_shared_lib = true; - break; - case Stage2ClangArgRDynamic: - rdynamic = true; - break; - case Stage2ClangArgWL: { - const char *arg = it.only_arg; - for (;;) { - size_t pos = 0; - while (arg[pos] != ',' && arg[pos] != 0) pos += 1; - linker_args.append(buf_create_from_mem(arg, pos)); - if (arg[pos] == 0) break; - arg += pos + 1; - } - break; - } - case Stage2ClangArgPreprocessOrAsm: - // this handles both -E and -S - only_pp_or_asm = true; - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - break; - case Stage2ClangArgOptimize: - // alright what release mode do they want? - if (strcmp(it.only_arg, "Os") == 0) { - build_mode = BuildModeSmallRelease; - } else if (strcmp(it.only_arg, "O2") == 0 || - strcmp(it.only_arg, "O3") == 0 || - strcmp(it.only_arg, "O4") == 0) - { - build_mode = BuildModeFastRelease; - } else if (strcmp(it.only_arg, "Og") == 0 || - strcmp(it.only_arg, "O0") == 0) - { - build_mode = BuildModeDebug; - } else { - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - } - break; - case Stage2ClangArgDebug: - strip = false; - if (strcmp(it.only_arg, "-g") == 0) { - // we handled with strip = false above - } else { - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - } - break; - case Stage2ClangArgSanitize: - if (strcmp(it.only_arg, "undefined") == 0) { - want_sanitize_c = WantCSanitizeEnabled; - } else { - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - } - break; - case Stage2ClangArgLinkerScript: - linker_script = it.only_arg; - break; - case Stage2ClangArgVerboseCmds: - verbose_cc = true; - verbose_link = true; - break; - case Stage2ClangArgForLinker: - linker_args.append(buf_create_from_str(it.only_arg)); - break; - case Stage2ClangArgLinkerInputZ: - linker_args.append(buf_create_from_str("-z")); - linker_args.append(buf_create_from_str(it.only_arg)); - break; - case Stage2ClangArgLibDir: - lib_dirs.append(it.only_arg); - break; - case Stage2ClangArgMCpu: - mcpu = it.only_arg; - break; - case Stage2ClangArgDepFile: - disable_c_depfile = true; - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - break; - case Stage2ClangArgFrameworkDir: - framework_dirs.append(it.only_arg); - break; - case Stage2ClangArgFramework: - frameworks.append(it.only_arg); - break; - case Stage2ClangArgNoStdLibInc: - want_native_include_dirs = false; - break; - } - } - // Parse linker args - for (size_t i = 0; i < linker_args.length; i += 1) { - Buf *arg = linker_args.at(i); - if (buf_eql_str(arg, "-soname")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - Buf *soname_buf = linker_args.at(i); - override_soname = buf_ptr(soname_buf); - // use it as --name - // example: libsoundio.so.2 - size_t prefix = 0; - if (buf_starts_with_str(soname_buf, "lib")) { - prefix = 3; - } - size_t end = buf_len(soname_buf); - if (buf_ends_with_str(soname_buf, ".so")) { - end -= 3; - } else { - bool found_digit = false; - while (end > 0 && isdigit(buf_ptr(soname_buf)[end - 1])) { - found_digit = true; - end -= 1; - } - if (found_digit && end > 0 && buf_ptr(soname_buf)[end - 1] == '.') { - end -= 1; - } else { - end = buf_len(soname_buf); - } - if (buf_ends_with_str(buf_slice(soname_buf, prefix, end), ".so")) { - end -= 3; - } - } - out_name = buf_ptr(buf_slice(soname_buf, prefix, end)); - } else if (buf_eql_str(arg, "-rpath")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - Buf *rpath = linker_args.at(i); - rpath_list.append(buf_ptr(rpath)); - } else if (buf_eql_str(arg, "-I") || - buf_eql_str(arg, "--dynamic-linker") || - buf_eql_str(arg, "-dynamic-linker")) - { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - dynamic_linker = buf_ptr(linker_args.at(i)); - } else if (buf_eql_str(arg, "-E") || - buf_eql_str(arg, "--export-dynamic") || - buf_eql_str(arg, "-export-dynamic")) - { - rdynamic = true; - } else if (buf_eql_str(arg, "--version-script")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - version_script = linker_args.at(i); - } else if (buf_starts_with_str(arg, "-O")) { - linker_optimization = arg; - } else if (buf_eql_str(arg, "--gc-sections")) { - linker_gc_sections = OptionalBoolTrue; - } else if (buf_eql_str(arg, "--no-gc-sections")) { - linker_gc_sections = OptionalBoolFalse; - } else if (buf_eql_str(arg, "--allow-shlib-undefined") || - buf_eql_str(arg, "-allow-shlib-undefined")) - { - linker_allow_shlib_undefined = OptionalBoolTrue; - } else if (buf_eql_str(arg, "--no-allow-shlib-undefined") || - buf_eql_str(arg, "-no-allow-shlib-undefined")) - { - linker_allow_shlib_undefined = OptionalBoolFalse; - } else if (buf_eql_str(arg, "-Bsymbolic")) { - linker_bind_global_refs_locally = OptionalBoolTrue; - } else if (buf_eql_str(arg, "-z")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - Buf *z_arg = linker_args.at(i); - if (buf_eql_str(z_arg, "nodelete")) { - linker_z_nodelete = true; - } else if (buf_eql_str(z_arg, "defs")) { - linker_z_defs = true; - } else { - fprintf(stderr, "warning: unsupported linker arg: -z %s\n", buf_ptr(z_arg)); - } - } else if (buf_eql_str(arg, "--major-image-version")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - is_versioned = true; - ver_major = atoi(buf_ptr(linker_args.at(i))); - } else if (buf_eql_str(arg, "--minor-image-version")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - is_versioned = true; - ver_minor = atoi(buf_ptr(linker_args.at(i))); - } else if (buf_eql_str(arg, "--stack")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - stack_size_override = atoi(buf_ptr(linker_args.at(i))); - } else { - fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg)); - } - } - - if (want_sanitize_c == WantCSanitizeEnabled && build_mode == BuildModeFastRelease) { - build_mode = BuildModeSafeRelease; - } - - if (only_pp_or_asm) { - cmd = CmdBuild; - out_type = OutTypeObj; - emit_bin = false; - // Transfer "objects" into c_source_files - for (size_t i = 0; i < objects.length; i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = objects.at(i); - c_source_files.append(c_file); - } - for (size_t i = 0; i < c_source_files.length; i += 1) { - Buf *src_path; - if (emit_bin_override_path != nullptr) { - src_path = buf_create_from_str(emit_bin_override_path); - } else { - src_path = buf_create_from_str(c_source_files.at(i)->source_path); - } - Buf basename = BUF_INIT; - os_path_split(src_path, nullptr, &basename); - c_source_files.at(i)->preprocessor_only_basename = buf_ptr(&basename); - } - } else if (!c_arg) { - cmd = CmdBuild; - if (is_shared_lib) { - out_type = OutTypeLib; - } else { - out_type = OutTypeExe; - } - if (emit_bin_override_path == nullptr) { - emit_bin_override_path = "a.out"; - enable_cache = CacheOptOn; - } - } else { - cmd = CmdBuild; - out_type = OutTypeObj; - } - if (c_source_files.length == 0 && objects.length == 0) { - // For example `zig cc` and no args should print the "no input files" message. - return ZigClang_main(argc, argv); - } - } else for (int i = 1; i < argc; i += 1) { - char *arg = argv[i]; - - if (arg[0] == '-') { - if (strcmp(arg, "--") == 0) { - if (cmd == CmdRun) { - runtime_args_start = i + 1; - break; // rest of the args are for the program - } else { - fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg); - } - } else if (strcmp(arg, "--release-fast") == 0) { - build_mode = BuildModeFastRelease; - } else if (strcmp(arg, "--release-safe") == 0) { - build_mode = BuildModeSafeRelease; - } else if (strcmp(arg, "--release-small") == 0) { - build_mode = BuildModeSmallRelease; - } else if (strcmp(arg, "--help") == 0) { - if (cmd == CmdLibC) { - return print_libc_usage(arg0, stdout, EXIT_SUCCESS); - } else { - return print_full_usage(arg0, stdout, EXIT_SUCCESS); - } - } else if (strcmp(arg, "--strip") == 0) { - strip = true; - } else if (strcmp(arg, "-dynamic") == 0) { - is_dynamic = true; - } else if (strcmp(arg, "--verbose-tokenize") == 0) { - verbose_tokenize = true; - } else if (strcmp(arg, "--verbose-ast") == 0) { - verbose_ast = true; - } else if (strcmp(arg, "--verbose-link") == 0) { - verbose_link = true; - } else if (strcmp(arg, "--verbose-ir") == 0) { - verbose_ir = true; - } else if (strcmp(arg, "--verbose-llvm-ir") == 0) { - verbose_llvm_ir = true; - } else if (strcmp(arg, "--verbose-cimport") == 0) { - verbose_cimport = true; - } else if (strcmp(arg, "--verbose-cc") == 0) { - verbose_cc = true; - } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) { - verbose_llvm_cpu_features = true; - } else if (strcmp(arg, "-rdynamic") == 0) { - rdynamic = true; - } else if (strcmp(arg, "--each-lib-rpath") == 0) { - each_lib_rpath = true; - } else if (strcmp(arg, "-ftime-report") == 0) { - timing_info = true; - } else if (strcmp(arg, "-fstack-report") == 0) { - stack_report = true; - } else if (strcmp(arg, "-fmem-report") == 0) { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem_report = true; - mem::report_print = true; -#else - fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n"); - return print_error_usage(arg0); -#endif - } else if (strcmp(arg, "-fdump-analysis") == 0) { - enable_dump_analysis = true; - } else if (strcmp(arg, "-femit-docs") == 0) { - enable_doc_generation = true; - } else if (strcmp(arg, "--enable-valgrind") == 0) { - valgrind_support = ValgrindSupportEnabled; - } else if (strcmp(arg, "--disable-valgrind") == 0) { - valgrind_support = ValgrindSupportDisabled; - } else if (strcmp(arg, "--eh-frame-hdr") == 0) { - link_eh_frame_hdr = true; - } else if (strcmp(arg, "-fPIC") == 0) { - want_pic = WantPICEnabled; - } else if (strcmp(arg, "-fno-PIC") == 0) { - want_pic = WantPICDisabled; - } else if (strcmp(arg, "-fstack-check") == 0) { - want_stack_check = WantStackCheckEnabled; - } else if (strcmp(arg, "-fno-stack-check") == 0) { - want_stack_check = WantStackCheckDisabled; - } else if (strcmp(arg, "-fsanitize-c") == 0) { - want_sanitize_c = WantCSanitizeEnabled; - } else if (strcmp(arg, "-fno-sanitize-c") == 0) { - want_sanitize_c = WantCSanitizeDisabled; - } else if (strcmp(arg, "--system-linker-hack") == 0) { - system_linker_hack = true; - } else if (strcmp(arg, "--single-threaded") == 0) { - want_single_threaded = true;; - } else if (strcmp(arg, "--bundle-compiler-rt") == 0) { - bundle_compiler_rt = true; - } else if (strcmp(arg, "-Bsymbolic") == 0) { - linker_bind_global_refs_locally = OptionalBoolTrue; - } else if (strcmp(arg, "--test-cmd-bin") == 0) { - test_exec_args.append(nullptr); - } else if (arg[1] == 'D' && arg[2] != 0) { - clang_argv.append("-D"); - clang_argv.append(&arg[2]); - } else if (arg[1] == 'L' && arg[2] != 0) { - // alias for --library-path - lib_dirs.append(&arg[2]); - } else if (arg[1] == 'l' && arg[2] != 0) { - // alias for --library - const char *l = &arg[2]; - if (strcmp(l, "c") == 0) { - have_libc = true; - link_libs.append("c"); - } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) { - have_libcpp = true; - link_libs.append("c++"); - } else { - link_libs.append(l); - } - } else if (arg[1] == 'I' && arg[2] != 0) { - clang_argv.append("-I"); - clang_argv.append(&arg[2]); - } else if (arg[1] == 'F' && arg[2] != 0) { - framework_dirs.append(&arg[2]); - } else if (strcmp(arg, "--pkg-begin") == 0) { - if (i + 2 >= argc) { - fprintf(stderr, "Expected 2 arguments after --pkg-begin\n"); - return print_error_usage(arg0); - } - CliPkg *new_cur_pkg = heap::c_allocator.create(); - i += 1; - new_cur_pkg->name = argv[i]; - i += 1; - new_cur_pkg->path = argv[i]; - new_cur_pkg->parent = cur_pkg; - cur_pkg->children.append(new_cur_pkg); - cur_pkg = new_cur_pkg; - } else if (strcmp(arg, "--pkg-end") == 0) { - if (cur_pkg->parent == nullptr) { - fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n"); - return EXIT_FAILURE; - } - cur_pkg = cur_pkg->parent; - } else if (strcmp(arg, "-ffunction-sections") == 0) { - function_sections = true; - } else if (strcmp(arg, "--test-evented-io") == 0) { - test_evented_io = true; - } else if (strcmp(arg, "-femit-bin") == 0) { - emit_bin = true; - } else if (strcmp(arg, "-fno-emit-bin") == 0) { - emit_bin = false; - } else if (strcmp(arg, "-femit-asm") == 0) { - emit_asm = true; - } else if (strcmp(arg, "-fno-emit-asm") == 0) { - emit_asm = false; - } else if (strcmp(arg, "-femit-llvm-ir") == 0) { - emit_llvm_ir = true; - } else if (strcmp(arg, "-fno-emit-llvm-ir") == 0) { - emit_llvm_ir = false; - } else if (strcmp(arg, "-femit-h") == 0) { - emit_h = true; - } else if (strcmp(arg, "-fno-emit-h") == 0 || strcmp(arg, "--disable-gen-h") == 0) { - // the --disable-gen-h is there to support godbolt. once they upgrade to -fno-emit-h then we can remove this - emit_h = false; - } else if (str_starts_with(arg, "-mcpu=")) { - mcpu = arg + strlen("-mcpu="); - } else if (i + 1 >= argc) { - fprintf(stderr, "Expected another argument after %s\n", arg); - return print_error_usage(arg0); - } else { - i += 1; - if (strcmp(arg, "--output-dir") == 0) { - output_dir = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "--color") == 0) { - if (strcmp(argv[i], "auto") == 0) { - color = ErrColorAuto; - } else if (strcmp(argv[i], "on") == 0) { - color = ErrColorOn; - } else if (strcmp(argv[i], "off") == 0) { - color = ErrColorOff; - } else { - fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--cache") == 0) { - if (strcmp(argv[i], "auto") == 0) { - enable_cache = CacheOptAuto; - } else if (strcmp(argv[i], "on") == 0) { - enable_cache = CacheOptOn; - } else if (strcmp(argv[i], "off") == 0) { - enable_cache = CacheOptOff; - } else { - fprintf(stderr, "--cache options are 'auto', 'on', or 'off'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--emit") == 0) { - if (strcmp(argv[i], "asm") == 0) { - emit_asm = true; - emit_bin = false; - } else if (strcmp(argv[i], "bin") == 0) { - emit_bin = true; - } else if (strcmp(argv[i], "llvm-ir") == 0) { - emit_llvm_ir = true; - emit_bin = false; - } else { - fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--name") == 0) { - out_name = argv[i]; - } else if (strcmp(arg, "--dynamic-linker") == 0) { - dynamic_linker = argv[i]; - } else if (strcmp(arg, "--libc") == 0) { - libc_txt = argv[i]; - } else if (strcmp(arg, "-D") == 0) { - clang_argv.append("-D"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-isystem") == 0) { - clang_argv.append("-isystem"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-I") == 0) { - clang_argv.append("-I"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-dirafter") == 0) { - clang_argv.append("-dirafter"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-mllvm") == 0) { - clang_argv.append("-mllvm"); - clang_argv.append(argv[i]); - - llvm_argv.append(argv[i]); - } else if (strcmp(arg, "-code-model") == 0) { - if (strcmp(argv[i], "default") == 0) { - code_model = CodeModelDefault; - } else if (strcmp(argv[i], "tiny") == 0) { - code_model = CodeModelTiny; - } else if (strcmp(argv[i], "small") == 0) { - code_model = CodeModelSmall; - } else if (strcmp(argv[i], "kernel") == 0) { - code_model = CodeModelKernel; - } else if (strcmp(argv[i], "medium") == 0) { - code_model = CodeModelMedium; - } else if (strcmp(argv[i], "large") == 0) { - code_model = CodeModelLarge; - } else { - fprintf(stderr, "-code-model options are 'default', 'tiny', 'small', 'kernel', 'medium', or 'large'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--override-lib-dir") == 0) { - override_lib_dir = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "--main-pkg-path") == 0) { - main_pkg_path = 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, "-F") == 0) { - framework_dirs.append(argv[i]); - } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) { - if (strcmp(argv[i], "c") == 0) { - have_libc = true; - link_libs.append("c"); - } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) { - have_libcpp = true; - link_libs.append("c++"); - } else { - link_libs.append(argv[i]); - } - } else if (strcmp(arg, "--forbid-library") == 0) { - forbidden_link_libs.append(argv[i]); - } else if (strcmp(arg, "--object") == 0) { - objects.append(argv[i]); - } else if (strcmp(arg, "--c-source") == 0) { - CFile *c_file = heap::c_allocator.create(); - for (;;) { - if (argv[i][0] == '-') { - c_file->args.append(argv[i]); - i += 1; - if (i < argc) { - continue; - } - - break; - } else { - c_file->source_path = argv[i]; - c_source_files.append(c_file); - break; - } - } - } else if (strcmp(arg, "--cache-dir") == 0) { - cache_dir = argv[i]; - } else if (strcmp(arg, "-target") == 0) { - target_string = argv[i]; - } else if (strcmp(arg, "-framework") == 0) { - frameworks.append(argv[i]); - } else if (strcmp(arg, "--linker-script") == 0) { - linker_script = argv[i]; - } else if (strcmp(arg, "--version-script") == 0) { - version_script = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "-rpath") == 0) { - rpath_list.append(argv[i]); - } else if (strcmp(arg, "--test-filter") == 0) { - test_filter = argv[i]; - } else if (strcmp(arg, "--test-name-prefix") == 0) { - test_name_prefix = argv[i]; - } else if (strcmp(arg, "--ver-major") == 0) { - is_versioned = true; - ver_major = atoi(argv[i]); - } else if (strcmp(arg, "--ver-minor") == 0) { - is_versioned = true; - ver_minor = atoi(argv[i]); - } else if (strcmp(arg, "--ver-patch") == 0) { - is_versioned = true; - ver_patch = atoi(argv[i]); - } else if (strcmp(arg, "--test-cmd") == 0) { - test_exec_args.append(argv[i]); - } else if (strcmp(arg, "--stack") == 0) { - stack_size_override = atoi(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 if (strcmp(arg, "-mcpu") == 0) { - mcpu = argv[i]; - } else { - fprintf(stderr, "Invalid argument: %s\n", arg); - return print_error_usage(arg0); - } - } - } else if (cmd == CmdNone) { - if (strcmp(arg, "build-exe") == 0) { - cmd = CmdBuild; - out_type = OutTypeExe; - } else if (strcmp(arg, "build-obj") == 0) { - cmd = CmdBuild; - out_type = OutTypeObj; - } else if (strcmp(arg, "build-lib") == 0) { - cmd = CmdBuild; - out_type = OutTypeLib; - } else if (strcmp(arg, "run") == 0) { - cmd = CmdRun; - out_type = OutTypeExe; - } else if (strcmp(arg, "version") == 0) { - cmd = CmdVersion; - } else if (strcmp(arg, "zen") == 0) { - cmd = CmdZen; - } else if (strcmp(arg, "libc") == 0) { - cmd = CmdLibC; - } else if (strcmp(arg, "translate-c") == 0) { - cmd = CmdTranslateC; - } else if (strcmp(arg, "test") == 0) { - cmd = CmdTest; - out_type = OutTypeExe; - } else if (strcmp(arg, "targets") == 0) { - cmd = CmdTargets; - } else if (strcmp(arg, "builtin") == 0) { - cmd = CmdBuiltin; - } else { - fprintf(stderr, "Unrecognized command: %s\n", arg); - return print_error_usage(arg0); - } - } else { - switch (cmd) { - case CmdBuild: - case CmdRun: - case CmdTranslateC: - case CmdTest: - case CmdLibC: - if (!in_file) { - in_file = arg; - } else { - fprintf(stderr, "Unexpected extra parameter: %s\n", arg); - return print_error_usage(arg0); - } - break; - case CmdBuiltin: - case CmdVersion: - case CmdZen: - case CmdTargets: - fprintf(stderr, "Unexpected extra parameter: %s\n", arg); - return print_error_usage(arg0); - case CmdNone: - zig_unreachable(); - } - } - } - - if (cur_pkg->parent != nullptr) { - fprintf(stderr, "Unmatched --pkg-begin\n"); - return EXIT_FAILURE; - } - - Stage2Progress *progress = stage2_progress_create(); - Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0); - if (color == ErrColorOff) stage2_progress_disable_tty(progress); - - init_all_targets(); - - ZigTarget target; - if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) { - fprintf(stderr, "invalid target: %s\n" - "See `%s targets` to display valid targets.\n", err_str(err), arg0); - return print_error_usage(arg0); - } - - if (!have_libc && ensure_libc_on_non_freestanding && target.os != OsFreestanding) { - have_libc = true; - link_libs.append("c"); - } - if (!have_libcpp && ensure_libcpp_on_non_freestanding && target.os != OsFreestanding) { - have_libcpp = true; - link_libs.append("c++"); - } - - Buf zig_triple_buf = BUF_INIT; - target_triple_zig(&zig_triple_buf, &target); - - // If both output_dir and enable_cache are provided, and doing build-lib, we - // will just do a file copy at the end. This helps when bootstrapping zig from zig0 - // because we want to pass something like this: - // zig0 build-lib --cache on --output-dir ${CMAKE_BINARY_DIR} - // And we don't have access to `zig0 build` because that would require detecting native libc - // on systems where we are not able to build a libc from source for them. - // But that's the only reason this works, so otherwise we give an error here. - Buf *final_output_dir_step = nullptr; - if (output_dir != nullptr && enable_cache == CacheOptOn) { - if (cmd == CmdBuild && out_type == OutTypeLib) { - final_output_dir_step = output_dir; - output_dir = nullptr; - } else { - fprintf(stderr, "`--output-dir` is incompatible with --cache on.\n"); - return print_error_usage(arg0); - } - } - - if (target_requires_pic(&target, have_libc) && want_pic == WantPICDisabled) { - fprintf(stderr, "`--disable-pic` is incompatible with target '%s'\n", buf_ptr(&zig_triple_buf)); - return print_error_usage(arg0); - } - - if ((emit_asm || emit_llvm_ir) && in_file == nullptr) { - fprintf(stderr, "A root source file is required when using `-femit-asm` or `-femit-llvm-ir`\n"); - return print_error_usage(arg0); - } - - if (llvm_argv.length > 1) { - llvm_argv.append(nullptr); - ZigLLVMParseCommandLineOptions(llvm_argv.length - 1, llvm_argv.items); - } - - switch (cmd) { - case CmdLibC: { - if (in_file) { - Stage2LibCInstallation libc; - if ((err = stage2_libc_parse(&libc, in_file))) { - fprintf(stderr, "unable to parse libc file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } - Stage2LibCInstallation libc; - if ((err = stage2_libc_find_native(&libc))) { - fprintf(stderr, "unable to find native libc file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - if ((err = stage2_libc_render(&libc, stdout))) { - fprintf(stderr, "unable to print libc file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } - case CmdBuiltin: { - CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, - out_type, build_mode, override_lib_dir, nullptr, nullptr, false, root_progress_node); - codegen_set_strip(g, strip); - for (size_t i = 0; i < link_libs.length; i += 1) { - LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); - link_lib->provided_explicitly = true; - } - g->subsystem = subsystem; - g->valgrind_support = valgrind_support; - g->want_pic = want_pic; - g->want_stack_check = want_stack_check; - g->want_sanitize_c = want_sanitize_c; - g->want_single_threaded = want_single_threaded; - g->test_is_evented = test_evented_io; - 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))); - return main_exit(root_progress_node, EXIT_FAILURE); - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } - case CmdRun: - case CmdBuild: - case CmdTranslateC: - case CmdTest: - { - if (cmd == CmdBuild && !in_file && objects.length == 0 && - c_source_files.length == 0) - { - fprintf(stderr, - "Expected at least one of these things:\n" - " * Zig root source file argument\n" - " * --object argument\n" - " * --c-source argument\n"); - return print_error_usage(arg0); - } else if ((cmd == CmdTranslateC || - cmd == CmdTest || cmd == CmdRun) && !in_file) - { - fprintf(stderr, "Expected source file argument.\n"); - return print_error_usage(arg0); - } else if (cmd == CmdRun && !emit_bin) { - fprintf(stderr, "Cannot run without emitting a binary file.\n"); - return print_error_usage(arg0); - } - - bool any_system_lib_dependencies = false; - for (size_t i = 0; i < link_libs.length; i += 1) { - if (!target_is_libc_lib_name(&target, link_libs.at(i)) && - !target_is_libcpp_lib_name(&target, link_libs.at(i))) - { - any_system_lib_dependencies = true; - break; - } - } - - if (target.is_native_os && (any_system_lib_dependencies || want_native_include_dirs)) { - Error err; - Stage2NativePaths paths; - if ((err = stage2_detect_native_paths(&paths))) { - fprintf(stderr, "unable to detect native system paths: %s\n", err_str(err)); - exit(1); - } - for (size_t i = 0; i < paths.warnings_len; i += 1) { - const char *warning = paths.warnings_ptr[i]; - fprintf(stderr, "warning: %s\n", warning); - } - for (size_t i = 0; i < paths.include_dirs_len; i += 1) { - const char *include_dir = paths.include_dirs_ptr[i]; - clang_argv.append("-isystem"); - clang_argv.append(include_dir); - } - for (size_t i = 0; i < paths.lib_dirs_len; i += 1) { - const char *lib_dir = paths.lib_dirs_ptr[i]; - lib_dirs.append(lib_dir); - } - for (size_t i = 0; i < paths.rpaths_len; i += 1) { - const char *rpath = paths.rpaths_ptr[i]; - rpath_list.append(rpath); - } - } - - - assert(cmd != CmdBuild || out_type != OutTypeUnknown); - - bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC); - - if (cmd == CmdRun) { - out_name = "run"; - } - - Buf *in_file_buf = nullptr; - - Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") : - (out_name == nullptr) ? nullptr : buf_create_from_str(out_name); - - if (in_file) { - in_file_buf = buf_create_from_str(in_file); - - if (need_name && buf_out_name == nullptr) { - Buf basename = BUF_INIT; - os_path_split(in_file_buf, nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - } - - if (need_name && buf_out_name == nullptr && c_source_files.length == 1) { - Buf basename = BUF_INIT; - os_path_split(buf_create_from_str(c_source_files.at(0)->source_path), nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - if (need_name && buf_out_name == nullptr && objects.length == 1) { - Buf basename = BUF_INIT; - os_path_split(buf_create_from_str(objects.at(0)), nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - if (need_name && buf_out_name == nullptr && emit_bin_override_path != nullptr) { - Buf basename = BUF_INIT; - os_path_split(buf_create_from_str(emit_bin_override_path), nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - - if (need_name && buf_out_name == nullptr) { - fprintf(stderr, "--name [name] not provided and unable to infer\n\n"); - return print_error_usage(arg0); - } - - Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf; - - if (cmd == CmdRun && buf_out_name == nullptr) { - buf_out_name = buf_create_from_str("run"); - } - Stage2LibCInstallation *libc = nullptr; - if (libc_txt != nullptr) { - libc = heap::c_allocator.create(); - if ((err = stage2_libc_parse(libc, libc_txt))) { - fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } - Buf *cache_dir_buf; - if (cache_dir == nullptr) { - if (cmd == CmdRun) { - cache_dir_buf = get_global_cache_dir(); - } else { - cache_dir_buf = buf_create_from_str(default_zig_cache_name); - } - } else { - cache_dir_buf = buf_create_from_str(cache_dir); - } - CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode, - override_lib_dir, libc, cache_dir_buf, cmd == CmdTest, root_progress_node); - if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2); - g->valgrind_support = valgrind_support; - g->link_eh_frame_hdr = link_eh_frame_hdr; - g->want_pic = want_pic; - g->want_stack_check = want_stack_check; - g->want_sanitize_c = want_sanitize_c; - g->subsystem = subsystem; - - g->enable_time_report = timing_info; - g->enable_stack_report = stack_report; - g->enable_dump_analysis = enable_dump_analysis; - g->enable_doc_generation = enable_doc_generation; - g->emit_bin = emit_bin; - g->emit_asm = emit_asm; - g->emit_llvm_ir = emit_llvm_ir; - - codegen_set_out_name(g, buf_out_name); - codegen_set_lib_version(g, is_versioned, ver_major, ver_minor, ver_patch); - g->want_single_threaded = want_single_threaded; - codegen_set_linker_script(g, linker_script); - g->version_script_path = version_script; - if (each_lib_rpath) - codegen_set_each_lib_rpath(g, each_lib_rpath); - - codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); - - codegen_set_strip(g, strip); - g->is_dynamic = is_dynamic; - g->verbose_tokenize = verbose_tokenize; - g->verbose_ast = verbose_ast; - g->verbose_link = verbose_link; - g->verbose_ir = verbose_ir; - g->verbose_llvm_ir = verbose_llvm_ir; - g->verbose_cimport = verbose_cimport; - g->verbose_cc = verbose_cc; - g->verbose_llvm_cpu_features = verbose_llvm_cpu_features; - g->output_dir = output_dir; - g->disable_gen_h = !emit_h; - g->bundle_compiler_rt = bundle_compiler_rt; - codegen_set_errmsg_color(g, color); - g->system_linker_hack = system_linker_hack; - g->function_sections = function_sections; - g->code_model = code_model; - g->disable_c_depfile = disable_c_depfile; - - g->linker_optimization = linker_optimization; - g->linker_gc_sections = linker_gc_sections; - g->linker_allow_shlib_undefined = linker_allow_shlib_undefined; - g->linker_bind_global_refs_locally = linker_bind_global_refs_locally; - g->linker_z_nodelete = linker_z_nodelete; - g->linker_z_defs = linker_z_defs; - g->stack_size_override = stack_size_override; - - if (override_soname) { - g->override_soname = buf_create_from_str(override_soname); - } - - for (size_t i = 0; i < lib_dirs.length; i += 1) { - codegen_add_lib_dir(g, lib_dirs.at(i)); - } - for (size_t i = 0; i < framework_dirs.length; i += 1) { - g->framework_dirs.append(framework_dirs.at(i)); - } - for (size_t i = 0; i < link_libs.length; i += 1) { - LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); - link_lib->provided_explicitly = true; - } - for (size_t i = 0; i < forbidden_link_libs.length; i += 1) { - Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i)); - codegen_add_forbidden_lib(g, forbidden_link_lib); - } - for (size_t i = 0; i < frameworks.length; i += 1) { - codegen_add_framework(g, frameworks.at(i)); - } - for (size_t i = 0; i < rpath_list.length; i += 1) { - codegen_add_rpath(g, rpath_list.at(i)); - } - - codegen_set_rdynamic(g, rdynamic); - - if (test_filter) { - codegen_set_test_filter(g, buf_create_from_str(test_filter)); - } - g->test_is_evented = test_evented_io; - - if (test_name_prefix) { - codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix)); - } - - add_package(g, cur_pkg, g->main_pkg); - - if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { - g->c_source_files = c_source_files; - for (size_t i = 0; i < objects.length; i += 1) { - codegen_add_object(g, buf_create_from_str(objects.at(i))); - } - } - - - if (cmd == CmdBuild || cmd == CmdRun) { - g->enable_cache = get_cache_opt(enable_cache, cmd == CmdRun); - codegen_build_and_link(g); - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - root_progress_node = nullptr; - } - if (timing_info) - codegen_print_timing_report(g, stdout); - if (stack_report) - zig_print_stack_report(g, stdout); - - if (cmd == CmdRun) { -#ifdef ZIG_ENABLE_MEM_PROFILE - if (mem::report_print) - mem::print_report(); -#endif - - const char *exec_path = buf_ptr(&g->bin_file_output_path); - ZigList args = {0}; - - args.append(exec_path); - if (runtime_args_start != -1) { - for (int i = runtime_args_start; i < argc; ++i) { - args.append(argv[i]); - } - } - args.append(nullptr); - - os_execv(exec_path, args.items); - - args.pop(); - Termination term; - os_spawn_process(args, &term); - return term.code; - } else if (cmd == CmdBuild) { - if (emit_bin_override_path != nullptr) { -#if defined(ZIG_OS_WINDOWS) - buf_replace(g->output_dir, '/', '\\'); -#endif - Buf *dest_path = buf_create_from_str(emit_bin_override_path); - Buf *source_path; - if (only_pp_or_asm) { - source_path = buf_alloc(); - Buf *pp_only_basename = buf_create_from_str( - c_source_files.at(0)->preprocessor_only_basename); - os_path_join(g->output_dir, pp_only_basename, source_path); - - } else { - source_path = &g->bin_file_output_path; - } - if ((err = os_update_file(source_path, dest_path))) { - fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(source_path), - buf_ptr(dest_path), err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } else if (only_pp_or_asm) { -#if defined(ZIG_OS_WINDOWS) - buf_replace(g->c_artifact_dir, '/', '\\'); -#endif - // dump the preprocessed output to stdout - for (size_t i = 0; i < c_source_files.length; i += 1) { - Buf *source_path = buf_alloc(); - Buf *pp_only_basename = buf_create_from_str( - c_source_files.at(i)->preprocessor_only_basename); - os_path_join(g->c_artifact_dir, pp_only_basename, source_path); - if ((err = os_dump_file(source_path, stdout))) { - fprintf(stderr, "unable to read %s: %s\n", buf_ptr(source_path), - err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } - } else if (g->enable_cache) { -#if defined(ZIG_OS_WINDOWS) - buf_replace(&g->bin_file_output_path, '/', '\\'); - buf_replace(g->output_dir, '/', '\\'); -#endif - if (final_output_dir_step != nullptr) { - Buf *dest_basename = buf_alloc(); - os_path_split(&g->bin_file_output_path, nullptr, dest_basename); - Buf *dest_path = buf_alloc(); - os_path_join(final_output_dir_step, dest_basename, dest_path); - - if ((err = os_update_file(&g->bin_file_output_path, dest_path))) { - fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(&g->bin_file_output_path), - buf_ptr(dest_path), err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } else { - if (printf("%s\n", buf_ptr(g->output_dir)) < 0) - return main_exit(root_progress_node, EXIT_FAILURE); - } - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } else { - zig_unreachable(); - } - } else if (cmd == CmdTranslateC) { - g->enable_cache = get_cache_opt(enable_cache, false); - codegen_translate_c(g, in_file_buf); - if (timing_info) - codegen_print_timing_report(g, stderr); - return main_exit(root_progress_node, EXIT_SUCCESS); - } else if (cmd == CmdTest) { - ZigTarget native; - if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) { - fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); - return EXIT_FAILURE; - } - - g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr); - codegen_build_and_link(g); - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - root_progress_node = nullptr; - } - - if (timing_info) { - codegen_print_timing_report(g, stdout); - } - - if (stack_report) { - zig_print_stack_report(g, stdout); - } - - if (!g->emit_bin) { - fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n"); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - - Buf *test_exe_path_unresolved = &g->bin_file_output_path; - Buf *test_exe_path = buf_alloc(); - *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1); - - if (!g->emit_bin) { - fprintf(stderr, "Created %s but skipping execution because no binary generated.\n", - buf_ptr(test_exe_path)); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - - for (size_t i = 0; i < test_exec_args.length; i += 1) { - if (test_exec_args.items[i] == nullptr) { - test_exec_args.items[i] = buf_ptr(test_exe_path); - } - } - - if (!target_can_exec(&native, &target) && test_exec_args.length == 0) { - fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", - buf_ptr(test_exe_path)); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - - Termination term; - if (test_exec_args.length == 0) { - test_exec_args.append(buf_ptr(test_exe_path)); - } - os_spawn_process(test_exec_args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); - fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); - } - return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1); - } else { - zig_unreachable(); - } - } - case CmdVersion: - printf("%s\n", ZIG_VERSION_STRING); - return main_exit(root_progress_node, EXIT_SUCCESS); - case CmdZen: { - const char *ptr; - size_t len; - stage2_zen(&ptr, &len); - fwrite(ptr, len, 1, stdout); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - case CmdTargets: - return stage2_cmd_targets(target_string, mcpu, dynamic_linker); - case CmdNone: - return print_full_usage(arg0, stderr, EXIT_FAILURE); - } - zig_unreachable(); -} - -int main(int argc, char **argv) { - stage2_attach_segfault_handler(); - os_init(); - mem::init(); - - auto result = main0(argc, argv); - -#ifdef ZIG_ENABLE_MEM_PROFILE - if (mem::report_print) - mem::intern_counters.print_report(); -#endif - mem::deinit(); - return result; -} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000000..d421322c17 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,3039 @@ +const std = @import("std"); +const assert = std.debug.assert; +const io = std.io; +const fs = std.fs; +const mem = std.mem; +const process = std.process; +const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const ast = std.zig.ast; +const warn = std.log.warn; + +const Compilation = @import("Compilation.zig"); +const link = @import("link.zig"); +const Package = @import("Package.zig"); +const zir = @import("zir.zig"); +const build_options = @import("build_options"); +const introspect = @import("introspect.zig"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const translate_c = @import("translate_c.zig"); +const Cache = @import("Cache.zig"); +const target_util = @import("target.zig"); + +pub fn fatal(comptime format: []const u8, args: anytype) noreturn { + std.log.emerg(format, args); + process.exit(1); +} + +pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB + +pub const Color = enum { + Auto, + Off, + On, +}; + +const usage = + \\Usage: zig [command] [options] + \\ + \\Commands: + \\ + \\ build Build project from build.zig + \\ build-exe Create executable from source or object files + \\ build-lib Create library from source or object files + \\ build-obj Create object from source or assembly + \\ cc Use Zig as a drop-in C compiler + \\ c++ Use Zig as a drop-in C++ compiler + \\ env Print lib path, std path, compiler id and version + \\ fmt Parse file and render in canonical zig format + \\ init-exe Initialize a `zig build` application in the cwd + \\ init-lib Initialize a `zig build` library in the cwd + \\ libc Display native libc paths file or validate one + \\ run Create executable and run immediately + \\ translate-c Convert C code to Zig code + \\ targets List available compilation targets + \\ test Create and run a test build + \\ version Print version number and exit + \\ zen Print zen of zig and exit + \\ + \\General Options: + \\ + \\ --help Print command-specific usage + \\ +; + +pub const log_level: std.log.Level = switch (std.builtin.mode) { + .Debug => .debug, + .ReleaseSafe, .ReleaseFast => .info, + .ReleaseSmall => .crit, +}; + +pub fn log( + comptime level: std.log.Level, + comptime scope: @TypeOf(.EnumLiteral), + comptime format: []const u8, + args: anytype, +) void { + // Hide debug messages unless added with `-Dlog=foo`. + if (@enumToInt(level) > @enumToInt(std.log.level) or + @enumToInt(level) > @enumToInt(std.log.Level.info)) + { + const scope_name = @tagName(scope); + const ok = comptime for (build_options.log_scopes) |log_scope| { + if (mem.eql(u8, log_scope, scope_name)) + break true; + } else return; + } + + // We only recognize 4 log levels in this application. + const level_txt = switch (level) { + .emerg, .alert, .crit, .err => "error", + .warn => "warning", + .notice, .info => "info", + .debug => "debug", + }; + const prefix1 = level_txt; + const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; + + // Print the message to stderr, silently ignoring any errors + std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args); +} + +var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; + +pub fn main() anyerror!void { + const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator; + defer if (!std.builtin.link_libc) { + _ = general_purpose_allocator.deinit(); + }; + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = &arena_instance.allocator; + + const args = try process.argsAlloc(arena); + return mainArgs(gpa, arena, args); +} + +pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { + if (args.len <= 1) { + std.log.info("{}", .{usage}); + fatal("expected command argument", .{}); + } + + const cmd = args[1]; + const cmd_args = args[2..]; + if (mem.eql(u8, cmd, "build-exe")) { + return buildOutputType(gpa, arena, args, .{ .build = .Exe }); + } else if (mem.eql(u8, cmd, "build-lib")) { + return buildOutputType(gpa, arena, args, .{ .build = .Lib }); + } else if (mem.eql(u8, cmd, "build-obj")) { + return buildOutputType(gpa, arena, args, .{ .build = .Obj }); + } else if (mem.eql(u8, cmd, "test")) { + return buildOutputType(gpa, arena, args, .zig_test); + } else if (mem.eql(u8, cmd, "run")) { + return buildOutputType(gpa, arena, args, .run); + } else if (mem.eql(u8, cmd, "cc")) { + return buildOutputType(gpa, arena, args, .cc); + } else if (mem.eql(u8, cmd, "c++")) { + return buildOutputType(gpa, arena, args, .cpp); + } else if (mem.eql(u8, cmd, "translate-c")) { + return buildOutputType(gpa, arena, args, .translate_c); + } else if (mem.eql(u8, cmd, "clang") or + mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) + { + return punt_to_clang(arena, args); + } else if (mem.eql(u8, cmd, "build")) { + return cmdBuild(gpa, arena, cmd_args); + } else if (mem.eql(u8, cmd, "fmt")) { + return cmdFmt(gpa, cmd_args); + } else if (mem.eql(u8, cmd, "libc")) { + return cmdLibC(gpa, cmd_args); + } else if (mem.eql(u8, cmd, "init-exe")) { + return cmdInit(gpa, arena, cmd_args, .Exe); + } else if (mem.eql(u8, cmd, "init-lib")) { + return cmdInit(gpa, arena, cmd_args, .Lib); + } else if (mem.eql(u8, cmd, "targets")) { + const info = try detectNativeTargetInfo(arena, .{}); + const stdout = io.getStdOut().outStream(); + return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); + } else if (mem.eql(u8, cmd, "version")) { + try std.io.getStdOut().writeAll(build_options.version ++ "\n"); + } else if (mem.eql(u8, cmd, "env")) { + try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream()); + } else if (mem.eql(u8, cmd, "zen")) { + try io.getStdOut().writeAll(info_zen); + } else if (mem.eql(u8, cmd, "help")) { + try io.getStdOut().writeAll(usage); + } else { + std.log.info("{}", .{usage}); + fatal("unknown command: {}", .{args[1]}); + } +} + +const usage_build_generic = + \\Usage: zig build-exe [files] + \\ zig build-lib [files] + \\ zig build-obj [files] + \\ zig test [files] + \\ zig run [file] [-- [args]] + \\ + \\Supported file types: + \\ .zig Zig source code + \\ .zir Zig Intermediate Representation code + \\ .o ELF object file + \\ .o MACH-O (macOS) object file + \\ .obj COFF (Windows) object file + \\ .lib COFF (Windows) static library + \\ .a ELF static library + \\ .so ELF shared object (dynamic link) + \\ .dll Windows Dynamic Link Library + \\ .dylib MACH-O (macOS) dynamic library + \\ .s Target-specific assembly source code + \\ .S Assembly with C preprocessor (requires LLVM extensions) + \\ .c C source code (requires LLVM extensions) + \\ .cpp C++ source code (requires LLVM extensions) + \\ Other C++ extensions: .C .cc .cxx + \\ + \\General Options: + \\ -h, --help Print this help and exit + \\ --watch Enable compiler REPL + \\ --color [auto|off|on] Enable or disable colored error messages + \\ -femit-bin[=path] (default) Output machine code + \\ -fno-emit-bin Do not output machine code + \\ -femit-asm[=path] Output .s (assembly code) + \\ -fno-emit-asm (default) Do not output .s (assembly code) + \\ -femit-zir[=path] Produce a .zir file with Zig IR + \\ -fno-emit-zir (default) Do not produce a .zir file with Zig IR + \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) + \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR + \\ -femit-h[=path] Generate a C header file (.h) + \\ -fno-emit-h (default) Do not generate a C header file (.h) + \\ -femit-docs[=path] Create a docs/ dir with html documentation + \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation + \\ -femit-analysis[=path] Write analysis JSON file with type information + \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information + \\ --show-builtin Output the source of @import("builtin") then exit + \\ --cache-dir [path] Override the local cache directory + \\ --global-cache-dir [path] Override the global cache directory + \\ --override-lib-dir [path] Override path to Zig installation lib directory + \\ --enable-cache Output to cache directory; print path to stdout + \\ + \\Compile Options: + \\ -target [name] -- see the targets command + \\ -mcpu [cpu] Specify target CPU and feature set + \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses + \\ small|kernel| + \\ medium|large] + \\ --name [name] Override root name (not a file path) + \\ -O [mode] Choose what to optimize for + \\ Debug (default) Optimizations off, safety on + \\ ReleaseFast Optimizations on, safety off + \\ ReleaseSafe Optimizations on, safety on + \\ ReleaseSmall Optimize for small binary, safety off + \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg + \\ --pkg-end Pop current pkg + \\ --main-pkg-path Set the directory of the root package + \\ -fPIC Force-enable Position Independent Code + \\ -fno-PIC Force-disable Position Independent Code + \\ -fstack-check Enable stack probing in unsafe builds + \\ -fno-stack-check Disable stack probing in safe builds + \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds + \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds + \\ -fvalgrind Include valgrind client requests in release builds + \\ -fno-valgrind Omit valgrind client requests in debug builds + \\ -fdll-export-fns Mark exported functions as DLL exports (Windows) + \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports + \\ --strip Omit debug symbols + \\ --single-threaded Code assumes it is only used single-threaded + \\ -ofmt=[mode] Override target object format + \\ elf Executable and Linking Format + \\ c Compile to C source code + \\ wasm WebAssembly + \\ pe Portable Executable (Windows) + \\ coff Common Object File Format (Windows) + \\ macho macOS relocatables + \\ hex (planned) Intel IHEX + \\ raw (planned) Dump machine code directly + \\ -dirafter [dir] Add directory to AFTER include search path + \\ -isystem [dir] Add directory to SYSTEM include search path + \\ -I[dir] Add directory to include search path + \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted) + \\ --libc [file] Provide a file which specifies libc paths + \\ -cflags [flags] -- Set extra flags for the next positional C source files + \\ -ffunction-sections Places each function in a separate section + \\ + \\Link Options: + \\ -l[lib], --library [lib] Link against system library + \\ -L[d], --library-directory [d] Add a directory to the library search path + \\ -T[script], --script [script] Use a custom linker script + \\ --version-script [path] Provide a version .map file + \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) + \\ --each-lib-rpath Add rpath for each used dynamic library + \\ --version [ver] Dynamic library semver + \\ -rdynamic Add all symbols to the dynamic symbol table + \\ -rpath [path] Add directory to the runtime library search path + \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker + \\ -dynamic Force output to be dynamically linked + \\ -static Force output to be statically linked + \\ -Bsymbolic Bind global references locally + \\ --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" + \\ --stack [size] Override default stack size + \\ -framework [name] (darwin) link against framework + \\ -F[dir] (darwin) add search path for frameworks + \\ + \\Test Options: + \\ --test-filter [text] Skip tests that do not match filter + \\ --test-name-prefix [text] Add prefix to all tests + \\ --test-cmd [arg] Specify test execution command one arg at a time + \\ --test-cmd-bin Appends test binary path to test cmd args + \\ --test-evented-io Runs the test in evented I/O mode + \\ + \\Debug Options (Zig Compiler Development): + \\ -ftime-report Print timing diagnostics + \\ -fstack-report Print stack size diagnostics + \\ --verbose-link Display linker invocations + \\ --verbose-cc Display C compiler invocations + \\ --verbose-tokenize Enable compiler debug output for tokenization + \\ --verbose-ast Enable compiler debug output for AST parsing + \\ --verbose-ir Enable compiler debug output for Zig IR + \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR + \\ --verbose-cimport Enable compiler debug output for C imports + \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features + \\ +; + +const repl_help = + \\Commands: + \\ update Detect changes to source files and update output files. + \\ help Print this text + \\ exit Quit this repl + \\ +; + +const Emit = union(enum) { + no, + yes_default_path, + yes: []const u8, + + const Resolved = struct { + data: ?Compilation.EmitLoc, + dir: ?fs.Dir, + + fn deinit(self: *Resolved) void { + if (self.dir) |*dir| { + dir.close(); + } + } + }; + + fn resolve(emit: Emit, default_basename: []const u8) !Resolved { + var resolved: Resolved = .{ .data = null, .dir = null }; + errdefer resolved.deinit(); + + switch (emit) { + .no => {}, + .yes_default_path => { + resolved.data = Compilation.EmitLoc{ + .directory = .{ .path = null, .handle = fs.cwd() }, + .basename = default_basename, + }; + }, + .yes => |full_path| { + const basename = fs.path.basename(full_path); + if (fs.path.dirname(full_path)) |dirname| { + const handle = try fs.cwd().openDir(dirname, .{}); + resolved = .{ + .dir = handle, + .data = Compilation.EmitLoc{ + .basename = basename, + .directory = .{ + .path = dirname, + .handle = handle, + }, + }, + }; + } else { + resolved.data = Compilation.EmitLoc{ + .basename = basename, + .directory = .{ .path = null, .handle = fs.cwd() }, + }; + } + }, + } + return resolved; + } +}; + +fn buildOutputType( + gpa: *Allocator, + arena: *Allocator, + all_args: []const []const u8, + arg_mode: union(enum) { + build: std.builtin.OutputMode, + cc, + cpp, + translate_c, + zig_test, + run, + }, +) !void { + var color: Color = .Auto; + var optimize_mode: std.builtin.Mode = .Debug; + var provided_name: ?[]const u8 = null; + var link_mode: ?std.builtin.LinkMode = null; + var dll_export_fns: ?bool = null; + var root_src_file: ?[]const u8 = null; + var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; + var have_version = false; + var strip = false; + var single_threaded = false; + var function_sections = false; + var watch = false; + var verbose_link = false; + var verbose_cc = false; + var verbose_tokenize = false; + var verbose_ast = false; + var verbose_ir = false; + var verbose_llvm_ir = false; + var verbose_cimport = false; + var verbose_llvm_cpu_features = false; + var time_report = false; + var stack_report = false; + var show_builtin = false; + var emit_bin: Emit = .yes_default_path; + var emit_asm: Emit = .no; + var emit_llvm_ir: Emit = .no; + var emit_zir: Emit = .no; + var emit_docs: Emit = .no; + var emit_analysis: Emit = .no; + var target_arch_os_abi: []const u8 = "native"; + var target_mcpu: ?[]const u8 = null; + var target_dynamic_linker: ?[]const u8 = null; + var target_ofmt: ?[]const u8 = null; + var output_mode: std.builtin.OutputMode = undefined; + var emit_h: Emit = undefined; + var ensure_libc_on_non_freestanding = false; + var ensure_libcpp_on_non_freestanding = false; + var link_libc = false; + var link_libcpp = false; + var want_native_include_dirs = false; + var enable_cache: ?bool = null; + var want_pic: ?bool = null; + var want_sanitize_c: ?bool = null; + var want_stack_check: ?bool = null; + var want_valgrind: ?bool = null; + var rdynamic: bool = false; + var linker_script: ?[]const u8 = null; + var version_script: ?[]const u8 = null; + var disable_c_depfile = false; + var override_soname: ?[]const u8 = null; + var linker_gc_sections: ?bool = null; + var linker_allow_shlib_undefined: ?bool = null; + var linker_bind_global_refs_locally: ?bool = null; + var linker_z_nodelete = false; + var linker_z_defs = false; + var test_evented_io = false; + var stack_size_override: ?u64 = null; + var use_llvm: ?bool = null; + var use_lld: ?bool = null; + var use_clang: ?bool = null; + var link_eh_frame_hdr = false; + var each_lib_rpath = false; + var libc_paths_file: ?[]const u8 = null; + var machine_code_model: std.builtin.CodeModel = .default; + var runtime_args_start: ?usize = null; + var test_filter: ?[]const u8 = null; + var test_name_prefix: ?[]const u8 = null; + var override_local_cache_dir: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = null; + var override_lib_dir: ?[]const u8 = null; + var main_pkg_path: ?[]const u8 = null; + var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; + var subsystem: ?std.Target.SubSystem = null; + + var system_libs = std.ArrayList([]const u8).init(gpa); + defer system_libs.deinit(); + + var clang_argv = std.ArrayList([]const u8).init(gpa); + defer clang_argv.deinit(); + + var extra_cflags = std.ArrayList([]const u8).init(gpa); + defer extra_cflags.deinit(); + + var lld_argv = std.ArrayList([]const u8).init(gpa); + defer lld_argv.deinit(); + + var lib_dirs = std.ArrayList([]const u8).init(gpa); + defer lib_dirs.deinit(); + + var rpath_list = std.ArrayList([]const u8).init(gpa); + defer rpath_list.deinit(); + + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa); + defer c_source_files.deinit(); + + var link_objects = std.ArrayList([]const u8).init(gpa); + defer link_objects.deinit(); + + var framework_dirs = std.ArrayList([]const u8).init(gpa); + defer framework_dirs.deinit(); + + var frameworks = std.ArrayList([]const u8).init(gpa); + defer frameworks.deinit(); + + // null means replace with the test executable binary + var test_exec_args = std.ArrayList(?[]const u8).init(gpa); + defer test_exec_args.deinit(); + + var root_pkg_memory: Package = .{ + .root_src_directory = undefined, + .root_src_path = undefined, + }; + defer root_pkg_memory.table.deinit(gpa); + var cur_pkg: *Package = &root_pkg_memory; + + switch (arg_mode) { + .build, .translate_c, .zig_test, .run => { + var optimize_mode_string: ?[]const u8 = null; + switch (arg_mode) { + .build => |m| { + output_mode = m; + }, + .translate_c => { + emit_bin = .no; + output_mode = .Obj; + }, + .zig_test, .run => { + output_mode = .Exe; + }, + else => unreachable, + } + // TODO finish self-hosted and add support for emitting C header files + emit_h = .no; + //switch (arg_mode) { + // .build => switch (output_mode) { + // .Exe => emit_h = .no, + // .Obj, .Lib => emit_h = .yes_default_path, + // }, + // .translate_c, .zig_test, .run => emit_h = .no, + // else => unreachable, + //} + const args = all_args[2..]; + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + try io.getStdOut().writeAll(usage_build_generic); + return cleanExit(); + } else if (mem.eql(u8, arg, "--")) { + if (arg_mode == .run) { + runtime_args_start = i + 1; + } else { + fatal("unexpected end-of-parameter mark: --", .{}); + } + } else if (mem.eql(u8, arg, "--pkg-begin")) { + if (i + 2 >= args.len) fatal("Expected 2 arguments after {}", .{arg}); + i += 1; + const pkg_name = args[i]; + i += 1; + const pkg_path = args[i]; + + const new_cur_pkg = try arena.create(Package); + new_cur_pkg.* = .{ + .root_src_directory = if (fs.path.dirname(pkg_path)) |dirname| + .{ + .path = dirname, + .handle = try fs.cwd().openDir(dirname, .{}), // TODO close this fd + } + else + .{ + .path = null, + .handle = fs.cwd(), + }, + .root_src_path = fs.path.basename(pkg_path), + .parent = cur_pkg, + }; + try cur_pkg.table.put(gpa, pkg_name, new_cur_pkg); + cur_pkg = new_cur_pkg; + } else if (mem.eql(u8, arg, "--pkg-end")) { + cur_pkg = cur_pkg.parent orelse + fatal("encountered --pkg-end with no matching --pkg-begin", .{}); + } else if (mem.eql(u8, arg, "--main-pkg-path")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + main_pkg_path = args[i]; + } else if (mem.eql(u8, arg, "-cflags")) { + extra_cflags.shrinkRetainingCapacity(0); + while (true) { + i += 1; + if (i + 1 >= args.len) fatal("expected -- after -cflags", .{}); + if (mem.eql(u8, args[i], "--")) break; + try extra_cflags.append(args[i]); + } + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + fatal("expected [auto|on|off] after --color", .{}); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + } + } else if (mem.eql(u8, arg, "--subsystem")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + if (mem.eql(u8, args[i], "console")) { + subsystem = .Console; + } else if (mem.eql(u8, args[i], "windows")) { + subsystem = .Windows; + } else if (mem.eql(u8, args[i], "posix")) { + subsystem = .Posix; + } else if (mem.eql(u8, args[i], "native")) { + subsystem = .Native; + } else if (mem.eql(u8, args[i], "efi_application")) { + subsystem = .EfiApplication; + } else if (mem.eql(u8, args[i], "efi_boot_service_driver")) { + subsystem = .EfiBootServiceDriver; + } else if (mem.eql(u8, args[i], "efi_rom")) { + subsystem = .EfiRom; + } else if (mem.eql(u8, args[i], "efi_runtime_driver")) { + subsystem = .EfiRuntimeDriver; + } else { + fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{ + args[i], + \\ console + \\ windows + \\ posix + \\ native + \\ efi_application + \\ efi_boot_service_driver + \\ efi_rom + \\ efi_runtime_driver + \\ + }); + } + } else if (mem.eql(u8, arg, "-O")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + optimize_mode_string = args[i]; + } else if (mem.eql(u8, arg, "--stack")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + } else if (mem.eql(u8, arg, "--name")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + provided_name = args[i]; + } else if (mem.eql(u8, arg, "-rpath")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try rpath_list.append(args[i]); + } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try lib_dirs.append(args[i]); + } else if (mem.eql(u8, arg, "-F")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try framework_dirs.append(args[i]); + } else if (mem.eql(u8, arg, "-framework")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try frameworks.append(args[i]); + } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + linker_script = args[i]; + } else if (mem.eql(u8, arg, "--version-script")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + version_script = args[i]; + } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + // We don't know whether this library is part of libc or libc++ until we resolve the target. + // So we simply append to the list for now. + i += 1; + try system_libs.append(args[i]); + } else if (mem.eql(u8, arg, "-D") or + mem.eql(u8, arg, "-isystem") or + mem.eql(u8, arg, "-I") or + mem.eql(u8, arg, "-dirafter")) + { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try clang_argv.append(arg); + try clang_argv.append(args[i]); + } else if (mem.eql(u8, arg, "--version")) { + if (i + 1 >= args.len) { + fatal("expected parameter after --version", .{}); + } + i += 1; + version = std.builtin.Version.parse(args[i]) catch |err| { + fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) }); + }; + have_version = true; + } else if (mem.eql(u8, arg, "-target")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + target_arch_os_abi = args[i]; + } else if (mem.eql(u8, arg, "-mcpu")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + target_mcpu = args[i]; + } else if (mem.eql(u8, arg, "-mcmodel")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + machine_code_model = parseCodeModel(args[i]); + } else if (mem.startsWith(u8, arg, "-ofmt=")) { + target_ofmt = arg["-ofmt=".len..]; + } else if (mem.startsWith(u8, arg, "-mcpu=")) { + target_mcpu = arg["-mcpu=".len..]; + } else if (mem.startsWith(u8, arg, "-mcmodel=")) { + machine_code_model = parseCodeModel(arg["-mcmodel=".len..]); + } else if (mem.startsWith(u8, arg, "-O")) { + optimize_mode_string = arg["-O".len..]; + } else if (mem.eql(u8, arg, "--dynamic-linker")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + target_dynamic_linker = args[i]; + } else if (mem.eql(u8, arg, "--libc")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + libc_paths_file = args[i]; + } else if (mem.eql(u8, arg, "--test-filter")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + test_filter = args[i]; + } else if (mem.eql(u8, arg, "--test-name-prefix")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + test_name_prefix = args[i]; + } else if (mem.eql(u8, arg, "--test-cmd")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try test_exec_args.append(args[i]); + } else if (mem.eql(u8, arg, "--cache-dir")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + override_local_cache_dir = args[i]; + } else if (mem.eql(u8, arg, "--global-cache-dir")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + override_global_cache_dir = args[i]; + } else if (mem.eql(u8, arg, "--override-lib-dir")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + override_lib_dir = args[i]; + } else if (mem.eql(u8, arg, "--each-lib-rpath")) { + each_lib_rpath = true; + } else if (mem.eql(u8, arg, "--enable-cache")) { + enable_cache = true; + } else if (mem.eql(u8, arg, "--test-cmd-bin")) { + try test_exec_args.append(null); + } else if (mem.eql(u8, arg, "--test-evented-io")) { + test_evented_io = true; + } else if (mem.eql(u8, arg, "--watch")) { + watch = true; + } else if (mem.eql(u8, arg, "-ftime-report")) { + time_report = true; + } else if (mem.eql(u8, arg, "-fstack-report")) { + stack_report = true; + } else if (mem.eql(u8, arg, "-fPIC")) { + want_pic = true; + } else if (mem.eql(u8, arg, "-fno-PIC")) { + want_pic = false; + } else if (mem.eql(u8, arg, "-fstack-check")) { + want_stack_check = true; + } else if (mem.eql(u8, arg, "-fno-stack-check")) { + want_stack_check = false; + } else if (mem.eql(u8, arg, "-fsanitize-c")) { + want_sanitize_c = true; + } else if (mem.eql(u8, arg, "-fno-sanitize-c")) { + want_sanitize_c = false; + } else if (mem.eql(u8, arg, "-fvalgrind")) { + want_valgrind = true; + } else if (mem.eql(u8, arg, "-fno-valgrind")) { + want_valgrind = false; + } else if (mem.eql(u8, arg, "-fLLVM")) { + use_llvm = true; + } else if (mem.eql(u8, arg, "-fno-LLVM")) { + use_llvm = false; + } else if (mem.eql(u8, arg, "-fLLD")) { + use_lld = true; + } else if (mem.eql(u8, arg, "-fno-LLD")) { + use_lld = false; + } else if (mem.eql(u8, arg, "-fClang")) { + use_clang = true; + } else if (mem.eql(u8, arg, "-fno-Clang")) { + use_clang = false; + } else if (mem.eql(u8, arg, "-rdynamic")) { + rdynamic = true; + } else if (mem.eql(u8, arg, "-femit-bin")) { + emit_bin = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-bin=")) { + emit_bin = .{ .yes = arg["-femit-bin=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-bin")) { + emit_bin = .no; + } else if (mem.eql(u8, arg, "-femit-zir")) { + emit_zir = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-zir=")) { + emit_zir = .{ .yes = arg["-femit-zir=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-zir")) { + emit_zir = .no; + } else if (mem.eql(u8, arg, "-femit-h")) { + emit_h = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-h=")) { + emit_h = .{ .yes = arg["-femit-h=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-h")) { + emit_h = .no; + } else if (mem.eql(u8, arg, "-femit-asm")) { + emit_asm = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-asm=")) { + emit_asm = .{ .yes = arg["-femit-asm=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-asm")) { + emit_asm = .no; + } else if (mem.eql(u8, arg, "-femit-llvm-ir")) { + emit_llvm_ir = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) { + emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) { + emit_llvm_ir = .no; + } else if (mem.eql(u8, arg, "-femit-docs")) { + emit_docs = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-docs=")) { + emit_docs = .{ .yes = arg["-femit-docs=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-docs")) { + emit_docs = .no; + } else if (mem.eql(u8, arg, "-femit-analysis")) { + emit_analysis = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-analysis=")) { + emit_analysis = .{ .yes = arg["-femit-analysis=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-analysis")) { + emit_analysis = .no; + } else if (mem.eql(u8, arg, "-dynamic")) { + link_mode = .Dynamic; + } else if (mem.eql(u8, arg, "-static")) { + link_mode = .Static; + } else if (mem.eql(u8, arg, "-fdll-export-fns")) { + dll_export_fns = true; + } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) { + dll_export_fns = false; + } else if (mem.eql(u8, arg, "--show-builtin")) { + show_builtin = true; + emit_bin = .no; + } else if (mem.eql(u8, arg, "--strip")) { + strip = true; + } else if (mem.eql(u8, arg, "--single-threaded")) { + single_threaded = true; + } else if (mem.eql(u8, arg, "-ffunction-sections")) { + function_sections = true; + } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { + link_eh_frame_hdr = true; + } else if (mem.eql(u8, arg, "-Bsymbolic")) { + linker_bind_global_refs_locally = true; + } else if (mem.eql(u8, arg, "--verbose-link")) { + verbose_link = true; + } else if (mem.eql(u8, arg, "--verbose-cc")) { + verbose_cc = true; + } else if (mem.eql(u8, arg, "--verbose-tokenize")) { + verbose_tokenize = true; + } else if (mem.eql(u8, arg, "--verbose-ast")) { + verbose_ast = true; + } else if (mem.eql(u8, arg, "--verbose-ir")) { + verbose_ir = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { + verbose_llvm_ir = true; + } else if (mem.eql(u8, arg, "--verbose-cimport")) { + verbose_cimport = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { + verbose_llvm_cpu_features = true; + } else if (mem.startsWith(u8, arg, "-T")) { + linker_script = arg[2..]; + } else if (mem.startsWith(u8, arg, "-L")) { + try lib_dirs.append(arg[2..]); + } else if (mem.startsWith(u8, arg, "-F")) { + try framework_dirs.append(arg[2..]); + } else if (mem.startsWith(u8, arg, "-l")) { + // We don't know whether this library is part of libc or libc++ until we resolve the target. + // So we simply append to the list for now. + try system_libs.append(arg[2..]); + } else if (mem.startsWith(u8, arg, "-D") or + mem.startsWith(u8, arg, "-I")) + { + try clang_argv.append(arg); + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else switch (Compilation.classifyFileExt(arg)) { + .object, .static_library => { + try link_objects.append(arg); + }, + .assembly, .c, .cpp, .h, .ll, .bc => { + try c_source_files.append(.{ + .src_path = arg, + .extra_flags = try arena.dupe([]const u8, extra_cflags.items), + }); + }, + .shared_library => { + fatal("linking against dynamic libraries not yet supported", .{}); + }, + .zig, .zir => { + if (root_src_file) |other| { + fatal("found another zig file '{}' after root source file '{}'", .{ arg, other }); + } else { + root_src_file = arg; + } + }, + .unknown => { + fatal("unrecognized file extension of parameter '{}'", .{arg}); + }, + } + } + if (optimize_mode_string) |s| { + optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse + fatal("unrecognized optimization mode: '{}'", .{s}); + } + }, + .cc, .cpp => { + emit_h = .no; + strip = true; + ensure_libc_on_non_freestanding = true; + ensure_libcpp_on_non_freestanding = arg_mode == .cpp; + want_native_include_dirs = true; + + const COutMode = enum { + link, + object, + assembly, + preprocessor, + }; + var c_out_mode: COutMode = .link; + var out_path: ?[]const u8 = null; + var is_shared_lib = false; + var linker_args = std.ArrayList([]const u8).init(arena); + var it = ClangArgIterator.init(arena, all_args); + while (it.has_next) { + it.next() catch |err| { + fatal("unable to parse command line parameters: {}", .{@errorName(err)}); + }; + switch (it.zig_equivalent) { + .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown + .o => out_path = it.only_arg, // -o + .c => c_out_mode = .object, // -c + .asm_only => c_out_mode = .assembly, // -S + .preprocess_only => c_out_mode = .preprocessor, // -E + .other => { + try clang_argv.appendSlice(it.other_args); + }, + .positional => { + const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg)); + switch (file_ext) { + .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }), + .unknown, .shared_library, .object, .static_library => { + try link_objects.append(it.only_arg); + }, + .zig, .zir => { + if (root_src_file) |other| { + fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other }); + } else { + root_src_file = it.only_arg; + } + }, + } + }, + .l => { + // -l + // We don't know whether this library is part of libc or libc++ until we resolve the target. + // So we simply append to the list for now. + try system_libs.append(it.only_arg); + }, + .ignore => {}, + .driver_punt => { + // Never mind what we're doing, just pass the args directly. For example --help. + return punt_to_clang(arena, all_args); + }, + .pic => want_pic = true, + .no_pic => want_pic = false, + .nostdlib => ensure_libc_on_non_freestanding = false, + .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false, + .shared => { + link_mode = .Dynamic; + is_shared_lib = true; + }, + .rdynamic => rdynamic = true, + .wl => { + var split_it = mem.split(it.only_arg, ","); + while (split_it.next()) |linker_arg| { + try linker_args.append(linker_arg); + } + }, + .optimize => { + // Alright, what release mode do they want? + if (mem.eql(u8, it.only_arg, "Os")) { + optimize_mode = .ReleaseSmall; + } else if (mem.eql(u8, it.only_arg, "O2") or + mem.eql(u8, it.only_arg, "O3") or + mem.eql(u8, it.only_arg, "O4")) + { + optimize_mode = .ReleaseFast; + } else if (mem.eql(u8, it.only_arg, "Og") or + mem.eql(u8, it.only_arg, "O0")) + { + optimize_mode = .Debug; + } else { + try clang_argv.appendSlice(it.other_args); + } + }, + .debug => { + strip = false; + if (mem.eql(u8, it.only_arg, "-g")) { + // We handled with strip = false above. + } else { + try clang_argv.appendSlice(it.other_args); + } + }, + .sanitize => { + if (mem.eql(u8, it.only_arg, "undefined")) { + want_sanitize_c = true; + } else { + try clang_argv.appendSlice(it.other_args); + } + }, + .linker_script => linker_script = it.only_arg, + .verbose_cmds => { + verbose_cc = true; + verbose_link = true; + }, + .for_linker => try linker_args.append(it.only_arg), + .linker_input_z => { + try linker_args.append("-z"); + try linker_args.append(it.only_arg); + }, + .lib_dir => try lib_dirs.append(it.only_arg), + .mcpu => target_mcpu = it.only_arg, + .dep_file => { + disable_c_depfile = true; + try clang_argv.appendSlice(it.other_args); + }, + .framework_dir => try framework_dirs.append(it.only_arg), + .framework => try frameworks.append(it.only_arg), + .nostdlibinc => want_native_include_dirs = false, + } + } + // Parse linker args. + var i: usize = 0; + while (i < linker_args.items.len) : (i += 1) { + const arg = linker_args.items[i]; + if (mem.eql(u8, arg, "-soname")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + const soname = linker_args.items[i]; + override_soname = soname; + // Use it as --name. + // Example: libsoundio.so.2 + var prefix: usize = 0; + if (mem.startsWith(u8, soname, "lib")) { + prefix = 3; + } + var end: usize = soname.len; + if (mem.endsWith(u8, soname, ".so")) { + end -= 3; + } else { + var found_digit = false; + while (end > 0 and std.ascii.isDigit(soname[end - 1])) { + found_digit = true; + end -= 1; + } + if (found_digit and end > 0 and soname[end - 1] == '.') { + end -= 1; + } else { + end = soname.len; + } + if (mem.endsWith(u8, soname[prefix..end], ".so")) { + end -= 3; + } + } + provided_name = soname[prefix..end]; + } else if (mem.eql(u8, arg, "-rpath")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + try rpath_list.append(linker_args.items[i]); + } else if (mem.eql(u8, arg, "-I") or + mem.eql(u8, arg, "--dynamic-linker") or + mem.eql(u8, arg, "-dynamic-linker")) + { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + target_dynamic_linker = linker_args.items[i]; + } else if (mem.eql(u8, arg, "-E") or + mem.eql(u8, arg, "--export-dynamic") or + mem.eql(u8, arg, "-export-dynamic")) + { + rdynamic = true; + } else if (mem.eql(u8, arg, "--version-script")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + version_script = linker_args.items[i]; + } else if (mem.startsWith(u8, arg, "-O")) { + try lld_argv.append(arg); + } else if (mem.eql(u8, arg, "--gc-sections")) { + linker_gc_sections = true; + } else if (mem.eql(u8, arg, "--no-gc-sections")) { + linker_gc_sections = false; + } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or + mem.eql(u8, arg, "-allow-shlib-undefined")) + { + linker_allow_shlib_undefined = true; + } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or + mem.eql(u8, arg, "-no-allow-shlib-undefined")) + { + linker_allow_shlib_undefined = false; + } else if (mem.eql(u8, arg, "-Bsymbolic")) { + linker_bind_global_refs_locally = true; + } else if (mem.eql(u8, arg, "-z")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + const z_arg = linker_args.items[i]; + if (mem.eql(u8, z_arg, "nodelete")) { + linker_z_nodelete = true; + } else if (mem.eql(u8, z_arg, "defs")) { + linker_z_defs = true; + } else { + warn("unsupported linker arg: -z {}", .{z_arg}); + } + } else if (mem.eql(u8, arg, "--major-image-version")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + have_version = true; + } else if (mem.eql(u8, arg, "--minor-image-version")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + have_version = true; + } else if (mem.eql(u8, arg, "--stack")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + } else { + warn("unsupported linker arg: {}", .{arg}); + } + } + + if (want_sanitize_c) |wsc| { + if (wsc and optimize_mode == .ReleaseFast) { + optimize_mode = .ReleaseSafe; + } + } + + switch (c_out_mode) { + .link => { + output_mode = if (is_shared_lib) .Lib else .Exe; + emit_bin = .{ .yes = out_path orelse "a.out" }; + enable_cache = true; + }, + .object => { + output_mode = .Obj; + if (out_path) |p| { + emit_bin = .{ .yes = p }; + } else { + emit_bin = .yes_default_path; + } + }, + .assembly => { + output_mode = .Obj; + emit_bin = .no; + if (out_path) |p| { + emit_asm = .{ .yes = p }; + } else { + emit_asm = .yes_default_path; + } + }, + .preprocessor => { + output_mode = .Obj; + // An error message is generated when there is more than 1 C source file. + if (c_source_files.items.len != 1) { + // For example `zig cc` and no args should print the "no input files" message. + return punt_to_clang(arena, all_args); + } + if (out_path) |p| { + emit_bin = .{ .yes = p }; + clang_preprocessor_mode = .yes; + } else { + clang_preprocessor_mode = .stdout; + } + }, + } + if (c_source_files.items.len == 0 and link_objects.items.len == 0) { + // For example `zig cc` and no args should print the "no input files" message. + return punt_to_clang(arena, all_args); + } + }, + } + + if (arg_mode == .translate_c and c_source_files.items.len != 1) { + fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); + } + + const root_name = if (provided_name) |n| n else blk: { + if (arg_mode == .zig_test) { + break :blk "test"; + } else if (root_src_file) |file| { + const basename = fs.path.basename(file); + break :blk mem.split(basename, ".").next().?; + } else if (c_source_files.items.len == 1) { + const basename = fs.path.basename(c_source_files.items[0].src_path); + break :blk mem.split(basename, ".").next().?; + } else if (link_objects.items.len == 1) { + const basename = fs.path.basename(link_objects.items[0]); + break :blk mem.split(basename, ".").next().?; + } else if (emit_bin == .yes) { + const basename = fs.path.basename(emit_bin.yes); + break :blk mem.split(basename, ".").next().?; + } else if (show_builtin) { + break :blk "builtin"; + } else if (arg_mode == .run) { + break :blk "run"; + } else { + fatal("--name [name] not provided and unable to infer", .{}); + } + }; + + var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{}; + const cross_target = std.zig.CrossTarget.parse(.{ + .arch_os_abi = target_arch_os_abi, + .cpu_features = target_mcpu, + .dynamic_linker = target_dynamic_linker, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpuModel => { + help: { + var help_text = std.ArrayList(u8).init(arena); + for (diags.arch.?.allCpuModels()) |cpu| { + help_text.writer().print(" {}\n", .{cpu.name}) catch break :help; + } + std.log.info("Available CPUs for architecture '{}': {}", .{ + @tagName(diags.arch.?), help_text.items, + }); + } + fatal("Unknown CPU: '{}'", .{diags.cpu_name.?}); + }, + error.UnknownCpuFeature => { + help: { + var help_text = std.ArrayList(u8).init(arena); + for (diags.arch.?.allFeaturesList()) |feature| { + help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help; + } + std.log.info("Available CPU features for architecture '{}': {}", .{ + @tagName(diags.arch.?), help_text.items, + }); + } + fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name}); + }, + else => |e| return e, + }; + + const target_info = try detectNativeTargetInfo(gpa, cross_target); + + if (target_info.target.os.tag != .freestanding) { + if (ensure_libc_on_non_freestanding) + link_libc = true; + if (ensure_libcpp_on_non_freestanding) + link_libcpp = true; + } + + // Now that we have target info, we can find out if any of the system libraries + // are part of libc or libc++. We remove them from the list and communicate their + // existence via flags instead. + { + var i: usize = 0; + while (i < system_libs.items.len) { + const lib_name = system_libs.items[i]; + if (target_util.is_libc_lib_name(target_info.target, lib_name)) { + link_libc = true; + _ = system_libs.orderedRemove(i); + continue; + } + if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) { + link_libcpp = true; + _ = system_libs.orderedRemove(i); + continue; + } + i += 1; + } + } + + if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) { + const paths = std.zig.system.NativePaths.detect(arena) catch |err| { + fatal("unable to detect native system paths: {}", .{@errorName(err)}); + }; + for (paths.warnings.items) |warning| { + warn("{}", .{warning}); + } + try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); + for (paths.include_dirs.items) |include_dir| { + clang_argv.appendAssumeCapacity("-isystem"); + clang_argv.appendAssumeCapacity(include_dir); + } + for (paths.lib_dirs.items) |lib_dir| { + try lib_dirs.append(lib_dir); + } + for (paths.rpaths.items) |rpath| { + try rpath_list.append(rpath); + } + } + + const object_format: std.Target.ObjectFormat = blk: { + const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat(); + if (mem.eql(u8, ofmt, "elf")) { + break :blk .elf; + } else if (mem.eql(u8, ofmt, "c")) { + break :blk .c; + } else if (mem.eql(u8, ofmt, "coff")) { + break :blk .coff; + } else if (mem.eql(u8, ofmt, "pe")) { + break :blk .pe; + } else if (mem.eql(u8, ofmt, "macho")) { + break :blk .macho; + } else if (mem.eql(u8, ofmt, "wasm")) { + break :blk .wasm; + } else if (mem.eql(u8, ofmt, "hex")) { + break :blk .hex; + } else if (mem.eql(u8, ofmt, "raw")) { + break :blk .raw; + } else { + fatal("unsupported object format: {}", .{ofmt}); + } + }; + + if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) { + const total_obj_count = c_source_files.items.len + + @boolToInt(root_src_file != null) + + link_objects.items.len; + if (total_obj_count > 1) { + fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)}); + } + } + + var cleanup_emit_bin_dir: ?fs.Dir = null; + defer if (cleanup_emit_bin_dir) |*dir| dir.close(); + + const have_enable_cache = enable_cache orelse false; + const optional_version = if (have_version) version else null; + + const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) { + .no => null, + .yes_default_path => Compilation.EmitLoc{ + .directory = blk: { + switch (arg_mode) { + .run, .zig_test => break :blk null, + else => { + if (have_enable_cache) { + break :blk null; + } else { + break :blk .{ .path = null, .handle = fs.cwd() }; + } + }, + } + }, + .basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target_info.target, + .output_mode = output_mode, + .link_mode = link_mode, + .object_format = object_format, + .version = optional_version, + }), + }, + .yes => |full_path| b: { + const basename = fs.path.basename(full_path); + if (have_enable_cache) { + break :b Compilation.EmitLoc{ + .basename = basename, + .directory = null, + }; + } + if (fs.path.dirname(full_path)) |dirname| { + const handle = fs.cwd().openDir(dirname, .{}) catch |err| { + fatal("unable to open output directory '{}': {}", .{ dirname, @errorName(err) }); + }; + cleanup_emit_bin_dir = handle; + break :b Compilation.EmitLoc{ + .basename = basename, + .directory = .{ + .path = dirname, + .handle = handle, + }, + }; + } else { + break :b Compilation.EmitLoc{ + .basename = basename, + .directory = .{ .path = null, .handle = fs.cwd() }, + }; + } + }, + }; + + const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}); + var emit_h_resolved = try emit_h.resolve(default_h_basename); + defer emit_h_resolved.deinit(); + + const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name}); + var emit_asm_resolved = try emit_asm.resolve(default_asm_basename); + defer emit_asm_resolved.deinit(); + + const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{}.ll", .{root_name}); + var emit_llvm_ir_resolved = try emit_llvm_ir.resolve(default_llvm_ir_basename); + defer emit_llvm_ir_resolved.deinit(); + + const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name}); + var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename); + defer emit_analysis_resolved.deinit(); + + var emit_docs_resolved = try emit_docs.resolve("docs"); + defer emit_docs_resolved.deinit(); + + const zir_out_path: ?[]const u8 = switch (emit_zir) { + .no => null, + .yes_default_path => blk: { + if (root_src_file) |rsf| { + if (mem.endsWith(u8, rsf, ".zir")) { + break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name}); + } + } + break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name}); + }, + .yes => |p| p, + }; + + var cleanup_root_dir: ?fs.Dir = null; + defer if (cleanup_root_dir) |*dir| dir.close(); + + const root_pkg: ?*Package = if (root_src_file) |src_path| blk: { + if (main_pkg_path) |p| { + const dir = try fs.cwd().openDir(p, .{}); + cleanup_root_dir = dir; + root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir }; + root_pkg_memory.root_src_path = try fs.path.relative(arena, p, src_path); + } else { + root_pkg_memory.root_src_directory = .{ .path = null, .handle = fs.cwd() }; + root_pkg_memory.root_src_path = src_path; + } + break :blk &root_pkg_memory; + } else null; + + const self_exe_path = try fs.selfExePathAlloc(arena); + var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| + .{ + .path = lib_dir, + .handle = try fs.cwd().openDir(lib_dir, .{}), + } + else + introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + + const random_seed = blk: { + var random_seed: u64 = undefined; + try std.crypto.randomBytes(mem.asBytes(&random_seed)); + break :blk random_seed; + }; + var default_prng = std.rand.DefaultPrng.init(random_seed); + + var libc_installation: ?LibCInstallation = null; + defer if (libc_installation) |*l| l.deinit(gpa); + + if (libc_paths_file) |paths_file| { + libc_installation = LibCInstallation.parse(gpa, paths_file) catch |err| { + fatal("unable to parse libc paths file: {}", .{@errorName(err)}); + }; + } + + var global_cache_directory: Compilation.Directory = l: { + const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + }; + defer global_cache_directory.handle.close(); + + var cleanup_local_cache_dir: ?fs.Dir = null; + defer if (cleanup_local_cache_dir) |*dir| dir.close(); + + var local_cache_directory: Compilation.Directory = l: { + if (override_local_cache_dir) |local_cache_dir_path| { + const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}); + cleanup_local_cache_dir = dir; + break :l .{ + .handle = dir, + .path = local_cache_dir_path, + }; + } + if (arg_mode == .run) { + break :l global_cache_directory; + } + const cache_dir_path = blk: { + if (root_pkg) |pkg| { + if (pkg.root_src_directory.path) |p| { + break :blk try fs.path.join(arena, &[_][]const u8{ p, "zig-cache" }); + } + } + break :blk "zig-cache"; + }; + const cache_parent_dir = if (root_pkg) |pkg| pkg.root_src_directory.handle else fs.cwd(); + const dir = try cache_parent_dir.makeOpenPath("zig-cache", .{}); + cleanup_local_cache_dir = dir; + break :l .{ + .handle = dir, + .path = cache_dir_path, + }; + }; + + if (build_options.have_llvm and emit_asm != .no) { + // LLVM has no way to set this non-globally. + const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; + @import("llvm.zig").ParseCommandLineOptions(argv.len, &argv); + } + + gimmeMoreOfThoseSweetSweetFileDescriptors(); + + const comp = Compilation.create(gpa, .{ + .zig_lib_directory = zig_lib_directory, + .local_cache_directory = local_cache_directory, + .global_cache_directory = global_cache_directory, + .root_name = root_name, + .target = target_info.target, + .is_native_os = cross_target.isNativeOs(), + .dynamic_linker = target_info.dynamic_linker.get(), + .output_mode = output_mode, + .root_pkg = root_pkg, + .emit_bin = emit_bin_loc, + .emit_h = emit_h_resolved.data, + .emit_asm = emit_asm_resolved.data, + .emit_llvm_ir = emit_llvm_ir_resolved.data, + .emit_docs = emit_docs_resolved.data, + .emit_analysis = emit_analysis_resolved.data, + .link_mode = link_mode, + .dll_export_fns = dll_export_fns, + .object_format = object_format, + .optimize_mode = optimize_mode, + .keep_source_files_loaded = zir_out_path != null, + .clang_argv = clang_argv.items, + .lld_argv = lld_argv.items, + .lib_dirs = lib_dirs.items, + .rpath_list = rpath_list.items, + .c_source_files = c_source_files.items, + .link_objects = link_objects.items, + .framework_dirs = framework_dirs.items, + .frameworks = frameworks.items, + .system_libs = system_libs.items, + .link_libc = link_libc, + .link_libcpp = link_libcpp, + .want_pic = want_pic, + .want_sanitize_c = want_sanitize_c, + .want_stack_check = want_stack_check, + .want_valgrind = want_valgrind, + .use_llvm = use_llvm, + .use_lld = use_lld, + .use_clang = use_clang, + .rdynamic = rdynamic, + .linker_script = linker_script, + .version_script = version_script, + .disable_c_depfile = disable_c_depfile, + .override_soname = override_soname, + .linker_gc_sections = linker_gc_sections, + .linker_allow_shlib_undefined = linker_allow_shlib_undefined, + .linker_bind_global_refs_locally = linker_bind_global_refs_locally, + .linker_z_nodelete = linker_z_nodelete, + .linker_z_defs = linker_z_defs, + .link_eh_frame_hdr = link_eh_frame_hdr, + .stack_size_override = stack_size_override, + .strip = strip, + .single_threaded = single_threaded, + .function_sections = function_sections, + .self_exe_path = self_exe_path, + .rand = &default_prng.random, + .clang_passthrough_mode = arg_mode != .build, + .clang_preprocessor_mode = clang_preprocessor_mode, + .version = optional_version, + .libc_installation = if (libc_installation) |*lci| lci else null, + .verbose_cc = verbose_cc, + .verbose_link = verbose_link, + .verbose_tokenize = verbose_tokenize, + .verbose_ast = verbose_ast, + .verbose_ir = verbose_ir, + .verbose_llvm_ir = verbose_llvm_ir, + .verbose_cimport = verbose_cimport, + .verbose_llvm_cpu_features = verbose_llvm_cpu_features, + .machine_code_model = machine_code_model, + .color = color, + .time_report = time_report, + .stack_report = stack_report, + .is_test = arg_mode == .zig_test, + .each_lib_rpath = each_lib_rpath, + .test_evented_io = test_evented_io, + .test_filter = test_filter, + .test_name_prefix = test_name_prefix, + .disable_lld_caching = !have_enable_cache, + .subsystem = subsystem, + }) catch |err| { + fatal("unable to create compilation: {}", .{@errorName(err)}); + }; + defer comp.destroy(); + + if (show_builtin) { + return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); + } + if (arg_mode == .translate_c) { + return cmdTranslateC(comp, arena, have_enable_cache); + } + + const hook: AfterUpdateHook = blk: { + if (!have_enable_cache) + break :blk .none; + + switch (emit_bin) { + .no => break :blk .none, + .yes_default_path => break :blk .{ + .print = comp.bin_file.options.emit.?.directory.path orelse ".", + }, + .yes => |full_path| break :blk .{ .update = full_path }, + } + }; + + try updateModule(gpa, comp, zir_out_path, hook); + + if (build_options.is_stage1 and comp.stage1_lock != null and watch) { + warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{}); + } + + switch (arg_mode) { + .run, .zig_test => run: { + const exe_loc = emit_bin_loc orelse break :run; + const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory; + const exe_path = try fs.path.join(arena, &[_][]const u8{ + exe_directory.path orelse ".", exe_loc.basename, + }); + + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); + + if (test_exec_args.items.len == 0) { + if (!std.Target.current.canExecBinariesOf(target_info.target)) { + switch (arg_mode) { + .zig_test => { + warn("created {s} but skipping execution because it is non-native", .{exe_path}); + if (!watch) return cleanExit(); + break :run; + }, + .run => fatal("unable to execute {s}: non-native", .{exe_path}), + else => unreachable, + } + } + try argv.append(exe_path); + } else { + for (test_exec_args.items) |arg| { + try argv.append(arg orelse exe_path); + } + } + if (runtime_args_start) |i| { + try argv.appendSlice(all_args[i..]); + } + // TODO On operating systems that support it, do an execve here rather than child process, + // when watch=false and arg_mode == .run + const child = try std.ChildProcess.init(argv.items, gpa); + defer child.deinit(); + + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = try child.spawnAndWait(); + switch (arg_mode) { + .run => { + switch (term) { + .Exited => |code| { + if (code == 0) { + if (!watch) return cleanExit(); + } else { + // TODO https://github.com/ziglang/zig/issues/6342 + process.exit(1); + } + }, + else => process.exit(1), + } + }, + .zig_test => { + switch (term) { + .Exited => |code| { + if (code == 0) { + if (!watch) return cleanExit(); + } else { + const cmd = try argvCmd(arena, argv.items); + fatal("the following test command failed with exit code {}:\n{}", .{ code, cmd }); + } + }, + else => { + const cmd = try argvCmd(arena, argv.items); + fatal("the following test command crashed:\n{}", .{cmd}); + }, + } + }, + else => unreachable, + } + }, + else => {}, + } + + const stdin = std.io.getStdIn().inStream(); + const stderr = std.io.getStdErr().outStream(); + var repl_buf: [1024]u8 = undefined; + + while (watch) { + try stderr.print("(zig) ", .{}); + if (output_mode == .Exe) { + try comp.makeBinFileExecutable(); + } + if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { + try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); + continue; + }) |line| { + const actual_line = mem.trimRight(u8, line, "\r\n "); + + if (mem.eql(u8, actual_line, "update")) { + if (output_mode == .Exe) { + try comp.makeBinFileWritable(); + } + try updateModule(gpa, comp, zir_out_path, hook); + } else if (mem.eql(u8, actual_line, "exit")) { + break; + } else if (mem.eql(u8, actual_line, "help")) { + try stderr.writeAll(repl_help); + } else { + try stderr.print("unknown command: {}\n", .{actual_line}); + } + } else { + break; + } + } +} + +const AfterUpdateHook = union(enum) { + none, + print: []const u8, + update: []const u8, +}; + +fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, hook: AfterUpdateHook) !void { + try comp.update(); + + var errors = try comp.getAllErrorsAlloc(); + defer errors.deinit(comp.gpa); + + if (errors.list.len != 0) { + for (errors.list) |full_err_msg| { + full_err_msg.renderToStdErr(); + } + } else switch (hook) { + .none => {}, + .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}), + .update => |full_path| _ = try comp.bin_file.options.emit.?.directory.handle.updateFile( + comp.bin_file.options.emit.?.sub_path, + fs.cwd(), + full_path, + .{}, + ), + } + + if (zir_out_path) |zop| { + const module = comp.bin_file.options.module orelse + fatal("-femit-zir with no zig source code", .{}); + var new_zir_module = try zir.emit(gpa, module); + defer new_zir_module.deinit(gpa); + + const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); + defer baf.destroy(); + + try new_zir_module.writeToStream(gpa, baf.stream()); + + try baf.finish(); + } +} + +fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void { + if (!build_options.have_llvm) + fatal("cannot translate-c: compiler built without LLVM extensions", .{}); + + assert(comp.c_source_files.len == 1); + const c_source_file = comp.c_source_files[0]; + + const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name}); + + var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); + defer if (enable_cache) man.deinit(); + + man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects + _ = man.addFile(c_source_file.src_path, null) catch |err| { + fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) }); + }; + + const digest = if (try man.hit()) man.final() else digest: { + var argv = std.ArrayList([]const u8).init(arena); + + var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); + defer zig_cache_tmp_dir.close(); + + const ext = Compilation.classifyFileExt(c_source_file.src_path); + const out_dep_path: ?[]const u8 = blk: { + if (comp.disable_c_depfile or !ext.clangSupportsDepFile()) + break :blk null; + + const c_src_basename = fs.path.basename(c_source_file.src_path); + const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename}); + const out_dep_path = try comp.tmpFilePath(arena, dep_basename); + break :blk out_dep_path; + }; + + try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path); + try argv.append(c_source_file.src_path); + + if (comp.verbose_cc) { + std.debug.print("clang ", .{}); + Compilation.dump_argv(argv.items); + } + + // Convert to null terminated args. + const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); + new_argv_with_sentinel[argv.items.len] = null; + const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); + const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); + var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; + const tree = translate_c.translate( + comp.gpa, + new_argv.ptr, + new_argv.ptr + new_argv.len, + &clang_errors, + c_headers_dir_path_z, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), + error.SemanticAnalyzeFail => { + for (clang_errors) |clang_err| { + std.debug.print("{}:{}:{}: {}\n", .{ + if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", + clang_err.line + 1, + clang_err.column + 1, + clang_err.msg_ptr[0..clang_err.msg_len], + }); + } + process.exit(1); + }, + }; + defer tree.deinit(); + + if (out_dep_path) |dep_file_path| { + const dep_basename = std.fs.path.basename(dep_file_path); + // Add the files depended on to the cache system. + try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); + // Just to save disk space, we delete the file because it is never needed again. + zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { + warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + }; + } + + const digest = man.final(); + const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); + defer o_dir.close(); + var zig_file = try o_dir.createFile(translated_zig_basename, .{}); + defer zig_file.close(); + + var bos = io.bufferedOutStream(zig_file.writer()); + _ = try std.zig.render(comp.gpa, bos.writer(), tree); + try bos.flush(); + + man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)}); + + break :digest digest; + }; + + if (enable_cache) { + const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ + "o", &digest, translated_zig_basename, + }); + try io.getStdOut().writer().print("{}\n", .{full_zig_path}); + return cleanExit(); + } else { + const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); + const zig_file = try comp.local_cache_directory.handle.openFile(out_zig_path, .{}); + defer zig_file.close(); + try io.getStdOut().writeFileAll(zig_file, .{}); + return cleanExit(); + } +} + +pub const usage_libc = + \\Usage: zig libc + \\ + \\ Detect the native libc installation and print the resulting + \\ paths to stdout. You can save this into a file and then edit + \\ the paths to create a cross compilation libc kit. Then you + \\ can pass `--libc [file]` for Zig to use it. + \\ + \\Usage: zig libc [paths_file] + \\ + \\ Parse a libc installation text file and validate it. + \\ +; + +pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { + var input_file: ?[]const u8 = null; + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + const stdout = io.getStdOut().writer(); + try stdout.writeAll(usage_libc); + return cleanExit(); + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else if (input_file != null) { + fatal("unexpected extra parameter: '{}'", .{arg}); + } else { + input_file = arg; + } + } + } + if (input_file) |libc_file| { + var libc = LibCInstallation.parse(gpa, libc_file) catch |err| { + fatal("unable to parse libc file: {}", .{@errorName(err)}); + }; + defer libc.deinit(gpa); + } else { + var libc = LibCInstallation.findNative(.{ + .allocator = gpa, + .verbose = true, + }) catch |err| { + fatal("unable to detect native libc: {}", .{@errorName(err)}); + }; + defer libc.deinit(gpa); + + var bos = io.bufferedOutStream(io.getStdOut().writer()); + try libc.render(bos.writer()); + try bos.flush(); + } +} + +pub const usage_init = + \\Usage: zig init-exe + \\ zig init-lib + \\ + \\ Initializes a `zig build` project in the current working + \\ directory. + \\ + \\Options: + \\ --help Print this help and exit + \\ + \\ +; + +pub fn cmdInit( + gpa: *Allocator, + arena: *Allocator, + args: []const []const u8, + output_mode: std.builtin.OutputMode, +) !void { + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + try io.getStdOut().writeAll(usage_init); + return cleanExit(); + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else { + fatal("unexpected extra parameter: '{}'", .{arg}); + } + } + } + const self_exe_path = try fs.selfExePathAlloc(arena); + var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + + const s = fs.path.sep_str; + const template_sub_path = switch (output_mode) { + .Obj => unreachable, + .Lib => "std" ++ s ++ "special" ++ s ++ "init-lib", + .Exe => "std" ++ s ++ "special" ++ s ++ "init-exe", + }; + var template_dir = try zig_lib_directory.handle.openDir(template_sub_path, .{}); + defer template_dir.close(); + + const cwd_path = try process.getCwdAlloc(arena); + const cwd_basename = fs.path.basename(cwd_path); + + const max_bytes = 10 * 1024 * 1024; + const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| { + fatal("unable to read template file 'build.zig': {}", .{@errorName(err)}); + }; + var modified_build_zig_contents = std.ArrayList(u8).init(arena); + try modified_build_zig_contents.ensureCapacity(build_zig_contents.len); + for (build_zig_contents) |c| { + if (c == '$') { + try modified_build_zig_contents.appendSlice(cwd_basename); + } else { + try modified_build_zig_contents.append(c); + } + } + const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| { + fatal("unable to read template file 'main.zig': {}", .{@errorName(err)}); + }; + if (fs.cwd().access("build.zig", .{})) |_| { + fatal("existing build.zig file would be overwritten", .{}); + } else |err| switch (err) { + error.FileNotFound => {}, + else => fatal("unable to test existence of build.zig: {}\n", .{@errorName(err)}), + } + var src_dir = try fs.cwd().makeOpenPath("src", .{}); + defer src_dir.close(); + + try src_dir.writeFile("main.zig", main_zig_contents); + try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items); + + std.log.info("Created build.zig", .{}); + std.log.info("Created src" ++ s ++ "main.zig", .{}); + + switch (output_mode) { + .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}), + .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}), + .Obj => unreachable, + } +} + +pub const usage_build = + \\Usage: zig build [steps] [options] + \\ + \\ Build a project from build.zig. + \\ + \\Options: + \\ --help Print this help and exit + \\ + \\ +; + +pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { + // We want to release all the locks before executing the child process, so we make a nice + // big block here to ensure the cleanup gets run when we extract out our argv. + const lock_and_argv = lock_and_argv: { + const self_exe_path = try fs.selfExePathAlloc(arena); + + var build_file: ?[]const u8 = null; + var override_lib_dir: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = null; + var override_local_cache_dir: ?[]const u8 = null; + var child_argv = std.ArrayList([]const u8).init(arena); + + const argv_index_exe = child_argv.items.len; + _ = try child_argv.addOne(); + + try child_argv.append(self_exe_path); + + const argv_index_build_file = child_argv.items.len; + _ = try child_argv.addOne(); + + const argv_index_cache_dir = child_argv.items.len; + _ = try child_argv.addOne(); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--build-file")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + build_file = args[i]; + continue; + } else if (mem.eql(u8, arg, "--override-lib-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + override_lib_dir = args[i]; + try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); + continue; + } else if (mem.eql(u8, arg, "--cache-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + override_local_cache_dir = args[i]; + try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); + continue; + } else if (mem.eql(u8, arg, "--global-cache-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + override_global_cache_dir = args[i]; + try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); + continue; + } + } + try child_argv.append(arg); + } + } + + var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| + .{ + .path = lib_dir, + .handle = try fs.cwd().openDir(lib_dir, .{}), + } + else + introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + + const std_special = "std" ++ fs.path.sep_str ++ "special"; + const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special}); + + var root_pkg: Package = .{ + .root_src_directory = .{ + .path = special_dir_path, + .handle = try zig_lib_directory.handle.openDir(std_special, .{}), + }, + .root_src_path = "build_runner.zig", + }; + defer root_pkg.root_src_directory.handle.close(); + + var cleanup_build_dir: ?fs.Dir = null; + defer if (cleanup_build_dir) |*dir| dir.close(); + + const cwd_path = try process.getCwdAlloc(arena); + const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig"; + const build_directory: Compilation.Directory = blk: { + if (build_file) |bf| { + if (fs.path.dirname(bf)) |dirname| { + const dir = try fs.cwd().openDir(dirname, .{}); + cleanup_build_dir = dir; + break :blk .{ .path = dirname, .handle = dir }; + } + + break :blk .{ .path = null, .handle = fs.cwd() }; + } + // Search up parent directories until we find build.zig. + var dirname: []const u8 = cwd_path; + while (true) { + const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); + if (fs.cwd().access(joined_path, .{})) |_| { + const dir = try fs.cwd().openDir(dirname, .{}); + break :blk .{ .path = dirname, .handle = dir }; + } else |err| switch (err) { + error.FileNotFound => { + dirname = fs.path.dirname(dirname) orelse { + std.log.info("{}", .{ + \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`, + \\or see `zig --help` for more options. + }); + fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{}); + }; + continue; + }, + else => |e| return e, + } + } + }; + child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path; + + var build_pkg: Package = .{ + .root_src_directory = build_directory, + .root_src_path = build_zig_basename, + }; + try root_pkg.table.put(arena, "@build", &build_pkg); + + var global_cache_directory: Compilation.Directory = l: { + const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + }; + defer global_cache_directory.handle.close(); + + var local_cache_directory: Compilation.Directory = l: { + if (override_local_cache_dir) |local_cache_dir_path| { + break :l .{ + .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}), + .path = local_cache_dir_path, + }; + } + const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"}); + break :l .{ + .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}), + .path = cache_dir_path, + }; + }; + defer local_cache_directory.handle.close(); + + child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path; + + gimmeMoreOfThoseSweetSweetFileDescriptors(); + + const cross_target: std.zig.CrossTarget = .{}; + const target_info = try detectNativeTargetInfo(gpa, cross_target); + + const exe_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = "build", + .target = target_info.target, + .output_mode = .Exe, + }); + const emit_bin: Compilation.EmitLoc = .{ + .directory = null, // Use the local zig-cache. + .basename = exe_basename, + }; + const random_seed = blk: { + var random_seed: u64 = undefined; + try std.crypto.randomBytes(mem.asBytes(&random_seed)); + break :blk random_seed; + }; + var default_prng = std.rand.DefaultPrng.init(random_seed); + const comp = Compilation.create(gpa, .{ + .zig_lib_directory = zig_lib_directory, + .local_cache_directory = local_cache_directory, + .global_cache_directory = global_cache_directory, + .root_name = "build", + .target = target_info.target, + .is_native_os = cross_target.isNativeOs(), + .dynamic_linker = target_info.dynamic_linker.get(), + .output_mode = .Exe, + .root_pkg = &root_pkg, + .emit_bin = emit_bin, + .emit_h = null, + .optimize_mode = .Debug, + .self_exe_path = self_exe_path, + .rand = &default_prng.random, + }) catch |err| { + fatal("unable to create compilation: {}", .{@errorName(err)}); + }; + defer comp.destroy(); + + try updateModule(gpa, comp, null, .none); + + child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join( + arena, + &[_][]const u8{exe_basename}, + ); + + break :lock_and_argv .{ + .child_argv = child_argv.items, + .lock = comp.bin_file.toOwnedLock(), + }; + }; + const child_argv = lock_and_argv.child_argv; + var lock = lock_and_argv.lock; + defer lock.release(); + + const child = try std.ChildProcess.init(child_argv, gpa); + defer child.deinit(); + + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = try child.spawnAndWait(); + switch (term) { + .Exited => |code| { + if (code == 0) return cleanExit(); + const cmd = try argvCmd(arena, child_argv); + fatal("the following build command failed with exit code {}:\n{}", .{ code, cmd }); + }, + else => { + const cmd = try argvCmd(arena, child_argv); + fatal("the following build command crashed:\n{}", .{cmd}); + }, + } +} + +fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 { + var cmd = std.ArrayList(u8).init(allocator); + defer cmd.deinit(); + for (argv[0 .. argv.len - 1]) |arg| { + try cmd.appendSlice(arg); + try cmd.append(' '); + } + try cmd.appendSlice(argv[argv.len - 1]); + return cmd.toOwnedSlice(); +} + +pub const usage_fmt = + \\Usage: zig fmt [file]... + \\ + \\ Formats the input files and modifies them in-place. + \\ Arguments can be files or directories, which are searched + \\ recursively. + \\ + \\Options: + \\ --help Print this help and exit + \\ --color [auto|off|on] Enable or disable colored error messages + \\ --stdin Format code from stdin; output to stdout + \\ --check List non-conforming files and exit with an error + \\ if the list is non-empty + \\ + \\ +; + +const Fmt = struct { + seen: SeenMap, + any_error: bool, + color: Color, + gpa: *Allocator, + out_buffer: std.ArrayList(u8), + + const SeenMap = std.AutoHashMap(fs.File.INode, void); +}; + +pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { + const stderr_file = io.getStdErr(); + var color: Color = .Auto; + var stdin_flag: bool = false; + var check_flag: bool = false; + var input_files = ArrayList([]const u8).init(gpa); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + const stdout = io.getStdOut().outStream(); + try stdout.writeAll(usage_fmt); + return cleanExit(); + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + fatal("expected [auto|on|off] after --color", .{}); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + } + } else if (mem.eql(u8, arg, "--stdin")) { + stdin_flag = true; + } else if (mem.eql(u8, arg, "--check")) { + check_flag = true; + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else { + try input_files.append(arg); + } + } + } + + if (stdin_flag) { + if (input_files.items.len != 0) { + fatal("cannot use --stdin with positional arguments", .{}); + } + + const stdin = io.getStdIn().inStream(); + + const source_code = try stdin.readAllAlloc(gpa, max_src_size); + defer gpa.free(source_code); + + const tree = std.zig.parse(gpa, source_code) catch |err| { + fatal("error parsing stdin: {}", .{err}); + }; + defer tree.deinit(); + + for (tree.errors) |parse_error| { + try printErrMsgToFile(gpa, parse_error, tree, "", stderr_file, color); + } + if (tree.errors.len != 0) { + process.exit(1); + } + if (check_flag) { + const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree); + const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); + process.exit(code); + } + + var bos = io.bufferedOutStream(io.getStdOut().writer()); + _ = try std.zig.render(gpa, bos.writer(), tree); + try bos.flush(); + return; + } + + if (input_files.items.len == 0) { + fatal("expected at least one source file argument", .{}); + } + + var fmt = Fmt{ + .gpa = gpa, + .seen = Fmt.SeenMap.init(gpa), + .any_error = false, + .color = color, + .out_buffer = std.ArrayList(u8).init(gpa), + }; + defer fmt.seen.deinit(); + defer fmt.out_buffer.deinit(); + + for (input_files.span()) |file_path| { + // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. + const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { + fatal("unable to open '{}': {}", .{ file_path, err }); + }; + defer gpa.free(real_path); + + try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path); + } + if (fmt.any_error) { + process.exit(1); + } +} + +const FmtError = error{ + SystemResources, + OperationAborted, + IoPending, + BrokenPipe, + Unexpected, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + OutOfMemory, + RenameAcrossMountPoints, + ReadOnlyFileSystem, + LinkQuotaExceeded, + FileBusy, + EndOfStream, + Unseekable, + NotOpenForWriting, +} || fs.File.OpenError; + +fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { + fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), + else => { + warn("unable to format '{}': {}", .{ file_path, err }); + fmt.any_error = true; + return; + }, + }; +} + +fn fmtPathDir( + fmt: *Fmt, + file_path: []const u8, + check_mode: bool, + parent_dir: fs.Dir, + parent_sub_path: []const u8, +) FmtError!void { + var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true }); + defer dir.close(); + + const stat = try dir.stat(); + if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; + + var dir_it = dir.iterate(); + while (try dir_it.next()) |entry| { + const is_dir = entry.kind == .Directory; + if (is_dir or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); + defer fmt.gpa.free(full_path); + + if (is_dir) { + try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); + } else { + fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { + warn("unable to format '{}': {}", .{ full_path, err }); + fmt.any_error = true; + return; + }; + } + } + } +} + +fn fmtPathFile( + fmt: *Fmt, + file_path: []const u8, + check_mode: bool, + dir: fs.Dir, + sub_path: []const u8, +) FmtError!void { + const source_file = try dir.openFile(sub_path, .{}); + var file_closed = false; + errdefer if (!file_closed) source_file.close(); + + const stat = try source_file.stat(); + + if (stat.kind == .Directory) + return error.IsDir; + + const source_code = source_file.readToEndAllocOptions( + fmt.gpa, + max_src_size, + stat.size, + @alignOf(u8), + null, + ) catch |err| switch (err) { + error.ConnectionResetByPeer => unreachable, + error.ConnectionTimedOut => unreachable, + error.NotOpenForReading => unreachable, + else => |e| return e, + }; + source_file.close(); + file_closed = true; + defer fmt.gpa.free(source_code); + + // Add to set after no longer possible to get error.IsDir. + if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; + + const tree = try std.zig.parse(fmt.gpa, source_code); + defer tree.deinit(); + + for (tree.errors) |parse_error| { + try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color); + } + if (tree.errors.len != 0) { + fmt.any_error = true; + return; + } + + if (check_mode) { + const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); + if (anything_changed) { + // TODO this should output to stdout instead of stderr. + std.debug.print("{}\n", .{file_path}); + fmt.any_error = true; + } + } else { + // As a heuristic, we make enough capacity for the same as the input source. + try fmt.out_buffer.ensureCapacity(source_code.len); + fmt.out_buffer.items.len = 0; + const writer = fmt.out_buffer.writer(); + const anything_changed = try std.zig.render(fmt.gpa, writer, tree); + if (!anything_changed) + return; // Good thing we didn't waste any file system access on this. + + var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); + defer af.deinit(); + + try af.file.writeAll(fmt.out_buffer.items); + try af.finish(); + // TODO this should output to stdout instead of stderr. + std.debug.print("{}\n", .{file_path}); + } +} + +fn printErrMsgToFile( + gpa: *mem.Allocator, + parse_error: ast.Error, + tree: *ast.Tree, + path: []const u8, + file: fs.File, + color: Color, +) !void { + const color_on = switch (color) { + .Auto => file.isTty(), + .On => true, + .Off => false, + }; + const lok_token = parse_error.loc(); + const span_first = lok_token; + const span_last = lok_token; + + const first_token = tree.token_locs[span_first]; + const last_token = tree.token_locs[span_last]; + const start_loc = tree.tokenLocationLoc(0, first_token); + const end_loc = tree.tokenLocationLoc(first_token.end, last_token); + + var text_buf = std.ArrayList(u8).init(gpa); + defer text_buf.deinit(); + const out_stream = text_buf.outStream(); + try parse_error.render(tree.token_ids, out_stream); + const text = text_buf.span(); + + const stream = file.outStream(); + try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); + + if (!color_on) return; + + // Print \r and \t as one space each so that column counts line up + for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| { + try stream.writeByte(switch (byte) { + '\r', '\t' => ' ', + else => byte, + }); + } + try stream.writeByte('\n'); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.writeByte('\n'); +} + +pub const info_zen = + \\ + \\ * Communicate intent precisely. + \\ * Edge cases matter. + \\ * Favor reading code over writing code. + \\ * Only one obvious way to do things. + \\ * Runtime crashes are better than bugs. + \\ * Compile errors are better than runtime crashes. + \\ * Incremental improvements. + \\ * Avoid local maximums. + \\ * Reduce the amount one must remember. + \\ * Focus on code rather than style. + \\ * Resource allocation may fail; resource deallocation must succeed. + \\ * Memory is a resource. + \\ * Together we serve the users. + \\ + \\ +; + +extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; + +/// TODO https://github.com/ziglang/zig/issues/3257 +fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} { + if (!build_options.have_llvm) + fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); + // Convert the args to the format Clang expects. + const argv = try arena.alloc(?[*:0]u8, args.len + 1); + for (args) |arg, i| { + argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + } + argv[args.len] = null; + const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr); + process.exit(@bitCast(u8, @truncate(i8, exit_code))); +} + +const clang_args = @import("clang_options.zig").list; + +pub const ClangArgIterator = struct { + has_next: bool, + zig_equivalent: ZigEquivalent, + only_arg: []const u8, + second_arg: []const u8, + other_args: []const []const u8, + argv: []const []const u8, + next_index: usize, + root_args: ?*Args, + allocator: *Allocator, + + pub const ZigEquivalent = enum { + target, + o, + c, + other, + positional, + l, + ignore, + driver_punt, + pic, + no_pic, + nostdlib, + nostdlib_cpp, + shared, + rdynamic, + wl, + preprocess_only, + asm_only, + optimize, + debug, + sanitize, + linker_script, + verbose_cmds, + for_linker, + linker_input_z, + lib_dir, + mcpu, + dep_file, + framework_dir, + framework, + nostdlibinc, + }; + + const Args = struct { + next_index: usize, + argv: []const []const u8, + }; + + fn init(allocator: *Allocator, argv: []const []const u8) ClangArgIterator { + return .{ + .next_index = 2, // `zig cc foo` this points to `foo` + .has_next = argv.len > 2, + .zig_equivalent = undefined, + .only_arg = undefined, + .second_arg = undefined, + .other_args = undefined, + .argv = argv, + .root_args = null, + .allocator = allocator, + }; + } + + fn next(self: *ClangArgIterator) !void { + assert(self.has_next); + assert(self.next_index < self.argv.len); + // In this state we know that the parameter we are looking at is a root parameter + // rather than an argument to a parameter. + // We adjust the len below when necessary. + self.other_args = (self.argv.ptr + self.next_index)[0..1]; + var arg = mem.span(self.argv[self.next_index]); + self.incrementArgIndex(); + + if (mem.startsWith(u8, arg, "@")) { + if (self.root_args != null) return error.NestedResponseFile; + + // This is a "compiler response file". We must parse the file and treat its + // contents as command line parameters. + const allocator = self.allocator; + const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit + const resp_file_path = arg[1..]; + const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { + fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) }); + }; + defer allocator.free(resp_contents); + // TODO is there a specification for this file format? Let's find it and make this parsing more robust + // at the very least I'm guessing this needs to handle quotes and `#` comments. + var it = mem.tokenize(resp_contents, " \t\r\n"); + var resp_arg_list = std.ArrayList([]const u8).init(allocator); + defer resp_arg_list.deinit(); + { + errdefer { + for (resp_arg_list.span()) |item| { + allocator.free(mem.span(item)); + } + } + while (it.next()) |token| { + const dupe_token = try mem.dupeZ(allocator, u8, token); + errdefer allocator.free(dupe_token); + try resp_arg_list.append(dupe_token); + } + const args = try allocator.create(Args); + errdefer allocator.destroy(args); + args.* = .{ + .next_index = self.next_index, + .argv = self.argv, + }; + self.root_args = args; + } + const resp_arg_slice = resp_arg_list.toOwnedSlice(); + self.next_index = 0; + self.argv = resp_arg_slice; + + if (resp_arg_slice.len == 0) { + self.resolveRespFileArgs(); + return; + } + + self.has_next = true; + self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary. + arg = mem.span(self.argv[self.next_index]); + self.incrementArgIndex(); + } + if (!mem.startsWith(u8, arg, "-")) { + self.zig_equivalent = .positional; + self.only_arg = arg; + return; + } + + find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) { + .flag => { + const prefix_len = clang_arg.matchEql(arg); + if (prefix_len > 0) { + self.zig_equivalent = clang_arg.zig_equivalent; + self.only_arg = arg[prefix_len..]; + + break :find_clang_arg; + } + }, + .joined, .comma_joined => { + // joined example: --target=foo + // comma_joined example: -Wl,-soname,libsoundio.so.2 + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len != 0) { + self.zig_equivalent = clang_arg.zig_equivalent; + self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part. + + break :find_clang_arg; + } + }, + .joined_or_separate => { + // Examples: `-lfoo`, `-l foo` + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len == arg.len) { + if (self.next_index >= self.argv.len) { + fatal("Expected parameter after '{}'", .{arg}); + } + self.only_arg = self.argv[self.next_index]; + self.incrementArgIndex(); + self.other_args.len += 1; + self.zig_equivalent = clang_arg.zig_equivalent; + + break :find_clang_arg; + } else if (prefix_len != 0) { + self.zig_equivalent = clang_arg.zig_equivalent; + self.only_arg = arg[prefix_len..]; + + break :find_clang_arg; + } + }, + .joined_and_separate => { + // Example: `-Xopenmp-target=riscv64-linux-unknown foo` + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len != 0) { + self.only_arg = arg[prefix_len..]; + if (self.next_index >= self.argv.len) { + fatal("Expected parameter after '{}'", .{arg}); + } + self.second_arg = self.argv[self.next_index]; + self.incrementArgIndex(); + self.other_args.len += 1; + self.zig_equivalent = clang_arg.zig_equivalent; + break :find_clang_arg; + } + }, + .separate => if (clang_arg.matchEql(arg) > 0) { + if (self.next_index >= self.argv.len) { + fatal("Expected parameter after '{}'", .{arg}); + } + self.only_arg = self.argv[self.next_index]; + self.incrementArgIndex(); + self.other_args.len += 1; + self.zig_equivalent = clang_arg.zig_equivalent; + break :find_clang_arg; + }, + .remaining_args_joined => { + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len != 0) { + @panic("TODO"); + } + }, + .multi_arg => if (clang_arg.matchEql(arg) > 0) { + @panic("TODO"); + }, + } + else { + fatal("Unknown Clang option: '{}'", .{arg}); + } + } + + fn incrementArgIndex(self: *ClangArgIterator) void { + self.next_index += 1; + self.resolveRespFileArgs(); + } + + fn resolveRespFileArgs(self: *ClangArgIterator) void { + const allocator = self.allocator; + if (self.next_index >= self.argv.len) { + if (self.root_args) |root_args| { + self.next_index = root_args.next_index; + self.argv = root_args.argv; + + allocator.destroy(root_args); + self.root_args = null; + } + if (self.next_index >= self.argv.len) { + self.has_next = false; + } + } + } +}; + +fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { + return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse + fatal("unsupported machine code model: '{}'", .{arg}); +} + +/// Raise the open file descriptor limit. Ask and ye shall receive. +/// For one example of why this is handy, consider the case of building musl libc. +/// We keep a lock open for each of the object files in the form of a file descriptor +/// until they are finally put into an archive file. This is to allow a zig-cache +/// garbage collector to run concurrently to zig processes, and to allow multiple +/// zig processes to run concurrently with each other, without clobbering each other. +fn gimmeMoreOfThoseSweetSweetFileDescriptors() void { + switch (std.Target.current.os.tag) { + .windows, .wasi, .uefi, .other, .freestanding => return, + // std lib is missing getrlimit/setrlimit. + // https://github.com/ziglang/zig/issues/6361 + //else => {}, + else => return, + } + const posix = std.os; + var lim = posix.getrlimit(posix.RLIMIT_NOFILE, &lim) catch return; // Oh well; we tried. + if (lim.cur == lim.max) return; + while (true) { + // Do a binary search for the limit. + var min: posix.rlim_t = lim.cur; + var max: posix.rlim_t = 1 << 20; + // But if there's a defined upper bound, don't search, just set it. + if (lim.max != posix.RLIM_INFINITY) { + min = lim.max; + max = lim.max; + } + while (true) { + lim.cur = min + (max - min) / 2; + if (posix.setrlimit(posix.RLIMIT_NOFILE, lim)) |_| { + min = lim.cur; + } else |_| { + max = lim.cur; + } + if (min + 1 < max) continue; + return; + } + } +} + +test "fds" { + gimmeMoreOfThoseSweetSweetFileDescriptors(); +} + +fn detectNativeCpuWithLLVM( + arch: std.Target.Cpu.Arch, + llvm_cpu_name_z: ?[*:0]const u8, + llvm_cpu_features_opt: ?[*:0]const u8, +) !std.Target.Cpu { + var result = std.Target.Cpu.baseline(arch); + + if (llvm_cpu_name_z) |cpu_name_z| { + const llvm_cpu_name = mem.spanZ(cpu_name_z); + + for (arch.allCpuModels()) |model| { + const this_llvm_name = model.llvm_name orelse continue; + if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { + // Here we use the non-dependencies-populated set, + // so that subtracting features later in this function + // affect the prepopulated set. + result = std.Target.Cpu{ + .arch = arch, + .model = model, + .features = model.features, + }; + break; + } + } + } + + const all_features = arch.allFeaturesList(); + + if (llvm_cpu_features_opt) |llvm_cpu_features| { + var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ","); + while (it.next()) |decorated_llvm_feat| { + var op: enum { + add, + sub, + } = undefined; + var llvm_feat: []const u8 = undefined; + if (mem.startsWith(u8, decorated_llvm_feat, "+")) { + op = .add; + llvm_feat = decorated_llvm_feat[1..]; + } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) { + op = .sub; + llvm_feat = decorated_llvm_feat[1..]; + } else { + return error.InvalidLlvmCpuFeaturesFormat; + } + for (all_features) |feature, index_usize| { + const this_llvm_name = feature.llvm_name orelse continue; + if (mem.eql(u8, llvm_feat, this_llvm_name)) { + const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize); + switch (op) { + .add => result.features.addFeature(index), + .sub => result.features.removeFeature(index), + } + break; + } + } + } + } + + result.features.populateDependencies(all_features); + return result; +} + +fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { + var info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target); + if (info.cpu_detection_unimplemented) { + const arch = std.Target.current.cpu.arch; + + // We want to just use detected_info.target but implementing + // CPU model & feature detection is todo so here we rely on LLVM. + // https://github.com/ziglang/zig/issues/4591 + if (!build_options.have_llvm) + fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)}); + + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + cross_target.updateCpuFeatures(&info.target.cpu.features); + info.target.cpu.arch = cross_target.getCpuArch(); + } + return info; +} + +/// Indicate that we are now terminating with a successful exit code. +/// In debug builds, this is a no-op, so that the calling code's +/// cleanup mechanisms are tested and so that external tools that +/// check for resource leaks can be accurate. In release builds, this +/// calls exit(0), and does not return. +pub fn cleanExit() void { + if (std.builtin.mode == .Debug) { + return; + } else { + process.exit(0); + } +} diff --git a/src/mem_profile.cpp b/src/mem_profile.cpp deleted file mode 100644 index 13ba57f913..0000000000 --- a/src/mem_profile.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "config.h" - -#ifdef ZIG_ENABLE_MEM_PROFILE - -#include "mem.hpp" -#include "mem_list.hpp" -#include "mem_profile.hpp" -#include "heap.hpp" - -namespace mem { - -void Profile::init(const char *name, const char *kind) { - this->name = name; - this->kind = kind; - this->usage_table.init(heap::bootstrap_allocator, 1024); -} - -void Profile::deinit() { - assert(this->name != nullptr); - if (mem::report_print) - this->print_report(); - this->usage_table.deinit(heap::bootstrap_allocator); - this->name = nullptr; -} - -void Profile::record_alloc(const TypeInfo &info, size_t count) { - if (count == 0) return; - auto existing_entry = this->usage_table.put_unique( - heap::bootstrap_allocator, - UsageKey{info.name_ptr, info.name_len}, - Entry{info, 1, count, 0, 0} ); - if (existing_entry != nullptr) { - assert(existing_entry->value.info.size == info.size); // allocated name does not match type - existing_entry->value.alloc.calls += 1; - existing_entry->value.alloc.objects += count; - } -} - -void Profile::record_dealloc(const TypeInfo &info, size_t count) { - if (count == 0) return; - auto existing_entry = this->usage_table.maybe_get(UsageKey{info.name_ptr, info.name_len}); - if (existing_entry == nullptr) { - fprintf(stderr, "deallocated name '"); - for (size_t i = 0; i < info.name_len; ++i) - fputc(info.name_ptr[i], stderr); - zig_panic("' (size %zu) not found in allocated table; compromised memory usage stats", info.size); - } - if (existing_entry->value.info.size != info.size) { - fprintf(stderr, "deallocated name '"); - for (size_t i = 0; i < info.name_len; ++i) - fputc(info.name_ptr[i], stderr); - zig_panic("' does not match expected type size %zu", info.size); - } - assert(existing_entry->value.alloc.calls - existing_entry->value.dealloc.calls > 0); - assert(existing_entry->value.alloc.objects - existing_entry->value.dealloc.objects >= count); - existing_entry->value.dealloc.calls += 1; - existing_entry->value.dealloc.objects += count; -} - -static size_t entry_remain_total_bytes(const Profile::Entry *entry) { - return (entry->alloc.objects - entry->dealloc.objects) * entry->info.size; -} - -static int entry_compare(const void *a, const void *b) { - size_t total_a = entry_remain_total_bytes(*reinterpret_cast(a)); - size_t total_b = entry_remain_total_bytes(*reinterpret_cast(b)); - if (total_a > total_b) - return -1; - if (total_a < total_b) - return 1; - return 0; -}; - -void Profile::print_report(FILE *file) { - if (!file) { - file = report_file; - if (!file) - file = stderr; - } - fprintf(file, "\n--- MEMORY PROFILE REPORT [%s]: %s ---\n", this->kind, this->name); - - List list; - auto it = this->usage_table.entry_iterator(); - for (;;) { - auto entry = it.next(); - if (!entry) - break; - list.append(&heap::bootstrap_allocator, &entry->value); - } - - qsort(list.items, list.length, sizeof(const Entry *), entry_compare); - - size_t total_bytes_alloc = 0; - size_t total_bytes_dealloc = 0; - - size_t total_calls_alloc = 0; - size_t total_calls_dealloc = 0; - - for (size_t i = 0; i < list.length; i += 1) { - const Entry *entry = list.at(i); - fprintf(file, " "); - for (size_t j = 0; j < entry->info.name_len; ++j) - fputc(entry->info.name_ptr[j], file); - fprintf(file, ": %zu bytes each", entry->info.size); - - fprintf(file, ", alloc{ %zu calls, %zu objects, total ", entry->alloc.calls, entry->alloc.objects); - const auto alloc_num_bytes = entry->alloc.objects * entry->info.size; - zig_pretty_print_bytes(file, alloc_num_bytes); - - fprintf(file, " }, dealloc{ %zu calls, %zu objects, total ", entry->dealloc.calls, entry->dealloc.objects); - const auto dealloc_num_bytes = entry->dealloc.objects * entry->info.size; - zig_pretty_print_bytes(file, dealloc_num_bytes); - - fprintf(file, " }, remain{ %zu calls, %zu objects, total ", - entry->alloc.calls - entry->dealloc.calls, - entry->alloc.objects - entry->dealloc.objects ); - const auto remain_num_bytes = alloc_num_bytes - dealloc_num_bytes; - zig_pretty_print_bytes(file, remain_num_bytes); - - fprintf(file, " }\n"); - - total_bytes_alloc += alloc_num_bytes; - total_bytes_dealloc += dealloc_num_bytes; - - total_calls_alloc += entry->alloc.calls; - total_calls_dealloc += entry->dealloc.calls; - } - - fprintf(file, "\n Total bytes allocated: "); - zig_pretty_print_bytes(file, total_bytes_alloc); - fprintf(file, ", deallocated: "); - zig_pretty_print_bytes(file, total_bytes_dealloc); - fprintf(file, ", remaining: "); - zig_pretty_print_bytes(file, total_bytes_alloc - total_bytes_dealloc); - - fprintf(file, "\n Total calls alloc: %zu, dealloc: %zu, remain: %zu\n", - total_calls_alloc, total_calls_dealloc, (total_calls_alloc - total_calls_dealloc)); - - list.deinit(&heap::bootstrap_allocator); -} - -uint32_t Profile::usage_hash(UsageKey key) { - // FNV 32-bit hash - uint32_t h = 2166136261; - for (size_t i = 0; i < key.name_len; ++i) { - h = h ^ key.name_ptr[i]; - h = h * 16777619; - } - return h; -} - -bool Profile::usage_equal(UsageKey a, UsageKey b) { - return memcmp(a.name_ptr, b.name_ptr, a.name_len > b.name_len ? a.name_len : b.name_len) == 0; -} - -void InternCounters::print_report(FILE *file) { - if (!file) { - file = report_file; - if (!file) - file = stderr; - } - fprintf(file, "\n--- IR INTERNING REPORT ---\n"); - fprintf(file, " undefined: interned %zu times\n", intern_counters.x_undefined); - fprintf(file, " void: interned %zu times\n", intern_counters.x_void); - fprintf(file, " null: interned %zu times\n", intern_counters.x_null); - fprintf(file, " unreachable: interned %zu times\n", intern_counters.x_unreachable); - fprintf(file, " zero_byte: interned %zu times\n", intern_counters.zero_byte); -} - -InternCounters intern_counters; - -} // namespace mem - -#endif diff --git a/src/mem_profile.hpp b/src/mem_profile.hpp deleted file mode 100644 index 3b13b7680b..0000000000 --- a/src/mem_profile.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_MEM_PROFILE_HPP -#define ZIG_MEM_PROFILE_HPP - -#include "config.h" - -#ifdef ZIG_ENABLE_MEM_PROFILE - -#include - -#include "mem.hpp" -#include "mem_hash_map.hpp" -#include "util.hpp" - -namespace mem { - -struct Profile { - void init(const char *name, const char *kind); - void deinit(); - - void record_alloc(const TypeInfo &info, size_t count); - void record_dealloc(const TypeInfo &info, size_t count); - - void print_report(FILE *file = nullptr); - - struct Entry { - TypeInfo info; - - struct Use { - size_t calls; - size_t objects; - } alloc, dealloc; - }; - -private: - const char *name; - const char *kind; - - struct UsageKey { - const char *name_ptr; - size_t name_len; - }; - - static uint32_t usage_hash(UsageKey key); - static bool usage_equal(UsageKey a, UsageKey b); - - HashMap usage_table; -}; - -struct InternCounters { - size_t x_undefined; - size_t x_void; - size_t x_null; - size_t x_unreachable; - size_t zero_byte; - - void print_report(FILE *file = nullptr); -}; - -extern InternCounters intern_counters; - -} // namespace mem - -#endif -#endif diff --git a/src/mem_type_info.hpp b/src/mem_type_info.hpp deleted file mode 100644 index 8698992ca0..0000000000 --- a/src/mem_type_info.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_MEM_TYPE_INFO_HPP -#define ZIG_MEM_TYPE_INFO_HPP - -#include "config.h" - -#ifndef ZIG_TYPE_INFO_IMPLEMENTATION -# ifdef ZIG_ENABLE_MEM_PROFILE -# define ZIG_TYPE_INFO_IMPLEMENTATION 1 -# else -# define ZIG_TYPE_INFO_IMPLEMENTATION 0 -# endif -#endif - -namespace mem { - -#if ZIG_TYPE_INFO_IMPLEMENTATION == 0 - -struct TypeInfo { - size_t size; - size_t alignment; - - template - static constexpr TypeInfo make() { - return {sizeof(T), alignof(T)}; - } -}; - -#elif ZIG_TYPE_INFO_IMPLEMENTATION == 1 - -// -// A non-portable way to get a human-readable type-name compatible with -// non-RTTI C++ compiler mode; eg. `-fno-rtti`. -// -// Minimum requirements are c++11 and a compiler that has a constant for the -// current function's decorated name whereby a template-type name can be -// computed. eg. `__PRETTY_FUNCTION__` or `__FUNCSIG__`. -// -// given the following snippet: -// -// | #include -// | -// | struct Top {}; -// | namespace mynamespace { -// | using custom = unsigned int; -// | struct Foo { -// | struct Bar {}; -// | }; -// | }; -// | -// | template -// | void foobar() { -// | #ifdef _MSC_VER -// | fprintf(stderr, "--> %s\n", __FUNCSIG__); -// | #else -// | fprintf(stderr, "--> %s\n", __PRETTY_FUNCTION__); -// | #endif -// | } -// | -// | int main() { -// | foobar(); -// | foobar(); -// | foobar(); -// | foobar(); -// | foobar(); -// | } -// -// gcc 9.2.0 produces: -// --> void foobar() [with T = Top] -// --> void foobar() [with T = unsigned int] -// --> void foobar() [with T = unsigned int] -// --> void foobar() [with T = mynamespace::Foo*] -// --> void foobar() [with T = mynamespace::Foo::Bar*] -// -// xcode 11.3.1/clang produces: -// --> void foobar() [T = Top] -// --> void foobar() [T = unsigned int] -// --> void foobar() [T = unsigned int] -// --> void foobar() [T = mynamespace::Foo *] -// --> void foobar() [T = mynamespace::Foo::Bar *] -// -// VStudio 2019 16.5.0/msvc produces: -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// -struct TypeInfo { - const char *name_ptr; - size_t name_len; - size_t size; - size_t alignment; - - static constexpr TypeInfo to_type_info(const char *str, size_t start, size_t end, size_t size, size_t alignment) { - return TypeInfo{str + start, end - start, size, alignment}; - } - - static constexpr size_t index_of(const char *str, char c) { - return *str == c ? 0 : 1 + index_of(str + 1, c); - } - - template - static constexpr const char *decorated_name() { -#ifdef _MSC_VER - return __FUNCSIG__; -#else - return __PRETTY_FUNCTION__; -#endif - } - - static constexpr TypeInfo extract(const char *decorated, size_t size, size_t alignment) { -#ifdef _MSC_VER - return to_type_info(decorated, index_of(decorated, '<') + 1, index_of(decorated, '>'), size, alignment); -#else - return to_type_info(decorated, index_of(decorated, '=') + 2, index_of(decorated, ']'), size, alignment); -#endif - } - - template - static constexpr TypeInfo make() { - return TypeInfo::extract(TypeInfo::decorated_name(), sizeof(T), alignof(T)); - } -}; - -#endif // ZIG_TYPE_INFO_IMPLEMENTATION - -} // namespace mem - -#endif diff --git a/src/mingw.zig b/src/mingw.zig new file mode 100644 index 0000000000..b6c8591ea4 --- /dev/null +++ b/src/mingw.zig @@ -0,0 +1,1092 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; +const log = std.log.scoped(.mingw); + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const Cache = @import("Cache.zig"); + +pub const CRTFile = enum { + crt2_o, + dllcrt2_o, + mingw32_lib, + msvcrt_os_lib, + mingwex_lib, + uuid_lib, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crt2_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-U__CRTDLL__", + "-D__MSVCRT__", + // Uncomment these 3 things for crtu + //"-DUNICODE", + //"-D_UNICODE", + //"-DWPRFLAG=1", + }); + return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", "crtexe.c", + }), + .extra_flags = args.items, + }, + }); + }, + + .dllcrt2_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-U__CRTDLL__", + "-D__MSVCRT__", + }); + return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", "crtdll.c", + }), + .extra_flags = args.items, + }, + }); + }, + + .mingw32_lib => { + var c_source_files: [mingw32_lib_deps.len]Compilation.CSourceFile = undefined; + for (mingw32_lib_deps) |dep, i| { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-DHAVE_CONFIG_H", + "-D_SYSCRT=1", + "-DCRTDLL=1", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "include", "any-windows-any", + }), + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + }); + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", dep, + }), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("mingw32", .Lib, &c_source_files); + }, + + .msvcrt_os_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + "-D__LIBMSVCRT__", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + + "-g", + "-O2", + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + + for (msvcrt_common_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", dep }), + .extra_flags = extra_flags, + }; + } + if (comp.getTarget().cpu.arch == .i386) { + for (msvcrt_i386_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else { + for (msvcrt_other_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } + return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items); + }, + + .mingwex_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + + for (mingwex_generic_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + const target = comp.getTarget(); + if (target.cpu.arch == .i386 or target.cpu.arch == .x86_64) { + for (mingwex_x86_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else if (target.cpu.arch.isARM()) { + if (target.cpu.arch.ptrBitWidth() == 32) { + for (mingwex_arm32_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else { + for (mingwex_arm64_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } + } else { + unreachable; + } + return comp.build_crt_file("mingwex", .Lib, c_source_files.items); + }, + + .uuid_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "include", "any-windows-any", + }), + }); + var c_source_files: [uuid_src.len]Compilation.CSourceFile = undefined; + for (uuid_src) |dep, i| { + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "libsrc", dep, + }), + .extra_flags = extra_flags, + }; + } + return comp.build_crt_file("uuid", .Lib, &c_source_files); + }, + } +} + +fn add_cc_args( + comp: *Compilation, + arena: *Allocator, + args: *std.ArrayList([]const u8), +) error{OutOfMemory}!void { + try args.appendSlice(&[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + }); + + const target = comp.getTarget(); + if (target.cpu.arch.isARM() and target.cpu.arch.ptrBitWidth() == 32) { + try args.append("-mfpu=vfp"); + } + + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + }); +} + +pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) { + error.FileNotFound => { + log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name }); + // In this case we will end up putting foo.lib onto the linker line and letting the linker + // use its library paths to look for libraries and report any problems. + return; + }, + else => |e| return e, + }; + + // We need to invoke `zig clang` to use the preprocessor. + if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; + const self_exe_path = comp.self_exe_path orelse return error.PreprocessorDisabled; + + const target = comp.getTarget(); + + var cache: Cache = .{ + .gpa = comp.gpa, + .manifest_dir = comp.cache_parent.manifest_dir, + }; + cache.hash.addBytes(build_options.version); + cache.hash.addOptionalBytes(comp.zig_lib_directory.path); + cache.hash.add(target.cpu.arch); + + var man = cache.obtain(); + defer man.deinit(); + + _ = try man.addFile(def_file_path, null); + + const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name}); + errdefer comp.gpa.free(final_lib_basename); + + if (try man.hit()) { + const digest = man.final(); + + try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); + comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{ + .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ + "o", &digest, final_lib_basename, + }), + .lock = man.toOwnedLock(), + }); + return; + } + + const digest = man.final(); + const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var o_dir = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}); + defer o_dir.close(); + + const final_def_basename = try std.fmt.allocPrint(arena, "{s}.def", .{lib_name}); + const def_final_path = try comp.global_cache_directory.join(arena, &[_][]const u8{ + "o", &digest, final_def_basename, + }); + + const target_def_arg = switch (target.cpu.arch) { + .i386 => "-DDEF_I386", + .x86_64 => "-DDEF_X64", + .arm, .armeb, .thumb, .thumbeb, .aarch64_32 => "-DDEF_ARM32", + .aarch64, .aarch64_be => "-DDEF_ARM64", + else => unreachable, + }; + + const args = [_][]const u8{ + self_exe_path, + "clang", + "-x", + "c", + def_file_path, + "-Wp,-w", + "-undef", + "-P", + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }), + target_def_arg, + "-E", + "-o", + def_final_path, + }; + + if (comp.verbose_cc) { + Compilation.dump_argv(&args); + } + + const child = try std.ChildProcess.init(&args, arena); + defer child.deinit(); + + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stdout_reader = child.stdout.?.reader(); + const stderr_reader = child.stderr.?.reader(); + + // TODO https://github.com/ziglang/zig/issues/6343 + const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + // TODO surface a proper error here + log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) }); + return error.ClangPreprocessorFailed; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO surface a proper error here + log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); + return error.ClangPreprocessorFailed; + } + }, + else => { + // TODO surface a proper error here + log.err("clang terminated unexpectedly with stderr: {}", .{stderr}); + return error.ClangPreprocessorFailed; + }, + } + + const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ + "o", &digest, final_lib_basename, + }); + errdefer comp.gpa.free(lib_final_path); + + const llvm = @import("llvm.zig"); + const arch_type = @import("target.zig").archToLLVM(target.cpu.arch); + const def_final_path_z = try arena.dupeZ(u8, def_final_path); + const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); + if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) { + // TODO surface a proper error here + log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); + return error.WritingImportLibFailed; + } + + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) }); + }; + + try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{ + .full_object_path = lib_final_path, + .lock = man.toOwnedLock(), + }); +} + +/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists. +fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u8 { + const target = comp.getTarget(); + + const lib_path = switch (target.cpu.arch) { + .i386 => "lib32", + .x86_64 => "lib64", + .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) { + 32 => "libarm32", + 64 => "libarm64", + else => unreachable, + }, + else => unreachable, + }; + + var override_path = std.ArrayList(u8).init(allocator); + defer override_path.deinit(); + + const s = path.sep_str; + + { + // Try the archtecture-specific path first. + const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def"; + if (comp.zig_lib_directory.path) |p| { + try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name }); + } else { + try override_path.writer().print(fmt_path, .{ lib_path, lib_name }); + } + if (std.fs.cwd().access(override_path.items, .{})) |_| { + return override_path.toOwnedSlice(); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + } + + { + // Try the generic version. + override_path.shrinkRetainingCapacity(0); + const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def"; + if (comp.zig_lib_directory.path) |p| { + try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); + } else { + try override_path.writer().print(fmt_path, .{lib_name}); + } + if (std.fs.cwd().access(override_path.items, .{})) |_| { + return override_path.toOwnedSlice(); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + } + + { + // Try the generic version and preprocess it. + override_path.shrinkRetainingCapacity(0); + const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in"; + if (comp.zig_lib_directory.path) |p| { + try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); + } else { + try override_path.writer().print(fmt_path, .{lib_name}); + } + if (std.fs.cwd().access(override_path.items, .{})) |_| { + return override_path.toOwnedSlice(); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + } + + return error.FileNotFound; +} + +const mingw32_lib_deps = [_][]const u8{ + "crt0_c.c", + "dll_argv.c", + "gccmain.c", + "natstart.c", + "pseudo-reloc-list.c", + "wildcard.c", + "charmax.c", + "crt0_w.c", + "dllargv.c", + "gs_support.c", + "_newmode.c", + "tlssup.c", + "xncommod.c", + "cinitexe.c", + "merr.c", + "usermatherr.c", + "pesect.c", + "udllargc.c", + "xthdloc.c", + "CRT_fp10.c", + "mingw_helpers.c", + "pseudo-reloc.c", + "udll_argv.c", + "xtxtmode.c", + "crt_handler.c", + "tlsthrd.c", + "tlsmthread.c", + "tlsmcrt.c", + "cxa_atexit.c", +}; +const msvcrt_common_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "_create_locale.c", + "misc" ++ path.sep_str ++ "_free_locale.c", + "misc" ++ path.sep_str ++ "onexit_table.c", + "misc" ++ path.sep_str ++ "register_tls_atexit.c", + "stdio" ++ path.sep_str ++ "acrt_iob_func.c", + "misc" ++ path.sep_str ++ "_configthreadlocale.c", + "misc" ++ path.sep_str ++ "_get_current_locale.c", + "misc" ++ path.sep_str ++ "invalid_parameter_handler.c", + "misc" ++ path.sep_str ++ "output_format.c", + "misc" ++ path.sep_str ++ "purecall.c", + "secapi" ++ path.sep_str ++ "_access_s.c", + "secapi" ++ path.sep_str ++ "_cgets_s.c", + "secapi" ++ path.sep_str ++ "_cgetws_s.c", + "secapi" ++ path.sep_str ++ "_chsize_s.c", + "secapi" ++ path.sep_str ++ "_controlfp_s.c", + "secapi" ++ path.sep_str ++ "_cprintf_s.c", + "secapi" ++ path.sep_str ++ "_cprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_ctime32_s.c", + "secapi" ++ path.sep_str ++ "_ctime64_s.c", + "secapi" ++ path.sep_str ++ "_cwprintf_s.c", + "secapi" ++ path.sep_str ++ "_cwprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_gmtime32_s.c", + "secapi" ++ path.sep_str ++ "_gmtime64_s.c", + "secapi" ++ path.sep_str ++ "_localtime32_s.c", + "secapi" ++ path.sep_str ++ "_localtime64_s.c", + "secapi" ++ path.sep_str ++ "_mktemp_s.c", + "secapi" ++ path.sep_str ++ "_sopen_s.c", + "secapi" ++ path.sep_str ++ "_strdate_s.c", + "secapi" ++ path.sep_str ++ "_strtime_s.c", + "secapi" ++ path.sep_str ++ "_umask_s.c", + "secapi" ++ path.sep_str ++ "_vcprintf_s.c", + "secapi" ++ path.sep_str ++ "_vcprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_vcwprintf_s.c", + "secapi" ++ path.sep_str ++ "_vcwprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_vscprintf_p.c", + "secapi" ++ path.sep_str ++ "_vscwprintf_p.c", + "secapi" ++ path.sep_str ++ "_vswprintf_p.c", + "secapi" ++ path.sep_str ++ "_waccess_s.c", + "secapi" ++ path.sep_str ++ "_wasctime_s.c", + "secapi" ++ path.sep_str ++ "_wctime32_s.c", + "secapi" ++ path.sep_str ++ "_wctime64_s.c", + "secapi" ++ path.sep_str ++ "_wstrtime_s.c", + "secapi" ++ path.sep_str ++ "_wmktemp_s.c", + "secapi" ++ path.sep_str ++ "_wstrdate_s.c", + "secapi" ++ path.sep_str ++ "asctime_s.c", + "secapi" ++ path.sep_str ++ "memcpy_s.c", + "secapi" ++ path.sep_str ++ "memmove_s.c", + "secapi" ++ path.sep_str ++ "rand_s.c", + "secapi" ++ path.sep_str ++ "sprintf_s.c", + "secapi" ++ path.sep_str ++ "strerror_s.c", + "secapi" ++ path.sep_str ++ "vsprintf_s.c", + "secapi" ++ path.sep_str ++ "wmemcpy_s.c", + "secapi" ++ path.sep_str ++ "wmemmove_s.c", + "stdio" ++ path.sep_str ++ "mingw_lock.c", +}; +const msvcrt_i386_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "lc_locale_func.c", + "misc" ++ path.sep_str ++ "___mb_cur_max_func.c", +}; + +const msvcrt_other_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "__p___argv.c", + "misc" ++ path.sep_str ++ "__p__acmdln.c", + "misc" ++ path.sep_str ++ "__p__fmode.c", + "misc" ++ path.sep_str ++ "__p__wcmdln.c", +}; +const mingwex_generic_src = [_][]const u8{ + "complex" ++ path.sep_str ++ "_cabs.c", + "complex" ++ path.sep_str ++ "cabs.c", + "complex" ++ path.sep_str ++ "cabsf.c", + "complex" ++ path.sep_str ++ "cabsl.c", + "complex" ++ path.sep_str ++ "cacos.c", + "complex" ++ path.sep_str ++ "cacosf.c", + "complex" ++ path.sep_str ++ "cacosl.c", + "complex" ++ path.sep_str ++ "carg.c", + "complex" ++ path.sep_str ++ "cargf.c", + "complex" ++ path.sep_str ++ "cargl.c", + "complex" ++ path.sep_str ++ "casin.c", + "complex" ++ path.sep_str ++ "casinf.c", + "complex" ++ path.sep_str ++ "casinl.c", + "complex" ++ path.sep_str ++ "catan.c", + "complex" ++ path.sep_str ++ "catanf.c", + "complex" ++ path.sep_str ++ "catanl.c", + "complex" ++ path.sep_str ++ "ccos.c", + "complex" ++ path.sep_str ++ "ccosf.c", + "complex" ++ path.sep_str ++ "ccosl.c", + "complex" ++ path.sep_str ++ "cexp.c", + "complex" ++ path.sep_str ++ "cexpf.c", + "complex" ++ path.sep_str ++ "cexpl.c", + "complex" ++ path.sep_str ++ "cimag.c", + "complex" ++ path.sep_str ++ "cimagf.c", + "complex" ++ path.sep_str ++ "cimagl.c", + "complex" ++ path.sep_str ++ "clog.c", + "complex" ++ path.sep_str ++ "clog10.c", + "complex" ++ path.sep_str ++ "clog10f.c", + "complex" ++ path.sep_str ++ "clog10l.c", + "complex" ++ path.sep_str ++ "clogf.c", + "complex" ++ path.sep_str ++ "clogl.c", + "complex" ++ path.sep_str ++ "conj.c", + "complex" ++ path.sep_str ++ "conjf.c", + "complex" ++ path.sep_str ++ "conjl.c", + "complex" ++ path.sep_str ++ "cpow.c", + "complex" ++ path.sep_str ++ "cpowf.c", + "complex" ++ path.sep_str ++ "cpowl.c", + "complex" ++ path.sep_str ++ "cproj.c", + "complex" ++ path.sep_str ++ "cprojf.c", + "complex" ++ path.sep_str ++ "cprojl.c", + "complex" ++ path.sep_str ++ "creal.c", + "complex" ++ path.sep_str ++ "crealf.c", + "complex" ++ path.sep_str ++ "creall.c", + "complex" ++ path.sep_str ++ "csin.c", + "complex" ++ path.sep_str ++ "csinf.c", + "complex" ++ path.sep_str ++ "csinl.c", + "complex" ++ path.sep_str ++ "csqrt.c", + "complex" ++ path.sep_str ++ "csqrtf.c", + "complex" ++ path.sep_str ++ "csqrtl.c", + "complex" ++ path.sep_str ++ "ctan.c", + "complex" ++ path.sep_str ++ "ctanf.c", + "complex" ++ path.sep_str ++ "ctanl.c", + "crt" ++ path.sep_str ++ "dllentry.c", + "crt" ++ path.sep_str ++ "dllmain.c", + "gdtoa" ++ path.sep_str ++ "arithchk.c", + "gdtoa" ++ path.sep_str ++ "dmisc.c", + "gdtoa" ++ path.sep_str ++ "dtoa.c", + "gdtoa" ++ path.sep_str ++ "g__fmt.c", + "gdtoa" ++ path.sep_str ++ "g_dfmt.c", + "gdtoa" ++ path.sep_str ++ "g_ffmt.c", + "gdtoa" ++ path.sep_str ++ "g_xfmt.c", + "gdtoa" ++ path.sep_str ++ "gdtoa.c", + "gdtoa" ++ path.sep_str ++ "gethex.c", + "gdtoa" ++ path.sep_str ++ "gmisc.c", + "gdtoa" ++ path.sep_str ++ "hd_init.c", + "gdtoa" ++ path.sep_str ++ "hexnan.c", + "gdtoa" ++ path.sep_str ++ "misc.c", + "gdtoa" ++ path.sep_str ++ "qnan.c", + "gdtoa" ++ path.sep_str ++ "smisc.c", + "gdtoa" ++ path.sep_str ++ "strtodg.c", + "gdtoa" ++ path.sep_str ++ "strtodnrp.c", + "gdtoa" ++ path.sep_str ++ "strtof.c", + "gdtoa" ++ path.sep_str ++ "strtopx.c", + "gdtoa" ++ path.sep_str ++ "sum.c", + "gdtoa" ++ path.sep_str ++ "ulp.c", + "math" ++ path.sep_str ++ "abs64.c", + "math" ++ path.sep_str ++ "cbrt.c", + "math" ++ path.sep_str ++ "cbrtf.c", + "math" ++ path.sep_str ++ "cbrtl.c", + "math" ++ path.sep_str ++ "cephes_emath.c", + "math" ++ path.sep_str ++ "copysign.c", + "math" ++ path.sep_str ++ "copysignf.c", + "math" ++ path.sep_str ++ "coshf.c", + "math" ++ path.sep_str ++ "coshl.c", + "math" ++ path.sep_str ++ "erfl.c", + "math" ++ path.sep_str ++ "expf.c", + "math" ++ path.sep_str ++ "fabs.c", + "math" ++ path.sep_str ++ "fabsf.c", + "math" ++ path.sep_str ++ "fabsl.c", + "math" ++ path.sep_str ++ "fdim.c", + "math" ++ path.sep_str ++ "fdimf.c", + "math" ++ path.sep_str ++ "fdiml.c", + "math" ++ path.sep_str ++ "fma.c", + "math" ++ path.sep_str ++ "fmaf.c", + "math" ++ path.sep_str ++ "fmal.c", + "math" ++ path.sep_str ++ "fmax.c", + "math" ++ path.sep_str ++ "fmaxf.c", + "math" ++ path.sep_str ++ "fmaxl.c", + "math" ++ path.sep_str ++ "fmin.c", + "math" ++ path.sep_str ++ "fminf.c", + "math" ++ path.sep_str ++ "fminl.c", + "math" ++ path.sep_str ++ "fp_consts.c", + "math" ++ path.sep_str ++ "fp_constsf.c", + "math" ++ path.sep_str ++ "fp_constsl.c", + "math" ++ path.sep_str ++ "fpclassify.c", + "math" ++ path.sep_str ++ "fpclassifyf.c", + "math" ++ path.sep_str ++ "fpclassifyl.c", + "math" ++ path.sep_str ++ "frexpf.c", + "math" ++ path.sep_str ++ "hypot.c", + "math" ++ path.sep_str ++ "hypotf.c", + "math" ++ path.sep_str ++ "hypotl.c", + "math" ++ path.sep_str ++ "isnan.c", + "math" ++ path.sep_str ++ "isnanf.c", + "math" ++ path.sep_str ++ "isnanl.c", + "math" ++ path.sep_str ++ "ldexpf.c", + "math" ++ path.sep_str ++ "lgamma.c", + "math" ++ path.sep_str ++ "lgammaf.c", + "math" ++ path.sep_str ++ "lgammal.c", + "math" ++ path.sep_str ++ "llrint.c", + "math" ++ path.sep_str ++ "llrintf.c", + "math" ++ path.sep_str ++ "llrintl.c", + "math" ++ path.sep_str ++ "llround.c", + "math" ++ path.sep_str ++ "llroundf.c", + "math" ++ path.sep_str ++ "llroundl.c", + "math" ++ path.sep_str ++ "log10f.c", + "math" ++ path.sep_str ++ "logf.c", + "math" ++ path.sep_str ++ "lrint.c", + "math" ++ path.sep_str ++ "lrintf.c", + "math" ++ path.sep_str ++ "lrintl.c", + "math" ++ path.sep_str ++ "lround.c", + "math" ++ path.sep_str ++ "lroundf.c", + "math" ++ path.sep_str ++ "lroundl.c", + "math" ++ path.sep_str ++ "modf.c", + "math" ++ path.sep_str ++ "modff.c", + "math" ++ path.sep_str ++ "modfl.c", + "math" ++ path.sep_str ++ "nextafterf.c", + "math" ++ path.sep_str ++ "nextafterl.c", + "math" ++ path.sep_str ++ "nexttoward.c", + "math" ++ path.sep_str ++ "nexttowardf.c", + "math" ++ path.sep_str ++ "powf.c", + "math" ++ path.sep_str ++ "powi.c", + "math" ++ path.sep_str ++ "powif.c", + "math" ++ path.sep_str ++ "powil.c", + "math" ++ path.sep_str ++ "rint.c", + "math" ++ path.sep_str ++ "rintf.c", + "math" ++ path.sep_str ++ "rintl.c", + "math" ++ path.sep_str ++ "round.c", + "math" ++ path.sep_str ++ "roundf.c", + "math" ++ path.sep_str ++ "roundl.c", + "math" ++ path.sep_str ++ "s_erf.c", + "math" ++ path.sep_str ++ "sf_erf.c", + "math" ++ path.sep_str ++ "signbit.c", + "math" ++ path.sep_str ++ "signbitf.c", + "math" ++ path.sep_str ++ "signbitl.c", + "math" ++ path.sep_str ++ "signgam.c", + "math" ++ path.sep_str ++ "sinhf.c", + "math" ++ path.sep_str ++ "sinhl.c", + "math" ++ path.sep_str ++ "sqrt.c", + "math" ++ path.sep_str ++ "sqrtf.c", + "math" ++ path.sep_str ++ "sqrtl.c", + "math" ++ path.sep_str ++ "tanhf.c", + "math" ++ path.sep_str ++ "tanhl.c", + "math" ++ path.sep_str ++ "tgamma.c", + "math" ++ path.sep_str ++ "tgammaf.c", + "math" ++ path.sep_str ++ "tgammal.c", + "math" ++ path.sep_str ++ "truncl.c", + "misc" ++ path.sep_str ++ "alarm.c", + "misc" ++ path.sep_str ++ "basename.c", + "misc" ++ path.sep_str ++ "btowc.c", + "misc" ++ path.sep_str ++ "delay-f.c", + "misc" ++ path.sep_str ++ "delay-n.c", + "misc" ++ path.sep_str ++ "delayimp.c", + "misc" ++ path.sep_str ++ "dirent.c", + "misc" ++ path.sep_str ++ "dirname.c", + "misc" ++ path.sep_str ++ "feclearexcept.c", + "misc" ++ path.sep_str ++ "fegetenv.c", + "misc" ++ path.sep_str ++ "fegetexceptflag.c", + "misc" ++ path.sep_str ++ "fegetround.c", + "misc" ++ path.sep_str ++ "feholdexcept.c", + "misc" ++ path.sep_str ++ "feraiseexcept.c", + "misc" ++ path.sep_str ++ "fesetenv.c", + "misc" ++ path.sep_str ++ "fesetexceptflag.c", + "misc" ++ path.sep_str ++ "fesetround.c", + "misc" ++ path.sep_str ++ "fetestexcept.c", + "misc" ++ path.sep_str ++ "feupdateenv.c", + "misc" ++ path.sep_str ++ "ftruncate.c", + "misc" ++ path.sep_str ++ "ftw.c", + "misc" ++ path.sep_str ++ "ftw64.c", + "misc" ++ path.sep_str ++ "fwide.c", + "misc" ++ path.sep_str ++ "getlogin.c", + "misc" ++ path.sep_str ++ "getopt.c", + "misc" ++ path.sep_str ++ "gettimeofday.c", + "misc" ++ path.sep_str ++ "imaxabs.c", + "misc" ++ path.sep_str ++ "imaxdiv.c", + "misc" ++ path.sep_str ++ "isblank.c", + "misc" ++ path.sep_str ++ "iswblank.c", + "misc" ++ path.sep_str ++ "mbrtowc.c", + "misc" ++ path.sep_str ++ "mbsinit.c", + "misc" ++ path.sep_str ++ "mempcpy.c", + "misc" ++ path.sep_str ++ "mingw-aligned-malloc.c", + "misc" ++ path.sep_str ++ "mingw-fseek.c", + "misc" ++ path.sep_str ++ "mingw_getsp.S", + "misc" ++ path.sep_str ++ "mingw_matherr.c", + "misc" ++ path.sep_str ++ "mingw_mbwc_convert.c", + "misc" ++ path.sep_str ++ "mingw_usleep.c", + "misc" ++ path.sep_str ++ "mingw_wcstod.c", + "misc" ++ path.sep_str ++ "mingw_wcstof.c", + "misc" ++ path.sep_str ++ "mingw_wcstold.c", + "misc" ++ path.sep_str ++ "mkstemp.c", + "misc" ++ path.sep_str ++ "seterrno.c", + "misc" ++ path.sep_str ++ "sleep.c", + "misc" ++ path.sep_str ++ "strnlen.c", + "misc" ++ path.sep_str ++ "strsafe.c", + "misc" ++ path.sep_str ++ "strtoimax.c", + "misc" ++ path.sep_str ++ "strtold.c", + "misc" ++ path.sep_str ++ "strtoumax.c", + "misc" ++ path.sep_str ++ "tdelete.c", + "misc" ++ path.sep_str ++ "tfind.c", + "misc" ++ path.sep_str ++ "tsearch.c", + "misc" ++ path.sep_str ++ "twalk.c", + "misc" ++ path.sep_str ++ "uchar_c16rtomb.c", + "misc" ++ path.sep_str ++ "uchar_c32rtomb.c", + "misc" ++ path.sep_str ++ "uchar_mbrtoc16.c", + "misc" ++ path.sep_str ++ "uchar_mbrtoc32.c", + "misc" ++ path.sep_str ++ "wassert.c", + "misc" ++ path.sep_str ++ "wcrtomb.c", + "misc" ++ path.sep_str ++ "wcsnlen.c", + "misc" ++ path.sep_str ++ "wcstof.c", + "misc" ++ path.sep_str ++ "wcstoimax.c", + "misc" ++ path.sep_str ++ "wcstold.c", + "misc" ++ path.sep_str ++ "wcstoumax.c", + "misc" ++ path.sep_str ++ "wctob.c", + "misc" ++ path.sep_str ++ "wctrans.c", + "misc" ++ path.sep_str ++ "wctype.c", + "misc" ++ path.sep_str ++ "wdirent.c", + "misc" ++ path.sep_str ++ "winbs_uint64.c", + "misc" ++ path.sep_str ++ "winbs_ulong.c", + "misc" ++ path.sep_str ++ "winbs_ushort.c", + "misc" ++ path.sep_str ++ "wmemchr.c", + "misc" ++ path.sep_str ++ "wmemcmp.c", + "misc" ++ path.sep_str ++ "wmemcpy.c", + "misc" ++ path.sep_str ++ "wmemmove.c", + "misc" ++ path.sep_str ++ "wmempcpy.c", + "misc" ++ path.sep_str ++ "wmemset.c", + "stdio" ++ path.sep_str ++ "_Exit.c", + "stdio" ++ path.sep_str ++ "_findfirst64i32.c", + "stdio" ++ path.sep_str ++ "_findnext64i32.c", + "stdio" ++ path.sep_str ++ "_fstat.c", + "stdio" ++ path.sep_str ++ "_fstat64i32.c", + "stdio" ++ path.sep_str ++ "_ftime.c", + "stdio" ++ path.sep_str ++ "_getc_nolock.c", + "stdio" ++ path.sep_str ++ "_getwc_nolock.c", + "stdio" ++ path.sep_str ++ "_putc_nolock.c", + "stdio" ++ path.sep_str ++ "_putwc_nolock.c", + "stdio" ++ path.sep_str ++ "_stat.c", + "stdio" ++ path.sep_str ++ "_stat64i32.c", + "stdio" ++ path.sep_str ++ "_wfindfirst64i32.c", + "stdio" ++ path.sep_str ++ "_wfindnext64i32.c", + "stdio" ++ path.sep_str ++ "_wstat.c", + "stdio" ++ path.sep_str ++ "_wstat64i32.c", + "stdio" ++ path.sep_str ++ "asprintf.c", + "stdio" ++ path.sep_str ++ "atoll.c", + "stdio" ++ path.sep_str ++ "fgetpos64.c", + "stdio" ++ path.sep_str ++ "fopen64.c", + "stdio" ++ path.sep_str ++ "fseeko32.c", + "stdio" ++ path.sep_str ++ "fseeko64.c", + "stdio" ++ path.sep_str ++ "fsetpos64.c", + "stdio" ++ path.sep_str ++ "ftello.c", + "stdio" ++ path.sep_str ++ "ftello64.c", + "stdio" ++ path.sep_str ++ "ftruncate64.c", + "stdio" ++ path.sep_str ++ "lltoa.c", + "stdio" ++ path.sep_str ++ "lltow.c", + "stdio" ++ path.sep_str ++ "lseek64.c", + "stdio" ++ path.sep_str ++ "mingw_asprintf.c", + "stdio" ++ path.sep_str ++ "mingw_fprintf.c", + "stdio" ++ path.sep_str ++ "mingw_fprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_fscanf.c", + "stdio" ++ path.sep_str ++ "mingw_fwscanf.c", + "stdio" ++ path.sep_str ++ "mingw_pformat.c", + "stdio" ++ path.sep_str ++ "mingw_pformatw.c", + "stdio" ++ path.sep_str ++ "mingw_printf.c", + "stdio" ++ path.sep_str ++ "mingw_printfw.c", + "stdio" ++ path.sep_str ++ "mingw_scanf.c", + "stdio" ++ path.sep_str ++ "mingw_snprintf.c", + "stdio" ++ path.sep_str ++ "mingw_snprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_sprintf.c", + "stdio" ++ path.sep_str ++ "mingw_sprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_sscanf.c", + "stdio" ++ path.sep_str ++ "mingw_swscanf.c", + "stdio" ++ path.sep_str ++ "mingw_vasprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vfprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vfprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vfscanf.c", + "stdio" ++ path.sep_str ++ "mingw_vprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vsnprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vsnprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vsprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vsprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_wscanf.c", + "stdio" ++ path.sep_str ++ "mingw_wvfscanf.c", + "stdio" ++ path.sep_str ++ "scanf.S", + "stdio" ++ path.sep_str ++ "snprintf.c", + "stdio" ++ path.sep_str ++ "snwprintf.c", + "stdio" ++ path.sep_str ++ "strtof.c", + "stdio" ++ path.sep_str ++ "strtok_r.c", + "stdio" ++ path.sep_str ++ "truncate.c", + "stdio" ++ path.sep_str ++ "ulltoa.c", + "stdio" ++ path.sep_str ++ "ulltow.c", + "stdio" ++ path.sep_str ++ "vasprintf.c", + "stdio" ++ path.sep_str ++ "vfscanf.c", + "stdio" ++ path.sep_str ++ "vfscanf2.S", + "stdio" ++ path.sep_str ++ "vfwscanf.c", + "stdio" ++ path.sep_str ++ "vfwscanf2.S", + "stdio" ++ path.sep_str ++ "vscanf.c", + "stdio" ++ path.sep_str ++ "vscanf2.S", + "stdio" ++ path.sep_str ++ "vsnprintf.c", + "stdio" ++ path.sep_str ++ "vsnwprintf.c", + "stdio" ++ path.sep_str ++ "vsscanf.c", + "stdio" ++ path.sep_str ++ "vsscanf2.S", + "stdio" ++ path.sep_str ++ "vswscanf.c", + "stdio" ++ path.sep_str ++ "vswscanf2.S", + "stdio" ++ path.sep_str ++ "vwscanf.c", + "stdio" ++ path.sep_str ++ "vwscanf2.S", + "stdio" ++ path.sep_str ++ "wtoll.c", +}; + +const mingwex_x86_src = [_][]const u8{ + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2l.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceill.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceil.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "copysignl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cos.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl_internal.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cossin.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2f.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1f.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floor.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmod.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogb.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "internal_logl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexp.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexpl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log10l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1p.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2f.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logb.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "pow.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "powl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainder.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquof.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquol.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquo.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbn.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sin.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl_internal.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "truncf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "trunc.S", +}; + +const mingwex_arm32_src = [_][]const u8{ + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "exp2.c", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "trunc.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "truncf.S", +}; + +const mingwex_arm64_src = [_][]const u8{ + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2f.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "truncf.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "trunc.S", +}; + +const uuid_src = [_][]const u8{ + "ativscp-uuid.c", + "atsmedia-uuid.c", + "bth-uuid.c", + "cguid-uuid.c", + "comcat-uuid.c", + "devguid.c", + "docobj-uuid.c", + "dxva-uuid.c", + "exdisp-uuid.c", + "extras-uuid.c", + "fwp-uuid.c", + "guid_nul.c", + "hlguids-uuid.c", + "hlink-uuid.c", + "mlang-uuid.c", + "msctf-uuid.c", + "mshtmhst-uuid.c", + "mshtml-uuid.c", + "msxml-uuid.c", + "netcon-uuid.c", + "ntddkbd-uuid.c", + "ntddmou-uuid.c", + "ntddpar-uuid.c", + "ntddscsi-uuid.c", + "ntddser-uuid.c", + "ntddstor-uuid.c", + "ntddvdeo-uuid.c", + "oaidl-uuid.c", + "objidl-uuid.c", + "objsafe-uuid.c", + "ocidl-uuid.c", + "oleacc-uuid.c", + "olectlid-uuid.c", + "oleidl-uuid.c", + "power-uuid.c", + "powrprof-uuid.c", + "uianimation-uuid.c", + "usbcamdi-uuid.c", + "usbiodef-uuid.c", + "uuid.c", + "vds-uuid.c", + "virtdisk-uuid.c", + "wia-uuid.c", +}; + +pub const always_link_libs = [_][]const u8{ + "advapi32", + "kernel32", + "msvcrt", + "ntdll", + "shell32", + "user32", +}; diff --git a/src/musl.zig b/src/musl.zig new file mode 100644 index 0000000000..ef4ea7236b --- /dev/null +++ b/src/musl.zig @@ -0,0 +1,2142 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); + +pub const CRTFile = enum { + crti_o, + crtn_o, + crt1_o, + scrt1_o, + libc_a, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crti_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-Qunused-arguments", + }); + return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crti.s"), + .extra_flags = args.items, + }, + }); + }, + .crtn_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-Qunused-arguments", + }); + return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crtn.s"), + .extra_flags = args.items, + }, + }); + }, + .crt1_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-fno-stack-protector", + "-DCRT", + }); + return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "musl", "crt", "crt1.c", + }), + .extra_flags = args.items, + }, + }); + }, + .scrt1_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-fPIC", + "-fno-stack-protector", + "-DCRT", + }); + return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "musl", "crt", "Scrt1.c", + }), + .extra_flags = args.items, + }, + }); + }, + .libc_a => { + // When there is a src//foo.* then it should substitute for src/foo.* + // Even a .s file can substitute for a .c file. + const target = comp.getTarget(); + const arch_name = target_util.archMuslName(target.cpu.arch); + var source_table = std.StringArrayHashMap(Ext).init(comp.gpa); + defer source_table.deinit(); + + try source_table.ensureCapacity(compat_time32_files.len + src_files.len); + + for (src_files) |src_file| { + try addSrcFile(arena, &source_table, src_file); + } + + const time32_compat_arch_list = [_][]const u8{ "arm", "i386", "mips", "powerpc" }; + for (time32_compat_arch_list) |time32_compat_arch| { + if (mem.eql(u8, arch_name, time32_compat_arch)) { + for (compat_time32_files) |compat_time32_file| { + try addSrcFile(arena, &source_table, compat_time32_file); + } + } + } + + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(comp.gpa); + defer c_source_files.deinit(); + + var override_path = std.ArrayList(u8).init(comp.gpa); + defer override_path.deinit(); + + const s = path.sep_str; + + for (source_table.items()) |entry| { + const src_file = entry.key; + const ext = entry.value; + + const dirname = path.dirname(src_file).?; + const basename = path.basename(src_file); + const noextbasename = mem.split(basename, ".").next().?; + const before_arch_dir = path.dirname(dirname).?; + const dirbasename = path.basename(dirname); + + var is_arch_specific = false; + // Architecture-specific implementations are under a / folder. + if (is_musl_arch_name(dirbasename)) { + if (!mem.eql(u8, dirbasename, arch_name)) + continue; // Not the architecture we're compiling for. + is_arch_specific = true; + } + if (!is_arch_specific) { + // Look for an arch specific override. + override_path.shrinkRetainingCapacity(0); + try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.s", .{ + dirname, arch_name, noextbasename, + }); + if (source_table.contains(override_path.items)) + continue; + + override_path.shrinkRetainingCapacity(0); + try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.S", .{ + dirname, arch_name, noextbasename, + }); + if (source_table.contains(override_path.items)) + continue; + + override_path.shrinkRetainingCapacity(0); + try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.c", .{ + dirname, arch_name, noextbasename, + }); + if (source_table.contains(override_path.items)) + continue; + } + + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, ext == .o3); + try args.appendSlice(&[_][]const u8{ + "-Qunused-arguments", + "-w", // disable all warnings + }); + const c_source_file = try c_source_files.addOne(); + c_source_file.* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", src_file }), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("c", .Lib, c_source_files.items); + }, + } +} + +fn is_musl_arch_name(name: []const u8) bool { + const musl_arch_names = [_][]const u8{ + "aarch64", + "arm", + "generic", + "i386", + "m68k", + "microblaze", + "mips", + "mips64", + "mipsn32", + "or1k", + "powerpc", + "powerpc64", + "riscv64", + "s390x", + "sh", + "x32", + "x86_64", + }; + for (musl_arch_names) |musl_arch_name| { + if (mem.eql(u8, musl_arch_name, name)) { + return true; + } + } + return false; +} + +const Ext = enum { + assembly, + normal, + o3, +}; + +fn addSrcFile(arena: *Allocator, source_table: *std.StringArrayHashMap(Ext), file_path: []const u8) !void { + const ext: Ext = ext: { + if (mem.endsWith(u8, file_path, ".c")) { + if (mem.startsWith(u8, file_path, "musl/src/malloc/") or + mem.startsWith(u8, file_path, "musl/src/string/") or + mem.startsWith(u8, file_path, "musl/src/internal/")) + { + break :ext .o3; + } else { + break :ext .assembly; + } + } else if (mem.endsWith(u8, file_path, ".s") or mem.endsWith(u8, file_path, ".S")) { + break :ext .assembly; + } else { + unreachable; + } + }; + // TODO do this at comptime on the comptime data rather than at runtime + // probably best to wait until self-hosted is done and our comptime execution + // is faster and uses less memory. + const key = if (path.sep != '/') blk: { + const mutable_file_path = try arena.dupe(u8, file_path); + for (mutable_file_path) |*c| { + if (c.* == '/') { + c.* = path.sep; + } + } + break :blk mutable_file_path; + } else file_path; + source_table.putAssumeCapacityNoClobber(key, ext); +} + +fn add_cc_args( + comp: *Compilation, + arena: *Allocator, + args: *std.ArrayList([]const u8), + want_O3: bool, +) error{OutOfMemory}!void { + const target = comp.getTarget(); + const arch_name = target_util.archMuslName(target.cpu.arch); + const os_name = @tagName(target.os.tag); + const triple = try std.fmt.allocPrint(arena, "{}-{}-musl", .{ arch_name, os_name }); + const o_arg = if (want_O3) "-O3" else "-Os"; + + try args.appendSlice(&[_][]const u8{ + "-std=c99", + "-ffreestanding", + // Musl adds these args to builds with gcc but clang does not support them. + //"-fexcess-precision=standard", + //"-frounding-math", + "-Wa,--noexecstack", + "-D_XOPEN_SOURCE=700", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "arch", arch_name }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "arch", "generic" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "src", "include" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "src", "internal" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "include" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", triple }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "generic-musl" }), + + o_arg, + + "-fomit-frame-pointer", + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + "-ffunction-sections", + "-fdata-sections", + }); +} + +fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { + const target = comp.getTarget(); + return comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "musl", "crt", target_util.archMuslName(target.cpu.arch), basename, + }); +} + +const src_files = [_][]const u8{ + "musl/src/aio/aio.c", + "musl/src/aio/aio_suspend.c", + "musl/src/aio/lio_listio.c", + "musl/src/complex/__cexp.c", + "musl/src/complex/__cexpf.c", + "musl/src/complex/cabs.c", + "musl/src/complex/cabsf.c", + "musl/src/complex/cabsl.c", + "musl/src/complex/cacos.c", + "musl/src/complex/cacosf.c", + "musl/src/complex/cacosh.c", + "musl/src/complex/cacoshf.c", + "musl/src/complex/cacoshl.c", + "musl/src/complex/cacosl.c", + "musl/src/complex/carg.c", + "musl/src/complex/cargf.c", + "musl/src/complex/cargl.c", + "musl/src/complex/casin.c", + "musl/src/complex/casinf.c", + "musl/src/complex/casinh.c", + "musl/src/complex/casinhf.c", + "musl/src/complex/casinhl.c", + "musl/src/complex/casinl.c", + "musl/src/complex/catan.c", + "musl/src/complex/catanf.c", + "musl/src/complex/catanh.c", + "musl/src/complex/catanhf.c", + "musl/src/complex/catanhl.c", + "musl/src/complex/catanl.c", + "musl/src/complex/ccos.c", + "musl/src/complex/ccosf.c", + "musl/src/complex/ccosh.c", + "musl/src/complex/ccoshf.c", + "musl/src/complex/ccoshl.c", + "musl/src/complex/ccosl.c", + "musl/src/complex/cexp.c", + "musl/src/complex/cexpf.c", + "musl/src/complex/cexpl.c", + "musl/src/complex/cimag.c", + "musl/src/complex/cimagf.c", + "musl/src/complex/cimagl.c", + "musl/src/complex/clog.c", + "musl/src/complex/clogf.c", + "musl/src/complex/clogl.c", + "musl/src/complex/conj.c", + "musl/src/complex/conjf.c", + "musl/src/complex/conjl.c", + "musl/src/complex/cpow.c", + "musl/src/complex/cpowf.c", + "musl/src/complex/cpowl.c", + "musl/src/complex/cproj.c", + "musl/src/complex/cprojf.c", + "musl/src/complex/cprojl.c", + "musl/src/complex/creal.c", + "musl/src/complex/crealf.c", + "musl/src/complex/creall.c", + "musl/src/complex/csin.c", + "musl/src/complex/csinf.c", + "musl/src/complex/csinh.c", + "musl/src/complex/csinhf.c", + "musl/src/complex/csinhl.c", + "musl/src/complex/csinl.c", + "musl/src/complex/csqrt.c", + "musl/src/complex/csqrtf.c", + "musl/src/complex/csqrtl.c", + "musl/src/complex/ctan.c", + "musl/src/complex/ctanf.c", + "musl/src/complex/ctanh.c", + "musl/src/complex/ctanhf.c", + "musl/src/complex/ctanhl.c", + "musl/src/complex/ctanl.c", + "musl/src/conf/confstr.c", + "musl/src/conf/fpathconf.c", + "musl/src/conf/legacy.c", + "musl/src/conf/pathconf.c", + "musl/src/conf/sysconf.c", + "musl/src/crypt/crypt.c", + "musl/src/crypt/crypt_blowfish.c", + "musl/src/crypt/crypt_des.c", + "musl/src/crypt/crypt_md5.c", + "musl/src/crypt/crypt_r.c", + "musl/src/crypt/crypt_sha256.c", + "musl/src/crypt/crypt_sha512.c", + "musl/src/crypt/encrypt.c", + "musl/src/ctype/__ctype_b_loc.c", + "musl/src/ctype/__ctype_get_mb_cur_max.c", + "musl/src/ctype/__ctype_tolower_loc.c", + "musl/src/ctype/__ctype_toupper_loc.c", + "musl/src/ctype/isalnum.c", + "musl/src/ctype/isalpha.c", + "musl/src/ctype/isascii.c", + "musl/src/ctype/isblank.c", + "musl/src/ctype/iscntrl.c", + "musl/src/ctype/isdigit.c", + "musl/src/ctype/isgraph.c", + "musl/src/ctype/islower.c", + "musl/src/ctype/isprint.c", + "musl/src/ctype/ispunct.c", + "musl/src/ctype/isspace.c", + "musl/src/ctype/isupper.c", + "musl/src/ctype/iswalnum.c", + "musl/src/ctype/iswalpha.c", + "musl/src/ctype/iswblank.c", + "musl/src/ctype/iswcntrl.c", + "musl/src/ctype/iswctype.c", + "musl/src/ctype/iswdigit.c", + "musl/src/ctype/iswgraph.c", + "musl/src/ctype/iswlower.c", + "musl/src/ctype/iswprint.c", + "musl/src/ctype/iswpunct.c", + "musl/src/ctype/iswspace.c", + "musl/src/ctype/iswupper.c", + "musl/src/ctype/iswxdigit.c", + "musl/src/ctype/isxdigit.c", + "musl/src/ctype/toascii.c", + "musl/src/ctype/tolower.c", + "musl/src/ctype/toupper.c", + "musl/src/ctype/towctrans.c", + "musl/src/ctype/wcswidth.c", + "musl/src/ctype/wctrans.c", + "musl/src/ctype/wcwidth.c", + "musl/src/dirent/alphasort.c", + "musl/src/dirent/closedir.c", + "musl/src/dirent/dirfd.c", + "musl/src/dirent/fdopendir.c", + "musl/src/dirent/opendir.c", + "musl/src/dirent/readdir.c", + "musl/src/dirent/readdir_r.c", + "musl/src/dirent/rewinddir.c", + "musl/src/dirent/scandir.c", + "musl/src/dirent/seekdir.c", + "musl/src/dirent/telldir.c", + "musl/src/dirent/versionsort.c", + "musl/src/env/__environ.c", + "musl/src/env/__init_tls.c", + "musl/src/env/__libc_start_main.c", + "musl/src/env/__reset_tls.c", + "musl/src/env/__stack_chk_fail.c", + "musl/src/env/clearenv.c", + "musl/src/env/getenv.c", + "musl/src/env/putenv.c", + "musl/src/env/secure_getenv.c", + "musl/src/env/setenv.c", + "musl/src/env/unsetenv.c", + "musl/src/errno/__errno_location.c", + "musl/src/errno/strerror.c", + "musl/src/exit/_Exit.c", + "musl/src/exit/abort.c", + "musl/src/exit/arm/__aeabi_atexit.c", + "musl/src/exit/assert.c", + "musl/src/exit/at_quick_exit.c", + "musl/src/exit/atexit.c", + "musl/src/exit/exit.c", + "musl/src/exit/quick_exit.c", + "musl/src/fcntl/creat.c", + "musl/src/fcntl/fcntl.c", + "musl/src/fcntl/open.c", + "musl/src/fcntl/openat.c", + "musl/src/fcntl/posix_fadvise.c", + "musl/src/fcntl/posix_fallocate.c", + "musl/src/fenv/__flt_rounds.c", + "musl/src/fenv/aarch64/fenv.s", + "musl/src/fenv/arm/fenv-hf.S", + "musl/src/fenv/arm/fenv.c", + "musl/src/fenv/fegetexceptflag.c", + "musl/src/fenv/feholdexcept.c", + "musl/src/fenv/fenv.c", + "musl/src/fenv/fesetexceptflag.c", + "musl/src/fenv/fesetround.c", + "musl/src/fenv/feupdateenv.c", + "musl/src/fenv/i386/fenv.s", + "musl/src/fenv/m68k/fenv.c", + "musl/src/fenv/mips/fenv-sf.c", + "musl/src/fenv/mips/fenv.S", + "musl/src/fenv/mips64/fenv-sf.c", + "musl/src/fenv/mips64/fenv.S", + "musl/src/fenv/mipsn32/fenv-sf.c", + "musl/src/fenv/mipsn32/fenv.S", + "musl/src/fenv/powerpc/fenv-sf.c", + "musl/src/fenv/powerpc/fenv.S", + "musl/src/fenv/powerpc64/fenv.c", + "musl/src/fenv/riscv64/fenv-sf.c", + "musl/src/fenv/riscv64/fenv.S", + "musl/src/fenv/s390x/fenv.c", + "musl/src/fenv/sh/fenv-nofpu.c", + "musl/src/fenv/sh/fenv.S", + "musl/src/fenv/x32/fenv.s", + "musl/src/fenv/x86_64/fenv.s", + "musl/src/internal/defsysinfo.c", + "musl/src/internal/floatscan.c", + "musl/src/internal/i386/defsysinfo.s", + "musl/src/internal/intscan.c", + "musl/src/internal/libc.c", + "musl/src/internal/procfdname.c", + "musl/src/internal/sh/__shcall.c", + "musl/src/internal/shgetc.c", + "musl/src/internal/syscall_ret.c", + "musl/src/internal/vdso.c", + "musl/src/internal/version.c", + "musl/src/ipc/ftok.c", + "musl/src/ipc/msgctl.c", + "musl/src/ipc/msgget.c", + "musl/src/ipc/msgrcv.c", + "musl/src/ipc/msgsnd.c", + "musl/src/ipc/semctl.c", + "musl/src/ipc/semget.c", + "musl/src/ipc/semop.c", + "musl/src/ipc/semtimedop.c", + "musl/src/ipc/shmat.c", + "musl/src/ipc/shmctl.c", + "musl/src/ipc/shmdt.c", + "musl/src/ipc/shmget.c", + "musl/src/ldso/__dlsym.c", + "musl/src/ldso/aarch64/dlsym.s", + "musl/src/ldso/aarch64/tlsdesc.s", + "musl/src/ldso/arm/dlsym.s", + "musl/src/ldso/arm/dlsym_time64.S", + "musl/src/ldso/arm/find_exidx.c", + "musl/src/ldso/arm/tlsdesc.S", + "musl/src/ldso/dl_iterate_phdr.c", + "musl/src/ldso/dladdr.c", + "musl/src/ldso/dlclose.c", + "musl/src/ldso/dlerror.c", + "musl/src/ldso/dlinfo.c", + "musl/src/ldso/dlopen.c", + "musl/src/ldso/dlsym.c", + "musl/src/ldso/i386/dlsym.s", + "musl/src/ldso/i386/dlsym_time64.S", + "musl/src/ldso/i386/tlsdesc.s", + "musl/src/ldso/m68k/dlsym.s", + "musl/src/ldso/m68k/dlsym_time64.S", + "musl/src/ldso/microblaze/dlsym.s", + "musl/src/ldso/microblaze/dlsym_time64.S", + "musl/src/ldso/mips/dlsym.s", + "musl/src/ldso/mips/dlsym_time64.S", + "musl/src/ldso/mips64/dlsym.s", + "musl/src/ldso/mipsn32/dlsym.s", + "musl/src/ldso/mipsn32/dlsym_time64.S", + "musl/src/ldso/or1k/dlsym.s", + "musl/src/ldso/or1k/dlsym_time64.S", + "musl/src/ldso/powerpc/dlsym.s", + "musl/src/ldso/powerpc/dlsym_time64.S", + "musl/src/ldso/powerpc64/dlsym.s", + "musl/src/ldso/riscv64/dlsym.s", + "musl/src/ldso/s390x/dlsym.s", + "musl/src/ldso/sh/dlsym.s", + "musl/src/ldso/sh/dlsym_time64.S", + "musl/src/ldso/tlsdesc.c", + "musl/src/ldso/x32/dlsym.s", + "musl/src/ldso/x86_64/dlsym.s", + "musl/src/ldso/x86_64/tlsdesc.s", + "musl/src/legacy/cuserid.c", + "musl/src/legacy/daemon.c", + "musl/src/legacy/err.c", + "musl/src/legacy/euidaccess.c", + "musl/src/legacy/ftw.c", + "musl/src/legacy/futimes.c", + "musl/src/legacy/getdtablesize.c", + "musl/src/legacy/getloadavg.c", + "musl/src/legacy/getpagesize.c", + "musl/src/legacy/getpass.c", + "musl/src/legacy/getusershell.c", + "musl/src/legacy/isastream.c", + "musl/src/legacy/lutimes.c", + "musl/src/legacy/ulimit.c", + "musl/src/legacy/utmpx.c", + "musl/src/legacy/valloc.c", + "musl/src/linux/adjtime.c", + "musl/src/linux/adjtimex.c", + "musl/src/linux/arch_prctl.c", + "musl/src/linux/brk.c", + "musl/src/linux/cache.c", + "musl/src/linux/cap.c", + "musl/src/linux/chroot.c", + "musl/src/linux/clock_adjtime.c", + "musl/src/linux/clone.c", + "musl/src/linux/copy_file_range.c", + "musl/src/linux/epoll.c", + "musl/src/linux/eventfd.c", + "musl/src/linux/fallocate.c", + "musl/src/linux/fanotify.c", + "musl/src/linux/flock.c", + "musl/src/linux/getdents.c", + "musl/src/linux/getrandom.c", + "musl/src/linux/inotify.c", + "musl/src/linux/ioperm.c", + "musl/src/linux/iopl.c", + "musl/src/linux/klogctl.c", + "musl/src/linux/membarrier.c", + "musl/src/linux/memfd_create.c", + "musl/src/linux/mlock2.c", + "musl/src/linux/module.c", + "musl/src/linux/mount.c", + "musl/src/linux/name_to_handle_at.c", + "musl/src/linux/open_by_handle_at.c", + "musl/src/linux/personality.c", + "musl/src/linux/pivot_root.c", + "musl/src/linux/ppoll.c", + "musl/src/linux/prctl.c", + "musl/src/linux/prlimit.c", + "musl/src/linux/process_vm.c", + "musl/src/linux/ptrace.c", + "musl/src/linux/quotactl.c", + "musl/src/linux/readahead.c", + "musl/src/linux/reboot.c", + "musl/src/linux/remap_file_pages.c", + "musl/src/linux/sbrk.c", + "musl/src/linux/sendfile.c", + "musl/src/linux/setfsgid.c", + "musl/src/linux/setfsuid.c", + "musl/src/linux/setgroups.c", + "musl/src/linux/sethostname.c", + "musl/src/linux/setns.c", + "musl/src/linux/settimeofday.c", + "musl/src/linux/signalfd.c", + "musl/src/linux/splice.c", + "musl/src/linux/stime.c", + "musl/src/linux/swap.c", + "musl/src/linux/sync_file_range.c", + "musl/src/linux/syncfs.c", + "musl/src/linux/sysinfo.c", + "musl/src/linux/tee.c", + "musl/src/linux/timerfd.c", + "musl/src/linux/unshare.c", + "musl/src/linux/utimes.c", + "musl/src/linux/vhangup.c", + "musl/src/linux/vmsplice.c", + "musl/src/linux/wait3.c", + "musl/src/linux/wait4.c", + "musl/src/linux/x32/sysinfo.c", + "musl/src/linux/xattr.c", + "musl/src/locale/__lctrans.c", + "musl/src/locale/__mo_lookup.c", + "musl/src/locale/bind_textdomain_codeset.c", + "musl/src/locale/c_locale.c", + "musl/src/locale/catclose.c", + "musl/src/locale/catgets.c", + "musl/src/locale/catopen.c", + "musl/src/locale/dcngettext.c", + "musl/src/locale/duplocale.c", + "musl/src/locale/freelocale.c", + "musl/src/locale/iconv.c", + "musl/src/locale/iconv_close.c", + "musl/src/locale/langinfo.c", + "musl/src/locale/locale_map.c", + "musl/src/locale/localeconv.c", + "musl/src/locale/newlocale.c", + "musl/src/locale/pleval.c", + "musl/src/locale/setlocale.c", + "musl/src/locale/strcoll.c", + "musl/src/locale/strfmon.c", + "musl/src/locale/strxfrm.c", + "musl/src/locale/textdomain.c", + "musl/src/locale/uselocale.c", + "musl/src/locale/wcscoll.c", + "musl/src/locale/wcsxfrm.c", + "musl/src/malloc/aligned_alloc.c", + "musl/src/malloc/expand_heap.c", + "musl/src/malloc/lite_malloc.c", + "musl/src/malloc/malloc.c", + "musl/src/malloc/malloc_usable_size.c", + "musl/src/malloc/memalign.c", + "musl/src/malloc/posix_memalign.c", + "musl/src/math/__cos.c", + "musl/src/math/__cosdf.c", + "musl/src/math/__cosl.c", + "musl/src/math/__expo2.c", + "musl/src/math/__expo2f.c", + "musl/src/math/__fpclassify.c", + "musl/src/math/__fpclassifyf.c", + "musl/src/math/__fpclassifyl.c", + "musl/src/math/__invtrigl.c", + "musl/src/math/__math_divzero.c", + "musl/src/math/__math_divzerof.c", + "musl/src/math/__math_invalid.c", + "musl/src/math/__math_invalidf.c", + "musl/src/math/__math_oflow.c", + "musl/src/math/__math_oflowf.c", + "musl/src/math/__math_uflow.c", + "musl/src/math/__math_uflowf.c", + "musl/src/math/__math_xflow.c", + "musl/src/math/__math_xflowf.c", + "musl/src/math/__polevll.c", + "musl/src/math/__rem_pio2.c", + "musl/src/math/__rem_pio2_large.c", + "musl/src/math/__rem_pio2f.c", + "musl/src/math/__rem_pio2l.c", + "musl/src/math/__signbit.c", + "musl/src/math/__signbitf.c", + "musl/src/math/__signbitl.c", + "musl/src/math/__sin.c", + "musl/src/math/__sindf.c", + "musl/src/math/__sinl.c", + "musl/src/math/__tan.c", + "musl/src/math/__tandf.c", + "musl/src/math/__tanl.c", + "musl/src/math/aarch64/ceil.c", + "musl/src/math/aarch64/ceilf.c", + "musl/src/math/aarch64/fabs.c", + "musl/src/math/aarch64/fabsf.c", + "musl/src/math/aarch64/floor.c", + "musl/src/math/aarch64/floorf.c", + "musl/src/math/aarch64/fma.c", + "musl/src/math/aarch64/fmaf.c", + "musl/src/math/aarch64/fmax.c", + "musl/src/math/aarch64/fmaxf.c", + "musl/src/math/aarch64/fmin.c", + "musl/src/math/aarch64/fminf.c", + "musl/src/math/aarch64/llrint.c", + "musl/src/math/aarch64/llrintf.c", + "musl/src/math/aarch64/llround.c", + "musl/src/math/aarch64/llroundf.c", + "musl/src/math/aarch64/lrint.c", + "musl/src/math/aarch64/lrintf.c", + "musl/src/math/aarch64/lround.c", + "musl/src/math/aarch64/lroundf.c", + "musl/src/math/aarch64/nearbyint.c", + "musl/src/math/aarch64/nearbyintf.c", + "musl/src/math/aarch64/rint.c", + "musl/src/math/aarch64/rintf.c", + "musl/src/math/aarch64/round.c", + "musl/src/math/aarch64/roundf.c", + "musl/src/math/aarch64/sqrt.c", + "musl/src/math/aarch64/sqrtf.c", + "musl/src/math/aarch64/trunc.c", + "musl/src/math/aarch64/truncf.c", + "musl/src/math/acos.c", + "musl/src/math/acosf.c", + "musl/src/math/acosh.c", + "musl/src/math/acoshf.c", + "musl/src/math/acoshl.c", + "musl/src/math/acosl.c", + "musl/src/math/arm/fabs.c", + "musl/src/math/arm/fabsf.c", + "musl/src/math/arm/fma.c", + "musl/src/math/arm/fmaf.c", + "musl/src/math/arm/sqrt.c", + "musl/src/math/arm/sqrtf.c", + "musl/src/math/asin.c", + "musl/src/math/asinf.c", + "musl/src/math/asinh.c", + "musl/src/math/asinhf.c", + "musl/src/math/asinhl.c", + "musl/src/math/asinl.c", + "musl/src/math/atan.c", + "musl/src/math/atan2.c", + "musl/src/math/atan2f.c", + "musl/src/math/atan2l.c", + "musl/src/math/atanf.c", + "musl/src/math/atanh.c", + "musl/src/math/atanhf.c", + "musl/src/math/atanhl.c", + "musl/src/math/atanl.c", + "musl/src/math/cbrt.c", + "musl/src/math/cbrtf.c", + "musl/src/math/cbrtl.c", + "musl/src/math/ceil.c", + "musl/src/math/ceilf.c", + "musl/src/math/ceill.c", + "musl/src/math/copysign.c", + "musl/src/math/copysignf.c", + "musl/src/math/copysignl.c", + "musl/src/math/cos.c", + "musl/src/math/cosf.c", + "musl/src/math/cosh.c", + "musl/src/math/coshf.c", + "musl/src/math/coshl.c", + "musl/src/math/cosl.c", + "musl/src/math/erf.c", + "musl/src/math/erff.c", + "musl/src/math/erfl.c", + "musl/src/math/exp.c", + "musl/src/math/exp10.c", + "musl/src/math/exp10f.c", + "musl/src/math/exp10l.c", + "musl/src/math/exp2.c", + "musl/src/math/exp2f.c", + "musl/src/math/exp2f_data.c", + "musl/src/math/exp2l.c", + "musl/src/math/exp_data.c", + "musl/src/math/expf.c", + "musl/src/math/expl.c", + "musl/src/math/expm1.c", + "musl/src/math/expm1f.c", + "musl/src/math/expm1l.c", + "musl/src/math/fabs.c", + "musl/src/math/fabsf.c", + "musl/src/math/fabsl.c", + "musl/src/math/fdim.c", + "musl/src/math/fdimf.c", + "musl/src/math/fdiml.c", + "musl/src/math/finite.c", + "musl/src/math/finitef.c", + "musl/src/math/floor.c", + "musl/src/math/floorf.c", + "musl/src/math/floorl.c", + "musl/src/math/fma.c", + "musl/src/math/fmaf.c", + "musl/src/math/fmal.c", + "musl/src/math/fmax.c", + "musl/src/math/fmaxf.c", + "musl/src/math/fmaxl.c", + "musl/src/math/fmin.c", + "musl/src/math/fminf.c", + "musl/src/math/fminl.c", + "musl/src/math/fmod.c", + "musl/src/math/fmodf.c", + "musl/src/math/fmodl.c", + "musl/src/math/frexp.c", + "musl/src/math/frexpf.c", + "musl/src/math/frexpl.c", + "musl/src/math/hypot.c", + "musl/src/math/hypotf.c", + "musl/src/math/hypotl.c", + "musl/src/math/i386/__invtrigl.s", + "musl/src/math/i386/acos.s", + "musl/src/math/i386/acosf.s", + "musl/src/math/i386/acosl.s", + "musl/src/math/i386/asin.s", + "musl/src/math/i386/asinf.s", + "musl/src/math/i386/asinl.s", + "musl/src/math/i386/atan.s", + "musl/src/math/i386/atan2.s", + "musl/src/math/i386/atan2f.s", + "musl/src/math/i386/atan2l.s", + "musl/src/math/i386/atanf.s", + "musl/src/math/i386/atanl.s", + "musl/src/math/i386/ceil.s", + "musl/src/math/i386/ceilf.s", + "musl/src/math/i386/ceill.s", + "musl/src/math/i386/exp2l.s", + "musl/src/math/i386/exp_ld.s", + "musl/src/math/i386/expl.s", + "musl/src/math/i386/expm1l.s", + "musl/src/math/i386/fabs.s", + "musl/src/math/i386/fabsf.s", + "musl/src/math/i386/fabsl.s", + "musl/src/math/i386/floor.s", + "musl/src/math/i386/floorf.s", + "musl/src/math/i386/floorl.s", + "musl/src/math/i386/fmod.s", + "musl/src/math/i386/fmodf.s", + "musl/src/math/i386/fmodl.s", + "musl/src/math/i386/hypot.s", + "musl/src/math/i386/hypotf.s", + "musl/src/math/i386/ldexp.s", + "musl/src/math/i386/ldexpf.s", + "musl/src/math/i386/ldexpl.s", + "musl/src/math/i386/llrint.s", + "musl/src/math/i386/llrintf.s", + "musl/src/math/i386/llrintl.s", + "musl/src/math/i386/log.s", + "musl/src/math/i386/log10.s", + "musl/src/math/i386/log10f.s", + "musl/src/math/i386/log10l.s", + "musl/src/math/i386/log1p.s", + "musl/src/math/i386/log1pf.s", + "musl/src/math/i386/log1pl.s", + "musl/src/math/i386/log2.s", + "musl/src/math/i386/log2f.s", + "musl/src/math/i386/log2l.s", + "musl/src/math/i386/logf.s", + "musl/src/math/i386/logl.s", + "musl/src/math/i386/lrint.s", + "musl/src/math/i386/lrintf.s", + "musl/src/math/i386/lrintl.s", + "musl/src/math/i386/remainder.s", + "musl/src/math/i386/remainderf.s", + "musl/src/math/i386/remainderl.s", + "musl/src/math/i386/remquo.s", + "musl/src/math/i386/remquof.s", + "musl/src/math/i386/remquol.s", + "musl/src/math/i386/rint.s", + "musl/src/math/i386/rintf.s", + "musl/src/math/i386/rintl.s", + "musl/src/math/i386/scalbln.s", + "musl/src/math/i386/scalblnf.s", + "musl/src/math/i386/scalblnl.s", + "musl/src/math/i386/scalbn.s", + "musl/src/math/i386/scalbnf.s", + "musl/src/math/i386/scalbnl.s", + "musl/src/math/i386/sqrt.s", + "musl/src/math/i386/sqrtf.s", + "musl/src/math/i386/sqrtl.s", + "musl/src/math/i386/trunc.s", + "musl/src/math/i386/truncf.s", + "musl/src/math/i386/truncl.s", + "musl/src/math/ilogb.c", + "musl/src/math/ilogbf.c", + "musl/src/math/ilogbl.c", + "musl/src/math/j0.c", + "musl/src/math/j0f.c", + "musl/src/math/j1.c", + "musl/src/math/j1f.c", + "musl/src/math/jn.c", + "musl/src/math/jnf.c", + "musl/src/math/ldexp.c", + "musl/src/math/ldexpf.c", + "musl/src/math/ldexpl.c", + "musl/src/math/lgamma.c", + "musl/src/math/lgamma_r.c", + "musl/src/math/lgammaf.c", + "musl/src/math/lgammaf_r.c", + "musl/src/math/lgammal.c", + "musl/src/math/llrint.c", + "musl/src/math/llrintf.c", + "musl/src/math/llrintl.c", + "musl/src/math/llround.c", + "musl/src/math/llroundf.c", + "musl/src/math/llroundl.c", + "musl/src/math/log.c", + "musl/src/math/log10.c", + "musl/src/math/log10f.c", + "musl/src/math/log10l.c", + "musl/src/math/log1p.c", + "musl/src/math/log1pf.c", + "musl/src/math/log1pl.c", + "musl/src/math/log2.c", + "musl/src/math/log2_data.c", + "musl/src/math/log2f.c", + "musl/src/math/log2f_data.c", + "musl/src/math/log2l.c", + "musl/src/math/log_data.c", + "musl/src/math/logb.c", + "musl/src/math/logbf.c", + "musl/src/math/logbl.c", + "musl/src/math/logf.c", + "musl/src/math/logf_data.c", + "musl/src/math/logl.c", + "musl/src/math/lrint.c", + "musl/src/math/lrintf.c", + "musl/src/math/lrintl.c", + "musl/src/math/lround.c", + "musl/src/math/lroundf.c", + "musl/src/math/lroundl.c", + "musl/src/math/mips/fabs.c", + "musl/src/math/mips/fabsf.c", + "musl/src/math/mips/sqrt.c", + "musl/src/math/mips/sqrtf.c", + "musl/src/math/modf.c", + "musl/src/math/modff.c", + "musl/src/math/modfl.c", + "musl/src/math/nan.c", + "musl/src/math/nanf.c", + "musl/src/math/nanl.c", + "musl/src/math/nearbyint.c", + "musl/src/math/nearbyintf.c", + "musl/src/math/nearbyintl.c", + "musl/src/math/nextafter.c", + "musl/src/math/nextafterf.c", + "musl/src/math/nextafterl.c", + "musl/src/math/nexttoward.c", + "musl/src/math/nexttowardf.c", + "musl/src/math/nexttowardl.c", + "musl/src/math/pow.c", + "musl/src/math/pow_data.c", + "musl/src/math/powerpc/fabs.c", + "musl/src/math/powerpc/fabsf.c", + "musl/src/math/powerpc/fma.c", + "musl/src/math/powerpc/fmaf.c", + "musl/src/math/powerpc/sqrt.c", + "musl/src/math/powerpc/sqrtf.c", + "musl/src/math/powerpc64/ceil.c", + "musl/src/math/powerpc64/ceilf.c", + "musl/src/math/powerpc64/fabs.c", + "musl/src/math/powerpc64/fabsf.c", + "musl/src/math/powerpc64/floor.c", + "musl/src/math/powerpc64/floorf.c", + "musl/src/math/powerpc64/fma.c", + "musl/src/math/powerpc64/fmaf.c", + "musl/src/math/powerpc64/fmax.c", + "musl/src/math/powerpc64/fmaxf.c", + "musl/src/math/powerpc64/fmin.c", + "musl/src/math/powerpc64/fminf.c", + "musl/src/math/powerpc64/lrint.c", + "musl/src/math/powerpc64/lrintf.c", + "musl/src/math/powerpc64/lround.c", + "musl/src/math/powerpc64/lroundf.c", + "musl/src/math/powerpc64/round.c", + "musl/src/math/powerpc64/roundf.c", + "musl/src/math/powerpc64/sqrt.c", + "musl/src/math/powerpc64/sqrtf.c", + "musl/src/math/powerpc64/trunc.c", + "musl/src/math/powerpc64/truncf.c", + "musl/src/math/powf.c", + "musl/src/math/powf_data.c", + "musl/src/math/powl.c", + "musl/src/math/remainder.c", + "musl/src/math/remainderf.c", + "musl/src/math/remainderl.c", + "musl/src/math/remquo.c", + "musl/src/math/remquof.c", + "musl/src/math/remquol.c", + "musl/src/math/rint.c", + "musl/src/math/rintf.c", + "musl/src/math/rintl.c", + "musl/src/math/riscv64/copysign.c", + "musl/src/math/riscv64/copysignf.c", + "musl/src/math/riscv64/fabs.c", + "musl/src/math/riscv64/fabsf.c", + "musl/src/math/riscv64/fma.c", + "musl/src/math/riscv64/fmaf.c", + "musl/src/math/riscv64/fmax.c", + "musl/src/math/riscv64/fmaxf.c", + "musl/src/math/riscv64/fmin.c", + "musl/src/math/riscv64/fminf.c", + "musl/src/math/riscv64/sqrt.c", + "musl/src/math/riscv64/sqrtf.c", + "musl/src/math/round.c", + "musl/src/math/roundf.c", + "musl/src/math/roundl.c", + "musl/src/math/s390x/ceil.c", + "musl/src/math/s390x/ceilf.c", + "musl/src/math/s390x/ceill.c", + "musl/src/math/s390x/fabs.c", + "musl/src/math/s390x/fabsf.c", + "musl/src/math/s390x/fabsl.c", + "musl/src/math/s390x/floor.c", + "musl/src/math/s390x/floorf.c", + "musl/src/math/s390x/floorl.c", + "musl/src/math/s390x/fma.c", + "musl/src/math/s390x/fmaf.c", + "musl/src/math/s390x/nearbyint.c", + "musl/src/math/s390x/nearbyintf.c", + "musl/src/math/s390x/nearbyintl.c", + "musl/src/math/s390x/rint.c", + "musl/src/math/s390x/rintf.c", + "musl/src/math/s390x/rintl.c", + "musl/src/math/s390x/round.c", + "musl/src/math/s390x/roundf.c", + "musl/src/math/s390x/roundl.c", + "musl/src/math/s390x/sqrt.c", + "musl/src/math/s390x/sqrtf.c", + "musl/src/math/s390x/sqrtl.c", + "musl/src/math/s390x/trunc.c", + "musl/src/math/s390x/truncf.c", + "musl/src/math/s390x/truncl.c", + "musl/src/math/scalb.c", + "musl/src/math/scalbf.c", + "musl/src/math/scalbln.c", + "musl/src/math/scalblnf.c", + "musl/src/math/scalblnl.c", + "musl/src/math/scalbn.c", + "musl/src/math/scalbnf.c", + "musl/src/math/scalbnl.c", + "musl/src/math/signgam.c", + "musl/src/math/significand.c", + "musl/src/math/significandf.c", + "musl/src/math/sin.c", + "musl/src/math/sincos.c", + "musl/src/math/sincosf.c", + "musl/src/math/sincosl.c", + "musl/src/math/sinf.c", + "musl/src/math/sinh.c", + "musl/src/math/sinhf.c", + "musl/src/math/sinhl.c", + "musl/src/math/sinl.c", + "musl/src/math/sqrt.c", + "musl/src/math/sqrtf.c", + "musl/src/math/sqrtl.c", + "musl/src/math/tan.c", + "musl/src/math/tanf.c", + "musl/src/math/tanh.c", + "musl/src/math/tanhf.c", + "musl/src/math/tanhl.c", + "musl/src/math/tanl.c", + "musl/src/math/tgamma.c", + "musl/src/math/tgammaf.c", + "musl/src/math/tgammal.c", + "musl/src/math/trunc.c", + "musl/src/math/truncf.c", + "musl/src/math/truncl.c", + "musl/src/math/x32/__invtrigl.s", + "musl/src/math/x32/acosl.s", + "musl/src/math/x32/asinl.s", + "musl/src/math/x32/atan2l.s", + "musl/src/math/x32/atanl.s", + "musl/src/math/x32/ceill.s", + "musl/src/math/x32/exp2l.s", + "musl/src/math/x32/expl.s", + "musl/src/math/x32/expm1l.s", + "musl/src/math/x32/fabs.s", + "musl/src/math/x32/fabsf.s", + "musl/src/math/x32/fabsl.s", + "musl/src/math/x32/floorl.s", + "musl/src/math/x32/fma.c", + "musl/src/math/x32/fmaf.c", + "musl/src/math/x32/fmodl.s", + "musl/src/math/x32/llrint.s", + "musl/src/math/x32/llrintf.s", + "musl/src/math/x32/llrintl.s", + "musl/src/math/x32/log10l.s", + "musl/src/math/x32/log1pl.s", + "musl/src/math/x32/log2l.s", + "musl/src/math/x32/logl.s", + "musl/src/math/x32/lrint.s", + "musl/src/math/x32/lrintf.s", + "musl/src/math/x32/lrintl.s", + "musl/src/math/x32/remainderl.s", + "musl/src/math/x32/rintl.s", + "musl/src/math/x32/sqrt.s", + "musl/src/math/x32/sqrtf.s", + "musl/src/math/x32/sqrtl.s", + "musl/src/math/x32/truncl.s", + "musl/src/math/x86_64/__invtrigl.s", + "musl/src/math/x86_64/acosl.s", + "musl/src/math/x86_64/asinl.s", + "musl/src/math/x86_64/atan2l.s", + "musl/src/math/x86_64/atanl.s", + "musl/src/math/x86_64/ceill.s", + "musl/src/math/x86_64/exp2l.s", + "musl/src/math/x86_64/expl.s", + "musl/src/math/x86_64/expm1l.s", + "musl/src/math/x86_64/fabs.s", + "musl/src/math/x86_64/fabsf.s", + "musl/src/math/x86_64/fabsl.s", + "musl/src/math/x86_64/floorl.s", + "musl/src/math/x86_64/fma.c", + "musl/src/math/x86_64/fmaf.c", + "musl/src/math/x86_64/fmodl.s", + "musl/src/math/x86_64/llrint.s", + "musl/src/math/x86_64/llrintf.s", + "musl/src/math/x86_64/llrintl.s", + "musl/src/math/x86_64/log10l.s", + "musl/src/math/x86_64/log1pl.s", + "musl/src/math/x86_64/log2l.s", + "musl/src/math/x86_64/logl.s", + "musl/src/math/x86_64/lrint.s", + "musl/src/math/x86_64/lrintf.s", + "musl/src/math/x86_64/lrintl.s", + "musl/src/math/x86_64/remainderl.s", + "musl/src/math/x86_64/rintl.s", + "musl/src/math/x86_64/sqrt.s", + "musl/src/math/x86_64/sqrtf.s", + "musl/src/math/x86_64/sqrtl.s", + "musl/src/math/x86_64/truncl.s", + "musl/src/misc/a64l.c", + "musl/src/misc/basename.c", + "musl/src/misc/dirname.c", + "musl/src/misc/ffs.c", + "musl/src/misc/ffsl.c", + "musl/src/misc/ffsll.c", + "musl/src/misc/fmtmsg.c", + "musl/src/misc/forkpty.c", + "musl/src/misc/get_current_dir_name.c", + "musl/src/misc/getauxval.c", + "musl/src/misc/getdomainname.c", + "musl/src/misc/getentropy.c", + "musl/src/misc/gethostid.c", + "musl/src/misc/getopt.c", + "musl/src/misc/getopt_long.c", + "musl/src/misc/getpriority.c", + "musl/src/misc/getresgid.c", + "musl/src/misc/getresuid.c", + "musl/src/misc/getrlimit.c", + "musl/src/misc/getrusage.c", + "musl/src/misc/getsubopt.c", + "musl/src/misc/initgroups.c", + "musl/src/misc/ioctl.c", + "musl/src/misc/issetugid.c", + "musl/src/misc/lockf.c", + "musl/src/misc/login_tty.c", + "musl/src/misc/mntent.c", + "musl/src/misc/nftw.c", + "musl/src/misc/openpty.c", + "musl/src/misc/ptsname.c", + "musl/src/misc/pty.c", + "musl/src/misc/realpath.c", + "musl/src/misc/setdomainname.c", + "musl/src/misc/setpriority.c", + "musl/src/misc/setrlimit.c", + "musl/src/misc/syscall.c", + "musl/src/misc/syslog.c", + "musl/src/misc/uname.c", + "musl/src/misc/wordexp.c", + "musl/src/mman/madvise.c", + "musl/src/mman/mincore.c", + "musl/src/mman/mlock.c", + "musl/src/mman/mlockall.c", + "musl/src/mman/mmap.c", + "musl/src/mman/mprotect.c", + "musl/src/mman/mremap.c", + "musl/src/mman/msync.c", + "musl/src/mman/munlock.c", + "musl/src/mman/munlockall.c", + "musl/src/mman/munmap.c", + "musl/src/mman/posix_madvise.c", + "musl/src/mman/shm_open.c", + "musl/src/mq/mq_close.c", + "musl/src/mq/mq_getattr.c", + "musl/src/mq/mq_notify.c", + "musl/src/mq/mq_open.c", + "musl/src/mq/mq_receive.c", + "musl/src/mq/mq_send.c", + "musl/src/mq/mq_setattr.c", + "musl/src/mq/mq_timedreceive.c", + "musl/src/mq/mq_timedsend.c", + "musl/src/mq/mq_unlink.c", + "musl/src/multibyte/btowc.c", + "musl/src/multibyte/c16rtomb.c", + "musl/src/multibyte/c32rtomb.c", + "musl/src/multibyte/internal.c", + "musl/src/multibyte/mblen.c", + "musl/src/multibyte/mbrlen.c", + "musl/src/multibyte/mbrtoc16.c", + "musl/src/multibyte/mbrtoc32.c", + "musl/src/multibyte/mbrtowc.c", + "musl/src/multibyte/mbsinit.c", + "musl/src/multibyte/mbsnrtowcs.c", + "musl/src/multibyte/mbsrtowcs.c", + "musl/src/multibyte/mbstowcs.c", + "musl/src/multibyte/mbtowc.c", + "musl/src/multibyte/wcrtomb.c", + "musl/src/multibyte/wcsnrtombs.c", + "musl/src/multibyte/wcsrtombs.c", + "musl/src/multibyte/wcstombs.c", + "musl/src/multibyte/wctob.c", + "musl/src/multibyte/wctomb.c", + "musl/src/network/accept.c", + "musl/src/network/accept4.c", + "musl/src/network/bind.c", + "musl/src/network/connect.c", + "musl/src/network/dn_comp.c", + "musl/src/network/dn_expand.c", + "musl/src/network/dn_skipname.c", + "musl/src/network/dns_parse.c", + "musl/src/network/ent.c", + "musl/src/network/ether.c", + "musl/src/network/freeaddrinfo.c", + "musl/src/network/gai_strerror.c", + "musl/src/network/getaddrinfo.c", + "musl/src/network/gethostbyaddr.c", + "musl/src/network/gethostbyaddr_r.c", + "musl/src/network/gethostbyname.c", + "musl/src/network/gethostbyname2.c", + "musl/src/network/gethostbyname2_r.c", + "musl/src/network/gethostbyname_r.c", + "musl/src/network/getifaddrs.c", + "musl/src/network/getnameinfo.c", + "musl/src/network/getpeername.c", + "musl/src/network/getservbyname.c", + "musl/src/network/getservbyname_r.c", + "musl/src/network/getservbyport.c", + "musl/src/network/getservbyport_r.c", + "musl/src/network/getsockname.c", + "musl/src/network/getsockopt.c", + "musl/src/network/h_errno.c", + "musl/src/network/herror.c", + "musl/src/network/hstrerror.c", + "musl/src/network/htonl.c", + "musl/src/network/htons.c", + "musl/src/network/if_freenameindex.c", + "musl/src/network/if_indextoname.c", + "musl/src/network/if_nameindex.c", + "musl/src/network/if_nametoindex.c", + "musl/src/network/in6addr_any.c", + "musl/src/network/in6addr_loopback.c", + "musl/src/network/inet_addr.c", + "musl/src/network/inet_aton.c", + "musl/src/network/inet_legacy.c", + "musl/src/network/inet_ntoa.c", + "musl/src/network/inet_ntop.c", + "musl/src/network/inet_pton.c", + "musl/src/network/listen.c", + "musl/src/network/lookup_ipliteral.c", + "musl/src/network/lookup_name.c", + "musl/src/network/lookup_serv.c", + "musl/src/network/netlink.c", + "musl/src/network/netname.c", + "musl/src/network/ns_parse.c", + "musl/src/network/ntohl.c", + "musl/src/network/ntohs.c", + "musl/src/network/proto.c", + "musl/src/network/recv.c", + "musl/src/network/recvfrom.c", + "musl/src/network/recvmmsg.c", + "musl/src/network/recvmsg.c", + "musl/src/network/res_init.c", + "musl/src/network/res_mkquery.c", + "musl/src/network/res_msend.c", + "musl/src/network/res_query.c", + "musl/src/network/res_querydomain.c", + "musl/src/network/res_send.c", + "musl/src/network/res_state.c", + "musl/src/network/resolvconf.c", + "musl/src/network/send.c", + "musl/src/network/sendmmsg.c", + "musl/src/network/sendmsg.c", + "musl/src/network/sendto.c", + "musl/src/network/serv.c", + "musl/src/network/setsockopt.c", + "musl/src/network/shutdown.c", + "musl/src/network/sockatmark.c", + "musl/src/network/socket.c", + "musl/src/network/socketpair.c", + "musl/src/passwd/fgetgrent.c", + "musl/src/passwd/fgetpwent.c", + "musl/src/passwd/fgetspent.c", + "musl/src/passwd/getgr_a.c", + "musl/src/passwd/getgr_r.c", + "musl/src/passwd/getgrent.c", + "musl/src/passwd/getgrent_a.c", + "musl/src/passwd/getgrouplist.c", + "musl/src/passwd/getpw_a.c", + "musl/src/passwd/getpw_r.c", + "musl/src/passwd/getpwent.c", + "musl/src/passwd/getpwent_a.c", + "musl/src/passwd/getspent.c", + "musl/src/passwd/getspnam.c", + "musl/src/passwd/getspnam_r.c", + "musl/src/passwd/lckpwdf.c", + "musl/src/passwd/nscd_query.c", + "musl/src/passwd/putgrent.c", + "musl/src/passwd/putpwent.c", + "musl/src/passwd/putspent.c", + "musl/src/prng/__rand48_step.c", + "musl/src/prng/__seed48.c", + "musl/src/prng/drand48.c", + "musl/src/prng/lcong48.c", + "musl/src/prng/lrand48.c", + "musl/src/prng/mrand48.c", + "musl/src/prng/rand.c", + "musl/src/prng/rand_r.c", + "musl/src/prng/random.c", + "musl/src/prng/seed48.c", + "musl/src/prng/srand48.c", + "musl/src/process/arm/vfork.s", + "musl/src/process/execl.c", + "musl/src/process/execle.c", + "musl/src/process/execlp.c", + "musl/src/process/execv.c", + "musl/src/process/execve.c", + "musl/src/process/execvp.c", + "musl/src/process/fexecve.c", + "musl/src/process/fork.c", + "musl/src/process/i386/vfork.s", + "musl/src/process/posix_spawn.c", + "musl/src/process/posix_spawn_file_actions_addchdir.c", + "musl/src/process/posix_spawn_file_actions_addclose.c", + "musl/src/process/posix_spawn_file_actions_adddup2.c", + "musl/src/process/posix_spawn_file_actions_addfchdir.c", + "musl/src/process/posix_spawn_file_actions_addopen.c", + "musl/src/process/posix_spawn_file_actions_destroy.c", + "musl/src/process/posix_spawn_file_actions_init.c", + "musl/src/process/posix_spawnattr_destroy.c", + "musl/src/process/posix_spawnattr_getflags.c", + "musl/src/process/posix_spawnattr_getpgroup.c", + "musl/src/process/posix_spawnattr_getsigdefault.c", + "musl/src/process/posix_spawnattr_getsigmask.c", + "musl/src/process/posix_spawnattr_init.c", + "musl/src/process/posix_spawnattr_sched.c", + "musl/src/process/posix_spawnattr_setflags.c", + "musl/src/process/posix_spawnattr_setpgroup.c", + "musl/src/process/posix_spawnattr_setsigdefault.c", + "musl/src/process/posix_spawnattr_setsigmask.c", + "musl/src/process/posix_spawnp.c", + "musl/src/process/s390x/vfork.s", + "musl/src/process/sh/vfork.s", + "musl/src/process/system.c", + "musl/src/process/vfork.c", + "musl/src/process/wait.c", + "musl/src/process/waitid.c", + "musl/src/process/waitpid.c", + "musl/src/process/x32/vfork.s", + "musl/src/process/x86_64/vfork.s", + "musl/src/regex/fnmatch.c", + "musl/src/regex/glob.c", + "musl/src/regex/regcomp.c", + "musl/src/regex/regerror.c", + "musl/src/regex/regexec.c", + "musl/src/regex/tre-mem.c", + "musl/src/sched/affinity.c", + "musl/src/sched/sched_cpucount.c", + "musl/src/sched/sched_get_priority_max.c", + "musl/src/sched/sched_getcpu.c", + "musl/src/sched/sched_getparam.c", + "musl/src/sched/sched_getscheduler.c", + "musl/src/sched/sched_rr_get_interval.c", + "musl/src/sched/sched_setparam.c", + "musl/src/sched/sched_setscheduler.c", + "musl/src/sched/sched_yield.c", + "musl/src/search/hsearch.c", + "musl/src/search/insque.c", + "musl/src/search/lsearch.c", + "musl/src/search/tdelete.c", + "musl/src/search/tdestroy.c", + "musl/src/search/tfind.c", + "musl/src/search/tsearch.c", + "musl/src/search/twalk.c", + "musl/src/select/poll.c", + "musl/src/select/pselect.c", + "musl/src/select/select.c", + "musl/src/setjmp/aarch64/longjmp.s", + "musl/src/setjmp/aarch64/setjmp.s", + "musl/src/setjmp/arm/longjmp.S", + "musl/src/setjmp/arm/setjmp.S", + "musl/src/setjmp/i386/longjmp.s", + "musl/src/setjmp/i386/setjmp.s", + "musl/src/setjmp/longjmp.c", + "musl/src/setjmp/m68k/longjmp.s", + "musl/src/setjmp/m68k/setjmp.s", + "musl/src/setjmp/microblaze/longjmp.s", + "musl/src/setjmp/microblaze/setjmp.s", + "musl/src/setjmp/mips/longjmp.S", + "musl/src/setjmp/mips/setjmp.S", + "musl/src/setjmp/mips64/longjmp.S", + "musl/src/setjmp/mips64/setjmp.S", + "musl/src/setjmp/mipsn32/longjmp.S", + "musl/src/setjmp/mipsn32/setjmp.S", + "musl/src/setjmp/or1k/longjmp.s", + "musl/src/setjmp/or1k/setjmp.s", + "musl/src/setjmp/powerpc/longjmp.S", + "musl/src/setjmp/powerpc/setjmp.S", + "musl/src/setjmp/powerpc64/longjmp.s", + "musl/src/setjmp/powerpc64/setjmp.s", + "musl/src/setjmp/riscv64/longjmp.S", + "musl/src/setjmp/riscv64/setjmp.S", + "musl/src/setjmp/s390x/longjmp.s", + "musl/src/setjmp/s390x/setjmp.s", + "musl/src/setjmp/setjmp.c", + "musl/src/setjmp/sh/longjmp.S", + "musl/src/setjmp/sh/setjmp.S", + "musl/src/setjmp/x32/longjmp.s", + "musl/src/setjmp/x32/setjmp.s", + "musl/src/setjmp/x86_64/longjmp.s", + "musl/src/setjmp/x86_64/setjmp.s", + "musl/src/signal/aarch64/restore.s", + "musl/src/signal/aarch64/sigsetjmp.s", + "musl/src/signal/arm/restore.s", + "musl/src/signal/arm/sigsetjmp.s", + "musl/src/signal/block.c", + "musl/src/signal/getitimer.c", + "musl/src/signal/i386/restore.s", + "musl/src/signal/i386/sigsetjmp.s", + "musl/src/signal/kill.c", + "musl/src/signal/killpg.c", + "musl/src/signal/m68k/sigsetjmp.s", + "musl/src/signal/microblaze/restore.s", + "musl/src/signal/microblaze/sigsetjmp.s", + "musl/src/signal/mips/restore.s", + "musl/src/signal/mips/sigsetjmp.s", + "musl/src/signal/mips64/restore.s", + "musl/src/signal/mips64/sigsetjmp.s", + "musl/src/signal/mipsn32/restore.s", + "musl/src/signal/mipsn32/sigsetjmp.s", + "musl/src/signal/or1k/sigsetjmp.s", + "musl/src/signal/powerpc/restore.s", + "musl/src/signal/powerpc/sigsetjmp.s", + "musl/src/signal/powerpc64/restore.s", + "musl/src/signal/powerpc64/sigsetjmp.s", + "musl/src/signal/psiginfo.c", + "musl/src/signal/psignal.c", + "musl/src/signal/raise.c", + "musl/src/signal/restore.c", + "musl/src/signal/riscv64/restore.s", + "musl/src/signal/riscv64/sigsetjmp.s", + "musl/src/signal/s390x/restore.s", + "musl/src/signal/s390x/sigsetjmp.s", + "musl/src/signal/setitimer.c", + "musl/src/signal/sh/restore.s", + "musl/src/signal/sh/sigsetjmp.s", + "musl/src/signal/sigaction.c", + "musl/src/signal/sigaddset.c", + "musl/src/signal/sigaltstack.c", + "musl/src/signal/sigandset.c", + "musl/src/signal/sigdelset.c", + "musl/src/signal/sigemptyset.c", + "musl/src/signal/sigfillset.c", + "musl/src/signal/sighold.c", + "musl/src/signal/sigignore.c", + "musl/src/signal/siginterrupt.c", + "musl/src/signal/sigisemptyset.c", + "musl/src/signal/sigismember.c", + "musl/src/signal/siglongjmp.c", + "musl/src/signal/signal.c", + "musl/src/signal/sigorset.c", + "musl/src/signal/sigpause.c", + "musl/src/signal/sigpending.c", + "musl/src/signal/sigprocmask.c", + "musl/src/signal/sigqueue.c", + "musl/src/signal/sigrelse.c", + "musl/src/signal/sigrtmax.c", + "musl/src/signal/sigrtmin.c", + "musl/src/signal/sigset.c", + "musl/src/signal/sigsetjmp.c", + "musl/src/signal/sigsetjmp_tail.c", + "musl/src/signal/sigsuspend.c", + "musl/src/signal/sigtimedwait.c", + "musl/src/signal/sigwait.c", + "musl/src/signal/sigwaitinfo.c", + "musl/src/signal/x32/getitimer.c", + "musl/src/signal/x32/restore.s", + "musl/src/signal/x32/setitimer.c", + "musl/src/signal/x32/sigsetjmp.s", + "musl/src/signal/x86_64/restore.s", + "musl/src/signal/x86_64/sigsetjmp.s", + "musl/src/stat/__xstat.c", + "musl/src/stat/chmod.c", + "musl/src/stat/fchmod.c", + "musl/src/stat/fchmodat.c", + "musl/src/stat/fstat.c", + "musl/src/stat/fstatat.c", + "musl/src/stat/futimens.c", + "musl/src/stat/futimesat.c", + "musl/src/stat/lchmod.c", + "musl/src/stat/lstat.c", + "musl/src/stat/mkdir.c", + "musl/src/stat/mkdirat.c", + "musl/src/stat/mkfifo.c", + "musl/src/stat/mkfifoat.c", + "musl/src/stat/mknod.c", + "musl/src/stat/mknodat.c", + "musl/src/stat/stat.c", + "musl/src/stat/statvfs.c", + "musl/src/stat/umask.c", + "musl/src/stat/utimensat.c", + "musl/src/stdio/__fclose_ca.c", + "musl/src/stdio/__fdopen.c", + "musl/src/stdio/__fmodeflags.c", + "musl/src/stdio/__fopen_rb_ca.c", + "musl/src/stdio/__lockfile.c", + "musl/src/stdio/__overflow.c", + "musl/src/stdio/__stdio_close.c", + "musl/src/stdio/__stdio_exit.c", + "musl/src/stdio/__stdio_read.c", + "musl/src/stdio/__stdio_seek.c", + "musl/src/stdio/__stdio_write.c", + "musl/src/stdio/__stdout_write.c", + "musl/src/stdio/__string_read.c", + "musl/src/stdio/__toread.c", + "musl/src/stdio/__towrite.c", + "musl/src/stdio/__uflow.c", + "musl/src/stdio/asprintf.c", + "musl/src/stdio/clearerr.c", + "musl/src/stdio/dprintf.c", + "musl/src/stdio/ext.c", + "musl/src/stdio/ext2.c", + "musl/src/stdio/fclose.c", + "musl/src/stdio/feof.c", + "musl/src/stdio/ferror.c", + "musl/src/stdio/fflush.c", + "musl/src/stdio/fgetc.c", + "musl/src/stdio/fgetln.c", + "musl/src/stdio/fgetpos.c", + "musl/src/stdio/fgets.c", + "musl/src/stdio/fgetwc.c", + "musl/src/stdio/fgetws.c", + "musl/src/stdio/fileno.c", + "musl/src/stdio/flockfile.c", + "musl/src/stdio/fmemopen.c", + "musl/src/stdio/fopen.c", + "musl/src/stdio/fopencookie.c", + "musl/src/stdio/fprintf.c", + "musl/src/stdio/fputc.c", + "musl/src/stdio/fputs.c", + "musl/src/stdio/fputwc.c", + "musl/src/stdio/fputws.c", + "musl/src/stdio/fread.c", + "musl/src/stdio/freopen.c", + "musl/src/stdio/fscanf.c", + "musl/src/stdio/fseek.c", + "musl/src/stdio/fsetpos.c", + "musl/src/stdio/ftell.c", + "musl/src/stdio/ftrylockfile.c", + "musl/src/stdio/funlockfile.c", + "musl/src/stdio/fwide.c", + "musl/src/stdio/fwprintf.c", + "musl/src/stdio/fwrite.c", + "musl/src/stdio/fwscanf.c", + "musl/src/stdio/getc.c", + "musl/src/stdio/getc_unlocked.c", + "musl/src/stdio/getchar.c", + "musl/src/stdio/getchar_unlocked.c", + "musl/src/stdio/getdelim.c", + "musl/src/stdio/getline.c", + "musl/src/stdio/gets.c", + "musl/src/stdio/getw.c", + "musl/src/stdio/getwc.c", + "musl/src/stdio/getwchar.c", + "musl/src/stdio/ofl.c", + "musl/src/stdio/ofl_add.c", + "musl/src/stdio/open_memstream.c", + "musl/src/stdio/open_wmemstream.c", + "musl/src/stdio/pclose.c", + "musl/src/stdio/perror.c", + "musl/src/stdio/popen.c", + "musl/src/stdio/printf.c", + "musl/src/stdio/putc.c", + "musl/src/stdio/putc_unlocked.c", + "musl/src/stdio/putchar.c", + "musl/src/stdio/putchar_unlocked.c", + "musl/src/stdio/puts.c", + "musl/src/stdio/putw.c", + "musl/src/stdio/putwc.c", + "musl/src/stdio/putwchar.c", + "musl/src/stdio/remove.c", + "musl/src/stdio/rename.c", + "musl/src/stdio/rewind.c", + "musl/src/stdio/scanf.c", + "musl/src/stdio/setbuf.c", + "musl/src/stdio/setbuffer.c", + "musl/src/stdio/setlinebuf.c", + "musl/src/stdio/setvbuf.c", + "musl/src/stdio/snprintf.c", + "musl/src/stdio/sprintf.c", + "musl/src/stdio/sscanf.c", + "musl/src/stdio/stderr.c", + "musl/src/stdio/stdin.c", + "musl/src/stdio/stdout.c", + "musl/src/stdio/swprintf.c", + "musl/src/stdio/swscanf.c", + "musl/src/stdio/tempnam.c", + "musl/src/stdio/tmpfile.c", + "musl/src/stdio/tmpnam.c", + "musl/src/stdio/ungetc.c", + "musl/src/stdio/ungetwc.c", + "musl/src/stdio/vasprintf.c", + "musl/src/stdio/vdprintf.c", + "musl/src/stdio/vfprintf.c", + "musl/src/stdio/vfscanf.c", + "musl/src/stdio/vfwprintf.c", + "musl/src/stdio/vfwscanf.c", + "musl/src/stdio/vprintf.c", + "musl/src/stdio/vscanf.c", + "musl/src/stdio/vsnprintf.c", + "musl/src/stdio/vsprintf.c", + "musl/src/stdio/vsscanf.c", + "musl/src/stdio/vswprintf.c", + "musl/src/stdio/vswscanf.c", + "musl/src/stdio/vwprintf.c", + "musl/src/stdio/vwscanf.c", + "musl/src/stdio/wprintf.c", + "musl/src/stdio/wscanf.c", + "musl/src/stdlib/abs.c", + "musl/src/stdlib/atof.c", + "musl/src/stdlib/atoi.c", + "musl/src/stdlib/atol.c", + "musl/src/stdlib/atoll.c", + "musl/src/stdlib/bsearch.c", + "musl/src/stdlib/div.c", + "musl/src/stdlib/ecvt.c", + "musl/src/stdlib/fcvt.c", + "musl/src/stdlib/gcvt.c", + "musl/src/stdlib/imaxabs.c", + "musl/src/stdlib/imaxdiv.c", + "musl/src/stdlib/labs.c", + "musl/src/stdlib/ldiv.c", + "musl/src/stdlib/llabs.c", + "musl/src/stdlib/lldiv.c", + "musl/src/stdlib/qsort.c", + "musl/src/stdlib/strtod.c", + "musl/src/stdlib/strtol.c", + "musl/src/stdlib/wcstod.c", + "musl/src/stdlib/wcstol.c", + "musl/src/string/arm/__aeabi_memcpy.s", + "musl/src/string/arm/__aeabi_memset.s", + "musl/src/string/arm/memcpy.c", + "musl/src/string/arm/memcpy_le.S", + "musl/src/string/bcmp.c", + "musl/src/string/bcopy.c", + "musl/src/string/bzero.c", + "musl/src/string/explicit_bzero.c", + "musl/src/string/i386/memcpy.s", + "musl/src/string/i386/memmove.s", + "musl/src/string/i386/memset.s", + "musl/src/string/index.c", + "musl/src/string/memccpy.c", + "musl/src/string/memchr.c", + "musl/src/string/memcmp.c", + "musl/src/string/memcpy.c", + "musl/src/string/memmem.c", + "musl/src/string/memmove.c", + "musl/src/string/mempcpy.c", + "musl/src/string/memrchr.c", + "musl/src/string/memset.c", + "musl/src/string/rindex.c", + "musl/src/string/stpcpy.c", + "musl/src/string/stpncpy.c", + "musl/src/string/strcasecmp.c", + "musl/src/string/strcasestr.c", + "musl/src/string/strcat.c", + "musl/src/string/strchr.c", + "musl/src/string/strchrnul.c", + "musl/src/string/strcmp.c", + "musl/src/string/strcpy.c", + "musl/src/string/strcspn.c", + "musl/src/string/strdup.c", + "musl/src/string/strerror_r.c", + "musl/src/string/strlcat.c", + "musl/src/string/strlcpy.c", + "musl/src/string/strlen.c", + "musl/src/string/strncasecmp.c", + "musl/src/string/strncat.c", + "musl/src/string/strncmp.c", + "musl/src/string/strncpy.c", + "musl/src/string/strndup.c", + "musl/src/string/strnlen.c", + "musl/src/string/strpbrk.c", + "musl/src/string/strrchr.c", + "musl/src/string/strsep.c", + "musl/src/string/strsignal.c", + "musl/src/string/strspn.c", + "musl/src/string/strstr.c", + "musl/src/string/strtok.c", + "musl/src/string/strtok_r.c", + "musl/src/string/strverscmp.c", + "musl/src/string/swab.c", + "musl/src/string/wcpcpy.c", + "musl/src/string/wcpncpy.c", + "musl/src/string/wcscasecmp.c", + "musl/src/string/wcscasecmp_l.c", + "musl/src/string/wcscat.c", + "musl/src/string/wcschr.c", + "musl/src/string/wcscmp.c", + "musl/src/string/wcscpy.c", + "musl/src/string/wcscspn.c", + "musl/src/string/wcsdup.c", + "musl/src/string/wcslen.c", + "musl/src/string/wcsncasecmp.c", + "musl/src/string/wcsncasecmp_l.c", + "musl/src/string/wcsncat.c", + "musl/src/string/wcsncmp.c", + "musl/src/string/wcsncpy.c", + "musl/src/string/wcsnlen.c", + "musl/src/string/wcspbrk.c", + "musl/src/string/wcsrchr.c", + "musl/src/string/wcsspn.c", + "musl/src/string/wcsstr.c", + "musl/src/string/wcstok.c", + "musl/src/string/wcswcs.c", + "musl/src/string/wmemchr.c", + "musl/src/string/wmemcmp.c", + "musl/src/string/wmemcpy.c", + "musl/src/string/wmemmove.c", + "musl/src/string/wmemset.c", + "musl/src/string/x86_64/memcpy.s", + "musl/src/string/x86_64/memmove.s", + "musl/src/string/x86_64/memset.s", + "musl/src/temp/__randname.c", + "musl/src/temp/mkdtemp.c", + "musl/src/temp/mkostemp.c", + "musl/src/temp/mkostemps.c", + "musl/src/temp/mkstemp.c", + "musl/src/temp/mkstemps.c", + "musl/src/temp/mktemp.c", + "musl/src/termios/cfgetospeed.c", + "musl/src/termios/cfmakeraw.c", + "musl/src/termios/cfsetospeed.c", + "musl/src/termios/tcdrain.c", + "musl/src/termios/tcflow.c", + "musl/src/termios/tcflush.c", + "musl/src/termios/tcgetattr.c", + "musl/src/termios/tcgetsid.c", + "musl/src/termios/tcsendbreak.c", + "musl/src/termios/tcsetattr.c", + "musl/src/thread/__lock.c", + "musl/src/thread/__set_thread_area.c", + "musl/src/thread/__syscall_cp.c", + "musl/src/thread/__timedwait.c", + "musl/src/thread/__tls_get_addr.c", + "musl/src/thread/__unmapself.c", + "musl/src/thread/__wait.c", + "musl/src/thread/aarch64/__set_thread_area.s", + "musl/src/thread/aarch64/__unmapself.s", + "musl/src/thread/aarch64/clone.s", + "musl/src/thread/aarch64/syscall_cp.s", + "musl/src/thread/arm/__aeabi_read_tp.s", + "musl/src/thread/arm/__set_thread_area.c", + "musl/src/thread/arm/__unmapself.s", + "musl/src/thread/arm/atomics.s", + "musl/src/thread/arm/clone.s", + "musl/src/thread/arm/syscall_cp.s", + "musl/src/thread/call_once.c", + "musl/src/thread/clone.c", + "musl/src/thread/cnd_broadcast.c", + "musl/src/thread/cnd_destroy.c", + "musl/src/thread/cnd_init.c", + "musl/src/thread/cnd_signal.c", + "musl/src/thread/cnd_timedwait.c", + "musl/src/thread/cnd_wait.c", + "musl/src/thread/default_attr.c", + "musl/src/thread/i386/__set_thread_area.s", + "musl/src/thread/i386/__unmapself.s", + "musl/src/thread/i386/clone.s", + "musl/src/thread/i386/syscall_cp.s", + "musl/src/thread/i386/tls.s", + "musl/src/thread/lock_ptc.c", + "musl/src/thread/m68k/__m68k_read_tp.s", + "musl/src/thread/m68k/clone.s", + "musl/src/thread/m68k/syscall_cp.s", + "musl/src/thread/microblaze/__set_thread_area.s", + "musl/src/thread/microblaze/__unmapself.s", + "musl/src/thread/microblaze/clone.s", + "musl/src/thread/microblaze/syscall_cp.s", + "musl/src/thread/mips/__unmapself.s", + "musl/src/thread/mips/clone.s", + "musl/src/thread/mips/syscall_cp.s", + "musl/src/thread/mips64/__unmapself.s", + "musl/src/thread/mips64/clone.s", + "musl/src/thread/mips64/syscall_cp.s", + "musl/src/thread/mipsn32/__unmapself.s", + "musl/src/thread/mipsn32/clone.s", + "musl/src/thread/mipsn32/syscall_cp.s", + "musl/src/thread/mtx_destroy.c", + "musl/src/thread/mtx_init.c", + "musl/src/thread/mtx_lock.c", + "musl/src/thread/mtx_timedlock.c", + "musl/src/thread/mtx_trylock.c", + "musl/src/thread/mtx_unlock.c", + "musl/src/thread/or1k/__set_thread_area.s", + "musl/src/thread/or1k/__unmapself.s", + "musl/src/thread/or1k/clone.s", + "musl/src/thread/or1k/syscall_cp.s", + "musl/src/thread/powerpc/__set_thread_area.s", + "musl/src/thread/powerpc/__unmapself.s", + "musl/src/thread/powerpc/clone.s", + "musl/src/thread/powerpc/syscall_cp.s", + "musl/src/thread/powerpc64/__set_thread_area.s", + "musl/src/thread/powerpc64/__unmapself.s", + "musl/src/thread/powerpc64/clone.s", + "musl/src/thread/powerpc64/syscall_cp.s", + "musl/src/thread/pthread_atfork.c", + "musl/src/thread/pthread_attr_destroy.c", + "musl/src/thread/pthread_attr_get.c", + "musl/src/thread/pthread_attr_init.c", + "musl/src/thread/pthread_attr_setdetachstate.c", + "musl/src/thread/pthread_attr_setguardsize.c", + "musl/src/thread/pthread_attr_setinheritsched.c", + "musl/src/thread/pthread_attr_setschedparam.c", + "musl/src/thread/pthread_attr_setschedpolicy.c", + "musl/src/thread/pthread_attr_setscope.c", + "musl/src/thread/pthread_attr_setstack.c", + "musl/src/thread/pthread_attr_setstacksize.c", + "musl/src/thread/pthread_barrier_destroy.c", + "musl/src/thread/pthread_barrier_init.c", + "musl/src/thread/pthread_barrier_wait.c", + "musl/src/thread/pthread_barrierattr_destroy.c", + "musl/src/thread/pthread_barrierattr_init.c", + "musl/src/thread/pthread_barrierattr_setpshared.c", + "musl/src/thread/pthread_cancel.c", + "musl/src/thread/pthread_cleanup_push.c", + "musl/src/thread/pthread_cond_broadcast.c", + "musl/src/thread/pthread_cond_destroy.c", + "musl/src/thread/pthread_cond_init.c", + "musl/src/thread/pthread_cond_signal.c", + "musl/src/thread/pthread_cond_timedwait.c", + "musl/src/thread/pthread_cond_wait.c", + "musl/src/thread/pthread_condattr_destroy.c", + "musl/src/thread/pthread_condattr_init.c", + "musl/src/thread/pthread_condattr_setclock.c", + "musl/src/thread/pthread_condattr_setpshared.c", + "musl/src/thread/pthread_create.c", + "musl/src/thread/pthread_detach.c", + "musl/src/thread/pthread_equal.c", + "musl/src/thread/pthread_getattr_np.c", + "musl/src/thread/pthread_getconcurrency.c", + "musl/src/thread/pthread_getcpuclockid.c", + "musl/src/thread/pthread_getschedparam.c", + "musl/src/thread/pthread_getspecific.c", + "musl/src/thread/pthread_join.c", + "musl/src/thread/pthread_key_create.c", + "musl/src/thread/pthread_kill.c", + "musl/src/thread/pthread_mutex_consistent.c", + "musl/src/thread/pthread_mutex_destroy.c", + "musl/src/thread/pthread_mutex_getprioceiling.c", + "musl/src/thread/pthread_mutex_init.c", + "musl/src/thread/pthread_mutex_lock.c", + "musl/src/thread/pthread_mutex_setprioceiling.c", + "musl/src/thread/pthread_mutex_timedlock.c", + "musl/src/thread/pthread_mutex_trylock.c", + "musl/src/thread/pthread_mutex_unlock.c", + "musl/src/thread/pthread_mutexattr_destroy.c", + "musl/src/thread/pthread_mutexattr_init.c", + "musl/src/thread/pthread_mutexattr_setprotocol.c", + "musl/src/thread/pthread_mutexattr_setpshared.c", + "musl/src/thread/pthread_mutexattr_setrobust.c", + "musl/src/thread/pthread_mutexattr_settype.c", + "musl/src/thread/pthread_once.c", + "musl/src/thread/pthread_rwlock_destroy.c", + "musl/src/thread/pthread_rwlock_init.c", + "musl/src/thread/pthread_rwlock_rdlock.c", + "musl/src/thread/pthread_rwlock_timedrdlock.c", + "musl/src/thread/pthread_rwlock_timedwrlock.c", + "musl/src/thread/pthread_rwlock_tryrdlock.c", + "musl/src/thread/pthread_rwlock_trywrlock.c", + "musl/src/thread/pthread_rwlock_unlock.c", + "musl/src/thread/pthread_rwlock_wrlock.c", + "musl/src/thread/pthread_rwlockattr_destroy.c", + "musl/src/thread/pthread_rwlockattr_init.c", + "musl/src/thread/pthread_rwlockattr_setpshared.c", + "musl/src/thread/pthread_self.c", + "musl/src/thread/pthread_setattr_default_np.c", + "musl/src/thread/pthread_setcancelstate.c", + "musl/src/thread/pthread_setcanceltype.c", + "musl/src/thread/pthread_setconcurrency.c", + "musl/src/thread/pthread_setname_np.c", + "musl/src/thread/pthread_setschedparam.c", + "musl/src/thread/pthread_setschedprio.c", + "musl/src/thread/pthread_setspecific.c", + "musl/src/thread/pthread_sigmask.c", + "musl/src/thread/pthread_spin_destroy.c", + "musl/src/thread/pthread_spin_init.c", + "musl/src/thread/pthread_spin_lock.c", + "musl/src/thread/pthread_spin_trylock.c", + "musl/src/thread/pthread_spin_unlock.c", + "musl/src/thread/pthread_testcancel.c", + "musl/src/thread/riscv64/__set_thread_area.s", + "musl/src/thread/riscv64/__unmapself.s", + "musl/src/thread/riscv64/clone.s", + "musl/src/thread/riscv64/syscall_cp.s", + "musl/src/thread/s390x/__set_thread_area.s", + "musl/src/thread/s390x/__tls_get_offset.s", + "musl/src/thread/s390x/__unmapself.s", + "musl/src/thread/s390x/clone.s", + "musl/src/thread/s390x/syscall_cp.s", + "musl/src/thread/sem_destroy.c", + "musl/src/thread/sem_getvalue.c", + "musl/src/thread/sem_init.c", + "musl/src/thread/sem_open.c", + "musl/src/thread/sem_post.c", + "musl/src/thread/sem_timedwait.c", + "musl/src/thread/sem_trywait.c", + "musl/src/thread/sem_unlink.c", + "musl/src/thread/sem_wait.c", + "musl/src/thread/sh/__set_thread_area.c", + "musl/src/thread/sh/__unmapself.c", + "musl/src/thread/sh/__unmapself_mmu.s", + "musl/src/thread/sh/atomics.s", + "musl/src/thread/sh/clone.s", + "musl/src/thread/sh/syscall_cp.s", + "musl/src/thread/synccall.c", + "musl/src/thread/syscall_cp.c", + "musl/src/thread/thrd_create.c", + "musl/src/thread/thrd_exit.c", + "musl/src/thread/thrd_join.c", + "musl/src/thread/thrd_sleep.c", + "musl/src/thread/thrd_yield.c", + "musl/src/thread/tls.c", + "musl/src/thread/tss_create.c", + "musl/src/thread/tss_delete.c", + "musl/src/thread/tss_set.c", + "musl/src/thread/vmlock.c", + "musl/src/thread/x32/__set_thread_area.s", + "musl/src/thread/x32/__unmapself.s", + "musl/src/thread/x32/clone.s", + "musl/src/thread/x32/syscall_cp.s", + "musl/src/thread/x86_64/__set_thread_area.s", + "musl/src/thread/x86_64/__unmapself.s", + "musl/src/thread/x86_64/clone.s", + "musl/src/thread/x86_64/syscall_cp.s", + "musl/src/time/__map_file.c", + "musl/src/time/__month_to_secs.c", + "musl/src/time/__secs_to_tm.c", + "musl/src/time/__tm_to_secs.c", + "musl/src/time/__tz.c", + "musl/src/time/__year_to_secs.c", + "musl/src/time/asctime.c", + "musl/src/time/asctime_r.c", + "musl/src/time/clock.c", + "musl/src/time/clock_getcpuclockid.c", + "musl/src/time/clock_getres.c", + "musl/src/time/clock_gettime.c", + "musl/src/time/clock_nanosleep.c", + "musl/src/time/clock_settime.c", + "musl/src/time/ctime.c", + "musl/src/time/ctime_r.c", + "musl/src/time/difftime.c", + "musl/src/time/ftime.c", + "musl/src/time/getdate.c", + "musl/src/time/gettimeofday.c", + "musl/src/time/gmtime.c", + "musl/src/time/gmtime_r.c", + "musl/src/time/localtime.c", + "musl/src/time/localtime_r.c", + "musl/src/time/mktime.c", + "musl/src/time/nanosleep.c", + "musl/src/time/strftime.c", + "musl/src/time/strptime.c", + "musl/src/time/time.c", + "musl/src/time/timegm.c", + "musl/src/time/timer_create.c", + "musl/src/time/timer_delete.c", + "musl/src/time/timer_getoverrun.c", + "musl/src/time/timer_gettime.c", + "musl/src/time/timer_settime.c", + "musl/src/time/times.c", + "musl/src/time/timespec_get.c", + "musl/src/time/utime.c", + "musl/src/time/wcsftime.c", + "musl/src/unistd/_exit.c", + "musl/src/unistd/access.c", + "musl/src/unistd/acct.c", + "musl/src/unistd/alarm.c", + "musl/src/unistd/chdir.c", + "musl/src/unistd/chown.c", + "musl/src/unistd/close.c", + "musl/src/unistd/ctermid.c", + "musl/src/unistd/dup.c", + "musl/src/unistd/dup2.c", + "musl/src/unistd/dup3.c", + "musl/src/unistd/faccessat.c", + "musl/src/unistd/fchdir.c", + "musl/src/unistd/fchown.c", + "musl/src/unistd/fchownat.c", + "musl/src/unistd/fdatasync.c", + "musl/src/unistd/fsync.c", + "musl/src/unistd/ftruncate.c", + "musl/src/unistd/getcwd.c", + "musl/src/unistd/getegid.c", + "musl/src/unistd/geteuid.c", + "musl/src/unistd/getgid.c", + "musl/src/unistd/getgroups.c", + "musl/src/unistd/gethostname.c", + "musl/src/unistd/getlogin.c", + "musl/src/unistd/getlogin_r.c", + "musl/src/unistd/getpgid.c", + "musl/src/unistd/getpgrp.c", + "musl/src/unistd/getpid.c", + "musl/src/unistd/getppid.c", + "musl/src/unistd/getsid.c", + "musl/src/unistd/getuid.c", + "musl/src/unistd/isatty.c", + "musl/src/unistd/lchown.c", + "musl/src/unistd/link.c", + "musl/src/unistd/linkat.c", + "musl/src/unistd/lseek.c", + "musl/src/unistd/mips/pipe.s", + "musl/src/unistd/mips64/pipe.s", + "musl/src/unistd/mipsn32/lseek.c", + "musl/src/unistd/mipsn32/pipe.s", + "musl/src/unistd/nice.c", + "musl/src/unistd/pause.c", + "musl/src/unistd/pipe.c", + "musl/src/unistd/pipe2.c", + "musl/src/unistd/posix_close.c", + "musl/src/unistd/pread.c", + "musl/src/unistd/preadv.c", + "musl/src/unistd/pwrite.c", + "musl/src/unistd/pwritev.c", + "musl/src/unistd/read.c", + "musl/src/unistd/readlink.c", + "musl/src/unistd/readlinkat.c", + "musl/src/unistd/readv.c", + "musl/src/unistd/renameat.c", + "musl/src/unistd/rmdir.c", + "musl/src/unistd/setegid.c", + "musl/src/unistd/seteuid.c", + "musl/src/unistd/setgid.c", + "musl/src/unistd/setpgid.c", + "musl/src/unistd/setpgrp.c", + "musl/src/unistd/setregid.c", + "musl/src/unistd/setresgid.c", + "musl/src/unistd/setresuid.c", + "musl/src/unistd/setreuid.c", + "musl/src/unistd/setsid.c", + "musl/src/unistd/setuid.c", + "musl/src/unistd/setxid.c", + "musl/src/unistd/sh/pipe.s", + "musl/src/unistd/sleep.c", + "musl/src/unistd/symlink.c", + "musl/src/unistd/symlinkat.c", + "musl/src/unistd/sync.c", + "musl/src/unistd/tcgetpgrp.c", + "musl/src/unistd/tcsetpgrp.c", + "musl/src/unistd/truncate.c", + "musl/src/unistd/ttyname.c", + "musl/src/unistd/ttyname_r.c", + "musl/src/unistd/ualarm.c", + "musl/src/unistd/unlink.c", + "musl/src/unistd/unlinkat.c", + "musl/src/unistd/usleep.c", + "musl/src/unistd/write.c", + "musl/src/unistd/writev.c", + "musl/src/unistd/x32/lseek.c", +}; +const compat_time32_files = [_][]const u8{ + "musl/compat/time32/__xstat.c", + "musl/compat/time32/adjtime32.c", + "musl/compat/time32/adjtimex_time32.c", + "musl/compat/time32/aio_suspend_time32.c", + "musl/compat/time32/clock_adjtime32.c", + "musl/compat/time32/clock_getres_time32.c", + "musl/compat/time32/clock_gettime32.c", + "musl/compat/time32/clock_nanosleep_time32.c", + "musl/compat/time32/clock_settime32.c", + "musl/compat/time32/cnd_timedwait_time32.c", + "musl/compat/time32/ctime32.c", + "musl/compat/time32/ctime32_r.c", + "musl/compat/time32/difftime32.c", + "musl/compat/time32/fstat_time32.c", + "musl/compat/time32/fstatat_time32.c", + "musl/compat/time32/ftime32.c", + "musl/compat/time32/futimens_time32.c", + "musl/compat/time32/futimes_time32.c", + "musl/compat/time32/futimesat_time32.c", + "musl/compat/time32/getitimer_time32.c", + "musl/compat/time32/getrusage_time32.c", + "musl/compat/time32/gettimeofday_time32.c", + "musl/compat/time32/gmtime32.c", + "musl/compat/time32/gmtime32_r.c", + "musl/compat/time32/localtime32.c", + "musl/compat/time32/localtime32_r.c", + "musl/compat/time32/lstat_time32.c", + "musl/compat/time32/lutimes_time32.c", + "musl/compat/time32/mktime32.c", + "musl/compat/time32/mq_timedreceive_time32.c", + "musl/compat/time32/mq_timedsend_time32.c", + "musl/compat/time32/mtx_timedlock_time32.c", + "musl/compat/time32/nanosleep_time32.c", + "musl/compat/time32/ppoll_time32.c", + "musl/compat/time32/pselect_time32.c", + "musl/compat/time32/pthread_cond_timedwait_time32.c", + "musl/compat/time32/pthread_mutex_timedlock_time32.c", + "musl/compat/time32/pthread_rwlock_timedrdlock_time32.c", + "musl/compat/time32/pthread_rwlock_timedwrlock_time32.c", + "musl/compat/time32/pthread_timedjoin_np_time32.c", + "musl/compat/time32/recvmmsg_time32.c", + "musl/compat/time32/sched_rr_get_interval_time32.c", + "musl/compat/time32/select_time32.c", + "musl/compat/time32/sem_timedwait_time32.c", + "musl/compat/time32/semtimedop_time32.c", + "musl/compat/time32/setitimer_time32.c", + "musl/compat/time32/settimeofday_time32.c", + "musl/compat/time32/sigtimedwait_time32.c", + "musl/compat/time32/stat_time32.c", + "musl/compat/time32/stime32.c", + "musl/compat/time32/thrd_sleep_time32.c", + "musl/compat/time32/time32.c", + "musl/compat/time32/time32gm.c", + "musl/compat/time32/timer_gettime32.c", + "musl/compat/time32/timer_settime32.c", + "musl/compat/time32/timerfd_gettime32.c", + "musl/compat/time32/timerfd_settime32.c", + "musl/compat/time32/timespec_get_time32.c", + "musl/compat/time32/utime_time32.c", + "musl/compat/time32/utimensat_time32.c", + "musl/compat/time32/utimes_time32.c", + "musl/compat/time32/wait3_time32.c", + "musl/compat/time32/wait4_time32.c", +}; diff --git a/src-self-hosted/print_env.zig b/src/print_env.zig similarity index 62% rename from src-self-hosted/print_env.zig rename to src/print_env.zig index 9b68633d3e..d1956911e9 100644 --- a/src-self-hosted/print_env.zig +++ b/src/print_env.zig @@ -2,39 +2,39 @@ const std = @import("std"); const build_options = @import("build_options"); const introspect = @import("introspect.zig"); const Allocator = std.mem.Allocator; +const fatal = @import("main.zig").fatal; pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: anytype) !void { - const zig_lib_dir = introspect.resolveZigLibDir(gpa) catch |err| { - std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)}); - std.process.exit(1); - }; - defer gpa.free(zig_lib_dir); + const self_exe_path = try std.fs.selfExePathAlloc(gpa); + defer gpa.free(self_exe_path); - const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_dir, "std" }); + var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + }; + defer gpa.free(zig_lib_directory.path.?); + defer zig_lib_directory.handle.close(); + + const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_directory.path.?, "std" }); defer gpa.free(zig_std_dir); const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa); defer gpa.free(global_cache_dir); - const compiler_id_digest = try introspect.resolveCompilerId(gpa); - var compiler_id_buf: [compiler_id_digest.len * 2]u8 = undefined; - const compiler_id = std.fmt.bufPrint(&compiler_id_buf, "{x}", .{compiler_id_digest}) catch unreachable; - var bos = std.io.bufferedOutStream(stdout); const bos_stream = bos.outStream(); var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream); try jws.beginObject(); + try jws.objectField("zig_exe"); + try jws.emitString(self_exe_path); + try jws.objectField("lib_dir"); - try jws.emitString(zig_lib_dir); + try jws.emitString(zig_lib_directory.path.?); try jws.objectField("std_dir"); try jws.emitString(zig_std_dir); - try jws.objectField("id"); - try jws.emitString(compiler_id); - try jws.objectField("global_cache_dir"); try jws.emitString(global_cache_dir); diff --git a/src-self-hosted/print_targets.zig b/src/print_targets.zig similarity index 60% rename from src-self-hosted/print_targets.zig rename to src/print_targets.zig index 0fe755ffb4..724cb7a9ac 100644 --- a/src-self-hosted/print_targets.zig +++ b/src/print_targets.zig @@ -4,59 +4,11 @@ const io = std.io; const mem = std.mem; const Allocator = mem.Allocator; const Target = std.Target; +const target = @import("target.zig"); const assert = std.debug.assert; - +const glibc = @import("glibc.zig"); const introspect = @import("introspect.zig"); - -// TODO this is hard-coded until self-hosted gains this information canonically -const available_libcs = [_][]const u8{ - "aarch64_be-linux-gnu", - "aarch64_be-linux-musl", - "aarch64_be-windows-gnu", - "aarch64-linux-gnu", - "aarch64-linux-musl", - "aarch64-windows-gnu", - "armeb-linux-gnueabi", - "armeb-linux-gnueabihf", - "armeb-linux-musleabi", - "armeb-linux-musleabihf", - "armeb-windows-gnu", - "arm-linux-gnueabi", - "arm-linux-gnueabihf", - "arm-linux-musleabi", - "arm-linux-musleabihf", - "arm-windows-gnu", - "i386-linux-gnu", - "i386-linux-musl", - "i386-windows-gnu", - "mips64el-linux-gnuabi64", - "mips64el-linux-gnuabin32", - "mips64el-linux-musl", - "mips64-linux-gnuabi64", - "mips64-linux-gnuabin32", - "mips64-linux-musl", - "mipsel-linux-gnu", - "mipsel-linux-musl", - "mips-linux-gnu", - "mips-linux-musl", - "powerpc64le-linux-gnu", - "powerpc64le-linux-musl", - "powerpc64-linux-gnu", - "powerpc64-linux-musl", - "powerpc-linux-gnu", - "powerpc-linux-musl", - "riscv64-linux-gnu", - "riscv64-linux-musl", - "s390x-linux-gnu", - "s390x-linux-musl", - "sparc-linux-gnu", - "sparcv9-linux-gnu", - "wasm32-freestanding-musl", - "x86_64-linux-gnu", - "x86_64-linux-gnux32", - "x86_64-linux-musl", - "x86_64-windows-gnu", -}; +const fatal = @import("main.zig").fatal; pub fn cmdTargets( allocator: *Allocator, @@ -65,33 +17,14 @@ pub fn cmdTargets( stdout: anytype, native_target: Target, ) !void { - const available_glibcs = blk: { - const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch |err| { - std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)}); - std.process.exit(1); - }; - defer allocator.free(zig_lib_dir); - - var dir = try std.fs.cwd().openDir(zig_lib_dir, .{}); - defer dir.close(); - - const vers_txt = try dir.readFileAlloc(allocator, "libc" ++ std.fs.path.sep_str ++ "glibc" ++ std.fs.path.sep_str ++ "vers.txt", 10 * 1024); - defer allocator.free(vers_txt); - - var list = std.ArrayList(std.builtin.Version).init(allocator); - defer list.deinit(); - - var it = mem.tokenize(vers_txt, "\r\n"); - while (it.next()) |line| { - const prefix = "GLIBC_"; - assert(mem.startsWith(u8, line, prefix)); - const adjusted_line = line[prefix.len..]; - const ver = try std.builtin.Version.parse(adjusted_line); - try list.append(ver); - } - break :blk list.toOwnedSlice(); + var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| { + fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); }; - defer allocator.free(available_glibcs); + defer zig_lib_directory.handle.close(); + defer allocator.free(zig_lib_directory.path.?); + + const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle); + defer glibc_abi.destroy(allocator); var bos = io.bufferedOutStream(stdout); const bos_stream = bos.outStream(); @@ -127,18 +60,22 @@ pub fn cmdTargets( try jws.objectField("libc"); try jws.beginArray(); - for (available_libcs) |libc| { + for (target.available_libcs) |libc| { + const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi), + }); + defer allocator.free(tmp); try jws.arrayElem(); - try jws.emitString(libc); + try jws.emitString(tmp); } try jws.endArray(); try jws.objectField("glibc"); try jws.beginArray(); - for (available_glibcs) |glibc| { + for (glibc_abi.all_versions) |ver| { try jws.arrayElem(); - const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc}); + const tmp = try std.fmt.allocPrint(allocator, "{}", .{ver}); defer allocator.free(tmp); try jws.emitString(tmp); } @@ -215,7 +152,6 @@ pub fn cmdTargets( try jws.emitString(@tagName(native_target.os.tag)); try jws.objectField("abi"); try jws.emitString(@tagName(native_target.abi)); - // TODO implement native glibc version detection in self-hosted try jws.endObject(); try jws.endObject(); diff --git a/src/stage1.zig b/src/stage1.zig new file mode 100644 index 0000000000..a989ad4be3 --- /dev/null +++ b/src/stage1.zig @@ -0,0 +1,426 @@ +//! This is the main entry point for the Zig/C++ hybrid compiler (stage1). +//! It has the functions exported from Zig, called in C++, and bindings for +//! the functions exported from C++, called from Zig. + +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const CrossTarget = std.zig.CrossTarget; +const Target = std.Target; + +const build_options = @import("build_options"); +const stage2 = @import("main.zig"); +const fatal = stage2.fatal; +const Compilation = @import("Compilation.zig"); +const translate_c = @import("translate_c.zig"); +const target_util = @import("target.zig"); + +comptime { + assert(std.builtin.link_libc); + assert(build_options.is_stage1); + assert(build_options.have_llvm); + _ = @import("compiler_rt"); +} + +pub const log = stage2.log; +pub const log_level = stage2.log_level; + +pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int { + std.debug.maybeEnableSegfaultHandler(); + + zig_stage1_os_init(); + + const gpa = std.heap.c_allocator; + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = &arena_instance.allocator; + + const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"}); + for (args) |*arg, i| { + arg.* = mem.spanZ(argv[i]); + } + stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)}); + return 0; +} + +/// Matches stage2.Color; +pub const ErrColor = c_int; +/// Matches std.builtin.CodeModel +pub const CodeModel = c_int; +/// Matches std.Target.Os.Tag +pub const OS = c_int; +/// Matches std.builtin.BuildMode +pub const BuildMode = c_int; + +pub const TargetSubsystem = extern enum(c_int) { + Console, + Windows, + Posix, + Native, + EfiApplication, + EfiBootServiceDriver, + EfiRom, + EfiRuntimeDriver, + Auto, +}; + +pub const Pkg = extern struct { + name_ptr: [*]const u8, + name_len: usize, + path_ptr: [*]const u8, + path_len: usize, + children_ptr: [*]*Pkg, + children_len: usize, + parent: ?*Pkg, +}; + +pub const Module = extern struct { + root_name_ptr: [*]const u8, + root_name_len: usize, + emit_o_ptr: [*]const u8, + emit_o_len: usize, + emit_h_ptr: [*]const u8, + emit_h_len: usize, + emit_asm_ptr: [*]const u8, + emit_asm_len: usize, + emit_llvm_ir_ptr: [*]const u8, + emit_llvm_ir_len: usize, + emit_analysis_json_ptr: [*]const u8, + emit_analysis_json_len: usize, + emit_docs_ptr: [*]const u8, + emit_docs_len: usize, + builtin_zig_path_ptr: [*]const u8, + builtin_zig_path_len: usize, + test_filter_ptr: [*]const u8, + test_filter_len: usize, + test_name_prefix_ptr: [*]const u8, + test_name_prefix_len: usize, + userdata: usize, + root_pkg: *Pkg, + main_progress_node: ?*std.Progress.Node, + code_model: CodeModel, + subsystem: TargetSubsystem, + err_color: ErrColor, + pic: bool, + link_libc: bool, + link_libcpp: bool, + strip: bool, + is_single_threaded: bool, + dll_export_fns: bool, + link_mode_dynamic: bool, + valgrind_enabled: bool, + function_sections: bool, + enable_stack_probing: bool, + enable_time_report: bool, + enable_stack_report: bool, + test_is_evented: bool, + verbose_tokenize: bool, + verbose_ast: bool, + verbose_ir: bool, + verbose_llvm_ir: bool, + verbose_cimport: bool, + verbose_llvm_cpu_features: bool, + + // Set by stage1 + have_c_main: bool, + have_winmain: bool, + have_wwinmain: bool, + have_winmain_crt_startup: bool, + have_wwinmain_crt_startup: bool, + have_dllmain_crt_startup: bool, + + pub fn build_object(mod: *Module) void { + zig_stage1_build_object(mod); + } + + pub fn destroy(mod: *Module) void { + zig_stage1_destroy(mod); + } +}; + +extern fn zig_stage1_os_init() void; + +pub const create = zig_stage1_create; +extern fn zig_stage1_create( + optimize_mode: BuildMode, + main_pkg_path_ptr: [*]const u8, + main_pkg_path_len: usize, + root_src_path_ptr: [*]const u8, + root_src_path_len: usize, + zig_lib_dir_ptr: [*c]const u8, + zig_lib_dir_len: usize, + target: [*c]const Stage2Target, + is_test_build: bool, +) ?*Module; + +extern fn zig_stage1_build_object(*Module) void; +extern fn zig_stage1_destroy(*Module) void; + +// ABI warning +export fn stage2_panic(ptr: [*]const u8, len: usize) void { + @panic(ptr[0..len]); +} + +// ABI warning +const Error = extern enum { + None, + OutOfMemory, + InvalidFormat, + SemanticAnalyzeFail, + AccessDenied, + Interrupted, + SystemResources, + FileNotFound, + FileSystem, + FileTooBig, + DivByZero, + Overflow, + PathAlreadyExists, + Unexpected, + ExactDivRemainder, + NegativeDenominator, + ShiftedOutOneBits, + CCompileErrors, + EndOfFile, + IsDir, + NotDir, + UnsupportedOperatingSystem, + SharingViolation, + PipeBusy, + PrimitiveTypeNotFound, + CacheUnavailable, + PathTooLong, + CCompilerCannotFindFile, + NoCCompilerInstalled, + ReadingDepFile, + InvalidDepFile, + MissingArchitecture, + MissingOperatingSystem, + UnknownArchitecture, + UnknownOperatingSystem, + UnknownABI, + InvalidFilename, + DiskQuota, + DiskSpace, + UnexpectedWriteFailure, + UnexpectedSeekFailure, + UnexpectedFileTruncationFailure, + Unimplemented, + OperationAborted, + BrokenPipe, + NoSpaceLeft, + NotLazy, + IsAsync, + ImportOutsidePkgPath, + UnknownCpuModel, + UnknownCpuFeature, + InvalidCpuFeatures, + InvalidLlvmCpuFeaturesFormat, + UnknownApplicationBinaryInterface, + ASTUnitFailure, + BadPathName, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + DeviceBusy, + UnableToSpawnCCompiler, + CCompilerExitCode, + CCompilerCrashed, + CCompilerCannotFindHeaders, + LibCRuntimeNotFound, + LibCStdLibHeaderNotFound, + LibCKernel32LibNotFound, + UnsupportedArchitecture, + WindowsSdkNotFound, + UnknownDynamicLinkerPath, + TargetHasNoDynamicLinker, + InvalidAbiVersion, + InvalidOperatingSystemVersion, + UnknownClangOption, + NestedResponseFile, + ZigIsTheCCompiler, + FileBusy, + Locked, +}; + +// ABI warning +export fn stage2_attach_segfault_handler() void { + if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) { + std.debug.attachSegfaultHandler(); + } +} + +// ABI warning +export fn stage2_progress_create() *std.Progress { + const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); + ptr.* = std.Progress{}; + return ptr; +} + +// ABI warning +export fn stage2_progress_destroy(progress: *std.Progress) void { + std.heap.c_allocator.destroy(progress); +} + +// ABI warning +export fn stage2_progress_start_root( + progress: *std.Progress, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + return progress.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ) catch @panic("timer unsupported"); +} + +// ABI warning +export fn stage2_progress_disable_tty(progress: *std.Progress) void { + progress.terminal = null; +} + +// ABI warning +export fn stage2_progress_start( + node: *std.Progress.Node, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory"); + child_node.* = node.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ); + child_node.activate(); + return child_node; +} + +// ABI warning +export fn stage2_progress_end(node: *std.Progress.Node) void { + node.end(); + if (&node.context.root != node) { + std.heap.c_allocator.destroy(node); + } +} + +// ABI warning +export fn stage2_progress_complete_one(node: *std.Progress.Node) void { + node.completeOne(); +} + +// ABI warning +export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void { + node.completed_items = done_count; + node.estimated_total_items = total_count; + node.activate(); + node.context.maybeRefresh(); +} + +// ABI warning +pub const Stage2Target = extern struct { + arch: c_int, + os: OS, + abi: c_int, + + is_native_os: bool, + is_native_cpu: bool, + + llvm_cpu_name: ?[*:0]const u8, + llvm_cpu_features: ?[*:0]const u8, +}; + +// ABI warning +const Stage2SemVer = extern struct { + major: u32, + minor: u32, + patch: u32, +}; + +// ABI warning +export fn stage2_cimport( + stage1: *Module, + c_src_ptr: [*]const u8, + c_src_len: usize, + out_zig_path_ptr: *[*]const u8, + out_zig_path_len: *usize, + out_errors_ptr: *[*]translate_c.ClangErrMsg, + out_errors_len: *usize, +) Error { + const comp = @intToPtr(*Compilation, stage1.userdata); + const c_src = c_src_ptr[0..c_src_len]; + const result = comp.cImport(c_src) catch |err| switch (err) { + error.SystemResources => return .SystemResources, + error.OperationAborted => return .OperationAborted, + error.BrokenPipe => return .BrokenPipe, + error.DiskQuota => return .DiskQuota, + error.FileTooBig => return .FileTooBig, + error.NoSpaceLeft => return .NoSpaceLeft, + error.AccessDenied => return .AccessDenied, + error.OutOfMemory => return .OutOfMemory, + error.Unexpected => return .Unexpected, + error.InputOutput => return .FileSystem, + error.ASTUnitFailure => return .ASTUnitFailure, + error.CacheUnavailable => return .CacheUnavailable, + else => return .Unexpected, + }; + out_zig_path_ptr.* = result.out_zig_path.ptr; + out_zig_path_len.* = result.out_zig_path.len; + out_errors_ptr.* = result.errors.ptr; + out_errors_len.* = result.errors.len; + if (result.errors.len != 0) return .CCompileErrors; + return Error.None; +} + +export fn stage2_add_link_lib( + stage1: *Module, + lib_name_ptr: [*c]const u8, + lib_name_len: usize, + symbol_name_ptr: [*c]const u8, + symbol_name_len: usize, +) ?[*:0]const u8 { + const comp = @intToPtr(*Compilation, stage1.userdata); + const lib_name = std.ascii.allocLowerString(comp.gpa, lib_name_ptr[0..lib_name_len]) catch return "out of memory"; + const target = comp.getTarget(); + const is_libc = target_util.is_libc_lib_name(target, lib_name); + if (is_libc) { + if (!comp.bin_file.options.link_libc) { + return "dependency on libc must be explicitly specified in the build command"; + } + return null; + } + if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (!comp.bin_file.options.link_libcpp) { + return "dependency on libc++ must be explicitly specified in the build command"; + } + return null; + } + if (!target.isWasm() and !comp.bin_file.options.pic) { + return std.fmt.allocPrint0( + comp.gpa, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ) catch "out of memory"; + } + comp.stage1AddLinkLib(lib_name) catch |err| { + return std.fmt.allocPrint0(comp.gpa, "unable to add link lib '{s}': {s}", .{ + lib_name, @errorName(err), + }) catch "out of memory"; + }; + return null; +} + +export fn stage2_fetch_file( + stage1: *Module, + path_ptr: [*]const u8, + path_len: usize, + result_len: *usize, +) ?[*]const u8 { + const comp = @intToPtr(*Compilation, stage1.userdata); + const file_path = path_ptr[0..path_len]; + const max_file_size = std.math.maxInt(u32); + const contents = comp.stage1_cache_manifest.addFilePostFetch(file_path, max_file_size) catch return null; + result_len.* = contents.len; + return contents.ptr; +} diff --git a/src/all_types.hpp b/src/stage1/all_types.hpp similarity index 96% rename from src/all_types.hpp rename to src/stage1/all_types.hpp index 1fa04f2b79..7a5016d004 100644 --- a/src/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -10,7 +10,6 @@ #include "list.hpp" #include "buffer.hpp" -#include "cache_hash.hpp" #include "zig_llvm.h" #include "hash_map.hpp" #include "errmsg.hpp" @@ -1639,8 +1638,6 @@ struct ZigType { size_t abi_size; // Number of bits of information in this type. Known after ResolveStatusSizeKnown. size_t size_in_bits; - - bool gen_h_loop_flag; }; enum FnAnalState { @@ -1976,67 +1973,16 @@ struct TimeEvent { const char *name; }; -enum BuildMode { - BuildModeDebug, - BuildModeFastRelease, - BuildModeSafeRelease, - BuildModeSmallRelease, -}; - -enum CodeModel { - CodeModelDefault, - CodeModelTiny, - CodeModelSmall, - CodeModelKernel, - CodeModelMedium, - CodeModelLarge, -}; - -struct LinkLib { - Buf *name; - Buf *path; - ZigList symbols; // the list of symbols that we depend on from this lib - bool provided_explicitly; -}; - -enum ValgrindSupport { - ValgrindSupportAuto, - ValgrindSupportDisabled, - ValgrindSupportEnabled, -}; - -enum WantPIC { - WantPICAuto, - WantPICDisabled, - WantPICEnabled, -}; - -enum WantStackCheck { - WantStackCheckAuto, - WantStackCheckDisabled, - WantStackCheckEnabled, -}; - -enum WantCSanitize { - WantCSanitizeAuto, - WantCSanitizeDisabled, - WantCSanitizeEnabled, -}; - -enum OptionalBool { - OptionalBoolNull, - OptionalBoolFalse, - OptionalBoolTrue, -}; - struct CFile { ZigList args; const char *source_path; const char *preprocessor_only_basename; }; -// When adding fields, check if they should be added to the hash computation in build_with_cache struct CodeGen { + // Other code depends on this being first. + ZigStage1 stage1; + // arena allocator destroyed just prior to codegen emit heap::ArenaAllocator *pass1_arena; @@ -2048,8 +1994,6 @@ struct CodeGen { ZigLLVMDIBuilder *dbuilder; ZigLLVMDICompileUnit *compile_unit; ZigLLVMDIFile *compile_unit_file; - LinkLib *libc_link_lib; - LinkLib *libcpp_link_lib; LLVMTargetDataRef target_data_ref; LLVMTargetMachineRef target_machine; ZigLLVMDIFile *dummy_di_file; @@ -2104,7 +2048,6 @@ struct CodeGen { ZigList inline_fns; ZigList test_fns; ZigList errors_by_index; - ZigList caches_to_release; size_t largest_err_name_len; ZigList type_resolve_stack; @@ -2173,18 +2116,17 @@ struct CodeGen { Buf llvm_triple_str; Buf global_asm; Buf o_file_output_path; - Buf bin_file_output_path; + Buf h_file_output_path; Buf asm_file_output_path; Buf llvm_ir_file_output_path; + Buf analysis_json_output_path; + Buf docs_output_path; Buf *cache_dir; - // As an input parameter, mutually exclusive with enable_cache. But it gets - // populated in codegen_build_and_link. - Buf *output_dir; Buf *c_artifact_dir; const char **libc_include_dir_list; size_t libc_include_dir_len; - Buf *zig_c_headers_dir; // Cannot be overridden; derived from zig_lib_dir. + Buf *builtin_zig_path; Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir. IrInstSrc *invalid_inst_src; @@ -2206,118 +2148,46 @@ struct CodeGen { Stage2ProgressNode *main_progress_node; Stage2ProgressNode *sub_progress_node; - WantPIC want_pic; - WantStackCheck want_stack_check; - WantCSanitize want_sanitize_c; - CacheHash cache_hash; ErrColor err_color; uint32_t next_unresolved_index; unsigned pointer_size_bytes; - uint32_t target_os_index; - uint32_t target_arch_index; - uint32_t target_sub_arch_index; - uint32_t target_abi_index; - uint32_t target_oformat_index; bool is_big_endian; - bool have_c_main; - bool have_winmain; - bool have_wwinmain; - bool have_winmain_crt_startup; - bool have_wwinmain_crt_startup; - bool have_dllmain_crt_startup; bool have_err_ret_tracing; - bool link_eh_frame_hdr; - bool c_want_stdint; - bool c_want_stdbool; bool verbose_tokenize; bool verbose_ast; - bool verbose_link; bool verbose_ir; bool verbose_llvm_ir; bool verbose_cimport; - bool verbose_cc; bool verbose_llvm_cpu_features; bool error_during_imports; bool generate_error_name_table; - bool enable_cache; // mutually exclusive with output_dir bool enable_time_report; bool enable_stack_report; - bool system_linker_hack; bool reported_bad_link_libc_error; - bool is_dynamic; // shared library rather than static library. dynamic musl rather than static musl. bool need_frame_size_prefix_data; - bool disable_c_depfile; - - //////////////////////////// Participates in Input Parameter Cache Hash - /////// Note: there is a separate cache hash for builtin.zig, when adding fields, - /////// consider if they need to go into both. - ZigList link_libs_list; - // add -framework [name] args to linker - ZigList darwin_frameworks; - // add -rpath [name] args to linker - ZigList rpath_list; - ZigList forbidden_libs; - ZigList link_objects; - ZigList assembly_files; - ZigList c_source_files; - ZigList lib_dirs; - ZigList framework_dirs; - - Stage2LibCInstallation *libc; - - bool is_versioned; - size_t version_major; - size_t version_minor; - size_t version_patch; - const char *linker_script; - size_t stack_size_override; + bool link_libc; + bool link_libcpp; BuildMode build_mode; - OutType out_type; const ZigTarget *zig_target; TargetSubsystem subsystem; // careful using this directly; see detect_subsystem - ValgrindSupport valgrind_support; CodeModel code_model; - OptionalBool linker_gc_sections; - OptionalBool linker_allow_shlib_undefined; - OptionalBool linker_bind_global_refs_locally; bool strip_debug_symbols; bool is_test_build; bool is_single_threaded; - bool want_single_threaded; - bool linker_rdynamic; - bool each_lib_rpath; - bool is_dummy_so; - bool disable_gen_h; - bool bundle_compiler_rt; bool have_pic; - bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic + bool link_mode_dynamic; + bool dll_export_fns; bool have_stack_probing; - bool have_sanitize_c; bool function_sections; - bool enable_dump_analysis; - bool enable_doc_generation; - bool emit_bin; - bool emit_asm; - bool emit_llvm_ir; bool test_is_evented; - bool linker_z_nodelete; - bool linker_z_defs; + bool valgrind_enabled; Buf *root_out_name; Buf *test_filter; Buf *test_name_prefix; Buf *zig_lib_dir; Buf *zig_std_dir; - Buf *version_script_path; - Buf *override_soname; - Buf *linker_optimization; - - const char **llvm_argv; - size_t llvm_argv_len; - - const char **clang_argv; - size_t clang_argv_len; }; struct ZigVar { diff --git a/src/analyze.cpp b/src/stage1/analyze.cpp similarity index 99% rename from src/analyze.cpp rename to src/stage1/analyze.cpp index 3ba4fd7928..369c284684 100644 --- a/src/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -825,6 +825,7 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi } ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { + Error err; assert(ptr_type->id == ZigTypeIdPointer); assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); @@ -833,6 +834,11 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { return *parent_pointer; } + // We use the pointer type's abi size below, so we have to resolve it now. + if ((err = type_resolve(g, ptr_type, ResolveStatusSizeKnown))) { + codegen_report_errors_and_exit(g); + } + ZigType *entry = new_type_table_entry(ZigTypeIdStruct); buf_resize(&entry->name, 0); @@ -3489,19 +3495,19 @@ void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLink } void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) { - if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->libc_link_lib != nullptr) { - g->have_c_main = true; + if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->link_libc) { + g->stage1.have_c_main = true; } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) { if (strcmp(symbol_name, "WinMain") == 0) { - g->have_winmain = true; + g->stage1.have_winmain = true; } else if (strcmp(symbol_name, "wWinMain") == 0) { - g->have_wwinmain = true; + g->stage1.have_wwinmain = true; } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) { - g->have_winmain_crt_startup = true; + g->stage1.have_winmain_crt_startup = true; } else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) { - g->have_wwinmain_crt_startup = true; + g->stage1.have_wwinmain_crt_startup = true; } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) { - g->have_dllmain_crt_startup = true; + g->stage1.have_dllmain_crt_startup = true; } } @@ -7856,40 +7862,6 @@ const char *type_id_name(ZigTypeId id) { zig_unreachable(); } -LinkLib *create_link_lib(Buf *name) { - LinkLib *link_lib = heap::c_allocator.create(); - link_lib->name = name; - return link_lib; -} - -LinkLib *add_link_lib(CodeGen *g, Buf *name) { - bool is_libc = buf_eql_str(name, "c"); - bool is_libcpp = buf_eql_str(name, "c++") || buf_eql_str(name, "c++abi"); - - if (is_libc && g->libc_link_lib != nullptr) - return g->libc_link_lib; - - if (is_libcpp && g->libcpp_link_lib != nullptr) - return g->libcpp_link_lib; - - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *existing_lib = g->link_libs_list.at(i); - if (buf_eql_buf(existing_lib->name, name)) { - return existing_lib; - } - } - - LinkLib *link_lib = create_link_lib(name); - g->link_libs_list.append(link_lib); - - if (is_libc) - g->libc_link_lib = link_lib; - if (is_libcpp) - g->libcpp_link_lib = link_lib; - - return link_lib; -} - ZigType *get_align_amt_type(CodeGen *g) { if (g->align_amt_type == nullptr) { // according to LLVM the maximum alignment is 1 << 29. @@ -8010,12 +7982,13 @@ not_integer: return ErrorNone; } -Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) { - if (g->enable_cache) { - return cache_add_file_fetch(&g->cache_hash, resolved_path, contents); - } else { - return os_fetch_file_path(resolved_path, contents); - } +Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents_buf) { + size_t len; + const char *contents = stage2_fetch_file(&g->stage1, buf_ptr(resolved_path), buf_len(resolved_path), &len); + if (contents == nullptr) + return ErrorFileNotFound; + buf_init_from_mem(contents_buf, contents, len); + return ErrorNone; } static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) { diff --git a/src/analyze.hpp b/src/stage1/analyze.hpp similarity index 98% rename from src/analyze.hpp rename to src/stage1/analyze.hpp index 0df1a4ba91..07601e6dea 100644 --- a/src/analyze.hpp +++ b/src/stage1/analyze.hpp @@ -200,8 +200,6 @@ ZigTypeId type_id_at_index(size_t index); size_t type_id_len(); size_t type_id_index(ZigType *entry); ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); -LinkLib *create_link_lib(Buf *name); -LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); bool optional_value_is_null(ZigValue *val); uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry); @@ -256,7 +254,6 @@ Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_no void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn); Buf *type_bare_name(ZigType *t); Buf *type_h_name(ZigType *t); -Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose); LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type); ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type); diff --git a/src/ast_render.cpp b/src/stage1/ast_render.cpp similarity index 100% rename from src/ast_render.cpp rename to src/stage1/ast_render.cpp diff --git a/src/ast_render.hpp b/src/stage1/ast_render.hpp similarity index 100% rename from src/ast_render.hpp rename to src/stage1/ast_render.hpp diff --git a/src/bigfloat.cpp b/src/stage1/bigfloat.cpp similarity index 100% rename from src/bigfloat.cpp rename to src/stage1/bigfloat.cpp diff --git a/src/bigfloat.hpp b/src/stage1/bigfloat.hpp similarity index 100% rename from src/bigfloat.hpp rename to src/stage1/bigfloat.hpp diff --git a/src/bigint.cpp b/src/stage1/bigint.cpp similarity index 100% rename from src/bigint.cpp rename to src/stage1/bigint.cpp diff --git a/src/bigint.hpp b/src/stage1/bigint.hpp similarity index 100% rename from src/bigint.hpp rename to src/stage1/bigint.hpp diff --git a/src/buffer.cpp b/src/stage1/buffer.cpp similarity index 100% rename from src/buffer.cpp rename to src/stage1/buffer.cpp diff --git a/src/buffer.hpp b/src/stage1/buffer.hpp similarity index 100% rename from src/buffer.hpp rename to src/stage1/buffer.hpp diff --git a/src/codegen.cpp b/src/stage1/codegen.cpp similarity index 84% rename from src/codegen.cpp rename to src/stage1/codegen.cpp index b5c1ca3a41..2fc85b42fe 100644 --- a/src/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8,7 +8,6 @@ #include "analyze.hpp" #include "ast_render.hpp" #include "codegen.hpp" -#include "compiler.hpp" #include "config.h" #include "errmsg.hpp" #include "error.hpp" @@ -21,7 +20,6 @@ #include "stage2.h" #include "dump_analysis.hpp" #include "softfloat.hpp" -#include "mem_profile.hpp" #include #include @@ -72,39 +70,6 @@ static const char *symbols_that_llvm_depends_on[] = { // TODO probably all of compiler-rt needs to go here }; -void codegen_set_clang_argv(CodeGen *g, const char **args, size_t len) { - g->clang_argv = args; - g->clang_argv_len = len; -} - -void codegen_set_llvm_argv(CodeGen *g, const char **args, size_t len) { - g->llvm_argv = args; - g->llvm_argv_len = len; -} - -void codegen_set_test_filter(CodeGen *g, Buf *filter) { - g->test_filter = filter; -} - -void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) { - g->test_name_prefix = prefix; -} - -void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch) { - g->is_versioned = is_versioned; - g->version_major = major; - g->version_minor = minor; - g->version_patch = patch; -} - -void codegen_set_each_lib_rpath(CodeGen *g, bool each_lib_rpath) { - g->each_lib_rpath = each_lib_rpath; -} - -void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) { - g->err_color = err_color; -} - void codegen_set_strip(CodeGen *g, bool strip) { g->strip_debug_symbols = strip; if (!target_has_debug_info(g->zig_target)) { @@ -112,39 +77,6 @@ void codegen_set_strip(CodeGen *g, bool strip) { } } -void codegen_set_out_name(CodeGen *g, Buf *out_name) { - g->root_out_name = out_name; -} - -void codegen_add_lib_dir(CodeGen *g, const char *dir) { - g->lib_dirs.append(dir); -} - -void codegen_add_rpath(CodeGen *g, const char *name) { - g->rpath_list.append(buf_create_from_str(name)); -} - -LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) { - return add_link_lib(g, name); -} - -void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) { - codegen->forbidden_libs.append(lib); -} - -void codegen_add_framework(CodeGen *g, const char *framework) { - g->darwin_frameworks.append(buf_create_from_str(framework)); -} - -void codegen_set_rdynamic(CodeGen *g, bool rdynamic) { - g->linker_rdynamic = rdynamic; -} - -void codegen_set_linker_script(CodeGen *g, const char *linker_script) { - g->linker_script = linker_script; -} - - static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name); static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name); static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name); @@ -155,7 +87,6 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, LLVMValueRef result_loc, bool non_async); -static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -389,7 +320,7 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) { } static void maybe_export_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { - if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->is_dynamic) { + if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->dll_export_fns) { LLVMSetDLLStorageClass(global_value, LLVMDLLExportStorageClass); } } @@ -541,7 +472,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { g->build_mode != BuildModeSmallRelease && !fn->def_scope->safety_off; if (want_fn_safety) { - if (g->libc_link_lib != nullptr) { + if (g->link_libc) { addLLVMFnAttr(llvm_fn, "sspstrong"); addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4"); } @@ -3859,20 +3790,6 @@ static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default zig_unreachable(); } -static bool want_valgrind_support(CodeGen *g) { - if (!target_has_valgrind_support(g->zig_target)) - return false; - switch (g->valgrind_support) { - case ValgrindSupportDisabled: - return false; - case ValgrindSupportEnabled: - return true; - case ValgrindSupportAuto: - return g->build_mode == BuildModeDebug; - } - zig_unreachable(); -} - static void gen_valgrind_undef(CodeGen *g, LLVMValueRef dest_ptr, LLVMValueRef byte_count) { static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545; ZigType *usize = g->builtin_types.entry_usize; @@ -3895,7 +3812,7 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_ LLVMValueRef byte_count = LLVMConstInt(usize->llvm_type, size_bytes, false); ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); // then tell valgrind that the memory is undefined even though we just memset it - if (want_valgrind_support(g)) { + if (g->valgrind_enabled) { gen_valgrind_undef(g, dest_ptr, byte_count); } } @@ -5598,7 +5515,7 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, fill_char, len_val, get_ptr_align(g, ptr_type), ptr_type->data.pointer.is_volatile); - if (val_is_undef && want_valgrind_support(g)) { + if (val_is_undef && g->valgrind_enabled) { gen_valgrind_undef(g, dest_ptr_casted, len_val); } return nullptr; @@ -8326,9 +8243,9 @@ static void zig_llvm_emit_output(CodeGen *g) { const char *bin_filename = nullptr; const char *llvm_ir_filename = nullptr; - if (g->emit_bin) bin_filename = buf_ptr(&g->o_file_output_path); - if (g->emit_asm) asm_filename = buf_ptr(&g->asm_file_output_path); - if (g->emit_llvm_ir) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); + if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path); + if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path); + if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire // pipeline multiple times if this is requested. @@ -8350,13 +8267,6 @@ static void zig_llvm_emit_output(CodeGen *g) { exit(1); } - if (g->emit_bin) { - g->link_objects.append(&g->o_file_output_path); - if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) { - zig_link_add_compiler_rt(g, g->sub_progress_node); - } - } - LLVMDisposeModule(g->module); g->module = nullptr; LLVMDisposeTargetData(g->target_data_ref); @@ -8751,79 +8661,16 @@ static const char *subsystem_to_str(TargetSubsystem subsystem) { zig_unreachable(); } -static bool detect_dynamic_link(CodeGen *g) { - if (g->is_dynamic) - return true; - if (g->zig_target->os == OsFreestanding) - return false; - if (target_os_requires_libc(g->zig_target->os)) - return true; - if (g->libc_link_lib != nullptr && target_is_glibc(g->zig_target)) - return true; - // If there are no dynamic libraries then we can disable dynamic linking. - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *link_lib = g->link_libs_list.at(i); - if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) - continue; - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) - continue; - return true; - } - return false; -} - -static bool detect_pic(CodeGen *g) { - if (target_requires_pic(g->zig_target, g->libc_link_lib != nullptr)) - return true; - switch (g->want_pic) { - case WantPICDisabled: - return false; - case WantPICEnabled: - return true; - case WantPICAuto: - return g->have_dynamic_link; - } - zig_unreachable(); -} - -static bool detect_stack_probing(CodeGen *g) { - if (!target_supports_stack_probing(g->zig_target)) - return false; - switch (g->want_stack_check) { - case WantStackCheckDisabled: - return false; - case WantStackCheckEnabled: - return true; - case WantStackCheckAuto: - return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug; - } - zig_unreachable(); -} - -static bool detect_sanitize_c(CodeGen *g) { - if (!target_supports_sanitize_c(g->zig_target)) - return false; - switch (g->want_sanitize_c) { - case WantCSanitizeDisabled: - return false; - case WantCSanitizeEnabled: - return true; - case WantCSanitizeAuto: - return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug; - } - zig_unreachable(); -} - // Returns TargetSubsystemAuto to mean "no subsystem" TargetSubsystem detect_subsystem(CodeGen *g) { if (g->subsystem != TargetSubsystemAuto) return g->subsystem; if (g->zig_target->os == OsWindows) { - if (g->have_dllmain_crt_startup || (g->out_type == OutTypeLib && g->is_dynamic)) + if (g->stage1.have_dllmain_crt_startup) return TargetSubsystemAuto; - if (g->have_c_main || g->is_test_build || g->have_winmain_crt_startup || g->have_wwinmain_crt_startup) + if (g->stage1.have_c_main || g->is_test_build || g->stage1.have_winmain_crt_startup || g->stage1.have_wwinmain_crt_startup) return TargetSubsystemConsole; - if (g->have_winmain || g->have_wwinmain) + if (g->stage1.have_winmain || g->stage1.have_wwinmain) return TargetSubsystemWindows; } else if (g->zig_target->os == OsUefi) { return TargetSubsystemEfiApplication; @@ -8831,15 +8678,6 @@ TargetSubsystem detect_subsystem(CodeGen *g) { return TargetSubsystemAuto; } -static bool detect_single_threaded(CodeGen *g) { - if (g->want_single_threaded) - return true; - if (target_is_single_threaded(g->zig_target)) { - return true; - } - return false; -} - static bool detect_err_ret_tracing(CodeGen *g) { return !g->strip_debug_symbols && g->build_mode != BuildModeFastRelease && @@ -8847,30 +8685,30 @@ static bool detect_err_ret_tracing(CodeGen *g) { } static LLVMCodeModel to_llvm_code_model(CodeGen *g) { - switch (g->code_model) { - case CodeModelDefault: - return LLVMCodeModelDefault; - case CodeModelTiny: - return LLVMCodeModelTiny; - case CodeModelSmall: - return LLVMCodeModelSmall; - case CodeModelKernel: - return LLVMCodeModelKernel; - case CodeModelMedium: - return LLVMCodeModelMedium; - case CodeModelLarge: - return LLVMCodeModelLarge; - } + switch (g->code_model) { + case CodeModelDefault: + return LLVMCodeModelDefault; + case CodeModelTiny: + return LLVMCodeModelTiny; + case CodeModelSmall: + return LLVMCodeModelSmall; + case CodeModelKernel: + return LLVMCodeModelKernel; + case CodeModelMedium: + return LLVMCodeModelMedium; + case CodeModelLarge: + return LLVMCodeModelLarge; + } - zig_unreachable(); + zig_unreachable(); } Buf *codegen_generate_builtin_source(CodeGen *g) { - g->have_dynamic_link = detect_dynamic_link(g); - g->have_pic = detect_pic(g); - g->have_stack_probing = detect_stack_probing(g); - g->have_sanitize_c = detect_sanitize_c(g); - g->is_single_threaded = detect_single_threaded(g); + // Note that this only runs when zig0 is building the self-hosted zig compiler code, + // so it makes a few assumption that are always true for that case. Once we have + // built the stage2 zig components then zig is in charge of generating the builtin.zig + // file. + g->have_err_ret_tracing = detect_err_ret_tracing(g); Buf *contents = buf_alloc(); @@ -8884,7 +8722,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *name = target_os_name(os_type); if (os_type == g->zig_target->os) { - g->target_os_index = i; cur_os = name; } } @@ -8898,7 +8735,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { ZigLLVM_ArchType arch = target_arch_enum(arch_i); const char *arch_name = target_arch_name(arch); if (arch == g->zig_target->arch) { - g->target_arch_index = arch_i; cur_arch = arch_name; } } @@ -8913,7 +8749,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *name = target_abi_name(abi); if (abi == g->zig_target->abi) { - g->target_abi_index = i; cur_abi = name; } } @@ -8929,7 +8764,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target); if (oformat == target_oformat) { - g->target_oformat_index = i; cur_obj_fmt = name; } } @@ -8975,83 +8809,27 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { static_assert(TargetSubsystemEfiBootServiceDriver == 5, ""); static_assert(TargetSubsystemEfiRom == 6, ""); static_assert(TargetSubsystemEfiRuntimeDriver == 7, ""); - { - const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little"; - buf_appendf(contents, "pub const endian = %s;\n", endian_str); - } - const char *out_type = nullptr; - switch (g->out_type) { - case OutTypeExe: - out_type = "Exe"; - break; - case OutTypeLib: - out_type = "Lib"; - break; - case OutTypeObj: - case OutTypeUnknown: // This happens when running the `zig builtin` command. - out_type = "Obj"; - break; - } - buf_appendf(contents, "pub const output_mode = OutputMode.%s;\n", out_type); - const char *link_type = g->have_dynamic_link ? "Dynamic" : "Static"; - buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); - buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); + + buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch`\n"); + buf_append_str(contents, "pub const arch = Target.current.cpu.arch;\n"); + buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n"); + buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n"); + buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n"); + buf_appendf(contents, "pub const link_mode = LinkMode.Static;\n"); + buf_appendf(contents, "pub const is_test = false;\n"); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); - buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n"); - buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); - { - buf_append_str(contents, "pub const cpu: Cpu = "); - if (g->zig_target->cpu_builtin_str != nullptr) { - buf_append_str(contents, g->zig_target->cpu_builtin_str); - } else { - buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch); - } - } - { - buf_append_str(contents, "pub const os = "); - if (g->zig_target->os_builtin_str != nullptr) { - buf_append_str(contents, g->zig_target->os_builtin_str); - } else { - buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os); - } - } + buf_appendf(contents, "pub const cpu: Cpu = Target.Cpu.baseline(.%s);\n", cur_arch); + buf_appendf(contents, "pub const os = Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os); buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); - buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); - buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->libcpp_link_lib != nullptr)); + buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->link_libc)); + buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->link_libcpp)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); - buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g))); + buf_appendf(contents, "pub const valgrind_support = false;\n"); buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic)); buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols)); - - { - const char *code_model; - switch (g->code_model) { - case CodeModelDefault: - code_model = "default"; - break; - case CodeModelTiny: - code_model = "tiny"; - break; - case CodeModelSmall: - code_model = "small"; - break; - case CodeModelKernel: - code_model = "kernel"; - break; - case CodeModelMedium: - code_model = "medium"; - break; - case CodeModelLarge: - code_model = "large"; - break; - default: - zig_unreachable(); - } - - buf_appendf(contents, "pub const code_model = CodeModel.%s;\n", code_model); - } + buf_appendf(contents, "pub const code_model = CodeModel.default;\n"); { TargetSubsystem detected_subsystem = detect_subsystem(g); @@ -9060,15 +8838,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { } } - if (g->is_test_build) { - buf_appendf(contents, - "pub var test_functions: []TestFn = undefined; // overwritten later\n" - ); - - buf_appendf(contents, "pub const test_io_mode = %s;\n", - g->test_is_evented ? ".evented" : ".blocking"); - } - return contents; } @@ -9077,95 +8846,47 @@ static ZigPackage *create_test_runner_pkg(CodeGen *g) { } static Error define_builtin_compile_vars(CodeGen *g) { + Error err; + if (g->std_package == nullptr) return ErrorNone; - Error err; - - Buf *manifest_dir = buf_alloc(); - os_path_join(get_global_cache_dir(), buf_create_from_str("builtin"), manifest_dir); - - CacheHash cache_hash; - cache_init(&cache_hash, manifest_dir); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) - return err; - - // Only a few things affect builtin.zig - cache_buf(&cache_hash, compiler_id); - cache_int(&cache_hash, g->build_mode); - cache_bool(&cache_hash, g->strip_debug_symbols); - cache_int(&cache_hash, g->out_type); - cache_bool(&cache_hash, detect_dynamic_link(g)); - cache_bool(&cache_hash, g->is_test_build); - cache_bool(&cache_hash, g->is_single_threaded); - cache_bool(&cache_hash, g->test_is_evented); - cache_int(&cache_hash, g->code_model); - cache_int(&cache_hash, g->zig_target->is_native_os); - cache_int(&cache_hash, g->zig_target->is_native_cpu); - cache_int(&cache_hash, g->zig_target->arch); - cache_int(&cache_hash, g->zig_target->vendor); - cache_int(&cache_hash, g->zig_target->os); - cache_int(&cache_hash, g->zig_target->abi); - if (g->zig_target->cache_hash != nullptr) { - cache_mem(&cache_hash, g->zig_target->cache_hash, g->zig_target->cache_hash_len); - } - if (g->zig_target->glibc_or_darwin_version != nullptr) { - cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->major); - cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->minor); - cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->patch); - } - cache_bool(&cache_hash, g->have_err_ret_tracing); - cache_bool(&cache_hash, g->libc_link_lib != nullptr); - cache_bool(&cache_hash, g->libcpp_link_lib != nullptr); - cache_bool(&cache_hash, g->valgrind_support); - cache_bool(&cache_hash, g->link_eh_frame_hdr); - cache_int(&cache_hash, detect_subsystem(g)); - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(&cache_hash, &digest))) { - // Treat an invalid format error as a cache miss. - if (err != ErrorInvalidFormat) - return err; - } - - // We should always get a cache hit because there are no - // files in the input hash. - assert(buf_len(&digest) != 0); - - Buf *this_dir = buf_alloc(); - os_path_join(manifest_dir, &digest, this_dir); - - if ((err = os_make_path(this_dir))) - return err; + assert(g->main_pkg); const char *builtin_zig_basename = "builtin.zig"; - Buf *builtin_zig_path = buf_alloc(); - os_path_join(this_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); - bool hit; - if ((err = os_file_exists(builtin_zig_path, &hit))) - return err; Buf *contents; - if (hit) { - contents = buf_alloc(); - if ((err = os_fetch_file_path(builtin_zig_path, contents))) { - fprintf(stderr, "Unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); - exit(1); - } - } else { + if (g->builtin_zig_path == nullptr) { + // Then this is zig0 building stage2. We can make many assumptions about the compilation. + Buf *out_dir = buf_alloc(); + os_path_split(&g->o_file_output_path, out_dir, nullptr); + g->builtin_zig_path = buf_alloc(); + os_path_join(out_dir, buf_create_from_str(builtin_zig_basename), g->builtin_zig_path); + + Buf *resolve_paths[] = { g->builtin_zig_path, }; + *g->builtin_zig_path = os_path_resolve(resolve_paths, 1); + contents = codegen_generate_builtin_source(g); - if ((err = os_write_file(builtin_zig_path, contents))) { - fprintf(stderr, "Unable to write file '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); + if ((err = os_write_file(g->builtin_zig_path, contents))) { + fprintf(stderr, "Unable to write file '%s': %s\n", buf_ptr(g->builtin_zig_path), err_str(err)); exit(1); } + + g->compile_var_package = new_package(buf_ptr(out_dir), builtin_zig_basename, "builtin"); + } else { + Buf *resolve_paths[] = { g->builtin_zig_path, }; + *g->builtin_zig_path = os_path_resolve(resolve_paths, 1); + + contents = buf_alloc(); + if ((err = os_fetch_file_path(g->builtin_zig_path, contents))) { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(g->builtin_zig_path), err_str(err)); + exit(1); + } + Buf builtin_dirname = BUF_INIT; + os_path_dirname(g->builtin_zig_path, &builtin_dirname); + g->compile_var_package = new_package(buf_ptr(&builtin_dirname), builtin_zig_basename, "builtin"); } - assert(g->main_pkg); - assert(g->std_package); - g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename, "builtin"); if (g->is_test_build) { if (g->test_runner_package == nullptr) { g->test_runner_package = create_test_runner_pkg(g); @@ -9180,7 +8901,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg); - g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents, + g->compile_var_import = add_source_file(g, g->compile_var_package, g->builtin_zig_path, contents, SourceKindPkgMain); return ErrorNone; @@ -9190,17 +8911,15 @@ static void init(CodeGen *g) { if (g->module) return; - g->have_dynamic_link = detect_dynamic_link(g); - g->have_pic = detect_pic(g); - g->have_stack_probing = detect_stack_probing(g); - g->have_sanitize_c = detect_sanitize_c(g); - g->is_single_threaded = detect_single_threaded(g); - g->have_err_ret_tracing = detect_err_ret_tracing(g); - - if (target_is_single_threaded(g->zig_target)) { - g->is_single_threaded = true; + codegen_add_time_event(g, "Initialize"); + { + const char *progress_name = "Initialize"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); } + g->have_err_ret_tracing = detect_err_ret_tracing(g); + assert(g->root_out_name); g->module = LLVMModuleCreateWithName(buf_ptr(g->root_out_name)); @@ -9230,7 +8949,7 @@ static void init(CodeGen *g) { LLVMRelocMode reloc_mode; if (g->have_pic) { reloc_mode = LLVMRelocPIC; - } else if (g->have_dynamic_link) { + } else if (g->link_mode_dynamic) { reloc_mode = LLVMRelocDynamicNoPic; } else { reloc_mode = LLVMRelocStatic; @@ -9336,446 +9055,6 @@ static void init(CodeGen *g) { } } -static void detect_libc(CodeGen *g) { - Error err; - - if (g->libc != nullptr || g->libc_link_lib == nullptr) - return; - - if (target_can_build_libc(g->zig_target)) { - const char *generic_name = target_libc_generic_name(g->zig_target); - const char *arch_name = target_arch_name(g->zig_target->arch); - const char *abi_name = target_abi_name(g->zig_target->abi); - if (target_is_musl(g->zig_target)) { - // musl has some overrides. its headers are ABI-agnostic and so they all have the "musl" ABI name. - abi_name = "musl"; - // some architectures are handled by the same set of headers - arch_name = target_arch_musl_name(g->zig_target->arch); - } - Buf *arch_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-%s", - buf_ptr(g->zig_lib_dir), arch_name, target_os_name(g->zig_target->os), abi_name); - Buf *generic_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "generic-%s", - buf_ptr(g->zig_lib_dir), generic_name); - Buf *arch_os_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-any", - buf_ptr(g->zig_lib_dir), target_arch_name(g->zig_target->arch), target_os_name(g->zig_target->os)); - Buf *generic_os_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "any-%s-any", - buf_ptr(g->zig_lib_dir), target_os_name(g->zig_target->os)); - - g->libc_include_dir_len = 4; - g->libc_include_dir_list = heap::c_allocator.allocate(g->libc_include_dir_len); - g->libc_include_dir_list[0] = buf_ptr(arch_include_dir); - g->libc_include_dir_list[1] = buf_ptr(generic_include_dir); - g->libc_include_dir_list[2] = buf_ptr(arch_os_include_dir); - g->libc_include_dir_list[3] = buf_ptr(generic_os_include_dir); - return; - } - - if (g->zig_target->is_native_os) { - g->libc = heap::c_allocator.create(); - - if ((err = stage2_libc_find_native(g->libc))) { - fprintf(stderr, - "Unable to link against libc: Unable to find libc installation: %s\n" - "See `zig libc --help` for more details.\n", err_str(err)); - exit(1); - } - - bool want_sys_dir = !mem_eql_mem(g->libc->include_dir, g->libc->include_dir_len, - g->libc->sys_include_dir, g->libc->sys_include_dir_len); - size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0; - size_t dir_count = 1 + want_sys_dir + want_um_and_shared_dirs; - g->libc_include_dir_len = 0; - g->libc_include_dir_list = heap::c_allocator.allocate(dir_count); - - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_create_from_mem( - g->libc->include_dir, g->libc->include_dir_len)); - g->libc_include_dir_len += 1; - - if (want_sys_dir) { - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_create_from_mem( - g->libc->sys_include_dir, g->libc->sys_include_dir_len)); - g->libc_include_dir_len += 1; - } - - if (want_um_and_shared_dirs != 0) { - Buf *include_dir_parent = buf_alloc(); - os_path_join(buf_create_from_mem(g->libc->include_dir, g->libc->include_dir_len), - buf_create_from_str(".."), include_dir_parent); - - Buf *buff1 = buf_alloc(); - os_path_join(include_dir_parent, buf_create_from_str("um"), buff1); - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buff1); - g->libc_include_dir_len += 1; - - Buf *buff2 = buf_alloc(); - os_path_join(include_dir_parent, buf_create_from_str("shared"), buff2); - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buff2); - g->libc_include_dir_len += 1; - } - assert(g->libc_include_dir_len == dir_count); - } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) && - !target_os_is_darwin(g->zig_target->os)) - { - Buf triple_buf = BUF_INIT; - target_triple_zig(&triple_buf, g->zig_target); - fprintf(stderr, - "Zig is unable to provide a libc for the chosen target '%s'.\n" - "The target is non-native, so Zig also cannot use the native libc installation.\n" - "Choose a target which has a libc available (see `zig targets`), or\n" - "provide a libc installation text file (see `zig libc --help`).\n", buf_ptr(&triple_buf)); - exit(1); - } -} - -// does not add the "cc" arg -void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_path, - bool translate_c, FileExt source_kind) -{ - if (translate_c) { - args.append("-x"); - args.append("c"); - } - - args.append("-nostdinc"); - if (source_kind == FileExtCpp) { - args.append("-nostdinc++"); - } - args.append("-fno-spell-checking"); - - if (g->function_sections) { - args.append("-ffunction-sections"); - } - - if (!translate_c) { - switch (g->err_color) { - case ErrColorAuto: - break; - case ErrColorOff: - args.append("-fno-color-diagnostics"); - args.append("-fno-caret-diagnostics"); - break; - case ErrColorOn: - args.append("-fcolor-diagnostics"); - args.append("-fcaret-diagnostics"); - break; - } - } - - for (size_t i = 0; i < g->framework_dirs.length; i += 1) { - args.append("-iframework"); - args.append(g->framework_dirs.at(i)); - } - - if (g->libcpp_link_lib != nullptr) { - const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", - buf_ptr(g->zig_lib_dir))); - - const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", - buf_ptr(g->zig_lib_dir))); - - args.append("-isystem"); - args.append(libcxx_include_path); - - args.append("-isystem"); - args.append(libcxxabi_include_path); - - if (target_abi_is_musl(g->zig_target->abi)) { - args.append("-D_LIBCPP_HAS_MUSL_LIBC"); - } - args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); - } - - args.append("-target"); - args.append(buf_ptr(&g->llvm_triple_str)); - - switch (source_kind) { - case FileExtC: - case FileExtCpp: - case FileExtHeader: - // According to Rich Felker libc headers are supposed to go before C language headers. - // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics - // and other compiler specific items. - args.append("-isystem"); - args.append(buf_ptr(g->zig_c_headers_dir)); - - for (size_t i = 0; i < g->libc_include_dir_len; i += 1) { - const char *include_dir = g->libc_include_dir_list[i]; - args.append("-isystem"); - args.append(include_dir); - } - - if (g->zig_target->llvm_cpu_name != nullptr) { - args.append("-Xclang"); - args.append("-target-cpu"); - args.append("-Xclang"); - args.append(g->zig_target->llvm_cpu_name); - } - if (g->zig_target->llvm_cpu_features != nullptr) { - // https://github.com/ziglang/zig/issues/5017 - SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(",")); - Optional> flag = SplitIterator_next(&it); - while (flag.is_some) { - args.append("-Xclang"); - args.append("-target-feature"); - args.append("-Xclang"); - args.append(buf_ptr(buf_create_from_slice(flag.value))); - flag = SplitIterator_next(&it); - } - } - if (translate_c) { - // this gives us access to preprocessing entities, presumably at - // the cost of performance - args.append("-Xclang"); - args.append("-detailed-preprocessing-record"); - } - if (out_dep_path != nullptr) { - args.append("-MD"); - args.append("-MV"); - args.append("-MF"); - args.append(out_dep_path); - } - break; - case FileExtAsm: - case FileExtLLVMIr: - case FileExtLLVMBitCode: - case FileExtUnknown: - break; - } - for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) { - args.append(g->zig_target->llvm_cpu_features_asm_ptr[i]); - } - - if (g->zig_target->os == OsFreestanding) { - args.append("-ffreestanding"); - } - - // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning. - // So for this target, we disable this warning. - if (g->zig_target->os == OsWindows && target_abi_is_gnu(g->zig_target->abi)) { - args.append("-Wno-pragma-pack"); - } - - if (!g->strip_debug_symbols) { - args.append("-g"); - } - - if (codegen_have_frame_pointer(g)) { - args.append("-fno-omit-frame-pointer"); - } else { - args.append("-fomit-frame-pointer"); - } - - if (g->have_sanitize_c) { - args.append("-fsanitize=undefined"); - args.append("-fsanitize-trap=undefined"); - } - - switch (g->build_mode) { - case BuildModeDebug: - // windows c runtime requires -D_DEBUG if using debug libraries - args.append("-D_DEBUG"); - args.append("-Og"); - - if (g->libc_link_lib != nullptr) { - args.append("-fstack-protector-strong"); - args.append("--param"); - args.append("ssp-buffer-size=4"); - } else { - args.append("-fno-stack-protector"); - } - break; - case BuildModeSafeRelease: - // See the comment in the BuildModeFastRelease case for why we pass -O2 rather - // than -O3 here. - args.append("-O2"); - if (g->libc_link_lib != nullptr) { - args.append("-D_FORTIFY_SOURCE=2"); - args.append("-fstack-protector-strong"); - args.append("--param"); - args.append("ssp-buffer-size=4"); - } else { - args.append("-fno-stack-protector"); - } - break; - case BuildModeFastRelease: - args.append("-DNDEBUG"); - // Here we pass -O2 rather than -O3 because, although we do the equivalent of - // -O3 in Zig code, the justification for the difference here is that Zig - // has better detection and prevention of undefined behavior, so -O3 is safer for - // Zig code than it is for C code. Also, C programmers are used to their code - // running in -O2 and thus the -O3 path has been tested less. - args.append("-O2"); - args.append("-fno-stack-protector"); - break; - case BuildModeSmallRelease: - args.append("-DNDEBUG"); - args.append("-Os"); - args.append("-fno-stack-protector"); - break; - } - - if (target_supports_fpic(g->zig_target) && g->have_pic) { - args.append("-fPIC"); - } - - for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { - args.append(g->clang_argv[arg_i]); - } - -} - -void codegen_translate_c(CodeGen *g, Buf *full_path) { - Error err; - - Buf *src_basename = buf_alloc(); - Buf *src_dirname = buf_alloc(); - os_path_split(full_path, src_dirname, src_basename); - - Buf noextname = BUF_INIT; - os_path_extname(src_basename, &noextname, nullptr); - - Buf *zig_basename = buf_sprintf("%s.zig", buf_ptr(&noextname)); - - detect_libc(g); - - Buf cache_digest = BUF_INIT; - buf_resize(&cache_digest, 0); - - CacheHash *cache_hash = nullptr; - if (g->enable_cache) { - if ((err = create_c_object_cache(g, &cache_hash, true))) { - // Already printed error; verbose = true - exit(1); - } - cache_file(cache_hash, full_path); - // to distinguish from generating a C object - cache_buf(cache_hash, buf_create_from_str("translate-c")); - - if ((err = cache_hit(cache_hash, &cache_digest))) { - if (err != ErrorInvalidFormat) { - fprintf(stderr, "unable to check cache: %s\n", err_str(err)); - exit(1); - } - } - if (cache_hash->manifest_file_path != nullptr) { - g->caches_to_release.append(cache_hash); - } - } - - if (g->enable_cache && buf_len(&cache_digest) != 0) { - // cache hit - Buf *cached_path = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s" OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(&cache_digest), buf_ptr(zig_basename)); - fprintf(stdout, "%s\n", buf_ptr(cached_path)); - return; - } - - // cache miss or cache disabled - init(g); - - Buf *out_dep_path = nullptr; - const char *out_dep_path_cstr = nullptr; - - if (g->enable_cache) { - buf_alloc();// we can't know the digest until we do the C compiler invocation, so we - // need a tmp filename. - out_dep_path = buf_alloc(); - if ((err = get_tmp_filename(g, out_dep_path, buf_sprintf("%s.d", buf_ptr(zig_basename))))) { - fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err)); - exit(1); - } - out_dep_path_cstr = buf_ptr(out_dep_path); - } - - ZigList clang_argv = {0}; - add_cc_args(g, clang_argv, out_dep_path_cstr, true, FileExtC); - - clang_argv.append(buf_ptr(full_path)); - - if (g->verbose_cc) { - fprintf(stderr, "clang"); - for (size_t i = 0; i < clang_argv.length; i += 1) { - fprintf(stderr, " %s", clang_argv.at(i)); - } - fprintf(stderr, "\n"); - } - - clang_argv.append(nullptr); // to make the [start...end] argument work - - const char *resources_path = buf_ptr(g->zig_c_headers_dir); - Stage2ErrorMsg *errors_ptr; - size_t errors_len; - Stage2Ast *ast; - - err = stage2_translate_c(&ast, &errors_ptr, &errors_len, - &clang_argv.at(0), &clang_argv.last(), resources_path); - - if (err == ErrorCCompileErrors && errors_len > 0) { - for (size_t i = 0; i < errors_len; i += 1) { - Stage2ErrorMsg *clang_err = &errors_ptr[i]; - - ErrorMsg *err_msg = err_msg_create_with_offset( - clang_err->filename_ptr ? - buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : nullptr, - clang_err->line, clang_err->column, clang_err->offset, clang_err->source, - buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); - print_err_msg(err_msg, g->err_color); - } - exit(1); - } - - if (err) { - fprintf(stderr, "unable to parse C file: %s\n", err_str(err)); - exit(1); - } - - if (!g->enable_cache) { - stage2_render_ast(ast, stdout); - return; - } - - // add the files depended on to the cache system - if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) { - // Don't treat the absence of the .d file as a fatal error, the - // compiler may not produce one eg. when compiling .s files - if (err != ErrorFileNotFound) { - fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err)); - exit(1); - } - } - if (err != ErrorFileNotFound) { - os_delete_file(out_dep_path); - } - - if ((err = cache_final(cache_hash, &cache_digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - - Buf *artifact_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(&cache_digest)); - - if ((err = os_make_path(artifact_dir))) { - fprintf(stderr, "Unable to make dir: %s\n", err_str(err)); - exit(1); - } - - Buf *cached_path = buf_sprintf("%s" OS_SEP "%s", buf_ptr(artifact_dir), buf_ptr(zig_basename)); - - FILE *out_file = fopen(buf_ptr(cached_path), "wb"); - if (out_file == nullptr) { - fprintf(stderr, "Unable to open output file: %s\n", strerror(errno)); - exit(1); - } - stage2_render_ast(ast, out_file); - if (fclose(out_file) != 0) { - fprintf(stderr, "Unable to write to output file: %s\n", strerror(errno)); - exit(1); - } - fprintf(stdout, "%s\n", buf_ptr(cached_path)); -} - static void update_test_functions_builtin_decl(CodeGen *g) { Error err; @@ -9875,49 +9154,46 @@ static void gen_root_source(CodeGen *g) { assert(root_import_alias == g->root_import); assert(g->root_out_name); - assert(g->out_type != OutTypeUnknown); - if (!g->is_dummy_so) { - // Zig has lazy top level definitions. Here we semantically analyze the panic function. - Buf *import_target_path; - Buf full_path = BUF_INIT; - ZigType *std_import; - if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import, - &import_target_path, &full_path))) - { - if (err == ErrorFileNotFound) { - fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); - } else { - fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); - } - exit(1); + // Zig has lazy top level definitions. Here we semantically analyze the panic function. + Buf *import_target_path; + Buf full_path = BUF_INIT; + ZigType *std_import; + if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import, + &import_target_path, &full_path))) + { + if (err == ErrorFileNotFound) { + fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); + } else { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); } - - Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base, - buf_create_from_str("builtin")); - assert(builtin_tld != nullptr); - resolve_top_level_decl(g, builtin_tld, nullptr, false); - report_errors_and_maybe_exit(g); - assert(builtin_tld->id == TldIdVar); - TldVar *builtin_tld_var = (TldVar*)builtin_tld; - ZigValue *builtin_val = builtin_tld_var->var->const_value; - assert(builtin_val->type->id == ZigTypeIdMetaType); - ZigType *builtin_type = builtin_val->data.x_type; - - Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base, - buf_create_from_str("panic")); - assert(panic_tld != nullptr); - resolve_top_level_decl(g, panic_tld, nullptr, false); - report_errors_and_maybe_exit(g); - assert(panic_tld->id == TldIdVar); - TldVar *panic_tld_var = (TldVar*)panic_tld; - ZigValue *panic_fn_val = panic_tld_var->var->const_value; - assert(panic_fn_val->type->id == ZigTypeIdFn); - assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction); - g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; - assert(g->panic_fn != nullptr); + exit(1); } + Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base, + buf_create_from_str("builtin")); + assert(builtin_tld != nullptr); + resolve_top_level_decl(g, builtin_tld, nullptr, false); + report_errors_and_maybe_exit(g); + assert(builtin_tld->id == TldIdVar); + TldVar *builtin_tld_var = (TldVar*)builtin_tld; + ZigValue *builtin_val = builtin_tld_var->var->const_value; + assert(builtin_val->type->id == ZigTypeIdMetaType); + ZigType *builtin_type = builtin_val->data.x_type; + + Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base, + buf_create_from_str("panic")); + assert(panic_tld != nullptr); + resolve_top_level_decl(g, panic_tld, nullptr, false); + report_errors_and_maybe_exit(g); + assert(panic_tld->id == TldIdVar); + TldVar *panic_tld_var = (TldVar*)panic_tld; + ZigValue *panic_fn_val = panic_tld_var->var->const_value; + assert(panic_fn_val->type->id == ZigTypeIdFn); + assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction); + g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; + assert(g->panic_fn != nullptr); + if (!g->error_during_imports) { semantic_analyze(g); } @@ -9934,781 +9210,6 @@ static void gen_root_source(CodeGen *g) { } -static void print_zig_cc_cmd(ZigList *args) { - for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) { - const char *space_str = (arg_i == 0) ? "" : " "; - fprintf(stderr, "%s%s", space_str, args->at(arg_i)); - } - fprintf(stderr, "\n"); -} - -// Caller should delete the file when done or rename it into a better location. -static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix) { - Error err; - buf_resize(out, 0); - os_path_join(g->cache_dir, buf_create_from_str("tmp" OS_SEP), out); - if ((err = os_make_path(out))) { - return err; - } - const char base64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; - assert(array_length(base64) == 64 + 1); - for (size_t i = 0; i < 12; i += 1) { - buf_append_char(out, base64[rand() % 64]); - } - buf_append_char(out, '-'); - buf_append_buf(out, suffix); - return ErrorNone; -} - -Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose) { - Error err; - CacheHash *cache_hash = heap::c_allocator.create(); - Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(g->cache_dir)); - cache_init(cache_hash, manifest_dir); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - if (verbose) { - fprintf(stderr, "unable to get compiler id: %s\n", err_str(err)); - } - return err; - } - cache_buf(cache_hash, compiler_id); - cache_int(cache_hash, g->err_color); - cache_list_of_str(cache_hash, g->framework_dirs.items, g->framework_dirs.length); - cache_bool(cache_hash, g->libcpp_link_lib != nullptr); - cache_buf(cache_hash, g->zig_lib_dir); - cache_buf(cache_hash, g->zig_c_headers_dir); - cache_list_of_str(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len); - cache_int(cache_hash, g->zig_target->is_native_os); - cache_int(cache_hash, g->zig_target->is_native_cpu); - cache_int(cache_hash, g->zig_target->arch); - cache_int(cache_hash, g->zig_target->vendor); - cache_int(cache_hash, g->zig_target->os); - cache_int(cache_hash, g->zig_target->abi); - cache_bool(cache_hash, g->strip_debug_symbols); - cache_int(cache_hash, g->build_mode); - cache_bool(cache_hash, g->have_pic); - cache_bool(cache_hash, g->have_sanitize_c); - cache_bool(cache_hash, want_valgrind_support(g)); - cache_bool(cache_hash, g->function_sections); - cache_int(cache_hash, g->code_model); - cache_bool(cache_hash, codegen_have_frame_pointer(g)); - cache_bool(cache_hash, g->libc_link_lib); - if (g->zig_target->cache_hash != nullptr) { - cache_mem(cache_hash, g->zig_target->cache_hash, g->zig_target->cache_hash_len); - } - - for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { - cache_str(cache_hash, g->clang_argv[arg_i]); - } - - *out_cache_hash = cache_hash; - return ErrorNone; -} - -static bool need_llvm_module(CodeGen *g) { - return buf_len(&g->main_pkg->root_src_path) != 0; -} - -// before gen_c_objects -static bool main_output_dir_is_just_one_c_object_pre(CodeGen *g) { - return g->enable_cache && g->c_source_files.length == 1 && !need_llvm_module(g) && - g->out_type == OutTypeObj && g->link_objects.length == 0; -} - -// after gen_c_objects -static bool main_output_dir_is_just_one_c_object_post(CodeGen *g) { - return g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g) && g->out_type == OutTypeObj; -} - -// returns true if it was a cache miss -static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { - Error err; - - Buf *artifact_dir; - Buf *o_final_path; - - Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(g->cache_dir)); - - Buf *c_source_file = buf_create_from_str(c_file->source_path); - Buf *c_source_basename = buf_alloc(); - os_path_split(c_source_file, nullptr, c_source_basename); - - Stage2ProgressNode *child_prog_node = stage2_progress_start(g->sub_progress_node, buf_ptr(c_source_basename), - buf_len(c_source_basename), 0); - - Buf *final_o_basename = buf_alloc(); - if (c_file->preprocessor_only_basename == nullptr) { - // We special case when doing build-obj for just one C file - if (main_output_dir_is_just_one_c_object_pre(g)) { - buf_init_from_buf(final_o_basename, g->root_out_name); - } else { - os_path_extname(c_source_basename, final_o_basename, nullptr); - } - buf_append_str(final_o_basename, target_o_file_ext(g->zig_target)); - } else { - buf_init_from_str(final_o_basename, c_file->preprocessor_only_basename); - } - - CacheHash *cache_hash; - if ((err = create_c_object_cache(g, &cache_hash, true))) { - // Already printed error; verbose = true - exit(1); - } - cache_file(cache_hash, c_source_file); - - // Note: not directory args, just args that always have a file next - static const char *file_args[] = { - "-include", - }; - for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) { - const char *arg = c_file->args.at(arg_i); - cache_str(cache_hash, arg); - for (size_t file_arg_i = 0; file_arg_i < array_length(file_args); file_arg_i += 1) { - if (strcmp(arg, file_args[file_arg_i]) == 0 && arg_i + 1 < c_file->args.length) { - arg_i += 1; - cache_file(cache_hash, buf_create_from_str(c_file->args.at(arg_i))); - } - } - } - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(cache_hash, &digest))) { - if (err != ErrorInvalidFormat) { - if (err == ErrorCacheUnavailable) { - // already printed error - } else { - fprintf(stderr, "unable to check cache when compiling C object: %s\n", err_str(err)); - } - exit(1); - } - } - bool is_cache_miss = g->disable_c_depfile || (buf_len(&digest) == 0); - if (is_cache_miss) { - // we can't know the digest until we do the C compiler invocation, so we - // need a tmp filename. - Buf *out_obj_path = buf_alloc(); - if ((err = get_tmp_filename(g, out_obj_path, final_o_basename))) { - fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err)); - exit(1); - } - - Termination term; - ZigList args = {}; - args.append(buf_ptr(self_exe_path)); - args.append("clang"); - - if (c_file->preprocessor_only_basename == nullptr) { - args.append("-c"); - } - - Buf *out_dep_path = g->disable_c_depfile ? nullptr : buf_sprintf("%s.d", buf_ptr(out_obj_path)); - const char *out_dep_path_cstr = (out_dep_path == nullptr) ? nullptr : buf_ptr(out_dep_path); - FileExt ext = classify_file_ext(buf_ptr(c_source_basename), buf_len(c_source_basename)); - add_cc_args(g, args, out_dep_path_cstr, false, ext); - - args.append("-o"); - args.append(buf_ptr(out_obj_path)); - - args.append(buf_ptr(c_source_file)); - - for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) { - args.append(c_file->args.at(arg_i)); - } - - if (g->verbose_cc) { - print_zig_cc_cmd(&args); - } - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nThe following command failed:\n"); - print_zig_cc_cmd(&args); - exit(1); - } - - if (out_dep_path != nullptr) { - // add the files depended on to the cache system - if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) { - // Don't treat the absence of the .d file as a fatal error, the - // compiler may not produce one eg. when compiling .s files - if (err != ErrorFileNotFound) { - fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err)); - exit(1); - } - } - if (err != ErrorFileNotFound) { - os_delete_file(out_dep_path); - } - - if ((err = cache_final(cache_hash, &digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - } - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - if ((err = os_make_path(artifact_dir))) { - fprintf(stderr, "Unable to create output directory '%s': %s", - buf_ptr(artifact_dir), err_str(err)); - exit(1); - } - o_final_path = buf_alloc(); - os_path_join(artifact_dir, final_o_basename, o_final_path); - if ((err = os_rename(out_obj_path, o_final_path))) { - fprintf(stderr, "Unable to rename object: %s\n", err_str(err)); - exit(1); - } - } else { - // cache hit - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - o_final_path = buf_alloc(); - os_path_join(artifact_dir, final_o_basename, o_final_path); - } - - g->c_artifact_dir = artifact_dir; - g->link_objects.append(o_final_path); - g->caches_to_release.append(cache_hash); - - stage2_progress_end(child_prog_node); -} - -// returns true if we had any cache misses -static void gen_c_objects(CodeGen *g) { - Error err; - - if (g->c_source_files.length == 0) - return; - - Buf *self_exe_path = buf_alloc(); - if ((err = os_self_exe_path(self_exe_path))) { - fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err)); - exit(1); - } - - codegen_add_time_event(g, "Compile C Objects"); - const char *c_prog_name = "Compile C Objects"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, c_prog_name, strlen(c_prog_name), - g->c_source_files.length)); - - for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { - CFile *c_file = g->c_source_files.at(c_file_i); - gen_c_object(g, self_exe_path, c_file); - } -} - -void codegen_add_object(CodeGen *g, Buf *object_path) { - g->link_objects.append(object_path); -} - -// Must be coordinated with with CIntType enum -static const char *c_int_type_names[] = { - "short", - "unsigned short", - "int", - "unsigned int", - "long", - "unsigned long", - "long long", - "unsigned long long", -}; - -struct GenH { - ZigList types_to_declare; -}; - -static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_entry) { - if (type_entry->gen_h_loop_flag) - return; - type_entry->gen_h_loop_flag = true; - - switch (type_entry->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdBoundFn: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - zig_unreachable(); - case ZigTypeIdVoid: - case ZigTypeIdUnreachable: - return; - case ZigTypeIdBool: - g->c_want_stdbool = true; - return; - case ZigTypeIdInt: - g->c_want_stdint = true; - return; - case ZigTypeIdFloat: - return; - case ZigTypeIdOpaque: - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdStruct: - if(type_entry->data.structure.layout == ContainerLayoutExtern) { - for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - TypeStructField *field = type_entry->data.structure.fields[i]; - prepend_c_type_to_decl_list(g, gen_h, field->type_entry); - } - } - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdUnion: - for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { - TypeUnionField *field = &type_entry->data.unionation.fields[i]; - prepend_c_type_to_decl_list(g, gen_h, field->type_entry); - } - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdEnum: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.enumeration.tag_int_type); - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdPointer: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.pointer.child_type); - return; - case ZigTypeIdArray: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); - return; - case ZigTypeIdVector: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type); - return; - case ZigTypeIdOptional: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); - return; - case ZigTypeIdFn: - for (size_t i = 0; i < type_entry->data.fn.fn_type_id.param_count; i += 1) { - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.param_info[i].type); - } - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.return_type); - return; - } -} - -static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_buf) { - assert(type_entry); - - for (size_t i = 0; i < array_length(c_int_type_names); i += 1) { - if (type_entry == g->builtin_types.entry_c_int[i]) { - buf_init_from_str(out_buf, c_int_type_names[i]); - return; - } - } - if (type_entry == g->builtin_types.entry_c_longdouble) { - buf_init_from_str(out_buf, "long double"); - return; - } - if (type_entry == g->builtin_types.entry_c_void) { - buf_init_from_str(out_buf, "void"); - return; - } - if (type_entry == g->builtin_types.entry_isize) { - g->c_want_stdint = true; - buf_init_from_str(out_buf, "intptr_t"); - return; - } - if (type_entry == g->builtin_types.entry_usize) { - g->c_want_stdint = true; - buf_init_from_str(out_buf, "uintptr_t"); - return; - } - - prepend_c_type_to_decl_list(g, gen_h, type_entry); - - switch (type_entry->id) { - case ZigTypeIdVoid: - buf_init_from_str(out_buf, "void"); - break; - case ZigTypeIdBool: - buf_init_from_str(out_buf, "bool"); - break; - case ZigTypeIdUnreachable: - buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void"); - break; - case ZigTypeIdFloat: - switch (type_entry->data.floating.bit_count) { - case 32: - buf_init_from_str(out_buf, "float"); - break; - case 64: - buf_init_from_str(out_buf, "double"); - break; - case 80: - buf_init_from_str(out_buf, "__float80"); - break; - case 128: - buf_init_from_str(out_buf, "__float128"); - break; - default: - zig_unreachable(); - } - break; - case ZigTypeIdInt: - buf_resize(out_buf, 0); - buf_appendf(out_buf, "%sint%" PRIu32 "_t", - type_entry->data.integral.is_signed ? "" : "u", - type_entry->data.integral.bit_count); - break; - case ZigTypeIdPointer: - { - Buf child_buf = BUF_INIT; - ZigType *child_type = type_entry->data.pointer.child_type; - get_c_type(g, gen_h, child_type, &child_buf); - - const char *const_str = type_entry->data.pointer.is_const ? "const " : ""; - buf_resize(out_buf, 0); - buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf)); - break; - } - case ZigTypeIdOptional: - { - ZigType *child_type = type_entry->data.maybe.child_type; - if (!type_has_bits(g, child_type)) { - buf_init_from_str(out_buf, "bool"); - return; - } else if (type_is_nonnull_ptr(g, child_type)) { - return get_c_type(g, gen_h, child_type, out_buf); - } else { - zig_unreachable(); - } - } - case ZigTypeIdStruct: - case ZigTypeIdOpaque: - { - buf_init_from_str(out_buf, "struct "); - buf_append_buf(out_buf, type_h_name(type_entry)); - return; - } - case ZigTypeIdUnion: - { - buf_init_from_str(out_buf, "union "); - buf_append_buf(out_buf, type_h_name(type_entry)); - return; - } - case ZigTypeIdEnum: - { - buf_init_from_str(out_buf, "enum "); - buf_append_buf(out_buf, type_h_name(type_entry)); - return; - } - case ZigTypeIdArray: - { - ZigTypeArray *array_data = &type_entry->data.array; - - Buf *child_buf = buf_alloc(); - get_c_type(g, gen_h, array_data->child_type, child_buf); - - buf_resize(out_buf, 0); - buf_appendf(out_buf, "%s", buf_ptr(child_buf)); - return; - } - case ZigTypeIdVector: - zig_panic("TODO implement get_c_type for vector types"); - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdFn: - zig_panic("TODO implement get_c_type for more types"); - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdBoundFn: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - zig_unreachable(); - } -} - -static const char *preprocessor_alphabet1 = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static const char *preprocessor_alphabet2 = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -static bool need_to_preprocessor_mangle(Buf *src) { - for (size_t i = 0; i < buf_len(src); i += 1) { - const char *alphabet = (i == 0) ? preprocessor_alphabet1 : preprocessor_alphabet2; - uint8_t byte = buf_ptr(src)[i]; - if (strchr(alphabet, byte) == nullptr) { - return true; - } - } - return false; -} - -static Buf *preprocessor_mangle(Buf *src) { - if (!need_to_preprocessor_mangle(src)) { - return buf_create_from_buf(src); - } - Buf *result = buf_alloc(); - for (size_t i = 0; i < buf_len(src); i += 1) { - const char *alphabet = (i == 0) ? preprocessor_alphabet1 : preprocessor_alphabet2; - uint8_t byte = buf_ptr(src)[i]; - if (strchr(alphabet, byte) == nullptr) { - // perform escape - buf_appendf(result, "_%02x_", byte); - } else { - buf_append_char(result, byte); - } - } - return result; -} - -static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) { - for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) { - ZigType *type_entry = gen_h->types_to_declare.at(type_i); - switch (type_entry->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdArray: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdBoundFn: - case ZigTypeIdOptional: - case ZigTypeIdFn: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - zig_unreachable(); - - case ZigTypeIdEnum: - if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { - buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry))); - for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; - Buf *value_buf = buf_alloc(); - bigint_append_buf(value_buf, &enum_field->value, 10); - buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); - if (field_i != type_entry->data.enumeration.src_field_count - 1) { - buf_appendf(out_buf, ","); - } - buf_appendf(out_buf, "\n"); - } - buf_appendf(out_buf, "};\n\n"); - } else { - buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry))); - } - break; - case ZigTypeIdStruct: - if (type_entry->data.structure.layout == ContainerLayoutExtern) { - buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry))); - for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { - TypeStructField *struct_field = type_entry->data.structure.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); - - if (struct_field->type_entry->id == ZigTypeIdArray) { - buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), - buf_ptr(struct_field->name), - struct_field->type_entry->data.array.len); - } else { - buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); - } - - } - buf_appendf(out_buf, "};\n\n"); - } else { - buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); - } - break; - case ZigTypeIdUnion: - if (type_entry->data.unionation.layout == ContainerLayoutExtern) { - buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry))); - for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { - TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, union_field->type_entry, type_name_buf); - buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); - } - buf_appendf(out_buf, "};\n\n"); - } else { - buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry))); - } - break; - case ZigTypeIdOpaque: - buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); - break; - } - } -} - -static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) { - for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { - ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i); - - if (fn_table_entry->export_list.length == 0) - continue; - - FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; - - Buf return_type_c = BUF_INIT; - get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); - - Buf *symbol_name; - if (fn_table_entry->export_list.length == 0) { - symbol_name = &fn_table_entry->symbol_name; - } else { - GlobalExport *fn_export = &fn_table_entry->export_list.items[0]; - symbol_name = &fn_export->name; - } - - if (export_macro != nullptr) { - buf_appendf(out_buf, "%s %s %s(", - buf_ptr(export_macro), - buf_ptr(&return_type_c), - buf_ptr(symbol_name)); - } else { - buf_appendf(out_buf, "%s %s(", - buf_ptr(&return_type_c), - buf_ptr(symbol_name)); - } - - Buf param_type_c = BUF_INIT; - if (fn_type_id->param_count > 0) { - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; - AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); - Buf *param_name = param_decl_node->data.param_decl.name; - - const char *comma_str = (param_i == 0) ? "" : ", "; - const char *restrict_str = param_info->is_noalias ? "restrict" : ""; - get_c_type(g, gen_h, param_info->type, ¶m_type_c); - - if (param_info->type->id == ZigTypeIdArray) { - // Arrays decay to pointers - buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } else { - buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } - } - buf_appendf(out_buf, ")"); - } else { - buf_appendf(out_buf, "void)"); - } - - buf_appendf(out_buf, ";\n"); - } -} - -static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) { - for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) { - ZigVar* var = g->global_vars.at(exp_var_i)->var; - if (var->export_list.length == 0) - continue; - - Buf var_type_c = BUF_INIT; - get_c_type(g, gen_h, var->var_type, &var_type_c); - - if (export_macro != nullptr) { - buf_appendf(h_buf, "extern %s %s %s;\n", - buf_ptr(export_macro), - buf_ptr(&var_type_c), - var->name); - } else { - buf_appendf(h_buf, "extern %s %s;\n", - buf_ptr(&var_type_c), - var->name); - } - } -} - -static void gen_h_file(CodeGen *g) { - GenH gen_h_data = {0}; - GenH *gen_h = &gen_h_data; - - assert(!g->is_test_build); - assert(!g->disable_gen_h); - - Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name)); - - FILE *out_h = fopen(buf_ptr(out_h_path), "wb"); - if (!out_h) - zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno)); - - Buf *export_macro = nullptr; - if (g->is_dynamic) { - export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); - buf_upcase(export_macro); - } - - Buf fns_buf = BUF_INIT; - buf_resize(&fns_buf, 0); - gen_h_file_functions(g, gen_h, &fns_buf, export_macro); - - Buf vars_buf = BUF_INIT; - buf_resize(&vars_buf, 0); - gen_h_file_variables(g, gen_h, &vars_buf, export_macro); - - // Types will be populated by exported functions and variables so it has to run last. - Buf types_buf = BUF_INIT; - buf_resize(&types_buf, 0); - gen_h_file_types(g, gen_h, &types_buf); - - Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); - buf_upcase(ifdef_dance_name); - - fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); - fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); - - if (g->c_want_stdbool) - fprintf(out_h, "#include \n"); - if (g->c_want_stdint) - fprintf(out_h, "#include \n"); - - fprintf(out_h, "\n"); - - if (g->is_dynamic) { - fprintf(out_h, "#if defined(_WIN32)\n"); - fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro)); - fprintf(out_h, "#else\n"); - fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n", - buf_ptr(export_macro)); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - } - - fprintf(out_h, "#ifdef __cplusplus\n"); - fprintf(out_h, "extern \"C\" {\n"); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - - fprintf(out_h, "%s", buf_ptr(&types_buf)); - fprintf(out_h, "%s\n", buf_ptr(&fns_buf)); - fprintf(out_h, "%s\n", buf_ptr(&vars_buf)); - - fprintf(out_h, "#ifdef __cplusplus\n"); - fprintf(out_h, "} // extern \"C\"\n"); - fprintf(out_h, "#endif\n\n"); - - fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name)); - - if (fclose(out_h)) - zig_panic("unable to close h file: %s", strerror(errno)); -} - void codegen_print_timing_report(CodeGen *g, FILE *f) { double start_time = g->timing_events.at(0).time; double end_time = g->timing_events.last().time; @@ -10733,193 +9234,20 @@ void codegen_add_time_event(CodeGen *g, const char *name) { g->timing_events.append({seconds, name}); } -static void add_cache_pkg(CodeGen *g, CacheHash *ch, ZigPackage *pkg) { - if (buf_len(&pkg->root_src_path) == 0) - return; - pkg->added_to_cache = true; +void codegen_build_object(CodeGen *g) { + g->have_err_ret_tracing = detect_err_ret_tracing(g); - Buf *rel_full_path = buf_alloc(); - os_path_join(&pkg->root_src_dir, &pkg->root_src_path, rel_full_path); - cache_file(ch, rel_full_path); + init(g); - auto it = pkg->package_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; + codegen_add_time_event(g, "Semantic Analysis"); + const char *progress_name = "Semantic Analysis"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); - if (!pkg->added_to_cache) { - cache_buf(ch, entry->key); - add_cache_pkg(g, ch, entry->value); - } - } -} + gen_root_source(g); -// Called before init() -// is_cache_hit takes into account gen_c_objects -static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { - Error err; - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) - return err; - - CacheHash *ch = &g->cache_hash; - cache_init(ch, manifest_dir); - - add_cache_pkg(g, ch, g->main_pkg); - if (g->linker_script != nullptr) { - cache_file(ch, buf_create_from_str(g->linker_script)); - } - cache_buf(ch, compiler_id); - cache_buf(ch, g->root_out_name); - cache_buf(ch, g->zig_lib_dir); - cache_buf(ch, g->zig_std_dir); - cache_list_of_link_lib(ch, g->link_libs_list.items, g->link_libs_list.length); - cache_list_of_buf(ch, g->darwin_frameworks.items, g->darwin_frameworks.length); - cache_list_of_buf(ch, g->rpath_list.items, g->rpath_list.length); - cache_list_of_buf(ch, g->forbidden_libs.items, g->forbidden_libs.length); - cache_int(ch, g->build_mode); - cache_int(ch, g->out_type); - cache_bool(ch, g->zig_target->is_native_os); - cache_bool(ch, g->zig_target->is_native_cpu); - cache_int(ch, g->zig_target->arch); - cache_int(ch, g->zig_target->vendor); - cache_int(ch, g->zig_target->os); - cache_int(ch, g->zig_target->abi); - if (g->zig_target->cache_hash != nullptr) { - cache_mem(ch, g->zig_target->cache_hash, g->zig_target->cache_hash_len); - } - if (g->zig_target->glibc_or_darwin_version != nullptr) { - cache_int(ch, g->zig_target->glibc_or_darwin_version->major); - cache_int(ch, g->zig_target->glibc_or_darwin_version->minor); - cache_int(ch, g->zig_target->glibc_or_darwin_version->patch); - } - if (g->zig_target->dynamic_linker != nullptr) { - cache_str(ch, g->zig_target->dynamic_linker); - } - cache_int(ch, detect_subsystem(g)); - cache_bool(ch, g->strip_debug_symbols); - cache_bool(ch, g->is_test_build); - if (g->is_test_build) { - cache_buf_opt(ch, g->test_filter); - cache_buf_opt(ch, g->test_name_prefix); - cache_bool(ch, g->test_is_evented); - } - cache_bool(ch, g->link_eh_frame_hdr); - cache_bool(ch, g->is_single_threaded); - cache_bool(ch, g->linker_rdynamic); - cache_bool(ch, g->each_lib_rpath); - cache_bool(ch, g->disable_gen_h); - cache_bool(ch, g->bundle_compiler_rt); - cache_bool(ch, want_valgrind_support(g)); - cache_bool(ch, g->have_pic); - cache_bool(ch, g->have_dynamic_link); - cache_bool(ch, g->have_stack_probing); - cache_bool(ch, g->have_sanitize_c); - cache_bool(ch, g->is_dummy_so); - cache_bool(ch, g->function_sections); - cache_bool(ch, g->enable_dump_analysis); - cache_bool(ch, g->enable_doc_generation); - cache_bool(ch, g->emit_bin); - cache_bool(ch, g->emit_llvm_ir); - cache_bool(ch, g->emit_asm); - cache_bool(ch, g->is_versioned); - cache_usize(ch, g->version_major); - cache_usize(ch, g->version_minor); - cache_usize(ch, g->version_patch); - cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len); - cache_list_of_str(ch, g->clang_argv, g->clang_argv_len); - cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); - cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length); - if (g->libc) { - cache_slice(ch, Slice{g->libc->include_dir, g->libc->include_dir_len}); - cache_slice(ch, Slice{g->libc->sys_include_dir, g->libc->sys_include_dir_len}); - cache_slice(ch, Slice{g->libc->crt_dir, g->libc->crt_dir_len}); - cache_slice(ch, Slice{g->libc->msvc_lib_dir, g->libc->msvc_lib_dir_len}); - cache_slice(ch, Slice{g->libc->kernel32_lib_dir, g->libc->kernel32_lib_dir_len}); - } - cache_buf_opt(ch, g->version_script_path); - cache_buf_opt(ch, g->override_soname); - cache_buf_opt(ch, g->linker_optimization); - cache_int(ch, g->linker_gc_sections); - cache_int(ch, g->linker_allow_shlib_undefined); - cache_int(ch, g->linker_bind_global_refs_locally); - cache_bool(ch, g->linker_z_nodelete); - cache_bool(ch, g->linker_z_defs); - cache_usize(ch, g->stack_size_override); - - // gen_c_objects appends objects to g->link_objects which we want to include in the hash - gen_c_objects(g); - cache_list_of_file(ch, g->link_objects.items, g->link_objects.length); - - buf_resize(digest, 0); - if ((err = cache_hit(ch, digest))) { - if (err != ErrorInvalidFormat) - return err; - } - - if (ch->manifest_file_path != nullptr) { - g->caches_to_release.append(ch); - } - - return ErrorNone; -} - -static void resolve_out_paths(CodeGen *g) { - assert(g->output_dir != nullptr); - assert(g->root_out_name != nullptr); - - if (g->emit_bin) { - Buf *out_basename = buf_create_from_buf(g->root_out_name); - Buf *o_basename = buf_create_from_buf(g->root_out_name); - switch (g->out_type) { - case OutTypeUnknown: - zig_unreachable(); - case OutTypeObj: - if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache && - buf_eql_buf(o_basename, out_basename)) - { - // make it not collide with main output object - buf_append_str(o_basename, ".root"); - } - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_append_str(out_basename, target_o_file_ext(g->zig_target)); - break; - case OutTypeExe: - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_append_str(out_basename, target_exe_file_ext(g->zig_target)); - break; - case OutTypeLib: - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_resize(out_basename, 0); - buf_append_str(out_basename, target_lib_file_prefix(g->zig_target)); - buf_append_buf(out_basename, g->root_out_name); - buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, g->is_versioned, - g->version_major, g->version_minor, g->version_patch)); - break; - } - os_path_join(g->output_dir, o_basename, &g->o_file_output_path); - os_path_join(g->output_dir, out_basename, &g->bin_file_output_path); - } - if (g->emit_asm) { - Buf *asm_basename = buf_create_from_buf(g->root_out_name); - const char *asm_ext = target_asm_file_ext(g->zig_target); - buf_append_str(asm_basename, asm_ext); - os_path_join(g->output_dir, asm_basename, &g->asm_file_output_path); - } - if (g->emit_llvm_ir) { - Buf *llvm_ir_basename = buf_create_from_buf(g->root_out_name); - const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target); - buf_append_str(llvm_ir_basename, llvm_ir_ext); - os_path_join(g->output_dir, llvm_ir_basename, &g->llvm_ir_file_output_path); - } -} - -static void output_type_information(CodeGen *g) { - if (g->enable_dump_analysis) { - const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", - buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); + if (buf_len(&g->analysis_json_output_path) != 0) { + const char *analysis_json_filename = buf_ptr(&g->analysis_json_output_path); FILE *f = fopen(analysis_json_filename, "wb"); if (f == nullptr) { fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); @@ -10931,9 +9259,9 @@ static void output_type_information(CodeGen *g) { exit(1); } } - if (g->enable_doc_generation) { + if (buf_len(&g->docs_output_path) != 0) { Error err; - Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "docs", buf_ptr(g->output_dir)); + Buf *doc_dir_path = &g->docs_output_path; if ((err = os_make_path(doc_dir_path))) { fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); exit(1); @@ -10969,146 +9297,27 @@ static void output_type_information(CodeGen *g) { exit(1); } } -} -static void init_output_dir(CodeGen *g, Buf *digest) { - if (main_output_dir_is_just_one_c_object_post(g)) { - g->output_dir = buf_alloc(); - os_path_dirname(g->link_objects.at(0), g->output_dir); - } else { - g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(digest)); - } -} - -void codegen_build_and_link(CodeGen *g) { - Error err; - assert(g->out_type != OutTypeUnknown); - - if (!g->enable_cache) { - if (g->output_dir == nullptr) { - g->output_dir = buf_create_from_str("."); - } else if ((err = os_make_path(g->output_dir))) { - fprintf(stderr, "Unable to create output directory: %s\n", err_str(err)); - exit(1); - } + codegen_add_time_event(g, "Code Generation"); + { + const char *progress_name = "Code Generation"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); } - g->have_dynamic_link = detect_dynamic_link(g); - g->have_pic = detect_pic(g); - g->is_single_threaded = detect_single_threaded(g); - g->have_err_ret_tracing = detect_err_ret_tracing(g); - g->have_sanitize_c = detect_sanitize_c(g); - detect_libc(g); - - Buf digest = BUF_INIT; - if (g->enable_cache) { - Buf *manifest_dir = buf_alloc(); - os_path_join(g->cache_dir, buf_create_from_str(CACHE_HASH_SUBDIR), manifest_dir); - - if ((err = check_cache(g, manifest_dir, &digest))) { - if (err == ErrorCacheUnavailable) { - // message already printed - } else if (err == ErrorNotDir) { - fprintf(stderr, "Unable to check cache: %s is not a directory\n", - buf_ptr(manifest_dir)); - } else { - fprintf(stderr, "Unable to check cache: %s: %s\n", buf_ptr(manifest_dir), err_str(err)); - } - exit(1); - } - } else { - // There is a call to this in check_cache - gen_c_objects(g); + do_code_gen(g); + codegen_add_time_event(g, "LLVM Emit Output"); + { + const char *progress_name = "LLVM Emit Output"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); } + zig_llvm_emit_output(g); - if (g->enable_cache && buf_len(&digest) != 0) { - init_output_dir(g, &digest); - resolve_out_paths(g); - } else { - if (need_llvm_module(g)) { - init(g); - - codegen_add_time_event(g, "Semantic Analysis"); - const char *progress_name = "Semantic Analysis"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - - gen_root_source(g); - - } - if (g->enable_cache) { - if (buf_len(&digest) == 0) { - if ((err = cache_final(&g->cache_hash, &digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - } - init_output_dir(g, &digest); - - if ((err = os_make_path(g->output_dir))) { - fprintf(stderr, "Unable to create output directory: %s\n", err_str(err)); - exit(1); - } - } - resolve_out_paths(g); - - if (g->enable_dump_analysis || g->enable_doc_generation) { - output_type_information(g); - } - - if (need_llvm_module(g)) { - codegen_add_time_event(g, "Code Generation"); - { - const char *progress_name = "Code Generation"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - - do_code_gen(g); - codegen_add_time_event(g, "LLVM Emit Output"); - { - const char *progress_name = "LLVM Emit Output"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - zig_llvm_emit_output(g); - - if (!g->disable_gen_h && (g->out_type == OutTypeObj || g->out_type == OutTypeLib)) { - codegen_add_time_event(g, "Generate .h"); - { - const char *progress_name = "Generate .h"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - gen_h_file(g); - } - } - - // If we're outputting assembly or llvm IR we skip linking. - // If we're making a library or executable we must link. - // If there is more than one object, we have to link them (with -r). - // Finally, if we didn't make an object from zig source, and we don't have caching enabled, - // then we have an object from C source that we must copy to the output dir which we do with a -r link. - if (g->emit_bin && - (g->out_type != OutTypeObj || g->link_objects.length > 1 || - (!need_llvm_module(g) && !g->enable_cache))) - { - codegen_link(g); - } - } - - codegen_release_caches(g); codegen_add_time_event(g, "Done"); codegen_switch_sub_prog_node(g, nullptr); } -void codegen_release_caches(CodeGen *g) { - while (g->caches_to_release.length != 0) { - cache_release(g->caches_to_release.pop()); - } -} - ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, const char *pkg_path) { @@ -11125,75 +9334,31 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c return pkg; } -CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node) -{ - Stage2ProgressNode *child_progress_node = stage2_progress_start( - parent_progress_node ? parent_progress_node : parent_gen->sub_progress_node, - name, strlen(name), 0); - - CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_global_cache_dir(), false, child_progress_node); - child_gen->root_out_name = buf_create_from_str(name); - child_gen->disable_gen_h = true; - child_gen->want_stack_check = WantStackCheckDisabled; - child_gen->want_sanitize_c = WantCSanitizeDisabled; - child_gen->verbose_tokenize = parent_gen->verbose_tokenize; - child_gen->verbose_ast = parent_gen->verbose_ast; - child_gen->verbose_link = parent_gen->verbose_link; - child_gen->verbose_ir = parent_gen->verbose_ir; - child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir; - child_gen->verbose_cimport = parent_gen->verbose_cimport; - child_gen->verbose_cc = parent_gen->verbose_cc; - child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features; - child_gen->llvm_argv = parent_gen->llvm_argv; - - codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); - child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled; - child_gen->valgrind_support = ValgrindSupportDisabled; - - codegen_set_errmsg_color(child_gen, parent_gen->err_color); - - child_gen->enable_cache = true; - - return child_gen; +void codegen_destroy(CodeGen *g) { + if (g->pass1_arena != nullptr) { + g->pass1_arena->destruct(&heap::c_allocator); + g->pass1_arena = nullptr; + } + heap::c_allocator.destroy(g); } CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, - OutType out_type, BuildMode build_mode, Buf *override_lib_dir, - Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node) + BuildMode build_mode, Buf *override_lib_dir, + bool is_test_build) { CodeGen *g = heap::c_allocator.create(); - g->emit_bin = true; g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1"); - g->main_progress_node = progress_node; - - codegen_add_time_event(g, "Initialize"); - { - const char *progress_name = "Initialize"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } g->subsystem = TargetSubsystemAuto; - g->libc = libc; g->zig_target = target; - g->cache_dir = cache_dir; - if (override_lib_dir == nullptr) { - g->zig_lib_dir = get_zig_lib_dir(); - } else { - g->zig_lib_dir = override_lib_dir; - } + assert(override_lib_dir != nullptr); + g->zig_lib_dir = override_lib_dir; g->zig_std_dir = buf_alloc(); os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); - g->zig_c_headers_dir = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); - g->build_mode = build_mode; - g->out_type = out_type; g->import_table.init(32); g->builtin_fn_table.init(32); g->primitive_type_table.init(32); @@ -11236,7 +9401,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1); if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) { - fprintf(stderr, "Root source path '%s' outside main package path '%s'", + fprintf(stderr, "Root source path '%s' outside main package path '%s'\n", buf_ptr(root_src_path), buf_ptr(main_pkg_path)); exit(1); } @@ -11256,18 +9421,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget g->zig_std_special_dir = buf_alloc(); os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); - assert(target != nullptr); - if (!target->is_native_os) { - g->each_lib_rpath = false; - } else { - g->each_lib_rpath = true; - } - - if (target_os_requires_libc(g->zig_target->os)) { - g->libc_link_lib = create_link_lib(buf_create_from_str("c")); - g->link_libs_list.append(g->libc_link_lib); - } - target_triple_llvm(&g->llvm_triple_str, g->zig_target); g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8; @@ -11302,36 +9455,21 @@ void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node) { } ZigValue *CodeGen::Intern::for_undefined() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_undefined += 1; -#endif return &this->x_undefined; } ZigValue *CodeGen::Intern::for_void() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_void += 1; -#endif return &this->x_void; } ZigValue *CodeGen::Intern::for_null() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_null += 1; -#endif return &this->x_null; } ZigValue *CodeGen::Intern::for_unreachable() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_unreachable += 1; -#endif return &this->x_unreachable; } ZigValue *CodeGen::Intern::for_zero_byte() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.zero_byte += 1; -#endif return &this->zero_byte; } diff --git a/src/stage1/codegen.hpp b/src/stage1/codegen.hpp new file mode 100644 index 0000000000..33b2f74757 --- /dev/null +++ b/src/stage1/codegen.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_CODEGEN_HPP +#define ZIG_CODEGEN_HPP + +#include "parser.hpp" +#include "errmsg.hpp" +#include "target.hpp" +#include "stage2.h" + +#include + +CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, + BuildMode build_mode, Buf *zig_lib_dir, bool is_test_build); + +void codegen_build_object(CodeGen *g); +void codegen_destroy(CodeGen *); + +void codegen_add_time_event(CodeGen *g, const char *name); +void codegen_print_timing_report(CodeGen *g, FILE *f); + +ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, + const char *pkg_path); + +TargetSubsystem detect_subsystem(CodeGen *g); + +bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); +bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); + +ATTRIBUTE_NORETURN +void codegen_report_errors_and_exit(CodeGen *g); + +void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node); + +#endif diff --git a/src/config.h.in b/src/stage1/config.h.in similarity index 95% rename from src/config.h.in rename to src/stage1/config.h.in index 2ec6c25b38..8c147e7d65 100644 --- a/src/config.h.in +++ b/src/stage1/config.h.in @@ -22,6 +22,4 @@ #define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" -#cmakedefine ZIG_ENABLE_MEM_PROFILE - #endif diff --git a/src/dump_analysis.cpp b/src/stage1/dump_analysis.cpp similarity index 99% rename from src/dump_analysis.cpp rename to src/stage1/dump_analysis.cpp index 2f41341b14..df0d6f3ca2 100644 --- a/src/dump_analysis.cpp +++ b/src/stage1/dump_analysis.cpp @@ -6,11 +6,11 @@ */ #include "dump_analysis.hpp" -#include "compiler.hpp" #include "analyze.hpp" #include "config.h" #include "ir.hpp" #include "codegen.hpp" +#include "os.hpp" enum JsonWriterState { JsonWriterStateInvalid, @@ -1173,7 +1173,6 @@ static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) { } void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { - Error err; AnalDumpCtx ctx = {}; ctx.g = g; JsonWriter *jw = &ctx.jw; @@ -1199,15 +1198,6 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const jw_object_field(jw, "params"); jw_begin_object(jw); { - jw_object_field(jw, "zigId"); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); - exit(1); - } - jw_string(jw, buf_ptr(compiler_id)); - jw_object_field(jw, "zigVersion"); jw_string(jw, ZIG_VERSION_STRING); diff --git a/src/dump_analysis.hpp b/src/stage1/dump_analysis.hpp similarity index 100% rename from src/dump_analysis.hpp rename to src/stage1/dump_analysis.hpp diff --git a/src/stage1/empty.cpp b/src/stage1/empty.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/errmsg.cpp b/src/stage1/errmsg.cpp similarity index 100% rename from src/errmsg.cpp rename to src/stage1/errmsg.cpp diff --git a/src/errmsg.hpp b/src/stage1/errmsg.hpp similarity index 91% rename from src/errmsg.hpp rename to src/stage1/errmsg.hpp index e8b2f5872d..73cbd4e0d9 100644 --- a/src/errmsg.hpp +++ b/src/stage1/errmsg.hpp @@ -10,12 +10,7 @@ #include "buffer.hpp" #include "list.hpp" - -enum ErrColor { - ErrColorAuto, - ErrColorOff, - ErrColorOn, -}; +#include "stage1.h" struct ErrorMsg { size_t line_start; diff --git a/src/error.cpp b/src/stage1/error.cpp similarity index 100% rename from src/error.cpp rename to src/stage1/error.cpp diff --git a/src/error.hpp b/src/stage1/error.hpp similarity index 100% rename from src/error.hpp rename to src/stage1/error.hpp diff --git a/src/hash_map.hpp b/src/stage1/hash_map.hpp similarity index 100% rename from src/hash_map.hpp rename to src/stage1/hash_map.hpp diff --git a/src/heap.cpp b/src/stage1/heap.cpp similarity index 85% rename from src/heap.cpp rename to src/stage1/heap.cpp index 79c44d13dc..7e7a171bde 100644 --- a/src/heap.cpp +++ b/src/stage1/heap.cpp @@ -10,7 +10,6 @@ #include "config.h" #include "heap.hpp" -#include "mem_profile.hpp" namespace heap { @@ -48,21 +47,9 @@ void BootstrapAllocator::internal_deallocate(const mem::TypeInfo &info, void *pt mem::os::free(ptr); } -void CAllocator::init(const char *name) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile = bootstrap_allocator.create(); - this->profile->init(name, "CAllocator"); -#endif -} +void CAllocator::init(const char *name) { } -void CAllocator::deinit() { -#ifdef ZIG_ENABLE_MEM_PROFILE - assert(this->profile); - this->profile->deinit(); - bootstrap_allocator.destroy(this->profile); - this->profile = nullptr; -#endif -} +void CAllocator::deinit() { } CAllocator *CAllocator::construct(mem::Allocator *allocator, const char *name) { auto p = new(allocator->create()) CAllocator(); @@ -75,23 +62,11 @@ void CAllocator::destruct(mem::Allocator *allocator) { allocator->destroy(this); } -#ifdef ZIG_ENABLE_MEM_PROFILE -void CAllocator::print_report(FILE *file) { - this->profile->print_report(file); -} -#endif - void *CAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif return mem::os::calloc(count, info.size); } void *CAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif return mem::os::malloc(count * info.size); } @@ -103,17 +78,10 @@ void *CAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, } void *CAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, old_count); - this->profile->record_alloc(info, new_count); -#endif return mem::os::realloc(old_ptr, new_count * info.size); } void CAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, count); -#endif mem::os::free(ptr); } @@ -249,10 +217,6 @@ void ArenaAllocator::Impl::track_object(Object object) { } void ArenaAllocator::init(Allocator *backing, const char *name) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile = bootstrap_allocator.create(); - this->profile->init(name, "ArenaAllocator"); -#endif this->impl = bootstrap_allocator.create(); { auto &r = *this->impl; @@ -309,13 +273,6 @@ void ArenaAllocator::deinit() { t = prev; } } - -#ifdef ZIG_ENABLE_MEM_PROFILE - assert(this->profile); - this->profile->deinit(); - bootstrap_allocator.destroy(this->profile); - this->profile = nullptr; -#endif } ArenaAllocator *ArenaAllocator::construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name) { @@ -329,23 +286,11 @@ void ArenaAllocator::destruct(mem::Allocator *allocator) { allocator->destroy(this); } -#ifdef ZIG_ENABLE_MEM_PROFILE -void ArenaAllocator::print_report(FILE *file) { - this->profile->print_report(file); -} -#endif - void *ArenaAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif return this->impl->allocate(info, count); } void *ArenaAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif return this->impl->allocate(info, count); } @@ -354,17 +299,10 @@ void *ArenaAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_p } void *ArenaAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, old_count); - this->profile->record_alloc(info, new_count); -#endif return this->impl->reallocate(info, old_ptr, old_count, new_count); } void ArenaAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, count); -#endif // noop } diff --git a/src/heap.hpp b/src/stage1/heap.hpp similarity index 89% rename from src/heap.hpp rename to src/stage1/heap.hpp index e22ec42967..ec5c81026d 100644 --- a/src/heap.hpp +++ b/src/stage1/heap.hpp @@ -12,12 +12,6 @@ #include "util_base.hpp" #include "mem.hpp" -#ifdef ZIG_ENABLE_MEM_PROFILE -namespace mem { - struct Profile; -} -#endif - namespace heap { struct BootstrapAllocator final : mem::Allocator { @@ -40,9 +34,6 @@ struct CAllocator final : mem::Allocator { static CAllocator *construct(mem::Allocator *allocator, const char *name); void destruct(mem::Allocator *allocator) final; -#ifdef ZIG_ENABLE_MEM_PROFILE - void print_report(FILE *file = nullptr); -#endif private: ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; @@ -51,9 +42,6 @@ private: void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::Profile *profile; -#endif }; // @@ -71,9 +59,6 @@ struct ArenaAllocator final : mem::Allocator { static ArenaAllocator *construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name); void destruct(mem::Allocator *allocator) final; -#ifdef ZIG_ENABLE_MEM_PROFILE - void print_report(FILE *file = nullptr); -#endif private: ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; @@ -82,10 +67,6 @@ private: void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::Profile *profile; -#endif - struct Impl; Impl *impl; }; diff --git a/src/ir.cpp b/src/stage1/ir.cpp similarity index 99% rename from src/ir.cpp rename to src/stage1/ir.cpp index d8d7289dae..bb4ca8dbf3 100644 --- a/src/ir.cpp +++ b/src/stage1/ir.cpp @@ -22596,38 +22596,12 @@ static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name } static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { - bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name)); - if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) { - ir_add_error_node(ira, source_node, - buf_sprintf("dependency on library c must be explicitly specified in the build command")); + const char *msg = stage2_add_link_lib(&ira->codegen->stage1, buf_ptr(lib_name), buf_len(lib_name), + buf_ptr(symbol_name), buf_len(symbol_name)); + if (msg != nullptr) { + ir_add_error_node(ira, source_node, buf_create_from_str(msg)); ira->codegen->reported_bad_link_libc_error = true; } - - LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); - for (size_t i = 0; i < link_lib->symbols.length; i += 1) { - Buf *existing_symbol_name = link_lib->symbols.at(i); - if (buf_eql_buf(existing_symbol_name, symbol_name)) { - return; - } - } - - if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code", - buf_ptr(lib_name))); - add_error_note(ira->codegen, msg, source_node, - buf_sprintf("fixed by `--library %s` or `-fPIC`", buf_ptr(lib_name))); - ira->codegen->reported_bad_link_libc_error = true; - } - - for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) { - Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i); - if (buf_eql_buf(lib_name, forbidden_lib_name)) { - ir_add_error_node(ira, source_node, - buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name))); - } - } - link_lib->symbols.append(symbol_name); } static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst* source_instr) { @@ -26381,13 +26355,6 @@ static IrInstGen *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstSrcType return result; } -static void ir_cimport_cache_paths(Buf *cache_dir, Buf *tmp_c_file_digest, Buf *out_zig_dir, Buf *out_zig_path) { - buf_resize(out_zig_dir, 0); - buf_resize(out_zig_path, 0); - buf_appendf(out_zig_dir, "%s" OS_SEP "o" OS_SEP "%s", - buf_ptr(cache_dir), buf_ptr(tmp_c_file_digest)); - buf_appendf(out_zig_path, "%s" OS_SEP "cimport.zig", buf_ptr(out_zig_dir)); -} static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImport *instruction) { Error err; AstNode *node = instruction->base.base.source_node; @@ -26419,145 +26386,41 @@ static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImpo cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name); - CacheHash *cache_hash; - if ((err = create_c_object_cache(ira->codegen, &cache_hash, false))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to create cache: %s", err_str(err))); + const char *out_zig_path_ptr; + size_t out_zig_path_len; + Stage2ErrorMsg *errors_ptr; + size_t errors_len; + if ((err = stage2_cimport(&ira->codegen->stage1, + buf_ptr(&cimport_scope->buf), buf_len(&cimport_scope->buf), + &out_zig_path_ptr, &out_zig_path_len, + &errors_ptr, &errors_len))) + { + if (err != ErrorCCompileErrors) { + ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); + return ira->codegen->invalid_inst_gen; + } + + ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); + if (!ira->codegen->stage1.link_libc) { + add_error_note(ira->codegen, parent_err_msg, node, + buf_sprintf("libc headers not available; compilation does not link against libc")); + } + for (size_t i = 0; i < errors_len; i += 1) { + Stage2ErrorMsg *clang_err = &errors_ptr[i]; + // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null + if (clang_err->source && clang_err->filename_ptr) { + ErrorMsg *err_msg = err_msg_create_with_offset( + clang_err->filename_ptr ? + buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), + clang_err->line, clang_err->column, clang_err->offset, clang_err->source, + buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); + err_msg_add_note(parent_err_msg, err_msg); + } + } + return ira->codegen->invalid_inst_gen; } - cache_buf(cache_hash, &cimport_scope->buf); - - // Set this because we're not adding any files before checking for a hit. - cache_hash->force_check_manifest = true; - - Buf tmp_c_file_digest = BUF_INIT; - buf_resize(&tmp_c_file_digest, 0); - if ((err = cache_hit(cache_hash, &tmp_c_file_digest))) { - if (err != ErrorInvalidFormat) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to check cache: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - } - ira->codegen->caches_to_release.append(cache_hash); - - Buf *out_zig_dir = buf_alloc(); - Buf *out_zig_path = buf_alloc(); - if (buf_len(&tmp_c_file_digest) == 0 || cache_hash->files.length == 0) { - // Cache Miss - Buf *tmp_c_file_dir = buf_sprintf("%s" OS_SEP "o" OS_SEP "%s", - buf_ptr(ira->codegen->cache_dir), buf_ptr(&cache_hash->b64_digest)); - Buf *resolve_paths[] = { - tmp_c_file_dir, - buf_create_from_str("cimport.h"), - }; - Buf tmp_c_file_path = os_path_resolve(resolve_paths, 2); - - if ((err = os_make_path(tmp_c_file_dir))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make dir: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - - if ((err = os_write_file(&tmp_c_file_path, &cimport_scope->buf))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write .h file: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path)); - } - - Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path)); - - ZigList clang_argv = {0}; - - add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true, FileExtC); - - clang_argv.append(buf_ptr(&tmp_c_file_path)); - - if (ira->codegen->verbose_cc) { - fprintf(stderr, "clang"); - for (size_t i = 0; i < clang_argv.length; i += 1) { - fprintf(stderr, " %s", clang_argv.at(i)); - } - fprintf(stderr, "\n"); - } - - clang_argv.append(nullptr); // to make the [start...end] argument work - - Stage2ErrorMsg *errors_ptr; - size_t errors_len; - Stage2Ast *ast; - - const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir); - - if ((err = stage2_translate_c(&ast, &errors_ptr, &errors_len, - &clang_argv.at(0), &clang_argv.last(), resources_path))) - { - if (err != ErrorCCompileErrors) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - - ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); - if (ira->codegen->libc_link_lib == nullptr) { - add_error_note(ira->codegen, parent_err_msg, node, - buf_sprintf("libc headers not available; compilation does not link against libc")); - } - for (size_t i = 0; i < errors_len; i += 1) { - Stage2ErrorMsg *clang_err = &errors_ptr[i]; - // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null - if (clang_err->source && clang_err->filename_ptr) { - ErrorMsg *err_msg = err_msg_create_with_offset( - clang_err->filename_ptr ? - buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), - clang_err->line, clang_err->column, clang_err->offset, clang_err->source, - buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); - err_msg_add_note(parent_err_msg, err_msg); - } - } - - return ira->codegen->invalid_inst_gen; - } - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport .d file: %s\n", buf_ptr(tmp_dep_file)); - } - - if ((err = cache_add_dep_file(cache_hash, tmp_dep_file, false))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to parse .d file: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - if ((err = cache_final(cache_hash, &tmp_c_file_digest))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to finalize cache: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - - ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path); - if ((err = os_make_path(out_zig_dir))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make output dir: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - FILE *out_file = fopen(buf_ptr(out_zig_path), "wb"); - if (out_file == nullptr) { - ir_add_error_node(ira, node, - buf_sprintf("C import failed: unable to open output file: %s", strerror(errno))); - return ira->codegen->invalid_inst_gen; - } - stage2_render_ast(ast, out_file); - if (fclose(out_file) != 0) { - ir_add_error_node(ira, node, - buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno))); - return ira->codegen->invalid_inst_gen; - } - - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport output: %s\n", buf_ptr(out_zig_path)); - } - - } else { - // Cache Hit - ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path); - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport cache hit: %s\n", buf_ptr(out_zig_path)); - } - } + Buf *out_zig_path = buf_create_from_mem(out_zig_path_ptr, out_zig_path_len); Buf *import_code = buf_alloc(); if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) { diff --git a/src/ir.hpp b/src/stage1/ir.hpp similarity index 100% rename from src/ir.hpp rename to src/stage1/ir.hpp diff --git a/src/ir_print.cpp b/src/stage1/ir_print.cpp similarity index 100% rename from src/ir_print.cpp rename to src/stage1/ir_print.cpp diff --git a/src/ir_print.hpp b/src/stage1/ir_print.hpp similarity index 100% rename from src/ir_print.hpp rename to src/stage1/ir_print.hpp diff --git a/src/list.hpp b/src/stage1/list.hpp similarity index 100% rename from src/list.hpp rename to src/stage1/list.hpp diff --git a/src/mem.cpp b/src/stage1/mem.cpp similarity index 63% rename from src/mem.cpp rename to src/stage1/mem.cpp index 51ee9a27ee..48dbd791de 100644 --- a/src/mem.cpp +++ b/src/stage1/mem.cpp @@ -7,7 +7,6 @@ #include "config.h" #include "mem.hpp" -#include "mem_profile.hpp" #include "heap.hpp" namespace mem { @@ -22,16 +21,4 @@ void deinit() { heap::bootstrap_allocator_state.deinit(); } -#ifdef ZIG_ENABLE_MEM_PROFILE -void print_report(FILE *file) { - heap::c_allocator_state.print_report(file); - intern_counters.print_report(file); -} -#endif - -#ifdef ZIG_ENABLE_MEM_PROFILE -bool report_print = false; -FILE *report_file{nullptr}; -#endif - } // namespace mem diff --git a/src/mem.hpp b/src/stage1/mem.hpp similarity index 95% rename from src/mem.hpp rename to src/stage1/mem.hpp index 9e262b7d53..3008febbde 100644 --- a/src/mem.hpp +++ b/src/stage1/mem.hpp @@ -135,15 +135,6 @@ protected: virtual void internal_deallocate(const TypeInfo &info, void *ptr, size_t count) = 0; }; -#ifdef ZIG_ENABLE_MEM_PROFILE -void print_report(FILE *file = nullptr); - -// global memory report flag -extern bool report_print; -// global memory report default destination -extern FILE *report_file; -#endif - } // namespace mem #endif diff --git a/src/mem_hash_map.hpp b/src/stage1/mem_hash_map.hpp similarity index 100% rename from src/mem_hash_map.hpp rename to src/stage1/mem_hash_map.hpp diff --git a/src/mem_list.hpp b/src/stage1/mem_list.hpp similarity index 100% rename from src/mem_list.hpp rename to src/stage1/mem_list.hpp diff --git a/src/stage1/mem_type_info.hpp b/src/stage1/mem_type_info.hpp new file mode 100644 index 0000000000..d8a7933268 --- /dev/null +++ b/src/stage1/mem_type_info.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_MEM_TYPE_INFO_HPP +#define ZIG_MEM_TYPE_INFO_HPP + +#include "config.h" + +namespace mem { + +struct TypeInfo { + size_t size; + size_t alignment; + + template + static constexpr TypeInfo make() { + return {sizeof(T), alignof(T)}; + } +}; + +} // namespace mem + +#endif diff --git a/src/os.cpp b/src/stage1/os.cpp similarity index 100% rename from src/os.cpp rename to src/stage1/os.cpp diff --git a/src/os.hpp b/src/stage1/os.hpp similarity index 100% rename from src/os.hpp rename to src/stage1/os.hpp diff --git a/src/parse_f128.c b/src/stage1/parse_f128.c similarity index 100% rename from src/parse_f128.c rename to src/stage1/parse_f128.c diff --git a/src/parse_f128.h b/src/stage1/parse_f128.h similarity index 100% rename from src/parse_f128.h rename to src/stage1/parse_f128.h diff --git a/src/parser.cpp b/src/stage1/parser.cpp similarity index 100% rename from src/parser.cpp rename to src/stage1/parser.cpp diff --git a/src/parser.hpp b/src/stage1/parser.hpp similarity index 100% rename from src/parser.hpp rename to src/stage1/parser.hpp diff --git a/src/range_set.cpp b/src/stage1/range_set.cpp similarity index 100% rename from src/range_set.cpp rename to src/stage1/range_set.cpp diff --git a/src/range_set.hpp b/src/stage1/range_set.hpp similarity index 100% rename from src/range_set.hpp rename to src/stage1/range_set.hpp diff --git a/src/softfloat.hpp b/src/stage1/softfloat.hpp similarity index 100% rename from src/softfloat.hpp rename to src/stage1/softfloat.hpp diff --git a/src/softfloat_ext.cpp b/src/stage1/softfloat_ext.cpp similarity index 100% rename from src/softfloat_ext.cpp rename to src/stage1/softfloat_ext.cpp diff --git a/src/softfloat_ext.hpp b/src/stage1/softfloat_ext.hpp similarity index 100% rename from src/softfloat_ext.hpp rename to src/stage1/softfloat_ext.hpp diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp new file mode 100644 index 0000000000..1034f9ff88 --- /dev/null +++ b/src/stage1/stage1.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "stage1.h" +#include "os.hpp" +#include "all_types.hpp" +#include "codegen.hpp" + +void zig_stage1_os_init(void) { + os_init(); + mem::init(); + init_all_targets(); +} + +struct ZigStage1 *zig_stage1_create(BuildMode optimize_mode, + const char *main_pkg_path_ptr, size_t main_pkg_path_len, + const char *root_src_path_ptr, size_t root_src_path_len, + const char *zig_lib_dir_ptr, size_t zig_lib_dir_len, + const ZigTarget *target, bool is_test_build) +{ + Buf *main_pkg_path = (main_pkg_path_len == 0) ? + nullptr : buf_create_from_mem(main_pkg_path_ptr, main_pkg_path_len); + Buf *root_src_path = buf_create_from_mem(root_src_path_ptr, root_src_path_len); + Buf *zig_lib_dir = buf_create_from_mem(zig_lib_dir_ptr, zig_lib_dir_len); + CodeGen *g = codegen_create(main_pkg_path, root_src_path, target, optimize_mode, + zig_lib_dir, is_test_build); + return &g->stage1; +} + +void zig_stage1_destroy(struct ZigStage1 *stage1) { + CodeGen *codegen = reinterpret_cast(stage1); + codegen_destroy(codegen); +} + +static void add_package(CodeGen *g, ZigStage1Pkg *stage1_pkg, ZigPackage *pkg) { + for (size_t i = 0; i < stage1_pkg->children_len; i += 1) { + ZigStage1Pkg *child_cli_pkg = stage1_pkg->children_ptr[i]; + + Buf *dirname = buf_alloc(); + Buf *basename = buf_alloc(); + os_path_split(buf_create_from_mem(child_cli_pkg->path_ptr, child_cli_pkg->path_len), dirname, basename); + + ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename), + buf_ptr(buf_sprintf("%s.%.*s", buf_ptr(&pkg->pkg_path), + (int)child_cli_pkg->name_len, child_cli_pkg->name_ptr))); + auto entry = pkg->package_table.put_unique( + buf_create_from_mem(child_cli_pkg->name_ptr, child_cli_pkg->name_len), + child_pkg); + if (entry) { + ZigPackage *existing_pkg = entry->value; + Buf *full_path = buf_alloc(); + os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path); + fprintf(stderr, "Unable to add package '%.*s'->'%.*s': already exists as '%s'\n", + (int)child_cli_pkg->name_len, child_cli_pkg->name_ptr, + (int)child_cli_pkg->path_len, child_cli_pkg->path_ptr, + buf_ptr(full_path)); + exit(EXIT_FAILURE); + } + + add_package(g, child_cli_pkg, child_pkg); + } +} + +void zig_stage1_build_object(struct ZigStage1 *stage1) { + CodeGen *g = reinterpret_cast(stage1); + + g->root_out_name = buf_create_from_mem(stage1->root_name_ptr, stage1->root_name_len); + buf_init_from_mem(&g->o_file_output_path, stage1->emit_o_ptr, stage1->emit_o_len); + buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len); + buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len); + buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len); + buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len); + buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len); + + if (stage1->builtin_zig_path_len != 0) { + g->builtin_zig_path = buf_create_from_mem(stage1->builtin_zig_path_ptr, stage1->builtin_zig_path_len); + } + if (stage1->test_filter_len != 0) { + g->test_filter = buf_create_from_mem(stage1->test_filter_ptr, stage1->test_filter_len); + } + if (stage1->test_name_prefix_len != 0) { + g->test_name_prefix = buf_create_from_mem(stage1->test_name_prefix_ptr, stage1->test_name_prefix_len); + } + + g->link_mode_dynamic = stage1->link_mode_dynamic; + g->dll_export_fns = stage1->dll_export_fns; + g->have_pic = stage1->pic; + g->have_stack_probing = stage1->enable_stack_probing; + g->is_single_threaded = stage1->is_single_threaded; + g->valgrind_enabled = stage1->valgrind_enabled; + g->link_libc = stage1->link_libc; + g->link_libcpp = stage1->link_libcpp; + g->function_sections = stage1->function_sections; + + g->subsystem = stage1->subsystem; + + g->enable_time_report = stage1->enable_time_report; + g->enable_stack_report = stage1->enable_stack_report; + g->test_is_evented = stage1->test_is_evented; + + g->verbose_tokenize = stage1->verbose_tokenize; + g->verbose_ast = stage1->verbose_ast; + g->verbose_ir = stage1->verbose_ir; + g->verbose_llvm_ir = stage1->verbose_llvm_ir; + g->verbose_cimport = stage1->verbose_cimport; + g->verbose_llvm_cpu_features = stage1->verbose_llvm_cpu_features; + + g->err_color = stage1->err_color; + g->code_model = stage1->code_model; + + { + g->strip_debug_symbols = stage1->strip; + if (!target_has_debug_info(g->zig_target)) { + g->strip_debug_symbols = true; + } + } + + g->main_progress_node = stage1->main_progress_node; + + add_package(g, stage1->root_pkg, g->main_pkg); + + codegen_build_object(g); +} diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h new file mode 100644 index 0000000000..412118a6fa --- /dev/null +++ b/src/stage1/stage1.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +// This file deals with exposing stage1 C++ code to stage2 Zig code. + +#ifndef ZIG_STAGE1_H +#define ZIG_STAGE1_H + +#include "zig_llvm.h" + +#include + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +// ABI warning +enum ErrColor { + ErrColorAuto, + ErrColorOff, + ErrColorOn, +}; + +// ABI warning +enum CodeModel { + CodeModelDefault, + CodeModelTiny, + CodeModelSmall, + CodeModelKernel, + CodeModelMedium, + CodeModelLarge, +}; + +// ABI warning +enum TargetSubsystem { + TargetSubsystemConsole, + TargetSubsystemWindows, + TargetSubsystemPosix, + TargetSubsystemNative, + TargetSubsystemEfiApplication, + TargetSubsystemEfiBootServiceDriver, + TargetSubsystemEfiRom, + TargetSubsystemEfiRuntimeDriver, + + // This means Zig should infer the subsystem. + // It's last so that the indexes of other items can line up + // with the enum in builtin.zig. + TargetSubsystemAuto +}; + + +// ABI warning +// Synchronize with target.cpp::os_list +enum Os { + OsFreestanding, + OsAnanas, + OsCloudABI, + OsDragonFly, + OsFreeBSD, + OsFuchsia, + OsIOS, + OsKFreeBSD, + OsLinux, + OsLv2, // PS3 + OsMacOSX, + OsNetBSD, + OsOpenBSD, + OsSolaris, + OsWindows, + OsHaiku, + OsMinix, + OsRTEMS, + OsNaCl, // Native Client + OsCNK, // BG/P Compute-Node Kernel + OsAIX, + OsCUDA, // NVIDIA CUDA + OsNVCL, // NVIDIA OpenCL + OsAMDHSA, // AMD HSA Runtime + OsPS4, + OsELFIAMCU, + OsTvOS, // Apple tvOS + OsWatchOS, // Apple watchOS + OsMesa3D, + OsContiki, + OsAMDPAL, + OsHermitCore, + OsHurd, + OsWASI, + OsEmscripten, + OsUefi, + OsOther, +}; + +// ABI warning +struct ZigTarget { + enum ZigLLVM_ArchType arch; + enum Os os; + enum ZigLLVM_EnvironmentType abi; + + bool is_native_os; + bool is_native_cpu; + + const char *llvm_cpu_name; + const char *llvm_cpu_features; +}; + +// ABI warning +struct Stage2Progress; +// ABI warning +struct Stage2ProgressNode; + +enum BuildMode { + BuildModeDebug, + BuildModeSafeRelease, + BuildModeFastRelease, + BuildModeSmallRelease, +}; + + +struct ZigStage1Pkg { + const char *name_ptr; + size_t name_len; + + const char *path_ptr; + size_t path_len; + + struct ZigStage1Pkg **children_ptr; + size_t children_len; + + struct ZigStage1Pkg *parent; +}; + +// This struct is used by both main.cpp and stage1.zig. +struct ZigStage1 { + const char *root_name_ptr; + size_t root_name_len; + + const char *emit_o_ptr; + size_t emit_o_len; + + const char *emit_h_ptr; + size_t emit_h_len; + + const char *emit_asm_ptr; + size_t emit_asm_len; + + const char *emit_llvm_ir_ptr; + size_t emit_llvm_ir_len; + + const char *emit_analysis_json_ptr; + size_t emit_analysis_json_len; + + const char *emit_docs_ptr; + size_t emit_docs_len; + + const char *builtin_zig_path_ptr; + size_t builtin_zig_path_len; + + const char *test_filter_ptr; + size_t test_filter_len; + + const char *test_name_prefix_ptr; + size_t test_name_prefix_len; + + void *userdata; + struct ZigStage1Pkg *root_pkg; + struct Stage2ProgressNode *main_progress_node; + + enum CodeModel code_model; + enum TargetSubsystem subsystem; + enum ErrColor err_color; + + bool pic; + bool link_libc; + bool link_libcpp; + bool strip; + bool is_single_threaded; + bool dll_export_fns; + bool link_mode_dynamic; + bool valgrind_enabled; + bool function_sections; + bool enable_stack_probing; + bool enable_time_report; + bool enable_stack_report; + bool test_is_evented; + bool verbose_tokenize; + bool verbose_ast; + bool verbose_ir; + bool verbose_llvm_ir; + bool verbose_cimport; + bool verbose_llvm_cpu_features; + + // Set by stage1 + bool have_c_main; + bool have_winmain; + bool have_wwinmain; + bool have_winmain_crt_startup; + bool have_wwinmain_crt_startup; + bool have_dllmain_crt_startup; +}; + +ZIG_EXTERN_C void zig_stage1_os_init(void); + +ZIG_EXTERN_C struct ZigStage1 *zig_stage1_create(enum BuildMode optimize_mode, + const char *main_pkg_path_ptr, size_t main_pkg_path_len, + const char *root_src_path_ptr, size_t root_src_path_len, + const char *zig_lib_dir_ptr, size_t zig_lib_dir_len, + const struct ZigTarget *target, bool is_test_build); + +ZIG_EXTERN_C void zig_stage1_build_object(struct ZigStage1 *); + +ZIG_EXTERN_C void zig_stage1_destroy(struct ZigStage1 *); + +#endif diff --git a/src/stage1/stage2.h b/src/stage1/stage2.h new file mode 100644 index 0000000000..886a4c2660 --- /dev/null +++ b/src/stage1/stage2.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +// This file deals with exposing stage2 Zig code to stage1 C++ code. + +#ifndef ZIG_STAGE2_H +#define ZIG_STAGE2_H + +#include +#include +#include + +#include "stage1.h" + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +#if defined(_MSC_VER) +#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#endif + +// ABI warning: the types and declarations in this file must match both those in +// stage2.cpp and src/stage1.zig. + +// ABI warning +enum Error { + ErrorNone, + ErrorNoMem, + ErrorInvalidFormat, + ErrorSemanticAnalyzeFail, + ErrorAccess, + ErrorInterrupted, + ErrorSystemResources, + ErrorFileNotFound, + ErrorFileSystem, + ErrorFileTooBig, + ErrorDivByZero, + ErrorOverflow, + ErrorPathAlreadyExists, + ErrorUnexpected, + ErrorExactDivRemainder, + ErrorNegativeDenominator, + ErrorShiftedOutOneBits, + ErrorCCompileErrors, + ErrorEndOfFile, + ErrorIsDir, + ErrorNotDir, + ErrorUnsupportedOperatingSystem, + ErrorSharingViolation, + ErrorPipeBusy, + ErrorPrimitiveTypeNotFound, + ErrorCacheUnavailable, + ErrorPathTooLong, + ErrorCCompilerCannotFindFile, + ErrorNoCCompilerInstalled, + ErrorReadingDepFile, + ErrorInvalidDepFile, + ErrorMissingArchitecture, + ErrorMissingOperatingSystem, + ErrorUnknownArchitecture, + ErrorUnknownOperatingSystem, + ErrorUnknownABI, + ErrorInvalidFilename, + ErrorDiskQuota, + ErrorDiskSpace, + ErrorUnexpectedWriteFailure, + ErrorUnexpectedSeekFailure, + ErrorUnexpectedFileTruncationFailure, + ErrorUnimplemented, + ErrorOperationAborted, + ErrorBrokenPipe, + ErrorNoSpaceLeft, + ErrorNotLazy, + ErrorIsAsync, + ErrorImportOutsidePkgPath, + ErrorUnknownCpu, + ErrorUnknownCpuFeature, + ErrorInvalidCpuFeatures, + ErrorInvalidLlvmCpuFeaturesFormat, + ErrorUnknownApplicationBinaryInterface, + ErrorASTUnitFailure, + ErrorBadPathName, + ErrorSymLinkLoop, + ErrorProcessFdQuotaExceeded, + ErrorSystemFdQuotaExceeded, + ErrorNoDevice, + ErrorDeviceBusy, + ErrorUnableToSpawnCCompiler, + ErrorCCompilerExitCode, + ErrorCCompilerCrashed, + ErrorCCompilerCannotFindHeaders, + ErrorLibCRuntimeNotFound, + ErrorLibCStdLibHeaderNotFound, + ErrorLibCKernel32LibNotFound, + ErrorUnsupportedArchitecture, + ErrorWindowsSdkNotFound, + ErrorUnknownDynamicLinkerPath, + ErrorTargetHasNoDynamicLinker, + ErrorInvalidAbiVersion, + ErrorInvalidOperatingSystemVersion, + ErrorUnknownClangOption, + ErrorNestedResponseFile, + ErrorZigIsTheCCompiler, + ErrorFileBusy, + ErrorLocked, +}; + +// ABI warning +struct Stage2ErrorMsg { + const char *filename_ptr; // can be null + size_t filename_len; + const char *msg_ptr; + size_t msg_len; + const char *source; // valid until the ASTUnit is freed. can be null + unsigned line; // 0 based + unsigned column; // 0 based + unsigned offset; // byte offset into source +}; + +// ABI warning +ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len); + +// ABI warning +ZIG_EXTERN_C struct Stage2Progress *stage2_progress_create(void); +// ABI warning +ZIG_EXTERN_C void stage2_progress_disable_tty(struct Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C void stage2_progress_destroy(struct Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C struct Stage2ProgressNode *stage2_progress_start_root(struct Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C struct Stage2ProgressNode *stage2_progress_start(struct Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C void stage2_progress_end(struct Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_complete_one(struct Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_update_node(struct Stage2ProgressNode *node, + size_t completed_count, size_t estimated_total_items); + +// ABI warning +struct Stage2SemVer { + uint32_t major; + uint32_t minor; + uint32_t patch; +}; + +// ABI warning +ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker); + +// ABI warning +ZIG_EXTERN_C const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len, + size_t *result_len); + +// ABI warning +ZIG_EXTERN_C Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len, + const char **out_zig_path_ptr, size_t *out_zig_path_len, + struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len); + +// ABI warning +ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1, + const char *lib_name_ptr, size_t lib_name_len, + const char *symbol_name_ptr, size_t symbol_name_len); + +#endif diff --git a/src/target.cpp b/src/stage1/target.cpp similarity index 88% rename from src/target.cpp rename to src/stage1/target.cpp index dff134a01d..433c988a01 100644 --- a/src/target.cpp +++ b/src/stage1/target.cpp @@ -10,8 +10,6 @@ #include "target.hpp" #include "util.hpp" #include "os.hpp" -#include "compiler.hpp" -#include "glibc.hpp" #include @@ -346,33 +344,6 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) { return ZigLLVMGetEnvironmentTypeName(abi); } -Error target_parse_glibc_version(Stage2SemVer *glibc_ver, const char *text) { - glibc_ver->major = 2; - glibc_ver->minor = 0; - glibc_ver->patch = 0; - SplitIterator it = memSplit(str(text), str("GLIBC_.")); - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return ErrorUnknownABI; - glibc_ver->major = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); - } - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return ErrorNone; - glibc_ver->minor = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); - } - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return ErrorNone; - glibc_ver->patch = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); - } - return ErrorNone; -} - -void target_init_default_glibc_version(ZigTarget *target) { - *target->glibc_or_darwin_version = {2, 17, 0}; -} - Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) { *out_arch = ZigLLVM_UnknownArch; for (size_t arch_i = 0; arch_i < array_length(arch_list); arch_i += 1) { @@ -409,10 +380,6 @@ Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, si return ErrorUnknownABI; } -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) { - return stage2_target_parse(target, triple, mcpu, dynamic_linker); -} - const char *target_arch_name(ZigLLVM_ArchType arch) { return ZigLLVMGetArchTypeName(arch); } @@ -437,7 +404,7 @@ void target_triple_llvm(Buf *triple, const ZigTarget *target) { buf_resize(triple, 0); buf_appendf(triple, "%s-%s-%s-%s", ZigLLVMGetArchTypeName(target->arch), - ZigLLVMGetVendorTypeName(target->vendor), + ZigLLVMGetVendorTypeName(ZigLLVM_UnknownVendor), ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)), ZigLLVMGetEnvironmentTypeName(target->abi)); } @@ -756,66 +723,6 @@ const char *target_llvm_ir_file_ext(const ZigTarget *target) { return ".ll"; } -const char *target_exe_file_ext(const ZigTarget *target) { - if (target->os == OsWindows) { - return ".exe"; - } else if (target->os == OsUefi) { - return ".efi"; - } else if (target_is_wasm(target)) { - return ".wasm"; - } else { - return ""; - } -} - -const char *target_lib_file_prefix(const ZigTarget *target) { - if ((target->os == OsWindows && !target_abi_is_gnu(target->abi)) || - target->os == OsUefi || - target_is_wasm(target)) - { - return ""; - } else { - return "lib"; - } -} - -const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned, - size_t version_major, size_t version_minor, size_t version_patch) -{ - if (target_is_wasm(target)) { - return ".wasm"; - } - if (target->os == OsWindows || target->os == OsUefi) { - if (is_static) { - if (target->os == OsWindows && target_abi_is_gnu(target->abi)) { - return ".a"; - } else { - return ".lib"; - } - } else { - return ".dll"; - } - } else { - if (is_static) { - return ".a"; - } else if (target_os_is_darwin(target->os)) { - if (is_versioned) { - return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", - version_major, version_minor, version_patch)); - } else { - return ".dylib"; - } - } else { - if (is_versioned) { - return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, - version_major, version_minor, version_patch)); - } else { - return ".so"; - } - } - } -} - bool target_is_android(const ZigTarget *target) { return target->abi == ZigLLVM_Android; } @@ -992,40 +899,6 @@ bool target_os_requires_libc(Os os) { return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD || os == OsDragonFly); } -bool target_supports_fpic(const ZigTarget *target) { - // This is not whether the target supports Position Independent Code, but whether the -fPIC - // C compiler argument is valid. - return target->os != OsWindows; -} - -bool target_supports_clang_march_native(const ZigTarget *target) { - // Whether clang supports -march=native on this target. - // Arguably it should always work, but in reality it gives: - // error: the clang compiler does not support '-march=native' - // If we move CPU detection logic into Zig itelf, we will not need this, - // instead we will always pass target features and CPU configuration explicitly. - return target->arch != ZigLLVM_aarch64 && - target->arch != ZigLLVM_aarch64_be; -} - -bool target_supports_stack_probing(const ZigTarget *target) { - return target->os != OsWindows && target->os != OsUefi && (target->arch == ZigLLVM_x86 || target->arch == ZigLLVM_x86_64); -} - -bool target_supports_sanitize_c(const ZigTarget *target) { - return true; -} - -bool target_requires_pic(const ZigTarget *target, bool linking_libc) { - // This function returns whether non-pic code is completely invalid on the given target. - return target_is_android(target) || target->os == OsWindows || target->os == OsUefi || target_os_requires_libc(target->os) || - (linking_libc && target_is_glibc(target)); -} - -bool target_requires_pie(const ZigTarget *target) { - return target_is_android(target); -} - bool target_is_glibc(const ZigTarget *target) { return target->os == OsLinux && target_abi_is_gnu(target->abi); } @@ -1038,10 +911,6 @@ bool target_is_wasm(const ZigTarget *target) { return target->arch == ZigLLVM_wasm32 || target->arch == ZigLLVM_wasm64; } -bool target_is_single_threaded(const ZigTarget *target) { - return target_is_wasm(target); -} - ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { if (arch == ZigLLVM_wasm32 || arch == ZigLLVM_wasm64) { return ZigLLVM_Musl; @@ -1276,7 +1145,6 @@ void target_libc_enum(size_t index, ZigTarget *out_target) { out_target->arch = libcs_available[index].arch; out_target->os = libcs_available[index].os; out_target->abi = libcs_available[index].abi; - out_target->vendor = ZigLLVM_UnknownVendor; out_target->is_native_os = false; out_target->is_native_cpu = false; } diff --git a/src/target.hpp b/src/stage1/target.hpp similarity index 71% rename from src/target.hpp rename to src/stage1/target.hpp index 5e44301fff..d34c2aeae5 100644 --- a/src/target.hpp +++ b/src/stage1/target.hpp @@ -12,22 +12,6 @@ struct Buf; -enum TargetSubsystem { - TargetSubsystemConsole, - TargetSubsystemWindows, - TargetSubsystemPosix, - TargetSubsystemNative, - TargetSubsystemEfiApplication, - TargetSubsystemEfiBootServiceDriver, - TargetSubsystemEfiRom, - TargetSubsystemEfiRuntimeDriver, - - // This means Zig should infer the subsystem. - // It's last so that the indexes of other items can line up - // with the enum in builtin.zig. - TargetSubsystemAuto -}; - enum CIntType { CIntTypeShort, CIntTypeUShort, @@ -41,14 +25,10 @@ enum CIntType { CIntTypeCount, }; -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker); Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len); Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); -Error target_parse_glibc_version(Stage2SemVer *out, const char *text); -void target_init_default_glibc_version(ZigTarget *target); - size_t target_arch_count(void); ZigLLVM_ArchType target_arch_enum(size_t index); const char *target_arch_name(ZigLLVM_ArchType arch); @@ -85,10 +65,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id); const char *target_o_file_ext(const ZigTarget *target); const char *target_asm_file_ext(const ZigTarget *target); const char *target_llvm_ir_file_ext(const ZigTarget *target); -const char *target_exe_file_ext(const ZigTarget *target); -const char *target_lib_file_prefix(const ZigTarget *target); -const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned, - size_t version_major, size_t version_minor, size_t version_patch); bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); ZigLLVM_OSType get_llvm_os_type(Os os_type); @@ -104,10 +80,6 @@ bool target_can_build_libc(const ZigTarget *target); const char *target_libc_generic_name(const ZigTarget *target); bool target_is_libc_lib_name(const ZigTarget *target, const char *name); bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name); -bool target_supports_fpic(const ZigTarget *target); -bool target_supports_clang_march_native(const ZigTarget *target); -bool target_requires_pic(const ZigTarget *target, bool linking_libc); -bool target_requires_pie(const ZigTarget *target); bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi); bool target_abi_is_musl(ZigLLVM_EnvironmentType abi); bool target_is_glibc(const ZigTarget *target); @@ -115,9 +87,6 @@ bool target_is_musl(const ZigTarget *target); bool target_is_wasm(const ZigTarget *target); bool target_is_riscv(const ZigTarget *target); bool target_is_android(const ZigTarget *target); -bool target_is_single_threaded(const ZigTarget *target); -bool target_supports_stack_probing(const ZigTarget *target); -bool target_supports_sanitize_c(const ZigTarget *target); bool target_has_debug_info(const ZigTarget *target); const char *target_arch_musl_name(ZigLLVM_ArchType arch); diff --git a/src/tokenizer.cpp b/src/stage1/tokenizer.cpp similarity index 100% rename from src/tokenizer.cpp rename to src/stage1/tokenizer.cpp diff --git a/src/tokenizer.hpp b/src/stage1/tokenizer.hpp similarity index 100% rename from src/tokenizer.hpp rename to src/stage1/tokenizer.hpp diff --git a/src/util.cpp b/src/stage1/util.cpp similarity index 100% rename from src/util.cpp rename to src/stage1/util.cpp diff --git a/src/util.hpp b/src/stage1/util.hpp similarity index 100% rename from src/util.hpp rename to src/stage1/util.hpp diff --git a/src/util_base.hpp b/src/stage1/util_base.hpp similarity index 100% rename from src/util_base.hpp rename to src/stage1/util_base.hpp diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp new file mode 100644 index 0000000000..839ff0263f --- /dev/null +++ b/src/stage1/zig0.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +// This file is the entry point for zig0, which is *only* used to build +// stage2, the self-hosted compiler, into an object file, which is then +// linked by the same build system (cmake) that linked this binary. + +#include "stage1.h" +#include "heap.hpp" +#include "stage2.h" +#include "target.hpp" +#include "error.hpp" +#include "util.hpp" +#include "buffer.hpp" +#include "os.hpp" + +#include +#include + +static int print_error_usage(const char *arg0) { + fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0); + return EXIT_FAILURE; +} + +static int print_full_usage(const char *arg0, FILE *file, int return_code) { + fprintf(file, + "Usage: %s [options] builds an object file\n" + "\n" + "Options:\n" + " --color [auto|off|on] enable or disable colored error messages\n" + " --name [name] override output name\n" + " -femit-bin=[path] Output machine code\n" + " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" + " --pkg-end pop current pkg\n" + " -ODebug build with optimizations on and safety off\n" + " -OReleaseFast build with optimizations on and safety off\n" + " -OReleaseSafe build with optimizations on and safety on\n" + " -OReleaseSmall build with size optimizations on and safety off\n" + " --single-threaded source may assume it is only used single-threaded\n" + " -dynamic create a shared library (.so; .dll; .dylib)\n" + " --strip exclude debug symbols\n" + " -target [name] -- see the targets command\n" + " -mcpu [cpu] specify target CPU and feature set\n" + " --verbose-tokenize enable compiler debug output for tokenization\n" + " --verbose-ast enable compiler debug output for AST parsing\n" + " --verbose-ir enable compiler debug output for Zig IR\n" + " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" + " --verbose-cimport enable compiler debug output for C imports\n" + " --verbose-llvm-cpu-features enable compiler debug output for LLVM CPU features\n" + "\n" + , arg0); + return return_code; +} + +static Os get_zig_os_type(ZigLLVM_OSType os_type) { + switch (os_type) { + case ZigLLVM_UnknownOS: + return OsFreestanding; + case ZigLLVM_Ananas: + return OsAnanas; + case ZigLLVM_CloudABI: + return OsCloudABI; + case ZigLLVM_DragonFly: + return OsDragonFly; + case ZigLLVM_FreeBSD: + return OsFreeBSD; + case ZigLLVM_Fuchsia: + return OsFuchsia; + case ZigLLVM_IOS: + return OsIOS; + case ZigLLVM_KFreeBSD: + return OsKFreeBSD; + case ZigLLVM_Linux: + return OsLinux; + case ZigLLVM_Lv2: + return OsLv2; + case ZigLLVM_Darwin: + case ZigLLVM_MacOSX: + return OsMacOSX; + case ZigLLVM_NetBSD: + return OsNetBSD; + case ZigLLVM_OpenBSD: + return OsOpenBSD; + case ZigLLVM_Solaris: + return OsSolaris; + case ZigLLVM_Win32: + return OsWindows; + case ZigLLVM_Haiku: + return OsHaiku; + case ZigLLVM_Minix: + return OsMinix; + case ZigLLVM_RTEMS: + return OsRTEMS; + case ZigLLVM_NaCl: + return OsNaCl; + case ZigLLVM_CNK: + return OsCNK; + case ZigLLVM_AIX: + return OsAIX; + case ZigLLVM_CUDA: + return OsCUDA; + case ZigLLVM_NVCL: + return OsNVCL; + case ZigLLVM_AMDHSA: + return OsAMDHSA; + case ZigLLVM_PS4: + return OsPS4; + case ZigLLVM_ELFIAMCU: + return OsELFIAMCU; + case ZigLLVM_TvOS: + return OsTvOS; + case ZigLLVM_WatchOS: + return OsWatchOS; + case ZigLLVM_Mesa3D: + return OsMesa3D; + case ZigLLVM_Contiki: + return OsContiki; + case ZigLLVM_AMDPAL: + return OsAMDPAL; + case ZigLLVM_HermitCore: + return OsHermitCore; + case ZigLLVM_Hurd: + return OsHurd; + case ZigLLVM_WASI: + return OsWASI; + case ZigLLVM_Emscripten: + return OsEmscripten; + } + zig_unreachable(); +} + +static void get_native_target(ZigTarget *target) { + // first zero initialize + *target = {}; + + ZigLLVM_OSType os_type; + ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os + ZigLLVM_VendorType trash; + ZigLLVMGetNativeTarget( + &target->arch, + &trash, + &os_type, + &target->abi, + &oformat); + target->os = get_zig_os_type(os_type); + target->is_native_os = true; + target->is_native_cpu = true; + if (target->abi == ZigLLVM_UnknownEnvironment) { + target->abi = target_default_abi(target->arch, target->os); + } +} + +static Error target_parse_triple(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker) +{ + Error err; + + if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) { + zig_triple = nullptr; + } + + if (zig_triple == nullptr) { + get_native_target(target); + + if (mcpu == nullptr) { + target->llvm_cpu_name = ZigLLVMGetHostCPUName(); + target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); + } else if (strcmp(mcpu, "baseline") == 0) { + target->is_native_os = false; + target->is_native_cpu = false; + target->llvm_cpu_name = ""; + target->llvm_cpu_features = ""; + } else { + const char *msg = "stage0 can't handle CPU/features in the target"; + stage2_panic(msg, strlen(msg)); + } + } else { + // first initialize all to zero + *target = {}; + + SplitIterator it = memSplit(str(zig_triple), str("-")); + + Optional> opt_archsub = SplitIterator_next(&it); + Optional> opt_os = SplitIterator_next(&it); + Optional> opt_abi = SplitIterator_next(&it); + + if (!opt_archsub.is_some) + return ErrorMissingArchitecture; + + if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) { + return err; + } + + if (!opt_os.is_some) + return ErrorMissingOperatingSystem; + + if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) { + return err; + } + + if (opt_abi.is_some) { + if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) { + return err; + } + } else { + target->abi = target_default_abi(target->arch, target->os); + } + + if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) { + const char *msg = "stage0 can't handle CPU/features in the target"; + stage2_panic(msg, strlen(msg)); + } + } + + return ErrorNone; +} + + +static bool str_starts_with(const char *s1, const char *s2) { + size_t s2_len = strlen(s2); + if (strlen(s1) < s2_len) { + return false; + } + return memcmp(s1, s2, s2_len) == 0; +} + +int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) { + if (root_progress_node != nullptr) { + stage2_progress_end(root_progress_node); + } + return exit_code; +} + +int main(int argc, char **argv) { + zig_stage1_os_init(); + + char *arg0 = argv[0]; + Error err; + + const char *in_file = nullptr; + const char *emit_bin_path = nullptr; + bool strip = false; + const char *out_name = nullptr; + bool verbose_tokenize = false; + bool verbose_ast = false; + bool verbose_ir = false; + bool verbose_llvm_ir = false; + bool verbose_cimport = false; + bool verbose_llvm_cpu_features = false; + ErrColor color = ErrColorAuto; + const char *dynamic_linker = nullptr; + bool link_libc = false; + bool link_libcpp = false; + const char *target_string = nullptr; + ZigStage1Pkg *cur_pkg = heap::c_allocator.create(); + BuildMode optimize_mode = BuildModeDebug; + TargetSubsystem subsystem = TargetSubsystemAuto; + const char *override_lib_dir = nullptr; + const char *mcpu = nullptr; + + for (int i = 1; i < argc; i += 1) { + char *arg = argv[i]; + + if (arg[0] == '-') { + if (strcmp(arg, "--") == 0) { + fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg); + } else if (strcmp(arg, "-ODebug") == 0) { + optimize_mode = BuildModeDebug; + } else if (strcmp(arg, "-OReleaseFast") == 0) { + optimize_mode = BuildModeFastRelease; + } else if (strcmp(arg, "-OReleaseSafe") == 0) { + optimize_mode = BuildModeSafeRelease; + } else if (strcmp(arg, "-OReleaseSmall") == 0) { + optimize_mode = BuildModeSmallRelease; + } else if (strcmp(arg, "--help") == 0) { + return print_full_usage(arg0, stdout, EXIT_SUCCESS); + } else if (strcmp(arg, "--strip") == 0) { + strip = true; + } else if (strcmp(arg, "--verbose-tokenize") == 0) { + verbose_tokenize = true; + } else if (strcmp(arg, "--verbose-ast") == 0) { + verbose_ast = true; + } else if (strcmp(arg, "--verbose-ir") == 0) { + verbose_ir = true; + } else if (strcmp(arg, "--verbose-llvm-ir") == 0) { + verbose_llvm_ir = true; + } else if (strcmp(arg, "--verbose-cimport") == 0) { + verbose_cimport = true; + } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) { + verbose_llvm_cpu_features = true; + } else if (arg[1] == 'l' && arg[2] != 0) { + // alias for --library + const char *l = &arg[2]; + if (strcmp(l, "c") == 0) { + link_libc = true; + } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) { + link_libcpp = true; + } + } else if (strcmp(arg, "--pkg-begin") == 0) { + if (i + 2 >= argc) { + fprintf(stderr, "Expected 2 arguments after --pkg-begin\n"); + return print_error_usage(arg0); + } + ZigStage1Pkg *new_cur_pkg = heap::c_allocator.create(); + i += 1; + new_cur_pkg->name_ptr = argv[i]; + new_cur_pkg->name_len = strlen(argv[i]); + i += 1; + new_cur_pkg->path_ptr = argv[i]; + new_cur_pkg->path_len = strlen(argv[i]); + new_cur_pkg->parent = cur_pkg; + cur_pkg->children_ptr = heap::c_allocator.reallocate(cur_pkg->children_ptr, + cur_pkg->children_len, cur_pkg->children_len + 1); + cur_pkg->children_ptr[cur_pkg->children_len] = new_cur_pkg; + cur_pkg->children_len += 1; + + cur_pkg = new_cur_pkg; + } else if (strcmp(arg, "--pkg-end") == 0) { + if (cur_pkg->parent == nullptr) { + fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n"); + return EXIT_FAILURE; + } + cur_pkg = cur_pkg->parent; + } else if (str_starts_with(arg, "-mcpu=")) { + mcpu = arg + strlen("-mcpu="); + } else if (str_starts_with(arg, "-femit-bin=")) { + emit_bin_path = arg + strlen("-femit-bin="); + } else if (i + 1 >= argc) { + fprintf(stderr, "Expected another argument after %s\n", arg); + return print_error_usage(arg0); + } else { + i += 1; + if (strcmp(arg, "--color") == 0) { + if (strcmp(argv[i], "auto") == 0) { + color = ErrColorAuto; + } else if (strcmp(argv[i], "on") == 0) { + color = ErrColorOn; + } else if (strcmp(argv[i], "off") == 0) { + color = ErrColorOff; + } else { + fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n"); + return print_error_usage(arg0); + } + } else if (strcmp(arg, "--name") == 0) { + out_name = argv[i]; + } else if (strcmp(arg, "--dynamic-linker") == 0) { + dynamic_linker = argv[i]; + } else if (strcmp(arg, "--override-lib-dir") == 0) { + override_lib_dir = argv[i]; + } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) { + if (strcmp(argv[i], "c") == 0) { + link_libc = true; + } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) { + link_libcpp = true; + } + } else if (strcmp(arg, "-target") == 0) { + target_string = 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 if (strcmp(arg, "-mcpu") == 0) { + mcpu = argv[i]; + } else { + fprintf(stderr, "Invalid argument: %s\n", arg); + return print_error_usage(arg0); + } + } + } else if (!in_file) { + in_file = arg; + } else { + fprintf(stderr, "Unexpected extra parameter: %s\n", arg); + return print_error_usage(arg0); + } + } + + if (cur_pkg->parent != nullptr) { + fprintf(stderr, "Unmatched --pkg-begin\n"); + return EXIT_FAILURE; + } + + Stage2Progress *progress = stage2_progress_create(); + Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0); + if (color == ErrColorOff) stage2_progress_disable_tty(progress); + + ZigTarget target; + if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) { + fprintf(stderr, "invalid target: %s\n", err_str(err)); + return print_error_usage(arg0); + } + + if (in_file == nullptr) { + fprintf(stderr, "missing zig file\n"); + return print_error_usage(arg0); + } + + if (out_name == nullptr) { + fprintf(stderr, "missing --name\n"); + return print_error_usage(arg0); + } + + ZigStage1 *stage1 = zig_stage1_create(optimize_mode, + nullptr, 0, + in_file, strlen(in_file), + override_lib_dir, strlen(override_lib_dir), + &target, false); + + stage1->main_progress_node = root_progress_node; + stage1->root_name_ptr = out_name; + stage1->root_name_len = strlen(out_name); + stage1->strip = strip; + stage1->verbose_tokenize = verbose_tokenize; + stage1->verbose_ast = verbose_ast; + stage1->verbose_ir = verbose_ir; + stage1->verbose_llvm_ir = verbose_llvm_ir; + stage1->verbose_cimport = verbose_cimport; + stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features; + stage1->emit_o_ptr = emit_bin_path; + stage1->emit_o_len = strlen(emit_bin_path); + stage1->root_pkg = cur_pkg; + stage1->err_color = color; + stage1->link_libc = link_libc; + stage1->link_libcpp = link_libcpp; + stage1->subsystem = subsystem; + stage1->pic = true; + + zig_stage1_build_object(stage1); + + zig_stage1_destroy(stage1); + + return main_exit(root_progress_node, EXIT_SUCCESS); +} + +void stage2_panic(const char *ptr, size_t len) { + fwrite(ptr, 1, len, stderr); + fprintf(stderr, "\n"); + fflush(stderr); + abort(); +} + +struct Stage2Progress { + int trash; +}; + +struct Stage2ProgressNode { + int trash; +}; + +Stage2Progress *stage2_progress_create(void) { + return nullptr; +} + +void stage2_progress_destroy(Stage2Progress *progress) {} + +Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +void stage2_progress_end(Stage2ProgressNode *node) {} +void stage2_progress_complete_one(Stage2ProgressNode *node) {} +void stage2_progress_disable_tty(Stage2Progress *progress) {} +void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} + +const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len, + size_t *result_len) +{ + Error err; + Buf contents_buf = BUF_INIT; + Buf path_buf = BUF_INIT; + + buf_init_from_mem(&path_buf, path_ptr, path_len); + if ((err = os_fetch_file_path(&path_buf, &contents_buf))) { + return nullptr; + } + *result_len = buf_len(&contents_buf); + return buf_ptr(&contents_buf); +} + +Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len, + const char **out_zig_path_ptr, size_t *out_zig_path_len, + struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len) +{ + const char *msg = "stage0 called stage2_cimport"; + stage2_panic(msg, strlen(msg)); +} + +const char *stage2_add_link_lib(struct ZigStage1 *stage1, + const char *lib_name_ptr, size_t lib_name_len, + const char *symbol_name_ptr, size_t symbol_name_len) +{ + return nullptr; +} diff --git a/src/stage2.cpp b/src/stage2.cpp deleted file mode 100644 index 6c010de84f..0000000000 --- a/src/stage2.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// This file is a shim for zig1. The real implementations of these are in -// src-self-hosted/stage1.zig - -#include "stage2.h" -#include "util.hpp" -#include "zig_llvm.h" -#include "target.hpp" -#include -#include -#include - -Error stage2_translate_c(struct Stage2Ast **out_ast, - struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, const char *resources_path) -{ - const char *msg = "stage0 called stage2_translate_c"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) { - const char *msg = "stage0 called stage2_free_clang_errors"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_zen(const char **ptr, size_t *len) { - const char *msg = "stage0 called stage2_zen"; - stage2_panic(msg, strlen(msg)); -} - -int stage2_env(int argc, char** argv) { - const char *msg = "stage0 called stage2_env"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_attach_segfault_handler(void) { } - -void stage2_panic(const char *ptr, size_t len) { - fwrite(ptr, 1, len, stderr); - fprintf(stderr, "\n"); - fflush(stderr); - abort(); -} - -void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file) { - const char *msg = "stage0 called stage2_render_ast"; - stage2_panic(msg, strlen(msg)); -} - -int stage2_fmt(int argc, char **argv) { - const char *msg = "stage0 called stage2_fmt"; - stage2_panic(msg, strlen(msg)); -} - -stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len) { - const char *msg = "stage0 called stage2_DepTokenizer_init"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self) { - const char *msg = "stage0 called stage2_DepTokenizer_deinit"; - stage2_panic(msg, strlen(msg)); -} - -stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) { - const char *msg = "stage0 called stage2_DepTokenizer_next"; - stage2_panic(msg, strlen(msg)); -} - - -struct Stage2Progress { - int trash; -}; - -struct Stage2ProgressNode { - int trash; -}; - -Stage2Progress *stage2_progress_create(void) { - return nullptr; -} - -void stage2_progress_destroy(Stage2Progress *progress) {} - -Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, - const char *name_ptr, size_t name_len, size_t estimated_total_items) -{ - return nullptr; -} -Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, - const char *name_ptr, size_t name_len, size_t estimated_total_items) -{ - return nullptr; -} -void stage2_progress_end(Stage2ProgressNode *node) {} -void stage2_progress_complete_one(Stage2ProgressNode *node) {} -void stage2_progress_disable_tty(Stage2Progress *progress) {} -void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} - -static Os get_zig_os_type(ZigLLVM_OSType os_type) { - switch (os_type) { - case ZigLLVM_UnknownOS: - return OsFreestanding; - case ZigLLVM_Ananas: - return OsAnanas; - case ZigLLVM_CloudABI: - return OsCloudABI; - case ZigLLVM_DragonFly: - return OsDragonFly; - case ZigLLVM_FreeBSD: - return OsFreeBSD; - case ZigLLVM_Fuchsia: - return OsFuchsia; - case ZigLLVM_IOS: - return OsIOS; - case ZigLLVM_KFreeBSD: - return OsKFreeBSD; - case ZigLLVM_Linux: - return OsLinux; - case ZigLLVM_Lv2: - return OsLv2; - case ZigLLVM_Darwin: - case ZigLLVM_MacOSX: - return OsMacOSX; - case ZigLLVM_NetBSD: - return OsNetBSD; - case ZigLLVM_OpenBSD: - return OsOpenBSD; - case ZigLLVM_Solaris: - return OsSolaris; - case ZigLLVM_Win32: - return OsWindows; - case ZigLLVM_Haiku: - return OsHaiku; - case ZigLLVM_Minix: - return OsMinix; - case ZigLLVM_RTEMS: - return OsRTEMS; - case ZigLLVM_NaCl: - return OsNaCl; - case ZigLLVM_CNK: - return OsCNK; - case ZigLLVM_AIX: - return OsAIX; - case ZigLLVM_CUDA: - return OsCUDA; - case ZigLLVM_NVCL: - return OsNVCL; - case ZigLLVM_AMDHSA: - return OsAMDHSA; - case ZigLLVM_PS4: - return OsPS4; - case ZigLLVM_ELFIAMCU: - return OsELFIAMCU; - case ZigLLVM_TvOS: - return OsTvOS; - case ZigLLVM_WatchOS: - return OsWatchOS; - case ZigLLVM_Mesa3D: - return OsMesa3D; - case ZigLLVM_Contiki: - return OsContiki; - case ZigLLVM_AMDPAL: - return OsAMDPAL; - case ZigLLVM_HermitCore: - return OsHermitCore; - case ZigLLVM_Hurd: - return OsHurd; - case ZigLLVM_WASI: - return OsWASI; - case ZigLLVM_Emscripten: - return OsEmscripten; - } - zig_unreachable(); -} - -static void get_native_target(ZigTarget *target) { - // first zero initialize - *target = {}; - - ZigLLVM_OSType os_type; - ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os - ZigLLVMGetNativeTarget( - &target->arch, - &target->vendor, - &os_type, - &target->abi, - &oformat); - target->os = get_zig_os_type(os_type); - target->is_native_os = true; - target->is_native_cpu = true; - if (target->abi == ZigLLVM_UnknownEnvironment) { - target->abi = target_default_abi(target->arch, target->os); - } - if (target_is_glibc(target)) { - target->glibc_or_darwin_version = heap::c_allocator.create(); - target_init_default_glibc_version(target); - } -} - -Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, - const char *dynamic_linker) -{ - Error err; - - if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) { - zig_triple = nullptr; - } - - if (zig_triple == nullptr) { - get_native_target(target); - - if (mcpu == nullptr) { - target->llvm_cpu_name = ZigLLVMGetHostCPUName(); - target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); - target->cache_hash = "native\n\n"; - } else if (strcmp(mcpu, "baseline") == 0) { - target->is_native_os = false; - target->is_native_cpu = false; - target->llvm_cpu_name = ""; - target->llvm_cpu_features = ""; - target->cache_hash = "baseline\n\n"; - } else { - const char *msg = "stage0 can't handle CPU/features in the target"; - stage2_panic(msg, strlen(msg)); - } - } else { - // first initialize all to zero - *target = {}; - - SplitIterator it = memSplit(str(zig_triple), str("-")); - - Optional> opt_archsub = SplitIterator_next(&it); - Optional> opt_os = SplitIterator_next(&it); - Optional> opt_abi = SplitIterator_next(&it); - - if (!opt_archsub.is_some) - return ErrorMissingArchitecture; - - if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) { - return err; - } - - if (!opt_os.is_some) - return ErrorMissingOperatingSystem; - - if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) { - return err; - } - - if (opt_abi.is_some) { - if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) { - return err; - } - } else { - target->abi = target_default_abi(target->arch, target->os); - } - - if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) { - const char *msg = "stage0 can't handle CPU/features in the target"; - stage2_panic(msg, strlen(msg)); - } - target->cache_hash = "\n\n"; - } - - target->cache_hash_len = strlen(target->cache_hash); - - if (dynamic_linker != nullptr) { - target->dynamic_linker = dynamic_linker; - } - - return ErrorNone; -} - -int stage2_cmd_targets(const char *zig_triple, const char *mcpu, const char *dynamic_linker) { - const char *msg = "stage0 called stage2_cmd_targets"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file) { - libc->include_dir = "/dummy/include"; - libc->include_dir_len = strlen(libc->include_dir); - libc->sys_include_dir = "/dummy/sys/include"; - libc->sys_include_dir_len = strlen(libc->sys_include_dir); - libc->crt_dir = ""; - libc->crt_dir_len = strlen(libc->crt_dir); - libc->msvc_lib_dir = ""; - libc->msvc_lib_dir_len = strlen(libc->msvc_lib_dir); - libc->kernel32_lib_dir = ""; - libc->kernel32_lib_dir_len = strlen(libc->kernel32_lib_dir); - return ErrorNone; -} - -enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file) { - const char *msg = "stage0 called stage2_libc_render"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) { - const char *msg = "stage0 called stage2_libc_find_native"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) { - native_paths->include_dirs_ptr = nullptr; - native_paths->include_dirs_len = 0; - - native_paths->lib_dirs_ptr = nullptr; - native_paths->lib_dirs_len = 0; - - native_paths->rpaths_ptr = nullptr; - native_paths->rpaths_len = 0; - - native_paths->warnings_ptr = nullptr; - native_paths->warnings_len = 0; - - return ErrorNone; -} - -void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it, - size_t argc, char **argv) -{ - const char *msg = "stage0 called stage2_clang_arg_iterator"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) { - const char *msg = "stage0 called stage2_clang_arg_next"; - stage2_panic(msg, strlen(msg)); -} - -const bool stage2_is_zig0 = true; diff --git a/src/stage2.h b/src/stage2.h deleted file mode 100644 index 38a1f77d46..0000000000 --- a/src/stage2.h +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_STAGE2_H -#define ZIG_STAGE2_H - -#include -#include -#include - -#include "zig_llvm.h" - -#ifdef __cplusplus -#define ZIG_EXTERN_C extern "C" -#else -#define ZIG_EXTERN_C -#endif - -#if defined(_MSC_VER) -#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn) -#else -#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn)) -#endif - -// ABI warning: the types and declarations in this file must match both those in -// stage2.cpp and src-self-hosted/stage2.zig. - -// ABI warning -enum Error { - ErrorNone, - ErrorNoMem, - ErrorInvalidFormat, - ErrorSemanticAnalyzeFail, - ErrorAccess, - ErrorInterrupted, - ErrorSystemResources, - ErrorFileNotFound, - ErrorFileSystem, - ErrorFileTooBig, - ErrorDivByZero, - ErrorOverflow, - ErrorPathAlreadyExists, - ErrorUnexpected, - ErrorExactDivRemainder, - ErrorNegativeDenominator, - ErrorShiftedOutOneBits, - ErrorCCompileErrors, - ErrorEndOfFile, - ErrorIsDir, - ErrorNotDir, - ErrorUnsupportedOperatingSystem, - ErrorSharingViolation, - ErrorPipeBusy, - ErrorPrimitiveTypeNotFound, - ErrorCacheUnavailable, - ErrorPathTooLong, - ErrorCCompilerCannotFindFile, - ErrorNoCCompilerInstalled, - ErrorReadingDepFile, - ErrorInvalidDepFile, - ErrorMissingArchitecture, - ErrorMissingOperatingSystem, - ErrorUnknownArchitecture, - ErrorUnknownOperatingSystem, - ErrorUnknownABI, - ErrorInvalidFilename, - ErrorDiskQuota, - ErrorDiskSpace, - ErrorUnexpectedWriteFailure, - ErrorUnexpectedSeekFailure, - ErrorUnexpectedFileTruncationFailure, - ErrorUnimplemented, - ErrorOperationAborted, - ErrorBrokenPipe, - ErrorNoSpaceLeft, - ErrorNotLazy, - ErrorIsAsync, - ErrorImportOutsidePkgPath, - ErrorUnknownCpu, - ErrorUnknownCpuFeature, - ErrorInvalidCpuFeatures, - ErrorInvalidLlvmCpuFeaturesFormat, - ErrorUnknownApplicationBinaryInterface, - ErrorASTUnitFailure, - ErrorBadPathName, - ErrorSymLinkLoop, - ErrorProcessFdQuotaExceeded, - ErrorSystemFdQuotaExceeded, - ErrorNoDevice, - ErrorDeviceBusy, - ErrorUnableToSpawnCCompiler, - ErrorCCompilerExitCode, - ErrorCCompilerCrashed, - ErrorCCompilerCannotFindHeaders, - ErrorLibCRuntimeNotFound, - ErrorLibCStdLibHeaderNotFound, - ErrorLibCKernel32LibNotFound, - ErrorUnsupportedArchitecture, - ErrorWindowsSdkNotFound, - ErrorUnknownDynamicLinkerPath, - ErrorTargetHasNoDynamicLinker, - ErrorInvalidAbiVersion, - ErrorInvalidOperatingSystemVersion, - ErrorUnknownClangOption, - ErrorNestedResponseFile, - ErrorZigIsTheCCompiler, - ErrorFileBusy, - ErrorLocked, -}; - -// ABI warning -struct Stage2ErrorMsg { - const char *filename_ptr; // can be null - size_t filename_len; - const char *msg_ptr; - size_t msg_len; - const char *source; // valid until the ASTUnit is freed. can be null - unsigned line; // 0 based - unsigned column; // 0 based - unsigned offset; // byte offset into source -}; - -// ABI warning -struct Stage2Ast; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast, - struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, const char *resources_path); - -// ABI warning -ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len); - -// ABI warning -ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file); - -// ABI warning -ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len); - -// ABI warning -ZIG_EXTERN_C int stage2_env(int argc, char **argv); - -// ABI warning -ZIG_EXTERN_C void stage2_attach_segfault_handler(void); - -// ABI warning -ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len); - -// ABI warning -ZIG_EXTERN_C int stage2_fmt(int argc, char **argv); - -// ABI warning -struct stage2_DepTokenizer { - void *handle; -}; - -// ABI warning -struct stage2_DepNextResult { - enum TypeId { - error, - null, - target, - prereq, - }; - - TypeId type_id; - - // when ent == error --> error text - // when ent == null --> undefined - // when ent == target --> target pathname - // when ent == prereq --> prereq pathname - const char *textz; -}; - -// ABI warning -ZIG_EXTERN_C stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len); - -// ABI warning -ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self); - -// ABI warning -ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self); - -// ABI warning -struct Stage2Progress; -// ABI warning -struct Stage2ProgressNode; -// ABI warning -ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void); -// ABI warning -ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress); -// ABI warning -ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress); -// ABI warning -ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, - const char *name_ptr, size_t name_len, size_t estimated_total_items); -// ABI warning -ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, - const char *name_ptr, size_t name_len, size_t estimated_total_items); -// ABI warning -ZIG_EXTERN_C void stage2_progress_end(Stage2ProgressNode *node); -// ABI warning -ZIG_EXTERN_C void stage2_progress_complete_one(Stage2ProgressNode *node); -// ABI warning -ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node, - size_t completed_count, size_t estimated_total_items); - -// ABI warning -struct Stage2LibCInstallation { - const char *include_dir; - size_t include_dir_len; - const char *sys_include_dir; - size_t sys_include_dir_len; - const char *crt_dir; - size_t crt_dir_len; - const char *msvc_lib_dir; - size_t msvc_lib_dir_len; - const char *kernel32_lib_dir; - size_t kernel32_lib_dir_len; -}; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file); -// ABI warning -ZIG_EXTERN_C enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file); -// ABI warning -ZIG_EXTERN_C enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc); - -// ABI warning -// Synchronize with target.cpp::os_list -enum Os { - OsFreestanding, - OsAnanas, - OsCloudABI, - OsDragonFly, - OsFreeBSD, - OsFuchsia, - OsIOS, - OsKFreeBSD, - OsLinux, - OsLv2, // PS3 - OsMacOSX, - OsNetBSD, - OsOpenBSD, - OsSolaris, - OsWindows, - OsHaiku, - OsMinix, - OsRTEMS, - OsNaCl, // Native Client - OsCNK, // BG/P Compute-Node Kernel - OsAIX, - OsCUDA, // NVIDIA CUDA - OsNVCL, // NVIDIA OpenCL - OsAMDHSA, // AMD HSA Runtime - OsPS4, - OsELFIAMCU, - OsTvOS, // Apple tvOS - OsWatchOS, // Apple watchOS - OsMesa3D, - OsContiki, - OsAMDPAL, - OsHermitCore, - OsHurd, - OsWASI, - OsEmscripten, - OsUefi, - OsOther, -}; - -// ABI warning -struct Stage2SemVer { - uint32_t major; - uint32_t minor; - uint32_t patch; -}; - -// ABI warning -struct ZigTarget { - enum ZigLLVM_ArchType arch; - enum ZigLLVM_VendorType vendor; - - enum ZigLLVM_EnvironmentType abi; - Os os; - - bool is_native_os; - bool is_native_cpu; - - // null means default. this is double-purposed to be darwin min version - struct Stage2SemVer *glibc_or_darwin_version; - - const char *llvm_cpu_name; - const char *llvm_cpu_features; - const char *cpu_builtin_str; - const char *cache_hash; - size_t cache_hash_len; - const char *os_builtin_str; - const char *dynamic_linker; - const char *standard_dynamic_linker_path; - - const char **llvm_cpu_features_asm_ptr; - size_t llvm_cpu_features_asm_len; -}; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, - const char *dynamic_linker); - -// ABI warning -ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple, const char *mcpu, const char *dynamic_linker); - - -// ABI warning -struct Stage2NativePaths { - const char **include_dirs_ptr; - size_t include_dirs_len; - const char **lib_dirs_ptr; - size_t lib_dirs_len; - const char **rpaths_ptr; - size_t rpaths_len; - const char **warnings_ptr; - size_t warnings_len; -}; -// ABI warning -ZIG_EXTERN_C enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths); - -// ABI warning -enum Stage2ClangArg { - Stage2ClangArgTarget, - Stage2ClangArgO, - Stage2ClangArgC, - Stage2ClangArgOther, - Stage2ClangArgPositional, - Stage2ClangArgL, - Stage2ClangArgIgnore, - Stage2ClangArgDriverPunt, - Stage2ClangArgPIC, - Stage2ClangArgNoPIC, - Stage2ClangArgNoStdLib, - Stage2ClangArgNoStdLibCpp, - Stage2ClangArgShared, - Stage2ClangArgRDynamic, - Stage2ClangArgWL, - Stage2ClangArgPreprocessOrAsm, - Stage2ClangArgOptimize, - Stage2ClangArgDebug, - Stage2ClangArgSanitize, - Stage2ClangArgLinkerScript, - Stage2ClangArgVerboseCmds, - Stage2ClangArgForLinker, - Stage2ClangArgLinkerInputZ, - Stage2ClangArgLibDir, - Stage2ClangArgMCpu, - Stage2ClangArgDepFile, - Stage2ClangArgFrameworkDir, - Stage2ClangArgFramework, - Stage2ClangArgNoStdLibInc, -}; - -// ABI warning -struct Stage2ClangArgIterator { - bool has_next; - enum Stage2ClangArg kind; - const char *only_arg; - const char *second_arg; - const char **other_args_ptr; - size_t other_args_len; - const char **argv_ptr; - size_t argv_len; - size_t next_index; - size_t root_args; -}; - -// ABI warning -ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it, - size_t argc, char **argv); - -// ABI warning -ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it); - -// ABI warning -ZIG_EXTERN_C const bool stage2_is_zig0; - -#endif diff --git a/src/target.zig b/src/target.zig new file mode 100644 index 0000000000..fc0c7a0745 --- /dev/null +++ b/src/target.zig @@ -0,0 +1,342 @@ +const std = @import("std"); +const llvm = @import("llvm.zig"); + +pub const ArchOsAbi = struct { + arch: std.Target.Cpu.Arch, + os: std.Target.Os.Tag, + abi: std.Target.Abi, +}; + +pub const available_libcs = [_]ArchOsAbi{ + .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu }, + .{ .arch = .aarch64_be, .os = .linux, .abi = .musl }, + .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu }, + .{ .arch = .aarch64, .os = .linux, .abi = .gnu }, + .{ .arch = .aarch64, .os = .linux, .abi = .musl }, + .{ .arch = .aarch64, .os = .windows, .abi = .gnu }, + .{ .arch = .armeb, .os = .linux, .abi = .gnueabi }, + .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf }, + .{ .arch = .armeb, .os = .linux, .abi = .musleabi }, + .{ .arch = .armeb, .os = .linux, .abi = .musleabihf }, + .{ .arch = .armeb, .os = .windows, .abi = .gnu }, + .{ .arch = .arm, .os = .linux, .abi = .gnueabi }, + .{ .arch = .arm, .os = .linux, .abi = .gnueabihf }, + .{ .arch = .arm, .os = .linux, .abi = .musleabi }, + .{ .arch = .arm, .os = .linux, .abi = .musleabihf }, + .{ .arch = .arm, .os = .windows, .abi = .gnu }, + .{ .arch = .i386, .os = .linux, .abi = .gnu }, + .{ .arch = .i386, .os = .linux, .abi = .musl }, + .{ .arch = .i386, .os = .windows, .abi = .gnu }, + .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 }, + .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 }, + .{ .arch = .mips64el, .os = .linux, .abi = .musl }, + .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 }, + .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 }, + .{ .arch = .mips64, .os = .linux, .abi = .musl }, + .{ .arch = .mipsel, .os = .linux, .abi = .gnu }, + .{ .arch = .mipsel, .os = .linux, .abi = .musl }, + .{ .arch = .mips, .os = .linux, .abi = .gnu }, + .{ .arch = .mips, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu }, + .{ .arch = .powerpc64le, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc64, .os = .linux, .abi = .gnu }, + .{ .arch = .powerpc64, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc, .os = .linux, .abi = .gnu }, + .{ .arch = .powerpc, .os = .linux, .abi = .musl }, + .{ .arch = .riscv64, .os = .linux, .abi = .gnu }, + .{ .arch = .riscv64, .os = .linux, .abi = .musl }, + .{ .arch = .s390x, .os = .linux, .abi = .gnu }, + .{ .arch = .s390x, .os = .linux, .abi = .musl }, + .{ .arch = .sparc, .os = .linux, .abi = .gnu }, + .{ .arch = .sparcv9, .os = .linux, .abi = .gnu }, + .{ .arch = .wasm32, .os = .freestanding, .abi = .musl }, + .{ .arch = .x86_64, .os = .linux, .abi = .gnu }, + .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 }, + .{ .arch = .x86_64, .os = .linux, .abi = .musl }, + .{ .arch = .x86_64, .os = .windows, .abi = .gnu }, +}; + +pub fn libCGenericName(target: std.Target) [:0]const u8 { + if (target.os.tag == .windows) + return "mingw"; + switch (target.abi) { + .gnu, + .gnuabin32, + .gnuabi64, + .gnueabi, + .gnueabihf, + .gnux32, + => return "glibc", + .musl, + .musleabi, + .musleabihf, + .none, + => return "musl", + .code16, + .eabi, + .eabihf, + .android, + .msvc, + .itanium, + .cygnus, + .coreclr, + .simulator, + .macabi, + => unreachable, + } +} + +pub fn archMuslName(arch: std.Target.Cpu.Arch) [:0]const u8 { + switch (arch) { + .aarch64, .aarch64_be => return "aarch64", + .arm, .armeb => return "arm", + .mips, .mipsel => return "mips", + .mips64el, .mips64 => return "mips64", + .powerpc => return "powerpc", + .powerpc64, .powerpc64le => return "powerpc64", + .s390x => return "s390x", + .i386 => return "i386", + .x86_64 => return "x86_64", + .riscv64 => return "riscv64", + else => unreachable, + } +} + +pub fn canBuildLibC(target: std.Target) bool { + for (available_libcs) |libc| { + if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) { + return true; + } + } + return false; +} + +pub fn cannotDynamicLink(target: std.Target) bool { + return switch (target.os.tag) { + .freestanding, .other => true, + else => false, + }; +} + +/// On Darwin, we always link libSystem which contains libc. +/// Similarly on FreeBSD and NetBSD we always link system libc +/// since this is the stable syscall interface. +pub fn osRequiresLibC(target: std.Target) bool { + return switch (target.os.tag) { + .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true, + else => false, + }; +} + +pub fn libcNeedsLibUnwind(target: std.Target) bool { + return switch (target.os.tag) { + .windows, + .macosx, + .ios, + .watchos, + .tvos, + .freestanding, + => false, + + else => true, + }; +} + +pub fn requiresPIE(target: std.Target) bool { + return target.isAndroid() or target.isDarwin(); +} + +/// This function returns whether non-pic code is completely invalid on the given target. +pub fn requiresPIC(target: std.Target, linking_libc: bool) bool { + return target.isAndroid() or + target.os.tag == .windows or target.os.tag == .uefi or + osRequiresLibC(target) or + (linking_libc and target.isGnuLibC()); +} + +/// This is not whether the target supports Position Independent Code, but whether the -fPIC +/// C compiler argument is valid to Clang. +pub fn supports_fpic(target: std.Target) bool { + return target.os.tag != .windows; +} + +pub fn libc_needs_crti_crtn(target: std.Target) bool { + return !(target.cpu.arch.isRISCV() or target.isAndroid()); +} + +pub fn isSingleThreaded(target: std.Target) bool { + return target.isWasm(); +} + +/// Valgrind supports more, but Zig does not support them yet. +pub fn hasValgrindSupport(target: std.Target) bool { + switch (target.cpu.arch) { + .x86_64 => { + return target.os.tag == .linux or target.isDarwin() or target.os.tag == .solaris or + (target.os.tag == .windows and target.abi != .msvc); + }, + else => return false, + } +} + +pub fn supportsStackProbing(target: std.Target) bool { + return target.os.tag != .windows and target.os.tag != .uefi and + (target.cpu.arch == .i386 or target.cpu.arch == .x86_64); +} + +pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { + return switch (os_tag) { + .freestanding, .other => .UnknownOS, + .windows, .uefi => .Win32, + .ananas => .Ananas, + .cloudabi => .CloudABI, + .dragonfly => .DragonFly, + .freebsd => .FreeBSD, + .fuchsia => .Fuchsia, + .ios => .IOS, + .kfreebsd => .KFreeBSD, + .linux => .Linux, + .lv2 => .Lv2, + .macosx => .MacOSX, + .netbsd => .NetBSD, + .openbsd => .OpenBSD, + .solaris => .Solaris, + .haiku => .Haiku, + .minix => .Minix, + .rtems => .RTEMS, + .nacl => .NaCl, + .cnk => .CNK, + .aix => .AIX, + .cuda => .CUDA, + .nvcl => .NVCL, + .amdhsa => .AMDHSA, + .ps4 => .PS4, + .elfiamcu => .ELFIAMCU, + .tvos => .TvOS, + .watchos => .WatchOS, + .mesa3d => .Mesa3D, + .contiki => .Contiki, + .amdpal => .AMDPAL, + .hermit => .HermitCore, + .hurd => .Hurd, + .wasi => .WASI, + .emscripten => .Emscripten, + }; +} + +pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { + return switch (arch_tag) { + .arm => .arm, + .armeb => .armeb, + .aarch64 => .aarch64, + .aarch64_be => .aarch64_be, + .aarch64_32 => .aarch64_32, + .arc => .arc, + .avr => .avr, + .bpfel => .bpfel, + .bpfeb => .bpfeb, + .hexagon => .hexagon, + .mips => .mips, + .mipsel => .mipsel, + .mips64 => .mips64, + .mips64el => .mips64el, + .msp430 => .msp430, + .powerpc => .ppc, + .powerpc64 => .ppc64, + .powerpc64le => .ppc64le, + .r600 => .r600, + .amdgcn => .amdgcn, + .riscv32 => .riscv32, + .riscv64 => .riscv64, + .sparc => .sparc, + .sparcv9 => .sparcv9, + .sparcel => .sparcel, + .s390x => .systemz, + .tce => .tce, + .tcele => .tcele, + .thumb => .thumb, + .thumbeb => .thumbeb, + .i386 => .x86, + .x86_64 => .x86_64, + .xcore => .xcore, + .nvptx => .nvptx, + .nvptx64 => .nvptx64, + .le32 => .le32, + .le64 => .le64, + .amdil => .amdil, + .amdil64 => .amdil64, + .hsail => .hsail, + .hsail64 => .hsail64, + .spir => .spir, + .spir64 => .spir64, + .kalimba => .kalimba, + .shave => .shave, + .lanai => .lanai, + .wasm32 => .wasm32, + .wasm64 => .wasm64, + .renderscript32 => .renderscript32, + .renderscript64 => .renderscript64, + .ve => .ve, + .spu_2 => .UnknownArch, + }; +} + +fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { + if (ignore_case) { + return std.ascii.eqlIgnoreCase(a, b); + } else { + return std.mem.eql(u8, a, b); + } +} + +pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool { + const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; + + if (eqlIgnoreCase(ignore_case, name, "c")) + return true; + + if (target.isMinGW()) { + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + + return false; + } + + if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) { + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + if (eqlIgnoreCase(ignore_case, name, "rt")) + return true; + if (eqlIgnoreCase(ignore_case, name, "pthread")) + return true; + if (eqlIgnoreCase(ignore_case, name, "crypt")) + return true; + if (eqlIgnoreCase(ignore_case, name, "util")) + return true; + if (eqlIgnoreCase(ignore_case, name, "xnet")) + return true; + if (eqlIgnoreCase(ignore_case, name, "resolv")) + return true; + if (eqlIgnoreCase(ignore_case, name, "dl")) + return true; + if (eqlIgnoreCase(ignore_case, name, "util")) + return true; + } + + if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System")) + return true; + + return false; +} + +pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool { + const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; + + return eqlIgnoreCase(ignore_case, name, "c++") or + eqlIgnoreCase(ignore_case, name, "stdc++") or + eqlIgnoreCase(ignore_case, name, "c++abi"); +} + +pub fn hasDebugInfo(target: std.Target) bool { + return !target.cpu.arch.isWasm(); +} diff --git a/src-self-hosted/test.zig b/src/test.zig similarity index 89% rename from src-self-hosted/test.zig rename to src/test.zig index aef48e198b..8ad11efa9c 100644 --- a/src-self-hosted/test.zig +++ b/src/test.zig @@ -1,9 +1,10 @@ const std = @import("std"); const link = @import("link.zig"); -const Module = @import("Module.zig"); +const Compilation = @import("Compilation.zig"); const Allocator = std.mem.Allocator; const zir = @import("zir.zig"); const Package = @import("Package.zig"); +const introspect = @import("introspect.zig"); const build_options = @import("build_options"); const enable_qemu: bool = build_options.enable_qemu; const enable_wine: bool = build_options.enable_wine; @@ -60,7 +61,7 @@ pub const TestContext = struct { ZIR, }; - /// A Case consists of a set of *updates*. The same Module is used for each + /// A Case consists of a set of *updates*. The same Compilation is used for each /// update, so each update's source is treated as a single file being /// updated by the test harness and incrementally compiled. pub const Case = struct { @@ -406,6 +407,17 @@ pub const TestContext = struct { const root_node = try progress.start("tests", self.cases.items.len); defer root_node.end(); + var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator); + defer zig_lib_directory.handle.close(); + defer std.testing.allocator.free(zig_lib_directory.path.?); + + const random_seed = blk: { + var random_seed: u64 = undefined; + try std.crypto.randomBytes(std.mem.asBytes(&random_seed)); + break :blk random_seed; + }; + var default_prng = std.rand.DefaultPrng.init(random_seed); + for (self.cases.items) |case| { var prg_node = root_node.start(case.name, case.updates.items.len); prg_node.activate(); @@ -416,11 +428,18 @@ pub const TestContext = struct { progress.initial_delay_ns = 0; progress.refresh_rate_ns = 0; - try self.runOneCase(std.testing.allocator, &prg_node, case); + try self.runOneCase(std.testing.allocator, &prg_node, case, zig_lib_directory, &default_prng.random); } } - fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void { + fn runOneCase( + self: *TestContext, + allocator: *Allocator, + root_node: *std.Progress.Node, + case: Case, + zig_lib_directory: Compilation.Directory, + rand: *std.rand.Random, + ) !void { const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target); const target = target_info.target; @@ -431,13 +450,45 @@ pub const TestContext = struct { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); - const tmp_src_path = if (case.extension == .Zig) "test_case.zig" else if (case.extension == .ZIR) "test_case.zir" else unreachable; - const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); - defer root_pkg.destroy(); + var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); + defer cache_dir.close(); + const bogus_path = "bogus"; // TODO this will need to be fixed before we can test LLVM extensions + const zig_cache_directory: Compilation.Directory = .{ + .handle = cache_dir, + .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }), + }; - const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null); + const tmp_src_path = switch (case.extension) { + .Zig => "test_case.zig", + .ZIR => "test_case.zir", + }; - var module = try Module.init(allocator, .{ + var root_pkg: Package = .{ + .root_src_directory = .{ .path = bogus_path, .handle = tmp.dir }, + .root_src_path = tmp_src_path, + }; + + const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null; + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = "test_case", + .target = target, + .output_mode = case.output_mode, + .object_format = ofmt, + }); + + const emit_directory: Compilation.Directory = .{ + .path = bogus_path, + .handle = tmp.dir, + }; + const emit_bin: Compilation.EmitLoc = .{ + .directory = emit_directory, + .basename = bin_name, + }; + const comp = try Compilation.create(allocator, .{ + .local_cache_directory = zig_cache_directory, + .global_cache_directory = zig_cache_directory, + .zig_lib_directory = zig_lib_directory, + .rand = rand, .root_name = "test_case", .target = target, // TODO: support tests for object file building, and library builds @@ -446,13 +497,13 @@ pub const TestContext = struct { .output_mode = case.output_mode, // TODO: support testing optimizations .optimize_mode = .Debug, - .bin_file_dir = tmp.dir, - .bin_file_path = bin_name, - .root_pkg = root_pkg, + .emit_bin = emit_bin, + .root_pkg = &root_pkg, .keep_source_files_loaded = true, - .object_format = if (case.cbe) .c else null, + .object_format = ofmt, + .is_native_os = case.target.isNativeOs(), }); - defer module.deinit(); + defer comp.destroy(); for (case.updates.items) |update, update_index| { var update_node = root_node.start("update", 3); @@ -466,20 +517,20 @@ pub const TestContext = struct { var module_node = update_node.start("parse/analysis/codegen", null); module_node.activate(); - try module.makeBinFileWritable(); - try module.update(); + try comp.makeBinFileWritable(); + try comp.update(); module_node.end(); if (update.case != .Error) { - var all_errors = try module.getAllErrorsAlloc(); + var all_errors = try comp.getAllErrorsAlloc(); defer all_errors.deinit(allocator); if (all_errors.list.len != 0) { - std.debug.print("\nErrors occurred updating the module:\n================\n", .{}); + std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); for (all_errors.list) |err| { std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg }); } if (case.cbe) { - const C = module.bin_file.cast(link.File.C).?; + const C = comp.bin_file.cast(link.File.C).?; std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items}); } std.debug.print("Test failed.\n", .{}); @@ -510,7 +561,7 @@ pub const TestContext = struct { update_node.estimated_total_items = 5; var emit_node = update_node.start("emit", null); emit_node.activate(); - var new_zir_module = try zir.emit(allocator, module); + var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?); defer new_zir_module.deinit(allocator); emit_node.end(); @@ -545,7 +596,7 @@ pub const TestContext = struct { for (handled_errors) |*h| { h.* = false; } - var all_errors = try module.getAllErrorsAlloc(); + var all_errors = try comp.getAllErrorsAlloc(); defer all_errors.deinit(allocator); for (all_errors.list) |a| { for (e) |ex, i| { @@ -627,7 +678,7 @@ pub const TestContext = struct { }, } - try module.makeBinFileExecutable(); + try comp.makeBinFileExecutable(); break :x try std.ChildProcess.exec(.{ .allocator = allocator, @@ -693,23 +744,23 @@ pub const TestContext = struct { } var interpreter = spu.Interpreter(struct { - RAM: [0x10000]u8 = undefined, + RAM: [0x10000]u8 = undefined, - pub fn read8(bus: @This(), addr: u16) u8 { - return bus.RAM[addr]; - } - pub fn read16(bus: @This(), addr: u16) u16 { - return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); - } + pub fn read8(bus: @This(), addr: u16) u8 { + return bus.RAM[addr]; + } + pub fn read16(bus: @This(), addr: u16) u16 { + return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); + } - pub fn write8(bus: *@This(), addr: u16, val: u8) void { - bus.RAM[addr] = val; - } + pub fn write8(bus: *@This(), addr: u16, val: u8) void { + bus.RAM[addr] = val; + } - pub fn write16(bus: *@This(), addr: u16, val: u16) void { - std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); - } - }){ + pub fn write16(bus: *@This(), addr: u16, val: u16) void { + std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); + } + }){ .bus = .{}, }; diff --git a/src-self-hosted/tracy.zig b/src/tracy.zig similarity index 100% rename from src-self-hosted/tracy.zig rename to src/tracy.zig diff --git a/src-self-hosted/translate_c.zig b/src/translate_c.zig similarity index 99% rename from src-self-hosted/translate_c.zig rename to src/translate_c.zig index a5619d56fe..66b5752930 100644 --- a/src-self-hosted/translate_c.zig +++ b/src/translate_c.zig @@ -1,5 +1,5 @@ -// This is the userland implementation of translate-c which is used by both stage1 -// and stage2. +//! This is the userland implementation of translate-c which is used by both stage1 +//! and stage2. const std = @import("std"); const assert = std.debug.assert; diff --git a/src-self-hosted/type.zig b/src/type.zig similarity index 100% rename from src-self-hosted/type.zig rename to src/type.zig diff --git a/src-self-hosted/value.zig b/src/value.zig similarity index 100% rename from src-self-hosted/value.zig rename to src/value.zig diff --git a/src/windows_sdk.h b/src/windows_sdk.h index e887209559..a4707b3de0 100644 --- a/src/windows_sdk.h +++ b/src/windows_sdk.h @@ -16,7 +16,7 @@ #include -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig struct ZigWindowsSDK { const char *path10_ptr; size_t path10_len; @@ -34,7 +34,7 @@ struct ZigWindowsSDK { size_t msvc_lib_dir_len; }; -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig enum ZigFindWindowsSdkError { ZigFindWindowsSdkErrorNone, ZigFindWindowsSdkErrorOutOfMemory, @@ -42,10 +42,10 @@ enum ZigFindWindowsSdkError { ZigFindWindowsSdkErrorPathTooLong, }; -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); #endif diff --git a/src-self-hosted/windows_sdk.zig b/src/windows_sdk.zig similarity index 100% rename from src-self-hosted/windows_sdk.zig rename to src/windows_sdk.zig diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 21d0c5c0ca..31c4404083 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -13,7 +13,6 @@ * 3. Prevent C++ from infecting the rest of the project. */ #include "zig_clang.h" -#include "list.hpp" #if __GNUC__ >= 8 #pragma GCC diagnostic push @@ -2186,7 +2185,7 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char // Take ownership of the err_unit ASTUnit object so that it won't be // free'd when we return, invalidating the error message pointers clang::ASTUnit *unit = ast_unit ? ast_unit : err_unit.release(); - ZigList errors = {}; + Stage2ErrorMsg *errors = nullptr; for (clang::ASTUnit::stored_diag_iterator it = unit->stored_diag_begin(), it_end = unit->stored_diag_end(); it != it_end; ++it) @@ -2204,7 +2203,10 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char llvm::StringRef msg_str_ref = it->getMessage(); - Stage2ErrorMsg *msg = errors.add_one(); + *errors_len += 1; + errors = reinterpret_cast(realloc(errors, sizeof(Stage2ErrorMsg) * *errors_len)); + if (errors == nullptr) abort(); + Stage2ErrorMsg *msg = &errors[*errors_len - 1]; memset(msg, 0, sizeof(*msg)); msg->msg_ptr = (const char *)msg_str_ref.bytes_begin(); @@ -2242,8 +2244,7 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char } } - *errors_ptr = errors.items; - *errors_len = errors.length; + *errors_ptr = errors; return nullptr; } diff --git a/src/zig_clang.h b/src/zig_clang.h index 2c358d880e..f9ce9c34ed 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -14,7 +14,7 @@ // ATTENTION: If you modify this file, be sure to update the corresponding // extern function declarations in the self-hosted compiler file -// src-self-hosted/clang.zig. +// src/clang.zig. struct ZigClangSourceLocation { unsigned ID; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index e5b9df625c..08823050ad 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -927,7 +927,7 @@ class MyOStream: public raw_ostream { }; bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch, - const char *output_lib_path, const bool kill_at) + const char *output_lib_path, bool kill_at) { COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; diff --git a/src/zig_llvm.h b/src/zig_llvm.h index f07684f2a4..007d8afc1f 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -60,7 +60,7 @@ enum ZigLLVMABIType { ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, - LLVMCodeModel CodeModel, bool function_sections, ZigLLVMABIType float_abi, const char *abi_name); + LLVMCodeModel CodeModel, bool function_sections, enum ZigLLVMABIType float_abi, const char *abi_name); ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); @@ -500,8 +500,8 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char * ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, enum ZigLLVM_OSType os_type); -bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch, - const char *output_lib_path, const bool kill_at); +ZIG_EXTERN_C bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch, + const char *output_lib_path, bool kill_at); ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type, enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type, diff --git a/src-self-hosted/zir.zig b/src/zir.zig similarity index 99% rename from src-self-hosted/zir.zig rename to src/zir.zig index b6d7fab4c5..7e723fc674 100644 --- a/src-self-hosted/zir.zig +++ b/src/zir.zig @@ -1700,12 +1700,12 @@ const Parser = struct { } }; -pub fn emit(allocator: *Allocator, old_module: IrModule) !Module { +pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { var ctx: EmitZIR = .{ .allocator = allocator, .decls = .{}, .arena = std.heap.ArenaAllocator.init(allocator), - .old_module = &old_module, + .old_module = old_module, .next_auto_name = 0, .names = std.StringArrayHashMap(void).init(allocator), .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), @@ -2539,7 +2539,7 @@ const EmitZIR = struct { return self.emitUnnamedDecl(&fntype_inst.base); }, .Int => { - const info = ty.intInfo(self.old_module.target()); + const info = ty.intInfo(self.old_module.getTarget()); const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false"); const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64); bits_payload.* = .{ .int = info.bits }; diff --git a/src-self-hosted/zir_sema.zig b/src/zir_sema.zig similarity index 99% rename from src-self-hosted/zir_sema.zig rename to src/zir_sema.zig index c99da39c04..10543d2ee6 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src/zir_sema.zig @@ -199,10 +199,10 @@ pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError // We don't fully codegen the decl until later, but we do need to reserve a global // offset table index for it. This allows us to codegen decls out of dependency order, // increasing how many computations can be done in parallel. - try mod.bin_file.allocateDeclIndexes(decl); - try mod.work_queue.writeItem(.{ .codegen_decl = decl }); + try mod.comp.bin_file.allocateDeclIndexes(decl); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); } else if (prev_type_has_bits) { - mod.bin_file.freeDecl(decl); + mod.comp.bin_file.freeDecl(decl); } return type_changed; diff --git a/test/cli.zig b/test/cli.zig index 77d79ed98e..7a0a7d6459 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -58,7 +58,7 @@ fn printCmd(cwd: []const u8, argv: []const []const u8) void { std.debug.warn("\n", .{}); } -fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { +fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult { const max_output_size = 100 * 1024; const result = ChildProcess.exec(.{ .allocator = a, @@ -72,7 +72,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { }; switch (result.term) { .Exited => |code| { - if (code != 0) { + if ((code != 0) == expect_0) { std.debug.warn("The following command exited with error code {}:\n", .{code}); printCmd(cwd, argv); std.debug.warn("stderr:\n{}\n", .{result.stderr}); @@ -90,15 +90,15 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { } fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-lib" }); - const test_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "test" }); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); + const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" }); testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n")); } fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); - const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" }); - testing.expect(std.mem.eql(u8, run_result.stderr, "All your codebase are belong to us.\n")); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); + const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" }); + testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n")); } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { @@ -118,17 +118,20 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { \\} ); - const args = [_][]const u8{ + var args = std.ArrayList([]const u8).init(a); + try args.appendSlice(&[_][]const u8{ zig_exe, "build-obj", "--cache-dir", dir_path, "--name", "example", - "--output-dir", dir_path, - "--emit", "asm", - "-mllvm", "--x86-asm-syntax=intel", - "--strip", "--release-fast", - example_zig_path, "--disable-gen-h", - }; - _ = try exec(dir_path, &args); + "-fno-emit-bin", "-fno-emit-h", + "--strip", "-OReleaseFast", + example_zig_path, + }); + + const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path}); + try args.append(emit_asm_arg); + + _ = try exec(dir_path, true, args.items); const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize)); testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null); @@ -137,23 +140,25 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { } fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); - const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist" }); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); + const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" }); + const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path}); const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" }); - _ = try exec(dir_path, &[_][]const u8{ - zig_exe, "build-exe", source_path, "--output-dir", output_path, - }); + const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg }); + const s = std.fs.path.sep_str; + const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; + testing.expectEqualStrings(expected, result.stderr); } fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); const unformatted_code = " // no reason for indent"; const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" }); try fs.cwd().writeFile(fmt1_zig_path, unformatted_code); - const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); + const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); // stderr should be file path + \n testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path)); testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n'); @@ -161,12 +166,12 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" }); try fs.cwd().writeFile(fmt2_zig_path, unformatted_code); - const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path }); + const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); // running it on the dir, only the new file should be changed testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path)); testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n'); - const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path }); + const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); // both files have been formatted, nothing should change now testing.expect(run_result3.stderr.len == 0); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 6ae857d1a8..7a33de4f19 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2355,7 +2355,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ exit(0); \\} , &[_][]const u8{ - "tmp.zig:3:5: error: dependency on library c must be explicitly specified in the build command", + "tmp.zig:3:5: error: dependency on libc must be explicitly specified in the build command", }); cases.addTest("libc headers note", diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 0608221866..2a176e0368 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; // These tests should work with all platforms, but we're using linux_x64 for // now for consistency. Will be expanded eventually. diff --git a/test/stage2/spu-ii.zig b/test/stage2/spu-ii.zig index 1316f19d0d..aa091c4174 100644 --- a/test/stage2/spu-ii.zig +++ b/test/stage2/spu-ii.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; const spu = std.zig.CrossTarget{ .cpu_arch = .spu_2, diff --git a/test/stage2/test.zig b/test/stage2/test.zig index ad81e463b9..a22dc23d36 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -1,8 +1,11 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; + +// Self-hosted has differing levels of support for various architectures. For now we pass explicit +// target parameters to each test case. At some point we will take this to the next level and have +// a set of targets that all test cases run on unless specifically overridden. For now, each test +// case applies to only the specified target. -// self-hosted does not yet support PE executable files / COFF object files -// or mach-o files. So we do these test cases cross compiling for x86_64-linux. const linux_x64 = std.zig.CrossTarget{ .cpu_arch = .x86_64, .os_tag = .linux, @@ -73,7 +76,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); // Now change the message only @@ -105,7 +108,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", ); // Now we print it twice. @@ -181,7 +184,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -216,7 +219,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -241,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -268,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); } @@ -295,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); } @@ -326,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -359,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -395,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -432,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -462,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -496,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -520,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -559,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "hello\nhello\nhello\nhello\n", ); @@ -596,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -638,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -690,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -752,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -785,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -817,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -842,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -868,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -901,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "hello\nhello\nhello\nhello\nhello\n", ); } @@ -920,7 +923,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , "42\n", ); @@ -938,7 +941,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , "42\n", ); @@ -954,7 +957,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , // This is what you get when you take the bits of the IEE-754 // representation of 42.0 and reinterpret them as an unsigned // integer. Guess that's a bug in wasmtime. diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig index 78d971d104..f87fca3748 100644 --- a/test/stage2/zir.zig +++ b/test/stage2/zir.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; // self-hosted does not yet support PE executable files / COFF object files // or mach-o files. So we do the ZIR transform test cases cross compiling for // x86_64-linux. @@ -156,7 +156,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ %0 = call(@a, []) \\ %1 = returnvoid() \\}) - , + , &[_][]const u8{ ":18:21: error: message", }, diff --git a/test/tests.zig b/test/tests.zig index 6598a05ea7..58b2b50094 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -634,7 +634,7 @@ pub const StackTracesContext = struct { warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); - const child = std.ChildProcess.init(args.span(), b.allocator) catch unreachable; + const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable; defer child.deinit(); child.stdin_behavior = .Ignore; @@ -643,7 +643,7 @@ pub const StackTracesContext = struct { child.env_map = b.env_map; if (b.verbose) { - printInvocation(args.span()); + printInvocation(args.items); } child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); @@ -666,23 +666,23 @@ pub const StackTracesContext = struct { code, expect_code, }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; } }, .Signal => |signum| { warn("Process {} terminated on signal {}\n", .{ full_exe_path, signum }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; }, .Stopped => |signum| { warn("Process {} stopped on signal {}\n", .{ full_exe_path, signum }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; }, .Unknown => |code| { warn("Process {} terminated unexpectedly with error code {}\n", .{ full_exe_path, code }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; }, } @@ -837,34 +837,27 @@ pub const CompileErrorContext = struct { } else { try zig_args.append("build-obj"); } - const root_src_basename = self.case.sources.span()[0].filename; + const root_src_basename = self.case.sources.items[0].filename; try zig_args.append(self.write_src.getOutputPath(root_src_basename)); zig_args.append("--name") catch unreachable; zig_args.append("test") catch unreachable; - zig_args.append("--output-dir") catch unreachable; - zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable; - if (!self.case.target.isNative()) { try zig_args.append("-target"); try zig_args.append(try self.case.target.zigTriple(b.allocator)); } - switch (self.build_mode) { - Mode.Debug => {}, - Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, - Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, - Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, - } + zig_args.append("-O") catch unreachable; + zig_args.append(@tagName(self.build_mode)) catch unreachable; warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); if (b.verbose) { - printInvocation(zig_args.span()); + printInvocation(zig_args.items); } - const child = std.ChildProcess.init(zig_args.span(), b.allocator) catch unreachable; + const child = std.ChildProcess.init(zig_args.items, b.allocator) catch unreachable; defer child.deinit(); child.env_map = b.env_map; @@ -886,19 +879,19 @@ pub const CompileErrorContext = struct { switch (term) { .Exited => |code| { if (code == 0) { - printInvocation(zig_args.span()); + printInvocation(zig_args.items); return error.CompilationIncorrectlySucceeded; } }, else => { warn("Process {} terminated unexpectedly\n", .{b.zig_exe}); - printInvocation(zig_args.span()); + printInvocation(zig_args.items); return error.TestFailed; }, } - const stdout = stdout_buf.span(); - const stderr = stderr_buf.span(); + const stdout = stdout_buf.items; + const stderr = stderr_buf.items; if (stdout.len != 0) { warn( @@ -927,12 +920,12 @@ pub const CompileErrorContext = struct { if (!ok) { warn("\n======== Expected these compile errors: ========\n", .{}); - for (self.case.expected_errors.span()) |expected| { + for (self.case.expected_errors.items) |expected| { warn("{}\n", .{expected}); } } } else { - for (self.case.expected_errors.span()) |expected| { + for (self.case.expected_errors.items) |expected| { if (mem.indexOf(u8, stderr, expected) == null) { warn( \\ @@ -1032,7 +1025,7 @@ pub const CompileErrorContext = struct { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } const write_src = b.addWriteFiles(); - for (case.sources.span()) |src_file| { + for (case.sources.items) |src_file| { write_src.add(src_file.filename, src_file.source); } @@ -1079,7 +1072,7 @@ pub const StandaloneContext = struct { zig_args.append("--verbose") catch unreachable; } - const run_cmd = b.addSystemCommand(zig_args.span()); + const run_cmd = b.addSystemCommand(zig_args.items); const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); log_step.step.dependOn(&run_cmd.step); @@ -1179,7 +1172,7 @@ pub const GenHContext = struct { const full_h_path = self.obj.getOutputHPath(); const actual_h = try io.readFileAlloc(b.allocator, full_h_path); - for (self.case.expected_lines.span()) |expected_line| { + for (self.case.expected_lines.items) |expected_line| { if (mem.indexOf(u8, actual_h, expected_line) == null) { warn( \\ @@ -1240,7 +1233,7 @@ pub const GenHContext = struct { } const write_src = b.addWriteFiles(); - for (case.sources.span()) |src_file| { + for (case.sources.items) |src_file| { write_src.add(src_file.filename, src_file.source); } diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index ea63e767bb..8b7811aa26 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -116,19 +116,19 @@ const known_options = [_]KnownOpt{ }, .{ .name = "E", - .ident = "pp_or_asm", + .ident = "preprocess_only", }, .{ .name = "preprocess", - .ident = "pp_or_asm", + .ident = "preprocess_only", }, .{ .name = "S", - .ident = "pp_or_asm", + .ident = "asm_only", }, .{ .name = "assemble", - .ident = "pp_or_asm", + .ident = "asm_only", }, .{ .name = "O1", @@ -346,7 +346,7 @@ pub fn main() anyerror!void { for (blacklisted_options) |blacklisted_key| { if (std.mem.eql(u8, blacklisted_key, kv.key)) continue :it_map; } - if (kv.value.Object.get("Name").?.value.String.len == 0) continue; + if (kv.value.Object.get("Name").?.String.len == 0) continue; try all_objects.append(&kv.value.Object); } } @@ -365,11 +365,11 @@ pub fn main() anyerror!void { ); for (all_objects.span()) |obj| { - const name = obj.get("Name").?.value.String; + const name = obj.get("Name").?.String; var pd1 = false; var pd2 = false; var pslash = false; - for (obj.get("Prefixes").?.value.Array.span()) |prefix_json| { + for (obj.get("Prefixes").?.Array.span()) |prefix_json| { const prefix = prefix_json.String; if (std.mem.eql(u8, prefix, "-")) { pd1 = true; @@ -465,7 +465,7 @@ const Syntax = union(enum) { self: Syntax, comptime fmt: []const u8, options: std.fmt.FormatOptions, - out_stream: var, + out_stream: anytype, ) !void { switch (self) { .multi_arg => |n| return out_stream.print(".{{.{}={}}}", .{ @tagName(self), n }), @@ -475,8 +475,8 @@ const Syntax = union(enum) { }; fn objSyntax(obj: *json.ObjectMap) Syntax { - const num_args = @intCast(u8, obj.get("NumArgs").?.value.Integer); - for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| { + const num_args = @intCast(u8, obj.get("NumArgs").?.Integer); + for (obj.get("!superclasses").?.Array.span()) |superclass_json| { const superclass = superclass_json.String; if (std.mem.eql(u8, superclass, "Joined")) { return .joined; @@ -510,19 +510,19 @@ fn objSyntax(obj: *json.ObjectMap) Syntax { return .{ .multi_arg = num_args }; } } - const name = obj.get("Name").?.value.String; + const name = obj.get("Name").?.String; if (std.mem.eql(u8, name, "")) { return .flag; } else if (std.mem.eql(u8, name, "")) { return .flag; } - const kind_def = obj.get("Kind").?.value.Object.get("def").?.value.String; + const kind_def = obj.get("Kind").?.Object.get("def").?.String; if (std.mem.eql(u8, kind_def, "KIND_FLAG")) { return .flag; } - const key = obj.get("!name").?.value.String; + const key = obj.get("!name").?.String; std.debug.warn("{} (key {}) has unrecognized superclasses:\n", .{ name, key }); - for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| { + for (obj.get("!superclasses").?.Array.span()) |superclass_json| { std.debug.warn(" {}\n", .{superclass_json.String}); } std.process.exit(1); @@ -560,15 +560,15 @@ fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool { } if (!a_match_with_eql and !b_match_with_eql) { - const a_name = a.get("Name").?.value.String; - const b_name = b.get("Name").?.value.String; + const a_name = a.get("Name").?.String; + const b_name = b.get("Name").?.String; if (a_name.len != b_name.len) { return a_name.len > b_name.len; } } - const a_key = a.get("!name").?.value.String; - const b_key = b.get("!name").?.value.String; + const a_key = a.get("!name").?.String; + const b_key = b.get("!name").?.String; return std.mem.lessThan(u8, a_key, b_key); }