การทำงานกับทรัพยากร Dependency ภายนอก

Bazel จะขึ้นอยู่กับเป้าหมายจากโปรเจ็กต์อื่นๆ ทรัพยากร Dependency จากโปรเจ็กต์อื่นๆ เหล่านี้เรียกว่าทรัพยากร Dependency ภายนอก

ไฟล์ WORKSPACE (หรือไฟล์ WORKSPACE.bazel) ในไดเรกทอรีพื้นที่ทำงานจะบอก Bazel ถึงวิธีหาแหล่งที่มาของโปรเจ็กต์อื่นๆ โปรเจ็กต์อื่นๆ เหล่านี้จะมีไฟล์ BUILD อย่างน้อย 1 ไฟล์ที่มีเป้าหมายของตัวเองได้ ไฟล์ BUILD รายการภายในโปรเจ็กต์หลักอาจขึ้นอยู่กับเป้าหมายภายนอกเหล่านี้โดยใช้ชื่อไฟล์จากไฟล์ WORKSPACE

ตัวอย่างเช่น สมมติว่าในระบบมี 2 โปรเจ็กต์ ดังนี้

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

หาก project1 ต้องการที่จะใช้เป้าหมาย :foo ซึ่งกำหนดไว้ใน /home/user/project2/BUILD ก็อาจระบุให้พบที่เก็บชื่อ project2 ที่ /home/user/project2 เป้าหมายใน /home/user/project1/BUILD ก็อาจขึ้นอยู่กับ @project2//:foo

ไฟล์ WORKSPACE ช่วยให้ผู้ใช้เข้าถึงเป้าหมายจากส่วนอื่นของระบบไฟล์หรือดาวน์โหลดจากอินเทอร์เน็ตได้ โดยใช้ไวยากรณ์เดียวกันกับไฟล์ BUILD แต่จะอนุญาตชุดกฎที่แตกต่างกันที่เรียกว่ากฎที่เก็บ (บางครั้งเรียกว่ากฎพื้นที่ทำงาน) Bazel มาพร้อมกับกฎที่เก็บในตัว 2-3 ข้อและชุดกฎที่เก็บ Starlark ที่ฝังไว้ นอกจากนี้ ผู้ใช้ยังเขียนกฎที่เก็บที่กำหนดเองเพื่อให้มีลักษณะการทำงานที่ซับซ้อนขึ้นได้ด้วย

ประเภทของทรัพยากร Dependency ภายนอกที่รองรับ

คุณใช้ทรัพยากร Dependency ภายนอกได้ 2-3 ประเภท

ขึ้นอยู่กับโปรเจ็กต์ Bazel อื่นๆ

หากต้องการใช้เป้าหมายจากโปรเจ็กต์ Bazel ที่ 2 ให้ใช้ local_repository, git_repository หรือ http_archive เพื่อเชื่อมโยงโปรเจ็กต์จากระบบไฟล์ในเครื่อง อ้างอิงที่เก็บ Git หรือดาวน์โหลดโปรเจ็กต์ (ตามลำดับ)

ตัวอย่างเช่น สมมติว่าคุณทำงานในโปรเจ็กต์ my-project/ และคุณต้องการอิงตามเป้าหมายจากโปรเจ็กต์ coworkers-project/ ของเพื่อนร่วมงาน ทั้ง 2 โปรเจ็กต์ใช้ Bazel คุณจึงสามารถเพิ่มโปรเจ็กต์ของเพื่อนร่วมงานเป็นแบบพึ่งพิงภายนอกได้ แล้วใช้เป้าหมายที่เพื่อนร่วมงานได้กำหนดไว้จากไฟล์ BUILD ของคุณเอง คุณต้องเพิ่มรายการต่อไปนี้ลงใน my_project/WORKSPACE

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

หากเพื่อนร่วมงานมีเป้าหมาย //foo:bar โปรเจ็กต์ของคุณจะเรียกโปรเจ็กต์ว่า @coworkers_project//foo:bar ได้ ชื่อโปรเจ็กต์ภายนอกต้องเป็นชื่อพื้นที่ทำงานที่ถูกต้อง

ขึ้นอยู่กับโปรเจ็กต์ที่ไม่ใช่แบบ Bazel

กฎที่ขึ้นต้นด้วย new_ เช่น new_local_repository จะช่วยให้คุณสร้างเป้าหมายจากโปรเจ็กต์ที่ไม่ได้ใช้ Bazel ได้

