├── .gitignore ├── README.md ├── build.zig ├── build.zig.zon ├── build_c_app.zig ├── build_c_shared_lib.zig ├── build_c_static_lib.zig ├── build_test_zmath.zig ├── build_zig_app.zig ├── build_zig_app_shared.zig ├── build_zig_app_static.zig ├── include └── zmath.h ├── src ├── c_app.c ├── zig_app.zig ├── zig_c_wrapper.zig ├── zmath.c ├── zmath.zig └── zmath_ext.zig └── tests └── test_zmath.zig /.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache/ 2 | zig-out/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Understanding How Zig and C Interact 2 | 3 | A journey to understanding how Zig interacts with C and how someone not 4 | well-versed in C can leverage the power of third-party C libraries. 5 | 6 | ## What This Doesn't Cover 7 | 8 | - Using Zig in C 9 | - Using C 10 | - C at all 11 | 12 | ## TOC 13 | 14 | - [Utilizing This Project](#utilizing-this-project) 15 | - [Zig Build Commands](#zig-build-commands) 16 | - [The Journey](#the-journey) 17 | - [Create a Simple C application](#create-a-simple-c-application) 18 | - [Compiling the application using `zig cc`](#compiling-the-application-using-zig-cc) 19 | - [Leverage Zig's build system to build the C application](#leverage-zigs-build-system-to-build-the-c-application) 20 | - [Create a Zig application that links to the C library](#create-a-zig-application-that-links-to-the-c-library) 21 | - [Using Zig to build a C Library](#using-zig-to-build-a-c-library) 22 | - [Create a Zig wrapper around a C Function](#create-a-zig-wrapper-around-a-c-function) 23 | - [Linking to the Static/Shared Library](#linking-to-the-staticshared-library) 24 | - [Side Quests](#side-quests) 25 | - [Testing C code in Zig](#testing-c-code-in-zig) 26 | - [Resources](#resources) 27 | - [Thanks](#thanks) 28 | 29 | ## Utilizing this Project 30 | 31 | ### Zig Build Commands 32 | ```sh 33 | zig build [steps] [options] 34 | 35 | Steps: 36 | install (default) Copy build artifacts to prefix path 37 | uninstall Remove build artifacts from prefix path 38 | c_app Run a C application built with Zig's build system. 39 | zig_app Run a Zig application linked to C source code. 40 | zmath_static Create a static library from C source code. 41 | zmath_shared Create a shared library from C source code. 42 | zig_app_shared Run a Zig application that is linked to a shared library. 43 | zig_app_static Run a Zig application that is linked to a static library. 44 | tests Run a Zig tests of C source code. 45 | ``` 46 | 47 | ## The Journey 48 | ### Create a Simple C Application 49 | 50 | Let's create a simple math library in C, with functions declared in a header 51 | file and implemented in the source file. 52 | 53 | [`zmath.h`](include/zmath.h) 54 | ```c 55 | extern int add(int a, int b); 56 | extern int sub(int a, int b); 57 | ``` 58 | _Note: `extern` is used here to export our functions._ 59 | 60 | [`zmath.c`](src/zmath.c) 61 | ```c 62 | #include "zmath.h" 63 | 64 | int add(int a, int b) { 65 | return a + b; 66 | } 67 | 68 | int sub(int a, int b) { 69 | return a - b; 70 | } 71 | 72 | ``` 73 | 74 | Next we create a simple application to utilize this library. 75 | 76 | [`c_app.c`](src/c_app.c) 77 | ```c 78 | #include 79 | #include "zmath.h" 80 | 81 | int main(void) { 82 | int a = 10; 83 | int b = 5; 84 | 85 | int resultAdd = add(a, b); 86 | printf("%d + %d = %d\n", a, b, resultAdd); 87 | 88 | int resultSub = sub(a, b); 89 | printf("%d - %d = %d\n", a, b, resultSub); 90 | 91 | return 0; 92 | } 93 | ``` 94 | 95 | ### Compiling the application using `zig cc` 96 | 97 | If you're familiar with `gcc`, this is no problem. Here's the command to compile 98 | this application: 99 | 100 | ```sh 101 | zig cc -Iinclude src/c_app.c src/zmath.c -o zig-out/bin/c_app.exe 102 | ``` 103 | _The nice part about Zig is that it's a cross-compiler, so feel free to ignore that I'm on Windows._ 104 | 105 | Now run the resulting executable: 106 | 107 | ```sh 108 | > ./zig-out/bin/c_app.exe 109 | 10 + 5 = 15 110 | 10 - 5 = 5 111 | ``` 112 | 113 | ### Leverage Zig's build system to build the C application 114 | 115 | Now we can create a file called `build.zig`, which Zig will use to build our application. 116 | 117 | [`build.zig`](build_c_app.zig) 118 | ```c 119 | const std = @import("std"); 120 | 121 | pub fn build(b: *std.Build) void { 122 | const target = b.standardTargetOptions(.{}); 123 | const optimize = b.standardOptimizeOption(.{}); 124 | 125 | const exe = b.addExecutable(.{ 126 | .name = "c_app", 127 | .target = target, 128 | .optimize = optimize, 129 | }); 130 | 131 | exe.addIncludePath(b.path("include")); 132 | exe.addCSourceFiles(.{ 133 | .files = &[_][]const u8{ 134 | "src/main.c", 135 | "src/zmath.c" 136 | } 137 | }); 138 | 139 | exe.linkLibC(); 140 | 141 | b.installArtifact(exe); 142 | } 143 | ``` 144 | 145 | Things to note: 146 | - `b.addExecutable()` allows us to create an executable with given options, in 147 | this case default target options and optimizations. It also allows us to name 148 | our executable. 149 | - `exe.addIncludePath()` takes a `LazyPath`, and it just so happens that 150 | `std.Build` (the type of `b`) contains a function to return a `LazyPath` object 151 | given a string. 152 | - `exe.addCSourceFiles()` takes a `struct` that includes a `files` property. 153 | This property is a pointer to an array of strings. I'll break down 154 | `&[_][]const u8 {}` real quick in plain English: 155 | 156 | ```c 157 | &[_][]const u8 { 158 | "..", 159 | "...", 160 | // etc 161 | } 162 | 163 | // const u8 is a character 164 | // []const u8 is an array of characters, or a string 165 | // [_] is zig for "create an array with size automatically determined at compile time" 166 | // [_][]const u8 is an array of strings 167 | // & gives us a pointer to an object's memory address 168 | // &[_][]const u8 is a pointer to an array of strings 169 | 170 | 171 | // a reference to an automatically sized array of array of constants u8 objects. 172 | // a pointer to the address in memory where an array of arrays of u8 exists 173 | ``` 174 | 175 | _Probably overkill of an explanation, but maybe someone will benefit from this._ 176 | 177 | - Next is `exe.linkLibC()`. This is the extremely convenient way that Zig links 178 | to libc. There's also `linkLibCpp()`, which could be useful to keep in mind. If 179 | you use a standard library, such as `stdio.h`, make sure to include `linkLibC()`, 180 | otherwise you'll get a compilation error when trying to build. 181 | 182 | Now we kick off the Zig build process: 183 | 184 | ```sh 185 | zig build 186 | ``` 187 | 188 | And run the resulting executable: 189 | 190 | ```sh 191 | ./zig-out/bin/c_compiled_with_zig_build.exe 192 | 10 + 5 = 15 193 | 10 - 5 = 5 194 | ``` 195 | 196 | Same results as compiling with `zig cc`! Very cool. Let's move on to using a bit more Zig. 197 | 198 | ### Create a Zig application that links to the C library 199 | 200 | Basically, we want to recreate `c_app.c` in Zig. In this case, this is trivial. 201 | 202 | [`zig_app.zig`](src/zig_app.zig) 203 | ```c 204 | const std = @import("std"); 205 | const zmath = @cImport(@cInclude("zmath.h")); 206 | 207 | pub fn main() !void { 208 | const stdio = std.io.getStdOut().writer(); 209 | 210 | const a = 10; 211 | const b = 5; 212 | 213 | const resultAdd = zmath.add(a, b); 214 | try stdio.print("{} + {} = {}\n", .{ a, b, resultAdd }); 215 | 216 | const resultSub = zmath.sub(a, b); 217 | try stdio.print("{} - {} = {}\n", .{ a, b, resultSub }); 218 | } 219 | ``` 220 | 221 | This is fairly simple to understand. What's important is that we are including 222 | our C headers using Zig's `@cImport()` and `@cInclude()`. 223 | 224 | Next we have to modify `build.zig` and point it to our `zig_app.zig` file 225 | instead of `c_app.c`. 226 | 227 | [`build.zig`](build_zig_app.zig) 228 | ```c 229 | const std = @import("std"); 230 | 231 | pub fn build(b: *std.Build) void { 232 | const target = b.standardTargetOptions(.{}); 233 | const optimize = b.standardOptimizeOption(.{}); 234 | 235 | const exe = b.addExecutable(.{ 236 | .name = "build_zig_linked_to_c", 237 | .root_source_file = b.path("src/zig_app.zig"), 238 | .target = target, 239 | .optimize = optimize, 240 | }); 241 | 242 | exe.addIncludePath(b.path("include")); 243 | exe.addCSourceFile(.{ 244 | .file = b.path("src/zmath.c"), 245 | }); 246 | 247 | exe.linkLibC(); 248 | 249 | return exe; 250 | } 251 | ``` 252 | 253 | The only thing to pay attention to here is the `root_source_file` parameter in 254 | `addExecutable()`. Here we point to a Zig file with `pub fn main() void` 255 | implemented. This is a drop in replacement of `c_app.c`, or rather a Zig file 256 | using a C library (when source code is available). 257 | 258 | 259 | ### Using Zig to build a C Library 260 | 261 | Up until now, we've been utilizing the C source code, since it's available to us, 262 | but this is not always the case. Sometimes we may have a static or shared library 263 | that we need to link against, rather than compiling the source code ourselves. 264 | 265 | _If you want a deeper understanding of static and shared libraries, check out 266 | the links in the [Resources](#resources) section._ 267 | 268 | [`build.zig`](build_c_static_lib.zig) 269 | ```c 270 | const std = @import("std"); 271 | 272 | pub fn build(b: *std.Build) *std.Build.Step.Compile { 273 | const target = b.standardTargetOptions(.{}); 274 | const optimize = b.standardOptimizeOption(.{}); 275 | 276 | const lib = b.addStaticLibrary(.{ 277 | .name = "zmath_static", 278 | .target = target, 279 | .optimize = optimize, 280 | }); 281 | 282 | lib.addIncludePath(b.path("include")); 283 | lib.addCSourceFiles(.{ .files = &[_][]const u8{"src/zmath.c"} }); 284 | 285 | lib.linkLibC(); 286 | 287 | b.installArtifact(lib); 288 | } 289 | ``` 290 | 291 | We add our source files, include directory, link it to libc and install it. 292 | When we run `zig build`, our library will be compiled to a static library file, 293 | `zig-out/lib/zmath-static.lib`. 294 | 295 | If you want to compile shared libraries instead, there's not much difference. 296 | Instead of `b.addStaticLibrary()`, use `b.addSharedLibrary()`. 297 | 298 | _[See the difference in build.zig](build_c_shared_lib.zig)_ 299 | 300 | ### Create a Zig wrapper around a C Function 301 | 302 | This is a simple example, so wrapping the C function in Zig may be overkill. 303 | Either way, the idea is to wrap the call to our C functions in Zig, such that we 304 | have a Zig interface between our application code and our C code. This allows us 305 | to handle errors in a Zig fashion and pass proper types to the C code while 306 | exposing them to the application code. 307 | 308 | For this we'll create a new Zig file, `zmath_ext.zig` 309 | 310 | [`zmath_ext.zig`](src/zmath_ext.zig) 311 | ```c 312 | const zmath_h = @cImport(@cInclude("zmath.h")); 313 | 314 | pub extern fn add(a: c_int, b: c_int) callconv(.C) c_int; 315 | pub extern fn sub(a: c_int, b: c_int) callconv(.C) c_int; 316 | ``` 317 | 318 | This file is a declaration of external functions we wish to use in Zig. We'll 319 | next create a `zmath.zig`, in which we will create Zig functions that will 320 | expose Zig data types through our API and cast the parameters to their 321 | corresponding C data types before calling the C functions. 322 | 323 | [`zmath.zig`](src/zmath.zig) 324 | ```c 325 | const zmath_ext = @import("zmath_ext.zig"); 326 | 327 | pub fn add(a: i32, b: i32) !i32 { 328 | const x = @as(c_int, @intCast(a)); 329 | const y = @as(c_int, @intCast(b)); 330 | return zmath_ext.add(x, y); 331 | } 332 | 333 | pub fn sub(a: i32, b: i32) !i32 { 334 | const x = @as(c_int, @intCast(a)); 335 | const y = @as(c_int, @intCast(b)); 336 | return zmath_ext.sub(x, y); 337 | } 338 | ``` 339 | 340 | As you can see, we translate the C types to Zig specific types for use in Zig 341 | applications. We cast our input parameters to their C equivalent (`c_int`) for 342 | the C function's parameters. You'll also notice the return type contains `!`, 343 | meaning these functions will now return errors. This means within our 344 | application, we'll need to call the function with `try`. 345 | 346 | We'll create a new zig file for trying out the wrapper functions, called 347 | `zig_c_wrapper.zig`. This is mostly to distinguish between our previous examples, 348 | but this is just showing we no longer use `@cImport()` directly, and instead 349 | utilize `zmath.zig` (our wrapper functions), to interact with the C code. 350 | 351 | [`zig_c_wrapper.zig`](src/zig_c_wrapper.zig) 352 | ```c 353 | const std = @import("std"); 354 | const zmath = @import("zmath.zig"); 355 | 356 | pub fn main() !void { 357 | const stdio = std.io.getStdOut().writer(); 358 | 359 | const a = 10; 360 | const b = 5; 361 | 362 | const resultAdd = try zmath.add(a, b); 363 | try stdio.print("{d} + {d} = {d}\n", .{ a, b, resultAdd }); 364 | 365 | const resultSub = try zmath.sub(a, b); 366 | try stdio.print("{d} - {d} = {d}\n", .{ a, b, resultSub }); 367 | } 368 | ``` 369 | 370 | ### Linking to the Static/Shared Library 371 | 372 | With this in place, and our static/shared library created, we can use `build.zig` 373 | to link our application to our library. 374 | 375 | [`build.zig` for static libray](build_zig_app_static.zig) 376 | ```c 377 | const std = @import("std"); 378 | 379 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 380 | const exe = b.addExecutable(.{ 381 | .name = "zig_app_shared", 382 | .root_source_file = b.path("src/zig_c_wrapper.zig"), 383 | .target = target, 384 | .optimize = optimize, 385 | }); 386 | 387 | exe.addObjectFile(b.path("zig-out/lib/zmath-shared.lib")); 388 | 389 | return exe; 390 | } 391 | ``` 392 | 393 | It would be wise to note that here we built our library and then import it from 394 | a path (using `addObjectFile()`). If you build from source, use `addObject(*Compile)` 395 | instead and pass in the proper object. This is because Zig will compile faster 396 | than your OS can save the library file, causing the build to fail because the 397 | library file could not be found during the build time of this object (at least 398 | when using `dependsOn()`, like we do in the main [`build.zig`](build.zig) file 399 | for this repo). 400 | 401 | 402 | ## Side Quests 403 | 404 | Some extra thoughts about working with Zig and intergrating/interacting with C. 405 | 406 | ### Testing C code in Zig 407 | 408 | I was curious if I could use Zig's testing features to test C code. There's 409 | literally no reason why you couldn't, so here's how you do it. 410 | 411 | [`test_zmath.zig`](tests/test_zmath.zig) 412 | ```c 413 | const std = @import("std"); 414 | const testing = std.testing; 415 | 416 | const zmath = @cImport(@cInclude("zmath.h")); 417 | 418 | test "zmath.add() works" { 419 | try testing.expect(zmath.add(1, 2) == 3); 420 | try testing.expect(zmath.add(12, 12) == 24); 421 | } 422 | 423 | test "zmath.sub() works" { 424 | try testing.expect(zmath.sub(2, 1) == 1); 425 | try testing.expect(zmath.sub(12, 12) == 0); 426 | } 427 | ``` 428 | _Strive to write good tests, this is just a proof of concept._ 429 | 430 | [`build.zig`](build_test_zmath.zig) 431 | ```c 432 | const std = @import("std"); 433 | 434 | pub fn build(b: *std.Build) void { 435 | const target = b.standardTargetOptions(.{}); 436 | const optimize = b.standardOptimizeOption(.{}); 437 | 438 | const tests_exe = b.addTest(.{ 439 | .name = "test_zmath", 440 | .root_source_file = b.path("tests/test_zmath.zig"), 441 | .target = target, 442 | .optimize = optimize, 443 | }); 444 | 445 | tests_exe.addIncludePath(b.path("include")); 446 | tests_exe.addCSourceFile(.{ 447 | .file = b.path("src/zmath.c"), 448 | }); 449 | 450 | tests_exe.linkLibC(); 451 | 452 | b.installArtifact(tests_exe); 453 | } 454 | ``` 455 | 456 | ## Resources 457 | - https://mtlynch.io/notes/zig-call-c-simple/ 458 | What initially made me want to tackle this subject, this article is a great 459 | starting point for understanding C and Zig. 460 | - [Wikipedia article for "Shared Library"](https://en.wikipedia.org/wiki/Shared_library) 461 | - [Wikipedia article for "Static Library"](https://en.wikipedia.org/wiki/Static_library) 462 | - [Discussion about this repository on Ziggit](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/7) 463 | 464 | 465 | 466 | ## Thanks 467 | 468 | That's it. It was a long journey, but one that came with lots of learning and 469 | experimenting. Zig is wonderful and being able to use C code without having to 470 | use C is a game changer for me. 471 | 472 | Thanks for sticking around and reading. If you have feedback or suggestions, 473 | please don't hesitate to create an issue and we can work through it together. 474 | 475 | - Ramon 476 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const module_c_app = @import("build_c_app.zig"); 4 | const module_zig_app = @import("build_zig_app.zig"); 5 | const module_lib_static = @import("build_c_static_lib.zig"); 6 | const module_lib_shared = @import("build_c_shared_lib.zig"); 7 | const module_zig_app_static = @import("build_zig_app_static.zig"); 8 | const module_zig_app_shared = @import("build_zig_app_shared.zig"); 9 | const module_tests = @import("build_test_zmath.zig"); 10 | 11 | pub fn build(b: *std.Build) void { 12 | const target = b.standardTargetOptions(.{}); 13 | const optimize = b.standardOptimizeOption(.{}); 14 | 15 | // c_app 16 | const c_app = module_c_app.build(b, target, optimize); 17 | const run_c_app = b.addRunArtifact(c_app); 18 | 19 | b.installArtifact(c_app); 20 | 21 | const run_c_app_step = b.step("c_app", "Run a C application built with Zig's build system."); 22 | run_c_app_step.dependOn(&run_c_app.step); 23 | 24 | // zig_app 25 | const zig_app = module_zig_app.build(b, target, optimize); 26 | const run_zig_app = b.addRunArtifact(zig_app); 27 | 28 | b.installArtifact(zig_app); 29 | 30 | const run_zig_app_step = b.step("zig_app", "Run a Zig application linked to C source code."); 31 | run_zig_app_step.dependOn(&run_zig_app.step); 32 | 33 | // zmath_static 34 | const lib_static = module_lib_static.build(b, target, optimize); 35 | const install_lib_static = b.addInstallArtifact(lib_static, .{}); 36 | 37 | b.installArtifact(lib_static); 38 | 39 | const install_lib_static_step = b.step("zmath_static", "Create a static library from C source code."); 40 | install_lib_static_step.dependOn(&install_lib_static.step); 41 | 42 | // zmath_shared 43 | const lib_shared = module_lib_shared.build(b, target, optimize); 44 | const install_lib_shared = b.addInstallArtifact(lib_shared, .{}); 45 | 46 | b.installArtifact(lib_shared); 47 | 48 | const install_lib_shared_step = b.step("zmath_shared", "Create a shared library from C source code."); 49 | install_lib_shared_step.dependOn(&install_lib_shared.step); 50 | 51 | // zig_app_shared 52 | const zig_app_shared = module_zig_app_shared.build(b, target, optimize); 53 | const run_zig_app_shared = b.addInstallArtifact(zig_app_shared, .{}); 54 | 55 | b.installArtifact(zig_app_shared); 56 | 57 | const run_zig_app_shared_step = b.step("zig_app_shared", "Run a Zig application that is linked to a shared library."); 58 | run_zig_app_shared_step.dependOn(&install_lib_shared.step); // create and install shared library 59 | run_zig_app_shared_step.dependOn(&run_zig_app_shared.step); 60 | 61 | // zig_app_static 62 | const zig_app_static = module_zig_app_static.build(b, target, optimize); 63 | const run_zig_app_static = b.addInstallArtifact(zig_app_static, .{}); 64 | 65 | b.installArtifact(zig_app_static); 66 | 67 | const run_zig_app_static_step = b.step("zig_app_static", "Run a Zig application that is linked to a static library."); 68 | run_zig_app_static_step.dependOn(&install_lib_static.step); // create and install static library 69 | run_zig_app_static_step.dependOn(&run_zig_app_static.step); 70 | 71 | // tests 72 | const tests = module_tests.build(b, target, optimize); 73 | const run_tests = b.addRunArtifact(tests); 74 | 75 | b.installArtifact(tests); 76 | 77 | const run_tests_step = b.step("tests", "Run a Zig tests of C source code."); 78 | run_tests_step.dependOn(&run_tests.step); 79 | } 80 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | // This is the default name used by packages depending on this one. For 3 | // example, when a user runs `zig fetch --save `, this field is used 4 | // as the key in the `dependencies` table. Although the user can choose a 5 | // different name, most users will stick with this provided value. 6 | // 7 | // It is redundant to include "zig" in this name because it is already 8 | // within the Zig package namespace. 9 | .name = "zig-c-bindings", 10 | 11 | // This is a [Semantic Version](https://semver.org/). 12 | // In a future version of Zig it will be used for package deduplication. 13 | .version = "0.0.0", 14 | 15 | // This field is optional. 16 | // This is currently advisory only; Zig does not yet do anything 17 | // with this value. 18 | //.minimum_zig_version = "0.11.0", 19 | 20 | // This field is optional. 21 | // Each dependency must either provide a `url` and `hash`, or a `path`. 22 | // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. 23 | // Once all dependencies are fetched, `zig build` no longer requires 24 | // internet connectivity. 25 | .dependencies = .{ 26 | // See `zig fetch --save ` for a command-line interface for adding dependencies. 27 | //.example = .{ 28 | // // When updating this field to a new URL, be sure to delete the corresponding 29 | // // `hash`, otherwise you are communicating that you expect to find the old hash at 30 | // // the new URL. 31 | // .url = "https://example.com/foo.tar.gz", 32 | // 33 | // // This is computed from the file contents of the directory of files that is 34 | // // obtained after fetching `url` and applying the inclusion rules given by 35 | // // `paths`. 36 | // // 37 | // // This field is the source of truth; packages do not come from a `url`; they 38 | // // come from a `hash`. `url` is just one of many possible mirrors for how to 39 | // // obtain a package matching this `hash`. 40 | // // 41 | // // Uses the [multihash](https://multiformats.io/multihash/) format. 42 | // .hash = "...", 43 | // 44 | // // When this is provided, the package is found in a directory relative to the 45 | // // build root. In this case the package's hash is irrelevant and therefore not 46 | // // computed. This field and `url` are mutually exclusive. 47 | // .path = "foo", 48 | // 49 | // // When this is set to `true`, a package is declared to be lazily 50 | // // fetched. This makes the dependency only get fetched if it is 51 | // // actually used. 52 | // .lazy = false, 53 | //}, 54 | }, 55 | 56 | // Specifies the set of files and directories that are included in this package. 57 | // Only files and directories listed here are included in the `hash` that 58 | // is computed for this package. Only files listed here will remain on disk 59 | // when using the zig package manager. As a rule of thumb, one should list 60 | // files required for compilation plus any license(s). 61 | // Paths are relative to the build root. Use the empty string (`""`) to refer to 62 | // the build root itself. 63 | // A directory listed here means that all files within, recursively, are included. 64 | .paths = .{ 65 | "build.zig", 66 | "build.zig.zon", 67 | "src", 68 | // For example... 69 | //"LICENSE", 70 | //"README.md", 71 | }, 72 | } 73 | -------------------------------------------------------------------------------- /build_c_app.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const exe = b.addExecutable(.{ 5 | .name = "c_app", 6 | .target = target, 7 | .optimize = optimize, 8 | }); 9 | 10 | exe.addIncludePath(b.path("include")); 11 | exe.addCSourceFiles(.{ .files = &[_][]const u8{ "src/c_app.c", "src/zmath.c" } }); 12 | 13 | exe.linkLibC(); 14 | 15 | return exe; 16 | } 17 | -------------------------------------------------------------------------------- /build_c_shared_lib.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const lib = b.addSharedLibrary(.{ 5 | .name = "zmath-shared", 6 | .target = target, 7 | .optimize = optimize, 8 | }); 9 | 10 | lib.addIncludePath(b.path("include")); 11 | lib.addCSourceFiles(.{ .files = &[_][]const u8{"src/zmath.c"} }); 12 | 13 | lib.linkLibC(); 14 | 15 | return lib; 16 | } 17 | -------------------------------------------------------------------------------- /build_c_static_lib.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const lib = b.addStaticLibrary(.{ 5 | .name = "zmath-static", 6 | .target = target, 7 | .optimize = optimize, 8 | }); 9 | 10 | lib.addIncludePath(b.path("include")); 11 | lib.addCSourceFiles(.{ .files = &[_][]const u8{"src/zmath.c"} }); 12 | 13 | lib.linkLibC(); 14 | 15 | return lib; 16 | } 17 | -------------------------------------------------------------------------------- /build_test_zmath.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const exe = b.addTest(.{ 5 | .name = "test_zmath", 6 | .root_source_file = b.path("tests/test_zmath.zig"), 7 | .target = target, 8 | .optimize = optimize, 9 | }); 10 | 11 | exe.addIncludePath(b.path("include")); 12 | exe.addCSourceFile(.{ 13 | .file = b.path("src/zmath.c"), 14 | }); 15 | 16 | exe.linkLibC(); 17 | 18 | return exe; 19 | } 20 | -------------------------------------------------------------------------------- /build_zig_app.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const exe = b.addExecutable(.{ 5 | .name = "zig_app", 6 | .root_source_file = b.path("src/zig_app.zig"), 7 | .target = target, 8 | .optimize = optimize, 9 | }); 10 | 11 | exe.addIncludePath(b.path("include")); 12 | exe.addCSourceFile(.{ 13 | .file = b.path("src/zmath.c"), 14 | }); 15 | 16 | exe.linkLibC(); 17 | 18 | return exe; 19 | } 20 | -------------------------------------------------------------------------------- /build_zig_app_shared.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const exe = b.addExecutable(.{ 5 | .name = "zig_app_shared", 6 | .root_source_file = b.path("src/zig_c_wrapper.zig"), 7 | .target = target, 8 | .optimize = optimize, 9 | }); 10 | 11 | exe.addObjectFile(b.path("zig-out/lib/zmath-shared.lib")); 12 | 13 | return exe; 14 | } 15 | -------------------------------------------------------------------------------- /build_zig_app_static.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { 4 | const exe = b.addExecutable(.{ 5 | .name = "zig_app_static", 6 | .root_source_file = b.path("src/zig_c_wrapper.zig"), 7 | .target = target, 8 | .optimize = optimize, 9 | }); 10 | 11 | exe.addObjectFile(b.path("zig-out/lib/zmath-static.lib")); 12 | 13 | return exe; 14 | } 15 | -------------------------------------------------------------------------------- /include/zmath.h: -------------------------------------------------------------------------------- 1 | extern int add(int a, int b); 2 | extern int sub(int a, int b); 3 | -------------------------------------------------------------------------------- /src/c_app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "zmath.h" 3 | 4 | int main(void) { 5 | int a = 10; 6 | int b = 5; 7 | 8 | int resultAdd = add(a, b); 9 | printf("%d + %d = %d\n", a, b, resultAdd); 10 | 11 | int resultSub = sub(a, b); 12 | printf("%d - %d = %d\n", a, b, resultSub); 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/zig_app.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const zmath = @cImport(@cInclude("zmath.h")); 3 | 4 | pub fn main() !void { 5 | const stdio = std.io.getStdOut().writer(); 6 | 7 | const a = 10; 8 | const b = 5; 9 | 10 | const resultAdd = zmath.add(a, b); 11 | try stdio.print("{} + {} = {}\n", .{ a, b, resultAdd }); 12 | 13 | const resultSub = zmath.sub(a, b); 14 | try stdio.print("{} - {} = {}\n", .{ a, b, resultSub }); 15 | } 16 | -------------------------------------------------------------------------------- /src/zig_c_wrapper.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const zmath = @import("zmath.zig"); 3 | 4 | pub fn main() !void { 5 | const stdio = std.io.getStdOut().writer(); 6 | 7 | const a = 10; 8 | const b = 5; 9 | 10 | const resultAdd = try zmath.add(a, b); 11 | try stdio.print("{d} + {d} = {d}\n", .{ a, b, resultAdd }); 12 | 13 | const resultSub = try zmath.sub(a, b); 14 | try stdio.print("{d} - {d} = {d}\n", .{ a, b, resultSub }); 15 | } 16 | -------------------------------------------------------------------------------- /src/zmath.c: -------------------------------------------------------------------------------- 1 | #include "zmath.h" 2 | 3 | int add(int a, int b) { 4 | return a + b; 5 | } 6 | 7 | int sub(int a, int b) { 8 | return a - b; 9 | } 10 | -------------------------------------------------------------------------------- /src/zmath.zig: -------------------------------------------------------------------------------- 1 | const zmath_ext = @import("zmath_ext.zig"); 2 | 3 | pub fn add(a: i32, b: i32) !i32 { 4 | const x = @as(c_int, @intCast(a)); 5 | const y = @as(c_int, @intCast(b)); 6 | return zmath_ext.add(x, y); 7 | } 8 | 9 | pub fn sub(a: i32, b: i32) !i32 { 10 | const x = @as(c_int, @intCast(a)); 11 | const y = @as(c_int, @intCast(b)); 12 | return zmath_ext.sub(x, y); 13 | } 14 | -------------------------------------------------------------------------------- /src/zmath_ext.zig: -------------------------------------------------------------------------------- 1 | const zmath_h = @cImport(@cInclude("zmath.h")); 2 | 3 | pub extern fn add(a: c_int, b: c_int) callconv(.C) c_int; 4 | pub extern fn sub(a: c_int, b: c_int) callconv(.C) c_int; 5 | -------------------------------------------------------------------------------- /tests/test_zmath.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | 4 | const zmath = @cImport(@cInclude("zmath.h")); 5 | 6 | test "zmath.add() works" { 7 | try testing.expect(zmath.add(1, 2) == 3); 8 | try testing.expect(zmath.add(12, 12) == 24); 9 | } 10 | 11 | test "zmath.sub() works" { 12 | try testing.expect(zmath.sub(2, 1) == 1); 13 | try testing.expect(zmath.sub(12, 12) == 0); 14 | } 15 | --------------------------------------------------------------------------------