Aturan

Laporkan masalah Lihat sumber Nightly · 7.4 . 7,3 · 7,2 · 7,1 · 7,0 · 6,5

Aturan menentukan serangkaian tindakan yang dilakukan Bazel input untuk menghasilkan satu set {i>output<i}, yang direferensikan pada penyedia yang ditampilkan oleh fungsi implementasi. Misalnya, sebuah C++ aturan biner mungkin:

  1. Ambil sekumpulan file sumber .cpp (input).
  2. Jalankan g++ di file sumber (tindakan).
  3. Tampilkan penyedia DefaultInfo dengan output yang dapat dieksekusi dan file lainnya untuk disediakan saat runtime.
  4. Tampilkan penyedia CcInfo dengan informasi khusus C++ yang dikumpulkan dari target dan dependensinya.

Dari perspektif Bazel, g++ dan library C++ standar juga merupakan input untuk aturan ini. Sebagai penulis aturan, Anda tidak hanya harus mempertimbangkan input yang disediakan pengguna ke aturan, tetapi juga semua alat dan library yang diperlukan untuk menjalankan tindakan.

Sebelum membuat atau mengubah aturan apa pun, pastikan Anda memahami fase build Bazel. Penting untuk memahami tiga fase build (pemuatan, analisis, dan eksekusi). Juga berguna untuk pelajari makro untuk memahami perbedaan antara aturan dan makro. Untuk memulai, tinjau Tutorial Aturan terlebih dahulu. Kemudian, gunakan halaman ini sebagai referensi.

Beberapa aturan dibuat di Bazel sendiri. Aturan native ini, seperti cc_library dan java_binary, menyediakan beberapa dukungan inti untuk bahasa tertentu. Dengan menentukan aturan sendiri, Anda dapat menambahkan dukungan serupa untuk bahasa dan alat yang tidak didukung Bazel secara native.

Bazel menyediakan model ekstensi untuk menulis aturan menggunakan bahasa Starlark. Aturan ini ditulis dalam file .bzl, yang dapat dimuat langsung dari file BUILD.

Saat menentukan aturan sendiri, Anda dapat memutuskan atribut yang didukung dan cara aturan tersebut menghasilkan output.

Fungsi implementation aturan menentukan perilaku persisnya selama fase analisis. Fungsi ini tidak menjalankan menggunakan perintah eksternal. Sebaliknya, ini mendaftarkan tindakan yang akan digunakan nanti selama fase eksekusi untuk membuat output aturan, jika output tersebut diperlukan.

Pembuatan aturan

Di dalam file .bzl, gunakan fungsi rule untuk menentukan aturan, dan menyimpan hasilnya dalam variabel global. Panggilan ke rule menentukan atribut dan atribut fungsi penerapan:

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "deps": attr.label_list(),
        ...
    },
)

Ini menentukan jenis aturan bernama example_library.

Panggilan ke rule juga harus menentukan apakah aturan membuat output dapat dieksekusi (dengan executable=True), atau secara khusus dapat dieksekusi pengujian (dengan test=True). Jika yang terakhir, aturan tersebut adalah aturan pengujian, dan nama aturan harus diakhiri dengan _test.

Pembuatan instance target

Aturan dapat dimuat dan dipanggil dalam file BUILD:

load('//some/pkg:rules.bzl', 'example_library')

example_library(
    name = "example_target",
    deps = [":another_target"],
    ...
)

Setiap panggilan ke aturan build tidak menampilkan nilai, tetapi memiliki efek samping dari penetapan target. Langkah ini disebut membuat instance aturan. Ini menetapkan nama untuk target baru dan nilai untuk atribut target.

Aturan juga dapat dipanggil dari fungsi Starlark dan dimuat dalam file .bzl. Fungsi Starlark yang memanggil aturan disebut makro Starlark. Makro Starlark pada akhirnya harus dipanggil dari file BUILD, dan hanya dapat dipanggil selama fase pemuatan, saat BUILD file dievaluasi untuk membuat instance target.

Atribut

Atribut adalah argumen aturan. Atribut dapat memberikan nilai tertentu ke penerapan target, atau mereka dapat merujuk ke target, sehingga membuat grafik dependensi.

Atribut khusus aturan, seperti srcs atau deps, ditentukan dengan meneruskan peta dari nama atribut ke skema (dibuat menggunakan modul attr) ke parameter attrs dari rule. Atribut umum, seperti name dan visibility, secara implisit ditambahkan ke semua aturan. Atribut tambahan ditambahkan secara implisit ke aturan yang dapat dieksekusi dan pengujian secara khusus. Atribut yang secara implisit ditambahkan ke aturan tidak bisa dimasukkan dalam kamus yang diteruskan ke attrs.

Atribut dependensi

Aturan yang memproses kode sumber biasanya menentukan atribut berikut untuk menangani berbagai jenis dependensi:

  • srcs menentukan file sumber yang diproses oleh tindakan target. Sering kali, skema atribut menentukan ekstensi file yang diharapkan untuk jenis file sumber yang diproses aturan. Aturan untuk bahasa dengan file header umumnya menentukan atribut hdrs terpisah untuk header yang diproses oleh target dan konsumennya.
  • deps menentukan dependensi kode untuk target. Skema atribut harus menentukan penyedia yang harus menyediakan dependensi tersebut. (Misalnya, cc_library menyediakan CcInfo.)
  • data menentukan file yang akan disediakan saat runtime untuk semua file yang dapat dieksekusi yang bergantung pada target. Tindakan ini akan memungkinkan file arbitrer ditentukan.
example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = [".example"]),
        "hdrs": attr.label_list(allow_files = [".header"]),
        "deps": attr.label_list(providers = [ExampleInfo]),
        "data": attr.label_list(allow_files = True),
        ...
    },
)

Ini adalah contoh atribut dependensi. Atribut yang menentukan label input (yang didefinisikan dengan attr.label_list, attr.label, atau attr.label_keyed_string_dict) menentukan dependensi jenis tertentu antara target dan target yang labelnya (atau label yang sesuai Label objek) tercantum dalam atribut tersebut saat target didefinisikan. Repositori, dan mungkin jalur, untuk label ini telah diselesaikan relatif terhadap target yang ditentukan.

example_library(
    name = "my_target",
    deps = [":other_target"],
)

example_library(
    name = "other_target",
    ...
)

