สร้างโปรแกรมด้วย Bazel

หน้านี้จะกล่าวถึงวิธีสร้างโปรแกรมด้วย Bazel, ไวยากรณ์คำสั่ง build และไวยากรณ์รูปแบบเป้าหมาย

คู่มือเริ่มใช้งานฉบับย่อ

หากต้องการเรียกใช้ Bazel ให้ไปที่ไดเรกทอรีพื้นที่ทำงานพื้นฐานหรือไดเรกทอรีย่อยใดๆ ของไดเรกทอรีแล้วพิมพ์ bazel โปรดดูสร้างหาก ต้องการสร้างพื้นที่ทำงานใหม่

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

คำสั่งที่ใช้ได้

  • analyze-profile: วิเคราะห์ข้อมูลโปรไฟล์การสร้าง
  • aquery: ดำเนินการค้นหาในกราฟการดำเนินการหลังการวิเคราะห์
  • build: สร้างเป้าหมายที่ระบุ
  • canonicalize-flags: กำหนดแฟล็ก Bazel ตามรูปแบบบัญญัติ
  • clean: นำไฟล์เอาต์พุตออกและหยุดเซิร์ฟเวอร์ (ไม่บังคับ)
  • cquery: เรียกใช้การค้นหากราฟความเกี่ยวข้องหลังการวิเคราะห์
  • dump: แสดงสถานะภายในของกระบวนการเซิร์ฟเวอร์ Bazel
  • help: พิมพ์ความช่วยเหลือสำหรับคำสั่งหรือดัชนี
  • info: แสดงข้อมูลรันไทม์เกี่ยวกับเซิร์ฟเวอร์ Bazel
  • fetch: ดึงข้อมูลการอ้างอิงภายนอกทั้งหมดของเป้าหมาย
  • mobile-install: ติดตั้งแอปในอุปกรณ์เคลื่อนที่
  • query: เรียกใช้การค้นหากราฟทรัพยากร Dependency
  • run: เรียกใช้เป้าหมายที่ระบุ
  • shutdown: หยุดเซิร์ฟเวอร์ Bazel
  • test: สร้างและเรียกใช้เป้าหมายการทดสอบที่ระบุ
  • version: พิมพ์ข้อมูลเวอร์ชันของ Bazel

การขอความช่วยเหลือ

  • bazel help command: ความช่วยเหลือและตัวเลือกสำหรับรูปภาพ command
  • bazel helpstartup_options: ตัวเลือกสำหรับ JVM โฮสติ้ง Bazel
  • bazel helptarget-syntax: อธิบายไวยากรณ์สำหรับการระบุเป้าหมาย
  • bazel help info-keys: แสดงรายการคีย์ที่ใช้โดยคำสั่ง info

เครื่องมือ bazel ทำงานได้หลายอย่าง ซึ่งเรียกว่าคําสั่ง รายการที่ใช้กันมากที่สุดคือ bazel build และ bazel test คุณสามารถเรียกดูความช่วยเหลือออนไลน์ได้โดยใช้ bazel help

การสร้างเป้าหมายเดียว

คุณต้องมีเวิร์กスペースก่อนจึงจะเริ่มสร้างได้ พื้นที่ทํางานคือลําดับชั้นไดเรกทอรีที่มีไฟล์ต้นทางทั้งหมดที่จําเป็นสําหรับการสร้างแอปพลิเคชัน Bazel ช่วยให้คุณทำการบิลด์จากวอลุ่มแบบอ่านอย่างเดียวได้

หากต้องการสร้างโปรแกรมด้วย Bazel ให้พิมพ์ bazel build ตามด้วยเป้าหมายที่ต้องการสร้าง

bazel build //foo

หลังจากออกคำสั่งให้สร้าง //foo คุณจะเห็นเอาต์พุตคล้ายกับตัวอย่างต่อไปนี้

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

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

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

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

หากคุณพิมพ์คำสั่งเดิมอีกครั้ง บิลด์นั้นจะเสร็จสิ้นเร็วขึ้นมาก

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

