Xây dựng chương trình bằng Bazel

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

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à mẫu đích.

Bắt đầu nhanh

Để chạy Bazel, hãy chuyển đến thư mục không gian làm việc cơ sở của bạn hoặc bất kỳ thư mục con nào của nó và loại bazel. 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 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 truy vấn trên biểu đồ hành động sau khi phân tích.
  • 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à tuỳ ý dừng máy chủ.
  • cquery: Thực thi truy vấn biểu đồ phần phụ thuộc bài 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 phần trợ giúp cho các lệnh hoặc chỉ mục.
  • info: Hiện thông tin 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 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ử được chỉ định.
  • version: In thông tin phiên bản của Bazel.

Nhận trợ giúp

  • bazel help command: Trợ giúp và tuỳ chọn cho ảnh in command
  • bazel helpstartup_options: Các lựa chọn cho máy ảo JVM lưu trữ Bazel.
  • bazel helptarget-syntax: Giải thích cú pháp để chỉ định mục tiêu.
  • bazel help info-keys: Cho thấy danh sách các khoá mà 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. Thông tin phổ biến nhất được sử dụng là bazel buildbazel test. Bạn có thể duyệt xem phần trợ giúp trực tuyến bằng bazel help.

Xây dựng 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 . Bazel cho phép bạn tạo một bản dựng từ một bản dựng hoàn toàn chỉ có thể đọc âm lượng.

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

bazel build //foo

Sau khi 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. Chiến dịch 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 BUILD của mục tiêu và phần phụ thuộc bắc cầu, các tệp được liệt kê trong các 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 phân tích chúng để đảm bảo tính chính xác và tạo hành động 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 in thông báo về tiến trình. Tiến trình thông báo bao gồm bước xây 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) vì nó và số lượt hoàn thành trên tổng số thao tác đối với bản dựng. Là khi bắt đầu tạo bản dựng, tổng số thao tác thường tăng lên khi Bazel phát hiện ra toàn bộ biểu đồ hành động, nhưng số liệu sẽ ổn định trong vòng vài giây.

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

Nếu bạn nhập lại chính lệnh này, 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à 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ó gì đó thay đổi trong 'foo' hoặc phần phụ thuộc, Bazel sẽ thực thi lại một số thao tác tạo bản dựng hoặc hoàn tất một bản dựng tăng dần.

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

Bazel cho phép một số cách chỉ định mục tiêu cần xây dựng. Nói chung, chúng được gọi 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 từng mục tiêu, chẳng hạn như để khai báo các phần phụ thuộc trong tệp BUILD, mục tiêu của Bazel chỉ định nhiều mục tiêu. Mẫu mục tiêu là sự tổng quát của cú pháp nhãn cho 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, bất kỳ 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 chính xác .

Tất cả mẫu mục tiêu bắt đầu bằng // đều được phân giải tương ứng với mẫu hiện tại Workspace.

//foo/bar:wiz Chỉ //foo/bar:wiz mục tiêu duy nhất.
//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ả mục tiêu quy tắc trong mọi gói trong thư mục foo.
//foo/...:all Tất cả mục tiêu quy tắc trong mọi gói trong thư mục foo.
//foo/...:* Tất cả mục tiêu (quy tắc và tệp) trong mọi gói trong thư mục foo.
//foo/...:all-targets Tất cả mục tiêu (quy tắc và tệp) trong mọi gói trong thư mục foo.
//... Tất cả mục tiêu trong các gói trong không gian làm việc. Điều này không bao gồm 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" trong 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 // sẽ đượ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 đ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:
  • //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ì là //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 trong thư mục sẽ được dùng cho mẫu đích đệ quy, ngoại trừ những mã trỏ đến dưới cơ sở đầu ra, chẳng hạn như tính tiện lợi đường liên kết tượng trưng đượ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á đích đệ quy các mẫu trong bất kỳ thư mục nào chứa 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 trên gói, cho biết đệ quy tất cả các gói bên dưới 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 mục tiêu, khớp với tất cả quy tắc trong một gói. Hai chiến dịch này có thể như trong foo/...:all và khi sử dụng cả hai ký tự đại diện, giá trị này có thể là được viết tắt thành 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 phù hợp, bao gồm cả các tệp thường không được tạo theo bất kỳ quy tắc nào, chẳng hạn như _deploy.jar tệp được liên kết với quy tắc java_binary.

