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

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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 มาพร้อมกับกฎของที่เก็บในตัวและชุดกฎของที่เก็บ Starlark ที่ฝังไว้ นอกจากนี้ ผู้ใช้ยังเขียนกฎที่เก็บข้อมูลที่กำหนดเองเพื่อให้ได้ลักษณะการทำงานที่ซับซ้อนยิ่งขึ้นได้ด้วย

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

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

ขึ้นอยู่กับโปรเจ็กต์ 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/ เป็นทรัพยากร Dependency โปรเจ็กต์ของเพื่อนร่วมงานใช้ 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

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

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

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

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

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

กรณีการใช้งานบางส่วนมีดังนี้

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

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

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 เช่น เพื่อการแก้ปัญหาเวอร์ชันการขึ้นต่อกัน ให้เพิ่ม -Djava.net.preferIPv6Addresses=true ลงในตัวแปรสภาพแวดล้อม COURSIER_OPTS เพื่อระบุตัวเลือก JVM สำหรับ Coursier ด้วย

ทรัพยากร Dependency แบบทรานซิทีฟ

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

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

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

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

เลย์เอาต์

ระบบจะดาวน์โหลดทรัพยากร Dependency ภายนอกทั้งหมดไปยังไดเรกทอรีภายใต้ไดเรกทอรีย่อย external ในเอาต์พุตฐาน ในกรณีของที่เก็บข้อมูลในเครื่อง ระบบจะสร้าง Symlink ที่นั่นแทนที่จะสร้างไดเรกทอรีใหม่ คุณดูไดเรกทอรี 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 เองก็ใช้เทคนิคนี้เพื่อเริ่มต้นแบบออฟไลน์จากอาร์ติแฟกต์การจัดจำหน่าย โดยจะรวบรวมการอ้างอิงภายนอกที่จำเป็นทั้งหมด ไว้ใน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 และไม่มีการขึ้นต่อกันของระบบ
  • http_archive รองรับรายการ urls เป็นมิเรอร์ และ git_repository รองรับเฉพาะ remote รายการเดียว
  • http_archive ใช้ได้กับแคชที่เก็บ แต่ใช้ไม่ได้กับ git_repository ดูข้อมูลเพิ่มเติมได้ที่ #5116

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