Bazel 가이드: C++ 프로젝트 빌드

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
문제 신고 소스 보기

소개

Bazel을 처음 사용하시나요? 지금 바로 안내해드리겠습니다. Bazel 사용에 대한 간략한 소개는 이 첫 번째 빌드 튜토리얼을 참조하세요. 이 튜토리얼에서는 Bazel의 맥락에서 사용되는 주요 용어를 정의하고 Bazel 워크플로의 기본 사항을 설명합니다. 먼저 필요한 도구부터 점점 더 복잡해지는 세 가지 프로젝트를 빌드 및 실행하고, 이러한 도구가 더 복잡해지는 방법과 이유를 알아봅니다.

Bazel은 다국어 빌드를 지원하는 빌드 시스템이지만, 이 튜토리얼에서는 C++ 프로젝트를 예시로 사용하고 대부분의 언어에 적용되는 일반적인 가이드라인과 흐름을 제공합니다.

예상 완료 시간: 30분

기본 요건

아직 설치하지 않았다면 먼저 Bazel을 설치합니다. 이 튜토리얼에서는 소스 제어에 Git를 사용하므로 최상의 결과를 얻기 위해 Git도 설치합니다.

다음으로 선택한 명령줄 도구에서 다음을 실행하여 Bazel의 GitHub 저장소에서 샘플 프로젝트를 검색합니다.

git clone https://github.com/bazelbuild/examples

이 튜토리얼의 샘플 프로젝트는 examples/cpp-tutorial 디렉터리에 있습니다.

광고의 구조를 아래에서 살펴보세요.

examples
└── cpp-tutorial
    ├──stage1
    │  ├── main
    │  │   ├── BUILD
    │  │   └── hello-world.cc
    │  └── WORKSPACE
    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE
    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── WORKSPACE

파일 세트 세 개가 있으며 각 세트는 이 튜토리얼의 단계를 나타냅니다. 첫 번째 단계에서는 단일 패키지에 상주하는 단일 타겟을 빌드합니다. 두 번째 단계에서는 단일 패키지로 바이너리와 라이브러리를 모두 빌드합니다. 세 번째이자 마지막 단계에서는 여러 패키지가 있는 프로젝트를 빌드하고 여러 대상으로 이를 빌드합니다.

요약: 소개

Bazel (및 Git)을 설치하고 이 튜토리얼을 위한 저장소를 클론하여 Bazel을 사용한 첫 번째 빌드의 기반을 마련했습니다. 다음 섹션으로 이동하여 몇 가지 용어를 정의하고 작업공간을 설정합니다.

시작하기

작업공간 설정

프로젝트를 빌드하려면 먼저 작업공간을 설정해야 합니다. 작업공간은 프로젝트의 소스 파일과 Bazel의 빌드 출력을 포함하는 디렉터리입니다. 여기에는 다음과 같은 중요한 파일도 포함됩니다.

  • WORKSPACE file : 디렉터리와 해당 콘텐츠를 Bazel 작업공간으로 식별하고 프로젝트의 디렉터리 구조 루트에 있습니다.
  • Bazel에게 프로젝트의 여러 부분을 빌드하는 방법을 알려주는 하나 이상의 BUILD files . BUILD 파일이 포함된 작업공간 내 디렉터리는 패키지입니다. 이 가이드의 뒷부분에서 패키지에 대해 자세히 알아보세요.

향후 프로젝트에서 디렉터리를 Bazel 작업공간으로 지정하려면 해당 디렉터리에 WORKSPACE라는 빈 파일을 만듭니다. 이 튜토리얼에서는 각 단계에 WORKSPACE 파일이 이미 있습니다.

참고: Bazel이 프로젝트를 빌드할 때 모든 입력은 동일한 작업공간에 있어야 합니다. 서로 다른 작업공간에 있는 파일은 연결되지 않는 한 서로 독립적입니다. 작업공간 규칙에 대한 자세한 내용은 이 가이드에서 확인할 수 있습니다.

BUILD 파일 이해