Dalam contoh ini, other_target adalah dependensi my_target, sehingga other_target dianalisis terlebih dahulu. Akan terjadi kesalahan jika ada siklus di dalam grafik dependensi target.

Atribut pribadi dan dependensi implisit

Atribut dependensi dengan nilai default membuat dependensi implisit. Ini implisit karena merupakan bagian dari grafik target yang tidak ditentukan pengguna dalam file BUILD. Dependensi implisit berguna untuk melakukan hard code pada hubungan antara aturan dan alat (dependensi waktu build, seperti compiler), karena sebagian besar waktu pengguna tidak tertarik untuk menentukan alat yang digunakan aturan. Di dalam fungsi penerapan aturan, nilai ini diperlakukan sama dengan dependensi lainnya.

Jika Anda ingin menyediakan dependensi implisit tanpa mengizinkan pengguna untuk mengganti nilai tersebut, Anda dapat membuat atribut menjadi pribadi dengan memberinya nama yang dimulai dengan garis bawah (_). Atribut pribadi harus memiliki nilai default masing-masing. Secara umum, penggunaan atribut pribadi hanya masuk akal untuk dependensi implisit.

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        ...
        "_compiler": attr.label(
            default = Label("//tools:example_compiler"),
            allow_single_file = True,
            executable = True,
            cfg = "exec",
        ),
    },
)

Dalam contoh ini, setiap target jenis example_library memiliki dependensi pada compiler //tools:example_compiler. Hal ini memungkinkan fungsi implementasi example_library untuk menghasilkan tindakan yang memanggil compiler, meskipun pengguna tidak meneruskan labelnya sebagai input. Sejak _compiler adalah atribut pribadi, yang berarti ctx.attr._compiler akan selalu menunjuk ke //tools:example_compiler di semua target aturan ini . Atau, Anda dapat memberi nama atribut compiler tanpa garis bawah dan mempertahankan nilai default. Hal ini memungkinkan pengguna untuk mengganti kompilator berbeda jika perlu, tetapi tidak membutuhkan pemahaman terhadap label.

Dependensi implisit umumnya digunakan untuk alat yang berada di repositori yang sama dengan implementasi aturan. Jika alat berasal dari platform eksekusi atau repositori lain, aturan harus mendapatkan alat tersebut dari toolchain.

Atribut output

Atribut output, seperti attr.output dan attr.output_list, deklarasikan file output yang target dimunculkan. Atribut ini berbeda dari atribut dependensi dalam dua cara:

  • Parameter ini menentukan target file output, bukan merujuk ke target yang ditentukan di tempat lain.
  • Target file output bergantung pada target aturan yang dibuat instance-nya, bukan sebaliknya.

Biasanya, atribut output hanya digunakan saat aturan perlu membuat output dengan nama yang ditentukan pengguna yang tidak dapat didasarkan pada nama target. Jika aturan memiliki satu atribut output, biasanya bernama out atau outs.

Atribut output adalah cara yang lebih disukai untuk membuat output yang telah dideklarasikan sebelumnya, yang dapat secara khusus bergantung atau diminta di command line.

Fungsi penerapan

Setiap aturan memerlukan fungsi implementation. Fungsi ini dijalankan secara ketat dalam fase analisis dan mengubah grafik target yang dihasilkan dalam fase pemuatan menjadi grafik tindakan yang akan dilakukan selama fase eksekusi. Dengan demikian, implementasi tidak bisa benar-benar membaca atau menulis file.

Fungsi penerapan aturan biasanya bersifat pribadi (dinamai dengan awalan garis bawah). Secara konvensional, {i>string<i} diberi nama yang sama dengan aturannya, tetapi diberi akhiran dengan _impl.

Fungsi implementasi menggunakan tepat satu parameter: konteks aturan, yang secara konvensional diberi nama ctx. Fungsi ini menampilkan daftar penyedia.

Target

Dependensi direpresentasikan pada waktu analisis sebagai Target objek terstruktur dalam jumlah besar. Objek ini berisi penyedia yang dihasilkan saat fungsi implementasi target dijalankan.

ctx.attr memiliki kolom yang sesuai dengan nama setiap atribut dependensi, yang berisi objek Target yang mewakili setiap dependensi langsung melalui atribut tersebut. Untuk atribut label_list, ini adalah daftar Targets. Untuk atribut label, ini adalah satu Target atau None.

Daftar objek penyedia ditampilkan oleh fungsi implementasi target:

return [ExampleInfo(headers = depset(...))]

Hal tersebut dapat diakses menggunakan notasi indeks ([]), dengan jenis penyedia sebagai kunci. Ini bisa berupa penyedia kustom yang ditentukan di Starlark atau penyedia untuk aturan native tersedia sebagai Starlark variabel global.

Misalnya, jika aturan mengambil file header melalui atribut hdrs dan menyediakannya ke tindakan kompilasi target dan konsumennya, aturan tersebut dapat mengumpulkannya seperti ini:

def _example_library_impl(ctx):
    ...
    transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]

Untuk gaya lama yang mana struct ditampilkan dari fungsi implementasi target, bukan daftar objek penyedia:

return struct(example_info = struct(headers = depset(...)))

Penyedia dapat diambil dari kolom yang sesuai dari objek Target:

transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]

Gaya ini sangat tidak disarankan dan aturannya harus bermigrasi dari lokasi tersebut.

File

File direpresentasikan oleh objek File. Karena Bazel tidak melakukan I/O file selama fase analisis, objek ini tidak dapat digunakan untuk secara langsung membaca atau menulis isi file. Sebaliknya, akan diteruskan ke tindakan-emitting fungsi (lihat ctx.actions) untuk membuat bagian-bagian dari grafik tindakan.

File dapat berupa file sumber atau file yang dihasilkan. Setiap file yang dibuat harus merupakan output dari satu tindakan saja. File sumber tidak bisa berupa {i>output<i} dari tindakan apa pun.

Untuk setiap atribut dependensi, isian yang sesuai dari ctx.files berisi daftar output default dari semua dependensi melalui atribut tersebut:

def _example_library_impl(ctx):
    ...
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    ...

ctx.file berisi satu File atau None untuk atribut dependensi yang spesifikasinya ditetapkan allow_single_file=True. ctx.executable berperilaku sama seperti ctx.file, tetapi hanya berisi kolom untuk atribut dependensi yang spesifikasinya menetapkan executable=True.

Mendeklarasikan output

