Hướng dẫn của Bazel: Xây dựng dự án C++

Báo cáo vấn đề Xem nguồn Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Giới thiệu

Bạn mới sử dụng Bazel? Bạn đã đến đúng nơi. Hãy làm theo hướng dẫn về Bản dựng đầu tiên này để biết nội dung giới thiệu đơn giản về cách sử dụng Bazel. Hướng dẫn này xác định các thuật ngữ chính khi chúng được sử dụng trong ngữ cảnh của Bazel và hướng dẫn bạn về các kiến thức cơ bản của quy trình công việc Bazel. Bắt đầu với các công cụ cần thiết, bạn sẽ xây dựng và chạy 3 dự án với độ phức tạp ngày càng cao, đồng thời tìm hiểu cách thức và lý do khiến các dự án đó trở nên phức tạp hơn.

Mặc dù Bazel là một hệ thống xây dựng hỗ trợ các bản dựng đa ngôn ngữ, nhưng hướng dẫn này sử dụng dự án C++ làm ví dụ và cung cấp các nguyên tắc và quy trình chung áp dụng cho hầu hết các ngôn ngữ.

Thời gian hoàn thành ước tính: 30 phút.

Điều kiện tiên quyết

Bắt đầu bằng cách cài đặt Bazel, nếu bạn chưa cài đặt. Hướng dẫn này sử dụng Git để kiểm soát nguồn, vì vậy, để có kết quả tốt nhất, hãy cài đặt Git.

Tiếp theo, hãy truy xuất dự án mẫu từ kho lưu trữ GitHub của Bazel bằng cách chạy lệnh sau trong công cụ dòng lệnh mà bạn chọn:

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

Dự án mẫu cho hướng dẫn này nằm trong thư mục examples/cpp-tutorial.

Hãy xem cách cấu trúc của tệp này:

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

Có ba nhóm tệp, mỗi nhóm đại diện cho một giai đoạn trong hướng dẫn này. Ở giai đoạn đầu tiên, bạn sẽ tạo một mục tiêu duy nhất nằm trong một gói duy nhất. Ở giai đoạn thứ hai, bạn sẽ tạo cả tệp nhị phân và thư viện từ một gói duy nhất. Trong giai đoạn thứ ba và cũng là giai đoạn cuối cùng, bạn sẽ tạo một dự án có nhiều gói và xây dựng dự án đó với nhiều mục tiêu.

Tóm tắt: Giới thiệu

Bằng cách cài đặt Bazel (và Git) và nhân bản kho lưu trữ cho hướng dẫn này, bạn đã đặt nền tảng cho bản dựng đầu tiên của mình bằng Bazel. Tiếp tục sang phần tiếp theo để xác định một số thuật ngữ và thiết lập không gian làm việc.

Bắt đầu

Trước khi có thể tạo một dự án, bạn cần thiết lập không gian làm việc của dự án đó. Không gian làm việc là một thư mục chứa các tệp nguồn của dự án và đầu ra bản dựng của Bazel. Thư viện này cũng chứa các tệp quan trọng sau:

  • Tệp MODULE.bazel xác định thư mục và nội dung của thư mục đó là một không gian làm việc Bazel và nằm ở gốc của cấu trúc thư mục dự án. Đây cũng là nơi bạn chỉ định các phần phụ thuộc bên ngoài.
  • Một hoặc nhiều tệp BUILD, cho Bazel biết cách tạo các phần khác nhau của dự án. Thư mục trong không gian làm việc chứa tệp BUILD là một gói. (Tìm hiểu thêm về các gói trong phần sau của hướng dẫn này.)

Trong các dự án sau này, để chỉ định một thư mục làm không gian làm việc Bazel, hãy tạo một tệp trống có tên là MODULE.bazel trong thư mục đó. Đối với mục đích của hướng dẫn này, tệp MODULE.bazel đã có trong mỗi giai đoạn.

Tìm hiểu về tệp BUILD

