Hàm

Báo cáo vấn đề Xem nguồn Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Nội dung

gói hàng

package(default_deprecation, default_package_metadata, default_testonly, default_visibility, features)

Hàm này khai báo siêu dữ liệu áp dụng cho mọi quy tắc trong gói. Bạn có thể sử dụng hàm này tối đa một lần trong một gói (tệp BUILD).

Đối với đối tượng khai báo siêu dữ liệu áp dụng cho mọi quy tắc trong toàn bộ kho lưu trữ, hãy sử dụng hàm repo() trong tệp REPO.bazel ở gốc kho lưu trữ của bạn. Hàm repo() có đối số giống hệt như package().

Hàm package() phải được gọi ngay sau tất cả các câu lệnh load() ở đầu tệp, trước bất kỳ quy tắc nào.

Đối số

Thuộc tính Mô tả
default_applicable_licenses

Tên thay thế cho default_package_metadata.

default_visibility

Danh sách nhãn; mặc định là []

Chế độ hiển thị mặc định của các mục tiêu quy tắc cấp cao nhất và macro tượng trưng trong gói này – tức là các mục tiêu và macro tượng trưng không được khai báo trong một macro tượng trưng. Thuộc tính này sẽ bị bỏ qua nếu mục tiêu hoặc macro chỉ định giá trị visibility.

Để biết thông tin chi tiết về cú pháp của thuộc tính này, hãy xem tài liệu về khả năng hiển thị. Chế độ hiển thị mặc định của gói không áp dụng cho exports_files (công khai theo mặc định).

default_deprecation

Chuỗi; giá trị mặc định là ""

Đặt thông báo deprecation mặc định cho tất cả các quy tắc trong gói này.

default_package_metadata

Danh sách nhãn; mặc định là []

Đặt danh sách mục tiêu siêu dữ liệu mặc định áp dụng cho tất cả các mục tiêu khác trong gói. Đây thường là các mục tiêu liên quan đến khai báo gói và giấy phép OSS. Hãy xem rules_license để biết ví dụ.

default_testonly

Boolean; mặc định là False trừ phi có ghi chú

Đặt thuộc tính testonly mặc định cho tất cả các quy tắc trong gói này.

Trong các gói thuộc javatests, giá trị mặc định là True.

features

Liệt kê các chuỗi; mặc định là []

Đặt nhiều cờ ảnh hưởng đến ngữ nghĩa của tệp BUILD này.

Tính năng này chủ yếu được những người làm việc trên hệ thống bản dựng sử dụng để gắn thẻ các gói cần một số loại xử lý đặc biệt. Không sử dụng tuỳ chọn này trừ phi có người làm việc trên hệ thống xây dựng yêu cầu rõ ràng.

Ví dụ

Khai báo dưới đây khai báo rằng các quy tắc trong gói này chỉ hiển thị cho các thành viên của nhóm gói //foo:target. Tuyên bố về chế độ hiển thị riêng lẻ trên một quy tắc (nếu có) sẽ ghi đè quy cách này.
package(default_visibility = ["//foo:target"])

package_group

package_group(name, packages, includes)

Hàm này xác định một tập hợp các gói và liên kết một nhãn với tập hợp đó. Bạn có thể tham chiếu nhãn trong các thuộc tính visibility.

Nhóm gói chủ yếu được dùng để kiểm soát chế độ hiển thị. Mọi gói trong cây nguồn đều có thể tham chiếu đến một mục tiêu hiển thị công khai. Bạn chỉ có thể tham chiếu mục tiêu có thể thấy riêng tư trong gói riêng của mục tiêu đó (không phải gói con). Trong khoảng giữa những trường hợp cực đoan này, một đích đến có thể cho phép truy cập vào gói của riêng nó, cộng với bất kỳ gói nào được mô tả bởi một hoặc nhiều nhóm gói. Để biết giải thích chi tiết hơn về hệ thống chế độ hiển thị, hãy xem thuộc tính visibility (chế độ hiển thị).