BUILD 파일에는 Bazel에 대한 여러 유형의 지침이 있습니다. 각 BUILD 파일에는 실행 가능한 바이너리 또는 라이브러리와 같이 원하는 출력을 빌드하는 방법을 Bazel에 알려주는 하나 이상의 규칙이 필요합니다. BUILD 파일에 있는 빌드 규칙의 각 인스턴스는 대상이라고 하며 특정 소스 파일 및 종속 항목을 가리킵니다. 대상은 다른 대상을 가리킬 수도 있습니다.

cpp-tutorial/stage1/main 디렉터리에서 BUILD 파일을 살펴보세요.

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

이 예시에서 hello-world 대상은 Bazel의 기본 제공 cc_binary rule를 인스턴스화합니다. 규칙은 종속 항목 없이 hello-world.cc 소스 파일에서 독립 실행 가능한 실행 가능한 바이너리를 빌드하도록 Bazel에 지시합니다.

요약: 시작하기

이제 몇 가지 주요 용어와 이 프로젝트와 Bazel의 일반적인 의미를 살펴보겠습니다. 다음 섹션에서는 프로젝트의 1단계를 빌드하고 테스트합니다.

1단계: 단일 타겟, 단일 패키지

이제 프로젝트의 첫 번째 부분을 빌드해보겠습니다. 시각적 참조를 위해 프로젝트의 1단계 섹션 구조는 다음과 같습니다.

examples
└── cpp-tutorial
    └──stage1
       ├── main
       │   ├── BUILD
       │   └── hello-world.cc
       └── WORKSPACE

다음을 실행하여 cpp-tutorial/stage1 디렉터리로 이동합니다.

cd cpp-tutorial/stage1

다음을 실행합니다.

bazel build //main:hello-world

타겟 라벨에서 //main: 부분은 작업공간의 루트를 기준으로 한 BUILD 파일의 위치이고 hello-worldBUILD 파일의 타겟 이름입니다.

Bazel은 다음과 같은 것을 생성합니다.

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s

첫 번째 Bazel 대상을 빌드했습니다. Bazel은 빌드 출력을 작업공간의 루트에 있는 bazel-bin 디렉터리에 배치합니다.

이제 새로 빌드한 바이너리를 테스트합니다.

bazel-bin/main/hello-world

그러면 'Hello world' 메시지가 출력됩니다.

다음은 1단계의 종속 항목 그래프입니다.

hello-world의 종속 항목 그래프는 단일 소스 파일과 함께 단일 대상을 표시합니다.

요약: 1단계

이제 첫 번째 빌드를 완료했으므로 빌드가 어떻게 구조화되는지에 대한 기본적인 아이디어가 떠올랐습니다. 다음 단계에서는 다른 타겟을 추가하여 복잡성을 추가합니다.

2단계: 여러 빌드 타겟

작은 프로젝트에는 단일 대상면 충분하지만 대규모 프로젝트를 여러 타겟과 패키지로 분할할 수 있습니다. 이렇게 하면 빠르게 증가하는 빌드(즉, 변경된 사항만 다시 빌드)가 가능하며, 한 번에 프로젝트의 여러 부분을 빌드하여 빌드 속도를 높일 수 있습니다. 이 튜토리얼 단계에서는 대상을 추가하고 다음 단계에서는 패키지를 추가합니다.

이 디렉터리는 2단계에서 사용할 디렉터리입니다.

    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE

cpp-tutorial/stage2/main 디렉터리의 BUILD 파일을 아래에서 확인하세요.

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

BUILD 파일을 사용하여 Bazel은 먼저 hello-greet 라이브러리를 빌드한 다음(Bazel의 기본 제공 cc_library rule 사용) hello-world 바이너리를 빌드합니다. hello-world 대상의 deps 속성은 hello-world 바이너리를 빌드하려면 hello-greet 라이브러리가 필요하다고 Bazel에 알립니다.

이 새 버전의 프로젝트를 빌드하기 전에 다음을 실행하여 디렉터리를 변경하고 cpp-tutorial/stage2 디렉터리로 전환해야 합니다.

cd ../stage2

이제 다음과 같은 익숙한 명령어를 사용하여 새 바이너리를 빌드할 수 있습니다.