ตัวอย่างเช่น สมมติว่าคุณกำลังทำงานในโปรเจ็กต์ my-project/ และคุณต้องการขึ้นอยู่กับโปรเจ็กต์ของเพื่อนร่วมงานชื่อ coworkers-project/ โปรเจ็กต์ของเพื่อนร่วมงานใช้ make ในการสร้าง แต่คุณต้องการใช้ไฟล์ .so ไฟล์ใดไฟล์หนึ่งที่โปรแกรมสร้าง ในการดำเนินการดังกล่าว ให้เพิ่มรายการต่อไปนี้ลงใน my_project/WORKSPACE

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

build_file ระบุไฟล์ BUILD เพื่อวางซ้อนบนโปรเจ็กต์ที่มีอยู่ เช่น

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

จากนั้นคุณจะใช้ @coworkers_project//:some-lib จากไฟล์ BUILD ของโปรเจ็กต์ได้

ขึ้นอยู่กับแพ็กเกจภายนอก

อาร์ติแฟกต์และที่เก็บของ Maven

ใช้ชุดกฎ rules_jvm_external เพื่อดาวน์โหลดอาร์ติแฟกต์จากที่เก็บ Maven และทำให้ใช้งานได้เป็นแบบอ้างอิงของ Java

กำลังดึงข้อมูลการอ้างอิง

โดยค่าเริ่มต้น ระบบจะดึงข้อมูลทรัพยากร Dependency ภายนอกตามต้องการในช่วง bazel build หากต้องการดึงข้อมูลทรัพยากร Dependency ที่จำเป็นสำหรับชุดเป้าหมายที่เฉพาะเจาะจงล่วงหน้า ให้ใช้ bazel fetch หากต้องการดึงข้อมูลทรัพยากร Dependency ภายนอกทั้งหมดอย่างไม่มีเงื่อนไข ให้ใช้ bazel sync เนื่องจากที่เก็บที่ดึงข้อมูลจัดเก็บไว้ในฐานเอาต์พุต การดึงข้อมูลจะเกิดขึ้นต่อพื้นที่ทำงาน

การซ่อนทรัพยากร Dependency

หากเป็นไปได้ เราขอแนะนำให้มีนโยบายเวอร์ชันเดียวในโปรเจ็กต์ ซึ่งจำเป็นสำหรับทรัพยากร Dependency ที่คุณคอมไพล์และนำมารวมกับไบนารีสุดท้าย แต่ในกรณีที่ไม่จริง ก็อาจซ่อนทรัพยากร Dependency ได้ ลองพิจารณาสถานการณ์ต่อไปนี้

โปรเจ็กต์ของฉัน/WorkSPACE

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 = "..."
)

ทรัพยากร Dependency ทั้ง A และ B จะขึ้นอยู่กับ testrunner แต่จะขึ้นอยู่กับ testrunner เวอร์ชันอื่น ไม่มีเหตุผลที่นักวิ่งทดสอบเหล่านี้จะไม่อยู่ร่วมกันภายใน myproject อย่างสงบ แต่ทั้ง 2 อย่างนี้จะชนกันเองเพราะมีชื่อเดียวกัน หากต้องการประกาศทรัพยากร Dependency ทั้งคู่ ให้อัปเดต 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"}
)

กลไกนี้สามารถใช้ในการผูกเพชรได้ด้วย เช่น ถ้า A และ B มีทรัพยากร Dependency เดียวกัน แต่เรียกโดยใช้ชื่อต่างกัน ทรัพยากร Dependency เหล่านั้นจะเข้าร่วมใน myproject/WORKSPACE ได้

การลบล้างที่เก็บจากบรรทัดคำสั่ง

หากต้องการลบล้างที่เก็บที่ประกาศด้วยที่เก็บภายในจากบรรทัดคำสั่ง ให้ใช้แฟล็ก --override_repository การใช้แฟล็กนี้จะเปลี่ยนแปลงเนื้อหาของที่เก็บภายนอกโดยไม่ต้องเปลี่ยนซอร์สโค้ด

เช่น หากต้องการลบล้าง @foo ไปยังไดเรกทอรีในเครื่อง /path/to/local/foo ให้ส่งแฟล็ก --override_repository=foo=/path/to/local/foo

ตัวอย่างการใช้งานมีดังนี้

  • การแก้ปัญหา เช่น คุณอาจลบล้างที่เก็บ http_archive เป็นไดเรกทอรีในเครื่องเพื่อให้ทำการเปลี่ยนแปลงได้ง่ายขึ้น
  • การขายหุ้น หากคุณอยู่ในสภาพแวดล้อมที่เรียกใช้เครือข่ายไม่ได้ ให้ลบล้างกฎที่เก็บตามเครือข่ายเพื่อชี้ไปยังไดเรกทอรีในเครื่องแทน

การใช้พร็อกซี

