Hàm

Báo cáo sự cố Xem nguồn Hằng đêm · 7,3 · 7.2 · 7.1 · 7,0 · 6,5

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. Mã này được sử dụng tối đa một lần trong gói (tệp BUILD).

Đối với đối tác 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 REPO.bazel ở gốc của kho lưu trữ. Hàm repo() nhận chính xác các đối số giống như package().

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

Đối số

Thuộc tính Mô tả
default_applicable_licenses

Bí danh 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 quy tắc trong gói này.

Mọi quy tắc trong gói này đều có chế độ hiển thị được chỉ định trong thuộc tính này, trừ phi có quy định khác trong thuộc tính visibility của quy tắc. Để 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ề mức độ hiển thị. Chế độ hiển thị mặc định của gói không áp dụng cho exports_files, tức là công khai theo mặc định.

default_deprecation

String; giá trị mặc định là ""

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

default_package_metadata

Danh sách nhãn; giá trị 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 việc khai báo giấy phép và gói OSS. Hãy xem rules_license để biết ví dụ.

default_testonly

Boolean; giá trị mặc định là False, ngoại trừ trường hợp có ghi chú

Đặt mặc định Thuộc tính testonly cho tất 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

Danh sách 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 xây dựng sử dụng để gắn thẻ các gói cần xử lý theo cách đặc biệt. Đừng sử dụng tính năng 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ụ

Phần khai báo dưới đây khai báo rằng các quy tắc trong gói này là chỉ hiển thị cho các thành viên của gói nhóm //foo:target. Khai báo từng chế độ hiển thị đối với quy tắc, nếu có, hãy 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 Thuộc tính visibility.

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

Một gói nhất định được coi là thuộc 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 là mục tiêu về mặt kỹ thuật, nhưng không được tạo theo quy tắc và không có biện pháp bảo vệ chế độ hiển thị nào.

Đối số

Thuộc tính Mô tả
name

Tên; bắt buộc

Tên duy nhất cho mục tiêu này.

packages

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

Danh sách không có hoặc có nhiều thông số kỹ thuật của gói.

Mỗi chuỗi thông số kỹ thuật của 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 hai dấu gạch chéo. 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ó /... theo sau. Ví dụ: //foo/... chỉ định tập hợp //foo và tất cả các gói con. //... chỉ định tất cả các gói trong kho lưu trữ hiện tại.
  3. Chuỗi public hoặc private mà chỉ định từng gói hoặc không gói nào. (Biểu mẫu này yêu cầu bạn phải đặt cờ --incompatible_package_group_has_public_syntax.)

Ngoài ra, hai kiểu thông số kỹ thuật đầu tiên của gói cũng có thể có tiền tố - để cho biết chúng bị phủ định.

