Why would you want that #
My main reason for wanting to build c code with Zig is the easy cross compilation and use of Musl Libc.
1$ zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSmall
With that command you can easy build a project for x86_64-linux and linking with musl statically.
How to do it the wrong way #
If you want to only build a single c file with zig you can use zig cc
and pass the same
optioans as to gcc. At least for linking.
Lets assume we have a project which uses libcurl for making https request. The code looks like this:
1#include <stdio.h>
2#include <curl/curl.h>
3#include <string.h>
4
5int main(int argc, char ** argv) {
6 if (argc < 2) {
7 fprintf(stderr, "ERROR: missing argument\n\tUsage: %s <url with out https://>\n", argv[0]);
8 return -1;
9 }
10 int domain_len = strlen(argv[1]);
11 char url[8 + domain_len + 1];
12 memset(url, 0, 8 + domain_len + 1);
13 memcpy(url, "https://", 8);
14 memcpy(url + 8, argv[1], domain_len);
15 url[8 + domain_len] = '\0';
16 curl_global_sslset(CURLSSLBACKEND_OPENSSL, NULL, NULL);
17
18 curl_global_init(CURL_GLOBAL_DEFAULT);
19 CURL* curl;
20 curl = curl_easy_init();
21
22 if( !curl) {
23 fprintf(stderr, "ERROR: Failed to initialize curl\n");
24 curl_global_cleanup();
25 return -1;
26 }
27 curl_easy_setopt(curl, CURLOPT_URL, url);
28 curl_easy_setopt(curl, CURLOPT_CA_CACHE_TIMEOUT, 604800L);
29
30 CURLcode res = curl_easy_perform(curl);
31 if (res != CURLE_OK) {
32 fprintf(stderr, "ERROR: curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
33 curl_global_cleanup();
34 return -1;
35 }
36 curl_easy_cleanup(curl);
37 curl_global_cleanup();
38}
compiling the code looks like this: cc $(pkg-config --libs libcurl) -g -Wall -Wextra $(pkg-config --cflags libcurl) -o curl-test main.c
.
When we want to use zig cc
we just need to replace cc
with zig cc
: zig cc $(pkg-config --libs libcurl) -g -Wall -Wextra $(pkg-config --cflags libcurl) -o curl-test main.c
The correct way (build.zig) #
But i dont want to memorize a specific command, run CC="zig cc" make
or tap my up arrow to get to the curect command.
I want to just run zig build
or zig build -Doptimize=ReleaseSmall
for a release build.
To be able to do that we can make use of the build.zig file. The build.zig file configures a build system, and yes the file is just zig code.
1const std = @import("std");
2
3pub fn build(b: *std.Build) !void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
6 const exe = b.addExecutable(.{
7 .name = "curl-test",
8 .target = target,
9 .optimize = optimize,
10 });
11 exe.linkLibC();
12 exe.addCSourceFiles(&.{"main.c"}, &.{ "-Wall", "-Wextra" });
13 exe.linkSystemLibrary("libcurl");
14 b.installArtifact(exe);
15}
Lets go through what this file does.
First we need to import Zig's stdlib.
We then define the function which configures the build system.
We get the target
and optimize
options set by -Dtarget
abd -Doptimize
.
The const exe = b.addExecutable
file defines our executable. The name
parameter defines how the file will be named.
target
and optimize
you make the executable actually use the command line provided options.
To use a c library like Libcurl we need to link libc, this is done by exe.linkLibC()
.
The next line addes C Source files and we then link a system library. This can be the pkg-config
and the name the linker uses.
In this case it could be libcurl
(used by pkg-config) or curl
(used by linker when running with arg -lcurl
)
and the last line tells the build system to install it to zig-out/bin/<our name defined in addExecutable>
.