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

Báo cáo vấn đề Xem nguồn Hằng đêm · 7,3 · 7,2 · 7.1 · 7 · 6,5

Giới thiệu

Bạn mới sử dụng Bazel? Bạn đã đến đúng nơi rồi đấy. Hãy làm theo hướng dẫn Tạo đầu tiên này để biết hướng dẫn đơn giản về cách sử dụng Bazel. Hướng dẫn này định nghĩa các thuật ngữ chính vì các thuật ngữ đó được sử dụng trong bối cảnh của Bazel và hướng dẫn bạn tìm hiểu những thông tin cơ bản về Bazel quy trình làm việc. Bắt đầu với các công cụ cần thiết, bạn sẽ tạo và chạy 3 giai đoạn các dự án ngày càng phức tạp hơn và tìm hiểu cách thức cũng như lý do khiến chúng 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ữ, hướng dẫn này sẽ dùng một dự án C++ làm ví dụ đồng thời 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 rồi. 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 sau đây 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 examples/cpp-tutorial thư mục.

Hãy xem cấu trúc của huy hiệu 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 tập hợp tệp, mỗi tập đại diện cho một giai đoạn trong hướng dẫn này. Trong 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. Trong 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. Ở vòng thứ ba và cuối cùng bạn sẽ tạo một dự án có nhiều gói và dùng 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à sao chép kho lưu trữ cho hướng dẫn này, bạn đã đặt nền móng cho công trình đầu tiên của bạn với Bazel. Chuyển đến phần tiếp theo để định nghĩa một số thuật ngữ và thiết lập không gian làm việc.

Bắt đầu

Trước khi tạo dự án, bạn cần thiết lập không gian làm việc của dự án đó. Một không gian làm việc là một thư mục lưu giữ các tệp nguồn của dự án và dữ liệu đầu ra của 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 trong đó là một không gian làm việc Bazel và hoạt động tại thư mục gốc của dự án cấu trúc. Đâ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 BUILD tệp để cho Bazel biết cách xây dựng các phần khác nhau của dự án. Một thư mục trong không gian làm việc chứa tệp BUILD là một gói. (Thông tin khác về các gói ở phần sau của hướng dẫn này).

Trong các dự án trong tương lai, để 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 MODULE.bazel trong thư mục đó. Theo mục đích của tài liệu này, hướng dẫn, 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ột BUILD tệp cần có ít nhất một tệp quy tắc dưới dạng một bộ hướng dẫn, cho Bazel biết cách tạo đầ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ột nhắm mục tiêu và trỏ đến một tập hợp các tệp nguồn và phần phụ thuộc. Mục tiêu có thể đồng thời 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ể của hàm tích hợp sẵn của Bazel Quy tắc cc_binary. Quy tắc yêu cầu Bazel tạo một tệp nhị phân thực thi độc lập từ hello-world.cc> tệp nguồn 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 và ý nghĩa của chúng khi nói đến thuật ngữ dự án này nói chung và Bazel nói chung. Trong phần tiếp theo, bạn sẽ tạo 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 sang 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 đích, phần //main: là vị trí của tệp BUILD tương ứng với 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 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: 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 kết quả của bản dựng trong Thư mục bazel-bin ở gốc không gian làm việc.

Bây giờ, hãy kiểm thử tệp nhị phân được tạo mới, đó là:

bazel-bin/main/hello-world

Kết quả là một "Hello world" được in .

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 thông qua một nguồn duy nhất
.

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

Giờ đây, bạn đã hoàn thành bản dựng đầu tiên của mình, bạn có ý tưởng cơ bản về cách có cấu trúc rõ rà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 tách 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 tăng dần – tức là Bazel chỉ xây dựng lại những nội dung thay đổi – và tăng tốc bản dựng bằng cách tạo 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 sẽ thêm mục tiêu và phần tiếp theo sẽ thêm gói.

Đây là thư mục mà bạn đang xử lý 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 cc_library được tích hợp sẵn của Bazel ), thì phương thức Tệp nhị phân hello-world. Thuộc tính deps trong mục tiêu hello-world cho biết Bazel nói rằng thư viện hello-greet là bắt buộc để tạo hello-world nhị phân.

Trước khi xây dựng phiên bản mới này của dự án, bạn cần thay đổi các 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 tạo ra thành phần 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 được tạo mới, tệp này trả về một "Hello world" khác:

bazel-bin/main/hello-world

Nếu bây giờ bạn sửa đổi hello-greet.cc và tạo lại dự án, thì chỉ Bazel 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 rằng hello-world phụ thuộc vào một dữ liệu đầu vào bổ sung có tên 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
sửa đổi tệp.

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

Lúc này, bạn đã xây dựng dự án với hai mục tiêu. Bản dựng mục tiêu hello-world một tệp nguồn và phụ thuộc vào một mục tiêu khác (//main:hello-greet), điều này tạo hai tệp nguồn bổ sung. 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 thêm một lớp chức năng khác và tạo 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, trong đó mỗi thư mục chứa một BUILD . Do đó, đối với Bazel, không gian làm việc hiện chứa 2 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 đó có nhãn mục tiêu //lib:hello-time) – Bazel biết thao tác 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 phần phụ thuộc biểu đồ:

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

Để tạo bản dựng thành công, bạn cần đặt 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 thuộc tính chế độ hiển thị. Điều này là do 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 Tệp BUILD. Bazel sử dụng chế độ hiển thị mục tiêu để ngăn chặn các vấn đề như thư viện chứa chi tiết 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 này của dự án. Chuyển sang cpp-tutorial/stage3 bằng cách chạy:

cd  ../stage3

Một lần nữa, hãy chạy 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

Lúc này, bạn đã xây dựng dự án dưới dạng hai gói với 3 mục tiêu và đã hiểu các yếu tố phụ thuộc giữa chúng, trang bị cho bạn để tiếp tục và xây dựng tương lai dự án của Bazel. Trong phần tiếp theo, hãy xem cách tiếp tục Hành trình của 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 của mình với Bazel, nhưng đây chỉ là đầu. Dưới đây là một số tài nguyên khác để tiếp tục tìm hiểu cùng Bazel:

Chúc bạn tạo dựng thành công!