Nhóm gói chứa bất kỳ gói nào khớp với ít nhất một trong thông số kỹ thuật tích cực và không có thông số kỹ thuật tiêu cực nào Ví dụ: giá trị [//foo/..., -//foo/tests/...] bao gồm tất cả các gói con của //foo mà không phải là 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, nó cũng giống như việc đặt thuộc tính thành danh sách trống, cũng giống như việc đặt danh sách này thành danh sách chứa chỉ private.

Lưu ý: Trước Bazel 6.0, thông số kỹ thuật //... có hành vi cũ giống với public. Chiến dịch này được khắc phục khi --incompatible_fix_package_group_reporoot_syntax là là bật 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ự là một phần của bazel query --output=proto (hoặc --output=xml), dấu gạch chéo ở đầu đã bị bỏ qua. Ví dụ: //pkg/foo/... sẽ xuất ra dưới dạng \"pkg/foo/...\". Hành vi này được khắc phục khi --incompatible_package_group_includes_double_slash là là bật mặc định sau Bazel 6.0.

includes

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

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

Các nhãn trong thuộc tính này phải tham chiếu đến các nhóm gói khác. Các gói trong nhóm gói được tham chiếu sẽ được đưa vào quá trình này nhóm gói. Đây là 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, sau đó mọi gói trong c cũng sẽ là thành viên của a.

Khi sử dụng cùng với thông số kỹ thuật của gói bị bỏ qua, xin lưu ý rằng cho mỗi nhóm được tính toán độc lập và các kết quả sau đó được tập hợp với nhau. Điều này có nghĩa là các thông số kỹ thuật trong một nhóm không ảnh hưởng đến các thông số kỹ thuật trong một nhóm khác.

Ví dụ

Nội dung khai báo package_group sau đây chỉ định một nhóm gói có tên là "tropical" (trái cây 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 chỉ định nhóm gói của một hư cấu ứng dụng:

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 sang một gói khác nếu chúng đượ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à dữ liệu đầ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 lật. 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. Đáp khai báo chế độ hiển thị cũng có thể được chỉ định; trong trường hợp này, các tệp sẽ được 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 sẽ 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. Giấy phép cũng có thể được chỉ định.

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 dịch vụ các gói có thể sử dụng thuộc tính này, ví dụ như trong thuộc tính data 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 đường dẫn mới, có thể thay đổi và được sắp xế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 hay 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 của 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 các phân đoạn đường dẫn khác. Như thường lệ với đường dẫn Unix, các 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 bất kỳ chuỗi con nào 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 /. Ký tự đại diện này có thể dùng nhiều lần trong một phân đoạn đường dẫn. Ngoài ra, ký tự đại diện ** có thể so khớp với 0 hoặc nhiều đoạn đường dẫn đầy đủ, nhưng phải được khai báo là một đ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ừ khi 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à gói con)
  • foo/a*.htm* khớp với mọi tệp trong foo/ thư mục 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ừ khi 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); 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 của gói con trong thư mục con cấp đầu tiên của gói foo/; nếu exclude_directories được đặt thành 0, foo thư mục cũng khớp với mẫu; trong trường hợp này, ** là được xem là khớp với các đoạn đường dẫn bằng 0
  • **/a.txt so khớp 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à gói con.
  • **/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 ít nhất một thư mục trên đường dẫn thu được được gọi 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à gói con của gói này
  • foo**/a.txt là một mẫu không hợp lệ, vì ** phải tự đứng vững dưới dạng một phân đoạn
  • foo/ là mẫu không hợp lệ vì đ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 của thư mục type sẽ bị bỏ qua khỏi kết quả (mặc định là 1).

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

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

  1. glob() chạy trong quá trình đánh giá tệp BUILD, glob() chỉ khớp với các tệp trong cây nguồn, không bao giờ tệp được tạo. Nếu bạn đang xây dựng 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 vào danh sách rõ ràng các tệp vào cụm từ tìm kiếm. 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 tệp nguồn phù hợp, thì quy tắc đó sẽ "bóng" tệp.

    Để hiểu điều này, hãy nhớ rằng glob() trả về một danh sách đườ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ũng có tác dụng tương tự như việc liệt kê rõ ràng các đường dẫn đã so khớp. Ví dụ: nếu glob() có lợi nhuận ["Foo.java", "bar/Baz.java"], nhưng cũng có một quy tắc trong gói có tên "Foo.java" (điều này được phép, mặc dù Bazel có cảnh báo về điều này), thì người dùng glob() sẽ sử dụng "Foo.java" quy tắc (kết quả) thay vì "Foo.java" tệp. Xem GitHub vấn đề #10395 để biết thêm 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ó ký tự đại diện. Tuy nhiên...
  4. Nhãn không được phép vượt qua ranh giới gói và hình cầu thì không khớp với 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 ở 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 là x/y hoặc gói đó đã được đánh dấu là đã xoá bằng cờ --deleted_packages.

  5. Quy định hạn chế ở trên áp dụng cho tất cả biểu thức toàn cầu, bất kể ký tự đại diện nào chúng sử dụng.
  6. Một tệp ẩn có tên tệp bắt đầu bằng . được khớp hoàn toàn bằng 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 hợ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 sẽ không khớp. 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ể bao gồm các 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 gộp không cần thiết cũng như mức sử dụng 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" (loại trừ).
  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. Điều đó có nghĩa là glob(["**"], exclude_directories = 0) khớp với tất cả các tệp và thư mục một cách nghiêm ngặt trong thư mục của gói hiện tại (nhưng tất nhiên không đi vào thư mục của các gói con – hãy xem ghi chú trước đó về điều đó).

