Làm việc với các phần phụ thuộc bên ngoài

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 sự cố Xem nguồn

Bazel có thể phụ thuộc vào các mục tiêu từ các dự án khác. Các phần phụ thuộc từ những dự án khác này được gọi là phần phụ thuộc bên ngoài.

Tệp WORKSPACE (hoặc tệp WORKSPACE.bazel) trong thư mục không gian làm việc cho Bazel biết cách lấy nguồn của các dự án khác. Các dự án khác này có thể chứa một hoặc nhiều tệp BUILD với mục tiêu riêng. Các tệp BUILD trong dự án chính có thể phụ thuộc vào các mục tiêu bên ngoài này bằng cách sử dụng tên của các tệp đó từ tệp WORKSPACE.

Ví dụ: giả sử có hai dự án trên một hệ thống:

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

Nếu project1 muốn phụ thuộc vào một mục tiêu, :foo, được xác định trong /home/user/project2/BUILD, thì mục đó có thể chỉ định rằng hệ thống có thể tìm thấy kho lưu trữ có tên project2 tại /home/user/project2. Sau đó, các mục tiêu trong /home/user/project1/BUILD có thể phụ thuộc vào @project2//:foo.

Tệp WORKSPACE cho phép người dùng dựa vào mục tiêu từ các phần khác của hệ thống tệp hoặc được tải xuống từ Internet. Quy tắc này sử dụng cú pháp giống như tệp BUILD, nhưng cho phép một bộ quy tắc khác có tên là quy tắc kho lưu trữ (đôi khi còn được gọi là quy tắc không gian làm việc). Bazel đi kèm với một vài quy tắc kho lưu trữ tích hợp sẵn và một bộ quy tắc kho lưu trữ Starlark được nhúng. Người dùng cũng có thể viết các quy tắc lưu trữ tuỳ chỉnh để có được hành vi phức tạp hơn.

Các loại phần phụ thuộc bên ngoài được hỗ trợ

Bạn có thể dùng một số loại phần phụ thuộc bên ngoài cơ bản:

Tuỳ thuộc vào các dự án khác của Bazel

Nếu muốn sử dụng mục tiêu từ dự án Bazel thứ hai, bạn có thể sử dụng local_repository, git_repository hoặc http_archive để liên kết tượng trưng với hệ thống tệp cục bộ, tham chiếu kho lưu trữ git hoặc tải xuống (tương ứng).

Ví dụ: giả sử bạn đang thực hiện một dự án, my-project/ và bạn muốn phụ thuộc vào các mục tiêu từ dự án đồng nghiệp của mình, coworkers-project/. Cả hai dự án đều sử dụng Bazel, vì vậy, bạn có thể thêm dự án của đồng nghiệp của mình làm phần phụ thuộc bên ngoài, sau đó sử dụng mọi mục tiêu mà đồng nghiệp đã xác định từ các tệp Builder của riêng bạn. Bạn sẽ thêm đoạn mã sau vào my_project/WORKSPACE:

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

Nếu đồng nghiệp của bạn có //foo:bar mục tiêu, thì dự án có thể gọi dự án này là @coworkers_project//foo:bar. Tên dự án bên ngoài phải là tên không gian làm việc hợp lệ.

Tuỳ thuộc vào các dự án không thuộc Bielel

Các quy tắc có tiền tố new_, chẳng hạn như new_local_repository, cho phép bạn tạo mục tiêu từ các dự án không sử dụng Bazel.

Ví dụ: giả sử bạn đang làm việc trên một dự án, my-project/ và bạn muốn phụ thuộc vào dự án coworkers-project/ của đồng nghiệp. Dự án của đồng nghiệp của bạn sử dụng make để tạo, nhưng bạn muốn phụ thuộc vào một trong các tệp .so mà dự án tạo ra. Để thực hiện việc này, hãy thêm nội dung sau vào my_project/WORKSPACE:

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file chỉ định một tệp BUILD để phủ trên dự án hiện có, ví dụ:

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

Sau đó, bạn có thể phụ thuộc vào @coworkers_project//:some-lib từ các tệp BUILD của dự án.

Tuỳ thuộc vào gói bên ngoài

Cấu phần phần mềm và kho lưu trữ Maven

Hãy sử dụng bộ quy tắc rules_jvm_external để tải các cấu phần phần mềm xuống từ kho lưu trữ Maven và cung cấp các cấu phần phần mềm đó dưới dạng các phần phụ thuộc Java.

Đang tìm nạp các phần phụ thuộc

Theo mặc định, các phần phụ thuộc bên ngoài được tìm nạp khi cần trong bazel build. Nếu bạn muốn tìm nạp trước các phần phụ thuộc cần thiết cho một tập hợp các mục tiêu cụ thể, hãy sử dụng bazel fetch. Để tìm nạp tất cả các phần phụ thuộc bên ngoài, hãy sử dụng bazel sync. Khi kho lưu trữ được tìm nạp được lưu trữ trong cơ sở đầu ra, sẽ tìm nạp các hoạt động diễn ra trên mỗi không gian làm việc.