Selama fase analisis, fungsi penerapan aturan dapat membuat output. Karena semua label harus diketahui selama fase pemuatan, label tambahan ini output tidak memiliki label. Objek File untuk output dapat dibuat menggunakan ctx.actions.declare_file dan ctx.actions.declare_directory. Sering kali, nama output didasarkan pada nama target, ctx.label.name:

def _example_library_impl(ctx):
  ...
  output_file = ctx.actions.declare_file(ctx.label.name + ".output")
  ...

Untuk output yang telah dideklarasikan sebelumnya, seperti yang dibuat untuk atribut output, objek File dapat diambil dari kolom yang sesuai dari ctx.outputs.

Tindakan

Tindakan menjelaskan cara membuat kumpulan output dari kumpulan input, misalnya "jalankan gcc di hello.c dan dapatkan hello.o". Saat tindakan dibuat, Bazel tidak menjalankan perintah dengan seketika. Ia mendaftarkannya dalam grafik dependensi, karena suatu tindakan dapat bergantung pada {i>output<i} dari tindakan lain. Misalnya, dalam C, linker harus dipanggil setelah compiler.

Fungsi tujuan umum yang membuat tindakan didefinisikan di ctx.actions:

ctx.actions.args dapat digunakan untuk mengumpulkan argumen tindakan secara efisien. Menghindari depset yang diratakan hingga waktu eksekusi:

def _example_library_impl(ctx):
    ...

    transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    inputs = depset(srcs, transitive=[headers])
    output_file = ctx.actions.declare_file(ctx.label.name + ".output")

    args = ctx.actions.args()
    args.add_joined("-h", headers, join_with=",")
    args.add_joined("-s", srcs, join_with=",")
    args.add("-o", output_file)

    ctx.actions.run(
        mnemonic = "ExampleCompile",
        executable = ctx.executable._compiler,
        arguments = [args],
        inputs = inputs,
        outputs = [output_file],
    )
    ...

Tindakan mengambil daftar atau depset file input dan membuat daftar file output (tidak kosong). Kumpulan file input dan output harus diketahui selama fase analisis. Hal itu mungkin tergantung pada nilai , termasuk penyedia dari dependensi, tetapi tidak dapat bergantung pada hasil eksekusi. Misalnya, jika tindakan Anda menjalankan perintah {i>unzip<i}, Anda harus menentukan file mana yang ingin di-inflate (sebelum menjalankan ekstrak). Tindakan yang membuat jumlah file yang bervariasi secara internal dapat menggabungkannya dalam satu file (seperti zip, tar, atau format arsip lainnya).

Tindakan harus mencantumkan semua inputnya. Mencantumkan input yang tidak digunakan diizinkan, tetapi tidak efisien.

Tindakan harus membuat semua outputnya. Mereka dapat menulis file lain, tapi segala sesuatu yang tidak ada dalam {i>output<i} tidak akan tersedia bagi konsumen. Semua output yang dinyatakan harus ditulis oleh tindakan tertentu.

Tindakan sebanding dengan fungsi murni: Tindakan tersebut seharusnya hanya bergantung pada input yang diberikan, dan menghindari mengakses informasi komputer, nama pengguna, jam, jaringan, atau perangkat I/O (kecuali untuk input pembacaan dan output penulisan). Ini adalah penting karena {i>output<i} akan di-cache dan digunakan kembali.

Dependensi diselesaikan oleh Bazel, yang akan menentukan tindakan mana yang dijalankan. Ini adalah error jika ada siklus dalam grafik dependensi. Membuat suatu tindakan tidak menjamin bahwa tindakan itu akan dijalankan, itu tergantung pada apakah output-nya diperlukan untuk build.

Penyedia

Penyedia adalah informasi yang diekspos oleh aturan ke aturan lain yang yang bergantung pada {i>database.<i} Data ini dapat mencakup file output, library, parameter yang akan diteruskan pada baris perintah alat, atau apa pun yang harus diketahui oleh konsumen target lebih lanjut.

Karena fungsi implementasi aturan hanya bisa membaca penyedia dari dependensi langsung target yang dibuat instance, aturan perlu meneruskan informasi dari dependensi target yang perlu diketahui oleh konsumen, umumnya dengan mengakumulasinya ke dalam depset.

Penyedia target ditentukan oleh daftar objek Provider yang ditampilkan oleh fungsi implementasi.

Fungsi implementasi lama juga dapat ditulis dalam gaya lama di mana fungsi implementasi menampilkan struct, bukan daftar objek penyedia. Gaya ini sangat tidak dianjurkan dan aturan harus dimigrasikan darinya.

Output default

Output default target adalah output yang diminta secara default saat target diminta untuk dibuat di baris perintah. Misalnya, java_library target //pkg:foo memiliki foo.jar sebagai output default, sehingga akan dibuat oleh perintah bazel build //pkg:foo.

Output default ditentukan oleh parameter files DefaultInfo:

def _example_library_impl(ctx):
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        ...
    ]

Jika DefaultInfo tidak ditampilkan oleh penerapan aturan atau parameter files tidak ditentukan, DefaultInfo.files akan ditetapkan secara default ke semua output yang telah dideklarasikan sebelumnya (umumnya, yang dibuat oleh atribut output).

Aturan yang melakukan tindakan harus memberikan output default, meskipun output tersebut tidak diharapkan untuk digunakan secara langsung. Tindakan yang tidak ada dalam grafik output yang diminta akan dipangkas. Jika {i>output<i} hanya digunakan oleh konsumen target, tindakan tersebut tidak akan dilakukan ketika target dibuat secara terpisah. Ini membuat proses debug menjadi lebih sulit karena membangun ulang hanya target yang gagal tidak akan melakukan reka ulang kegagalan.

Runfiles

Runfile adalah sekumpulan file yang digunakan oleh target saat runtime (bukan saat build). Selama fase eksekusi, Bazel membuat hierarki direktori yang berisi symlink yang mengarah ke runfile. Tindakan ini akan melakukan staging lingkungan untuk biner sehingga dapat mengakses runfile selama runtime.

Runfile dapat ditambahkan secara manual selama pembuatan aturan. Objek runfiles dapat dibuat dengan metode runfiles di konteks aturan, ctx.runfiles dan diteruskan ke runfiles pada DefaultInfo. Output yang dapat dieksekusi dari aturan yang dapat dieksekusi secara implisit ditambahkan ke runfile.

