Xây dựng chương trình với Bazel

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Báo cáo vấn đề Xem nguồn

Trang này trình bày cách tạo chương trình bằng Bazel, cú pháp lệnh tạo và cú pháp mẫu mục tiêu.

Bắt đầu nhanh

Để chạy Bazel, hãy chuyển đến thư mục workspace cơ sở hoặc bất kỳ thư mục con nào trong đó và nhập bazel. Hãy xem bản dựng nếu bạn cần tạo một không gian làm việc mới.

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

Các lệnh có sẵn

  • analyze-profile: Phân tích dữ liệu hồ sơ bản dựng.
  • aquery: Thực thi truy vấn trên biểu đồ hành động sau khi phân tích.
  • build: Xây dựng các mục tiêu được chỉ định.
  • canonicalize-flags: Chuẩn hóa cờ Bazel.
  • clean: Xoá tệp đầu ra và dừng máy chủ.
  • cquery: Thực thi truy vấn biểu đồ phần phụ thuộc sau khi phân tích.
  • dump: Kết xuất trạng thái nội bộ của quy trình máy chủ Bazel.
  • help: In trợ giúp về các lệnh hoặc chỉ mục.
  • info: Hiển thị thông tin về thời gian chạy về máy chủ bazel.
  • fetch: Tìm nạp tất cả phần phụ thuộc bên ngoài của một mục tiêu.
  • mobile-install: Cài đặt ứng dụng trên thiết bị di động.
  • query: Thực thi một truy vấn biểu đồ phần phụ thuộc.
  • run: Chạy mục tiêu đã chỉ định.
  • shutdown: Dừng máy chủ của Bazel.
  • test: Xây dựng và chạy các mục tiêu kiểm thử đã chỉ định.
  • version: In thông tin phiên bản của Bazel.

Nhận trợ giúp

  • bazel help command: In trợ giúp và các tùy chọn cho command.
  • bazel helpstartup_options: Các tùy chọn dành cho JVM lưu trữ Bazel.
  • bazel helptarget-syntax: Giải thích về cú pháp để chỉ định mục tiêu.
  • bazel help info-keys: Hiển thị danh sách các khoá được lệnh thông tin sử dụng.

Công cụ bazel thực hiện nhiều hàm, được gọi là lệnh. Các lệnh gọi phổ biến nhất là bazel buildbazel test. Bạn có thể duyệt qua các thông báo trợ giúp trực tuyến bằng cách sử dụng bazel help.

Xây dựng một mục tiêu

Để có thể bắt đầu xây dựng, bạn cần có một không gian làm việc. Không gian làm việc là một cây thư mục chứa tất cả các tệp nguồn cần thiết để tạo ứng dụng của bạn. Bazel cho phép bạn thực hiện bản dựng từ khối lượng hoàn toàn chỉ đọc.

Để tạo một chương trình bằng Bazel, hãy nhập bazel build, sau đó nhập mục tiêu mà bạn muốn tạo.

bazel build //foo

Sau khi tạo lệnh tạo //foo, bạn sẽ thấy kết quả xuất ra có dạng như sau:

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

Trước tiên, Bazel tải tất cả các gói trong biểu đồ phần phụ thuộc của mục tiêu. Dữ liệu này bao gồm các phần phụ thuộc đã khai báo, các tệp được liệt kê trực tiếp trong tệp BUILD của mục tiêu và các phần phụ thuộc bắc cầu, các tệp được liệt kê trong các tệp BUILD về các phần phụ thuộc của mục tiêu. Sau khi xác định được tất cả các phần phụ thuộc, Bazel sẽ phân tích các phần phụ thuộc đó để đảm bảo độ chính xác và tạo thao tác tạo bản dựng. Cuối cùng, Bazel sẽ thực thi trình biên dịch và các công cụ khác của bản dựng.

Trong giai đoạn thực thi bản dựng, Bazel sẽ in thông báo tiến trình. Thông báo tiến trình bao gồm bước tạo bản dựng hiện tại (chẳng hạn như trình biên dịch hoặc trình liên kết) khi bước đó bắt đầu và số đã hoàn thành trên tổng số hành động tạo bản dựng. Khi bản dựng bắt đầu, tổng số hành động thường tăng lên khi Bazel phát hiện thấy toàn bộ biểu đồ hành động, nhưng số lượng ổn định trong vòng vài giây.

Vào cuối bản dựng, Bazel sẽ in các mục tiêu đã được yêu cầu, liệu các mục tiêu đó có được tạo thành công hay không và nếu có, thì có thể tìm thấy các tệp đầu ra ở đâu. Các tập lệnh chạy bản dựng có thể phân tích cú pháp kết quả đầu ra này một cách đáng tin cậy; hãy xem --show_result để biết thêm thông tin chi tiết.

Nếu bạn nhập lại cùng một lệnh, bản dựng sẽ hoàn tất nhanh hơn nhiều.

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