นี่เป็นบิลด์ Null เพราะไม่มีอะไรเปลี่ยนแปลง จึงไม่มีแพ็กเกจให้โหลดซ้ำ และไม่มีขั้นตอนการสร้างที่จะทำงาน หากมีการเปลี่ยนแปลงใน "foo" หรือข้อกําหนดของ "foo" Bazel จะดําเนินการบางอย่างในบิลด์อีกครั้ง หรือทําบิลด์แบบเพิ่มให้เสร็จสมบูรณ์

การสร้างเป้าหมายหลายรายการ

Bazel ระบุเป้าหมายที่จะสร้างได้หลายวิธี รายการเหล่านี้เรียกรวมกันว่ารูปแบบเป้าหมาย ไวยากรณ์นี้ใช้ในคําสั่ง เช่น build, test หรือ query

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

รูปแบบเป้าหมายทั้งหมดที่ขึ้นต้นด้วย // จะได้รับการแก้ไขโดยสัมพันธ์กับเวิร์กสเปซปัจจุบัน

//foo/bar:wiz เป้าหมายเดียว //foo/bar:wiz
//foo/bar เทียบเท่ากับ //foo/bar:bar
//foo/bar:all เป้าหมายกฎทั้งหมดในแพ็กเกจ foo/bar
//foo/... เป้าหมายกฎทั้งหมดในแพ็กเกจทั้งหมดที่อยู่ภายใต้ไดเรกทอรี foo
//foo/...:all เป้าหมายกฎทั้งหมดในแพ็กเกจทั้งหมดภายใต้ไดเรกทอรี foo
//foo/...:* เป้าหมายทั้งหมด (กฎและไฟล์) ในแพ็กเกจทั้งหมดที่อยู่ภายใต้ไดเรกทอรี foo
//foo/...:all-targets เป้าหมายทั้งหมด (กฎและไฟล์) ในแพ็กเกจทั้งหมดภายใต้ไดเรกทอรี foo
//... เป้าหมายทั้งหมดในแพ็กเกจในพื้นที่ทำงาน โดยไม่รวมเป้าหมายจากที่เก็บข้อมูลภายนอก
//:all เป้าหมายทั้งหมดในแพ็กเกจระดับบนสุด หากมีไฟล์ `BUILD` ที่รูทของเวิร์กスペース

ระบบจะแก้ไขรูปแบบเป้าหมายที่ไม่ได้ขึ้นต้นด้วย // โดยสัมพันธ์กับไดเรกทอรีทํางานปัจจุบัน ตัวอย่างเหล่านี้จะถือว่าไดเรกทอรีทํางานคือ foo

:foo เทียบเท่ากับ //foo:foo
bar:wiz เทียบเท่ากับ //foo/bar:wiz
bar/wiz เทียบเท่ากับ
  • //foo/bar/wiz:wiz if foo/bar/wiz is a package
  • //foo/bar:wiz หาก foo/bar เป็นแพ็กเกจ
  • จ่าย //foo:bar/wiz
bar:all เทียบเท่ากับ //foo/bar:all
:all เทียบเท่ากับ //foo:all
...:all เทียบเท่ากับ //foo/...:all
... เทียบเท่ากับ //foo/...:all
bar/...:all เทียบเท่ากับ //foo/bar/...:all

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

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

foo/... คือไวลด์การ์ดที่อยู่เหนือแพ็กเกจ ซึ่งบ่งบอกว่าแพ็กเกจทั้งหมดอยู่ภายใต้ไดเรกทอรี foo (สำหรับรูททั้งหมดของเส้นทางแพ็กเกจ) :all เป็นไวลด์การ์ดสำหรับ targets ซึ่งจะจับคู่กฎทั้งหมดภายในแพ็กเกจ ทั้ง 2 รายการนี้อาจมีการรวมเข้าด้วยกัน เช่น ใน foo/...:all และเมื่อใช้ไวลด์การ์ดทั้ง 2 รายการนี้ อาจเป็นตัวย่อเป็น foo/...