Bazel จะเลือกที่อยู่พร็อกซีจากตัวแปรสภาพแวดล้อม HTTPS_PROXY และ HTTP_PROXY และใช้ที่อยู่เหล่านี้ในการดาวน์โหลดไฟล์ HTTP/HTTPS (หากระบุ)

การสนับสนุนสำหรับ IPv6

ในเครื่องที่ใช้ IPv6 เท่านั้น Bazel จะดาวน์โหลดทรัพยากร Dependency ได้โดยไม่มีการเปลี่ยนแปลง อย่างไรก็ตาม ในเครื่อง IPv4/IPv6 แบบ 2 สแต็ก Bazel จะใช้รูปแบบเดียวกันกับ Java คือหากเปิดใช้ IPv4 ระบบจะเลือกใช้ IPv4 ในบางสถานการณ์ เช่น เมื่อเครือข่าย IPv4 แก้ไข/เข้าถึงที่อยู่ภายนอกไม่ได้ อาจทำให้เกิดข้อยกเว้น Network unreachable รายการและสร้างความล้มเหลว ในกรณีเหล่านี้ คุณลบล้างลักษณะการทำงานของ Bazel เพื่อใช้ IPv6 ได้ด้วยการใช้พร็อพเพอร์ตี้ระบบ java.net.preferIPv6Addresses=true กล่าวอย่างเจาะจงคือ

  • ใช้ --host_jvm_args=-Djava.net.preferIPv6Addresses=true ตัวเลือกการเริ่มต้นใช้งาน ตัวอย่างเช่น โดยการเพิ่มบรรทัดต่อไปนี้ในไฟล์ .bazelrc

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

  • หากคุณใช้เป้าหมายบิลด์ของ Java ที่ต้องเชื่อมต่อกับอินเทอร์เน็ตด้วย (บางครั้งการทดสอบการผสานรวมก็จําเป็น) ให้ใช้ --jvmopt=-Djava.net.preferIPv6Addresses=true เครื่องมือแฟล็ก ด้วย ตัวอย่างเช่น ใส่บรรทัดต่อไปนี้ในไฟล์ .bazelrc

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • หากใช้ rules_jvm_external สำหรับความละเอียดของเวอร์ชันทรัพยากร Dependency แล้ว ให้เพิ่ม -Djava.net.preferIPv6Addresses=true ไปยังตัวแปรสภาพแวดล้อม COURSIER_OPTS เพื่อมอบตัวเลือก JVM สำหรับ Coursier ด้วย

ทรัพยากร Dependency ชั่วคราว

Bazel จะอ่านเฉพาะทรัพยากร Dependency ที่ระบุไว้ในไฟล์ WORKSPACE เท่านั้น หากโปรเจ็กต์ (A) ขึ้นอยู่กับโปรเจ็กต์อื่น (B) ซึ่งแสดงรายการทรัพยากร Dependency ในโปรเจ็กต์ที่ 3 (C) ในไฟล์ WORKSPACE ของโปรเจ็กต์ คุณจะต้องเพิ่มทั้ง B และ C ลงในไฟล์ WORKSPACE ของโปรเจ็กต์ ข้อกำหนดนี้สามารถทำให้ไฟล์ WORKSPACE มีขนาดบอลลูนได้ แต่จะจำกัดโอกาสในการมีไลบรารีหนึ่งให้รวม C ในเวอร์ชัน 1.0 และอีกไลบรารีหนึ่งมี C ที่เวอร์ชัน 2.0

การแคชทรัพยากร Dependency ภายนอก

โดยค่าเริ่มต้น Bazel จะดาวน์โหลดทรัพยากร Dependency ภายนอกอีกครั้งก็ต่อเมื่อมีการเปลี่ยนแปลงคำจำกัดความ โดยBazel จะพิจารณาการเปลี่ยนแปลงไฟล์ที่นำมาอ้างอิงในคำจำกัดความ (เช่น แพตช์หรือไฟล์ BUILD) ด้วย

หากต้องการบังคับดาวน์โหลดใหม่ ให้ใช้ bazel sync

เลย์เอาต์

ระบบจะดาวน์โหลดทรัพยากร Dependency ภายนอกทั้งหมดไปยังไดเรกทอรีภายใต้ไดเรกทอรีย่อย external ในฐานเอาต์พุต ในกรณีที่เป็นที่เก็บในเครื่อง ระบบจะสร้างลิงก์สัญลักษณ์ขึ้นในที่ดังกล่าวแทนการสร้างไดเรกทอรีใหม่ คุณจะดูไดเรกทอรี external ได้โดยเรียกใช้คำสั่งต่อไปนี้

ls $(bazel info output_base)/external