Beberapa aturan menetapkan atribut, yang diberi nama secara umum data, yang outputnya ditambahkan target runfiles. Runfile juga harus digabungkan dari data, serta dari atribut apa pun yang mungkin memberikan kode untuk eksekusi akhir, umumnya srcs (yang mungkin berisi target filegroup dengan data terkait) dan deps.

def _example_library_impl(ctx):
    ...
    runfiles = ctx.runfiles(files = ctx.files.data)
    transitive_runfiles = []
    for runfiles_attr in (
        ctx.attr.srcs,
        ctx.attr.hdrs,
        ctx.attr.deps,
        ctx.attr.data,
    ):
        for target in runfiles_attr:
            transitive_runfiles.append(target[DefaultInfo].default_runfiles)
    runfiles = runfiles.merge_all(transitive_runfiles)
    return [
        DefaultInfo(..., runfiles = runfiles),
        ...
    ]

Penyedia kustom

Penyedia dapat ditentukan menggunakan fungsi provider untuk menyampaikan informasi khusus aturan:

ExampleInfo = provider(
    "Info needed to compile/link Example code.",
    fields={
        "headers": "depset of header Files from transitive dependencies.",
        "files_to_link": "depset of Files from compilation.",
    })

Fungsi implementasi aturan kemudian dapat membuat dan menampilkan instance penyedia:

def _example_library_impl(ctx):
  ...
  return [
      ...
      ExampleInfo(
          headers = headers,
          files_to_link = depset(
              [output_file],
              transitive = [
                  dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
              ],
          ),
      )
  ]
Inisialisasi kustom penyedia

Anda dapat melindungi pembuatan instance penyedia dengan logika prapemrosesan dan validasi kustom. Hal ini dapat digunakan untuk memastikan bahwa semua instance penyedia mematuhi invarian tertentu, atau memberi pengguna API yang lebih bersih untuk untuk mendapatkan instance.

Hal ini dilakukan dengan meneruskan callback init ke Fungsi provider. Jika callback ini diberikan, jenis nilai yang ditampilkan provider() akan berubah menjadi tuple dari dua nilai: simbol penyedia yang merupakan nilai yang ditampilkan biasa saat init tidak digunakan, dan "konstruktor mentah".

Dalam hal ini, saat simbol penyedia dipanggil, simbol tersebut akan meneruskan argumen ke callback init, bukan langsung menampilkan instance baru. Nilai yang ditampilkan callback harus berupa dict yang memetakan nama kolom (string) ke nilai; ini digunakan untuk melakukan inisialisasi kolom instance baru. Perhatikan bahwa callback dapat memiliki tanda tangan apa pun, dan jika argumen tidak cocok dengan tanda tangan, error akan dilaporkan seolah-olah callback dipanggil secara langsung.

Sebaliknya, konstruktor mentah akan mengabaikan callback init.

Contoh berikut menggunakan init untuk memproses dan memvalidasi argumennya:

# //pkg:exampleinfo.bzl

_core_headers = [...]  # private constant representing standard library files

# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
    if not files_to_link and not allow_empty_files_to_link:
        fail("files_to_link may not be empty")
    all_headers = depset(_core_headers, transitive = headers)
    return {'files_to_link': files_to_link, 'headers': all_headers}

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init)

export ExampleInfo

Implementasi aturan kemudian dapat membuat instance penyedia sebagai berikut:

    ExampleInfo(
        files_to_link=my_files_to_link,  # may not be empty
        headers = my_headers,  # will automatically include the core headers
    )

Konstruktor mentah dapat digunakan untuk menentukan fungsi factory publik alternatif yang tidak melalui logika init. Misalnya, dalam exampleinfo.bzl kita dapat menentukan:

def make_barebones_exampleinfo(headers):
    """Returns an ExampleInfo with no files_to_link and only the specified headers."""
    return _new_exampleinfo(files_to_link = depset(), headers = all_headers)

Biasanya, konstruktor mentah terikat dengan variabel yang namanya diawali dengan garis bawah (_new_exampleinfo di atas), sehingga kode pengguna tidak dapat memuat dan membuat instance penyedia arbitrer.

Penggunaan lain untuk init adalah hanya mencegah pengguna memanggil penyedia simbol sama sekali, dan memaksa mereka untuk menggunakan fungsi {i>factory<i} sebagai gantinya:

def _exampleinfo_init_banned(*args, **kwargs):
    fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init_banned)

def make_exampleinfo(...):
    ...
    return _new_exampleinfo(...)

Aturan yang dapat dijalankan dan aturan pengujian

Aturan yang dapat dieksekusi menentukan target yang dapat dipanggil oleh perintah bazel run. Aturan pengujian adalah jenis aturan khusus yang dapat dieksekusi yang targetnya juga dipanggil oleh perintah bazel test. Aturan yang dapat dijalankan dan pengujian dibuat oleh menetapkan executable atau Argumen test ke True dalam panggilan ke rule:

example_binary = rule(
   implementation = _example_binary_impl,
   executable = True,
   ...
)

example_test = rule(
   implementation = _example_binary_impl,
   test = True,
   ...
)

Aturan pengujian harus memiliki nama yang diakhiri dengan _test. (Nama target pengujian juga sering kali berakhiran _test menurut konvensi, tetapi ini tidak wajib.) Aturan non-pengujian tidak boleh memiliki akhiran ini.

Kedua jenis aturan tersebut harus menghasilkan file output yang dapat dieksekusi (yang mungkin atau tidak dideklarasikan sebelumnya) yang akan dipanggil oleh perintah run atau test. Untuk memberitahu Bazel {i>output<i} aturan mana yang akan digunakan sebagai {i>executable<i} ini, teruskan sebagai Argumen executable dari DefaultInfo yang ditampilkan penyedia layanan. executable tersebut ditambahkan ke output default aturan (sehingga Anda tidak perlu meneruskannya ke executable dan files). executable juga secara implisit ditambahkan ke runfile:

def _example_binary_impl(ctx):
    executable = ctx.actions.declare_file(ctx.label.name)
    ...
    return [
        DefaultInfo(executable = executable, ...),
        ...
    ]

Tindakan yang menghasilkan file ini harus menetapkan bit yang dapat dieksekusi pada file. Untuk tindakan ctx.actions.run atau ctx.actions.run_shell, hal ini harus dilakukan oleh alat pokok yang dipanggil oleh tindakan. Untuk tindakan ctx.actions.write, teruskan is_executable=True.