นอกจากนี้ :* (หรือ :all-targets) เป็นไวลด์การ์ดที่ตรงกับเป้าหมายทุกรายการในแพ็กเกจที่ตรงกัน ซึ่งรวมถึงไฟล์ที่ไม่ได้สร้างตามกฎใดๆ โดยทั่วไป เช่น ไฟล์ _deploy.jar ที่เชื่อมโยงกับกฎ java_binary

ซึ่งหมายความว่า :* หมายถึงเซตที่ซ้อนกันของ :all แม้ว่าอาจทำให้เกิดความสับสน แต่ไวยากรณ์นี้ช่วยให้ใช้ไวลด์การ์ด :all ที่คุ้นเคยสำหรับบิลด์ทั่วไปได้ ซึ่งไม่ต้องการเป้าหมายการสร้างอย่าง _deploy.jar

นอกจากนี้ Bazel ยังอนุญาตให้ใช้เครื่องหมายทับแทนเครื่องหมายโคลอนที่ไวยากรณ์ป้ายกำกับกำหนดได้ ซึ่งวิธีนี้มักจะสะดวกเมื่อใช้การขยายชื่อไฟล์ Bash เช่น foo/bar/wiz มีค่าเท่ากับ //foo/bar:wiz (หากมีแพ็กเกจ foo/bar) หรือ //foo:bar/wiz (หากมีแพ็กเกจ foo)

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

bazel build foo/... bar/...

หมายถึง "สร้างเป้าหมายทั้งหมดภายใต้ foo และเป้าหมายทั้งหมดภายใต้ bar" ในขณะที่

bazel build -- foo/... -foo/bar/...

หมายความว่า "สร้างเป้าหมายทั้งหมดที่อยู่ใต้ foo ยกเว้นเป้าหมายที่อยู่ใต้ foo/bar" (ต้องใช้อาร์กิวเมนต์ -- เพื่อป้องกันไม่ให้ระบบตีความอาร์กิวเมนต์ต่อๆ ไปซึ่งขึ้นต้นด้วย - เป็นตัวเลือกเพิ่มเติม)

อย่างไรก็ตาม โปรดทราบว่าการลบเป้าหมายด้วยวิธีนี้ไม่ได้รับประกันว่าระบบจะไม่สร้างเป้าหมายดังกล่าว เนื่องจากเป้าหมายเหล่านั้นอาจเป็นทรัพยากรของเป้าหมายที่ไม่ได้ถูกลบ ตัวอย่างเช่น หากมีเป้าหมาย //foo:all-apis ที่เป้าหมายอื่นๆ ขึ้นอยู่กับ //foo/bar:api เป้าหมายหลังจะสร้างขึ้นเพื่อเป็นส่วนหนึ่งของการสร้างเป้าหมายแรก

เป้าหมายที่มี tags = ["manual"] จะไม่รวมอยู่ในรูปแบบเป้าหมายที่ใช้ไวลด์การ์ด (..., :*, :all ฯลฯ) เมื่อระบุไว้ในคำสั่ง เช่น bazel build และ bazel test คุณควรระบุเป้าหมายทดสอบดังกล่าวด้วยรูปแบบเป้าหมายที่ชัดเจนในบรรทัดคำสั่งหากต้องการให้ Bazel บิลด์/ทดสอบ ในทางตรงกันข้าม bazel query จะไม่กรองข้อมูลดังกล่าวโดยอัตโนมัติ (เพราะจะเป็นการขัดต่อวัตถุประสงค์ของ bazel query)

การดึงข้อมูลทรัพยากร Dependency ภายนอก

โดยค่าเริ่มต้น Bazel จะดาวน์โหลดและสร้างลิงก์สัญลักษณ์สำหรับทรัพยากร Dependency ภายนอกระหว่างการสร้าง อย่างไรก็ตาม การดำเนินการดังกล่าวอาจไม่เป็นที่ต้องการ เนื่องจากต้องการทราบเมื่อมีการเพิ่มทรัพยากร Dependency ภายนอกรายการใหม่เข้ามา หรือคุณต้องการ "ทรัพยากร Dependency ล่วงหน้า" (เช่น ก่อนใช้งานเที่ยวบินที่คุณจะออฟไลน์) หากต้องการป้องกันไม่ให้เพิ่มการพึ่งพาใหม่ระหว่างการสร้าง คุณสามารถระบุ Flag --fetch=false โปรดทราบว่า Flag นี้มีผลกับกฎของที่เก็บซึ่งไม่ได้ชี้ไปยังไดเรกทอรีในระบบไฟล์ในเครื่องเท่านั้น การเปลี่ยนแปลง เช่น การเปลี่ยนแปลง local_repository, new_local_repository และกฎของที่เก็บข้อมูล Android SDK และ NDK จะมีผลเสมอ ไม่ว่าค่า --fetch จะเป็นอย่างไรก็ตาม