Phần phụ thuộc đổ bóng

Bất cứ khi nào có thể, bạn nên có một chính sách phiên bản duy nhất trong dự án của mình. Thuộc tính này là bắt buộc đối với các phần phụ thuộc mà bạn biên dịch và kết thúc trong tệp nhị phân cuối cùng. Tuy nhiên, trong trường hợp điều này không đúng, có thể bóng phụ thuộc. Hãy xem xét trường hợp sau:

dự án/WORKSPACE của tôi

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/WORKSPACE

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

Cả hai phần phụ thuộc AB đều phụ thuộc vào testrunner, nhưng chúng phụ thuộc vào nhiều phiên bản của testrunner. Không có lý do để các trình chạy kiểm thử này không tồn tại một cách hòa bình trong myproject, tuy nhiên, chúng sẽ xung đột với mỗi trình chạy khác vì chúng có cùng tên. Để khai báo cả hai phần phụ thuộc, hãy cập nhật myproject/WORKSPACE:

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

Cơ chế này cũng có thể dùng để kết hợp kim cương. Ví dụ: nếu AB có cùng phần phụ thuộc nhưng gọi tên này bằng các tên khác, thì các phần phụ thuộc đó có thể được tham gia trong Myproject/WORKSPACE.

Ghi đè kho lưu trữ từ dòng lệnh

Để ghi đè một kho lưu trữ đã khai báo bằng một kho lưu trữ cục bộ từ dòng lệnh, hãy sử dụng cờ --override_repository. Việc sử dụng cờ này sẽ thay đổi nội dung của các kho lưu trữ bên ngoài mà không thay đổi mã nguồn của bạn.

Ví dụ: để ghi đè @foo vào thư mục cục bộ /path/to/local/foo, hãy truyền cờ --override_repository=foo=/path/to/local/foo.

Một số trường hợp sử dụng gồm có:

  • Khắc phục các vấn đề. Ví dụ: bạn có thể ghi đè kho lưu trữ http_archive vào một thư mục cục bộ để có thể thay đổi dễ dàng hơn.
  • Đang cung cấp. Nếu bạn đang ở trong một môi trường mà bạn không thể thực hiện lệnh gọi mạng, hãy ghi đè các quy tắc kho lưu trữ dựa trên mạng để trỏ đến các thư mục cục bộ.

Sử dụng proxy

Bazel sẽ nhận địa chỉ proxy từ các biến môi trường HTTPS_PROXYHTTP_PROXY và sử dụng các biến đó để tải tệp HTTP/HTTPS xuống (nếu được chỉ định).

Hỗ trợ IPv6

Trên các máy chỉ dành cho IPv6, Bazel sẽ có thể tải các phần phụ thuộc xuống mà không cần thay đổi. Tuy nhiên, trên các máy IPv4/IPv6 ngăn xếp kép, Bazel tuân theo quy ước tương tự như Java: nếu IPv4 được bật, IPv4 sẽ được ưu tiên. Trong một số trường hợp, chẳng hạn như khi mạng IPv4 không thể phân giải/truy cập vào các địa chỉ bên ngoài, điều này có thể gây ra ngoại lệ Network unreachable và lỗi bản dựng. Trong những trường hợp này, bạn có thể ghi đè hành vi của Bazel để ưu tiên IPv6 bằng cách sử dụng thuộc tính hệ thống java.net.preferIPv6Addresses=true. Cụ thể:

  • Hãy sử dụng --host_jvm_args=-Djava.net.preferIPv6Addresses=true tuỳ chọn khởi động, ví dụ: bằng cách thêm dòng sau vào tệp .bazelrc của bạn:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • Nếu bạn đang chạy các mục tiêu bản dựng Java cần kết nối với Internet (các hoạt động kiểm thử tích hợp đôi khi cần điều đó), hãy sử dụng --jvmopt=-Djava.net.preferIPv6Addresses=true cờ công cụ, ví dụ: bằng cách đặt dòng sau trong tệp .bazelrc:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • Nếu bạn đang sử dụng rule_jvm_external, ví dụ: để phân giải phiên bản phần phụ thuộc, hãy thêm -Djava.net.preferIPv6Addresses=true vào biến môi trường COURSIER_OPTS để cung cấp các tuỳ chọn JVM cho Coursier

Phần phụ thuộc bắc cầu

Bazel chỉ đọc các phần phụ thuộc được liệt kê trong tệp WORKSPACE. Nếu dự án của bạn (A) phụ thuộc vào một dự án khác (B) liệt kê phần phụ thuộc trên một dự án thứ ba (C) trong tệp WORKSPACE của dự án đó, bạn sẽ phải thêm cả BC vào tệp WORKSPACE của dự án. Yêu cầu này có thể tăng kích thước tệp WORKSPACE, nhưng giới hạn cơ hội có một thư viện bao gồm C ở phiên bản 1.0 và một thư viện khác bao gồm C ở mức 2.0.