Một gói nhất định được coi là nằm trong nhóm nếu gói đó khớp với thuộc tính packages hoặc đã có trong một trong các nhóm gói khác được đề cập trong thuộc tính includes.

Nhóm gói về mặt kỹ thuật là mục tiêu, nhưng không được tạo theo quy tắc và bản thân nhóm gói không có biện pháp bảo vệ khả năng hiển thị nào.

Đối số

Thuộc tính Mô tả
name

Tên; bắt buộc

Tên riêng cho mục tiêu này.

packages

Danh sách các chuỗi; mặc định là []

Một danh sách gồm 0 hoặc nhiều quy cách đóng gói.

Mỗi chuỗi chỉ định gói có thể có một trong các dạng sau:

  1. Tên đầy đủ của một gói, không có kho lưu trữ, bắt đầu bằng dấu gạch chéo kép. Ví dụ: //foo/bar chỉ định gói có tên đó và nằm trong cùng một kho lưu trữ với nhóm gói.
  2. Như trên, nhưng có /... ở cuối. Ví dụ: //foo/... chỉ định tập hợp //foo và tất cả các gói con của tập hợp đó. //... chỉ định tất cả các gói trong kho lưu trữ hiện tại.
  3. Các chuỗi public hoặc private, lần lượt chỉ định mọi gói hoặc không có gói nào. (Biểu mẫu này yêu cầu bạn đặt cờ --incompatible_package_group_has_public_syntax.)

Ngoài ra, hai loại quy cách gói đầu tiên cũng có thể có tiền tố - để cho biết rằng chúng bị phủ định.