หากคุณไม่อนุญาตให้ดึงข้อมูลระหว่างการบิลด์และ Bazel พบข้อกําหนดภายนอกใหม่ การบิลด์จะดำเนินการไม่สำเร็จ

คุณสามารถดึงข้อมูล Dependency ด้วยตนเองได้โดยเรียกใช้ bazel fetch หากไม่อนุญาตให้ดึงข้อมูลระหว่างการสร้าง คุณจะต้องเรียกใช้ bazel fetch ดังนี้

  • ก่อนสร้างครั้งแรก
  • หลังจากเพิ่มทรัพยากรภายนอกใหม่

เมื่อเรียกใช้แล้ว คุณไม่ควรเรียกใช้อีกจนกว่าไฟล์ WORKSPACE จะมีการเปลี่ยนแปลง

fetch ใช้รายการเป้าหมายเพื่อดึงข้อมูลการพึ่งพา ตัวอย่างเช่น คำสั่งนี้จะดึงข้อมูล Dependency ที่จําเป็นสําหรับการสร้าง //foo:bar และ //bar:baz

bazel fetch //foo:bar //bar:baz

หากต้องการดึงข้อมูลทรัพยากร Dependency ภายนอกทั้งหมดสำหรับพื้นที่ทำงาน ให้เรียกใช้

bazel fetch //...

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

แคชที่เก็บ

Bazel พยายามหลีกเลี่ยงการดึงข้อมูลไฟล์เดียวกันหลายครั้ง แม้ว่าจะต้องใช้ไฟล์เดียวกันในพื้นที่ทำงานอื่น หรือในกรณีที่คำจำกัดความของที่เก็บภายนอกมีการเปลี่ยนแปลง แต่ยังคงต้องใช้ไฟล์เดียวกันในการดาวน์โหลด โดย Bazel จะแคชไฟล์ทั้งหมดที่ดาวน์โหลดไว้ในแคชของที่เก็บ โดยค่าเริ่มต้นจะอยู่ที่ ~/.cache/bazel/_bazel_$USER/cache/repos/v1/ คุณเปลี่ยนตำแหน่งได้โดยตัวเลือก --repository_cache โดยจะมีการแชร์แคชระหว่างพื้นที่ทำงานทั้งหมดและ Bazel เวอร์ชันที่ติดตั้ง ระบบจะนํารายการจากแคชหาก Bazel ทราบอย่างแน่ชัดว่ามีสําเนาของไฟล์ที่ถูกต้อง กล่าวคือ หากคําขอดาวน์โหลดมีผลรวม SHA256 ของไฟล์ที่ระบุและไฟล์ที่มีแฮชนั้นอยู่ในแคช ดังนั้น การระบุแฮชสำหรับไฟล์ภายนอกแต่ละไฟล์จึงไม่ใช่แค่แนวคิดที่ดีจากมุมมองด้านความปลอดภัยเท่านั้น แต่ยังช่วยหลีกเลี่ยงการดาวน์โหลดที่ไม่จำเป็นด้วย

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

ไดเรกทอรีไฟล์สำหรับเผยแพร่

ไดเรกทอรีการเผยแพร่เป็นกลไกอีกอย่างหนึ่งของ Bazel เพื่อหลีกเลี่ยงการดาวน์โหลดที่ไม่จำเป็น Bazel จะค้นหาไดเรกทอรีการกระจายก่อนแคชของที่เก็บ ความแตกต่างหลักๆ คือไดเรกทอรีการเผยแพร่ต้องเตรียมด้วยตนเอง

