ระบบบิลด์ที่อิงตามอาร์ติแฟกต์

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

ระบบบิลด์ที่อิงตามอาร์ติแฟกต์มีงานจำนวนน้อยที่ระบบกำหนด ซึ่งวิศวกรสามารถกำหนดค่าได้ในลักษณะที่จำกัด วิศวกรยังคงบอกระบบว่าควรสร้างอะไร แต่ระบบบิลด์จะเป็นตัวกำหนดวิธีสร้าง เช่นเดียวกับ ระบบบิลด์ที่อิงตามงาน ระบบบิลด์ที่อิงตามอาร์ติแฟกต์ เช่น Bazel ยังคง มีไฟล์บิลด์ แต่เนื้อหาของไฟล์บิลด์เหล่านั้นจะแตกต่างกันมาก แทนที่จะเป็นชุดคำสั่งที่จำเป็นในภาษาการเขียนสคริปต์ที่สมบูรณ์แบบของทัวริงซึ่งอธิบายวิธีสร้างเอาต์พุต ไฟล์ BUILD ใน Bazel เป็นไฟล์ Manifest ที่ประกาศซึ่งอธิบายชุดอาร์ติแฟกต์ที่จะสร้าง การขึ้นต่อกัน และชุดตัวเลือกแบบจำกัดที่ส่งผลต่อวิธีสร้าง เมื่อวิศวกรเรียกใช้ bazel ในบรรทัดคำสั่ง วิศวกรจะระบุชุดเป้าหมายที่จะสร้าง (สิ่งที่) และ Bazel จะรับผิดชอบในการกำหนดค่า เรียกใช้ และกำหนดเวลาขั้นตอนการคอมไพล์ (วิธี) เนื่องจากตอนนี้ระบบบิลด์ควบคุมเครื่องมือที่จะเรียกใช้ได้เต็มที่ ระบบจึงรับประกันได้ดียิ่งขึ้น ซึ่งช่วยให้มีประสิทธิภาพมากขึ้นมากในขณะที่ยังคงรับประกันความถูกต้อง

มุมมองด้านฟังก์ชัน

การเปรียบเทียบระหว่างระบบบิลด์ที่อิงตามอาร์ติแฟกต์กับการเขียนโปรแกรมเชิงฟังก์ชันนั้นทำได้ง่าย ภาษาโปรแกรมแบบคำสั่งดั้งเดิม (เช่น Java, C และ Python) จะระบุรายการคำสั่งที่จะดำเนินการทีละรายการในลักษณะเดียวกับที่ระบบบิลด์แบบงานช่วยให้นักโปรแกรมกำหนดชุดขั้นตอนที่จะดำเนินการได้ ในทางตรงกันข้าม ภาษาโปรแกรมเชิงฟังก์ชัน (เช่น Haskell และ ML) มีโครงสร้างคล้ายกับชุดสมการทางคณิตศาสตร์มากกว่า ในภาษาฟังก์ชัน โปรแกรมเมอร์จะอธิบายการคำนวณที่จะดำเนินการ แต่ ปล่อยให้รายละเอียดของเวลาและวิธีดำเนินการคำนวณนั้นเป็นหน้าที่ของ คอมไพเลอร์

