오브젝트 파일 섞기

오브젝트 파일 섞기 (Mixing Object Files)

Zig 오브젝트 파일은 C ABI를 따르는 다른 어떤 오브젝트 파일과도 섞어 쓸 수 있어요. 아래 예시를 따라가 볼게요.

출처: Zig Documentation

본문

먼저 base64.zig에서 C 쪽에 노출할 함수를 export로 내보내요.

const base64 = @import("std").base64;

export fn decode_base_64(
    dest_ptr: [*]u8,
    dest_len: usize,
    source_ptr: [*]const u8,
    source_len: usize,
) usize {
    const src = source_ptr[0..source_len];
    const dest = dest_ptr[0..dest_len];
    const base64_decoder = base64.standard.Decoder;
    const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
    base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
    return decoded_size;
}

이제 이 함수를 C에서 호출해요. 이때 base64.zig에서 직접 생성된 base64.h 헤더를 #include로 가져와서 씁니다.

// This header is generated by zig from base64.zig
#include "base64.h"

#include <string.h>
#include <stdio.h>

int main(int argc, char **argv) {
    const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz";
    char buf[200];

    size_t len = decode_base_64(buf, 200, encoded, strlen(encoded));
    buf[len] = 0;
    puts(buf);

    return 0;
}

두 파일을 한 바이너리로 묶는 건 빌드 스크립트에서 담당해요. Zig 오브젝트(addObject)를 만들고, C 소스와 함께 실행 파일에 합칩니다.

const std = @import("std");

pub fn build(b: *std.Build) void {
    const obj = b.addObject(.{
        .name = "base64",
        .root_module = b.createModule(.{
            .root_source_file = b.path("base64.zig"),
        }),
    });

    const exe = b.addExecutable(.{
        .name = "test",
        .root_module = b.createModule(.{
            .link_libc = true,
        }),
    });
    exe.root_module.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
    exe.root_module.addObject(obj);
    b.installArtifact(exe);
}

빌드해서 실행하면, Zig 쪽에서 디코딩한 문자열이 그대로 출력돼요.

$ zig build
$ ./zig-out/bin/test
all your base are belong to us

더 알아보기