Điều này ngụ ý rằng :* biểu thị tập mẹ của :all; trong khi có thể gây nhầm lẫn, 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 mà trong đó người dùng không muốn tạo 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 bắt buộc bởi cú pháp nhãn; điều này thường thuận tiện khi sử dụ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 cho //foo:bar/wiz (nếu có gói foo).

Nhiều lệnh Bazel chấp nhận một danh sách các mẫu mục tiêu làm đối số và tất cả tuân theo toán tử phủ định tiền tố -. Hàm này có thể được dùng để trừ đi một tập hợp các mục tiêu từ tập hợp được các đối số trước đó chỉ định. 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/...

nghĩa là "xây dựng tất cả các mục tiêu có giá trị thấp hơn foo tất cả các mục tiêu có giá trị dưới bar", trong khi

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

có nghĩa là "xây dựng tất cả mục tiêu dưới foo ngoại trừ các mục tiêu dưới foo/bar". (Các Bắt buộc phải có đối số -- để ngăn các đối số tiếp theo bắt đầu bằng - khỏi bị coi là tuỳ chọn bổ sung).

Điều quan trọng cần chỉ ra 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ã này sẽ không được tạo, vì chúng có thể là phần phụ thuộc của mục tiêu chưa bị trừ. Ví dụ: nếu có //foo:all-apis mục tiêu cái này trong số các thành phần khác phụ thuộc vào //foo/bar:api, thì cái sau sẽ được xây dựng dưới dạng một phần của quá trình xây dựng mô hình trước đó.

Những mục tiêu có tags = ["manual"] không có trong mẫu mục tiêu theo 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 như vậy. kiểm thử mục tiêu với các mẫu mục tiêu rõ ràng trên dòng lệnh nếu bạn muốn Bazel để xây dựng/kiểm thử chúng. Ngược lại, bazel query không thực hiện bất kỳ tự động lọc như vậ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 tượng trưng với các phần phụ thuộc bên ngoài trong bản dựng. Tuy nhiên, điều này có thể không được mong muốn vì bạn muốn biết khi các phần phụ thuộc mới bên ngoài được thêm vào hoặc vì bạn muốn "tìm nạp trước" phần phụ thuộc (ví dụ: trước chuyến bay mà bạn sẽ ngoại tuyến). Nếu bạn muốn ngăn chặn việc thêm các phần phụ thuộc mới trong quá trình xây dựng, bạn có thể chỉ định cờ --fetch=false. Lưu ý rằng chỉ cờ này áp dụng cho các quy tắc kho lưu trữ không trỏ đến một thư mục trong tệp cục bộ hệ thống tệp. Các thay đổi, ví dụ như thành local_repository, new_local_repository và các quy tắc về kho lưu trữ NDK và SDK Android sẽ luôn có hiệu lực bất kể giá trị --fetch là gì .

Nếu bạn không cho phép tìm nạp trong quá trình tạo bản dựng và Bazel tìm thấy dữ liệu bên ngoài mới thì bản dựng của bạn 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 bạn không cho phép trong quá trình tìm nạp bản dựng, bạn cần chạy bazel fetch:

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

Sau khi chạy chương trình khuyến mãi, bạn không cần chạy lại cho đến khi WORKSPACE tệp thay đổi.

fetch lấy danh sách mục tiêu để tìm nạp các phần phụ thuộc. Cho 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//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 mã:

bazel fetch //...