ซึ่งสอดคล้องกับแนวคิดในการประกาศไฟล์ Manifest ในระบบบิลด์ที่อิงตามอาร์ติแฟกต์ และปล่อยให้ระบบพิจารณาวิธีการดำเนินการบิลด์ ปัญหาหลายอย่างไม่สามารถ แสดงออกได้อย่างง่ายดายโดยใช้การเขียนโปรแกรมเชิงฟังก์ชัน แต่ปัญหาที่ทำได้จะได้รับประโยชน์ อย่างมากจากภาษาดังกล่าว เนื่องจากภาษามักจะสามารถขนานโปรแกรมดังกล่าวได้อย่างง่ายดาย และรับประกันความถูกต้องของโปรแกรมได้อย่างมั่นใจ ซึ่งเป็นสิ่งที่ เป็นไปไม่ได้ในภาษาแบบคำสั่ง ปัญหาที่ง่ายที่สุดในการแสดงออกโดยใช้ การเขียนโปรแกรมเชิงฟังก์ชันคือปัญหาที่เกี่ยวข้องกับการเปลี่ยนข้อมูลหนึ่ง เป็นอีกข้อมูลหนึ่งโดยใช้ชุดกฎหรือฟังก์ชัน และนั่นคือสิ่งที่ระบบบิลด์เป็น ระบบทั้งหมดเป็นฟังก์ชันทางคณิตศาสตร์ที่รับไฟล์ต้นฉบับ (และเครื่องมือต่างๆ เช่น คอมไพเลอร์) เป็นอินพุตและสร้างไบนารีเป็นเอาต์พุต จึงไม่น่าแปลกใจที่การสร้างระบบบิลด์ตามหลักการของการเขียนโปรแกรมเชิงฟังก์ชันจะทำงานได้ดี

ทำความเข้าใจระบบบิลด์ที่อิงตามอาร์ติแฟกต์

ระบบบิลด์ของ Google ชื่อ Blaze เป็นระบบบิลด์ที่อิงตามอาร์ติแฟกต์ระบบแรก Bazel เป็นเวอร์ชันโอเพนซอร์สของ Blaze

ไฟล์บิลด์ (โดยปกติจะมีชื่อว่า BUILD) ใน Bazel มีลักษณะดังนี้

java_binary(
    name = "MyBinary",
    srcs = ["MyBinary.java"],
    deps = [
        ":mylib",
    ],
)
java_library(
    name = "mylib",
    srcs = ["MyLibrary.java", "MyHelper.java"],
    visibility = ["//java/com/example/myproduct:__subpackages__"],
    deps = [
        "//java/com/example/common",
        "//java/com/example/myproduct/otherlib",
    ],
)

ใน Bazel ไฟล์ BUILD จะกําหนดเป้าหมาย ซึ่งเป้าหมาย 2 ประเภทในที่นี้คือ java_binary และ java_library เป้าหมายทุกรายการสอดคล้องกับอาร์ติแฟกต์ที่ระบบสร้างได้ โดยเป้าหมายไบนารีจะสร้างไบนารีที่เรียกใช้ได้โดยตรง และเป้าหมายไลบรารีจะสร้างไลบรารีที่ไบนารีหรือไลบรารีอื่นๆ ใช้ได้ เป้าหมายทุกรายการมีสิ่งต่อไปนี้

  • name: วิธีอ้างอิงเป้าหมายในบรรทัดคำสั่งและโดยเป้าหมายอื่นๆ
  • srcs: ไฟล์ต้นฉบับที่จะคอมไพล์เพื่อสร้างอาร์ติแฟกต์สำหรับเป้าหมาย
  • deps: เป้าหมายอื่นๆ ที่ต้องสร้างก่อนเป้าหมายนี้และลิงก์เข้าไป

