การอ้างอิง

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

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

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

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

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

เป้าหมาย X มีทรัพยากร Dependency ที่ประกาศในเป้าหมาย Y หากมี Edge ของทรัพยากร Dependency จาก X ถึง Y ในแพ็กเกจ X

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

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

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

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

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

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

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

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

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

กฎการสร้างส่วนใหญ่มีแอตทริบิวต์ 3 รายการสำหรับการระบุทรัพยากร Dependency ทั่วไปประเภทต่างๆ ได้แก่ srcs, deps และ data ซึ่งจะอธิบายไว้ด้านล่าง ดูรายละเอียดเพิ่มเติมได้ที่แอตทริบิวต์ที่ใช้ได้กับกฎทั้งหมด

นอกจากนี้ กฎหลายข้อยังมีแอตทริบิวต์เพิ่มเติมสำหรับประเภททรัพยากร Dependency เฉพาะกฎ เช่น compiler หรือ resources ดูรายละเอียดได้ใน Build Encyclopedia

ทรัพยากร Dependency srcs รายการ

ไฟล์ที่กฎหรือกฎใช้โดยตรงซึ่งส่งออกไฟล์ต้นฉบับ

ทรัพยากร Dependency deps รายการ

กฎที่ชี้ไปยังโมดูลที่คอมไพล์แยกกัน โดยให้ไฟล์ส่วนหัว สัญลักษณ์ ไลบรารี ข้อมูล ฯลฯ

ทรัพยากร Dependency data รายการ

เป้าหมายบิลด์อาจต้องการไฟล์ข้อมูลบางรายการเพื่อให้ทำงานได้อย่างถูกต้อง ไฟล์ข้อมูลเหล่านี้ไม่ใช่ซอร์สโค้ด แต่ไม่ส่งผลกระทบต่อวิธีสร้างเป้าหมาย เช่น การทดสอบ 1 หน่วยอาจเปรียบเทียบผลลัพธ์ของฟังก์ชันกับเนื้อหาของไฟล์ เมื่อสร้างการทดสอบ 1 หน่วยแล้ว คุณไม่จำเป็นต้องใช้ไฟล์ แต่ต้องใช้เมื่อทำการทดสอบ เช่นเดียวกันกับเครื่องมือที่เปิดในระหว่างการดำเนินการ

ระบบบิลด์จะทำการทดสอบในไดเรกทอรีที่แยกออกมาซึ่งมีเฉพาะไฟล์ที่ระบุว่าเป็น 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 ว่าเป็นทรัพยากร Dependency ในการทดสอบได้

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