Bạn không cần chạy tìm nạp bazel nếu có tất cả các công cụ hiện có bằng cách sử dụng (từ các lọ 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ỳ mục 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ùng một tệp cần 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ữ đã thay đổi nhưng vẫn cần tải xuống cùng một tệp. Để làm như vậy, bazel lưu tất cả các tệp đã tải xuống vào bộ nhớ đệm của kho lưu trữ vào bộ nhớ đệm. Theo mặc định, đặt tại ~/.cache/bazel/_bazel_$USER/cache/repos/v1/. Chiến lược phát hành đĩa đơn vị trí có thể được thay đổi bằng tuỳ chọn --repository_cache. Chiến lược phát hành đĩa đơn bộ nhớ đệm được chia sẻ giữa mọi không gian làm việc và các phiên bản đã cài đặt của bazel. Một mục nhập được lấy từ bộ nhớ đệm nếu Bazel biết chắc chắn rằng mình có bản sao của đúng tệp, 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ó tổng đó hàm băm trong bộ nhớ đệm. Vì vậy, việc chỉ định hàm băm cho từng tệp bên ngoài là không chỉ là một ý tưởng hay về khía cạnh bảo mật; điều này cũng giúp tránh tải xuống không cần thiết.

Khi mỗi lượt truy cập vào bộ nhớ đệm, thời gian sửa đổi tệp trong bộ nhớ đệm sẽ là đã cập nhật. Bằng cách này, lần sử dụng tệp cuối cùng trong thư mục bộ nhớ đệm có thể dễ dàng được xác định, ví dụ: xoá bộ nhớ đệm theo cách thủ công. Bộ nhớ đệm không bao giờ được tự động xoá, vì tệp này có thể chứa bản sao của một tệp không có sẵn lâu hơn ở thượng nguồ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 việc không cần thiết nội dung tải xuống. 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 tài liệu hướng dẫn chuẩn bị.

Sử dụng --distdir=/path/to-directory , bạn có thể chỉ định thêm các thư mục chỉ đọc để tìm kiếm tệp thay vì tìm nạp chúng. Tệp được lấy từ mộ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à hàm băm của tệp là bằng với mã được chỉ định trong yêu cầu tải xuống. Phương thức này chỉ hiệu quả nếu hàm băm của tệp được chỉ định trong phần khai báo WORKSPACE.

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

Chạy Bazel trong một môi trường thoáng đãng

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

Tuy nhiên, những phần phụ thuộc ngầm ẩn này có thể gây ra sự cố khi chạy trong một môi trường thoáng đãng, ngay cả khi bạn đã bán 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 mạng, sau đó chuyển họ đến môi trường thông thoáng theo phương pháp tiếp cận ngoại tuyến.

Để chuẩn bị thư mục phân phối, hãy sử dụng --distdir cờ. Bạn sẽ phải thực hiện 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 ẩn có thể khác nhau đối với mỗi 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 khoảng không quảng cáo, trước tiên hãy kiểm tra cây nguồn Bazel ở đúng phiên bản:

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

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

bazel build @additional_distfiles//:archives.tar

Xuất tệp tar này sang một thư mục mà bạn có thể sao chép vào tệp đồng hồ môi trường. Hãy lưu ý cờ --strip-components, vì --distdir có thể là khá khó khăn với mức độ lồng ghép của 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 gian thoáng mát, hãy truyền --distdir cờ trỏ tới thư mục. Để thuận tiện, bạn có thể thêm tệp này dưới dạng .bazelrc mục nhập:

build --distdir=path/to/directory

Cấu hình bản 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 đều có thể là được chia thành hai loại riêng biệt. Loại đầu tiên là nội tại thông tin đượ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à tập hợp hoàn chỉnh các phần phụ thuộc bắc cầu của 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 hoặc bằng công cụ xây dựng: lựa chọn cấu trúc mục tiêu, biên dịch và liên kết và các tuỳ chọn cấu hình chuỗi công cụ khác. Chúng tôi đề cập đến một tập hợp đầy đủ của dữ liệu môi trường dưới dạng một cấu hình.

Trong bất kỳ bản dựng nhất định nào, có thể có nhiều cấu hình. Hãy cân nhắc sử dụng biên dịch chéo, trong đó bạn tạo một tệp thực thi //foo:bin cho tệp 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 tạo //foo:bin bằng một chuỗi công cụ có khả năng tạo 64 bit tệp thực thi, nhưng hệ thống xây dựng cũng phải xây dựng nhiều công cụ khác nhau được dùng trong quá trình ví dụ: các công cụ được tạo từ nguồn, sau đó tạo được sử dụng trong máy trạm – và các quy tắc này phải được xây dựng để chạy trên máy trạm của bạn. Như vậy chúng tôi có thể xác định hai cấu hình: cấu hình thực thi, được sử dụng để tạo các công cụ chạy trong quá trình tạo bản 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" hơn cả mặc dù từ đó đã có nhiều nghĩa), được sử 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ả hai yêu cầu mục tiêu bản dựng (//foo:bin) và một hoặc nhiều công cụ, ví dụ: một số cơ sở thư viện. Những thư viện như vậy phải được xây dựng nhiều lần cho bộ phận thực thi và mục tiêu . Bazel đảm bảo rằng tất cả các biến thể đều được tạo, và các tệp phái sinh được tách biệt để tránh can thiệp; thường như vậy các mục tiêu 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 sẽ 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 xây dựng thì đây rất có thể là lời giải thích.

Giám đốc điều hành được lấy từ cấu hình mục tiêu như sau:

  • Nền tảng thực thi cho mục tiêu yêu cầu sẽ trở thành nền tảng đích cho cấu hình bộ phận thực thi.
  • 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).
  • Sử dụng cùng giá trị của các lựa chọn này như được chỉ định trong yêu cầu cấu hình: --compiler, --use_ijars và nếu --host_crosstool_top là được sử dụng, thì giá trị --host_cpu sẽ được dùng để tra cứu default_toolchain trong Crosstool (bỏ qua --compiler) đối với máy chủ lưu trữ .
  • 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 các 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 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 vào một vị trí đặc biệt, khác với vị trí mà bất kỳ cấu hình yêu cầu nào có thể có.
  • Loại bỏ việc đó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.