Đây là một bản dựng rỗng. Vì không có gì thay đổi, nên không có gói nào để tải lại và không có bước xây dựng nào để thực thi. Nếu có thay đổi trong "foo" hoặc các phần phụ thuộc, Bazel sẽ thực thi lại một số hành động xây dựng hoặc hoàn tất bản dựng tăng dần.

Xây dựng nhiều mục tiêu

Bazel cho phép xây dựng một số cách để chỉ định mục tiêu. Đây được gọi là mẫu mục tiêu. Cú pháp này được sử dụng trong các lệnh như build, test hoặc query.

Trong khi nhãn được dùng để chỉ định từng mục tiêu riêng lẻ, chẳng hạn như để khai báo các phần phụ thuộc trong tệp BUILD, thì mẫu mục tiêu của Bazel sẽ chỉ định nhiều mục tiêu. Mẫu mục tiêu là nội dung tổng quát về cú pháp nhãn cho nhóm mục tiêu, bằng cách sử dụng ký tự đại diện. Trong trường hợp đơn giản nhất, mọi nhãn hợp lệ cũng là một mẫu mục tiêu hợp lệ, xác định một nhóm chính xác một mục tiêu.

Tất cả mẫu mục tiêu bắt đầu bằng // đều được phân giải dựa trên không gian làm việc hiện tại.

//foo/bar:wiz Chỉ mục tiêu duy nhất //foo/bar:wiz.
//foo/bar Tương đương với //foo/bar:bar.
//foo/bar:all Tất cả mục tiêu quy tắc trong gói foo/bar.
//foo/... Tất cả các mục tiêu quy tắc trong tất cả các gói bên dưới thư mục foo.
//foo/...:all Tất cả các mục tiêu quy tắc trong tất cả các gói bên dưới thư mục foo.
//foo/...:* Tất cả mục tiêu (quy tắc và tệp) trong tất cả các gói bên dưới thư mục foo.
//foo/...:all-targets Tất cả mục tiêu (quy tắc và tệp) trong tất cả các gói bên dưới thư mục foo.
//... Tất cả mục tiêu trong các gói trong không gian làm việc. Số liệu này không bao gồm các mục tiêu từ kho lưu trữ bên ngoài.
//:all Tất cả mục tiêu trong gói cấp cao nhất nếu có tệp `BUILD` ở gốc của không gian làm việc.

Các mẫu mục tiêu không bắt đầu bằng // được phân giải tương ứng với thư mục làm việc hiện tại. Những ví dụ này giả định một thư mục hoạt động của foo:

:foo Tương đương với //foo:foo.
bar:wiz Tương đương với //foo/bar:wiz.
bar/wiz Tương đương với:
  • //foo/bar/wiz:wiz nếu foo/bar/wiz là một gói
  • //foo/bar:wiz nếu foo/bar là một gói
  • Nếu không thì //foo:bar/wiz
bar:all Tương đương với //foo/bar:all.
:all Tương đương với //foo:all.
...:all Tương đương với //foo/...:all.
... Tương đương với //foo/...:all.
bar/...:all Tương đương với //foo/bar/...:all.

Theo mặc định, các đường liên kết tượng trưng thư mục sẽ được theo dõi đối với các mẫu mục tiêu định kỳ, ngoại trừ các mẫu liên kết trỏ đến cơ sở đầu ra, chẳng hạn như các đường liên kết tượng trưng tiện lợi được tạo trong thư mục gốc của không gian làm việc.

Ngoài ra, Bazel không đi theo các đường liên kết tượng trưng khi đánh giá các mẫu mục tiêu định kỳ trong bất kỳ thư mục nào chứa tệp như sau: DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN

foo/... là ký tự đại diện trên gói, cho biết tất cả các gói đệ quy trong thư mục foo (đối với tất cả các gốc của đường dẫn gói). :all là một ký tự đại diện trên các mục tiêu, khớp với tất cả các quy tắc trong một gói. Hai thẻ này có thể được kết hợp với nhau, như trong foo/...:all và khi sử dụng cả hai ký tự đại diện, thì tên này có thể được viết tắt là foo/....

Ngoài ra, :* (hoặc :all-targets) là ký tự đại diện khớp với mọi mục tiêu trong các gói đã khớp, bao gồm cả các tệp thường không được tạo bởi quy tắc nào, chẳng hạn như các tệp _deploy.jar liên kết với quy tắc java_binary.

Điều này có nghĩa là :* biểu thị một tập hợp mẹ:all; tuy có thể gây nhầm lẫn, nhưng cú pháp này cho phép sử dụng ký tự đại diện :all quen thuộc cho các bản dựng điển hình, trong đó các mục tiêu bản dựng như _deploy.jar không được mong muốn.