โปรดทราบว่าการเรียกใช้ bazel clean จะไม่ลบไดเรกทอรีภายนอกออก หากต้องการนำอาร์ติแฟกต์ภายนอกทั้งหมดออก ให้ใช้ bazel clean --expunge

บิลด์แบบออฟไลน์

ในบางครั้ง อาจจำเป็นหรือจำเป็นต้องเรียกใช้บิลด์ในรูปแบบออฟไลน์ สำหรับกรณีการใช้งานที่เรียบง่าย เช่น ขณะเดินทางบนเครื่องบิน การดึงข้อมูลล่วงหน้าที่เก็บที่ต้องการด้วย bazel fetch หรือ bazel sync ก็เพียงพอแล้ว นอกจากนี้ การใช้ตัวเลือก --nofetch จะเป็นการปิดใช้การดึงข้อมูลที่เก็บเพิ่มเติมในระหว่างบิลด์

สำหรับบิลด์แบบออฟไลน์ที่แท้จริง ซึ่งการมอบไฟล์ที่จำเป็นต้องทำโดยเอนทิตีที่แตกต่างจาก Bayel ส่วน Bazel จะรองรับตัวเลือก --distdir เมื่อใดก็ตามที่กฎที่เก็บขอให้ Bazel ดึงข้อมูลไฟล์ผ่าน ctx.download หรือ ctx.download_and_extract และให้ผลรวมแฮชของไฟล์ที่ต้องการ อันดับแรก Bazel จะดูไดเรกทอรีที่ระบุโดยตัวเลือกนั้นสำหรับไฟล์ที่ตรงกับชื่อพื้นฐานของ URL แรกที่ระบุ และใช้สำเนาในเครื่องนั้นหากแฮชตรงกัน

Bazel ใช้เทคนิคนี้เพื่อเปิดระบบออฟไลน์จากอาร์ติแฟกต์การจัดจำหน่าย โดยรวบรวมการพึ่งพาภายนอกทั้งหมดที่จำเป็นในdistdir_tarภายใน

อย่างไรก็ตาม bazel จะช่วยให้ใช้คำสั่งที่กำหนดเองในกฎที่เก็บได้ โดยไม่ต้องทราบว่าระบบเรียกใช้เครือข่ายหรือไม่ ดังนั้น Bazel จึงไม่มีตัวเลือกในการบังคับให้บิลด์ออฟไลน์โดยสมบูรณ์ ดังนั้น การทดสอบว่าบิลด์ทำงานแบบออฟไลน์ได้อย่างถูกต้องหรือไม่นั้น จำเป็นต้องมีการบล็อกเครือข่ายจากภายนอก ดังเช่นที่ Bazel ทำในการทดสอบ Bootstrapped

แนวทางปฏิบัติแนะนำ

กฎที่เก็บ

โดยทั่วไป กฎที่เก็บควรประกอบด้วยส่วนต่อไปนี้

  • การตรวจหาการตั้งค่าระบบและการเขียนไฟล์ลงในไฟล์
  • ค้นหาแหล่งข้อมูลจากที่อื่นในระบบ
  • กำลังดาวน์โหลดทรัพยากรจาก URL
  • การสร้างหรือเชื่อมโยงไฟล์ BUILD เข้ากับไดเรกทอรีที่เก็บภายนอก

หลีกเลี่ยงการใช้ repository_ctx.execute เมื่อเป็นไปได้ เช่น เมื่อใช้ไลบรารี C++ ที่ไม่ใช่แบบ Bazel ซึ่งมีบิลด์ที่ใช้ Make ก็ควรใช้ repository_ctx.download() แล้วเขียนไฟล์ BUILD ที่ใช้สร้างบิลด์นั้น แทนการเรียกใช้ ctx.execute(["make"])

ต้องการใช้ http_archive กับ git_repository และ new_git_repository เหตุผลมีดังนี้

  • กฎที่เก็บ Git ขึ้นอยู่กับ git(1) ของระบบ ขณะที่เครื่องมือดาวน์โหลด HTTP จะสร้างขึ้นใน Bazel และไม่มีทรัพยากร Dependency ของระบบ
  • http_archive รองรับรายการ urls เป็นมิเรอร์ และ git_repository รองรับ remote เพียงรายการเดียวเท่านั้น
  • http_archive ใช้งานได้กับแคชของที่เก็บ แต่ใช้ไม่ได้กับ git_repository ดู #5116 สำหรับข้อมูลเพิ่มเติม

อย่าใช้ bind() ดู "พิจารณานำการเชื่อมโยงออก" ที่มีการพูดถึงปัญหาและทางเลือกอื่นเป็นเวลานาน