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

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

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

มุมมองเชิงฟังก์ชัน

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

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

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

Blaze ซึ่งเป็นระบบบิลด์ของ Google เป็นระบบบิลด์ที่อิงตามอาร์ติแฟกต์ระบบแรก 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 อาจอยู่ในแพ็กเกจเดียวกัน (เช่น MyBinary’s ทรัพยากร Dependency ใน :mylib) หรืออยู่ในแพ็กเกจอื่นในลำดับชั้นของซอร์สเดียวกัน (เช่น mylib’s ทรัพยากร Dependency ใน //java/com/example/common)

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

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

โดยพื้นฐานแล้ว สิ่งที่เกิดขึ้นที่นี่อาจดูไม่แตกต่างจากสิ่งที่เกิดขึ้นเมื่อใช้ระบบบิลด์ที่อิงตามงานมากนัก แน่นอนว่าผลลัพธ์สุดท้ายคือไบนารีเดียวกัน และกระบวนการสร้างไบนารีเกี่ยวข้องกับการวิเคราะห์ขั้นตอนต่างๆ เพื่อค้นหาทรัพยากร Dependency ระหว่างขั้นตอนเหล่านั้น แล้วจึงเรียกใช้ขั้นตอนเหล่านั้นตามลำดับ แต่ก็มีความแตกต่างที่สำคัญ ความแตกต่างแรกปรากฏในขั้นตอนที่ 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 จะจัดการกำหนดเวลาการดำเนินการและแคชผลลัพธ์ตามความเหมาะสม

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

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

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

การทำให้ทรัพยากร Dependency ภายนอกเป็นแบบกำหนดได้

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

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

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

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

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

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