Sebagai perilaku lama, aturan yang dapat dieksekusi memiliki output khusus ctx.outputs.executable yang telah dideklarasikan sebelumnya. File ini berfungsi sebagai default yang dapat dieksekusi jika Anda tidak menentukannya menggunakan DefaultInfo; tidak boleh digunakan sebaliknya. Mekanisme output ini tidak digunakan lagi karena tidak mendukung penyesuaian nama file yang dapat dieksekusi pada waktu analisis.

Lihat contoh aturan yang dapat dieksekusi dan aturan pengujian.

Aturan yang dapat dijalankan dan aturan pengujian memiliki didefinisikan secara implisit, selain yang ditambahkan untuk semua aturan. Default atribut yang ditambahkan secara implisit tidak dapat diubah, meskipun hal ini dapat diatasi dengan menggabungkan aturan pribadi dalam makro Starlark yang mengubah default:

def example_test(size="small", **kwargs):
  _example_test(size=size, **kwargs)

_example_test = rule(
 ...
)

Lokasi runfile

Saat target yang dapat dieksekusi dijalankan dengan bazel run (atau test), root direktori runfile berdekatan dengan file yang dapat dieksekusi. Jalur tersebut berhubungan sebagai berikut:

# Given launcher_path and runfile_file:
runfiles_root = launcher_path.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
    runfiles_root, workspace_name, runfile_path)

Jalur ke File di direktori runfiles sesuai dengan File.short_path.

Biner yang dieksekusi langsung oleh bazel berdekatan dengan root direktori runfiles. Akan tetapi, biner yang disebut dari runfile tidak dapat membuat asumsi yang sama. Untuk mengurangi hal ini, setiap biner harus menyediakan cara untuk menerima root runfile-nya sebagai parameter menggunakan argumen/flag lingkungan atau command line. Ini memungkinkan biner meneruskan root runfile kanonis yang benar ke kode biner yang dipanggilnya. Jika tidak ditetapkan, biner dapat menebak bahwa ini adalah biner pertama yang dipanggil dan mencari direktori runfile yang berdekatan.

Topik lanjutan

Meminta file output

Satu target dapat memiliki beberapa file output. Saat perintah bazel build berjalan, beberapa {i>output<i} dari target yang diberikan ke perintah itu dianggap diminta. Bazel hanya mem-build file yang diminta ini dan file yang menjadi dependensinya secara langsung atau tidak langsung. (Dalam hal grafik aksi, Bazel hanya menjalankan tindakan yang dapat dijangkau sebagai dependensi transitif dari file yang diminta.)

Selain output default, setiap output yang dideklarasikan sebelumnya dapat secara eksplisit diminta pada baris perintah. Aturan dapat menentukan output melalui atribut output. Dalam hal ini, pengguna secara eksplisit memilih label untuk output saat membuat instance aturan. Untuk mendapatkan File untuk atribut output, gunakan atribut dari ctx.outputs. Aturan juga dapat menentukan output yang telah dideklarasikan sebelumnya secara implisit berdasarkan nama target, tetapi fitur ini tidak digunakan lagi.

Selain output default, ada grup output, yang merupakan koleksi {i>output file<i} yang mungkin diminta bersama. Permintaan ini dapat diminta dengan --output_groups Misalnya, jika target //pkg:mytarget adalah jenis aturan yang memiliki grup output debug_files, file ini dapat dibuat dengan menjalankan bazel build //pkg:mytarget --output_groups=debug_files. Karena {i>output<i} yang tidak dideklarasikan tidak memiliki label, permintaan hanya dapat diminta dengan muncul di output default atau output ras.

Grup output dapat ditentukan dengan Penyedia OutputGroupInfo. Perhatikan bahwa tidak seperti banyak penyedia bawaan, OutputGroupInfo dapat mengambil parameter dengan nama arbitrer untuk menentukan grup output dengan nama tersebut:

def _example_library_impl(ctx):
    ...
    debug_file = ctx.actions.declare_file(name + ".pdb")
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        OutputGroupInfo(
            debug_files = depset([debug_file]),
            all_files = depset([output_file, debug_file]),
        ),
        ...
    ]

Selain itu, tidak seperti sebagian besar penyedia, OutputGroupInfo dapat ditampilkan oleh aspek dan target aturan tempat aspek tersebut diterapkan, selama aspek dan target aturan tidak menentukan grup output yang sama. Dalam hal ini, penyedia yang dihasilkan akan digabungkan.

Perhatikan bahwa OutputGroupInfo umumnya tidak boleh digunakan untuk menyampaikan jenis file tertentu dari target ke tindakan konsumennya. Sebagai gantinya, tentukan penyedia khusus aturan untuk hal tersebut.

Konfigurasi

Bayangkan Anda ingin membangun biner C++ untuk arsitektur yang berbeda. Build dapat bersifat kompleks dan melibatkan beberapa langkah. Beberapa biner menengah, seperti compiler dan generator kode, harus berjalan di platform eksekusi (yang dapat berupa host Anda, atau eksekutor jarak jauh). Beberapa biner seperti {i>output<i} akhir harus dibangun untuk arsitektur target.

Oleh karena itu, Bazel memiliki konsep "konfigurasi" dan transisi. Tujuan target paling atas (yang diminta di baris perintah) dibangun di &quot;target&quot; konfigurasi sementara alat yang harus berjalan pada platform eksekusi dibangun dalam {i>exec<i} konfigurasi Anda. Aturan dapat menghasilkan tindakan yang berbeda berdasarkan pada konfigurasi, misalnya untuk mengubah arsitektur {i>cpu<i} yang diteruskan ke compiler. Dalam beberapa kasus, library yang sama mungkin diperlukan untuk konfigurasi yang berbeda. Jika hal ini terjadi, kode akan dianalisis dan berpotensi di-build beberapa kali.

Secara default, Bazel mem-build dependensi target dalam konfigurasi yang sama dengan target itu sendiri, dengan kata lain tanpa transisi. Jika dependensi adalah alat yang diperlukan untuk membantu mem-build target, atribut yang sesuai harus menentukan transisi ke konfigurasi exec. Hal ini menyebabkan alat dan semua dependensinya di-build untuk platform eksekusi.

