เป้าหมาย A
ขึ้นอยู่กับเป้าหมาย B
หาก A
ต้องใช้ B
ในเวลาที่สร้างหรือเวลาดำเนินการ ความสัมพันธ์ขึ้นอยู่กับจะทําให้เกิด Directed Acyclic Graph (DAG) เหนือเป้าหมาย และเรียกกันว่ากราฟ Dependency
ทรัพยากร Dependency โดยตรงของเป้าหมายคือเป้าหมายอื่นๆ ที่เข้าถึงได้ด้วยเส้นทางที่มีความยาว 1 ในกราฟทรัพยากร Dependency Dependency แบบทรานซิทีฟของเป้าหมายคือเป้าหมายที่เป้าหมายนั้นต้องอาศัยผ่านเส้นทางที่มีความยาวเท่าใดก็ได้ในกราฟ
อันที่จริงแล้ว ในบริบทของบิลด์จะมีกราฟทรัพยากร Dependency 2 รายการ ได้แก่ กราฟทรัพยากร Dependency จริงและกราฟทรัพยากร Dependency ที่ประกาศ ส่วนใหญ่แล้ว กราฟ 2 รูปแบบนี้มีความคล้ายคลึงกันมากจนไม่จำเป็นต้องแยกความแตกต่าง แต่ความแตกต่างนี้มีประโยชน์สำหรับการสนทนาด้านล่าง
ทรัพยากร Dependency จริงและที่ประกาศ
เป้าหมาย X
ขึ้นอยู่กับเป้าหมาย Y
จริงๆ หาก Y
ต้องมีอยู่ สร้างขึ้น และอัปเดตแล้วเพื่อให้สร้าง X
ได้อย่างถูกต้อง สร้างอาจหมายถึงสร้าง ประมวลผล คอมไพล์ ลิงก์ เก็บถาวร บีบอัด ดำเนินการ หรืองานประเภทอื่นๆ ที่เกิดขึ้นเป็นประจำระหว่างการสร้าง
X
เป้าหมายมีทรัพยากร Dependency ที่ประกาศใน Y
เป้าหมาย หากมีขีดจำกัดทรัพยากร Dependency จาก X
ไปยัง Y
ในแพ็กเกจของ X
เพื่อให้ได้บิลด์ที่ถูกต้อง กราฟของทรัพยากร Dependency จริง A ต้องเป็นกราฟย่อยของกราฟทรัพยากร Dependency ที่ประกาศ D กล่าวคือ โหนด x --> y
ที่เชื่อมต่อโดยตรงทุกคู่ใน A จะต้องเชื่อมต่อโดยตรงใน D ด้วย กล่าวได้ว่า D เป็นการประมาณที่มากเกินไปของ A
ผู้เขียนไฟล์ BUILD
ต้องประกาศการพึ่งพาโดยตรงจริงทั้งหมดสำหรับกฎทุกข้อต่อระบบบิลด์อย่างชัดเจน และห้ามประกาศเพิ่มเติม
การไม่ปฏิบัติตามหลักการนี้จะทำให้เกิดลักษณะการทำงานที่ไม่ระบุ: บิลด์อาจไม่สำเร็จ แต่ที่แย่กว่านั้นคือบิลด์อาจขึ้นอยู่กับการดำเนินการก่อนหน้าบางอย่าง หรือขึ้นอยู่กับข้อกำหนดที่พึ่งพาซึ่งประกาศไว้ซึ่งเป้าหมายมี Bazel จะตรวจหาการขึ้นต่อกันที่ขาดหายไปและรายงานข้อผิดพลาด แต่การตรวจสอบนี้อาจไม่เสร็จสมบูรณ์ในทุกกรณี
คุณไม่จำเป็นต้อง (และไม่ควร) พยายามแสดงรายการทั้งหมดที่นำเข้าโดยอ้อม แม้ว่า A
จะต้องการ ณ เวลาที่ดำเนินการก็ตาม
ในระหว่างการสร้างเป้าหมาย X
เครื่องมือสร้างจะตรวจสอบการปิดทั้งหมดแบบทรานซิทีฟของ X
เพื่อให้แน่ใจว่าการเปลี่ยนแปลงในเป้าหมายเหล่านั้นจะแสดงในผลลัพธ์สุดท้าย โดยสร้างไฟล์กลางขึ้นมาใหม่ตามความจำเป็น
ลักษณะของข้อกําหนดซึ่งกันและกันทําให้เกิดข้อผิดพลาดที่พบบ่อย บางครั้ง โค้ดในไฟล์หนึ่งอาจใช้โค้ดที่ได้จากข้อกําหนด โดยอ้อม ซึ่งเป็นขอบแบบเปลี่ยนผ่านแต่ไม่ใช่ขอบโดยตรงในกราฟข้อกําหนดที่ประกาศ Dependency ทางอ้อมจะไม่ปรากฏในไฟล์ BUILD
เนื่องจากกฎไม่ได้ขึ้นอยู่กับผู้ให้บริการโดยตรง จึงไม่มีวิธีติดตามการเปลี่ยนแปลง ดังที่แสดงในไทม์ไลน์ตัวอย่างต่อไปนี้
1. ทรัพยากร Dependency ที่ประกาศตรงกับทรัพยากร Dependency จริง
ตอนแรกทุกอย่างทำงานได้ตามปกติ โค้ดในแพ็กเกจ a
ใช้โค้ดในแพ็กเกจ b
โค้ดในแพ็กเกจ b
ใช้โค้ดในแพ็กเกจ c
ดังนั้น a
จึงขึ้นอยู่กับ c
a/BUILD |
b/BUILD |
---|---|
rule( name = "a", srcs = "a.in", deps = "//b:b", ) |
rule( name = "b", srcs = "b.in", deps = "//c:c", ) |
a / a.in |
b / b.in |
import b; b.foo(); |
import c; function foo() { c.bar(); } |
ทรัพยากร Dependency ที่ประกาศมีค่าใกล้เคียงของการอ้างอิงจริงมากเกินไป ทุกอย่างเรียบร้อยดี
2. การเพิ่มการพึ่งพาที่ไม่ได้ประกาศ
เกิดอันตรายแฝงเมื่อมีคนเพิ่มโค้ดไปยัง a
ที่สร้างทรัพยากร Dependency จริง โดยตรงใน c
แต่ลืมประกาศในไฟล์บิลด์ a/BUILD
a / a.in |
|
---|---|
import b; import c; b.foo(); c.garply(); |
|
Dependency ที่ประกาศจะไม่ประมาณค่า Dependency จริงมากเกินไปอีกต่อไป
การดำเนินการนี้อาจสร้างได้สําเร็จ เนื่องจาก Closure แบบทรานซิทีฟของกราฟ 2 รายการเท่ากัน แต่ซ่อนปัญหาไว้: a
มีการพึ่งพา c
จริงแต่ไม่ได้ประกาศ
3. ความคลาดเคลื่อนระหว่างกราฟความเกี่ยวข้องที่ประกาศไว้กับกราฟความเกี่ยวข้องจริง
อันตรายจะปรากฏขึ้นเมื่อมีคนรีแฟกทอริง b
เพื่อไม่ให้ขึ้นอยู่กับ c
อีกต่อไป ซึ่งทำให้ a
ใช้งานไม่ได้โดยไม่ตั้งใจ
b/BUILD |
|
---|---|
rule( name = "b", srcs = "b.in", deps = "//d:d", ) |
|
b / b.in |
|
import d; function foo() { d.baz(); } |
|
ตอนนี้กราฟทรัพยากร Dependency ที่ประกาศไว้จะประมาณทรัพยากร Dependency จริงต่ำเกินไป แม้ว่าจะปิดแบบทรานซิทีฟแล้วก็ตาม บิลด์มีแนวโน้มที่จะดำเนินการไม่สำเร็จ
ปัญหานี้อาจหลีกเลี่ยงได้ด้วยการยืนยันว่ามีการประกาศการพึ่งพาจริงจาก a
ไปยัง c
ที่ระบุไว้ในขั้นตอนที่ 2 อย่างถูกต้องในไฟล์ BUILD
ประเภทของทรัพยากร Dependency
กฎการสร้างส่วนใหญ่มีแอตทริบิวต์ 3 แบบสำหรับการระบุทรัพยากร Dependency ทั่วไปประเภทต่างๆ ได้แก่ srcs
, deps
และ data
โปรดดูคำอธิบายด้านล่าง ดูรายละเอียดเพิ่มเติมได้ที่แอตทริบิวต์ที่ใช้ร่วมกันสำหรับกฎทั้งหมด
กฎจํานวนมากยังมีแอตทริบิวต์เพิ่มเติมสําหรับประเภทของข้อกําหนดเฉพาะของกฎ เช่น compiler
หรือ resources
โปรดดูรายละเอียดในสารานุกรมเกี่ยวกับการสร้าง
srcs
Dependency
ไฟล์ที่กฎหรือกฎที่ใช้เอาต์พุตไฟล์ต้นฉบับใช้โดยตรง
deps
Dependency
กฎที่ชี้ไปยังโมดูลที่คอมไพล์แยกต่างหากซึ่งให้ไฟล์ส่วนหัว สัญลักษณ์ ไลบรารี ข้อมูล ฯลฯ
data
Dependency
เป้าหมายของบิลด์อาจต้องมีไฟล์ข้อมูลบางไฟล์เพื่อให้ทํางานได้อย่างถูกต้อง ไฟล์ข้อมูลเหล่านี้ไม่ใช่ซอร์สโค้ด จึงไม่ส่งผลต่อวิธีสร้างเป้าหมาย เช่น การทดสอบหน่วยอาจเปรียบเทียบเอาต์พุตของฟังก์ชันกับเนื้อหาของไฟล์ เมื่อสร้างการทดสอบหน่วย คุณไม่จำเป็นต้องใช้ไฟล์ แต่ต้องใช้เมื่อเรียกใช้การทดสอบ เช่นเดียวกันกับเครื่องมือที่เปิดขึ้นมาในระหว่างการดำเนินการ
ระบบบิลด์จะทำการทดสอบในไดเรกทอรีแยกต่างหากที่มีเฉพาะไฟล์ที่แสดงเป็น data
เท่านั้น ดังนั้น หากไบนารี/ไลบรารี/การทดสอบต้องใช้ไฟล์บางไฟล์เพื่อทำงาน ให้ระบุไฟล์เหล่านั้น (หรือกฎการสร้างที่มีไฟล์เหล่านั้น) ใน data
เช่น
# I need a config file from a directory named env:
java_binary(
name = "setenv",
...
data = [":env/default_env.txt"],
)
# I need test data from another directory
sh_test(
name = "regtest",
srcs = ["regtest.sh"],
data = [
"//data:file1.txt",
"//data:file2.txt",
...
],
)
ไฟล์เหล่านี้พร้อมใช้งานโดยใช้เส้นทางที่เกี่ยวข้อง path/to/data/file
ในการทดสอบ คุณสามารถอ้างอิงไฟล์เหล่านี้ได้โดยรวมเส้นทางของไดเรกทอรีต้นทางของการทดสอบเข้ากับเส้นทางแบบสัมพัทธ์กับ Workspace เช่น ${TEST_SRCDIR}/workspace/path/to/data/file
การใช้ป้ายกำกับเพื่ออ้างอิงไดเรกทอรี
เมื่อดูไฟล์ BUILD
คุณอาจสังเกตเห็นว่าป้ายกำกับ data
บางรายการอ้างอิงถึงไดเรกทอรี ป้ายกำกับเหล่านี้ลงท้ายด้วย /.
หรือ /
ดังตัวอย่างต่อไปนี้ ซึ่งคุณไม่ควรใช้
ไม่แนะนำ —
data = ["//data/regression:unittest/."]
ไม่แนะนำ —
data = ["testdata/."]
ไม่แนะนำ —
data = ["testdata/"]
วิธีนี้ดูจะสะดวกดี โดยเฉพาะสำหรับการทดสอบ เนื่องจากช่วยให้การทดสอบใช้ไฟล์ข้อมูลทั้งหมดในไดเรกทอรีได้
แต่พยายามอย่าทำ ระบบของบิลด์ต้องตระหนักถึงชุดไฟล์ทั้งหมดที่เป็นอินพุตของบิลด์ (หรือการทดสอบ) เพื่อให้แน่ใจว่ามีการสร้างใหม่แบบเพิ่มขึ้นเรื่อยๆ (และการดำเนินการทดสอบอีกครั้ง) อย่างถูกต้องหลังการเปลี่ยนแปลง เมื่อคุณระบุไดเรกทอรี ระบบบิลด์จะสร้างใหม่เฉพาะเมื่อมีการเปลี่ยนแปลงของไดเรกทอรีเท่านั้น (เนื่องจากการเพิ่มหรือลบไฟล์) แต่จะไม่ตรวจพบการแก้ไขแต่ละไฟล์ เนื่องจากการเปลี่ยนแปลงเหล่านั้นจะไม่ส่งผลต่อไดเรกทอรีที่ล้อมรอบอยู่
คุณควรระบุชุดไฟล์ที่อยู่ในไดเรกทอรีอย่างชัดเจนหรือใช้ฟังก์ชัน glob()
แทนการระบุไดเรกทอรีเป็นอินพุตสำหรับระบบบิลด์ (ใช้ **
เพื่อบังคับให้ glob()
เป็นแบบซ้ำซ้อน)
แนะนำ —
data = glob(["testdata/**"])
แต่น่าเสียดายที่บางสถานการณ์จำเป็นต้องใช้ป้ายกำกับไดเรกทอรี
เช่น หากไดเรกทอรี testdata
มีไฟล์ที่มีชื่อไม่เป็นไปตามไวยากรณ์ของป้ายกำกับ การแจกแจงไฟล์อย่างชัดแจ้งหรือการใช้ฟังก์ชัน glob()
จะทำให้เกิดข้อผิดพลาดป้ายกำกับไม่ถูกต้อง ในกรณีนี้ คุณต้องใช้ป้ายกำกับไดเรกทอรี แต่โปรดระวังความเสี่ยงที่อาจเกิดขึ้นจากการสร้างขึ้นใหม่ที่ไม่ถูกต้องตามที่อธิบายไว้ข้างต้น
หากต้องใช้ป้ายกำกับไดเรกทอรี โปรดทราบว่าคุณจะอ้างอิงแพ็กเกจหลักด้วยเส้นทางสัมพัทธ์ ../
ไม่ได้ โปรดใช้เส้นทางสัมบูรณ์ เช่น //data/regression:unittest/.
แทน
กฎภายนอก เช่น การทดสอบ ที่ต้องใช้ไฟล์หลายไฟล์จะต้องประกาศการพึ่งพาไฟล์เหล่านั้นอย่างชัดเจน คุณสามารถใช้ filegroup()
เพื่อจัดกลุ่มไฟล์ไว้ในไฟล์ BUILD
ดังนี้
filegroup(
name = 'my_data',
srcs = glob(['my_unittest_data/*'])
)
จากนั้นคุณจะอ้างอิงป้ายกํากับ my_data
เป็นการพึ่งพาข้อมูลในการทดสอบได้
สร้างไฟล์ | ระดับการเข้าถึง |