Chỉnh sửa bản dựng 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 chính xác mức độ gia tăng tạo lại bản dựng. Các công cụ xây dựng trước đây, đặc biệt là những công cụ dựa trên Make, hãy thực hiện những giả định sai lầm trong việc 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 lên đơn điệu. Mặc dù đây là trường hợp điển hình, rất dễ mắc sai lầm giả định này; đang đồng bộ hoá với sửa đổi tệp sớm hơn làm thời gian sửa đổi tệp đó giảm; Các hệ thống dựa trên nguyên tắc sẽ không được tạo lại.

Nói chung, tính năng Make phát hiện các thay đổi đối với tệp sẽ không phát hiện thấy các thay đổi vào các lệnh. Nếu bạn thay đổi các tuỳ chọn được truyền cho trình biên dịch trong một bản 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ỏ theo cách thủ công các đầu ra không hợp lệ của bản dựng trước bằng make clean.

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

Bazel tránh những giả định này và các 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 tạo bản dựng nếu tập hợp của tệp đầu vào (và dấu thời gian của chúng) đến bước tạo bản dựng đó và quá trình biên dịch cho bước tạo bản dựng đó, khớp chính xác với một lệnh 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 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 đầu ra tệp hoặc đối với chính lệnh, sẽ dẫn đến việc thực thi lại bước tạo bản dựng.

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

Xây dựng 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 bản dựng là nhất quán khi tất cả các giá trị dự kiến có tồn tại các tệp đầu ra và nội dung của chúng chính xác không, như được chỉ định trong các bước hoặc các quy tắc cần thiết để tạo các quảng cáo đó. 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à không nhất quán cho đến khi bạn chạy công cụ xây dựng để hoàn tất thành công. Chúng tôi mô tả tình huống này là không ổn định tính không nhất quán, bởi vì tính nhất quán chỉ mang tính tạm thời và tính nhất quán sẽ được khôi phục bằng chạy công cụ bản dựng.

Có một loại sự không nhất quán khác gây nguy hại: ổn định sự không nhất quán. Nếu bản dựng đạt đến trạng thái không nhất quán, ổn định thì lặp lại việc gọi thành công công cụ tạo không khôi phục tính nhất quán: bản dựng gặp sự cố "mắc kẹt" và kết quả đầ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 thuộc loại Make (và các công cụ xây dựng khác) loại make clean. Phát hiện ra rằng công cụ xây dựng đã bị lỗi theo cách này (và sau đó khôi phục từ đó) 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 cấu trúc nhất quán là vứt bỏ tất cả kết quả đầu ra của bản dựng trước đó rồi bắt đầu lại: làm cho mỗi bản dựng là một bản dựng sạch. Phương pháp này rõ ràng tốn quá nhiều thời gian nên khó thực thi (ngoại trừ dành cho kỹ sư phát hành) và do đó, để trở nên hữu ích, công cụ xây dựng phải có khả năng để tạo các bản dựng tăng dần mà không làm ảnh hưởng đến tính nhất quán.