Nhóm gói chứa mọi gói khớp với ít nhất một trong các quy cách dương và không có quy cách âm nào. Ví dụ: giá trị [//foo/..., -//foo/tests/...] bao gồm tất cả các gói con của //foo không phải là gói con của //foo/tests. (//foo được đưa vào trong khi //foo/tests thì không.)

Ngoài chế độ hiển thị công khai, không có cách nào để chỉ định trực tiếp các gói bên ngoài kho lưu trữ hiện tại.

Nếu thiếu thuộc tính này, thì tức là bạn đã đặt thuộc tính này thành một danh sách trống, tức là bạn đã đặt thuộc tính này thành một danh sách chỉ chứa private.

Lưu ý: Trước Bazel 6.0, quy cách //... có hành vi cũ giống như public. Hành vi này được cố định khi --incompatible_fix_package_group_reporoot_syntax được bật, đây là hành vi mặc định sau Bazel 6.0.

Lưu ý: Trước Bazel 6.0, khi thuộc tính này được chuyển đổi tuần tự thành một phần của bazel query --output=proto (hoặc --output=xml), các dấu gạch chéo ở đầu sẽ bị bỏ qua. Ví dụ: //pkg/foo/... sẽ xuất ra dưới dạng \"pkg/foo/...\". Hành vi này được cố định khi --incompatible_package_group_includes_double_slash được bật, đây là hành vi mặc định sau Bazel 6.0.

includes

Danh sách nhãn; mặc định là []

Các nhóm gói khác có trong nhóm gói này.

Nhãn trong thuộc tính này phải đề cập đến các nhóm gói khác. Các gói trong nhóm gói được tham chiếu được coi là một phần của nhóm gói này. Đây là mối quan hệ bắc cầu – nếu nhóm gói a bao gồm nhóm gói bb bao gồm nhóm gói c, thì mọi gói trong c cũng sẽ là thành viên của a.

Khi được dùng cùng với các quy cách gói bị phủ định, hãy lưu ý rằng trước tiên, tập hợp các gói cho mỗi nhóm được tính toán độc lập và sau đó, các kết quả được hợp nhất với nhau. Điều này có nghĩa là các quy cách bị phủ định trong một nhóm không ảnh hưởng đến các quy cách trong một nhóm khác.

Ví dụ

Khai báo package_group sau đây chỉ định một nhóm gói có tên là "tropical" (nhiệt đới) chứa các loại trái cây nhiệt đới.

package_group(
    name = "tropical",
    packages = [
        "//fruits/mango",
        "//fruits/orange",
        "//fruits/papaya/...",
    ],
)

Các khai báo sau đây chỉ định các nhóm gói của một ứng dụng hư cấu:

package_group(
    name = "fooapp",
    includes = [
        ":controller",
        ":model",
        ":view",
    ],
)

package_group(
    name = "model",
    packages = ["//fooapp/database"],
)

package_group(
    name = "view",
    packages = [
        "//fooapp/swingui",
        "//fooapp/webui",
    ],
)

package_group(
    name = "controller",
    packages = ["//fooapp/algorithm"],
)

exports_files

exports_files([label, ...], visibility, licenses)

exports_files() chỉ định danh sách các tệp thuộc gói này được xuất sang các gói khác.

Tệp BUILD cho một gói chỉ có thể tham chiếu trực tiếp đến các tệp nguồn thuộc một gói khác nếu các tệp đó được xuất rõ ràng bằng câu lệnh exports_files(). Đọc thêm về chế độ hiển thị của tệp.

Theo hành vi cũ, các tệp được đề cập làm thông tin đầu vào cho một quy tắc cũng được xuất với chế độ hiển thị mặc định cho đến khi cờ --incompatible_no_implicit_file_export được chuyển. Tuy nhiên, bạn không nên dựa vào hành vi này và chủ động di chuyển khỏi hành vi này.

Đối số

Đối số là danh sách tên của các tệp trong gói hiện tại. Bạn cũng có thể chỉ định một khai báo về khả năng hiển thị; trong trường hợp này, các tệp sẽ hiển thị cho các mục tiêu được chỉ định. Nếu bạn không chỉ định chế độ hiển thị, thì mọi gói đều có thể nhìn thấy các tệp, ngay cả khi bạn đã chỉ định chế độ hiển thị mặc định của gói trong hàm package. Bạn cũng có thể chỉ định giấy phép.

Ví dụ:

Ví dụ sau đây xuất golden.txt, một tệp văn bản từ gói test_data, để các gói khác có thể dùng tệp này, chẳng hạn như trong thuộc tính data của các kiểm thử.

# from //test_data/BUILD

exports_files(["golden.txt"])

glob

glob(include, exclude=[], exclude_directories=1, allow_empty=True)

Glob là một hàm trợ giúp tìm tất cả các tệp khớp với một số mẫu đường dẫn nhất định và trả về một danh sách mới, có thể thay đổi và được sắp xếp theo đường dẫn của các tệp đó. Glob chỉ tìm kiếm các tệp trong gói của riêng nó và chỉ tìm các tệp nguồn (không phải tệp được tạo cũng như các mục tiêu khác).

Nhãn của tệp nguồn sẽ được đưa vào kết quả nếu đường dẫn tương đối theo gói của tệp khớp với bất kỳ mẫu include nào và không khớp với mẫu exclude nào.

Danh sách includeexclude chứa các mẫu đường dẫn tương ứng với gói hiện tại. Mỗi mẫu có thể bao gồm một hoặc nhiều đoạn đường dẫn. Như thường lệ với các đường dẫn Unix, những phân đoạn này được phân tách bằng /. Các phân đoạn trong mẫu được so khớp với các phân đoạn của đường dẫn. Các phân đoạn có thể chứa ký tự đại diện *: ký tự này khớp với mọi chuỗi con trong phân đoạn đường dẫn (ngay cả chuỗi con trống), ngoại trừ dấu phân cách thư mục /. Bạn có thể dùng ký tự đại diện này nhiều lần trong một phân đoạn đường dẫn. Ngoài ra, ký tự đại diện ** có thể khớp với 0 hoặc nhiều phân đoạn đường dẫn đầy đủ, nhưng phải được khai báo là một phân đoạn đường dẫn độc lập.

Ví dụ:
  • foo/bar.txt khớp chính xác với tệp foo/bar.txt trong gói này (trừ phi foo/ là một gói con)
  • foo/*.txt khớp với mọi tệp trong thư mục foo/ nếu tệp kết thúc bằng .txt (trừ phi foo/ là một gói con)
  • foo/a*.htm* khớp với mọi tệp trong thư mục foo/ bắt đầu bằng a, sau đó có một chuỗi tuỳ ý (có thể trống), sau đó có .htm và kết thúc bằng một chuỗi tuỳ ý khác (trừ phi foo/ là một gói con); chẳng hạn như foo/axx.htmfoo/a.html hoặc foo/axxx.html
  • foo/* khớp với mọi tệp trong thư mục foo/ (trừ phi foo/ là một gói con); nó không khớp với chính thư mục foo ngay cả khi exclude_directories được đặt thành 0
  • foo/** khớp với mọi tệp trong mọi thư mục con không phải thư mục con của gói trong thư mục con cấp đầu tiên của gói foo/; nếu exclude_directories được đặt thành 0, thì bản thân thư mục foo cũng khớp với mẫu; trong trường hợp này, ** được coi là khớp với 0 phân đoạn đường dẫn
  • **/a.txt khớp với các tệp a.txt trong thư mục của gói này, cộng với các thư mục con không phải là thư mục con của gói.
  • **/bar/**/*.txt khớp với mọi tệp .txt trong mọi thư mục con không phải là gói con của gói này, nếu có ít nhất một thư mục trên đường dẫn kết quả có tên là bar, chẳng hạn như xxx/bar/yyy/zzz/a.txt hoặc bar/a.txt (hãy nhớ rằng ** cũng khớp với 0 phân đoạn) hoặc bar/zzz/a.txt
  • ** khớp với mọi tệp trong mọi thư mục con không phải là thư mục con của gói này
  • foo**/a.txt là một mẫu không hợp lệ, vì ** phải đứng một mình dưới dạng một đoạn
  • foo/ là một mẫu không hợp lệ, vì phân đoạn thứ hai được xác định sau / là một chuỗi trống

Nếu đối số exclude_directories được bật (đặt thành 1), các tệp thuộc loại thư mục sẽ bị bỏ qua trong kết quả (mặc định là 1).

Nếu đối số allow_empty được đặt thành False, hàm glob sẽ báo lỗi nếu kết quả là danh sách trống.

Có một số điểm hạn chế và cảnh báo quan trọng:

  1. glob() chạy trong quá trình đánh giá tệp BUILD, nên glob() chỉ khớp các tệp trong cây nguồn của bạn, không bao giờ khớp các tệp được tạo. Nếu đang tạo một mục tiêu yêu cầu cả tệp nguồn và tệp được tạo, bạn phải thêm một danh sách rõ ràng các tệp được tạo vào glob. Hãy xem ví dụ bên dưới với :mylib:gen_java_srcs.

  2. Nếu một quy tắc có cùng tên với một tệp nguồn được so khớp, thì quy tắc đó sẽ "che khuất" tệp.

    Để hiểu rõ điều này, hãy nhớ rằng glob() trả về một danh sách các đường dẫn, vì vậy, việc sử dụng glob() trong thuộc tính của các quy tắc khác (ví dụ: srcs = glob(["*.cc"])) có tác dụng tương tự như việc liệt kê rõ ràng các đường dẫn trùng khớp. Ví dụ: nếu glob() tạo ra ["Foo.java", "bar/Baz.java"] nhưng cũng có một quy tắc trong gói có tên "Foo.java" (được phép, mặc dù Bazel cảnh báo về quy tắc này), thì người dùng glob() sẽ sử dụng quy tắc "Foo.java" (đầu ra của quy tắc) thay vì tệp "Foo.java". Hãy xem vấn đề 10395 trên GitHub để biết thêm thông tin chi tiết.

  3. Glob có thể khớp với các tệp trong thư mục con. Và tên thư mục con có thể được sử dụng ký tự đại diện. Tuy nhiên...
  4. Nhãn không được phép vượt quá ranh giới gói và glob không khớp với các tệp trong gói con.

    Ví dụ: biểu thức glob **/*.cc trong gói x không bao gồm x/y/z.cc nếu x/y tồn tại dưới dạng một gói (dưới dạng x/y/BUILD hoặc ở một nơi khác trên đường dẫn gói). Điều này có nghĩa là kết quả của biểu thức glob thực sự phụ thuộc vào sự tồn tại của các tệp BUILD — tức là cùng một biểu thức glob sẽ bao gồm x/y/z.cc nếu không có gói nào có tên x/y hoặc gói đó được đánh dấu là đã bị xoá bằng cờ --deleted_packages.

  5. Quy định hạn chế nêu trên áp dụng cho tất cả biểu thức glob, bất kể chúng sử dụng ký tự đại diện nào.
  6. Một tệp ẩn có tên tệp bắt đầu bằng . hoàn toàn khớp với cả ký tự đại diện ***. Nếu bạn muốn so khớp một tệp ẩn với một mẫu phức tạp, thì mẫu của bạn cần bắt đầu bằng .. Ví dụ: *.*.txt sẽ khớp với .foo.txt, nhưng *.txt thì không. Các thư mục ẩn cũng được so khớp theo cách tương tự. Các thư mục ẩn có thể chứa những tệp không bắt buộc làm dữ liệu đầu vào và có thể làm tăng số lượng tệp được glob không cần thiết cũng như mức tiêu thụ bộ nhớ. Để loại trừ các thư mục ẩn, hãy thêm các thư mục đó vào đối số danh sách "exclude".
  7. Ký tự đại diện "**" có một trường hợp đặc biệt: mẫu "**" không khớp với đường dẫn thư mục của gói. Tức là glob(["**"], exclude_directories = 0) khớp với tất cả các tệp và thư mục một cách bắc cầu hoàn toàn trong thư mục của gói hiện tại (nhưng tất nhiên là không đi vào thư mục của các gói con – hãy xem lưu ý trước đó về vấn đề này).

Nói chung, bạn nên cố gắng cung cấp một tiện ích phù hợp (ví dụ: *.html) thay vì sử dụng "*" không có phần mở rộng cho mẫu glob. Tên rõ ràng hơn vừa tự ghi lại vừa đảm bảo rằng bạn không vô tình khớp các tệp sao lưu hoặc các tệp tự động lưu của emacs/vi/...

Khi viết các quy tắc xây dựng, bạn có thể liệt kê các phần tử của glob. Điều này cho phép tạo các quy tắc riêng cho mọi đầu vào, chẳng hạn như. Hãy xem phần ví dụ về mẫu mở rộng bên dưới.

Ví dụ về glob

Tạo một thư viện Java được tạo từ tất cả các tệp java trong thư mục này và tất cả các tệp do quy tắc :gen_java_srcs tạo.

java_library(
    name = "mylib",
    srcs = glob(["*.java"]) + [":gen_java_srcs"],
    deps = "...",
)

genrule(
    name = "gen_java_srcs",
    outs = [
        "Foo.java",
        "Bar.java",
    ],
    ...
)

Bao gồm tất cả các tệp txt trong thư mục testdata, ngoại trừ experimental.txt. Xin lưu ý rằng các tệp trong thư mục con của testdata sẽ không được đưa vào. Nếu bạn muốn đưa các tệp đó vào, hãy sử dụng glob đệ quy (**).

sh_test(
    name = "mytest",
    srcs = ["mytest.sh"],
    data = glob(
        ["testdata/*.txt"],
        exclude = ["testdata/experimental.txt"],
    ),
)

Ví dụ về glob đệ quy

Tạo chương trình kiểm thử phụ thuộc vào tất cả các tệp txt trong thư mục testdata và mọi thư mục con của thư mục đó (cũng như các thư mục con của thư mục con, v.v.). Các thư mục con chứa tệp BUILD sẽ bị bỏ qua. (Xem các giới hạn và cảnh báo ở trên.)

sh_test(
    name = "mytest",
    srcs = ["mytest.sh"],
    data = glob(["testdata/**/*.txt"]),
)