Untuk setiap atribut dependensi, Anda dapat menggunakan cfg untuk menentukan apakah dependensi harus membangun dalam konfigurasi yang sama atau transisi ke konfigurasi exec. Jika atribut dependensi memiliki flag executable=True, cfg harus ditetapkan secara eksplisit. Hal ini untuk mencegah terjadinya kesalahan pembuatan alat secara tidak sengaja konfigurasi Anda. Lihat contoh

Secara umum, sumber, library dependen, dan file yang dapat dieksekusi yang akan diperlukan saat runtime dapat menggunakan konfigurasi yang sama.

Alat yang dijalankan sebagai bagian dari build (seperti compiler atau generator kode) harus dibuat untuk konfigurasi exec. Dalam hal ini, tentukan cfg="exec" dalam atribut.

Jika tidak, file yang dapat dieksekusi yang digunakan saat runtime (seperti bagian dari pengujian) harus dibangun untuk konfigurasi target. Dalam hal ini, tentukan cfg="target" dalam atribut.

cfg="target" sebenarnya tidak melakukan apa pun: ini hanya nilai kenyamanan untuk membantu desainer aturan untuk menjelaskan niat mereka secara eksplisit. Jika executable=False, yang berarti cfg bersifat opsional, hanya tetapkan ini jika benar-benar membantu keterbacaan.

Anda juga dapat menggunakan cfg=my_transition untuk menggunakan transisi yang ditentukan pengguna, yang memungkinkan penulis aturan memiliki fleksibilitas yang besar dalam mengubah konfigurasi, dengan kelemahan membuat grafik build lebih besar dan kurang dapat dipahami.

Catatan: Secara historis, Bazel tidak memiliki konsep platform eksekusi, dan sebagai gantinya, semua tindakan build dianggap berjalan di mesin host. Roti Bazel sebelum 6.0 membuat "host" yang berbeda untuk mewakilinya. Jika Anda melihat referensi ke "{i>host<i}" dalam kode atau dokumentasi lama, itulah yang pengguna. Sebaiknya gunakan Bazel 6.0 atau yang lebih baru untuk menghindari overhead konseptual tambahan ini.

Fragmen konfigurasi

Aturan dapat mengakses fragmen konfigurasi seperti cpp, java, dan jvm. Namun, semua fragmen yang diperlukan harus dideklarasikan untuk menghindari error akses:

def _impl(ctx):
    # Using ctx.fragments.cpp leads to an error since it was not declared.
    x = ctx.fragments.java
    ...

my_rule = rule(
    implementation = _impl,
    fragments = ["java"],      # Required fragments of the target configuration
    host_fragments = ["java"], # Required fragments of the host configuration
    ...
)

Biasanya, jalur relatif file di hierarki runfiles sama dengan jalur relatif file itu dalam pohon sumber atau pohon {i>output<i} yang dihasilkan. Jika argumen ini harus berbeda karena alasan tertentu, Anda dapat menentukan argumen root_symlinks atau symlinks. root_symlinks adalah kamus yang memetakan jalur ke file, dengan jalur yang relatif terhadap root direktori runfile. Tujuan Kamus symlinks sama, tetapi jalur secara implisit diawali dengan nama ruang kerja utama (bukan nama repositori yang berisi target saat ini).

    ...
    runfiles = ctx.runfiles(
        root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
        symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
    )
    # Creates something like:
    # sometarget.runfiles/
    #     some/
    #         path/
    #             here.foo -> some_data_file2
    #     <workspace_name>/
    #         some/
    #             path/
    #                 here.bar -> some_data_file3

Jika symlinks atau root_symlinks digunakan, berhati-hatilah agar tidak memetakan dua file ke jalur yang sama di hierarki runfiles. Tindakan ini akan menyebabkan build gagal dengan kesalahan yang menjelaskan konflik. Untuk memperbaikinya, Anda perlu mengubah Argumen ctx.runfiles untuk menghilangkan tabrakan. Pemeriksaan ini akan dilakukan untuk semua target yang menggunakan aturan Anda, serta target apa pun yang bergantung pada target. Hal ini sangat berisiko jika alat Anda cenderung digunakan secara transitif oleh alat lain; nama symlink harus unik di seluruh runfile alat dan semua dependensinya.

Cakupan kode

Saat perintah coverage dijalankan, mungkin perlu menambahkan instrumentasi cakupan untuk target tertentu. Tujuan juga mengumpulkan daftar file sumber yang diinstrumentasikan. Subset dari target yang dianggap dikontrol oleh tanda --instrumentation_filter Target pengujian dikecualikan, kecuali jika --instrument_test_targets ditentukan.

Jika implementasi aturan menambahkan instrumentasi cakupan pada waktu build, implementasi tersebut perlu memperhitungkan hal itu dalam fungsi implementasinya. ctx.coverage_instrumented menampilkan nilai benar dalam mode cakupan jika sumber target harus diinstrumentasikan:

# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
  # Do something to turn on coverage for this compile action

Logika yang harus selalu aktif dalam mode cakupan (apakah sumber target yang secara khusus diinstrumentasikan atau tidak) dapat ditentukan berdasarkan ctx.configuration.coverage_enabled.

Jika aturan secara langsung menyertakan sumber dari dependensinya sebelum kompilasi (seperti file header), mungkin juga perlu mengaktifkan instrumentasi waktu kompilasi jika dependensi' sumber harus diinstrumentasikan:

# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
    (ctx.coverage_instrumented() or
     any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
    # Do something to turn on coverage for this compile action

Aturan juga harus menyediakan informasi tentang atribut mana yang relevan dengan cakupan dengan penyedia InstrumentedFilesInfo, yang dibuat menggunakan coverage_common.instrumented_files_info. Parameter dependency_attributes dari instrumented_files_info harus mencantumkan semua atribut dependensi runtime, termasuk dependensi kode seperti deps dan dependensi data seperti data. Parameter source_attributes harus mencantumkan atribut file sumber aturan jika instrumentasi cakupan mungkin ditambahkan:

def _example_library_impl(ctx):
    ...
    return [
        ...
        coverage_common.instrumented_files_info(
            ctx,
            dependency_attributes = ["deps", "data"],
            # Omitted if coverage is not supported for this rule:
            source_attributes = ["srcs", "hdrs"],
        )
        ...
    ]

Jika InstrumentedFilesInfo tidak ditampilkan, InstrumentedFilesInfo default akan dibuat dengan setiap atribut dependensi non-alat yang tidak menetapkan cfg ke "host" atau "exec" dalam skema atribut) di dependency_attributes. (Ini bukan perilaku yang ideal, karena menempatkan atribut seperti srcs di dependency_attributes, bukan source_attributes, tetapi menghindari perlunya konfigurasi cakupan eksplisit untuk semua aturan dalam rantai dependensi.)

