การอ้างอิง

รายงานปัญหา ดูแหล่งข้อมูล ดูแหล่งข้อมูล 7.3 · 7.2 · 7.1 · 7.0 · 6.5

เป้าหมาย A ขึ้นอยู่กับเป้าหมาย B หาก A ต้องใช้ B ในเวลาที่สร้างหรือเวลาดำเนินการ ความสัมพันธ์ขึ้นอยู่กับจะทําให้เกิด Directed Acyclic Graph (DAG) เหนือเป้าหมาย และเรียกกันว่ากราฟ Dependency

ทรัพยากร Dependency โดยตรงของเป้าหมายคือเป้าหมายอื่นๆ ที่เข้าถึงได้ด้วยเส้นทางที่มีความยาว 1 ในกราฟทรัพยากร Dependency Dependency แบบทรานซิทีฟของเป้าหมายคือเป้าหมายที่เป้าหมายนั้นต้องอาศัยผ่านเส้นทางที่มีความยาวเท่าใดก็ได้ในกราฟ

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

ทรัพยากร Dependency จริงและที่ประกาศ

เป้าหมาย X ขึ้นอยู่กับเป้าหมาย Y จริงๆ หาก Y ต้องมีอยู่ สร้างขึ้น และอัปเดตแล้วเพื่อให้สร้าง X ได้อย่างถูกต้อง สร้างอาจหมายถึงสร้าง ประมวลผล คอมไพล์ ลิงก์ เก็บถาวร บีบอัด ดำเนินการ หรืองานประเภทอื่นๆ ที่เกิดขึ้นเป็นประจำระหว่างการสร้าง

เป้าหมาย X มีการขึ้นต่อกันที่ประกาศไว้กับเป้าหมาย Y หากมีขอบเขตการขึ้นต่อกันจาก 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 ที่ประกาศซึ่งมีลูกศรเชื่อม a, b และ c
กราฟทรัพยากร Dependency ที่ประกาศ
กราฟทรัพยากร Dependency จริงที่ตรงกับกราฟทรัพยากร Dependency ที่ประกาศไว้ซึ่งมีลูกศรเชื่อม a, b และ c
กราฟทรัพยากร Dependency จริง

ทรัพยากร Dependency ที่ประกาศไว้ใกล้เคียงกับทรัพยากร Dependency จริงมากเกินไป ทุกอย่างเรียบร้อยดี

2. การเพิ่มทรัพยากร Dependency ที่ไม่ได้ประกาศ

อันตรายแฝงจะปรากฏขึ้นเมื่อมีผู้เพิ่มโค้ดลงใน a ซึ่งสร้างการพึ่งพาจริงโดยตรงใน c แต่ลืมประกาศในไฟล์บิลด์ a/BUILD

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
กราฟทรัพยากร Dependency ที่ประกาศซึ่งมีลูกศรเชื่อม a, b และ c
กราฟทรัพยากร Dependency ที่ประกาศไว้
กราฟทรัพยากร Dependency จริงมีลูกศรเชื่อม a, b และ c ตอนนี้ลูกศรจะเชื่อมต่อ A กับ C ด้วย ซึ่งไม่ตรงกับกราฟทรัพยากร Dependency ที่ประกาศไว้
กราฟทรัพยากร Dependency ตามจริง

Dependency ที่ประกาศจะไม่ประมาณค่า Dependency จริงมากเกินไปอีกต่อไป วิธีนี้อาจถือว่าเป็นปัญหา เนื่องจากการปิดแบบสับเปลี่ยนของกราฟทั้ง 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 ที่ประกาศซึ่งมีลูกศรเชื่อมระหว่าง a กับ b
                  b ไม่เชื่อมต่อกับ c แล้ว ซึ่งจะทำลายการเชื่อมต่อของ a กับ c
กราฟทรัพยากร Dependency ที่ประกาศ
กราฟความเกี่ยวข้องจริงที่แสดงการเชื่อมต่อ a กับ b และ c แต่ b ไม่ได้เชื่อมต่อกับ c อีกต่อไป
กราฟทรัพยากร Dependency จริง

ตอนนี้กราฟทรัพยากร Dependency ที่ประกาศไว้จะประมาณทรัพยากร Dependency จริงต่ำเกินไป แม้ว่าจะปิดแบบทรานซิทีฟแล้วก็ตาม บิลด์มีแนวโน้มที่จะดำเนินการไม่สำเร็จ

ปัญหานี้อาจหลีกเลี่ยงได้ด้วยการยืนยันว่ามีการประกาศการพึ่งพาจริงจาก a ไปยัง c ที่ระบุไว้ในขั้นตอนที่ 2 อย่างถูกต้องในไฟล์ BUILD

ประเภทของทรัพยากร Dependency

กฎการสร้างส่วนใหญ่มีแอตทริบิวต์ 3 รายการสําหรับระบุข้อกําหนดทั่วไปประเภทต่างๆ ได้แก่ 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 เป็นการพึ่งพาข้อมูลในการทดสอบได้

ไฟล์ BUILD ระดับการเข้าถึง