Dependency อาจอยู่ในแพ็กเกจเดียวกัน (เช่น Dependency ของ MyBinary ใน :mylib) หรืออยู่ในแพ็กเกจอื่นในลำดับชั้นของแหล่งที่มาเดียวกัน (เช่น Dependency ของ mylib ใน //java/com/example/common)

เช่นเดียวกับระบบบิลด์ที่อิงตามงาน คุณจะทำการบิลด์โดยใช้เครื่องมือบรรทัดคำสั่งของ Bazel หากต้องการสร้างMyBinaryเป้าหมาย ให้เรียกใช้ bazel build :MyBinary หลังจากป้อนคำสั่งดังกล่าวเป็นครั้งแรกในที่เก็บที่ล้างข้อมูลขยะแล้ว Bazel จะทำสิ่งต่อไปนี้

  1. แยกวิเคราะห์ไฟล์ BUILD ทุกไฟล์ในพื้นที่ทำงานเพื่อสร้างกราฟการอ้างอิง ระหว่างอาร์ติแฟกต์
  2. ใช้กราฟเพื่อกำหนดการขึ้นต่อกันแบบทรานซิทีฟของ MyBinary นั่นคือ ทุกเป้าหมายที่ MyBinary ขึ้นต่อกัน และทุกเป้าหมายที่เป้าหมายเหล่านั้นขึ้นต่อกันแบบเรียกซ้ำ
  3. สร้างการอ้างอิงแต่ละรายการตามลำดับ Bazel เริ่มต้นด้วยการบิลด์แต่ละ เป้าหมายที่ไม่มีการขึ้นต่อกันอื่นๆ และติดตามการขึ้นต่อกันที่ ยังต้องบิลด์สำหรับแต่ละเป้าหมาย ทันทีที่สร้างทรัพยากร Dependency ทั้งหมดของเป้าหมาย Bazel จะเริ่มสร้างเป้าหมายนั้น กระบวนการนี้จะ ดำเนินต่อไปจนกว่าจะมีการสร้างการอ้างอิงแบบทรานซิทีฟทั้งหมดของ MyBinary
  4. สร้าง MyBinary เพื่อสร้างไบนารีที่เรียกใช้งานได้ขั้นสุดท้ายซึ่งลิงก์ใน การอ้างอิงทั้งหมดที่สร้างขึ้นในขั้นตอนที่ 3

โดยพื้นฐานแล้ว สิ่งที่เกิดขึ้นที่นี่อาจดูไม่แตกต่างจากสิ่งที่เกิดขึ้นเมื่อใช้ระบบบิลด์ตามงานมากนัก แน่นอนว่าผลลัพธ์สุดท้ายคือไบนารีเดียวกัน และกระบวนการสร้างไบนารีนี้เกี่ยวข้องกับการวิเคราะห์ขั้นตอนต่างๆ เพื่อค้นหาการขึ้นต่อกันระหว่างขั้นตอนเหล่านั้น แล้วจึงเรียกใช้ขั้นตอนเหล่านั้นตามลำดับ แต่ก็มีความแตกต่างที่สำคัญ รายการแรกจะปรากฏในขั้นตอนที่ 3 เนื่องจาก Bazel รู้ว่าแต่ละเป้าหมายจะสร้างไลบรารี Java เท่านั้น จึงรู้ว่าสิ่งที่ต้องทำคือเรียกใช้คอมไพเลอร์ Java แทนที่จะเป็นสคริปต์ที่ผู้ใช้กำหนดเองโดยพลการ จึงรู้ว่าการเรียกใช้ขั้นตอนเหล่านี้แบบขนานนั้นปลอดภัย ซึ่งจะช่วยปรับปรุงประสิทธิภาพได้มากเมื่อเทียบกับการสร้างเป้าหมายทีละรายการในเครื่องแบบมัลติคอร์ และทำได้ก็เพราะว่าแนวทางที่อิงตามอาร์ติแฟกต์จะทำให้ระบบบิลด์เป็นผู้รับผิดชอบกลยุทธ์การดำเนินการของตัวเอง เพื่อให้มั่นใจได้มากขึ้นเกี่ยวกับการทำงานแบบคู่ขนาน

แต่ประโยชน์ไม่ได้มีแค่การทำงานแบบคู่ขนาน สิ่งต่อไปที่แนวทางนี้มอบให้เราจะเห็นได้ชัดเมื่อนักพัฒนาซอฟต์แวร์พิมพ์คำสั่งเดิมbazel build :MyBinaryเป็นครั้งที่ 2 โดยไม่มีการเปลี่ยนแปลงใดๆ Bazel จะออกภายในไม่ถึง 1 วินาทีพร้อมข้อความที่ระบุว่าเป้าหมายเป็นเวอร์ชันล่าสุด ซึ่งเป็นไปได้เนื่องจากกระบวนทัศน์การเขียนโปรแกรมเชิงฟังก์ชันที่เราพูดถึงก่อนหน้านี้ Bazel รู้ว่าแต่ละเป้าหมายเป็นผลลัพธ์ของการเรียกใช้คอมไพเลอร์ Java เท่านั้น และรู้ว่าเอาต์พุตจากคอมไพเลอร์ Java ขึ้นอยู่กับอินพุตเท่านั้น ดังนั้นตราบใดที่อินพุตยังไม่เปลี่ยนแปลง ก็สามารถนำเอาต์พุตกลับมาใช้ใหม่ได้ และการวิเคราะห์นี้จะทำงานในทุกระดับ หาก MyBinary.java มีการเปลี่ยนแปลง Bazel จะทราบ ว่าต้องสร้าง MyBinary ใหม่ แต่จะนำ mylib กลับมาใช้ซ้ำ หากไฟล์ต้นฉบับสำหรับ //java/com/example/common มีการเปลี่ยนแปลง Bazel จะทราบว่าต้องสร้างไลบรารี mylib และ MyBinary ใหม่ แต่จะใช้ //java/com/example/myproduct/otherlib ซ้ำ เนื่องจาก Bazel ทราบคุณสมบัติของเครื่องมือที่เรียกใช้ในทุกขั้นตอน จึงสามารถสร้างอาร์ติแฟกต์ชุดขั้นต่ำใหม่ได้ทุกครั้ง พร้อมรับประกันว่าจะไม่สร้างบิลด์ที่ล้าสมัย

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

เคล็ดลับอื่นๆ ที่ยอดเยี่ยมของ Bazel

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

เครื่องมือเป็นทรัพยากร Dependency

ปัญหาหนึ่งที่เราพบก่อนหน้านี้คือบิลด์ขึ้นอยู่กับเครื่องมือที่ติดตั้งในเครื่องของเรา และการสร้างบิลด์ซ้ำในระบบต่างๆ อาจทำได้ยากเนื่องจากเครื่องมือมีเวอร์ชันหรือตำแหน่งที่แตกต่างกัน ปัญหายิ่งซับซ้อนมากขึ้น เมื่อโปรเจ็กต์ของคุณใช้ภาษาที่ต้องใช้เครื่องมือที่แตกต่างกันตามแพลตฟอร์มที่ใช้สร้างหรือคอมไพล์ (เช่น Windows กับ Linux) และแต่ละแพลตฟอร์มต้องใช้ชุดเครื่องมือที่แตกต่างกันเล็กน้อยเพื่อทำงานเดียวกัน

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

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

  • การกำหนดค่าโฮสต์: สร้างเครื่องมือที่ทำงานระหว่างการสร้าง
  • การกำหนดค่าเป้าหมาย: สร้างไบนารีที่คุณขอในท้ายที่สุด

การขยายระบบบิลด์

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

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

ระบบนี้ไม่สามารถรับประกันได้ 100% เนื่องจากไม่มีวิธีหยุดไม่ให้นักพัฒนาแอป ดำเนินการบางอย่าง เช่น การนำกระบวนการที่ไม่แน่นอนมาใช้เป็นส่วนหนึ่งของ การดำเนินการ แต่ในทางปฏิบัติแล้ว กรณีนี้ไม่ค่อยเกิดขึ้น และการผลักดัน ความเป็นไปได้ในการละเมิดไปจนถึงระดับการดำเนินการจะช่วยลดโอกาส ที่จะเกิดข้อผิดพลาดลงอย่างมาก กฎที่รองรับภาษาและเครื่องมือที่ใช้กันทั่วไปหลายอย่างมี ให้ใช้งานอย่างแพร่หลายทางออนไลน์ และโปรเจ็กต์ส่วนใหญ่ไม่จำเป็นต้องกำหนดกฎของตัวเอง แม้แต่สำหรับกฎที่ต้องใช้คำจำกัดความ ก็จำเป็นต้องกำหนดไว้ในที่เดียว ส่วนกลางในที่เก็บ ซึ่งหมายความว่าวิศวกรส่วนใหญ่จะใช้กฎเหล่านั้นได้ โดยไม่ต้องกังวลเกี่ยวกับการใช้งาน

การแยกสภาพแวดล้อม

การดำเนินการดูเหมือนอาจพบปัญหาเดียวกันกับงานในระบบอื่นๆ คุณยังคงเขียนการดำเนินการที่ทั้งเขียนไปยังไฟล์เดียวกันและลงท้ายด้วยการขัดแย้งกันได้ใช่ไหม ในความเป็นจริงแล้ว Bazel ทำให้ความขัดแย้งเหล่านี้เป็นไปไม่ได้ด้วยการใช้แซนด์บ็อกซ์ ในระบบที่รองรับ การดำเนินการทุกอย่างจะแยกจากการดำเนินการอื่นๆ ทั้งหมดผ่านแซนด์บ็อกซ์ของระบบไฟล์ กล่าวคือ แต่ละการดำเนินการจะดูได้เฉพาะมุมมองที่จำกัดของ ระบบไฟล์ ซึ่งรวมถึงอินพุตที่ประกาศไว้และเอาต์พุตที่สร้างขึ้น ระบบต่างๆ เช่น LXC ใน Linux จะบังคับใช้ข้อจำกัดนี้ ซึ่งเป็นเทคโนโลยีเดียวกับที่อยู่เบื้องหลัง Docker ซึ่งหมายความว่าการดำเนินการจะขัดแย้งกันไม่ได้เนื่องจากอ่านไฟล์ที่ไม่ได้ประกาศไม่ได้ และระบบจะทิ้งไฟล์ที่เขียนแต่ไม่ได้ประกาศเมื่อการดำเนินการเสร็จสิ้น นอกจากนี้ Bazel ยังใช้แซนด์บ็อกซ์เพื่อจำกัดการสื่อสารผ่านเครือข่ายของ Actions ด้วย

การทำให้ทรัพยากร Dependency ภายนอกเป็นแบบดีเทอร์มินิสติก

แต่ก็ยังมีปัญหาหนึ่งที่ยังคงอยู่ นั่นคือระบบบิลด์มักจะต้องดาวน์โหลด การอ้างอิง (ไม่ว่าจะเป็นเครื่องมือหรือไลบรารี) จากแหล่งที่มาภายนอกแทนที่จะ สร้างโดยตรง คุณดูตัวอย่างได้ผ่าน@com_google_common_guava_guava//jar Dependency ซึ่งจะดาวน์โหลดไฟล์ JAR จาก Maven

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

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

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

หากอาร์ติแฟกต์ที่เราดาวน์โหลดมีแฮชแตกต่างจากที่ประกาศไว้ใน ไฟล์ Manifest บิลด์จะล้มเหลว เว้นแต่จะมีการอัปเดตแฮชในไฟล์ Manifest ซึ่งสามารถทำได้โดยอัตโนมัติ แต่การเปลี่ยนแปลงนั้นต้องได้รับการอนุมัติและตรวจสอบใน การควบคุมแหล่งที่มาก่อนที่บิลด์จะยอมรับการอ้างอิงใหม่ ซึ่งหมายความว่า จะมีบันทึกเสมอเมื่อมีการอัปเดตการอ้างอิง และการอ้างอิงภายนอก จะเปลี่ยนแปลงไม่ได้หากไม่มีการเปลี่ยนแปลงที่สอดคล้องกันในแหล่งที่มาของพื้นที่ทำงาน นอกจากนี้ยังหมายความว่าเมื่อตรวจสอบซอร์สโค้ดเวอร์ชันเก่ากว่า การสร้างจะรับประกันได้ว่าจะใช้การอ้างอิงเดียวกันกับที่ใช้ ณ จุด ที่เวอร์ชันนั้นได้รับการเช็คอิน (หรือจะล้มเหลวหากการอ้างอิงเหล่านั้น ไม่พร้อมใช้งานอีกต่อไป)

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