เมื่อใช้ตัวเลือก --distdir=/path/to-directory คุณจะระบุไดเรกทอรีแบบอ่านอย่างเดียวเพิ่มเติมเพื่อค้นหาไฟล์แทนการดึงข้อมูลได้ จะมีการนำไฟล์มาจากไดเรกทอรีดังกล่าวหากชื่อไฟล์เท่ากับชื่อฐานของ URL และนอกจากนี้แฮชของไฟล์จะเท่ากับค่าที่ระบุไว้ในคำขอดาวน์โหลด การดำเนินการนี้จะใช้ได้เมื่อมีการระบุแฮชของไฟล์ในการประกาศ WORKSPACE เท่านั้น

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

เรียกใช้ Bazel ในสภาพแวดล้อมที่มีการแยกเครือข่าย

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

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

หากต้องการเตรียมไดเรกทอรีการเผยแพร่ ให้ใช้ตัวเลือก--distdir คุณจะต้องทําเช่นนี้ 1 ครั้งสําหรับไบนารี Bazel เวอร์ชันใหม่ทุกเวอร์ชัน เนื่องจากความเกี่ยวข้องโดยนัยอาจแตกต่างกันไปในแต่ละรุ่น

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

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

จากนั้นสร้าง tarball ที่มีข้อกำหนดเบื้องต้นรันไทม์โดยนัยสำหรับ Bazel เวอร์ชันที่เฉพาะเจาะจงนั้น โดยทำดังนี้

bazel build @additional_distfiles//:archives.tar

ส่งออก tarball นี้ไปยังไดเรกทอรีที่คัดลอกไปยังสภาพแวดล้อมที่มีการแยกเครือข่ายได้ โปรดสังเกต Flag --strip-components เนื่องจาก --distdir อาจทำงานได้ไม่ดีนักกับระดับการซ้อนไดเรกทอรี

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

สุดท้าย เมื่อใช้ Bazel ในสภาพแวดล้อมที่มีการป้องกันอากาศ ให้ส่ง Flag --distdir ที่ชี้ไปยังไดเรกทอรี คุณสามารถเพิ่มเป็น.bazelrc รายการได้เพื่อความสะดวก

build --distdir=path/to/directory

การกำหนดค่าบิลด์และการคอมไพล์ข้าม

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

อาจมีการกำหนดค่ามากกว่า 1 รายการในบิลด์หนึ่งๆ ลองใช้เครื่องมือคอมไพล์แบบครอสคอมไพล์เพื่อสร้างไฟล์ปฏิบัติการ //foo:bin สำหรับสถาปัตยกรรมแบบ 64 บิต แต่เวิร์กสเตชันของคุณเป็นเครื่องแบบ 32 บิต แน่นอนว่าการสร้างจะต้องสร้าง //foo:bin โดยใช้ชุดเครื่องมือที่สามารถสร้างไฟล์ปฏิบัติการ 64 บิต แต่ระบบการสร้างยังต้องสร้างเครื่องมือต่างๆ ที่ใช้ในการสร้างด้วย เช่น เครื่องมือที่สร้างจากซอร์สโค้ด จากนั้นนำไปใช้ใน genrule และเครื่องมือเหล่านี้ต้องสร้างขึ้นให้ทำงานบนเวิร์กสเตชันได้ ดังนั้น เราจึงระบุการกําหนดค่าได้ 2 รายการ ได้แก่ การกําหนดค่า exec ซึ่งใช้สําหรับการสร้างเครื่องมือที่ทํางานระหว่างการบิลด์ และการกําหนดค่าเป้าหมาย (หรือการกําหนดค่าคําขอ แต่เราใช้คำว่า "การกําหนดค่าเป้าหมาย" บ่อยกว่า แม้ว่าคําดังกล่าวจะมีความหมายหลายอย่างอยู่แล้ว) ซึ่งใช้สําหรับการสร้างไบนารีที่คุณขอในท้ายที่สุด