Ngoài ra, Bazel cũng cho phép sử dụng dấu gạch chéo thay vì dấu hai chấm theo yêu cầu của cú pháp nhãn. Việc này thường thuận tiện khi sử dụng tính năng mở rộng tên tệp Bash. Ví dụ: foo/bar/wiz tương đương với //foo/bar:wiz (nếu có một gói foo/bar) hoặc //foo:bar/wiz (nếu có một gói foo).

Nhiều lệnh Bazel chấp nhận danh sách mẫu mục tiêu dưới dạng đối số và tất cả đều tuân theo toán tử phủ định tiền tố -. Bạn có thể sử dụng công cụ này để trừ một tập hợp mục tiêu từ tập hợp các đối số đã chỉ định trước đó. Xin lưu ý rằng điều này có nghĩa là đơn đặt hàng rất quan trọng. Ví dụ:

bazel build foo/... bar/...

có nghĩa là "tạo tất cả mục tiêu dưới foo tất cả mục tiêu dưới bar", trong khi

bazel build -- foo/... -foo/bar/...

có nghĩa là "tạo tất cả mục tiêu bên dưới foo ngoại trừ những mục tiêu dưới foo/bar". (Đối số -- là bắt buộc để ngăn các đối số tiếp theo bắt đầu bằng - được hiểu là các tuỳ chọn bổ sung).

Tuy nhiên, điều quan trọng cần lưu ý là việc trừ đi các mục tiêu theo cách này sẽ không đảm bảo rằng các mục tiêu đó không được tạo vì chúng có thể là phần phụ thuộc của các mục tiêu không bị trừ. Ví dụ: nếu có một //foo:all-apis mục tiêu mà trong số những mục tiêu khác phụ thuộc vào //foo/bar:api, thì mục tiêu sau sẽ được xây dựng như một phần trong quá trình xây dựng phiên bản cũ.

Các mục tiêu có tags = ["manual"] không được bao gồm trong các mẫu mục tiêu ký tự đại diện (..., :*, :all, v.v.) khi được chỉ định trong các lệnh như bazel buildbazel test; bạn nên chỉ định các mục tiêu thử nghiệm đó bằng các mẫu mục tiêu rõ ràng trên dòng lệnh nếu bạn muốn Bazel tạo/kiểm thử các mục tiêu đó. Ngược lại, bazel query không tự động thực hiện bất kỳ quá trình lọc nào như vậy (điều này sẽ đánh bại mục đích của bazel query).

Đang tìm nạp các phần phụ thuộc bên ngoài

Theo mặc định, Bazel sẽ tải xuống và liên kết các phần phụ thuộc bên ngoài trong quá trình xây dựng. Tuy nhiên, điều này là không mong muốn, vì bạn muốn biết thời điểm thêm các phần phụ thuộc bên ngoài mới hoặc vì bạn muốn các phần phụ thuộc "tìm nạp trước" (chẳng hạn như trước một chuyến bay mà bạn sẽ không có kết nối mạng). Nếu muốn ngăn việc thêm các phần phụ thuộc mới trong quá trình tạo bản dựng, bạn có thể chỉ định cờ --fetch=false. Lưu ý rằng cờ này chỉ áp dụng cho các quy tắc kho lưu trữ không trỏ đến một thư mục trong hệ thống tệp cục bộ. Ví dụ: các thay đổi đối với local_repository, new_local_repository cũng như các quy tắc kho lưu trữ SDK Android và NDK sẽ luôn có hiệu lực bất kể giá trị --fetch .

Nếu bạn không cho phép tìm nạp trong các bản dựng và Bazel tìm thấy các phần phụ thuộc bên ngoài mới, thì bản dựng của bạn sẽ không hoạt động.

Bạn có thể tìm nạp các phần phụ thuộc theo cách thủ công bằng cách chạy bazel fetch. Nếu không cho phép tìm nạp trong quá trình tạo bản dựng, bạn cần chạy bazel fetch:

  • Trước khi bạn tạo lần đầu.
  • Sau khi bạn thêm phần phụ thuộc bên ngoài mới.

Sau khi chạy, bạn không cần phải chạy lại tệp cho đến khi tệp WORKSPACE thay đổi.

fetch lấy danh sách các mục tiêu để tìm nạp các phần phụ thuộc. Ví dụ: lệnh này sẽ tìm nạp các phần phụ thuộc cần thiết để tạo //foo:bar//bar:baz:

bazel fetch //foo:bar //bar:baz

Để tìm nạp tất cả phần phụ thuộc bên ngoài cho một không gian làm việc, hãy chạy:

bazel fetch //...

Bạn không cần phải chạy tính năng tìm nạp bazel nếu có tất cả công cụ đang sử dụng (từ thư viện thư viện đến chính JDK) trong thư mục gốc của không gian làm việc. Tuy nhiên, nếu bạn đang sử dụng bên ngoài thư mục không gian làm việc, thì Bazel sẽ tự động chạy bazel fetch trước khi chạy bazel build.

Bộ nhớ đệm của kho lưu trữ