Tạo một thư viện được tạo từ tất cả các tệp java trong thư mục này và tất cả các thư mục con, ngoại trừ những thư mục có đường dẫn chứa một thư mục có tên là testing. Bạn nên tránh mẫu này nếu có thể, vì mẫu này có thể làm giảm mức tăng của bản dựng và do đó làm tăng thời gian xây dựng.

java_library(
    name = "mylib",
    srcs = glob(
        ["**/*.java"],
        exclude = ["**/testing/**"],
    ),
)

Ví dụ về mẫu mở rộng

Tạo một genrule riêng cho *_test.cc trong thư mục hiện tại để đếm số dòng trong tệp.

# Conveniently, the build language supports list comprehensions.
[genrule(
    name = "count_lines_" + f[:-3],  # strip ".cc"
    srcs = [f],
    outs = ["%s-linecount.txt" % f[:-3]],
    cmd = "wc -l $< >$@",
 ) for f in glob(["*_test.cc"])]

Nếu tệp BUILD ở trên nằm trong gói //foo và gói này chứa 3 tệp trùng khớp, a_test.cc, b_test.cc và c_test.cc, thì việc chạy bazel query '//foo:all' sẽ liệt kê tất cả các quy tắc đã được tạo:

$ bazel query '//foo:all' | sort
//foo:count_lines_a_test
//foo:count_lines_b_test
//foo:count_lines_c_test

