Commit 3997828a61
Changed files (6)
lib
std
build
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);
},
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) {
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");
}
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);
}
}
test/standalone/c_compiler/test.c
@@ -1,25 +1,28 @@
#include <assert.h>
#include <stdio.h>
+#include <stdlib.h>
-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;
}
test/standalone/c_compiler/test.cpp
@@ -1,15 +1,33 @@
-#include <iostream>
#include <cassert>
+#include <iostream>
+
+#ifndef _LIBCPP_HAS_NO_THREADS
+#include <future>
+#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 <x ; ++i) {
+ if (x % i == 0) {
+ return false;
+ }
+ }
+ return true;
+}
class CTest {
public:
- CTest(int val) : m_val(val) {};
- virtual ~CTest() {}
+ CTest(int val) : m_val(val) {
+ tls_counter++;
+ };
+ virtual ~CTest() {}
- virtual int getVal() const { return m_val; }
- virtual void printVal() { std::cout << "val=" << m_val << std::endl; }
+ virtual int getVal() const { return m_val; }
+ virtual void printVal() { std::cout << "val=" << m_val << std::endl; }
private:
- int m_val;
+ int m_val;
};
@@ -18,16 +36,30 @@ CTest global(runtime_val); // test if global initializers are called.
int main (int argc, char *argv[])
{
- assert(global.getVal() == 456);
+ assert(global.getVal() == 456);
+ auto t = std::make_unique<CTest>(123);
+ assert(t->getVal() != 456);
+ assert(tls_counter == 2);
+ if (argc > 1) {
+ t->printVal();
+ }
+ bool ok = t->getVal() == 123;
- auto* t = new CTest(123);
- assert(t->getVal()!=456);
+ if (!ok) abort();
- if (argc>1) t->printVal();
- bool ok = t->getVal() == 123;
- delete t;
+#ifndef _LIBCPP_HAS_NO_THREADS
+ std::future<bool> fut = std::async(is_prime, 313);
+ bool ret = fut.get();
+ assert(ret);
+#endif
- if (!ok) abort();
+#ifndef _LIBCPP_NO_EXCEPTIONS
+ try {
+ throw 20;
+ } catch (int e) {
+ assert(e == 20);
+ }
+#endif
- return 0;
+ return EXIT_SUCCESS;
}