Bazel cố gắng tránh tìm nạp cùng một tệp nhiều lần, ngay cả khi cần cùng một tệp trong các không gian làm việc khác nhau hoặc nếu định nghĩa của kho lưu trữ bên ngoài thay đổi nhưng vẫn cần cùng tệp để tải xuống. Để thực hiện việc này, Bazel lưu tất cả các tệp đã tải xuống vào bộ nhớ đệm lưu trữ, theo mặc định, tệp này nằm tại ~/.cache/bazel/_bazel_$USER/cache/repos/v1/. Bạn có thể thay đổi vị trí bằng tùy chọn --repository_cache. Bộ nhớ đệm được chia sẻ giữa tất cả các không gian làm việc và các phiên bản được cài đặt của bazel. Một mục được lấy từ bộ nhớ đệm nếu Bazel biết chắc mục đó có bản sao của tệp chính xác, tức là nếu yêu cầu tải xuống có tổng SHA256 của tệp được chỉ định và tệp có hàm băm đó nằm trong bộ nhớ đệm. Vì vậy, việc chỉ định hàm băm cho mỗi tệp bên ngoài không chỉ là ý tưởng hay từ góc độ bảo mật mà còn giúp tránh các lượt tải xuống không cần thiết.

Sau mỗi lần truy cập vào bộ nhớ đệm, thời gian sửa đổi của tệp đó trong bộ nhớ đệm sẽ được cập nhật. Bằng cách này, bạn có thể dễ dàng xác định việc sử dụng tệp cuối cùng trong thư mục bộ nhớ đệm, chẳng hạn như dọn dẹp bộ nhớ đệm theo cách thủ công. Bộ nhớ đệm không bao giờ được tự động dọn dẹp vì bộ nhớ đệm này có thể chứa bản sao của một tệp không còn tồn tại ở cấp trên.

Thư mục tệp phân phối

Thư mục phân phối là một cơ chế Bazel khác để tránh tải xuống không cần thiết. Bazel tìm kiếm các thư mục phân phối trước bộ nhớ đệm kho lưu trữ. Điểm khác biệt chính là thư mục phân phối yêu cầu chuẩn bị thủ công.

Khi sử dụng tuỳ chọn --distdir=/path/to-directory, bạn có thể chỉ định thêm các thư mục chỉ đọc để tìm tệp thay vì tìm nạp các tệp đó. Một tệp được lấy từ một thư mục như vậy nếu tên tệp bằng tên cơ sở của URL và hàm băm của tệp bằng với tệp được chỉ định trong yêu cầu tải xuống. Điều này chỉ hoạt động nếu hàm băm tệp được chỉ định trong khai báo WORKSPACE.

Mặc dù điều kiện trên tên tệp là không cần thiết cho độ chính xác, nhưng điều này sẽ làm giảm số lượng tệp đề xuất thành một tệp cho mỗi thư mục được chỉ định. Bằng cách này, việc chỉ định thư mục tệp phân phối vẫn hiệu quả, ngay cả khi số lượng tệp trong một thư mục như vậy tăng lên quá mức.

Chạy Bazel trong môi trường máy bay

Để duy trì kích thước tệp nhị phân của Bazel, các phần phụ thuộc ngầm của Bazel được tìm nạp qua mạng trong khi chạy lần đầu tiên. Những phần phụ thuộc ngầm ẩn này chứa các chuỗi công cụ và quy tắc có thể không cần thiết cho tất cả mọi người. Ví dụ: các công cụ Android chỉ được bỏ nhóm và chỉ tìm nạp khi xây dựng dự án Android.

Tuy nhiên, các phần phụ thuộc ngầm ẩn này có thể gây ra sự cố khi chạy Bazel trong môi trường phát sóng, ngay cả khi bạn đã cung cấp tất cả phần phụ thuộc WORKSPACE. Để giải quyết vấn đề đó, bạn có thể chuẩn bị một thư mục phân phối chứa các phần phụ thuộc này trên một máy có quyền truy cập mạng, sau đó chuyển các phần phụ thuộc đó sang môi trường phát sóng bằng phương pháp ngoại tuyến.

Để chuẩn bị thư mục phân phối, hãy sử dụng cờ --distdir. Bạn sẽ cần làm việc này một lần cho mọi phiên bản nhị phân mới của Bazel, vì các phần phụ thuộc ngầm có thể sẽ khác nhau tuỳ theo bản phát hành.

Để xây dựng các phần phụ thuộc này bên ngoài môi trường phát sóng, trước tiên, hãy kiểm tra cây nguồn Bazel ở phiên bản phù hợp:

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

Sau đó, hãy tạo tệp tar chứa các phần phụ thuộc thời gian chạy ngầm cho phiên bản Bazel cụ thể đó:

bazel build @additional_distfiles//:archives.tar

Xuất tệp tar này vào một thư mục có thể được sao chép vào môi trường có tên miền. Lưu ý cờ --strip-components--distdir có thể khá tinh tế với cấp lồng thư mục:

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

