การทำงานกับทรัพยากร 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 ภายนอกประเภทพื้นฐานบางประเภทได้ ดังนี้

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

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

ตัวอย่างเช่น สมมติว่าคุณกำลังทำงานในโปรเจ็กต์ my-project/ และต้องการขึ้นอยู่กับเป้าหมายจากโปรเจ็กต์ของเพื่อนร่วมงาน coworkers-project/ ทั้ง 2 โปรเจ็กต์ใช้ Bazel ดังนั้นคุณจึงเพิ่มโปรเจ็กต์ของเพื่อนร่วมงานเป็นทรัพยากร Dependency ภายนอก แล้วใช้เป้าหมายใดก็ได้ที่เพื่อนร่วมงานกำหนดไว้จากไฟล์ 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 และทำให้พร้อมใช้งานเป็นทรัพยากร Dependency ของ Java

การดึงทรัพยากร Dependency

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

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

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

myproject/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 ไม่ได้ แต่จะขัดแย้งกันเนื่องจากมีชื่อเดียวกัน หากต้องการประกาศทรัพยากร Dependency ทั้ง 2 รายการ ให้อัปเดต 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 นั้นด้วยชื่อต่างๆ กัน คุณสามารถรวมทรัพยากร 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 แบบ Dual-stack 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

การสร้างแบบออฟไลน์

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

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

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

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

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

กฎที่เก็บ

โดยทั่วไปกฎที่เก็บควรมีหน้าที่รับผิดชอบในเรื่องต่อไปนี้

  • การตรวจหาการตั้งค่าระบบและการเขียนการตั้งค่าเหล่านั้นลงในไฟล์
  • การค้นหาทรัพยากรที่อื่นในระบบ
  • การดาวน์โหลดทรัพยากรจาก 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() ดู "พิจารณานำ bind" เพื่ออ่านการสนทนาอย่างละเอียดเกี่ยวกับปัญหาและทางเลือกอื่นๆ