Tindakan Validasi

Terkadang Anda perlu memvalidasi sesuatu tentang build, dan informasi yang diperlukan untuk melakukan validasi itu hanya tersedia dalam artefak (file sumber atau file yang dihasilkan). Karena informasi ini ada dalam artefak, aturan tidak dapat melakukan validasi ini pada waktu analisis karena aturan tidak dapat membaca file. Sebagai gantinya, tindakan harus melakukan validasi ini pada waktu eksekusi. Jika validasi gagal, tindakan akan gagal, sehingga build juga akan gagal.

Contoh validasi yang mungkin dijalankan adalah analisis statis, analisis lint, pemeriksaan dependensi dan konsistensi, dan pemeriksaan gaya.

Tindakan validasi juga dapat membantu meningkatkan performa build dengan memindahkan bagian tindakan yang tidak diperlukan untuk membangun artefak menjadi tindakan terpisah. Misalnya, jika satu tindakan yang melakukan kompilasi dan linting dapat dipisahkan menjadi tindakan kompilasi dan tindakan linting, tindakan linting dapat dijalankan sebagai tindakan validasi dan dijalankan secara paralel dengan tindakan lainnya.

"Tindakan validasi" ini sering kali tidak menghasilkan apa pun yang digunakan di tempat lain dalam build, karena mereka hanya perlu menegaskan hal-hal tentang input mereka. Ini menimbulkan masalah: Jika tindakan validasi tidak menghasilkan apa pun yang digunakan di tempat lain dalam build, bagaimana cara aturan menjalankan tindakan? Secara historis, pendekatannya adalah membuat tindakan validasi menghasilkan file kosong, dan menambahkan output tersebut secara artifisial ke input beberapa tindakan penting lainnya dalam build:

Ini berhasil, karena Bazel akan selalu menjalankan tindakan validasi saat kompilasi dijalankan, tetapi ini memiliki kelemahan yang signifikan:

  1. Tindakan validasi berada di jalur kritis build. Karena Bazel berpikir bahwa {i>output<i} kosong diperlukan untuk menjalankan tindakan kompilasi, ia akan menjalankan tindakan validasi terlebih dahulu, meskipun tindakan kompilasi akan mengabaikan input. Tindakan ini akan mengurangi paralelisme dan memperlambat build.

  2. Jika tindakan lain dalam build mungkin berjalan, bukan tindakan kompilasi, output kosong dari tindakan validasi juga harus ditambahkan ke tindakan tersebut (misalnya, output jar sumber java_library). Hal ini juga menjadi masalah jika tindakan baru yang mungkin berjalan, bukan tindakan kompilasi, ditambahkan nanti, dan output validasi kosong tidak sengaja dihapus.

Solusi untuk masalah ini adalah menggunakan Grup Output Validasi.

Grup Output Validasi

Grup Output Validasi adalah grup output yang dirancang untuk menyimpan {i>output<i} yang tidak digunakan dari tindakan validasi, sehingga mereka tidak perlu ditambahkan ke input tindakan lain.

Grup ini bersifat khusus karena output-nya selalu diminta, terlepas dari nilai tanda --output_groups, dan terlepas dari bagaimana target diperlukan (misalnya, di command line, sebagai dependensi, atau melalui output implisit target). Perlu diperhatikan bahwa caching dan inkrementalitas normal masih berlaku: jika input ke tindakan validasi belum berubah dan tindakan validasi sebelumnya berhasil, maka tindakan validasi tidak akan akan dijalankan.

Menggunakan grup {i>output<i} ini masih membutuhkan tindakan validasi yang menghasilkan beberapa file, bahkan yang kosong. Hal ini mungkin memerlukan penggabungan beberapa alat yang biasanya tidak membuat output sehingga file dibuat.

Tindakan validasi target tidak berjalan dalam tiga kasus:

  • Saat target bergantung pada alat
  • Ketika target dijadikan dependensi sebagai dependensi implisit (misalnya, atribut yang diawali dengan "_")
  • Saat target di-build di konfigurasi host atau exec.

Diasumsikan bahwa target ini memiliki build dan pengujian terpisah yang akan menemukan kegagalan validasi.

Menggunakan Grup Output Validasi

Grup Output Validasi diberi nama _validation dan digunakan seperti yang lainnya grup output:

def _rule_with_validation_impl(ctx):

  ctx.actions.write(ctx.outputs.main, "main output\n")

  ctx.actions.write(ctx.outputs.implicit, "implicit output\n")

  validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
  ctx.actions.run(
      outputs = [validation_output],
      executable = ctx.executable._validation_tool,
      arguments = [validation_output.path])

  return [
    DefaultInfo(files = depset([ctx.outputs.main])),
    OutputGroupInfo(_validation = depset([validation_output])),
  ]


rule_with_validation = rule(
  implementation = _rule_with_validation_impl,
  outputs = {
    "main": "%{name}.main",
    "implicit": "%{name}.implicit",
  },
  attrs = {
    "_validation_tool": attr.label(
        default = Label("//validation_actions:validation_tool"),
        executable = True,
        cfg = "exec"),
  }
)

Perhatikan bahwa file output validasi tidak ditambahkan ke DefaultInfo atau input ke tindakan lainnya. Tindakan validasi untuk target jenis aturan ini akan tetap berjalan jika target bergantung pada label, atau parameter output implisit bergantung secara langsung atau tidak langsung.

Biasanya, output tindakan validasi hanya masuk ke grup output validasi, dan tidak ditambahkan ke input tindakan lain, karena hal ini dapat mengalahkan peningkatan paralelisme. Namun, perlu diketahui bahwa Bazel saat ini memiliki pemeriksaan khusus untuk menegakkan ini. Oleh karena itu, Anda harus menguji bahwa output tindakan validasi tidak ditambahkan ke input tindakan apa pun dalam tes untuk aturan Starlark. Contoh:

load("@bazel_skylib//lib:unittest.bzl", "analysistest")