chọn

select(
    {conditionA: valuesA, conditionB: valuesB, ...},
    no_match_error = "custom message"
)

select() là hàm trợ giúp giúp thuộc tính quy tắc có thể định cấu hình. Bạn có thể thay thế phía bên phải của hầu hết mọi hoạt động chỉ định thuộc tính để giá trị của hoạt động này phụ thuộc vào các cờ Bazel trên dòng lệnh. Ví dụ: bạn có thể sử dụng phương thức này để xác định các phần phụ thuộc dành riêng cho nền tảng hoặc để nhúng các tài nguyên khác nhau tuỳ thuộc vào việc một quy tắc được tạo ở chế độ "nhà phát triển" hay "phát hành".

Cách sử dụng cơ bản như sau:

sh_binary(
    name = "mytarget",
    srcs = select({
        ":conditionA": ["mytarget_a.sh"],
        ":conditionB": ["mytarget_b.sh"],
        "//conditions:default": ["mytarget_default.sh"]
    })
)

Thao tác này giúp bạn có thể định cấu hình thuộc tính srcs của sh_binary bằng cách thay thế việc chỉ định danh sách nhãn thông thường bằng một lệnh gọi select ánh xạ các điều kiện cấu hình với các giá trị phù hợp. Mỗi điều kiện là một giá trị tham chiếu nhãn đến config_setting hoặc constraint_value. Điều kiện này sẽ "khớp" nếu cấu hình của mục tiêu khớp với một tập hợp giá trị dự kiến. Sau đó, giá trị của mytarget#srcs sẽ trở thành bất kỳ danh sách nhãn nào khớp với lệnh gọi hiện tại.

