Lobsters

Extend Lua with Zig 1: Hello World

Zig로 Lua 확장하기 1: Hello World

Lua 개발자를 대상으로 Zig의 기본 문법과 컴파일 시점 실행 개념을 소개하고, `hello.zig`를 실행 파일로 빌드하는 과정을 안내합니다. Zig 빌드 스크립트에서 모듈과 실행 단계를 구성하는 방법도 다룹니다.

AI 요약

Lua를 알고 있지만 Zig는 처음인 개발자를 위한 연재의 첫 글입니다. 이번 편은 Zig로 Hello World를 출력하고, Zig의 컴파일 시점 실행과 빌드 시스템을 익히는 데 집중합니다. 연재의 목표는 Lua에서 직접 호출할 Zig 함수를 만드는 것이지만, 이 글에서는 Lua 연동에 앞서 Zig 프로그램과 빌드 구성을 준비합니다.

Zig의 기본 구조와 컴파일 시점 실행

예제 프로그램은 std 모듈을 @import로 불러오고, 공개 함수 main에서 std.debug.print를 호출합니다. Zig 파일은 네임스페이스처럼 동작하며, 파일 최상위 선언은 기본적으로 해당 파일 안에서만 보입니다. Zig 런타임에서 호출할 main에는 pub를 붙여 공개합니다. 함수는 fn으로 선언하고 반환형은 인자 목록 뒤에 적습니다.

글은 @import()와 상수 선언을 모두 컴파일 과정에서 평가되는 선언으로 설명합니다. Zig의 컴파일은 파일을 의미 분석하는 과정에서 최상위 코드도 실행합니다. 다만 일반 함수 호출은 자동으로 실행하지 않으므로 컴파일 시점에 호출하려면 comptime foo()처럼 표시해야 합니다. 최상위 범위에 반복문 같은 절차적 코드를 바로 쓸 수는 없습니다. 컴파일 시점 계산이 필요하면 comptime 블록에 코드를 넣고, 이름 있는 블록에서 break로 결과값을 돌려줄 수 있습니다.

Lua와 비교하면, Lua의 do ... end 블록은 선택 사항이지만 Zig에서 최상위 컴파일 시점 계산을 묶으려면 블록이 필요합니다. Zig에서는 익명 함수 선언도 지원하지 않아, 함수를 값으로 대입할 때 컨테이너 범위로 이동하는 방법이 필요합니다. 글은 이 차이를 이후 예제에서 다룰 내용으로 남겨 둡니다.

제네릭 함수와 출력

std.debug.print의 형식 문자열 인자는 comptime으로 지정되어 컴파일 시점에 알려져야 합니다. 두 번째 인자는 anytype으로 받아 형식을 일반화합니다. 형식 문자열을 컴파일 시점에 알면 컴파일러가 인자 형식을 검증하고 필요한 코드를 생성합니다. []const u8은 변경할 수 없는 바이트 슬라이스이며, Zig에는 별도의 문자열 타입이 없습니다. std.debug.print와 문자열 서식화 함수는 특별한 내장 권한이나 매크로 대신 일반 Zig 코드와 comptime으로 구현합니다. 예제 출력은 의도적으로 표준 출력이 아니라 표준 오류로 향합니다. 표준 출력 쓰기는 Lua 설정을 진행한 뒤 다룰 예정입니다.

`build.zig`에서 빌드와 실행 구성

글은 build.zig에 pub fn build(b: *std.Build) void를 선언하며 빌드 스크립트를 시작합니다. zig build가 이 함수를 실행합니다. b.standardTargetOptions(.{})와 b.standardOptimizeOption(.{})로 대상 플랫폼과 최적화 설정을 받습니다. 대상 옵션은 기본적으로 현재 환경을 따르며 -Dtarget=으로 지정할 수 있습니다. 최적화 선택지는 기본값인 debug와 실행 속도, 안전성 검사, 파일 크기를 각각 중시하는 fast, safe, small입니다.

다음으로 b.addModule을 호출해 hello.zig를 루트 소스 파일로 지정하고, 대상과 최적화 옵션을 모듈에 전달합니다. 파일 경로에는 b.path()를 사용합니다. 빌드 시스템이 의존성에 속한 경로나 앞선 빌드 단계에서 생성한 파일도 같은 방식으로 다룰 수 있도록 추상화하기 때문입니다. @import()의 상대 경로는 현재 모듈 루트 파일이 있는 디렉터리 하위로 제한되며, 파일 하나는 최대 하나의 모듈에만 속할 수 있습니다. 여러 모듈에서 같은 파일을 가져오려면 공통 모듈로 구성해야 합니다.

b.addExecutable은 모듈을 실행 파일로 만들고, b.installArtifact는 이를 설치 대상으로 등록합니다. zig build를 실행하면 실행 파일이 zig-out/bin에 놓입니다. 마지막으로 b.step("run", ...)과 b.addRunArtifact(exe)를 사용해 zig build run 명령을 구성합니다. run.addPassthruArgs()는 명령 뒤에 --로 전달한 인자를 실행 파일에 넘깁니다. 실행 단계는 설치 단계에 의존하므로 프로그램을 실행하기 전에 실행 파일을 설치합니다.

원문: Robbie Lyman 블로그 / 번역·요약: Trawling