Cuối cùng, khi bạn sử dụng Bazel trong môi trường phát sóng, hãy truyền cờ --distdir trỏ đến thư mục. Để thuận tiện, bạn có thể thêm tệp này dưới dạng một mục .bazelrc:

build --distdir=path/to/directory

Cấu hình xây dựng và biên dịch chéo

Tất cả dữ liệu đầu vào chỉ định hành vi và kết quả của một bản dựng nhất định có thể được chia thành hai danh mục riêng biệt. Loại đầu tiên là thông tin nội tại được lưu trữ trong các tệp BUILD của dự án: quy tắc xây dựng, giá trị của các thuộc tính và quy tắc đầy đủ của các phần phụ thuộc bắc cầu của dự án đó. Loại thứ hai là dữ liệu bên ngoài hoặc dữ liệu môi trường do người dùng cung cấp hoặc công cụ xây dựng: lựa chọn cấu trúc mục tiêu, các tùy chọn biên dịch và liên kết, cùng các tùy chọn cấu hình chuỗi công cụ khác. Chúng tôi gọi toàn bộ tập dữ liệu môi trường là cấu hình.

Bất kỳ bản dựng nào cũng có thể có nhiều cấu hình. Hãy cân nhắc việc biên dịch chéo, trong đó bạn tạo một tệp thực thi //foo:bin cho kiến trúc 64 bit, nhưng máy trạm là một máy 32 bit. Rõ ràng là bản dựng này sẽ yêu cầu tạo bản dựng //foo:bin bằng một chuỗi công cụ có khả năng tạo các tệp thực thi 64 bit. Tuy nhiên, hệ thống bản dựng cũng phải tạo nhiều công cụ khác nhau được sử dụng trong bản dựng, ví dụ: các công cụ được tạo từ nguồn, sau đó được sử dụng trong genrule và phải được tạo để chạy trên máy trạm. Do đó, chúng ta có thể xác định hai cấu hình: cấu hình máy chủ, được dùng cho các công cụ xây dựng chạy trong quá trình xây dựng và cấu hình mục tiêu (hoặc cấu hình yêu cầu, nhưng chúng tôi thường nói "cấu hình mục tiêu" mặc dù từ đó đã có nhiều nghĩa) dùng để tạo tệp nhị phân mà bạn yêu cầu cuối cùng.