Lưu ý:

  • Bạn chỉ có thể chọn một điều kiện duy nhất trong mỗi lần gọi.
  • Nếu có nhiều điều kiện trùng khớp và một điều kiện là trường hợp đặc biệt của các điều kiện khác, thì điều kiện đặc biệt sẽ được ưu tiên. Điều kiện B được coi là một chuyên môn của điều kiện A nếu B có tất cả các cờ và giá trị ràng buộc giống như A cộng với một số cờ hoặc giá trị ràng buộc bổ sung. Điều này cũng có nghĩa là tính năng phân giải chuyên môn hoá không được thiết kế để tạo một thứ tự như minh hoạ trong Ví dụ 2 bên dưới.
  • Nếu nhiều điều kiện trùng khớp và một điều kiện không phải là trường hợp chuyên biệt của tất cả các điều kiện khác, thì Bazel sẽ gặp lỗi, trừ phi tất cả các điều kiện đều phân giải thành cùng một giá trị.
  • Nhãn giả đặc biệt //conditions:default được coi là khớp nếu không có điều kiện nào khác khớp. Nếu bạn bỏ qua điều kiện này, thì một số quy tắc khác phải khớp để tránh xảy ra lỗi.
  • select có thể được nhúng bên trong một chỉ định thuộc tính lớn hơn. Vì vậy, srcs = ["common.sh"] + select({ ":conditionA": ["myrule_a.sh"], ...}) srcs = select({ ":conditionA": ["a.sh"]}) + select({ ":conditionB": ["b.sh"]}) là các biểu thức hợp lệ.
  • select hoạt động với hầu hết, nhưng không phải tất cả, các thuộc tính. Các thuộc tính không tương thích được đánh dấu nonconfigurable trong tài liệu.

    gói con

    subpackages(include, exclude=[], allow_empty=True)

    subpackages() là một hàm trợ giúp, tương tự như glob() liệt kê các gói con thay vì các tệp và thư mục. Thao tác này sử dụng các mẫu đường dẫn giống như glob() và có thể so khớp mọi gói con là thành phần trực tiếp của tệp BUILD hiện đang tải. Hãy xem glob để biết nội dung giải thích chi tiết và ví dụ về các mẫu bao gồm và loại trừ.

    Danh sách các gói con được trả về sẽ được sắp xếp theo thứ tự và chứa các đường dẫn tương ứng với gói tải hiện tại khớp với các mẫu đã cho trong include chứ không phải các mẫu trong exclude.

    Ví dụ:

    Ví dụ sau đây liệt kê tất cả các gói con trực tiếp cho gói foo/BUILD

    # The following BUILD files exist:
    # foo/BUILD
    # foo/bar/baz/BUILD
    # foo/bar/but/bad/BUILD
    # foo/sub/BUILD
    # foo/sub/deeper/BUILD
    #
    # In foo/BUILD a call to
    subs1 = subpackages(include = ["**"])
    
    # results in subs1 == ["sub", "bar/baz", "bar/but/bad"]
    #
    # 'sub/deeper' is not included because it is a subpackage of 'foo/sub' not of
    # 'foo'
    
    subs2 = subpackages(include = ["bar/*"])
    # results in subs2 = ["bar/baz"]
    #
    # Since 'bar' is not a subpackage itself, this looks for any subpackages under
    # all first level subdirectories of 'bar'.
    
    subs3 = subpackages(include = ["bar/**"])
    # results in subs3 = ["bar/baz", "bar/but/bad"]
    #
    # Since bar is not a subpackage itself, this looks for any subpackages which are
    # (1) under all subdirectories of 'bar' which can be at any level, (2) not a
    # subpackage of another subpackages.
    
    subs4 = subpackages(include = ["sub"])
    subs5 = subpackages(include = ["sub/*"])
    subs6 = subpackages(include = ["sub/**"])
    # results in subs4 and subs6 being ["sub"]
    # results in subs5 = [].
    #
    # In subs4, expression "sub" checks whether 'foo/sub' is a package (i.e. is a
    # subpackage of 'foo').
    # In subs5, "sub/*" looks for subpackages under directory 'foo/sub'. Since
    # 'foo/sub' is already a subpackage itself, the subdirectories will not be
    # traversed anymore.
    # In subs6, 'foo/sub' is a subpackage itself and matches pattern "sub/**", so it
    # is returned. But the subdirectories of 'foo/sub' will not be traversed
    # anymore.
    

    Nhìn chung, thay vì gọi trực tiếp hàm này, người dùng nên sử dụng mô-đun "subpackages" của skylib.