Nói chung, bạn nên thử cung cấp tiện ích mở rộng thích hợp (ví dụ: *.html) thay vì sử dụng dấu '*' đơn thuần cho mẫu cầu đường. 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 so khớp bản sao lưu tệp, hoặc emacs/vi/... tệp tự động lưu.

Khi viết các quy tắc bản dựng, bạn có thể liệt kê các phần tử của khối cầu. Ví dụ: điều này cho phép tạo các quy tắc riêng lẻ cho mỗi dữ liệu đầu vào. Hãy xem phần ví dụ về glob mở rộng bên dưới.

Ví dụ về ánh sáng loá

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 dữ liệu kiểm thử thư mục, ngoại trừ tệp experiment.txt. Lưu ý rằng các tệp trong thư mục con của dữ liệu kiểm thử sẽ không được đưa vào. Nếu bạn muốn thêm các tệp đó, 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ề vòng tròn đệ quy

Đảm bả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 dữ liệu kiểm thử và bất kỳ tệp nào của các thư mục con (và thư mục con của chúng, 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à lưu ý ở 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ả thư mục con ngoại trừ những thư mục có đường dẫn bao gồm một thư mục có tên là kiểm thử. 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 độ gia tăng của bản dựng, do đó làm tăng thời gian xây dựng.

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

Ví dụ về băng thông rộng 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 ba tệp khớp là 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 đã 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 tạo thuộc tính quy tắc có thể định cấu hình. Tham số này có thể thay thế phía bên phải của gần như mọi thao tác gán thuộc tính vì vậy giá trị của thuộc tính đó phụ thuộc vào cờ Bazel dòng lệnh. Ví dụ: bạn có thể sử dụng tính năng 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 nhiều tài nguyên tuỳ thuộc vào việc một quy tắc được tạo ở chế độ "nhà phát triển" hay "bản 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"]
    })
)

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

Lưu ý:

  • Chỉ chọn một điều kiện trên bất kỳ lệnh gọi nào.
  • Nếu nhiều điều kiện khớp và một điều kiện là chuyên môn của các điều kiện khác, thì điều kiện chuyên môn sẽ được ưu tiên. Điều kiện B được coi là chuyên biệt 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. Việc này cũng có nghĩa là quá trình phân giải chuyên môn không được thiết kế để tạo thứ tự như được minh hoạ trong Ví dụ 2 bên dưới.
  • Nếu nhiều điều kiện khớp và một điều kiện không phải là chuyên biệt của tất cả các điều kiện khác, thì Bazel sẽ không thành công và báo lỗi, trừ khi 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, một số quy tắc khác phải khớp để tránh lỗi.
  • select có thể được nhúng bên trong gán thuộc tính. 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ả) thuộc tính. Các thuộc tính không tương thích được đánh dấu là 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ì tệp và thư mục. Chiến dịch này sử dụng cùng một đường dẫn dưới dạng glob() và có thể khớp với bất kỳ gói con nào là thành phần con trực tiếp của tệp BUILD đang tải. Xem phần glob để biết nội dung giải thích chi tiết và các ví dụ về phần bao gồm và loại trừ mẫu.

    Danh sách kết quả của các gói con được trả về theo thứ tự được sắp xếp và chứa đườ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 URL 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 "gói con" của skylib.