bazel build //main:hello-world

다시 Bazel이 다음과 같은 결과를 생성합니다.

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s

이제 새로 빌드한 바이너리를 테스트하여 'Hello world'를 다시 반환합니다.

bazel-bin/main/hello-world

이제 hello-greet.cc를 수정하고 프로젝트를 다시 빌드하면 Bazel이 해당 파일만 다시 컴파일합니다.

종속 항목 그래프를 보면 hello-world가 이전과 같은 입력에 종속되지만 빌드 구조는 다른 것을 확인할 수 있습니다.

`hello-world` 의 종속 항목 그래프는 파일 수정 후 구조 변경사항을 표시합니다.

요약: 2단계

이제 대상 두 개로 프로젝트가 빌드되었습니다. hello-world 타겟은 하나의 소스 파일을 빌드하고 다른 두 대상 파일(//main:hello-greet)에 종속되어 두 개의 추가 소스 파일을 빌드합니다. 다음 섹션에서는 한 단계 더 나아가 다른 패키지를 추가합니다.

3단계: 여러 패키지

이 다음 단계에서는 다른 정보 표시 레이어를 추가하고 여러 패키지가 있는 프로젝트를 빌드합니다. 아래에서 cpp-tutorial/stage3 디렉터리의 구조와 콘텐츠를 살펴보세요.

└──stage3
   ├── main
   │   ├── BUILD
   │   ├── hello-world.cc
   │   ├── hello-greet.cc
   │   └── hello-greet.h
   ├── lib
   │   ├── BUILD
   │   ├── hello-time.cc
   │   └── hello-time.h
   └── WORKSPACE

이제 두 개의 하위 디렉터리가 있고 각 디렉터리에는 BUILD 파일이 있는 것을 확인할 수 있습니다. 따라서 Bazel에 대한 작업공간의 패키지 두 개(libmain)가 포함됩니다.

lib/BUILD 파일을 살펴보세요.

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

main/BUILD 파일에서 다음을 실행합니다.

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "//lib:hello-time",
    ],
)

기본 패키지의 hello-world 대상은 lib 패키지의 hello-time 대상 (따라서 대상 라벨 //lib:hello-time)에 종속됩니다. Bazel은 deps 속성을 통해 이를 알고 있습니다. 종속 항목 그래프에서 이를 확인할 수 있습니다.

`hello-world` 의 종속 항목 그래프는 기본 패키지의 타겟이 `lib` 패키지의 타겟에 어떻게 종속되는지 보여줍니다.

성공적으로 빌드하려면 공개 상태 속성을 사용하여 lib/BUILD//lib:hello-time 대상을 main/BUILD의 타겟에 명시적으로 표시합니다. 기본적으로 대상은 동일한 BUILD 파일의 다른 타겟에만 표시되기 때문입니다. Bazel은 대상 공개 상태를 사용하여 구현 세부정보가 포함된 라이브러리와 같은 API가 공개 API로 유출되는 문제를 방지합니다.

이제 이 프로젝트의 최종 버전을 빌드합니다. 다음을 실행하여 cpp-tutorial/stage3 디렉터리로 전환합니다.

cd  ../stage3

다시 다음 명령어를 실행합니다.

bazel build //main:hello-world

Bazel은 다음과 같은 것을 생성합니다.

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s

이제 이 튜토리얼의 마지막 바이너리를 테스트하여 최종 Hello world 메시지를 표시합니다.

bazel-bin/main/hello-world

요약: 3단계

이제 프로젝트를 세 개의 패키지가 포함된 두 개의 패키지로 빌드했으며 이 둘 간의 종속 항목을 이해했습니다. 따라서 Bazel을 사용하여 향후 프로젝트를 빌드할 수 있습니다. 다음 섹션에서는 Bazel 여정을 계속하는 방법을 알아봅니다.

다음 단계

Bazel을 사용하여 첫 번째 기본 빌드를 완료했지만, 이것은 시작에 불과합니다. 다음은 Bazel을 사용하여 계속 학습할 수 있는 몇 가지 추가 리소스입니다.

즐겁게 빌드하세요!