C 라이브러리 내보내기

C 라이브러리 내보내기 (Exporting a C Library)

Zig의 대표적인 활용 사례 중 하나가 바로 C ABI로 라이브러리를 내보내서 다른 프로그래밍 언어에서 호출할 수 있게 만드는 거예요. 함수·변수·타입 앞에 export 키워드를 붙이면 그 대상이 라이브러리 API의 일부가 됩니다.

출처: Zig Documentation

본문

export 키워드는 함수·변수·타입 앞에 붙여서 그 대상이 라이브러리의 공개 API가 되게 해요. 먼저 덧셈 함수를 내보내 볼게요.

export fn add(a: i32, b: i32) i32 {
    return a + b;
}

이렇게 내보낸 함수를 정적 라이브러리로 만들려면 이렇게 해요.

$ zig build-lib mathtest.zig

공유 라이브러리(shared library)로 만들려면 -dynamic 옵션만 추가하면 돼요.

$ zig build-lib mathtest.zig -dynamic

이번엔 Zig Build System으로 똑같은 걸 만들어 볼게요. C 쪽에서 Zig 함수를 호출하는 테스트 프로그램을 준비합니다. mathtest.h 헤더는 Zig가 mathtest.zig로부터 자동 생성한 거예요.

// This header is generated by zig from mathtest.zig
#include "mathtest.h"
#include <stdio.h>

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

이제 빌드 스크립트에서 라이브러리(addLibrary)와 테스트 실행 파일을 만들고, 실행 파일이 그 라이브러리를 링크하도록 연결해요.

const std = @import("std");

pub fn build(b: *std.Build) void {
    const lib = b.addLibrary(.{
        .linkage = .dynamic,
        .name = "mathtest",
        .root_module = b.createModule(.{
            .root_source_file = b.path("mathtest.zig"),
        }),
        .version = .{ .major = 1, .minor = 0, .patch = 0 },
    });
    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.linkLibrary(lib);

    b.default_step.dependOn(&exe.step);

    const run_cmd = exe.run();

    const test_step = b.step("test", "Test the program");
    test_step.dependOn(&run_cmd.step);
}

zig build test로 빌드하고 테스트를 돌리면, Zig 쪽 add가 계산한 42 + 1337 = 1379가 출력돼요.

$ zig build test
1379

더 알아보기

  • 함수·변수·타입을 라이브러리 API로 공개하는 export 키워드에 대한 자세한 내용은 export에서 볼 수 있어요.