Trang này trình bày cách tạo một 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 không gian làm việc cơ sở hoặc bất kỳ thư mục con nào của thư mục đó rồi nhập bazel
. Hãy xem phần build (tạo) 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 câu lệnh có thể dùng
analyze-profile
: Phân tích dữ liệu hồ sơ bản dựng.aquery
: Thực thi một truy vấn trên biểu đồ thao tác phân tích sau.build
: Tạo các mục tiêu đã chỉ định.canonicalize-flags
: Chuẩn hoá cờ Bazel.clean
: Xoá các tệp đầu ra và có thể dừng máy chủ.cquery
: Thực thi một truy vấn biểu đồ phần phụ thuộc phân tích sau.dump
: Kết xuất trạng thái nội bộ của quy trình máy chủ Bazel.help
: In thông tin trợ giúp cho các lệnh hoặc chỉ mục.info
: Hiển thị thông tin thời gian chạy về máy chủ bazel.fetch
: Tìm nạp tất cả cá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ủ Bazel.test
: Tạo và chạy các mục tiêu kiểm thử đã chỉ định.version
: In thông tin phiên bản cho Bazel.
Nhận trợ giúp
bazel help command
: In phần trợ giúp và các lựa chọn chocommand
.bazel help
startup_options
: Các lựa chọn cho JVM lưu trữ Bazel.bazel help
target-syntax
: Giải thích cú pháp để chỉ định mục tiêu.bazel help info-keys
: Hiển thị danh sách các khoá mà lệnh info sử dụng.
Công cụ bazel
thực hiện nhiều chức năng, được gọi là lệnh. Các mã thường dùng nhất là bazel build
và bazel test
. Bạn có thể duyệt xem các thông báo trợ giúp trực tuyến bằng cách sử dụng bazel help
.
Tạo một mục tiêu
Trước khi có thể bắt đầu một bản 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ừ một ổ đĩa hoàn toàn chỉ có thể đọc.
Để tạo một chương trình bằng Bazel, hãy nhập bazel build
, theo sau là mục tiêu mà bạn muốn tạo.
bazel build //foo
Sau khi đưa ra lệnh tạo //foo
, bạn sẽ thấy kết quả tương tự 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. Điề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 tệp BUILD
của các phần phụ thuộc của mục tiêu. Sau khi xác định tất cả các phần phụ thuộc, Bazel sẽ phân tích chúng để đảm bảo tính chính xác và tạo ra các thao tác xây dựng. Cuối cùng, Bazel 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 các thông báo về tiến trình. Thông báo tiến trình bao gồm bước tạo 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 này bắt đầu và số lượng đã hoàn thành so với tổng số hành động tạo. Khi bản dựng bắt đầu, tổng số thao tác thường tăng lên khi Bazel phát hiện toàn bộ biểu đồ thao tác, nhưng số lượng này sẽ ổn định trong vòng vài giây.
Vào cuối quá trình tạo, Bazel sẽ in những mục tiêu được yêu cầu, liệu chúng có được tạo thành công hay không và nếu có thì bạn 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 đầu ra này một cách đáng tin cậy; hãy xem --show_result
để biết thêm chi tiết.
Nếu bạn nhập lại cùng một lệnh, quá trình tạo 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 nào để thực thi. Nếu có thay đổi trong "foo" hoặc các phần phụ thuộc của nó, Bazel sẽ thực thi lại một số thao tác xây dựng hoặc hoàn tất một bản dựng gia tăng.
Xây dựng nhiều mục tiêu
Bazel cho phép một số cách để chỉ định các mục tiêu cần tạo. Những thông tin này được gọi chung là mẫu mục tiêu. Cú pháp này được dùng trong các lệnh như build
, test
hoặc query
.
Trong khi nhãn được dùng để chỉ định các 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ì các mẫu mục tiêu của Bazel chỉ định nhiều mục tiêu. Mẫu mục tiêu là một khái quát hoá của cú pháp nhãn cho các tập hợp mục tiêu, 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 tập hợp gồm chính xác một mục tiêu.
Tất cả các mẫu mục tiêu bắt đầu bằng //
đều được phân giải tương ứng với không gian làm việc hiện tại.
//foo/bar:wiz |
Chỉ có một 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 của 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ả cá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ả cá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ả các mục tiêu trong các gói trong không gian làm việc. Không bao gồm các mục tiêu từ kho lưu trữ bên ngoài. |
//:all |
Tất cả cá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 đang hoạt động hiện tại. Các ví dụ này giả định thư mục đang hoạt động là 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:
|
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 symlink thư mục sẽ được tuân theo đối với các mẫu mục tiêu đệ quy, ngoại trừ những mẫu trỏ đến dưới cơ sở đầu ra, chẳng hạn như các symlink 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 tuân theo các đường liên kết tượng trưng khi đánh giá các mẫu mục tiêu đệ quy trong bất kỳ thư mục nào chứa một tệp có tên như sau: DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN
foo/...
là ký tự đại diện cho packages, cho biết tất cả các gói một cách đệ quy bên dưới thư mục foo
(cho 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. Bạn có thể kết hợp hai ký tự này, chẳng hạn như foo/...:all
. Khi sử dụng cả hai ký tự đại diện, bạn có thể viết tắt thành foo/...
.
Ngoài ra, :*
(hoặc :all-targets
) là một ký tự đại diện khớp với mọi mục tiêu trong các gói được so khớp, bao gồm cả những tệp thường không được tạo theo bất kỳ quy tắc nào, chẳng hạn như các tệp _deploy.jar
được liên kết với các quy tắc java_binary
.
Điều này ngụ ý rằng :*
biểu thị một tập hợp siêu cấp của :all
; mặc dù 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 thông thường, trong đó không mong muốn xây dựng các mục tiêu như _deploy.jar
.
Ngoài ra, Bazel cho phép sử dụng dấu gạch chéo thay vì dấu hai chấm mà cú pháp nhãn yêu cầu; điều 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ó gói foo/bar
) hoặc với //foo:bar/wiz
(nếu có gói foo
).
Nhiều lệnh Bazel chấp nhận danh sách các mẫu mục tiêu làm đối số và tất cả các lệnh này đều tuân theo toán tử phủ định tiền tố -
. Bạn có thể dùng tham số này để trừ một nhóm mục tiêu khỏi nhóm do các đối số trước đó chỉ định. Xin lưu ý rằng điều này có nghĩa là thứ tự rất quan trọng. Ví dụ:
bazel build foo/... bar/...
có nghĩa là "tạo tất cả các mục tiêu bên dưới foo
và tất cả các mục tiêu bên dưới bar
", trong khi
bazel build -- foo/... -foo/bar/...
có nghĩa là "tạo tất cả các mục tiêu bên dưới foo
ngoại trừ những mục tiêu bên 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 diễn giải là các lựa chọn bổ sung.)
Tuy nhiên, điều quan trọng cần lưu ý là việc trừ 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à các phần phụ thuộc của những mục tiêu không bị trừ. Ví dụ: nếu có một mục tiêu //foo:all-apis
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 tạo như một phần của việc tạo mục tiêu trước.
Các mục tiêu có tags = ["manual"]
không được đưa vào các mẫu mục tiêu có ký tự đại diện (...
, :*
, :all
, v.v.) khi được chỉ định trong các lệnh như bazel build
và bazel test
(nhưng chúng được đưa vào các mẫu mục tiêu có ký tự đại diện âm, tức là chúng sẽ bị trừ đi). Bạn nên chỉ định các mục tiêu kiểm thử như vậy bằng các mẫu mục tiêu rõ ràng trên dòng lệnh nếu muốn Bazel tạo/kiểm thử chúng. Ngược lại, bazel query
không tự động thực hiện bất kỳ hoạt động lọc nào như vậy (điều này sẽ làm mất mục đích của bazel query
).
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 tượng trưng các phần phụ thuộc bên ngoài trong quá trình tạo. Tuy nhiên, điều này có thể không mong muốn, có thể là do 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 do bạn muốn "tìm nạp trước" các phần phụ thuộc (ví dụ: trước khi bay khi bạn không có 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
. Xin 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
và các quy tắc kho lưu trữ Android SDK 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 quá trình tạo và Bazel tìm thấy các phần phụ thuộc bên ngoài mới, thì quá trình tạo sẽ không thành cô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 xây dựng, bạn sẽ cần chạy bazel fetch
:
- Trước khi bạn tạo lần đầu tiên.
- Sau khi bạn thêm một phần phụ thuộc bên ngoài mới.
Sau khi chạy, bạn không cần chạy lại 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ụ: thao tác này sẽ tìm nạp các phần phụ thuộc cần thiết để tạo //foo:bar
và //bar:baz
:
bazel fetch //foo:bar //bar:baz
Để tìm nạp tất cả cá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 chạy bazel fetch nếu có tất cả các công cụ mà bạn đang sử dụng (từ các tệp jar 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ất kỳ nội dung nào 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 về một kho lưu trữ bên ngoài đã thay đổi nhưng vẫn cần cùng một tệp để tải xuống. Để làm như vậy, bazel sẽ lưu vào bộ nhớ đệm tất cả các tệp được tải xuống trong bộ nhớ đệm của kho lưu trữ. Theo mặc định, bộ nhớ đệm này nằm ở ~/.cache/bazel/_bazel_$USER/cache/repos/v1/
. Bạn có thể thay đổi vị trí bằng lựa 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 bazel đã cài đặt.
Một mục được lấy từ bộ nhớ đệm nếu Bazel chắc chắn rằng 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à một tệp có hàm băm đó nằm trong bộ nhớ đệm. Vì vậy, việc chỉ định một hàm băm cho mỗi tệp bên ngoài không chỉ là một ý tưởng hay về mặt bảo mật mà còn giúp tránh được các lượt tải xuống không cần thiết.
Sau mỗi lần truy cập 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 lần sử dụng gần đây nhất của một tệp 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 dọn dẹp tự động, vì bộ nhớ đệm có thể chứa bản sao của một tệp không còn có sẵn ở nguồn.
Thư mục tệp phân phối
Thư mục phân phối là một cơ chế khác của Bazel để tránh các lượt 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 của kho lưu trữ. Điểm khác biệt chính là thư mục phân phối yêu cầu bạn chuẩn bị theo cách thủ công.
Khi sử dụng lựa chọn --distdir=/path/to-directory
, bạn có thể chỉ định các thư mục chỉ đọc bổ sung để tìm tệp thay vì tìm nạp chúng. Một tệp sẽ được lấy từ thư mục như vậy nếu tên tệp bằng với tên cơ sở của URL và ngoài ra, hàm băm của tệp bằng với hàm băm được chỉ định trong yêu cầu tải xuống. Điều này chỉ hoạt động nếu bạn chỉ định hàm băm tệp trong khai báo WORKSPACE.
Mặc dù điều kiện về tên tệp không cần thiết để đảm bảo tính chính xác, nhưng điều kiện này sẽ giảm số lượng tệp đề xuất xuống còn một tệp cho mỗi thư mục được chỉ định. Bằng cách này, việc chỉ định các thư mục tệp phân phối vẫn hiệu quả, ngay cả khi số lượng tệp trong thư mục đó tăng lên.
Chạy Bazel trong môi trường không có kết nối Internet
Để giữ cho kích thước nhị phân của Bazel ở mức nhỏ, các phần phụ thuộc ngầm định của Bazel sẽ được tìm nạp qua mạng trong lần chạy đầu tiên. Các phần phụ thuộc ngầm này chứa các chuỗi công cụ và quy tắc mà không phải ai cũng cần. Ví dụ: các công cụ Android được tách riêng và chỉ được tìm nạp khi tạo dự án Android.
Tuy nhiên, những phần phụ thuộc ngầm này có thể gây ra vấn đề khi chạy Bazel trong môi trường không có kết nối Internet, ngay cả khi bạn đã cung cấp tất cả cá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 vào mạng, sau đó chuyển chúng sang môi trường không có kết nối Internet bằng phương pháp ngoại tuyến.
Để chuẩn bị thư mục phân phối, hãy dùng cờ --distdir
. Bạn sẽ cần thực hiện việc này một lần cho mỗi phiên bản nhị phân Bazel mới, vì các phần phụ thuộc ngầm ẩn có thể khác nhau đối với mỗi bản phát hành.
Để tạo các phần phụ thuộc này bên ngoài môi trường không có kết nối Internet, 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 tarball chứa các phần phụ thuộc thời gian chạy ngầm định cho phiên bản Bazel cụ thể đó:
bazel build @additional_distfiles//:archives.tar
Xuất tarball này sang một thư mục có thể sao chép vào môi trường không có kết nối Internet của bạn. Lưu ý cờ --strip-components
, vì --distdir
có thể khá khó chịu với cấp độ lồng ghép 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 không có kết nối Internet, hãy truyền cờ --distdir
trỏ đến thư mục. Để thuận tiện, bạn có thể thêm nó dưới dạng một mục .bazelrc
:
build --distdir=path/to/directory
Cấu hình bản dựng và biên dịch chéo
Tất cả các đầ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 bản dựng, giá trị của các thuộc tính và toàn bộ tập hợp các phần phụ thuộc bắc cầu.
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 hoặc công cụ tạo cung cấp: lựa chọn về cấu trúc mục tiêu, các lựa chọn biên dịch và liên kết, cũng như các lựa chọn cấu hình chuỗi công cụ khác. Chúng tôi gọi một bộ dữ liệu môi trường hoàn chỉnh là cấu hình.
Trong một bản dựng bất kỳ, 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 của bạn là máy 32 bit. Rõ ràng, bản dựng sẽ yêu cầu xây 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, nhưng hệ thống xây dựng cũng phải tạo nhiều công cụ được dùng trong chính bản dựng đó (ví dụ: các công cụ được tạo từ nguồn, sau đó được dùng trong, chẳng hạn như một genrule) và các công cụ này phải được tạo để chạy trên máy trạm của bạn. Do đó, chúng ta có thể xác định 2 cấu hình: cấu hình exec, được dùng để tạo các công cụ chạy trong quá trình tạo và cấu hình mục tiêu (hoặc cấu hình yêu cầu, nhưng chúng ta thường nói "cấu hình mục tiêu" hơn mặc dù từ đó đã có nhiều ý nghĩa), được dùng để tạo tệp nhị phân mà bạn đã yêu cầu.
Thông thường, có nhiều thư viện là điều kiện tiên quyết của 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ụ thực thi, ví dụ: một số thư viện cơ sở. Bạn phải tạo các thư viện như vậy 2 lần, một lần cho cấu hình exec và một lần cho cấu hình mục tiêu. Bazel đảm bảo rằng cả hai biến thể đều được tạo và các tệp phái sinh được tách biệt để tránh gây trở ngại; thường thì các mục tiêu như vậy có thể được tạo đồng thời vì chúng độc lập với nhau. Nếu bạn thấy thông báo tiến trình cho biết một mục tiêu nhất định đang được tạo hai lần, thì đây rất có thể là lý do.
Cấu hình exec được lấy từ cấu hình đích như sau:
- Sử dụng cùng phiên bản Crosstool (
--crosstool_top
) như được chỉ định trong cấu hình yêu cầu, trừ phi bạn chỉ định--host_crosstool_top
. - Sử dụng giá trị của
--host_cpu
cho--cpu
(mặc định:k8
). - Sử dụng các giá trị tương tự của những lựa chọn này như được chỉ định trong cấu hình yêu cầu:
--compiler
,--use_ijars
và nếu--host_crosstool_top
được dùng, thì giá trị của--host_cpu
sẽ được dùng để tra cứudefault_toolchain
trong Crosstool (bỏ qua--compiler
) cho cấu hình exec. - 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ái sinh ở một vị trí đặc biệt, khác biệt với vị trí được dùng bởi mọi cấu hình yêu cầu có thể có.
- Ngăn chặn việc đóng dấu các tệp nhị phân bằng dữ liệu bản dựng (xem các lựa 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 thực thi riêng biệt so với cấu hình yêu cầu. Quan trọng nhất:
Trước tiên, bằng cách sử dụng các tệp nhị phân được tối ưu hoá và rút gọn, bạn sẽ giảm thời gian dành cho việc 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 phân tán.
Thứ hai, bằng cách tách rời cấu hình thực thi 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 rất tốn kém do những thay đổi nhỏ đối với cấu hình yêu cầu (chẳng hạn như thay đổi các lựa chọn của trình liên kết), như mô tả trước đó.
Sửa lỗi tạo lại tăng dần
Một trong những mục tiêu chính của dự án Bazel là đảm bảo quá trình tạo lại gia tăng diễn ra chính xác. Các công cụ tạo 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 hợp lý trong quá trình triển khai các bản dựng gia tăng.
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, nhưng rất dễ vi phạm giả định này; việc đồng bộ hoá với một bản sửa đổi trước đó của tệp sẽ khiến thời gian sửa đổi của tệp đó giảm xuống; các hệ thống dựa trên Make sẽ không tạo lại.
Nói chung, mặc dù Make phát hiện các thay đổi đối với tệp, nhưng không phát hiện các thay đổi đối với lệnh. Nếu bạn thay đổi các lựa chọn được truyền đến trình biên dịch trong một bước dựng nhất định, Make sẽ không chạy lại trình biên dịch và bạn cần phải loại bỏ các đầu ra không hợp lệ của bản dựng trước theo cách thủ công bằng cách sử dụng make clean
.
Ngoài ra, Make không có khả năng chống lại việc chấm dứt không thành công một trong các quy trình con của nó sau khi quy trình con đó bắt đầu ghi vào tệp đầu ra. Mặc dù quá trình thực thi hiện tại của Make sẽ không thành công, nhưng lệnh gọi tiếp theo của Make sẽ giả định một cách mù quáng rằng tệp đầu ra bị cắt ngắn là hợp lệ (vì tệp này mới hơn các tệp đầu vào) và sẽ không được tạo lại. Tương tự, nếu quy trình Make bị huỷ, thì tình huống tương tự có thể xảy ra.
Bazel tránh những giả định này và những giả định khác. Bazel duy trì một cơ sở dữ liệu về tất cả công việc đã thực hiện trước đó và sẽ chỉ bỏ qua một bước xây dựng nếu thấy rằng tập hợp tệp đầu vào (và dấu thời gian của các tệp đó) cho bước xây dựng đó và lệnh biên dịch cho bước xây dựng đó hoàn toàn khớp với một tệp trong cơ sở dữ liệu, đồng thời tập hợp tệp đầu ra (và dấu thời gian của các tệp đó) cho mục cơ sở dữ liệu hoàn toàn khớp 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ẽ khiến bước tạo được thực thi lại.
Lợi ích của các bản dựng gia tăng chính xác đối với người dùng là: giảm thời gian lãng phí do nhầm lẫn. (Ngoài ra, bạn sẽ tốn ít thời gian chờ đợi các bản dựng lại do sử dụng make
clean
, cho dù cần thiết hay chủ động.)
Tạo tính nhất quán và các bản dựng gia tăng
Về mặt chính thức, chúng ta xác định trạng thái của một bản dựng là nhất quán khi tất cả các tệp đầu ra dự kiến đều tồn tại và nội dung của chúng là chính xác, theo quy định của 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 một 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 vào lần tiếp theo để hoàn tất thành công. Chúng tôi mô tả tình huống này là sự không nhất quán không ổn định, vì tình trạng này chỉ là tạm thời và tính nhất quán sẽ được khôi phục bằng cách chạy công cụ tạo.
Có một loại điểm không nhất quán khác gây ra nhiều tác hại: điểm 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ì việc gọi công cụ tạo bản dựng nhiều lần thành công sẽ không khôi phục được tính nhất quán: bản dựng đã "bị kẹt" và đầu ra 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ụ tạo khác) thuộc loại make clean
.
Việc phát hiện thấy công cụ xây dựng bị lỗi theo cách này (rồi khôi phục từ đó) có thể tốn thời gian và rất khó chịu.
Về mặt khái niệm, cách đơn giản nhất để đạt được một bản dựng nhất quán là loại bỏ tất cả các đầu ra bản dựng trước đó và bắt đầu lại: tạo mọi bản dựng thành một bản dựng sạch. Rõ ràng, phương pháp này quá tốn thời gian để có thể áp dụng (ngoại trừ có lẽ đối với các kỹ sư phát hành). Do đó, để hữu ích, công cụ tạo bản dựng phải có khả năng thực hiện các bản dựng gia tăng mà không ảnh hưởng đến tính nhất quán.
Việc phân tích chính xác các phần phụ thuộc gia tăng là rất khó, và như mô tả ở trên, nhiều công cụ xây dựng khác khô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 bạn gọi thành công công cụ tạo bản dựng mà 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 các tệp nguồn trong quá trình tạo, Bazel không đảm bảo tính nhất quán của kết quả trong quá trình tạo hiện tại. Nhưng điều này đảm bảo rằng kết quả của bản dựng tiếp theo sẽ khôi phục tính nhất quán.)
Giống như mọi đảm bảo, có một số điều khoản và điều kiện: có một số cách đã biết để chuyển sang trạng thái không nhất quán ổn định với Bazel. Chúng tôi không đảm bảo sẽ điều tra những vấn đề như vậy phát sinh từ các nỗ lực có chủ ý nhằm tìm lỗi trong quy 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 mọi trạng thái không nhất quán ổn định phát sinh từ việc sử dụng công cụ tạo bản dựng theo cách thông thường hoặc "hợp lý".
Nếu bạn phát hiện thấy trạng thái không nhất quán ổn định với Bazel, vui lòng báo cáo lỗi.
Thực thi trong môi trường hộp cát
Bazel sử dụng hộp cát để đảm bảo các thao tác chạy một cách khép kín và chính xác. Bazel chạy spawns (nói một cách đơn giản: các thao tác) trong các hộp cát chỉ chứa bộ tệp tối thiểu mà công cụ này yêu cầu để thực hiện công việc của mình. Hiện tại, tính năng hộp cát hoạt động trên Linux 3.12 trở lên khi bật tuỳ chọn CONFIG_USER_NS
, cũng như trên macOS 10.11 trở lên.
Bazel sẽ in một 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 được đảm bảo là khép kín và có thể ảnh hưởng đến hệ thống máy chủ theo những cách không xác định. Để tắt cảnh báo này, bạn có thể truyền cờ --ignore_unsupported_sandboxing
đến Bazel.
Trên một số nền tảng như các nút cụm Google Kubernetes Engine hoặc Debian, không gian tên người dùng sẽ bị huỷ kích hoạt theo mặc định do lo ngại về vấn đề 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 này tồn tại và chứa số 0, thì bạn có thể kích hoạt không gian tên 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 các quy tắc do chế độ thiết lập hệ thống. Triệu chứng thường là lỗi xuất 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 thử huỷ kích hoạt hộp cát cho genrules bằng --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 trình theo dõi vấn đề của chúng tôi và cho biết bản phân phối Linux mà 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 một bản phát hành tiếp theo.
Các giai đoạn của bản dựng
Trong Bazel, quá trình tạo bản dựng diễn ra trong 3 giai đoạn riêng biệt; với tư cách là người dùng, việc hiểu rõ sự khác biệt giữa các giai đoạn này sẽ giúp bạn nắm được thông tin chi tiết về các lựa chọn kiểm soát bản dựng (xem bên dưới).
Giai đoạn tải
Giai đoạn đầu tiên là tải. Trong giai đoạn này, tất cả các tệp BUILD cần thiết cho các mục tiêu ban đầu và bao đóng bắc cầu của các phần phụ thuộc sẽ đượ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 máy chủ Bazel khởi động, 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 tiếp theo, đặc biệt là nếu không có tệp BUILD nào thay đổi, quá trình tải sẽ diễn 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 từ vựng và ngữ pháp trong tệp BUILD và lỗi đánh giá.
Giai đoạn phân tích
Giai đoạn thứ hai, phân tích, bao gồm việc phân tích ngữ nghĩa và xác thực từng quy tắc xây dựng, việc tạo biểu đồ phần phụ thuộc của bản dựng và việc xác định chính xác những việc cần làm trong từng bước của bản dựng.
Giống như quá trình tải, quá trình phân tích cũng mất vài giây khi được tính toán toàn bộ. Tuy nhiên, Bazel lưu vào bộ nhớ đệm biểu đồ phần phụ thuộc từ bản dựng này sang bản dựng tiếp theo và chỉ phân tích lại những gì cần thiết. Điều này có thể giúp các bản dựng gia tăng diễn ra 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: các phần phụ thuộc không phù hợp, đầu vào không hợp lệ cho một quy tắc và tất cả cá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 diễn ra nhanh chóng vì Bazel tránh được I/O tệp 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 thực hiện. Đây là một thiết kế có chủ ý, giúp 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 trên giai đoạn tải.
Giai đoạn thực thi
Giai đoạn thứ ba và cũng là giai đoạn cuối cùng của quá trình tạo là thực thi. Giai đoạn này đảm bảo rằng đầu ra của mỗi bước trong quá trình tạo nhất quán với đầu vào, chạy lại các công cụ biên dịch/liên kết/v.v. khi 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 một công cụ do một số thao tác tạo thực thi hoặc lỗi của một công cụ khi không tạo ra được bộ đầu ra dự kiến.