build.zig raw
1 // THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
2 // OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
3 //
4 // Permission is hereby granted to use or copy this program
5 // for any purpose, provided the above notices are retained on all copies.
6 // Permission to modify the code and to distribute modified code is granted,
7 // provided the above notices are retained, and a notice that the code was
8 // modified is included with the above copyright notice.
9
10 // A script to build and test the collector using Zig build system.
11 // This script matches `CMakeLists.txt` file as much as possible.
12
13 const builtin = @import("builtin");
14 const std = @import("std");
15
16 // The Zig version here should match that in file `build.zig.zon`.
17 const zig_min_required_version = "0.14.0";
18
19 // TODO: specify `PACKAGE_VERSION` and `LIB*_VER_INFO`.
20
21 // Compared to the `cmake` script, some definitions and compiler options are
22 // hard-coded here, which is natural because `build.zig` is only built with
23 // the Zig build system and Zig ships with an embedded clang (as of zig 0.14).
24 // As a consequence, we do not have to support lots of different compilers
25 // (a notable exception is msvc target which implies use of the corresponding
26 // native compiler).
27 // And, on the contrary, we know exactly what we get and thus we can align on
28 // clang's capabilities rather than having to discover compiler capabilities.
29 // Similarly, since Zig ships `libc` headers for many platforms, we can, with
30 // the knowledge of the platform, determine what capabilities should be
31 // enabled or not.
32
33 comptime {
34 const required_ver = std.SemanticVersion.parse(zig_min_required_version)
35 catch unreachable;
36 if (builtin.zig_version.order(required_ver) == .lt) {
37 @compileError(std.fmt.comptimePrint(
38 "Zig version {} does not meet the build requirement of {}",
39 .{ builtin.zig_version, required_ver },
40 ));
41 }
42 }
43
44 pub fn build(b: *std.Build) void {
45 const optimize = b.standardOptimizeOption(.{});
46 const target = b.standardTargetOptions(.{});
47 const t = target.result;
48
49 const default_enable_threads = !t.cpu.arch.isWasm(); // emscripten/wasi
50
51 // Customize build by passing "-D<option_name>[=false]" in command line.
52 const enable_cplusplus = b.option(bool, "enable_cplusplus",
53 "C++ support") orelse false;
54 const build_shared_libs = b.option(bool, "BUILD_SHARED_LIBS",
55 "Build shared libraries (otherwise static ones)") orelse true;
56 const build_cord = b.option(bool, "build_cord",
57 "Build cord library") orelse true;
58 const cflags_extra = b.option([]const u8, "CFLAGS_EXTRA",
59 "Extra user-defined cflags") orelse "";
60 // TODO: support `enable_docs`
61 const enable_threads = b.option(bool, "enable_threads",
62 "Support threads") orelse default_enable_threads;
63 const enable_parallel_mark = b.option(bool, "enable_parallel_mark",
64 "Parallelize marking and free list construction") orelse true;
65 const enable_thread_local_alloc = b.option(bool,
66 "enable_thread_local_alloc",
67 "Turn on thread-local allocation optimization") orelse true;
68 const enable_threads_discovery = b.option(bool,
69 "enable_threads_discovery",
70 "Enable threads discovery in GC") orelse true;
71 const enable_rwlock = b.option(bool, "enable_rwlock",
72 "Enable reader mode of the allocator lock") orelse false;
73 const enable_throw_bad_alloc_library = b.option(bool,
74 "enable_throw_bad_alloc_library",
75 "Turn on C++ gctba library build") orelse true;
76 const enable_gcj_support = b.option(bool, "enable_gcj_support",
77 "Support for gcj") orelse true;
78 const enable_sigrt_signals = b.option(bool, "enable_sigrt_signals",
79 "Use SIGRTMIN-based signals for thread suspend/resume") orelse false;
80 const enable_valgrind_tracking = b.option(bool,
81 "enable_valgrind_tracking",
82 "Support tracking GC_malloc and friends for heap profiling tools")
83 orelse false;
84 const enable_gc_debug = b.option(bool, "enable_gc_debug",
85 "Support for pointer back-tracing") orelse false;
86 const disable_gc_debug = b.option(bool, "disable_gc_debug",
87 "Disable debugging like GC_dump and its callees") orelse false;
88 const enable_java_finalization = b.option(bool,
89 "enable_java_finalization",
90 "Support for java finalization") orelse true;
91 const enable_atomic_uncollectable = b.option(bool,
92 "enable_atomic_uncollectable",
93 "Support for atomic uncollectible allocation") orelse true;
94 const enable_redirect_malloc = b.option(bool, "enable_redirect_malloc",
95 "Redirect malloc and friend to GC routines") orelse false;
96 const enable_disclaim = b.option(bool, "enable_disclaim",
97 "Support alternative finalization interface") orelse true;
98 const enable_dynamic_pointer_mask = b.option(bool,
99 "enable_dynamic_pointer_mask",
100 "Support pointer mask/shift set at runtime") orelse false;
101 const enable_large_config = b.option(bool, "enable_large_config",
102 "Optimize for large heap or root set") orelse false;
103 const enable_gc_assertions = b.option(bool, "enable_gc_assertions",
104 "Enable collector-internal assertion checking") orelse false;
105 const enable_mmap = b.option(bool, "enable_mmap",
106 "Use mmap instead of sbrk to expand the heap") orelse false;
107 const enable_munmap = b.option(bool, "enable_munmap",
108 "Return page to the OS if empty for N collections") orelse true;
109 const enable_dynamic_loading = b.option(bool, "enable_dynamic_loading",
110 "Enable tracing of dynamic library data roots") orelse true;
111 const enable_register_main_static_data = b.option(bool,
112 "enable_register_main_static_data",
113 "Perform the initial guess of data root sets") orelse true;
114 const enable_checksums = b.option(bool, "enable_checksums",
115 "Report erroneously cleared dirty bits") orelse false;
116 const enable_werror = b.option(bool, "enable_werror",
117 "Pass -Werror to the C compiler (treat warnings as errors)")
118 orelse false;
119 const enable_single_obj_compilation = b.option(bool,
120 "enable_single_obj_compilation",
121 "Compile all libgc source files into single .o") orelse false;
122 const disable_single_obj_compilation = b.option(bool,
123 "disable_single_obj_compilation",
124 "Compile each libgc source file independently") orelse false;
125 const enable_handle_fork = b.option(bool, "enable_handle_fork",
126 "Attempt to ensure a usable collector after fork()") orelse true;
127 const disable_handle_fork = b.option(bool, "disable_handle_fork",
128 "Prohibit installation of pthread_atfork() handlers") orelse false;
129 // TODO: support `enable_emscripten_asyncify`
130 const install_headers = b.option(bool, "install_headers",
131 "Install header and pkg-config metadata files") orelse true;
132 // TODO: support `with_libatomic_ops`, `without_libatomic_ops`
133
134 var source_files: std.ArrayListUnmanaged([]const u8) = .empty;
135 defer source_files.deinit(b.allocator);
136 var flags: std.ArrayListUnmanaged([]const u8) = .empty;
137 defer flags.deinit(b.allocator);
138
139 // Always enabled.
140 flags.append(b.allocator, "-D ALL_INTERIOR_POINTERS") catch unreachable;
141 flags.append(b.allocator, "-D NO_EXECUTE_PERMISSION") catch unreachable;
142
143 // Output all warnings.
144 flags.appendSlice(b.allocator, &.{
145 "-Wall",
146 "-Wextra",
147 "-Wpedantic",
148 }) catch unreachable;
149
150 // Disable MS `crt` security warnings reported e.g. for `getenv`, `strcpy`.
151 if (t.abi == .msvc) {
152 flags.append(b.allocator,
153 "-D _CRT_SECURE_NO_DEPRECATE") catch unreachable;
154 }
155
156 source_files.appendSlice(b.allocator, &.{
157 "allchblk.c",
158 "alloc.c",
159 "blacklst.c",
160 "dbg_mlc.c",
161 "dyn_load.c",
162 "finalize.c",
163 "headers.c",
164 "mach_dep.c",
165 "malloc.c",
166 "mallocx.c",
167 "mark.c",
168 "mark_rts.c",
169 "misc.c",
170 "new_hblk.c",
171 "os_dep.c",
172 "ptr_chck.c",
173 "reclaim.c",
174 "typd_mlc.c",
175 }) catch unreachable;
176
177 if (enable_threads) {
178 flags.append(b.allocator, "-D GC_THREADS") catch unreachable;
179 if (enable_parallel_mark) {
180 flags.append(b.allocator, "-D PARALLEL_MARK") catch unreachable;
181 }
182 if (t.os.tag != .windows) { // assume `pthreads`
183 // TODO: support cygwin when supported by zig
184 // Zig comes with clang which supports GCC atomic intrinsics.
185 flags.append(b.allocator,
186 "-D GC_BUILTIN_ATOMIC") catch unreachable;
187 // TODO: define and use `THREADDLLIBS_LIST`
188 source_files.appendSlice(b.allocator, &.{
189 "gc_dlopen.c",
190 "pthread_start.c",
191 "pthread_support.c",
192 }) catch unreachable;
193 if (t.os.tag.isDarwin()) {
194 source_files.append(b.allocator,
195 "darwin_stop_world.c") catch unreachable;
196 } else {
197 source_files.append(b.allocator,
198 "pthread_stop_world.c") catch unreachable;
199 }
200 // Common defines for POSIX platforms.
201 flags.append(b.allocator, "-D _REENTRANT") catch unreachable;
202 // TODO: some targets might need `_PTHREADS` defined too.
203 if (enable_thread_local_alloc) {
204 flags.append(b.allocator,
205 "-D THREAD_LOCAL_ALLOC") catch unreachable;
206 source_files.appendSlice(b.allocator, &.{
207 "specific.c",
208 "thread_local_alloc.c",
209 }) catch unreachable;
210 }
211 // Message for clients: Explicit `GC_INIT` call may be required.
212 if (enable_handle_fork and !disable_handle_fork) {
213 flags.append(b.allocator, "-D HANDLE_FORK") catch unreachable;
214 }
215 if (enable_sigrt_signals) {
216 flags.append(b.allocator,
217 "-D GC_USESIGRT_SIGNALS") catch unreachable;
218 }
219 } else {
220 // Assume the GCC atomic intrinsics are supported.
221 flags.append(b.allocator,
222 "-D GC_BUILTIN_ATOMIC") catch unreachable;
223 if (enable_thread_local_alloc
224 and (enable_parallel_mark or !build_shared_libs)) {
225 // Imply `THREAD_LOCAL_ALLOC` unless `GC_DLL`.
226 flags.append(b.allocator,
227 "-D THREAD_LOCAL_ALLOC") catch unreachable;
228 source_files.append(b.allocator,
229 "thread_local_alloc.c") catch unreachable;
230 }
231 flags.append(b.allocator,
232 "-D EMPTY_GETENV_RESULTS") catch unreachable;
233 source_files.appendSlice(b.allocator, &.{
234 // Add `pthread_start.c` file just in case client defines
235 // `GC_WIN32_PTHREADS` macro.
236 "pthread_start.c",
237 "pthread_support.c",
238 "win32_threads.c",
239 }) catch unreachable;
240 }
241 }
242
243 // TODO: define/use `NEED_LIB_RT`
244
245 if (disable_handle_fork) {
246 flags.append(b.allocator, "-D NO_HANDLE_FORK") catch unreachable;
247 }
248
249 if (enable_gcj_support) {
250 flags.append(b.allocator, "-D GC_GCJ_SUPPORT") catch unreachable;
251 // TODO: do not define `GC_ENABLE_SUSPEND_THREAD` on kFreeBSD
252 // if `enable_thread_local_alloc` (a workaround for some bug).
253 flags.append(b.allocator,
254 "-D GC_ENABLE_SUSPEND_THREAD") catch unreachable;
255 source_files.append(b.allocator, "gcj_mlc.c") catch unreachable;
256 }
257
258 if (enable_disclaim) {
259 flags.append(b.allocator, "-D ENABLE_DISCLAIM") catch unreachable;
260 source_files.append(b.allocator, "fnlz_mlc.c") catch unreachable;
261 }
262
263 if (enable_dynamic_pointer_mask) {
264 flags.append(b.allocator, "-D DYNAMIC_POINTER_MASK") catch unreachable;
265 }
266
267 if (enable_java_finalization) {
268 flags.append(b.allocator, "-D JAVA_FINALIZATION") catch unreachable;
269 }
270
271 if (enable_atomic_uncollectable) {
272 flags.append(b.allocator,
273 "-D GC_ATOMIC_UNCOLLECTABLE") catch unreachable;
274 }
275
276 if (enable_valgrind_tracking) {
277 flags.append(b.allocator, "-D VALGRIND_TRACKING") catch unreachable;
278 }
279
280 if (enable_gc_debug) {
281 flags.append(b.allocator, "-D DBG_HDRS_ALL") catch unreachable;
282 flags.append(b.allocator, "-D KEEP_BACK_PTRS") catch unreachable;
283 if (t.os.tag == .linux) {
284 flags.append(b.allocator, "-D MAKE_BACK_GRAPH") catch unreachable;
285 // TODO: do not define `SAVE_CALL_COUNT` for e2k
286 flags.append(b.allocator,
287 "-D SAVE_CALL_COUNT=8") catch unreachable;
288 source_files.append(b.allocator, "backgraph.c") catch unreachable;
289 }
290 }
291
292 if (disable_gc_debug) {
293 flags.append(b.allocator, "-D NO_DEBUGGING") catch unreachable;
294 }
295 if (optimize != .Debug) {
296 flags.append(b.allocator, "-D NDEBUG") catch unreachable;
297 }
298
299 if (enable_redirect_malloc) {
300 if (enable_gc_debug) {
301 flags.append(b.allocator,
302 "-D REDIRECT_MALLOC=GC_debug_malloc_replacement")
303 catch unreachable;
304 flags.append(b.allocator,
305 "-D REDIRECT_REALLOC=GC_debug_realloc_replacement")
306 catch unreachable;
307 flags.append(b.allocator,
308 "-D REDIRECT_FREE=GC_debug_free") catch unreachable;
309 } else {
310 flags.append(b.allocator,
311 "-D REDIRECT_MALLOC=GC_malloc") catch unreachable;
312 }
313 if (t.os.tag == .windows) {
314 flags.append(b.allocator,
315 "-D REDIRECT_MALLOC_IN_HEADER") catch unreachable;
316 } else {
317 flags.append(b.allocator,
318 "-D GC_USE_DLOPEN_WRAP") catch unreachable;
319 }
320 }
321
322 if (enable_mmap or enable_munmap) {
323 flags.append(b.allocator, "-D USE_MMAP") catch unreachable;
324 }
325
326 if (enable_munmap) {
327 flags.append(b.allocator, "-D USE_MUNMAP") catch unreachable;
328 }
329
330 if (!enable_dynamic_loading) {
331 flags.append(b.allocator,
332 "-D IGNORE_DDYNAMIC_LOADING") catch unreachable;
333 }
334
335 if (!enable_register_main_static_data) {
336 flags.append(b.allocator,
337 "-D GC_DONT_REGISTER_MAIN_STATIC_DATA") catch unreachable;
338 }
339
340 if (enable_large_config) {
341 flags.append(b.allocator, "-D LARGE_CONFIG") catch unreachable;
342 }
343
344 if (enable_gc_assertions) {
345 flags.append(b.allocator, "-D GC_ASSERTIONS") catch unreachable;
346 }
347
348 if (!enable_threads_discovery) {
349 flags.append(b.allocator,
350 "-D GC_NO_THREADS_DISCOVERY") catch unreachable;
351 }
352
353 if (enable_rwlock) {
354 flags.append(b.allocator, "-D USE_RWLOCK") catch unreachable;
355 }
356
357 if (enable_checksums) {
358 if (enable_munmap or enable_threads) {
359 @panic("CHECKSUMS not compatible with USE_MUNMAP or threads");
360 }
361 flags.append(b.allocator, "-D CHECKSUMS") catch unreachable;
362 source_files.append(b.allocator, "checksums.c") catch unreachable;
363 }
364
365 if (enable_werror) {
366 flags.append(b.allocator, "-Werror") catch unreachable;
367 }
368
369 if (enable_single_obj_compilation
370 or (build_shared_libs and !disable_single_obj_compilation)) {
371 source_files.clearAndFree(b.allocator);
372 source_files.append(b.allocator, "extra/gc.c") catch unreachable;
373 if (enable_threads and !t.os.tag.isDarwin() and t.os.tag != .windows) {
374 flags.append(b.allocator,
375 "-D GC_PTHREAD_START_STANDALONE") catch unreachable;
376 source_files.append(b.allocator,
377 "pthread_start.c") catch unreachable;
378 }
379 }
380
381 // Add implementation of `backtrace` and `backtrace_symbols`.
382 if (t.abi == .msvc) {
383 source_files.append(b.allocator, "extra/msvc_dbg.c") catch unreachable;
384 }
385
386 // TODO: declare that the libraries do not refer to external symbols
387 // of `build_shared_libs`.
388
389 // `zig cc` supports this flag.
390 flags.appendSlice(b.allocator, &.{
391 // TODO: `-Wno-unused-command-line-argument`
392 // Prevent "__builtin_return_address with nonzero argument is unsafe".
393 "-Wno-frame-address",
394 }) catch unreachable;
395
396 if (build_shared_libs) {
397 flags.append(b.allocator, "-D GC_DLL") catch unreachable;
398 if (t.abi == .msvc) {
399 // TODO: depend on `user32.lib` file instead
400 flags.append(b.allocator,
401 "-D DONT_USE_USER32_DLL") catch unreachable;
402 } else {
403 // `zig cc` supports these flags.
404 flags.append(b.allocator,
405 "-D GC_VISIBILITY_HIDDEN_SET") catch unreachable;
406 flags.append(b.allocator,
407 "-fvisibility=hidden") catch unreachable;
408 }
409 } else {
410 flags.append(b.allocator, "-D GC_NOT_DLL") catch unreachable;
411 if (t.os.tag == .windows) {
412 // Do not require the clients to link with `user32` system library.
413 flags.append(b.allocator,
414 "-D DONT_USE_USER32_DLL") catch unreachable;
415 }
416 }
417
418 // Note: Zig uses clang which ships with these so, unless another
419 // sysroot/libc, etc. headers location is pointed out, it is fine to
420 // hard-code enable this.
421 // `-U GC_MISSING_EXECINFO_H`
422 // `-U GC_NO_SIGSETJMP`
423 flags.append(b.allocator, "-D HAVE_SYS_TYPES_H") catch unreachable;
424
425 if (t.abi == .msvc) {
426 // To workaround "extension used" error reported
427 // for `__try`/`__finally`.
428 flags.append(b.allocator, "-D NO_SEH_AVAILABLE") catch unreachable;
429 } else {
430 flags.append(b.allocator, "-D HAVE_UNISTD_H") catch unreachable;
431 }
432
433 const have_getcontext = !t.abi.isMusl() and t.os.tag != .windows;
434 if (!have_getcontext) {
435 flags.append(b.allocator, "-D NO_GETCONTEXT") catch unreachable;
436 }
437
438 if (!t.os.tag.isDarwin() and t.os.tag != .windows) {
439 // `dl_iterate_phdr` exists (as a strong symbol).
440 flags.append(b.allocator, "-D HAVE_DL_ITERATE_PHDR") catch unreachable;
441 if (enable_threads) {
442 // `pthread_sigmask` and `sigset_t` are available and needed.
443 flags.append(b.allocator,
444 "-D HAVE_PTHREAD_SIGMASK") catch unreachable;
445 }
446 }
447
448 // Build with `GC_wcsdup` support (`wcslen` is available).
449 flags.append(b.allocator, "-D GC_REQUIRE_WCSDUP") catch unreachable;
450
451 // `pthread_setname_np`, if available, may have 1, 2 or 3 arguments.
452 if (t.os.tag.isDarwin()) {
453 flags.append(b.allocator, "-D HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID")
454 catch unreachable;
455 } else if (t.os.tag == .linux) {
456 flags.append(b.allocator,
457 "-D HAVE_PTHREAD_SETNAME_NP_WITH_TID") catch unreachable;
458 } else {
459 // TODO: support `HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG`
460 // and `HAVE_PTHREAD_SET_NAME_NP` targets.
461 }
462
463 if (t.os.tag != .windows) {
464 // Define to use `dladdr` function (used for debugging).
465 flags.append(b.allocator, "-D HAVE_DLADDR") catch unreachable;
466 }
467
468 // TODO: as of zig 0.14, exception.h and getsect.h are not provided
469 // by zig itself for Darwin target.
470 if (t.os.tag.isDarwin() and !target.query.isNative()) {
471 flags.append(b.allocator,
472 "-D MISSING_MACH_O_GETSECT_H") catch unreachable;
473 flags.append(b.allocator,
474 "-D NO_MPROTECT_VDB") catch unreachable;
475 }
476
477 if (enable_cplusplus and enable_werror) {
478 if (build_shared_libs and t.os.tag == .windows or t.abi == .msvc) {
479 // Avoid "replacement operator new[] cannot be declared inline"
480 // warnings.
481 flags.append(b.allocator,
482 "-Wno-inline-new-delete") catch unreachable;
483 }
484 if (t.abi == .msvc) {
485 // TODO: as of zig 0.14,
486 // "argument unused during compilation: -nostdinc++" warning is
487 // reported if using MS compiler.
488 flags.append(b.allocator, "-Wno-unused-command-line-argument")
489 catch unreachable;
490 }
491 }
492
493 // Extra user-defined flags (if any) to pass to the compiler.
494 if (cflags_extra.len > 0) {
495 // Split it up on a space and append each part to flags separately.
496 var tokenizer = std.mem.tokenizeScalar(u8, cflags_extra, ' ');
497 while (tokenizer.next()) |token| {
498 flags.append(b.allocator, token) catch unreachable;
499 }
500 }
501
502 // TODO: convert `VER_INFO` values to `VERSION`, `SOVERSION` ones
503 const gc = b.addLibrary(.{
504 .linkage = if (build_shared_libs) .dynamic else .static,
505 .name = "gc",
506 .root_module = b.createModule(.{
507 .target = target,
508 .optimize = optimize,
509 }),
510 });
511 gc.addCSourceFiles(.{
512 .files = source_files.items,
513 .flags = flags.items,
514 });
515 gc.addIncludePath(b.path("include"));
516 gc.linkLibC();
517
518 var gccpp: *std.Build.Step.Compile = undefined;
519 var gctba: *std.Build.Step.Compile = undefined;
520 if (enable_cplusplus) {
521 gccpp = b.addLibrary(.{
522 .linkage = if (build_shared_libs) .dynamic else .static,
523 .name = "gccpp",
524 .root_module = b.createModule(.{
525 .target = target,
526 .optimize = optimize,
527 }),
528 });
529 gccpp.addCSourceFiles(.{
530 .files = &.{
531 "gc_badalc.cc",
532 "gc_cpp.cc",
533 },
534 .flags = flags.items,
535 });
536 gccpp.addIncludePath(b.path("include"));
537 gccpp.linkLibrary(gc);
538 linkLibCpp(gccpp);
539 if (enable_throw_bad_alloc_library) {
540 // The same as `gccpp` but contains only `gc_badalc`.
541 gctba = b.addLibrary(.{
542 .linkage = if (build_shared_libs) .dynamic else .static,
543 .name = "gctba",
544 .root_module = b.createModule(.{
545 .target = target,
546 .optimize = optimize,
547 }),
548 });
549 gctba.addCSourceFiles(.{
550 .files = &.{
551 "gc_badalc.cc",
552 },
553 .flags = flags.items,
554 });
555 gctba.addIncludePath(b.path("include"));
556 gctba.linkLibrary(gc);
557 linkLibCpp(gctba);
558 }
559 }
560
561 var cord: *std.Build.Step.Compile = undefined;
562 if (build_cord) {
563 cord = b.addLibrary(.{
564 .linkage = if (build_shared_libs) .dynamic else .static,
565 .name = "cord",
566 .root_module = b.createModule(.{
567 .target = target,
568 .optimize = optimize,
569 })
570 });
571 cord.addCSourceFiles(.{
572 .files = &.{
573 "cord/cordbscs.c",
574 "cord/cordprnt.c",
575 "cord/cordxtra.c",
576 },
577 .flags = flags.items,
578 });
579 cord.addIncludePath(b.path("include"));
580 cord.linkLibrary(gc);
581 cord.linkLibC();
582 }
583
584 if (install_headers) {
585 installHeader(b, gc, "gc.h");
586 installHeader(b, gc, "gc/gc.h");
587 installHeader(b, gc, "gc/gc_backptr.h");
588 installHeader(b, gc, "gc/gc_config_macros.h");
589 installHeader(b, gc, "gc/gc_inline.h");
590 installHeader(b, gc, "gc/gc_mark.h");
591 installHeader(b, gc, "gc/gc_tiny_fl.h");
592 installHeader(b, gc, "gc/gc_typed.h");
593 installHeader(b, gc, "gc/gc_version.h");
594 installHeader(b, gc, "gc/javaxfc.h");
595 installHeader(b, gc, "gc/leak_detector.h");
596 if (enable_cplusplus) {
597 installHeader(b, gccpp, "gc_cpp.h");
598 installHeader(b, gccpp, "gc/gc_allocator.h");
599 installHeader(b, gccpp, "gc/gc_cpp.h");
600 if (enable_throw_bad_alloc_library) {
601 // The same headers as `gccpp` library has.
602 installHeader(b, gctba, "gc_cpp.h");
603 installHeader(b, gctba, "gc/gc_allocator.h");
604 installHeader(b, gctba, "gc/gc_cpp.h");
605 }
606 }
607 if (enable_disclaim) {
608 installHeader(b, gc, "gc/gc_disclaim.h");
609 }
610 if (enable_gcj_support) {
611 installHeader(b, gc, "gc/gc_gcj.h");
612 }
613 if (enable_threads) {
614 installHeader(b, gc, "gc/gc_pthread_redirects.h");
615 }
616 if (build_cord) {
617 installHeader(b, cord, "gc/cord.h");
618 installHeader(b, cord, "gc/cord_pos.h");
619 installHeader(b, cord, "gc/ec.h");
620 }
621 // TODO: compose and install `bdw-gc.pc` and `pkgconfig`.
622 }
623
624 b.installArtifact(gc);
625 if (enable_cplusplus) {
626 b.installArtifact(gccpp);
627 if (enable_throw_bad_alloc_library) {
628 b.installArtifact(gctba);
629 }
630 }
631 if (build_cord) {
632 b.installArtifact(cord);
633 }
634
635 // Note: there is no `build_tests` option, as the tests are built
636 // only if `test` step is requested.
637 const test_step = b.step("test", "Run tests");
638 addTest(b, gc, test_step, flags, "gctest", "tests/gctest.c");
639 if (build_cord) {
640 addTestExt(b, gc, cord, test_step, flags,
641 "cordtest", "cord/tests/cordtest.c");
642 // TODO: add `de` test (Windows only)
643 }
644 addTest(b, gc, test_step, flags, "hugetest", "tests/huge.c");
645 addTest(b, gc, test_step, flags, "leaktest", "tests/leak.c");
646 addTest(b, gc, test_step, flags, "middletest", "tests/middle.c");
647 addTest(b, gc, test_step, flags, "realloctest", "tests/realloc.c");
648 addTest(b, gc, test_step, flags, "smashtest", "tests/smash.c");
649 // TODO: add `staticroots` test
650 if (enable_gc_debug) {
651 addTest(b, gc, test_step, flags, "tracetest", "tests/trace.c");
652 }
653 if (enable_threads) {
654 addTest(b, gc, test_step, flags, "atomicopstest", "tests/atomicops.c");
655 addTest(b, gc, test_step, flags,
656 "initfromthreadtest", "tests/initfromthread.c");
657 addTest(b, gc, test_step, flags,
658 "subthreadcreatetest", "tests/subthreadcreate.c");
659 addTest(b, gc, test_step, flags,
660 "threadleaktest", "tests/threadleak.c");
661 if (t.os.tag != .windows) {
662 addTest(b, gc, test_step, flags,
663 "threadkeytest", "tests/threadkey.c");
664 }
665 }
666 if (enable_cplusplus) {
667 addTestExt(b, gc, gccpp, test_step, flags, "cpptest", "tests/cpp.cc");
668 if (enable_throw_bad_alloc_library) {
669 addTestExt(b, gc, gctba, test_step, flags,
670 "treetest", "tests/tree.cc");
671 }
672 }
673 if (enable_disclaim) {
674 addTest(b, gc, test_step, flags,
675 "disclaim_bench", "tests/disclaim_bench.c");
676 addTest(b, gc, test_step, flags, "disclaimtest", "tests/disclaim.c");
677 addTest(b, gc, test_step, flags, "weakmaptest", "tests/weakmap.c");
678 }
679 }
680
681 fn linkLibCpp(lib: *std.Build.Step.Compile) void {
682 const t = lib.rootModuleTarget();
683 if (t.abi == .msvc) {
684 // TODO: as of zig 0.14, "unable to build libcxxabi" warning is
685 // reported if linking C++ code using MS compiler.
686 lib.linkLibC();
687 } else {
688 lib.linkLibCpp();
689 }
690 }
691
692 fn addTest(b: *std.Build, gc: *std.Build.Step.Compile,
693 test_step: *std.Build.Step,
694 flags: std.ArrayListUnmanaged([]const u8), testname: []const u8,
695 filename: []const u8) void {
696 addTestExt(b, gc, null, test_step, flags, testname, filename);
697 }
698
699 fn addTestExt(b: *std.Build, gc: *std.Build.Step.Compile,
700 lib2: ?*std.Build.Step.Compile, test_step: *std.Build.Step,
701 flags: std.ArrayListUnmanaged([]const u8), testname: []const u8,
702 filename: []const u8) void {
703 const test_exe = b.addExecutable(.{
704 .name = testname,
705 .root_module = b.createModule(.{
706 .optimize = gc.root_module.optimize.?,
707 .target = gc.root_module.resolved_target.?
708 })
709 });
710 test_exe.addCSourceFile(.{
711 .file = b.path(filename),
712 .flags = flags.items
713 });
714 test_exe.addIncludePath(b.path("include"));
715 test_exe.linkLibrary(gc);
716 if (lib2 != null) {
717 test_exe.linkLibrary(lib2.?);
718 }
719 test_exe.linkLibC();
720 const run_test_exe = b.addRunArtifact(test_exe);
721 test_step.dependOn(&run_test_exe.step);
722 }
723
724 fn installHeader(b: *std.Build, lib: *std.Build.Step.Compile,
725 hfile: []const u8) void {
726 const src_path = b.pathJoin(&.{ "include", hfile });
727 lib.installHeader(b.path(src_path), hfile);
728 }
729