# Creating a C Project with Zig Build System

> This article is inspired by [https://ziglang.org/learn/overview/#export-functions-variables-and-types-for-c-code-to-depend-on](https://ziglang.org/learn/overview/#export-functions-variables-and-types-for-c-code-to-depend-on)

In this article we will explore creating a C Project using the Zig Build System, the advantages towards this approach are the following:

* The ability to target multiple systems with ease
    
* Being able to mix C and Zig code with ease
    
* Using LLVM Optimizations
    
* Caching
    

Let's create a folder called `mathtest` and it will contain the following structure:

```bash
mathtest/
    build.zig
    mathtest.zig 
    test.c
```

Now let's begin with the easiest file `mathtest.zig` which will contain one function `add` that will be exported to `c`:

```rust
export fn add(a: i32, b: i32) callconv(.C) i32{
    return a + b;
}
```

Now if we were to manually link `mathtest.zig` with `test.c`, we would need `test.c` to have someway to know about our function from the shared library, so when we work on `test.c` we will put the `add` function as `extern` at the top.

```c
#include<stdio.h>
extern int add(int a, int b);

int main(int argc, char **argv){
    int result = add(42, 1337);
    printf("%d\n", result);
    return 0;
}
```

Now we are ready to work on the actual build system of Zig which will be read from `build.zig`. Firstly we will import the `Builder` type from the standard library which is located in the `build` namespace.

After of which we will create the `build` function which will be public, and take in a pointer to `Builder` as the parameter `b`, and return nothing or `void`.

```rust
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
// we will work inside here now
}
```

Inside of the function, we will need to declare two constants, `target` and `optimize`, which determine which target our binary will be compiling for, and the different optimization mode it will be set to respectively. To keep it simple, we will keep them at the defaults by using the method `b.standardTargetOptions` and `b.standardOptimizationOptions` respectively.

```rust
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
```

Now we are ready to create our shared library which will be assigned to the constant `lib` and created using the function `b.addSharedLibrary`. This function requires the type `SharedLibraryOptions`, which requires the following:

* `name: []const u8`
    
* `root_source_file: ?LazyPath`
    
* `target: CrossTarget`
    
* `optimize: std.builtin.Mode`
    

This can be seen done below:

```rust
    const lib = b.addSharedLibrary(
    .{
        .name = "mathtest", 
        .root_source_file = .{ .path = "mathtest.zig" }, 
        .target = target,
        .optimize = optimize 
    });
```

We are now ready to create our executable by firstly declaring the constant `exe` and assigning it to `b.addExecutable` with the `ExecutableOptions` set with `name` as "test". After we need to add a few things for our executable...

Firstly we need to add `test.c` as the file of our executable, this can be done using the method `addCSourceFile` which will expect a `LazyPath` (we will set path to `test.c`) as the file and `[][]const u8` for the flags (we will simply declare a slice with the only item being "-std=c99").

Secondly we need to link our library to our executable, which will be done with the `linkLibrary` method and `lib` can simply be passed into it.

Since this executable is a C file, we will also need to make sure to link `libc`, which is simply done using the method `linkLibC`.

With these options set, we can use `b.installArtifact` where the `installArtifact` method registers an artifact to be installed. An artifact in our context is the result of a build step from an executable file.

```rust
    const exe = b.addExecutable(.{ .name = "test" });
    exe.addCSourceFile(
        .{ 
            .file = .{ .path = "test.c" }, 
            .flags = &[_][]const u8{"-std=c99"} 
    });
    exe.linkLibrary(lib);
    exe.linkLibC();
    b.installArtifact(exe);
```

We are now ready to create our run command which will be invoked by the command `zig build run`, and for this to occur we first need the constant `run_cmd` which will be assigned to `b.addRunArtifact` and we will pass in `exe` to it.

We will have this run step to have a dependency to retrieve the install step from the `Builder` instance `b`. This is done using `run_cmd.step.dependOn(b.getInstallStep())`.

After we will create the run step (constant `run_step`) by using the `b.step` function which expects a name and description that will be used when you invoke `zig build --help`. For `run_step` to actually use our run command, we will have it depend on `run_cmd.step`.

```rust
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    const run_step = b.step("run", "Run the program");
    run_step.dependOn(&run_cmd.step);
```

Our full `build.zig` code can be seen below:

```rust
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const lib = b.addSharedLibrary(
    .{
        .name = "mathtest", 
        .root_source_file = .{ .path = "mathtest.zig" }, 
        .target = target,
        .optimize = optimize 
    });
    const exe = b.addExecutable(.{ .name = "test" });
    exe.addCSourceFile(
        .{ 
            .file = .{ .path = "test.c" }, 
            .flags = &[_][]const u8{"-std=c99"} 
    });
    exe.linkLibrary(lib);
    exe.linkLibC();
    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    const run_step = b.step("run", "Run the program");
    run_step.dependOn(&run_cmd.step);
}
```

When we run our project now, we can see the following output:

```bash
$ zig build run 
1379
```