Chức năng lưu các phần phụ thuộc bên ngoài vào bộ nhớ đệm

Theo mặc định, Bazel sẽ chỉ tải lại các phần phụ thuộc bên ngoài xuống nếu định nghĩa của các phần phụ thuộc đó thay đổi. Các thay đổi đối với tệp được tham chiếu trong định nghĩa (chẳng hạn như các bản vá hoặc tệp BUILD) cũng được xem xét theo hàm bazel.

Để buộc tải xuống lại, hãy sử dụng bazel sync.

Bố cục

Tất cả phần phụ thuộc bên ngoài đều được tải xuống một thư mục trong thư mục con externalcơ sở đầu ra. Trong trường hợp kho lưu trữ cục bộ, một đường liên kết tượng trưng sẽ được tạo tại đó thay vì tạo một thư mục mới. Bạn có thể thấy thư mục external bằng cách chạy:

ls $(bazel info output_base)/external

Xin lưu ý rằng việc chạy bazel clean sẽ không thực sự xoá thư mục bên ngoài. Để xoá tất cả cấu phần phần mềm bên ngoài, hãy sử dụng bazel clean --expunge.

Bản dựng ngoại tuyến

Đôi khi bạn muốn chạy một bản dựng theo cách ngoại tuyến. Đối với các trường hợp sử dụng đơn giản, chẳng hạn như di chuyển trên máy bay, bạn có thể tìm nạp trước các kho lưu trữ cần thiết bằng bazel fetch hoặc bazel sync. Hơn nữa, việc sử dụng tuỳ chọn --nofetch, tìm nạp các kho lưu trữ khác có thể bị vô hiệu hoá trong quá trình tạo bản dựng.

Đối với các bản dựng ngoại tuyến thực sự, trong đó việc cung cấp các tệp cần thiết sẽ được thực thể thực hiện khác với bazel, thì bazel sẽ hỗ trợ tuỳ chọn --distdir. Bất cứ khi nào một quy tắc kho lưu trữ yêu cầu bazel tìm nạp một tệp thông qua ctx.download hoặc ctx.download_and_extract, đồng thời cung cấp tổng số băm của tệp cần thiết, trước tiên, bazel sẽ xem xét các thư mục do tùy chọn đó chỉ định cho một tệp khớp với tên cơ sở của URL đầu tiên mà bạn cung cấp và sử dụng bản sao cục bộ đó nếu hàm băm khớp.

Bản thân Bazel sử dụng kỹ thuật này để tự khởi động ngoại tuyến từ cấu phần phần mềm phân phối. Công cụ này thực hiện việc này bằng cách thu thập tất cả phần phụ thuộc bên ngoài cần thiết trong distdir_tar nội bộ.

Tuy nhiên, bazel cho phép thực thi các lệnh tuỳ ý trong các quy tắc lưu trữ mà không cần biết liệu các lệnh đó có được gọi ra mạng hay không. Do đó, bazel không có tuỳ chọn nào để thực thi các bản dựng hoàn toàn ngoại tuyến. Vì vậy, việc kiểm thử xem một bản dựng có hoạt động đúng cách khi không có kết nối mạng hay không sẽ cần phải chặn mạng bên ngoài, như với bazel trong quá trình kiểm thử khởi động.

Các phương pháp hay nhất

Quy tắc kho lưu trữ

Quy tắc kho lưu trữ thường phải chịu trách nhiệm về:

  • Phát hiện các chế độ cài đặt hệ thống và ghi vào tệp.
  • Tìm tài nguyên ở nơi khác trên hệ thống.
  • Đang tải tài nguyên từ URL xuống.
  • Tạo hoặc liên kết các tệp BUILD vào thư mục kho lưu trữ bên ngoài.

Tránh sử dụng repository_ctx.execute khi có thể. Ví dụ: khi sử dụng một thư viện không phải Biel C++ có bản dựng sử dụng Make, bạn nên sử dụng repository_ctx.download() rồi viết tệp BUILD tạo bản dựng, thay vì chạy ctx.execute(["make"]).

Ưu tiên http_archive hơn git_repositorynew_git_repository. Lý do là:

  • Các quy tắc lưu trữ Git phụ thuộc vào hệ thống git(1) trong khi trình tải HTTP được tích hợp vào Bazel và không có phần phụ thuộc hệ thống.
  • http_archive hỗ trợ danh sách urls dưới dạng các phản chiếu và git_repository chỉ hỗ trợ một remote.
  • http_archive hoạt động với bộ nhớ đệm của kho lưu trữ, nhưng không hoạt động với git_repository. Hãy xem #5116 để biết thêm thông tin.

Không sử dụng bind(). Hãy xem mục "Cân nhắc việc xóa nội dung liên kết" để biết nội dung thảo luận dài về các vấn đề và lựa chọn thay thế.