Việc phân tích phần phụ thuộc gia tăng chính xác là rất khó và như đã mô tả ở trên, có nhiều các công cụ xây dựng khác làm kém hiệu quả trong việc tránh các trạng thái không nhất quán ổn định trong thời gian các bản dựng tăng dần. Ngược lại, Bazel đưa ra bảo đảm sau: sau khi gọi thành công công cụ xây dựng mà trong thời gian đó bạn không thực hiện chỉnh sửa nào, 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 một 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. 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ư tất cả các bảo đảm, có một số dòng chữ in nhỏ: 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 sẽ không đảm bảo điều tra những vấn đề như vậy phát sinh từ nỗ lực có chủ ý tìm lỗi trong bạn có thể phân tích phần phụ thuộc gia tăng, nhưng chúng tôi sẽ điều tra và nỗ lực hết sức để khắc phục tất cả trạng thái không nhất quán và ổn định phát sinh từ bình thường hoặc "hợp lý" việc sử dụng công cụ bản dựng.

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

Thực thi theo mô hình hộp cát

Bazel sử dụng hộp cát để đảm bảo các hành động chạy một cách khép kín và chính xác. Bazel chạy spawns (nói đơn giản: hành động) trong các hộp cát chỉ chứa tập hợp tệp tối thiểu mà công cụ yêu cầu để thực hiện công việc của mình. Hiện tại hộp cát hoạt động trên Linux 3.12 trở lên với tuỳ chọn CONFIG_USER_NS đã bật 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 thấy rằng các công trình xây dựng không được đảm bảo mang tính khép kín và có thể ảnh hưởng 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 hàm --ignore_unsupported_sandboxing gắn cờ cho Bazel.

Trên một số nền tảng như Google Kubernetes Nút cụm Engine hoặc Debian, không gian tên của người dùng bị vô hiệu hoá theo mặc định do vấn đề bảo mật các mối quan ngại của mình. 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 mã này tồn tại và chứa giá trị 0, thì không gian tên người dùng có thể được kích hoạt 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ể thực thi các quy tắc do hệ thống thiết lập. Triệu chứng thường là một lỗi đưa ra 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à với các quy tắc khác có --spawn_strategy=standalone Ngoài ra, vui lòng báo cáo lỗi về công cụ theo dõi lỗ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à khắc phục trong bản phát hành tiếp theo.

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

Trong Bazel, một quá trình xây dựng sẽ diễn ra theo ba 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 chúng cung cấp thông tin chi tiết về các tuỳ 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à đóng gói bắc cầu của các phần phụ thuộc sẽ được tải, được 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 khi số 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 nếu không có tệp BUILD nào được thay đổi, thì quá trình tải sẽ diễn ra rất nhanh chóng.

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à các lỗi đánh giá.

Giai đoạn phân tích

Giai đoạn thứ hai là 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 bản dựng, việc tạo biểu đồ phần phụ thuộc của bản dựng và xác định chính xác công việc cần làm trong mỗi bước của quá trình tạo bản dựng.

Giống như tải, bản 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 biểu đồ phần phụ thuộc vào bộ nhớ đệm giữa các bản dựng và chỉ phân tích lại những gì cần làm, có thể giúp các bản dựng gia tăng trở nên cực kỳ nhanh 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, không hợp lệ đầu vào vào quy tắc và tất cả các thông báo lỗi dành riêng cho quy tắc.

Giai đoạn tải và phân tích rất nhanh vì Bazel tránh được các tệp không cần thiết I/O ở giai đoạn này, chỉ đọc tệp BUILD để xác định công việc cần xong. Điều này là do thiết kế và khiến Bazel trở thành một 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 quá trình tải pha.

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 bản dựng là thực thi. Giai đoạn này đảm bảo rằng đầu ra của từng bước trong bản dựng nhất quán với dữ liệu đầu vào của bước đó, biên dịch/liên kết/v.v. khi cần thiết. Bước này là nơi 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 bản dựng. 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ụ được thực thi bởi một hành động nào đó trong bản dựng hoặc lỗi của công cụ trong việc tạo tập hợp đầu ra dự kiến.