diff --git a/.github/workflows/zig-build.yml b/.github/workflows/zig-build.yml new file mode 100644 index 000000000..0ede4fbd8 --- /dev/null +++ b/.github/workflows/zig-build.yml @@ -0,0 +1,52 @@ +# This workflow is for zig-basd build/test running on multiple platforms. +# TODO: move from nightly to zig 0.12 once it is released +name: zig build + +on: [push, pull_request] + +jobs: + build: + name: ${{ matrix.os }} thr:${{ matrix.enable_threads }} rwlock::${{ matrix.enable_rwlock }} redir:${{ matrix.redirect_malloc }} dll:${{ matrix.shared_libs }} cpp::${{ matrix.enable_cplusplus }} + runs-on: ${{ matrix.os }} + + strategy: + # Deliver the feedback for all matrix combinations. + fail-fast: false + + matrix: + os: [ ubuntu-latest ] + #os: [ macos-latest, ubuntu-latest, windows-latest ] + enable_cplusplus: [ false, true ] + build_type: [ Release ] + gc_assertions: [ true ] + large_config: [ true ] + enable_threads: [ false, true ] + enable_rwlock: [ false, true ] + redirect_malloc: [ false, true ] + shared_libs: [ false, true ] + exclude: + - enable_threads: false + enable_rwlock: true + - os: macos-latest + enable_cplusplus: false + - os: ubuntu-latest + enable_cplusplus: false + + steps: + - uses: actions/checkout@v4 + - name: "Install zig" + run: | + mkdir zig && cd zig && curl https://ziglang.org/builds/zig-linux-x86_64-0.12.0-dev.1814+5c0d58b71.tar.xz | tar Jx --strip-components=1 && cd .. + - name: Build + run: > + zig/zig build + -DBUILD_SHARED_LIBS=${{ matrix.shared_libs }} + -Dbuild_tests=true + -Denable_cplusplus=${{ matrix.enable_cplusplus }} + -Denable_gc_assertions=${{ matrix.gc_assertions }} + -Denable_large_config=${{ matrix.large_config }} + -Denable_redirect_malloc=${{ matrix.redirect_malloc }} + -Denable_rwlock=${{ matrix.enable_rwlock }} + -Denable_threads=${{ matrix.enable_threads }} + -Denable_werror=true + test diff --git a/.github/workflows/zig-xbuild.yml b/.github/workflows/zig-xbuild.yml new file mode 100644 index 000000000..84392de57 --- /dev/null +++ b/.github/workflows/zig-xbuild.yml @@ -0,0 +1,29 @@ +# This workflow uses Zig and its excellent cross-compilation support to test +# compiling for multiple platforms. No tests are actually run since it would +# require emulation. +# TODO: move from nightly to zig 0.12 once it is released +name: zig cross-compile + +on: [push, pull_request] + +jobs: + build: + name: ${{ matrix.ttriple }} dll:${{ matrix.shared_libs }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + ttriple: [ aarch64-linux-musl, wasm32-wasi, x86_64-linux-gnu.2.27, x86_64-linux-musl, x86_64-windows-gnu ] + shared_libs: [ false, true ] + + steps: + - uses: actions/checkout@v4 + - name: "Install zig" + run: | + mkdir zig && cd zig && curl https://ziglang.org/builds/zig-linux-x86_64-0.12.0-dev.1814+5c0d58b71.tar.xz | tar Jx --strip-components=1 && cd .. + - name: Build + run: > + zig/zig build + -Dtarget=${{ matrix.ttriple }} + -DBUILD_SHARED_LIBS=${{ matrix.shared_libs }} diff --git a/README.md b/README.md index 0c4639d47..d88851fdb 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,29 @@ stored on the thread's stack for the duration of their lifetime. (This is arguably a longstanding bug, but it hasn't been fixed yet.) -## Installation and Portability +## Building and Installing + +There are multiple ways to build the collector: + +- CMake +- GNU autoconf +- Zig + - somewhat experimental support for using the Zig build system + - Zig is excellent at cross-compilation +- Makefile + - a static Makefile that offers a subset of configurable options +- Manual C compilation + +### Configurable Macros + +The library can be configured more precisely during the build by defining +the macros listed in [README.macros](docs/README.macros) file. + +The library is built with threads support enabled (i.e. for thread-safe +operation) by default, unless explicitly disabled: +- `--disable-threads` is passed to `./configure` +- `-Denable_threads=OFF` is passed to `cmake` +- `-Denable_threads=false` is passed to `zig build` The collector operates silently in the default configuration. In the event of issues, this can usually be changed by defining the @@ -183,53 +205,56 @@ Things don't appear to add up for a variety of reasons, most notably fragmentation losses. These are probably much more significant for the contrived program `gctest` than for your application.) -On most Unix-like platforms, the collector can be built either using a -GNU autoconf-based build infrastructure (type `./configure; make` in the -simplest case), or using CMake (see the sample below), or with a classic -makefile by itself (type `make -f Makefile.direct`). +### GNU Autoconf -Please note that the collector source repository does not contain configure +Please note that the collector source repository does not contain `configure` and similar auto-generated files, thus the full procedure of autoconf-based build of `master` branch of the collector could look like: - git clone https://github.com/ivmai/bdwgc - cd bdwgc - git clone https://github.com/ivmai/libatomic_ops - ./autogen.sh - ./configure - make -j - make check +``` sh +git clone https://github.com/ivmai/bdwgc +cd bdwgc +./autogen.sh +./configure +make -j +make check +``` Cloning of `libatomic_ops` is now optional provided the compiler supports atomic intrinsics. See [README.autoconf](docs/README.autoconf) for details. -As noted above, alternatively, the collector could be built with CMake, like -this: +### CMake - mkdir out - cd out - cmake -Dbuild_tests=ON .. - cmake --build . - ctest +```sh +mkdir out && cd out +cmake -Dbuild_tests=ON .. +cmake --build . +ctest +``` See [README.cmake](docs/README.cmake) for details. -Finally, on most targets, the collector could be built and tested directly -with a single compiler invocation, like this: +### Zig - gcc -I include -o gctest tests/gctest.c extra/gc.c && ./gctest +Building using zig is in its simplest form straight forward: -On Windows, CMake could be used to build the library as described above or -by typing `nmake -f NT_MAKEFILE`, this assumes you have Microsoft command-line -tools installed and suitably configured. See -[README.win32](docs/platforms/README.win32) for details. +```sh +zig build +``` -The library is built with threads support on (i.e. for thread-safe operation) -by default, unless `--disable-threads` is passed to `./configure` (or -`-Denable_threads=OFF` is passed to `cmake` tool). +It is possible to configure the build through the use of variables, for example +`zig build -Denable_redirect_malloc -Denable_threads=false`. Zig offers +excellent cross-compilation functionality, for example to compile the collector +for MacOS on Apple Silicon (M1 / M2 / M3): -The library could be configured more precisely during the build by defining -the macros listed in [README.macros](docs/README.macros) file. +```sh +zig build -Dtarget=aarch64-macos-gnu +``` + +Currently, a nightly version of zig 0.12 is required, which can be downloaded +from https://ziglang.org/download/ + +### Makefile Below we focus on the collector build using classic makefile. For the Makefile.direct-based process, typing `make check` instead of `make` will @@ -243,19 +268,44 @@ desktops. It may use up to about 30 MB of memory. (The multi-threaded version will use more. 64-bit versions may use more.) `make check` will also, as its last step, attempt to build and test the "cord" string library.) -Makefile.direct will generate a library libgc.a which you should link against. -Typing `make -f Makefile.direct cords` will build the cord library (libcord.a) -as well. - The GNU style build process understands the usual targets. `make check` runs a number of tests. `make install` installs at least libgc, and libcord. Try `./configure --help` to see the configuration options. It is currently not possible to exercise all combinations of build options this way. +Makefile.direct will generate a library libgc.a which you should link against. +Typing `make -f Makefile.direct cords` will build the cord library (libcord.a) +as well. + + +### Manual C compilation + +Finally, on most targets, the collector could be built and tested directly +with a single compiler invocation, like this: + +``` sh +cc -I include -o gctest tests/gctest.c extra/gc.c && ./gctest +``` + +### Windows nmake + +On Windows, CMake could be used to build the library as described above or +by typing `nmake -f NT_MAKEFILE`, this assumes you have Microsoft command-line +tools installed and suitably configured. See +[README.win32](docs/platforms/README.win32) for details. + All include files that need to be used by clients will be put in the include subdirectory. (Normally this is just gc.h. `make cords` adds "cord.h" and "ec.h".) +### Atomic ops + +The GC requires atomic ops. Most modern compilers offer builtin atomics. In case +your compiler does not, you can download and use `libatomic_ops` from +https://github.com/ivmai/libatomic_ops + +## Portability + The collector currently is designed to run essentially unmodified on machines that use a flat 32-bit or 64-bit address space. That includes the vast majority of Workstations and x86 (i386 or later) PCs. diff --git a/build.zig b/build.zig new file mode 100644 index 000000000..7915e0fdf --- /dev/null +++ b/build.zig @@ -0,0 +1,456 @@ +// THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +// OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +// +// Permission is hereby granted to use or copy this program +// for any purpose, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. + +// Compared to the CMake config, a lot more definitions and compiler options are +// hard-coded here, which is natural because build.zig is built with the Zig +// build system and Zig ships with an embedded clang. As a consequence, we don't +// have to support lots of different compilers. On the contrary, we know exactly +// what we get and so we can align on clang's capabilities rather than having to +// discover compiler capabilities. Similarly, since Zig ships libc headers for +// many platforms, we can with knowledge of the platform determine what +// capabilities should be enabled or not. + +const std = @import("std"); +const Path = std.Build.LazyPath; +const print = @import("std").debug.print; + +pub fn build(b: *std.build.Builder) void { + const optimize = b.standardOptimizeOption(.{}); + const target = b.standardTargetOptions(.{}); + const t = target.toTarget(); + var default_enable_threads = true; + if (t.isWasm()) { // matches both emscripten and wasi + default_enable_threads = false; + } + const enable_cplusplus = b.option( + bool, "enable_cplusplus", "C++ support") orelse false; + const build_shared_libs = b.option( + bool, "BUILD_SHARED_LIBS", + "Build shared lib (or static when disabled)") orelse true; +// TODO: support building cord +// const build_cord = b.option( +// bool, "build_cord", "Build cord library") orelse false; + const build_tests = b.option( + bool, "build_tests", "Build tests") orelse false; +// const enable_docs = b.option( +// bool, "enable_docs", "Build and install documentation") orelse true; + const enable_threads = b.option( + bool, "enable_threads", + "Support threads") orelse default_enable_threads; + const enable_parallel_mark = b.option( + bool, "enable_parallel_mark", + "Parallelize marking and free list construction") orelse true; + const enable_thread_local_alloc = b.option( + bool, "enable_thread_local_alloc", + "Turn on thread-local allocation optimization") orelse true; + const enable_threads_discovery = b.option( + bool, "enable_threads_discovery", + "Enable threads discovery in GC") orelse true; + const enable_rwlock = b.option( + bool, "enable_rwlock", + "Enable reader mode of the allocator lock") orelse false; +// const enable_throw_bad_alloc_library = b.option( +// bool, "enable_throw_bad_alloc_library", +// "Turn on C++ gctba library build") orelse true; + const enable_gcj_support = b.option( + bool, "enable_gcj_support", "Support for gcj") orelse true; +// const enable_sigrt_signals = b.option( +// bool, "enable_sigrt_signals", +// "Use SIGRTMIN-based signals for thread suspend / resume") orelse false; +// const enable_gc_debug = b.option( +// bool, "enable_gc_debug", +// "Support for pointer back-tracing") orelse false; +// const disable_gc_debug = b.option( +// bool, "disable_gc_debug", +// "Disable debugging like GC_dump and its callees") orelse false; + const enable_java_finalization = b.option( + bool, "enable_java_finalization", + "Support for java finalization") orelse true; + const enable_atomic_uncollectable = b.option( + bool, "enable_atomic_uncollectable", + "Support for atomic uncollectible allocation") orelse true; + const enable_redirect_malloc = b.option( + bool, "enable_redirect_malloc", + "Redirect malloc and friend to GC routines") orelse false; + const enable_disclaim = b.option( + bool, "enable_disclaim", + "Support alternative finalization interface") orelse true; + const enable_dynamic_pointer_mask = b.option( + bool, "enable_dynamic_pointer_mask", + "Support pointer mask / shift set at runtime") orelse false; + const enable_large_config = b.option( + bool, "enable_large_config", + "Optimize for large heap or root set") orelse true; + const enable_gc_assertions = b.option( + bool, "enable_gc_assertions", + "Enable collector-internal assertion checking") orelse false; + const enable_mmap = b.option( + bool, "enable_mmap", + "Use mmap instead of sbrk to expand the heap") orelse true; + const enable_munmap = b.option( + bool, "enable_munmap", + "Return page to the OS if empty for N collections") orelse true; + const enable_dynamic_loading = b.option( + bool, "enable_dynamic_loading", + "Enable tracing of dynamic library data roots") orelse true; + const enable_register_main_static_data = b.option( + bool, "enable_register_main_static_data", + "Perform the initial guess of data root sets") orelse true; + const enable_checksums = b.option( + bool, "enable_checksums", + "Report erroneously cleared dirty bits") orelse false; + const enable_werror = b.option( + bool, "enable_werror", + "Pass -Werror to the C compiler (treat warnings as errors)") + orelse false; +// const enable_single_obj_compilation = b.option( +// bool, "enable_single_obj_compilation", +// "Compile all libgc source files into single .o") orelse false; +// const disable_single_obj_compilation = b.option( +// bool, "disable_single_obj_compilation", +// "Compile each libgc source file independently") orelse false; + const enable_handle_fork = b.option( + bool, "enable_handle_fork", + "Attempt to ensure a usable collector after fork()") orelse true; + const disable_handle_fork = b.option( + bool, "disable_handle_fork", + "Prohibit installation of pthread_atfork() handlers") orelse false; +// const enable_emscripten_asyncify = b.option( +// bool, "enable_emscripten_asyncify", +// "Use Emscripten asyncify feature") orelse false; + const install_headers = b.option( + bool, "install_headers", + "Install header and pkg-config metadata files") orelse true; +// const with_libatomic_ops = b.option( +// bool, "with_libatomic_ops", +// "Use an external libatomic_ops") orelse false; +// const without_libatomic_ops = b.option( +// bool, "without_libatomic_ops", +// "Use atomic_ops.h in libatomic_ops/src") orelse false; + + var lib = b.addStaticLibrary(.{ + .name = "gc", + .target = target, + .optimize = optimize, + }); + if (build_shared_libs) { + lib = b.addSharedLibrary(.{ + .name = "gc", + .target = target, + .optimize = optimize, + }); + } + + var source_files = std.ArrayList([]const u8).init(b.allocator); + defer source_files.deinit(); + var flags = std.ArrayList([]const u8).init(b.allocator); + defer flags.deinit(); + + source_files.appendSlice(&.{ + "allchblk.c", + "alloc.c", + "blacklst.c", + "dbg_mlc.c", + "dyn_load.c", + "finalize.c", + "headers.c", + "mach_dep.c", + "malloc.c", + "mallocx.c", + "mark.c", + "mark_rts.c", + "misc.c", + "new_hblk.c", + "obj_map.c", + "os_dep.c", + "ptr_chck.c", + "reclaim.c", + "typd_mlc.c", + }) catch unreachable; + + var have_getcontext = true; + if (t.abi.isMusl()) { + have_getcontext = false; + } + if (!have_getcontext) { + flags.append("-D NO_GETCONTEXT") catch unreachable; + } + + // Always enabled + flags.append("-D ALL_INTERIOR_POINTERS") catch unreachable; + flags.append("-D NO_EXECUTE_PERMISSION") catch unreachable; + // Zig comes with clang that supports atomics so we can hardcode this + flags.append("-D GC_BUILTIN_ATOMIC") catch unreachable; + + // zig/clang support all these + flags.appendSlice(&.{ + "-fno-sanitize=undefined", + "-Wall", + "-Wextra", + "-Wno-frame-address", + "-fno-strict-aliasing", + }) catch unreachable; + + if (enable_werror) { + flags.appendSlice(&.{ +// TODO: fix this, there's a unused parameter that clang compiles about + "-Werror", + }) catch unreachable; + } + + if (enable_threads) { + source_files.appendSlice(&.{ + "gc_dlopen.c", + "pthread_start.c", + "pthread_support.c" + }) catch unreachable; + flags.append("-D GC_THREADS") catch unreachable; + flags.append("-D _REENTRANT") catch unreachable; + if (enable_parallel_mark) { + flags.append("-D PARALLEL_MARK") catch unreachable; + } + if (enable_thread_local_alloc) { + flags.append("-D THREAD_LOCAL_ALLOC") catch unreachable; + source_files.appendSlice(&.{ + "specific.c", + "thread_local_alloc.c", + }) catch unreachable; + } + if (t.os.tag == .windows) { + source_files.appendSlice(&.{ + "win32_threads.c" + }) catch unreachable; + } else { + if (t.os.tag == .macos) { + source_files.appendSlice(&.{ + "darwin_stop_world.c" + }) catch unreachable; + } else { + source_files.appendSlice(&.{ + "pthread_stop_world.c" + }) catch unreachable; + flags.append("-D HAVE_PTHREAD_SIGMASK") catch unreachable; + if (enable_handle_fork and !disable_handle_fork) { + flags.append("-D HANDLE_FORK") catch unreachable; + } + } + } + } + + if (disable_handle_fork) { + flags.append("-D NO_HANDLE_FORK") catch unreachable; + } + + if (enable_gcj_support) { + flags.append("-D GC_GCJ_SUPPORT") catch unreachable; + // CMake has this conditioned on -kfreebsd-gnu + flags.append("-D GC_ENABLE_SUSPEND_THREAD") catch unreachable; + source_files.appendSlice(&.{ + "gcj_mlc.c" + }) catch unreachable; + } + + if (enable_disclaim) { + flags.append("-D ENABLE_DISCLAIM") catch unreachable; + source_files.appendSlice(&.{ + "fnlz_mlc.c" + }) catch unreachable; + } + + if (enable_dynamic_pointer_mask) { + flags.append("-D DYNAMIC_POINTER_MASK") catch unreachable; + } + + if (enable_java_finalization) { + flags.append("-D JAVA_FINALIZATION") catch unreachable; + } + + if (enable_atomic_uncollectable) { + flags.append("-D GC_ATOMIC_UNCOLLECTABLE") catch unreachable; + } + + if (enable_redirect_malloc) { + flags.append("-D REDIRECT_MALLOC=GC_malloc") catch unreachable; + flags.append("-D IGNORE_FREE") catch unreachable; + if (t.os.tag == .windows) { + flags.append("-D REDIRECT_MALLOC_IN_HEADER") catch unreachable; + } else { + flags.append("-D GC_USE_DLOPEN_WRAP") catch unreachable; + } + } + + if (enable_munmap) { + flags.append("-D USE_MMAP") catch unreachable; + flags.append("-D USE_MUNMAP") catch unreachable; + } else if (enable_mmap) { + flags.append("-D USE_MMAP") catch unreachable; + } + + if (!enable_dynamic_loading) { + flags.append("-D IGNORE_DDYNAMIC_LOADING") catch unreachable; + } + + if (!enable_register_main_static_data) { + flags.append("-D GC_DONT_REGISTER_MAIN_STATIC_DATA") catch unreachable; + } + + + if (enable_large_config) { + flags.append("-D LARGE_CONFIG") catch unreachable; + } + + if (enable_gc_assertions) { + flags.append("-D GC_ASSERTIONS") catch unreachable; + } + + if (!enable_threads_discovery) { + flags.append("-D GC_NO_THREADS_DISCOVERY") catch unreachable; + } + + if (enable_rwlock) { + flags.append("-D USE_RWLOCK") catch unreachable; + } + + if (enable_checksums) { + if (enable_munmap or enable_threads) { + @panic("CHECKSUMS not compatible with USE_MUNMAP or threads"); + } + flags.append("-D CHECKSUMS") catch unreachable; + source_files.appendSlice(&.{ + "checksums.c" + }) catch unreachable; + } + + if (build_shared_libs) { + source_files.clearAndFree(); + source_files.appendSlice(&.{ + "extra/gc.c" + }) catch unreachable; + if (enable_threads and !(target.isDarwin() or target.isWindows())) { + flags.append("-D GC_PTHREAD_START_STANDALONE") catch unreachable; + source_files.appendSlice(&.{ + "pthread_start.c", + }) catch unreachable; + } + } + + if (target.isDarwin()) { + flags.append("-D HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID") catch unreachable; + } else if (target.isLinux()) { + flags.append("-D HAVE_PTHREAD_SETNAME_NP_WITH_TID") catch unreachable; + } else { + // TODO: hmm, this probably needs more conditions on more platforms!? + flags.append("-D HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG") catch unreachable; + } + + // Zig uses clang which ships with these so unless another sysroot / libc + // etc headers is pointed out, it is fine to hard-code enable this. + flags.append("-D HAVE_DLADDR") catch unreachable; + flags.append("-D HAVE_DL_ITERATE_PHDR") catch unreachable; + flags.append("-D HAVE_SYS_TYPES_H") catch unreachable; + flags.append("-D HAVE_UNISTD_H") catch unreachable; + // We HAVE_WCSLEN, so enabling.. + flags.append("-D GC_REQUIRE_WCSDUP") catch unreachable; + + if (optimize == .Debug) { + flags.append("-D NDEBUG") catch unreachable; + } + + if (build_shared_libs) { + flags.append("-D GC_DLL") catch unreachable; +// TODO: what to do about visibility=hidden? is it good or bad? +// flags.append("-D GC_NO_VISIBILITY") catch unreachable; +// flags.append("-D GC_VISIBILITY_HIDDEN_SET") catch unreachable; +// flags.appendSlice(&.{ +// "-fvisibility=hidden", +// }) catch unreachable; + } else { + flags.append("-D GC_NOT_DLL") catch unreachable; + } + + lib.addCSourceFiles(.{ + .files = source_files.items, + .flags = flags.items, + }); + lib.addIncludePath(.{ .path = "include" }); + lib.linkLibC(); + if (install_headers) { + installHeader(b, lib, "gc.h"); + installHeader(b, lib, "gc/cord.h"); + installHeader(b, lib, "gc/cord_pos.h"); + installHeader(b, lib, "gc/ec.h"); + installHeader(b, lib, "gc/gc_backptr.h"); + installHeader(b, lib, "gc/gc_config_macros.h"); + installHeader(b, lib, "gc/gc_disclaim.h"); + installHeader(b, lib, "gc/gc_gcj.h"); + installHeader(b, lib, "gc/gc.h"); + installHeader(b, lib, "gc/gc_inline.h"); + installHeader(b, lib, "gc/gc_mark.h"); + installHeader(b, lib, "gc/gc_pthread_redirects.h"); + installHeader(b, lib, "gc/gc_tiny_fl.h"); + installHeader(b, lib, "gc/gc_typed.h"); + installHeader(b, lib, "gc/gc_version.h"); + installHeader(b, lib, "gc/javaxfc.h"); + installHeader(b, lib, "gc/leak_detector.h"); + + if (enable_cplusplus) { + installHeader(b, lib, "gc/gc_allocator.h"); + installHeader(b, lib, "gc/gc_cpp.h"); + } + } + + b.installArtifact(lib); + + const test_step = b.step("test", "Run tests"); + if (build_tests) { + addTest(b, lib, test_step, flags, "gctest"); + addTest(b, lib, test_step, flags, "huge"); + addTest(b, lib, test_step, flags, "leak"); + addTest(b, lib, test_step, flags, "middle"); + addTest(b, lib, test_step, flags, "realloc"); + addTest(b, lib, test_step, flags, "smash"); +// addTest(b, lib, test_step, flags, "staticroots"); + addTest(b, lib, test_step, flags, "atomicops"); + addTest(b, lib, test_step, flags, "subthreadcreate"); + addTest(b, lib, test_step, flags, "disclaim_bench"); + addTest(b, lib, test_step, flags, "disclaim"); + addTest(b, lib, test_step, flags, "weakmap"); + if (enable_threads) { + addTest(b, lib, test_step, flags, "initfromthread"); + addTest(b, lib, test_step, flags, "threadleak"); + addTest(b, lib, test_step, flags, "threadkey"); + } + } +} + +fn addTest(b: *std.Build, lib: *std.Build.Step.Compile, test_step: *std.Build.Step, flags: std.ArrayList([]const u8), testname: []const u8) void { + const filename = b.allocator.alloc(u8, "tests/".len + testname.len + ".c".len) catch @panic("OOM"); + _ = std.fmt.bufPrint(filename, "tests/{s}.c", .{testname}) catch @panic("Error joining paths"); + const test_exe = b.addExecutable(.{ + .name = testname, + .optimize = lib.optimize, + .target = lib.target + }); + test_exe.addCSourceFile(.{ + .file = Path.relative(filename), + .flags = flags.items + }); + test_exe.addIncludePath(.{ .path = "include" }); + test_exe.linkLibrary(lib); + test_exe.linkLibC(); + const run_test_exe = b.addRunArtifact(test_exe); + test_step.dependOn(&run_test_exe.step); +} + +fn installHeader(b: *std.Build, lib: *std.Build.Step.Compile, hfile: []const u8) void { + const inc_path = "include/"; + const src_path = b.allocator.alloc(u8, inc_path.len + hfile.len) catch @panic("OOM"); + _ = std.fmt.bufPrint(src_path, "{s}{s}", .{inc_path, hfile}) catch @panic("Error joining paths"); + lib.installHeader(src_path, hfile); +} diff --git a/malloc.c b/malloc.c index b78ea36b6..b2a854c0c 100644 --- a/malloc.c +++ b/malloc.c @@ -679,7 +679,9 @@ GC_API void GC_CALL GC_free(void * p) void free(void * p) { -# ifndef IGNORE_FREE +# ifdef IGNORE_FREE + (void)p; // suppress -Werror warning about unused variable +# else # if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES) /* Don't bother with initialization checks. If nothing */ /* has been initialized, the check fails, and that's safe, */