โดยทั่วไปแล้วจะมีไลบรารีจำนวนมากที่เป็นข้อกำหนดเบื้องต้นของทั้งเป้าหมายบิลด์ (//foo:bin) ที่ขอ และเครื่องมืออย่างน้อย 1 รายการ เช่น ไลบรารีพื้นฐานบางรายการ ไลบรารีดังกล่าวต้องสร้างขึ้นหลายครั้งสําหรับการกําหนดค่า exec และ target Bazel ดูแลให้มั่นใจว่ามีการสร้างตัวแปรทั้งหมด และไฟล์ที่ดึงมาจะแยกออกจากกันเพื่อหลีกเลี่ยงการรบกวน โดยทั่วไปเป้าหมายดังกล่าวสามารถสร้างพร้อมกัน เนื่องจากเป้าหมายทั้งสองจะแยกจากกัน หากเห็นข้อความความคืบหน้าที่ระบุว่ามีการสร้างเป้าหมายหนึ่งๆ หลายครั้ง สาเหตุนี้อาจเป็นคำอธิบายที่เป็นไปได้มากที่สุด

การกำหนดค่า exec จะมาจากการกำหนดค่าเป้าหมาย ดังนี้

  • แพลตฟอร์มการเรียกใช้สําหรับเป้าหมายคําขอจะกลายเป็นแพลตฟอร์มเป้าหมายสําหรับการกําหนดค่าการเรียกใช้
  • ใช้ Crosstool (--crosstool_top) เวอร์ชันเดียวกับที่ระบุในการกําหนดค่าคําขอ เว้นแต่จะมีการระบุ --host_crosstool_top
  • ใช้ค่าของ --host_cpu สำหรับ --cpu (ค่าเริ่มต้น: k8)
  • ใช้ค่าของตัวเลือกเหล่านี้เหมือนกับที่ระบุในการกำหนดค่าคําขอ --compiler, --use_ijars และหากใช้ --host_crosstool_top ระบบจะใช้ค่าของ --host_cpu เพื่อค้นหา default_toolchain ใน Crosstool (ไม่สนใจ --compiler) สําหรับการกําหนดค่าโฮสต์
  • ใช้ค่า --host_javabase สําหรับ --javabase
  • ใช้ค่า --host_java_toolchain สําหรับ --java_toolchain
  • ใช้บิลด์ที่เพิ่มประสิทธิภาพสำหรับโค้ด C++ (-c opt)
  • ไม่สร้างข้อมูลการแก้ไขข้อบกพร่อง (--copt=-g0)
  • นำข้อมูลการแก้ไขข้อบกพร่องออกจากไฟล์ปฏิบัติการและไลบรารีที่ใช้ร่วมกัน (--strip=always)
  • วางไฟล์ที่ดึงข้อมูลทั้งหมดไว้ในตำแหน่งพิเศษ ซึ่งแตกต่างจากตำแหน่งที่ใช้โดยการกำหนดค่าคำขอที่เป็นไปได้
  • ระงับการประทับเวลาของไบนารีด้วยข้อมูลบิลด์ (ดูตัวเลือก --embed_*)
  • ค่าอื่นๆ ทั้งหมดจะยังเป็นค่าเริ่มต้น

แก้ไขการสร้างใหม่แบบเพิ่มทีละส่วน

เป้าหมายหลักอย่างหนึ่งของโปรเจ็กต์ Bazel คือการสร้างใหม่แบบเพิ่มทีละน้อยที่ถูกต้อง เครื่องมือสร้างก่อนหน้านี้ โดยเฉพาะเครื่องมือที่สร้างจาก Make มีการสมมติฐานที่ไม่ถูกต้องหลายประการในการใช้งานการสร้างแบบเพิ่ม

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

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

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

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

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

สร้างความสอดคล้องและบิลด์ที่เพิ่มขึ้น

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

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

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

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

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

หากตรวจพบสถานะที่ไม่สอดคล้องกันซึ่งเสถียรกับ Bazel โปรดรายงานข้อบกพร่อง

การดำเนินการในโหมดแซนด์บ็อกซ์

Bazel ใช้แซนด์บ็อกซ์เพื่อรับประกันว่าการดำเนินการจะทำงานอย่างถูกต้องและปลอดภัย Bazel จะเรียกใช้การสร้าง (พูดง่ายๆ คือการดำเนินการ) ในแซนด์บ็อกซ์ที่มีเฉพาะชุดไฟล์ขั้นต่ำที่เครื่องมือต้องใช้ในการทำงาน ปัจจุบัน Sandboxing ใช้งานได้ใน Linux 3.12 ขึ้นไปที่เปิดใช้ตัวเลือก CONFIG_USER_NS และใน macOS 10.11 ขึ้นไป

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

ในแพลตฟอร์มบางแพลตฟอร์ม เช่น โหนดคลัสเตอร์ Google Kubernetes Engine หรือ Debian ระบบจะปิดใช้งานเนมสเปซของผู้ใช้โดยค่าเริ่มต้นเนื่องจากข้อกังวลด้านความปลอดภัย ตรวจสอบได้โดยดูที่ไฟล์ /proc/sys/kernel/unprivileged_userns_clone: หากมีไฟล์ดังกล่าวและมี 0 อยู่ แสดงว่าเปิดใช้งานเนมสเปซของผู้ใช้ได้ด้วย sudo sysctl kernel.unprivileged_userns_clone=1

ในบางกรณี Sandbox ของ Bazel ไม่สามารถเรียกใช้กฎได้เนื่องจากการตั้งค่าระบบ โดยทั่วไป อาการคือความล้มเหลวที่แสดงข้อความคล้ายกับ namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory ในกรณีนี้ ให้ลองปิดใช้งานแซนด์บ็อกซ์สำหรับ Genrule ที่มี --strategy=Genrule=standalone และกฎอื่นๆ ที่มี --spawn_strategy=standalone นอกจากนี้ โปรดรายงานข้อบกพร่องในเครื่องมือติดตามปัญหา และระบุว่าคุณใช้ Linux Distribution ใดอยู่เพื่อให้เราตรวจสอบและแก้ไขข้อบกพร่องในรุ่นต่อๆ ไปได้

ระยะต่างๆ ของการสร้าง

ใน Bazel การสร้างจะเกิดขึ้นใน 3 ระยะที่แตกต่างกัน ในฐานะผู้ใช้ การทำความเข้าใจความแตกต่างระหว่างระยะเหล่านี้จะช่วยให้คุณทราบข้อมูลเชิงลึกเกี่ยวกับตัวเลือกต่างๆ ที่ควบคุมการสร้าง (ดูด้านล่าง)

ระยะการโหลด

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

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

ข้อผิดพลาดที่รายงานในระยะนี้ ได้แก่ ไม่พบแพ็กเกจ ไม่พบเป้าหมาย ข้อผิดพลาดด้านคําศัพท์และไวยากรณ์ในไฟล์ BUILD และข้อผิดพลาดในการประเมิน

ระยะการวิเคราะห์

ระยะที่ 2 คือการวิเคราะห์ ซึ่งเกี่ยวข้องกับการวิเคราะห์เชิงความหมายและการตรวจสอบกฎการสร้างแต่ละข้อ การสร้างกราฟความเกี่ยวข้องของบิลด์ และการกำหนดสิ่งที่ต้องทําในแต่ละขั้นตอนของการสร้าง

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

ข้อผิดพลาดที่รายงานในระยะนี้ ได้แก่ Dependency ที่ไม่เหมาะสม อินพุตที่ไม่ถูกต้องของกฎ และข้อความแสดงข้อผิดพลาดเฉพาะกฎทั้งหมด

ระยะการโหลดและการวิเคราะห์จะรวดเร็วเนื่องจาก Bazel หลีกเลี่ยง I/O ของไฟล์ที่ไม่จำเป็นในระยะนี้ โดยจะอ่านเฉพาะไฟล์ BUILD เพื่อกำหนดงานที่จะทำ การดำเนินการนี้เป็นไปตามการออกแบบและทำให้ Bazel เป็นรากฐานที่ดีสำหรับเครื่องมือวิเคราะห์ เช่น คำสั่ง query ของ Bazel ซึ่งติดตั้งใช้งานในเฟสการโหลด

ระยะการดําเนินการ

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