Thông thường, có nhiều thư viện là điều kiện tiên quyết để thực hiện cả mục tiêu bản dựng được yêu cầu (//foo:bin) và một hoặc nhiều công cụ lưu trữ, ví dụ: một số thư viện cơ sở. Các thư viện như vậy phải được tạo hai lần, một lần cho cấu hình máy chủ và một lần cho cấu hình mục tiêu. Bazel xử lý việc đảm bảo rằng cả hai biến thể đều được xây dựng và các tệp dẫn xuất được tách riêng để tránh can thiệp; thường thì các mục tiêu như vậy có thể được tạo đồng thời vì các mục tiêu này độc lập với nhau. Nếu bạn thấy thông báo tiến trình cho biết rằng một mục tiêu nhất định đang được tạo hai lần thì rất có thể nội dung giải thích đó.

Bazel sử dụng một trong hai cách để chọn cấu hình máy chủ, dựa trên tùy chọn --distinct_host_configuration. Tuỳ chọn boolean này có chút tinh tế và tuỳ chọn cài đặt có thể cải thiện (hoặc làm giảm) tốc độ của các bản dựng.

--distinct_host_configuration=false

Khi tuỳ chọn này sai, cấu hình máy chủ và yêu cầu giống hệt nhau: tất cả công cụ cần thiết trong quá trình xây dựng sẽ được tạo giống hệt như các chương trình mục tiêu. Chế độ cài đặt này đồng nghĩa với việc không cần xây dựng hai thư viện nào trong một bản dựng.

Tuy nhiên, điều đó có nghĩa là mọi thay đổi đối với cấu hình yêu cầu của bạn cũng ảnh hưởng đến cấu hình máy chủ, khiến tất cả các công cụ được xây dựng lại và sau đó bất kỳ điều gì tuỳ thuộc vào đầu ra của công cụ cũng được tạo lại. Ví dụ: chỉ cần thay đổi tùy chọn trình liên kết giữa các bản dựng có thể khiến mọi công cụ được liên kết lại, sau đó tất cả hành động sử dụng chúng sẽ được thực thi lại, v.v. dẫn đến việc tạo lại rất lớn.

--distinct_host_configuration=true (mặc định)

Nếu tuỳ chọn này là true thì thay vì sử dụng cùng một cấu hình cho máy chủ và yêu cầu, cấu hình máy chủ hoàn toàn riêng biệt sẽ được sử dụng. Cấu hình máy chủ lưu trữ được lấy từ cấu hình mục tiêu như sau:

  • Sử dụng cùng một phiên bản Crosstool (--crosstool_top) như chỉ định trong cấu hình yêu cầu, trừ khi --host_crosstool_top được chỉ định.
  • Sử dụng giá trị của --host_cpu cho --cpu (mặc định: k8).
  • Hãy sử dụng cùng giá trị của các tuỳ chọn này như được chỉ định trong cấu hình yêu cầu: --compiler, --use_ijars và nếu sử dụng --host_crosstool_top, thì giá trị --host_cpu sẽ được dùng để tra cứu default_toolchain trong Crosstool (bỏ qua --compiler) cho cấu hình máy chủ.
  • Sử dụng giá trị của --host_javabase cho --javabase
  • Sử dụng giá trị của --host_java_toolchain cho --java_toolchain
  • Sử dụng bản dựng được tối ưu hoá cho mã C++ (-c opt).
  • Không tạo thông tin gỡ lỗi (--copt=-g0).
  • Xoá thông tin gỡ lỗi khỏi các tệp thực thi và thư viện dùng chung (--strip=always).
  • Đặt tất cả các tệp phát sinh ở một vị trí đặc biệt, khác với vị trí mà mọi cấu hình yêu cầu có thể sử dụng.
  • Chặn chế độ đóng dấu tệp nhị phân bằng dữ liệu bản dựng (xem các tuỳ chọn --embed_*).
  • Tất cả các giá trị khác vẫn giữ nguyên giá trị mặc định.

Có nhiều lý do khiến bạn nên chọn một cấu hình máy chủ riêng biệt từ cấu hình yêu cầu. Một số yếu tố bí truyền không thể đề cập ở đây, nhưng hai giá trị trong số đó đáng để chỉ ra.

Thứ nhất, bằng cách sử dụng các tệp nhị phân được loại bỏ, tối ưu hoá, bạn giảm thời gian liên kết và thực thi các công cụ, dung lượng ổ đĩa mà các công cụ chiếm dụng và thời gian I/O mạng trong các bản dựng được phân phối.

Thứ hai, bằng cách tách riêng các cấu hình máy chủ lưu trữ và yêu cầu trong tất cả các bản dựng, bạn sẽ tránh được các bản dựng lại quá tốn kém có thể gây ra những thay đổi nhỏ đối với cấu hình yêu cầu (chẳng hạn như thay đổi một tuỳ chọn trình liên kết), như đã mô tả trước đó.

Tuy nhiên, đối với một số bản dựng nhất định, tuỳ chọn này có thể là một trở ngại. Cụ thể, các bản dựng trong đó các thay đổi về cấu hình không thường xuyên (đặc biệt là các bản dựng Java nhất định) và các bản dựng trong đó số lượng mã phải được tạo trong cả cấu hình máy chủ và cấu hình mục tiêu lớn đều có thể không hưởng lợi.

Sửa chữa bản dựng lại gia tăng

Một trong những mục tiêu chính của dự án Bazel là đảm bảo các bản dựng lại gia tăng chính xác. Các công cụ bản dựng trước đây, đặc biệt là những công cụ dựa trên Make, đưa ra một số giả định không rõ ràng trong quá trình triển khai các bản dựng tăng dần.

Thứ nhất, dấu thời gian của các tệp tăng đơn điệu. Mặc dù đây là trường hợp điển hình, bạn rất dễ gặp lỗi giả định này; việc đồng bộ hoá với bản sửa đổi sớm hơn của tệp có thể làm giảm thời gian sửa đổi của tệp; Hệ thống dựa trên sản xuất sẽ không được tạo lại.

Nói chung, mặc dù Thực hiện phát hiện thay đổi đối với tệp, nhưng không phát hiện thay đổi đối với lệnh. Nếu bạn thay đổi các tùy chọn được truyền đến trình biên dịch trong một bước xây dựng cụ thể, Make sẽ không chạy lại trình biên dịch và cần phải loại bỏ các kết quả của bản dựng trước đó bằng cách sử dụng make clean theo cách thủ công.

Ngoài ra, Make không mạnh mẽ trước việc chấm dứt không thành công một trong các quy trình phụ của mình sau khi quy trình phụ đó bắt đầu ghi vào tệp đầu ra. Mặc dù việc thực thi Make hiện tại sẽ không thành công, lệnh gọi tiếp theo của Make sẽ giả định rằng tệp đầu ra bị cắt bớt hợp lệ (vì tệp này mới hơn giá trị nhập vào) và sẽ không được tạo lại. Tương tự, nếu quá trình Thực hiện bị tắt, thì tình huống tương tự có thể xảy ra.

Bazel tránh các giả định này và những giả định khác. Bazel duy trì cơ sở dữ liệu về tất cả công việc đã làm trước đó và sẽ chỉ bỏ qua một bước bản dựng nếu phát hiện thấy tập hợp các tệp đầu vào (và dấu thời gian của các tệp đó) đến bước bản dựng đó, và lệnh biên dịch cho bước xây dựng đó, khớp chính xác với tập hợp trong cơ sở dữ liệu, và rằng tập hợp các tệp đầu ra (và dấu thời gian của chúng) cho mục nhập cơ sở dữ liệu khớp chính xác với dấu thời gian của các tệp trên ổ đĩa. Mọi thay đổi đối với tệp đầu vào hoặc tệp đầu ra hoặc đối với chính lệnh sẽ gây ra việc thực thi lại bước tạo bản dựng.

Lợi ích cho người dùng là các bản dựng gia tăng chính xác là: lãng phí thời gian hơn do nhầm lẫn. (Ngoài ra, thời gian chờ bản dựng lại do sử dụng make clean ít hơn, cho dù là cần thiết hay trống trước.)

Tính nhất quán của bản dựng và các bản dựng tăng dần

Về mặt chính thức, chúng tôi xác định trạng thái của một bản dựng là nhất quán khi tất cả tệp đầu ra dự kiến tồn tại, và nội dung của các tệp này là chính xác, như được chỉ định theo các bước hoặc quy tắc cần thiết để tạo các tệp đó. Khi bạn chỉnh sửa tệp nguồn, trạng thái của bản dựng được cho là không nhất quán và vẫn không nhất quán cho đến khi bạn chạy công cụ tạo bản dựng tiếp theo để hoàn tất thành công. Chúng tôi mô tả tình trạng này là không nhất quán, vì đây chỉ là tạm thời và tính nhất quán được khôi phục bằng cách chạy công cụ bản dựng.

Có một loại không nhất quán khác gây hại: không nhất quán ổn định. Nếu bản dựng đạt đến trạng thái không nhất quán ổn định, thì lệnh gọi thành công lặp lại của công cụ xây dựng sẽ không khôi phục tính nhất quán: bản dựng bị "khiến" và kết quả vẫn không chính xác. Trạng thái không nhất quán ổn định là lý do chính khiến người dùng Make (và các công cụ xây dựng khác) nhập make clean. Việc phát hiện ra rằng công cụ xây dựng không thành công theo cách này (và sau đó khôi phục từ công cụ này) có thể tốn thời gian và rất khó chịu.

Về mặt lý thuyết, cách đơn giản nhất để có được một bản dựng nhất quán là loại bỏ tất cả kết quả của bản dựng trước đó và bắt đầu lại: tạo mọi bản dựng sạch. Phương pháp này rõ ràng là tốn thời gian để thiết thực (ngoại trừ đối với các kỹ sư phát hành). Do đó, để trở nên hữu ích, công cụ xây dựng phải có khả năng thực hiện các bản dựng tăng dần mà không ảnh hưởng đến tính nhất quán.

Rất khó để phân tích phần phụ thuộc gia tăng một cách chính xác. Như đã mô tả ở trên, nhiều công cụ xây dựng khác cũng làm tốt việc tránh các trạng thái không nhất quán ổn định trong các bản dựng gia tăng. Ngược lại, Bazel đảm bảo rằng: sau khi gọi thành công công cụ bản dựng mà trong đó bạn không chỉnh sửa, bản dựng sẽ ở trạng thái nhất quán. (Nếu bạn chỉnh sửa tệp nguồn trong bản dựng, Bazel không đảm bảo về tính nhất quán của kết quả của bản dựng hiện tại. Tuy nhiên, điều này đảm bảo rằng các kết quả của bản dựng tiếp theo sẽ khôi phục tính nhất quán.)

Như mọi đảm bảo, có một số chi tiết nhỏ: có một số cách xác định trạng thái không nhất quán với Bazel. Chúng tôi sẽ không đảm bảo điều tra những vấn đề như vậy phát sinh từ việc cố gắng tìm lỗi trong quá trình phân tích phần phụ thuộc gia tăng, nhưng chúng tôi sẽ điều tra và cố gắng hết sức để khắc phục tất cả các trạng thái không ổn định không ổn định phát sinh từ việc sử dụng công cụ tạo bản dựng thông thường hoặc "hợp lý".

Nếu bạn phát hiện trạng thái không nhất quán ổn định với Bazel, hãy báo cáo lỗi.

Thực thi hộp cát

Bazel sử dụng hộp cát để đảm bảo rằng các hành động sẽ chạy chính xác và chính xác. Bazel chạy spawns (nói nhanh: hành động) trong hộp cát chỉ chứa tập hợp tối thiểu các tệp mà công cụ yêu cầu để thực hiện công việc của nó. Hiện tại, hộp cát hoạt động trên Linux 3.12 trở lên khi bật tùy chọn CONFIG_USER_NS, cũng như trên macOS 10.11 trở lên.

Bazel sẽ in cảnh báo nếu hệ thống của bạn không hỗ trợ hộp cát để cảnh báo bạn về việc các bản dựng không đảm bảo sẽ là thông báo và có thể ảnh hưởng đến hệ thống lưu trữ theo những cách không xác định. Để tắt cảnh báo này, bạn có thể chuyển cờ --ignore_unsupported_sandboxing đến Bazel.

Trên một số nền tảng như nút cụm Google Kubernetes Engine hoặc Debian, theo mặc định, không gian tên người dùng sẽ bị huỷ kích hoạt do lo ngại về tính bảo mật. Bạn có thể kiểm tra điều này bằng cách xem tệp /proc/sys/kernel/unprivileged_userns_clone: nếu tệp tồn tại và chứa giá trị 0, thì bạn có thể kích hoạt không gian tên của người dùng bằng sudo sysctl kernel.unprivileged_userns_clone=1.

Trong một số trường hợp, hộp cát Bazel không thực thi được quy tắc do thiết lập hệ thống. Dấu hiệu này thường là một lỗi tạo ra một thông báo tương tự như namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory. Trong trường hợp đó, hãy cố gắng huỷ kích hoạt hộp cát cho quy tắc gen với --strategy=Genrule=standalone và cho các quy tắc khác bằng --spawn_strategy=standalone. Ngoài ra, vui lòng báo cáo lỗi trên công cụ theo dõi lỗi của chúng tôi và đề cập đến bản phân phối Linux bạn đang sử dụng để chúng tôi có thể điều tra và cung cấp bản sửa lỗi trong bản phát hành tiếp theo.

Các giai đoạn của một bản dựng

Trong Bazel, một bản dựng xảy ra trong 3 giai đoạn riêng biệt; với tư cách là người dùng, việc hiểu sự khác biệt giữa hai giai đoạn này cung cấp thông tin chi tiết về các tùy chọn kiểm soát bản dựng (xem bên dưới).

Giai đoạn tải

Đầu tiên là tải trong đó tất cả các tệp BUILD cần thiết cho các mục tiêu ban đầu và các phần phụ thuộc bắc cầu của các tệp này được tải, phân tích cú pháp, đánh giá và lưu vào bộ nhớ đệm.

Đối với bản dựng đầu tiên sau khi khởi động máy chủ Bazel, giai đoạn tải thường mất nhiều giây vì nhiều tệp BUILD được tải từ hệ thống tệp. Trong các bản dựng sau đó, đặc biệt nếu không có tệp BUILD nào thay đổi, thì quá trình tải xảy ra rất nhanh.

Các lỗi được báo cáo trong giai đoạn này bao gồm: không tìm thấy gói, không tìm thấy mục tiêu, lỗi ngữ pháp và ngữ pháp trong tệp BUILD và các lỗi đánh giá.

Giai đoạn phân tích

Giai đoạn thứ hai, phân tích, bao gồm hoạt động phân tích ngữ nghĩa và xác thực từng quy tắc tạo bản dựng, tạo biểu đồ phần phụ thuộc bản dựng và xác định chính xác công việc nào sẽ được thực hiện trong mỗi bước của bản dựng.

Giống như việc tải, quá trình phân tích cũng mất vài giây khi được tính toàn bộ. Tuy nhiên, Bazel lưu biểu đồ phần phụ thuộc vào bộ nhớ đệm từ một bản dựng sang một bản dựng tiếp theo và chỉ phân tích lại nội dung của nó. Điều này có thể làm cho các bản dựng gia tăng cực kỳ nhanh trong trường hợp các gói không thay đổi kể từ bản dựng trước.

Các lỗi được báo cáo ở giai đoạn này bao gồm: phần phụ thuộc không phù hợp, dữ liệu đầu vào không hợp lệ cho một quy tắc và tất cả thông báo lỗi dành riêng cho quy tắc.

Các giai đoạn tải và phân tích nhanh chóng vì Bazel tránh các tệp I/O không cần thiết ở giai đoạn này, chỉ đọc các tệp BUILD để xác định công việc cần hoàn thành. Điều này là do thiết kế và khiến Bazel trở thành nền tảng tốt cho các công cụ phân tích, chẳng hạn như lệnh query của Bazel, được triển khai ở đầu giai đoạn tải.

Giai đoạn thực thi

Giai đoạn thứ ba và là giai đoạn cuối cùng của bản dựng là quá trình thực thi. Giai đoạn này đảm bảo rằng các kết quả của mỗi bước trong bản dựng đều nhất quán với dữ liệu đầu vào của công cụ đó, chạy lại các công cụ biên dịch/liên kết/v.v. nếu cần. Đây là bước mà bản dựng dành phần lớn thời gian, từ vài giây đến hơn một giờ cho một bản dựng lớn. Các lỗi được báo cáo trong giai đoạn này bao gồm: thiếu tệp nguồn, lỗi trong công cụ được thực thi bởi một số hành động xây dựng hoặc lỗi công cụ có thể tạo nhóm kết quả đầu ra dự kiến.