เป้าหมาย A
จะขึ้นอยู่กับเป้าหมาย B
หาก A
จำเป็นต้องใช้ B
ณ เวลาที่สร้างหรือดำเนินการ ความสัมพันธ์ที่ขึ้นอยู่กับก่อให้เกิดกราฟแอไซเคิลโดยตรง (DAG) เหนือเป้าหมาย และเรียกว่ากราฟการพึ่งพากัน
ทรัพยากร Dependency โดยตรงของเป้าหมายคือเป้าหมายอื่นๆ ที่เข้าถึงได้ตามเส้นทางความยาว 1 ในกราฟการอ้างอิง ทรัพยากร Dependency แบบสับเปลี่ยนของเป้าหมายคือเป้าหมายที่ขึ้นอยู่กับเส้นทางที่มีความยาวผ่านกราฟ
ในบริบทของบิลด์จะมีกราฟการอ้างอิง 2 กราฟ ได้แก่ กราฟทรัพยากร Dependency จริง และกราฟทรัพยากร Dependency ที่ประกาศ ส่วนใหญ่แล้ว กราฟทั้ง 2 แบบจะคล้ายกันมากจนไม่จำเป็นต้องสร้างความแตกต่างนี้ แต่จะมีประโยชน์สำหรับการอภิปรายด้านล่าง
ทรัพยากร Dependency จริงและที่ประกาศไว้
เป้าหมาย X
ที่จริงแล้วจะขึ้นอยู่กับเป้าหมาย Y
หาก Y
ต้องมี สร้างแล้ว และเป็นปัจจุบันเพื่อให้ X
สร้างได้อย่างถูกต้อง สร้างอาจหมายถึงงานที่สร้างขึ้น ประมวลผล คอมไพล์ ลิงก์ เก็บถาวร บีบอัด ดำเนินการ หรืองานประเภทอื่นๆ ที่เกิดขึ้นเป็นประจำระหว่างบิลด์
เป้าหมาย X
มีทรัพยากร Dependency ที่ประกาศ ตามเป้าหมาย Y
หากมี Edge ของทรัพยากร Dependency จาก X
ถึง Y
ในแพ็กเกจของ X
สำหรับบิลด์ที่ถูกต้อง กราฟของทรัพยากร Dependency จริง A ต้องเป็นกราฟย่อยของกราฟทรัพยากร Dependency ที่ประกาศไว้ D กล่าวคือ โหนด x --> y
ทุกคู่ที่เชื่อมต่อโดยตรงใน A ต้องเชื่อมต่อกันโดยตรงใน D ด้วย อาจกล่าวได้ว่า D เป็นค่าใกล้เคียงของ A
ผู้เขียนไฟล์ BUILD
ต้องประกาศทรัพยากร Dependency จริงทั้งหมดอย่างชัดเจนสำหรับกฎทุกข้อกับระบบบิลด์ และไม่ต้องระบุอีก
การไม่ดำเนินการตามหลักการนี้จะทำให้เกิดลักษณะการทำงานที่ไม่ได้กำหนด กล่าวคือ บิลด์อาจล้มเหลว แต่ที่แย่กว่านั้นคือ บิลด์อาจขึ้นอยู่กับการดำเนินการก่อนหน้านี้ หรือทรัพยากร Dependency ที่ประกาศไว้อย่างชัดเจนซึ่งเป้าหมายเกิดขึ้น Bazel จะตรวจหาการขึ้นต่อกันที่หายไปและรายงานข้อผิดพลาด แต่การตรวจสอบนี้อาจไม่สำเร็จในทุกกรณี
คุณไม่จําเป็นต้อง (และไม่ควร) พยายามแสดงรายการทั้งหมดที่นําเข้าโดยอ้อม แม้ว่า A
จะจําเป็นในเวลาดำเนินการก็ตาม
ในระหว่างบิลด์ของเป้าหมาย X
เครื่องมือบิลด์จะตรวจสอบการปิดทรัพยากร Dependency ทั้งหมดของ X
เพื่อให้แน่ใจว่าการเปลี่ยนแปลงในเป้าหมายเหล่านั้นสะท้อนให้เห็นในผลลัพธ์สุดท้าย และสร้างสื่อกลางใหม่ตามที่จำเป็น
ลักษณะการแปรเปลี่ยนของทรัพยากร Dependency มักทำให้เกิดความผิดพลาดที่พบได้บ่อย บางครั้งโค้ดในไฟล์หนึ่งอาจใช้โค้ดที่มาจากทรัพยากร Dependency โดยอ้อม ซึ่งเป็นแบบสกรรมกริยาที่ไม่ใช่ทางตรงในกราฟการอ้างอิงที่ประกาศไว้ ทรัพยากร 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 ที่ประกาศมีราคาโดยประมาณของทรัพยากร Dependency จริงมากเกินไป ทุกอย่างเรียบร้อยดี
2. การเพิ่มทรัพยากร Dependency ที่ไม่ได้ประกาศ
จะเกิดอันตรายแฝงเมื่อมีคนเพิ่มโค้ดไปยัง a
ซึ่งสร้างทรัพยากร Dependency จริงโดยตรงบน c
แต่ลืมประกาศข้อมูลดังกล่าวในไฟล์บิลด์a/BUILD
a / a.in |
|
---|---|
import b; import c; b.foo(); c.garply(); |
|
ทรัพยากร Dependency ที่ประกาศไม่ได้ประเมินทรัพยากร Dependency จริงมากเกินไป
อาจถือว่าพอใช้ได้ เนื่องจากการปิดแบบสับเปลี่ยนของกราฟทั้ง 2 แบบเท่ากัน แต่มาสก์ปัญหาคือ a
มีการขึ้นต่อกันจริงแต่ไม่ได้ประกาศไว้บน c
3. ความแตกต่างระหว่างกราฟการอ้างอิงที่ประกาศกับกราฟแสดง Dependency จริง
อันตรายจะเปิดเผยเมื่อมีคนเปลี่ยนโครงสร้างภายใน 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
ซึ่งอธิบายไว้ด้านล่าง ดูรายละเอียดเพิ่มเติมได้ที่แอตทริบิวต์ที่ใช้ได้กับกฎทั้งหมด
นอกจากนี้ กฎอีกจำนวนมากยังมีแอตทริบิวต์เพิ่มเติมสำหรับประเภททรัพยากร Dependency ของกฎโดยเฉพาะ เช่น compiler
หรือ resources
ซึ่งอธิบายรายละเอียดไว้ใน Build Encyclopedia
ทรัพยากร Dependency srcs
รายการ
ไฟล์ที่กฎหรือกฎใช้โดยตรงซึ่งเอาต์พุตไฟล์ต้นทาง
ทรัพยากร Dependency deps
รายการ
กฎที่ชี้ไปยังโมดูลที่คอมไพล์แยกต่างหากที่ระบุไฟล์ส่วนหัว สัญลักษณ์ ไลบรารี ข้อมูล ฯลฯ
ทรัพยากร Dependency data
รายการ
เป้าหมายบิลด์อาจต้องใช้ไฟล์ข้อมูลบางรายการเพื่อให้ทำงานได้อย่างถูกต้อง ไฟล์ข้อมูลเหล่านี้ไม่ใช่ซอร์สโค้ด และจะไม่กระทบต่อวิธีสร้างเป้าหมาย เช่น การทดสอบหน่วยอาจเปรียบเทียบเอาต์พุตของฟังก์ชันกับเนื้อหาของไฟล์ คุณไม่จำเป็นต้องใช้ไฟล์เมื่อสร้างการทดสอบหน่วย แต่จำเป็นต้องใช้เมื่อทำการทดสอบ เช่นเดียวกันกับเครื่องมือที่เปิดในระหว่างการดำเนินการ
ระบบบิลด์จะเรียกใช้การทดสอบในไดเรกทอรีแบบแยกซึ่งมีเฉพาะไฟล์ที่ระบุว่าเป็น 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
ในการทดสอบ คุณจะอ้างอิงไฟล์เหล่านี้ได้โดยผนวกเส้นทางของไดเรกทอรีต้นทางของการทดสอบและเส้นทางที่เกี่ยวข้องกับพื้นที่ทำงาน เช่น ${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
เป็นการอ้างอิงข้อมูลในการทดสอบได้
ไฟล์บิลด์ | ระดับการเข้าถึง |