diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index e00fe3deb6..8cfbc3e89a 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -1,6 +1,7 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const build = std.build; +const CrossTarget = std.zig.CrossTarget; const Step = build.Step; const Builder = build.Builder; const LibExeObjStep = build.LibExeObjStep; @@ -142,6 +143,23 @@ pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; } +/// Returns true if the step could be run, otherwise false +pub fn isRunnable( + self: *RunStep, +) bool { + for (self.argv.items) |arg| { + switch (arg) { + .artifact => |artifact| { + _ = self.getExternalExecutor(artifact) catch { + return false; + }; + }, + else => {}, + } + } + return true; +} + pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; } @@ -154,6 +172,57 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { }; } +fn getExternalExecutor(self: *RunStep, artifact: *LibExeObjStep) !?[]const u8 { + const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; + const executor = self.builder.host.getExternalExecutor(artifact.target_info, .{ + .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, + .link_libc = artifact.is_linking_libc, + }); + switch (executor) { + .bad_dl, .bad_os_or_cpu => { + return error.NoExecutable; + }, + .native => { + return null; + }, + .rosetta => { + if (self.builder.enable_rosetta) { + return null; + } else { + return error.RosettaNotEnabled; + } + }, + .qemu => |bin_name| { + if (self.builder.enable_qemu) { + return bin_name; + } else { + return error.QemuNotEnabled; + } + }, + .wine => |bin_name| { + if (self.builder.enable_wine) { + return bin_name; + } else { + return error.WineNotEnabled; + } + }, + .wasmtime => |bin_name| { + if (self.builder.enable_wasmtime) { + return bin_name; + } else { + return error.WasmtimeNotEnabled; + } + }, + .darling => |bin_name| { + if (self.builder.enable_darling) { + return bin_name; + } else { + return error.DarlingNotEnabled; + } + }, + } +} + fn make(step: *Step) !void { const self = @fieldParentPtr(RunStep, "step", step); @@ -169,6 +238,9 @@ fn make(step: *Step) !void { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(artifact); } + if (try self.getExternalExecutor(artifact)) |executor| { + try argv_list.append(executor); + } const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder); try argv_list.append(executable_path); }, diff --git a/src/Compilation.zig b/src/Compilation.zig index 9126289804..3eb527b612 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1180,6 +1180,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (must_single_thread and !single_threaded) { return error.TargetRequiresSingleThreaded; } + if (!single_threaded and options.link_libcpp) { + if (options.target.cpu.arch.isARM()) { + log.warn( + \\libc++ does not work on multi-threaded ARM yet. + \\For more details: https://github.com/ziglang/zig/issues/6573 + , .{}); + return error.TargetRequiresSingleThreaded; + } + } const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: { var buf = std.ArrayList(u8).init(arena); @@ -3803,6 +3812,10 @@ pub fn addCCArgs( try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); try argv.append("-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS"); + + if (comp.bin_file.options.single_threaded) { + try argv.append("-D_LIBCPP_HAS_NO_THREADS"); + } else {} } if (comp.bin_file.options.link_libunwind) { diff --git a/src/libcxx.zig b/src/libcxx.zig index b2974e24c9..a5ab242339 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -128,6 +128,12 @@ pub fn buildLibCXX(comp: *Compilation) !void { continue; if (std.mem.startsWith(u8, cxx_src, "src/support/ibm/") and target.os.tag != .zos) continue; + if (comp.bin_file.options.single_threaded) { + if (std.mem.startsWith(u8, cxx_src, "src/support/win32/thread_win32.cpp")) { + continue; + } + try cflags.append("-D_LIBCPP_HAS_NO_THREADS"); + } try cflags.append("-DNDEBUG"); try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); @@ -145,8 +151,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { } if (target.os.tag == .wasi) { - // WASI doesn't support thread and exception yet. - try cflags.append("-D_LIBCPP_HAS_NO_THREADS"); + // WASI doesn't support exceptions yet. try cflags.append("-fno-exceptions"); } @@ -264,13 +269,20 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { var cflags = std.ArrayList([]const u8).init(arena); if (target.os.tag == .wasi) { - // WASI doesn't support thread and exception yet. - if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp") or - std.mem.startsWith(u8, cxxabi_src, "src/cxa_exception.cpp") or + // WASI doesn't support exceptions yet. + if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_exception.cpp") or std.mem.startsWith(u8, cxxabi_src, "src/cxa_personality.cpp")) continue; - try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); try cflags.append("-fno-exceptions"); + } + + // WASM targets are single threaded. + if (comp.bin_file.options.single_threaded) { + if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) { + continue; + } + try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); + try cflags.append("-D_LIBCPP_HAS_NO_THREADS"); } else { try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); } diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig index ad500c3eb3..5fb39d5a94 100644 --- a/test/standalone/c_compiler/build.zig +++ b/test/standalone/c_compiler/build.zig @@ -1,20 +1,21 @@ const std = @import("std"); -const builtin = @import("builtin"); const Builder = std.build.Builder; const CrossTarget = std.zig.CrossTarget; -// TODO integrate this with the std.build executor API -fn isRunnableTarget(t: CrossTarget) bool { - if (t.isNative()) return true; - - return (t.getOsTag() == builtin.os.tag and - t.getCpuArch() == builtin.cpu.arch); -} - pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const target = b.standardTargetOptions(.{}); + const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false; + const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false; + const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; + const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; + const single_threaded = b.option(bool, "single-threaded", "Test single threaded mode") orelse false; + b.enable_wine = is_wine_enabled; + b.enable_qemu = is_qemu_enabled; + b.enable_wasmtime = is_wasmtime_enabled; + b.enable_darling = is_darling_enabled; + const test_step = b.step("test", "Test the program"); const exe_c = b.addExecutable("test_c", null); @@ -29,9 +30,16 @@ pub fn build(b: *Builder) void { exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{}); exe_cpp.setBuildMode(mode); exe_cpp.setTarget(target); - exe_cpp.linkSystemLibrary("c++"); + exe_cpp.linkLibCpp(); + exe_cpp.single_threaded = single_threaded; + const os_tag = target.getOsTag(); + // macos C++ exceptions could be compiled, but not being catched, + // additional support is required, possibly unwind + DWARF CFI + if (target.getCpuArch().isWasm() or os_tag == .macos) { + exe_cpp.defineCMacro("_LIBCPP_NO_EXCEPTIONS", null); + } - switch (target.getOsTag()) { + switch (os_tag) { .windows => { // https://github.com/ziglang/zig/issues/8531 exe_cpp.want_lto = false; @@ -44,13 +52,17 @@ pub fn build(b: *Builder) void { else => {}, } - if (isRunnableTarget(target)) { - const run_c_cmd = exe_c.run(); + const run_c_cmd = exe_c.run(); + if (run_c_cmd.isRunnable()) { test_step.dependOn(&run_c_cmd.step); - const run_cpp_cmd = exe_cpp.run(); - test_step.dependOn(&run_cpp_cmd.step); } else { test_step.dependOn(&exe_c.step); + } + + const run_cpp_cmd = exe_cpp.run(); + if (run_cpp_cmd.isRunnable()) { + test_step.dependOn(&run_cpp_cmd.step); + } else { test_step.dependOn(&exe_cpp.step); } } diff --git a/test/standalone/c_compiler/test.c b/test/standalone/c_compiler/test.c index 842a63fc67..da0a137798 100644 --- a/test/standalone/c_compiler/test.c +++ b/test/standalone/c_compiler/test.c @@ -1,25 +1,28 @@ #include #include +#include -typedef struct { - int val; +typedef struct { + int val; } STest; int getVal(STest* data) { return data->val; } int main (int argc, char *argv[]) { - STest* data = (STest*)malloc(sizeof(STest)); - data->val = 123; + STest* data = (STest*)malloc(sizeof(STest)); + data->val = 123; - assert(getVal(data) != 456); - int ok = (getVal(data) == 123); + assert(getVal(data) != 456); + int ok = (getVal(data) == 123); - if (argc>1) fprintf(stdout, "val=%d\n", data->val); + if (argc > 1) { + fprintf(stdout, "val=%d\n", data->val); + } - free(data); + free(data); - if (!ok) abort(); + if (!ok) abort(); - return 0; + return EXIT_SUCCESS; } diff --git a/test/standalone/c_compiler/test.cpp b/test/standalone/c_compiler/test.cpp index 8c533d02cc..71b0c05996 100644 --- a/test/standalone/c_compiler/test.cpp +++ b/test/standalone/c_compiler/test.cpp @@ -1,15 +1,33 @@ -#include #include +#include + +#ifndef _LIBCPP_HAS_NO_THREADS +#include +#endif + +thread_local unsigned int tls_counter = 1; + +// a non-optimized way of checking for prime numbers: +bool is_prime(int x) { + for (int i = 2; i