Tệp BUILD chứa một số loại hướng dẫn cho Bazel. Mỗi tệp BUILD yêu cầu ít nhất một quy tắc dưới dạng một tập hợp hướng dẫn, cho Bazel biết cách tạo ra các đầu ra mà bạn muốn, chẳng hạn như các tệp nhị phân có thể thực thi hoặc thư viện. Mỗi thực thể của quy tắc bản dựng trong tệp BUILD được gọi là mục tiêu và trỏ đến một nhóm tệp nguồn và phần phụ thuộc cụ thể. Một mục tiêu cũng có thể trỏ đến các mục tiêu khác.

Hãy xem tệp BUILD trong thư mục cpp-tutorial/stage1/main:

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

Trong ví dụ này, mục tiêu hello-world tạo thực thể cho quy tắc cc_binary tích hợp sẵn của Bazel. Quy tắc này yêu cầu Bazel tạo một tệp nhị phân thực thi độc lập từ tệp nguồn hello-world.cc không có phần phụ thuộc.

Tóm tắt: bắt đầu

Bây giờ, bạn đã quen với một số thuật ngữ chính, cũng như ý nghĩa của những thuật ngữ đó trong bối cảnh của dự án này và Bazel nói chung. Trong phần tiếp theo, bạn sẽ xây dựng và kiểm thử Giai đoạn 1 của dự án.

Giai đoạn 1: một mục tiêu duy nhất, một gói duy nhất

Đã đến lúc xây dựng phần đầu tiên của dự án. Để tham khảo hình ảnh, cấu trúc của phần Giai đoạn 1 của dự án là:

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

Chạy lệnh sau để chuyển đến thư mục cpp-tutorial/stage1:

cd cpp-tutorial/stage1

Tiếp theo, hãy chạy:

bazel build //main:hello-world

Trong nhãn mục tiêu, phần //main: là vị trí của tệp BUILD so với thư mục gốc của không gian làm việc và hello-world là tên mục tiêu trong tệp BUILD.

Bazel sẽ tạo ra một kết quả như sau:

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

Bạn vừa tạo mục tiêu Bazel đầu tiên của mình. Bazel đặt đầu ra bản dựng trong thư mục bazel-bin ở gốc của không gian làm việc.

Bây giờ, hãy kiểm thử tệp nhị phân mới tạo:

bazel-bin/main/hello-world

Thao tác này sẽ in thông báo "Hello world".

Dưới đây là biểu đồ phần phụ thuộc của Giai đoạn 1:

Biểu đồ phần phụ thuộc cho hello-world hiển thị một mục tiêu duy nhất với một tệp nguồn duy nhất.

Tóm tắt: giai đoạn 1

Giờ thì bạn đã hoàn thành bản dựng đầu tiên, bạn đã có ý tưởng cơ bản về cấu trúc bản dựng. Ở giai đoạn tiếp theo, bạn sẽ tăng độ phức tạp bằng cách thêm một mục tiêu khác.

Giai đoạn 2: nhiều mục tiêu bản dựng

Mặc dù một mục tiêu duy nhất là đủ cho các dự án nhỏ, nhưng bạn có thể muốn chia các dự án lớn hơn thành nhiều mục tiêu và gói. Điều này cho phép các bản dựng gia tăng nhanh chóng – tức là Bazel chỉ xây dựng lại những nội dung thay đổi – và tăng tốc các bản dựng bằng cách xây dựng nhiều phần của dự án cùng một lúc. Giai đoạn này của hướng dẫn thêm một mục tiêu và giai đoạn tiếp theo sẽ thêm một gói.

Đây là thư mục bạn đang làm việc cho Giai đoạn 2:

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

Hãy xem tệp BUILD trong thư mục cpp-tutorial/stage2/main:

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",
    ],
)

Với tệp BUILD này, trước tiên, Bazel sẽ tạo thư viện hello-greet (sử dụng quy tắc cc_library tích hợp của Bazel), sau đó là tệp nhị phân hello-world. Thuộc tính deps trong mục tiêu hello-world cho Baazel biết rằng cần phải có thư viện hello-greet để tạo tệp nhị phân hello-world.

Trước khi có thể tạo phiên bản mới của dự án này, bạn cần thay đổi thư mục, chuyển sang thư mục cpp-tutorial/stage2 bằng cách chạy:

