본문 바로가기

building system/bazel

http_archive/git_repository 기반 bazel 예제 구현하기

반응형

개요

이전 게시글에서는 github에 있는 bazel_skylib를 git으로 다운 받았다. 다운받은 파일의 맨 상위에 WORKSPACE과 BUILD 파일이 있어서 또 다른 bazel 기반 프로젝트임을 인식하고 local_repository를 통해 외부 의존성을 걸어 사용했다.

이번 게시글에서는 bazel에서 기본 제공하는 '@bazel-tool' repository의 웹 다운로드, git 다운로드 기능을 사용해 컴파일과 동시에 필요한 repository를 자동 구성하고, 나머지 컴파일 연산을 진행하는 다소 흥미로운 샘플이다.

최종 결과물은 main.exe 파일과 c.jpg 파일을 같은 경로에 제공하고, main.exe를 실행하면 같은 경로에 있는 c.jpg를 압축해 c.zip 파일을 생성한다. c.jpg 파일을 결과물 폴더로 컴파일과 동시에 복사하기 위해 StarLark 언어로 bzl 코딩을 추가했다. 실제 웹에서 오래전에 업로드된 소스를 기반으로 윈도우에 작동하도록 일부 코딩을 수정했다.

풀어야 할 과제

전체 폴더 구성은 아래와 같다. 빌드 시스템 맨 상위에 WORKSPACE.bazel 파일이 존재한다.

WORKSPACE 파일은 빌드 시스템이 맨 상단 폴더를 알려주는 역할 외에, http_archive rule를 적용해 웹에서 파일을 다운로드 받는다. 다운로드 받은 파일이 맞는지 확인하기 위해 sha256 해쉬값을 옵션으로 사용한다. 다음으로 git_repository rule를 적용해 commit된 상태의 히스토리 git 파일을 다운받고, 추가적인 bazel BUILD 파일을 제공해서, 다운로드 받은 결과 BUILD 파일을 생성하고, bazel 환경에서 참여하도록 한다.

src 폴더의 main.cc를 통해 원하는 기능을 구현한다. 같은 폴더에 .copy_filegroups.bzl, c.jpg 파일이 함께 있다.

│  WORKSPACE.bazel
│
├─external
│      minizip.BUILD
│
└─src
        BUILD.bazel
        c.jpg
        copy_filegroups.bzl
        main.cc

WORKSPACE.bazel 파일

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:git.bzl",     "git_repository")

http_archive(
    name = "bazel_skylib",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz",
        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz",
    ],
)

load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()

git_repository (
    name = "minizip",
    remote = "https://github.com/domoticz/minizip.git",
    build_file = "minizip.BUILD",
    commit = "78eb93e8e0ba228f37b197d2022d66b73856c9ff",
)

http_archive rule로 bazel_skylib를 웹에서 다운받고 있다. 이번 예제에서는 실제적으로 사용하지 않는다. 다운받은 파일이 bazel 빌드 시스템 환경이기 때문에, 추가적인 작업없이 곧바로 '@bazl_skylib'로 repository를 사용할 수 있다.

압축된 파일을 사용할 때 http_archive rule룰 사용한다. 지원하는 압축 포맷은 "zip", "jar", "war", "aar", "tar", "tar.gz", "tgz", "tar.xz", "txz", "tar.zst", "tzst", tar.bz2, "ar", "deb" 정도다.

그외에도 http_file, http_jar rule이 존재한다.

git_repository는 git 파일을 다운받아 bazel repository로 사용한다. git 파일은 commit에 따라 다양한 히스토리 파일이 존재하기 때문에 tag 정보나 commit 해시 정보를 제공해야 원하는 파일을 다운받을 수 있다. 본인이 사용한 commit 해시는 github의 commit 항목에서 복사했다. 아래 스샷을 참고하세요. commit 해시 정보를 사용하기 때문에, 한번 다운로드 받아 컴파일이 완료되면 추가 다운로드를 진행하지 않는다. 즉 유일성이 보장된 파일로 판단한다.

minizip 라이브러리는 zlib과 함께 배포되기도 하는데, zlib를 기반으로 zip 파일을 만들어준다. 다행스럽게도 기본으로 제공하는 @bazel-tool repository에 최신 버전 zlib 1.3를 의미 지원하고 있기 때문에, zlib는 다운로드할 필요가 없다.
minizip는 bazel 빌드시스템이 아니다. 따라서 build_file 항목에 다운로드가 완료되면 BUILD라는 이름으로 복사해줄 소스 파일을 제공해야 한다. build_file에 특정 파일 이름을 적어주면 맨 상위 external 폴더에서 찾아 복사해주는 구조를 따른다.

external/minizip.BUILD 내용

minizip 파일은 과거에 몇번 사용한 경험이 있어, 아래처럼 BUILD 파일을 쉽게 만들어낼 수 있다. minizip.c는 cc_binary로, 나머지는 모두 cc_library로 구성하면 된다. 컴파일 과정에서 C4005 경고가 발생하는데, 이를 무시하기 위해 copts 항목에 컴파일 경고 무시 옵션을 추가했다.

minizip는 zlib가 필요한데, 기본 제공되는 "@bazel_tools//third_party/zlib" 폴더의 zlib 타겟을 deps 항목에 추가하면 된다.

중요한 내용은 includes 항목에 외부에서 사용할 수 있도록 헤더 파일 위치를, visibility 항목에서는 접근 가능하도록 "//visibility:public" 설정을 추가해야 한다.

