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

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

ระบบบิลด์ที่อิงตามอาร์ติแฟกต์มีงานเพียงไม่กี่งานที่กำหนดโดยระบบ ซึ่งวิศวกรสามารถกำหนดค่าได้ด้วยวิธีที่จำกัด วิศวกรยังคงบอกให้ระบบทราบว่าต้องสร้างอะไร แต่ระบบบิลด์จะกำหนดวิธีการสร้าง สำหรับระบบบิลด์แบบอิงตามงาน ระบบบิลด์ที่อิงตามอาร์ติแฟกต์ เช่น Bazel ยังคงมีบิลด์ไฟล์ แต่เนื้อหาของบิลด์ไฟล์เหล่านั้นแตกต่างกันอย่างมาก แทนที่จะเป็นชุดคำสั่งที่จำเป็นในภาษาสคริปต์ของตุรกี (Turing) ที่อธิบายวิธีสร้างเอาต์พุต แต่บิลด์บิลด์ใน Bazel คือไฟล์ Manifest เชิงประกาศที่อธิบายถึงชุดอาร์ติแฟกต์ที่จะสร้าง ทรัพยากร Dependency และชุดตัวเลือกที่จำกัดซึ่งส่งผลต่อวิธีสร้าง เมื่อวิศวกรเรียกใช้ 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 ทุกไฟล์ในพื้นที่ทำงานเพื่อสร้างกราฟทรัพยากร Dependency ระหว่างอาร์ติแฟกต์ต่างๆ
  2. ใช้กราฟเพื่อระบุทรัพยากร Dependency แบบสับเปลี่ยนของ MyBinary นั่นคือ ทุกเป้าหมายที่ MyBinary อ้างอิงอยู่และทุกเป้าหมายที่เป้าหมายเหล่านั้นอ้างอิงซ้ำ
  3. สร้างทรัพยากร Dependency เหล่านี้ตามลำดับ Bazel เริ่มต้นโดยการสร้างเป้าหมายแต่ละรายการที่ไม่มีทรัพยากร Dependency อื่นและคอยติดตามว่าทรัพยากร Dependency ใดที่ยังต้องสร้างสำหรับแต่ละเป้าหมาย ทันทีที่ระบบการพึ่งพาเป้าหมายทั้งหมดสร้างขึ้น Bazel จะเริ่มสร้างเป้าหมายนั้น กระบวนการนี้จะดำเนินไปจนกว่าจะมีการสร้างทรัพยากร Dependency ชั่วคราวของ MyBinary ครบทุกรายการ
  4. สร้าง MyBinary เพื่อสร้างไบนารีที่ดำเนินการได้ขั้นสุดท้ายซึ่งลิงก์กับทรัพยากร Dependency ทั้งหมดที่สร้างในขั้นตอนที่ 3

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

อย่างไรก็ตาม คุณจะได้รับประโยชน์มากกว่าการทำงานแบบพร้อมกัน สิ่งต่อไปที่วิธีการนี้ทำให้เราเห็นได้ชัดเมื่อนักพัฒนาซอฟต์แวร์พิมพ์ bazel build :MyBinary เป็นครั้งที่ 2 โดยไม่ได้ทำการเปลี่ยนแปลงใดๆ คือ Bazel ออกไปในไม่ถึงวินาทีด้วยข้อความที่บอกว่าเป้าหมายเป็นปัจจุบันแล้ว เป็นไปได้เพราะกระบวนทัศน์การเขียนโปรแกรมเชิงฟังก์ชันที่เราได้พูดถึงก่อนหน้านี้ โดย Bazel ทราบว่าแต่ละเป้าหมายเป็นผลลัพธ์จากการเรียกใช้คอมไพเลอร์ Java และรู้ว่าเอาต์พุตจากคอมไพเลอร์ Java จะขึ้นอยู่กับอินพุต ตราบใดที่อินพุตไม่มีการเปลี่ยนแปลง สามารถนำเอาต์พุตนั้นมาใช้ซ้ำได้ และการวิเคราะห์นี้ทำงานได้ในทุกระดับ หาก MyBinary.java มีการเปลี่ยนแปลง บาเซลรู้ว่าจะต้องสร้าง 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 ภายนอก

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

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

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

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

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

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