cd ../stage2

Bây giờ, bạn có thể tạo tệp nhị phân mới bằng lệnh quen thuộc sau:

bazel build //main:hello-world

Một lần nữa, Bazel sẽ tạo ra một kết quả như sau:

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

Bây giờ, bạn có thể kiểm thử tệp nhị phân mới tạo, tệp này sẽ trả về một "Hello world" khác:

bazel-bin/main/hello-world

Nếu bạn sửa đổi hello-greet.cc và tạo lại dự án, Bazel sẽ chỉ biên dịch lại tệp đó.

Nhìn vào biểu đồ phần phụ thuộc, bạn có thể thấy hello-world phụ thuộc vào một dữ liệu đầu vào bổ sung có tên là hello-greet:

Biểu đồ phần phụ thuộc cho "hello-world" cho thấy các thay đổi về phần phụ thuộc sau khi sửa đổi tệp.

Tóm tắt: giai đoạn 2

Bây giờ, bạn đã tạo dự án với hai mục tiêu. Mục tiêu hello-world tạo một tệp nguồn và phụ thuộc vào một mục tiêu khác (//main:hello-greet), mục tiêu này tạo thêm hai tệp nguồn. Trong phần tiếp theo, hãy tiến thêm một bước và thêm một gói khác.

Giai đoạn 3: nhiều gói

Giai đoạn tiếp theo này sẽ thêm một lớp chức năng phức tạp khác và tạo một dự án có nhiều gói. Hãy xem cấu trúc và nội dung của thư mục cpp-tutorial/stage3:

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

Bạn có thể thấy rằng hiện có hai thư mục con và mỗi thư mục chứa một tệp BUILD. Do đó, đối với Bazel, không gian làm việc hiện chứa hai gói: libmain.

Hãy xem tệp lib/BUILD:

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

Và tại tệp 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",
    ],
)

Mục tiêu hello-world trong gói chính phụ thuộc vào mục tiêu hello-time trong gói lib (do đó nhãn mục tiêu là //lib:hello-time) – Bazel biết điều này thông qua thuộc tính deps. Bạn có thể thấy điều này được phản ánh trong biểu đồ phần phụ thuộc:

Biểu đồ phần phụ thuộc cho "hello-world" cho biết cách mục tiêu trong gói chính phụ thuộc vào mục tiêu trong gói "lib".

Để bản dựng thành công, bạn phải làm cho mục tiêu //lib:hello-time trong lib/BUILD hiển thị rõ ràng cho các mục tiêu trong main/BUILD bằng cách sử dụng thuộc tính chế độ hiển thị. Lý do là theo mặc định, các mục tiêu chỉ hiển thị với các mục tiêu khác trong cùng một tệp BUILD. Bazel sử dụng chế độ hiển thị mục tiêu để ngăn các vấn đề như thư viện chứa thông tin chi tiết về việc triển khai bị rò rỉ vào API công khai.

Bây giờ, hãy tạo phiên bản cuối cùng của dự án này. Chuyển sang thư mục cpp-tutorial/stage3 bằng cách chạy:

cd  ../stage3

Chạy lại lệnh sau:

bazel build //main:hello-world

Bazel tạo ra thành phần có dạng như sau:

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

Bây giờ, hãy kiểm tra tệp nhị phân cuối cùng của hướng dẫn này để biết thông báo Hello world cuối cùng:

bazel-bin/main/hello-world

Tóm tắt: giai đoạn 3

Giờ đây, bạn đã tạo dự án dưới dạng hai gói với ba mục tiêu và hiểu rõ các phần phụ thuộc giữa các gói đó. Điều này giúp bạn có thể tiếp tục và tạo các dự án trong tương lai bằng Bazel. Trong phần tiếp theo, hãy xem cách tiếp tục hành trình khám phá về Bazel.

Các bước tiếp theo

Hiện bạn đã hoàn thành bản dựng cơ bản đầu tiên với Bazel, nhưng đây mới chỉ là bước khởi đầu. Dưới đây là một số tài nguyên khác để bạn tiếp tục tìm hiểu về Bazel:

Chúc bạn xây dựng vui vẻ!