HDRS =[
    "minizip/ioapi.h",
    "minizip/iowin32.h",
    "minizip/mztools.h",
    "minizip/unzip.h",
    "minizip/zip.h",
    "minizip/zipcrypt.h",
]

SCRS = [
    "ioapi.c",
    "iowin32.c",
    "miniunz.c",
    "mztools.c",
    "unzip.c",
    "zip.c",
]  

cc_library(
    name = "zip",
    hdrs = HDRS,
    srcs = SCRS,
    includes =["minizip/"],

    copts = [
    "/wd4005",
    ],

    deps = ["@bazel_tools//third_party/zlib:zlib"],
    visibility = ["//visibility:public"],
)

cc_binary(
    name = "minizip",
    srcs = ["minizip.c"],
    deps = [
    ":zip",
    ],
)

src/BUILD.bazel 파일 구현하기

load(
    "//src:copy_filegroups.bzl",
    "copy_filegroups_to_this_package",
)

filegroup(
    name = "test_files",
    srcs = glob([
        "*.jpg",
    ]),
)

copy_filegroups_to_this_package(
    name = "other_files_unnested",
    targeted_filegroups = [":test_files"],
)

cc_binary(
    name = "main",
    srcs = ["main.cc"],
    data = [":other_files_unnested"],
    deps = [ 
    "@minizip//:zip",
    ],
)

copy_filegroups_to_this_package rule는 filegroup rule로 설정한 파일 검색 결과를 package 폴더로 복사하도록 구현한 외부 파일 copy_filegroups.bzl 구현을 따른다.

cc_binary는 이렇게 만들어진 copy_filegroups_to_this_package rule 이름을 data 항목에서 사용한다. cc_binary의 deps 항목은 "@minizip" repository의 zip 타겟을 설정한다. minizip는 zlib를 순차적으로 끌어올 것이다.

copy_filegroups.bzl 구현보기

다소 익숙하지 않는 아래 코딩이 파일을 패키지 폴더로 복사하는 역할을 한다.

def _copy_filegroup_impl(ctx):
    all_input_files = [
        f for t in ctx.attr.targeted_filegroups for f in t.files.to_list()
    ]

    all_outputs = []
    for f in all_input_files:
        out = ctx.actions.declare_file(f.basename)
        all_outputs += [out]
        ctx.actions.run_shell(
            outputs = [out],
            inputs = depset([f]),
            arguments = [f.path, out.path],
            command = "cp -f \"\\$1\" \"\\$2\"")

    # Small sanity check
    if len(all_input_files) != len(all_outputs):
        fail("Output count should be 1-to-1 with input count.")

    return [
        DefaultInfo(
            files=depset(all_outputs),
            runfiles=ctx.runfiles(files=all_outputs))
    ]

copy_filegroups_to_this_package = rule(
    implementation=_copy_filegroup_impl,
    attrs={
        "targeted_filegroups": attr.label_list(),
    },
)

main.cc 파일

아래 코딩은 오래전에 구현한 소스를 그대로 가져왔다. 매우 직관적인 코딩이라 쉽게 이해될 수 있다. zip.h 파일을 추가하고 c.zip 파일을 생성한 후, 지금 시간을 기준으로 c.jpg를 압축 파일에 추가한다. 일반적으로 압축하는 파일 시간을 사용하지만.

#include <zip.h>
#include <fstream>
#include <ctime>
#include <iostream>

int main() {
    do {
        //zip 파일  생성
        zipFile zf = zipOpen("c.zip", APPEND_STATUS_CREATE);
        if (NULL == zf) break;

        zip_fileinfo info;
        memset(&info, 0, sizeof(zip_fileinfo));

        time_t nt;
        time(&nt);
        struct  tm* tdata = localtime(&nt);
        info.tmz_date.tm_hour = tdata->tm_hour;
        info.tmz_date.tm_mday = tdata->tm_mday;
        info.tmz_date.tm_min = tdata->tm_min;
        info.tmz_date.tm_mon = tdata->tm_mon;
        info.tmz_date.tm_sec = tdata->tm_sec;
        info.tmz_date.tm_year = tdata->tm_year;

        // zip 파일  목록  생성
        int ret = zipOpenNewFileInZip(
            zf, "c.jpg", &info, NULL, 0, NULL, 0, "comment",
            Z_DEFLATED, Z_DEFAULT_COMPRESSION
        );

        ///// 기록  시작...
        do {
            std::ifstream fp;
            fp.open("c.jpg", std::ios_base::binary);
            if (!fp) break;

            const  int BUF = 1024;
            Bytef in[BUF];

            do {
                fp.read((char*)in, BUF);
                int readsize = (int)fp.gcount();
                zipWriteInFileInZip(zf, (const  void*)in, readsize);
            } while (!fp.eof());

            fp.close();
        } while (false);

        //////기록  완료
        zipCloseFileInZip(zf);//zip 목록  닫기

        zipClose(zf, "global_comment");//zip 파일  닫기        

    } while (false);

    return 0;
}

빌드하기

WORKSPACE.bazel이 있는 경로에서 실행된 console 프로그램에 다음을 입력하고 수행한다.

bazelisk build //...

결과물 실행하기

WORKSPACE.bazel이 있는 경로에서 실행된 console 프로그램에 다음을 입력하고 수행하면 탐색기에 결과물 폴더가 표시된다.

explorer.exe bazel-out\x64_windows-fastbuild\bin\src

main..exe를 실행하면 c.zip 파일이 생성된 것을 확인할 수 있다.

git_http.zip
114.9 kB

반응형