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

รายงานปัญหา ดูแหล่งที่มา รุ่น Nightly · 8.0 7.4 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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 ภายนอกที่รองรับ

คุณใช้ทรัพยากรภายนอกประเภทพื้นฐานได้ 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 และทำให้อาร์ติแฟกต์ดังกล่าวพร้อมใช้งานเป็น Dependency ของ Java

กำลังดึงข้อมูล Dependency

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

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

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

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

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

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

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

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

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

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

การรองรับ IPv6

ในเครื่องที่ใช้ IPv6 เท่านั้น Bazel จะดาวน์โหลดข้อมูลที่ต้องพึ่งพาได้โดยไม่ต้องเปลี่ยนแปลง อย่างไรก็ตาม ในเครื่องแบบ Dual Stack IPv4/IPv6 นั้น 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 Flag เครื่องมือด้วย เช่น ใส่บรรทัดต่อไปนี้ในไฟล์ .bazelrc

    build --jvmopt=-Djava.net.preferIPv6Addresses

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

Dependency แบบทรานซิทีฟ

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

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

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