def _validation_outputs_test_impl(ctx):
  env = analysistest.begin(ctx)

  actions = analysistest.target_actions(env)
  target = analysistest.target_under_test(env)
  validation_outputs = target.output_groups._validation.to_list()
  for action in actions:
    for validation_output in validation_outputs:
      if validation_output in action.inputs.to_list():
        analysistest.fail(env,
            "%s is a validation action output, but is an input to action %s" % (
                validation_output, action))

  return analysistest.end(env)

validation_outputs_test = analysistest.make(_validation_outputs_test_impl)

Tanda Tindakan Validasi

Menjalankan tindakan validasi dikontrol oleh command line --run_validations , yang secara default disetel ke benar (true).

Fitur yang tidak digunakan lagi

Output yang tidak digunakan lagi

Ada dua cara yang tidak digunakan lagi untuk menggunakan output yang telah dideklarasikan sebelumnya:

  • Parameter outputs dari rule menentukan pemetaan antara nama atribut output dan template string untuk membuat label output yang telah dideklarasikan sebelumnya. Lebih memilih menggunakan output yang tidak dideklarasikan sebelumnya dan menambahkan output secara eksplisit ke DefaultInfo.files. Gunakan label sebagai input untuk aturan yang menggunakan output, bukan di label output.

  • Untuk aturan yang dapat dieksekusi, ctx.outputs.executable merujuk ke output yang dapat dieksekusi yang telah dideklarasikan sebelumnya dengan nama yang sama dengan target aturan. Sebaiknya deklarasikan output secara eksplisit, misalnya dengan ctx.actions.declare_file(ctx.label.name), dan pastikan perintah yang membuat file yang dapat dieksekusi menetapkan izinnya untuk mengizinkan eksekusi. Eksplisit teruskan output yang dapat dieksekusi ke parameter executable dari DefaultInfo.

Fitur runfile yang harus dihindari

Jenis ctx.runfiles dan runfiles memiliki kumpulan fitur yang kompleks, banyak di antaranya dipertahankan karena alasan lama. Rekomendasi berikut membantu mengurangi kerumitan:

  • Hindari penggunaan mode collect_data dan collect_default ctx.runfiles. Mode ini secara implisit mengumpulkan di tepi dependensi hardcode tertentu dengan cara yang membingungkan. Sebagai gantinya, tambahkan file menggunakan parameter files atau transitive_files dari ctx.runfiles, atau dengan menggabungkan runfile dari dependensi dengan runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles).

  • Hindari penggunaan data_runfiles dan default_runfiles dari konstruktor DefaultInfo. Tentukan DefaultInfo(runfiles = ...) sebagai gantinya. Perbedaan antara "default" dan "data" {i>runfile<i} dipertahankan untuk alasan lama. Misalnya, beberapa aturan menempatkan output defaultnya di data_runfiles, tetapi tidak di default_runfiles. Daripada menggunakan data_runfiles, aturan harus baik menyertakan output default dan juga menggabungkannya default_runfiles dari atribut yang menyediakan runfile (sering kali data).

  • Saat mengambil runfiles dari DefaultInfo (umumnya hanya untuk menggabungkan runfile antara aturan saat ini dan dependensinya), gunakan DefaultInfo.default_runfiles, bukan DefaultInfo.data_runfiles.

Bermigrasi dari penyedia lama

Sebelumnya, penyedia Bazel adalah kolom sederhana pada objek Target. Mereka diakses menggunakan operator titik, dan mereka dibuat dengan meletakkan kolom dalam struct yang ditampilkan oleh fungsi implementasi aturan.

Gaya ini tidak digunakan lagi dan tidak boleh digunakan dalam kode baru; lihat di bawah untuk informasi yang dapat membantu Anda bermigrasi. Mekanisme penyedia baru menghindari penggunaan nama bentrokan besar. API ini juga mendukung penyembunyian data, dengan mewajibkan kode apa pun yang mengakses instance penyedia untuk mengambilnya menggunakan simbol penyedia.

Untuk saat ini, penyedia lama masih didukung. Aturan dapat menampilkan penyedia lama dan modern sebagai berikut:

def _old_rule_impl(ctx):
  ...
  legacy_data = struct(x="foo", ...)
  modern_data = MyInfo(y="bar", ...)
  # When any legacy providers are returned, the top-level returned value is a
  # struct.
  return struct(
      # One key = value entry for each legacy provider.
      legacy_info = legacy_data,
      ...
      # Additional modern providers:
      providers = [modern_data, ...])

Jika dep adalah objek Target yang dihasilkan untuk instance aturan ini, penyedia dan kontennya dapat diambil sebagai dep.legacy_info.x dan dep[MyInfo].y.

Selain providers, struct yang ditampilkan juga dapat menggunakan beberapa kolom lain yang memiliki arti khusus (sehingga tidak membuat penyedia lama yang sesuai):

  • Kolom files, runfiles, data_runfiles, default_runfiles, dan executable sesuai dengan kolom bernama sama di DefaultInfo. Anda tidak diizinkan untuk menentukan salah satu kolom ini sekaligus menampilkan penyedia DefaultInfo.

  • Kolom output_groups mengambil nilai struct dan sesuai dengan OutputGroupInfo.

Di provides deklarasi aturan, dan di providers deklarasi dependensi penyedia lama diteruskan sebagai string dan penyedia modern yang diteruskan oleh simbol *Info-nya. Pastikan untuk mengubah dari string menjadi simbol saat bermigrasi. Untuk kumpulan aturan yang kompleks atau besar yang sulit diperbarui semua aturan secara menyeluruh, Anda mungkin lebih mudah jika mengikuti urutan langkah:

  1. Ubah aturan yang menghasilkan penyedia lama untuk menghasilkan penyedia lama dan modern, menggunakan sintaksis di atas. Untuk aturan yang mendeklarasikan bahwa mereka menampilkan penyedia lama, perbarui deklarasi tersebut untuk menyertakan penyedia lama dan modern.

  2. Ubah aturan yang menggunakan penyedia lama untuk menggunakan penyedia modern. Jika ada deklarasi atribut yang memerlukan penyedia lama, perbarui juga deklarasi tersebut agar memerlukan penyedia modern. Secara opsional, Anda dapat menyisipkan pekerjaan ini dengan langkah 1, dengan meminta konsumen menyetujui/mewajibkan provider: Menguji keberadaan penyedia lama menggunakan hasattr(target, 'foo'), atau penyedia baru yang menggunakan FooInfo in target.

  3. Hapus sepenuhnya penyedia lama dari semua aturan.