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단계: 여러 빌드 대상

소규모 프로젝트에는 단일 타겟으로도 충분하지만 대규모 프로젝트를 여러 타겟과 패키지로 분할하는 것이 좋습니다. 이를 통해 Bazel은 변경된 사항만 다시 빌드하므로 프로젝트의 여러 부분을 한 번에 빌드하여 빌드 속도를 높일 수 있습니다. 이 튜토리얼 단계에서는 대상을 추가하고 다음 단계에서는 패키지를 추가합니다.

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-worldhello-greet라는 추가 입력에 종속되어 있음을 알 수 있습니다.

'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로 누출되는 문제를 방지하기 위해 타겟 공개 상태를 사용합니다.

이제 프로젝트의 최종 버전을 빌드합니다. 다음을 실행하여 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을 계속 학습할 수 있는 추가 리소스입니다.

즐겁게 빌드하세요!