Bazel 튜토리얼: C++ 프로젝트 빌드

문제 신고 소스 보기 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

소개

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 작업공간으로 식별하며 프로젝트 디렉터리 구조의 루트에 있습니다.
  • 하나 이상의 BUILD files : Bazel에 프로젝트의 여러 부분을 빌드하는 방법을 알려줍니다. 작업공간 내에 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를 인스턴스화합니다. 이 규칙은 Bazel에게 종속 항목이 없는 hello-world.cc 소스 파일에서 독립형 실행 파일 바이너리를 빌드하도록 지시합니다.

요약: 시작하기

이제 주요 용어와 이 프로젝트 및 일반적인 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-greet 라이브러리가 hello-world 바이너리를 빌드하는 데 필요하다고 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` 패키지의 타겟에 종속되는 방식을 보여줍니다.

빌드가 성공하려면 visibility 속성을 사용하여 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단계

이제 타겟이 3개인 패키지 2개로 프로젝트를 빌드하고 패키지 간의 종속 항목을 이해했으므로 Bazel로 향후 프로젝트를 빌드할 수 있습니다. 다음 섹션에서는 Bazel 여정을 계속하는 방법을 살펴봅니다.

다음 단계

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

즐거운 빌드 되세요!