บทแนะนํานี้ครอบคลุมพื้นฐานการสร้างแอปพลิเคชัน Java ด้วย Bazel คุณสามารถตั้งค่าพื้นที่ทํางานและสร้างโปรเจ็กต์ Java ที่เรียบง่ายซึ่งอธิบายแนวคิด Bazel ที่สําคัญ เช่น เป้าหมายและไฟล์ BUILD
เวลาที่ใช้ดําเนินการโดยประมาณ: 30 นาที
สิ่งที่คุณจะได้เรียนรู้
ในบทแนะนํานี้ คุณจะได้ทราบวิธีทําสิ่งต่อไปนี้
- สร้างเป้าหมาย
- แสดงภาพทรัพยากร Dependency ของโปรเจ็กต์
- แยกโปรเจ็กต์ออกเป็นเป้าหมายและแพ็กเกจหลายรายการ
- ควบคุมระดับการเข้าถึงเป้าหมายบนแพ็กเกจต่างๆ
- อ้างอิงเป้าหมายผ่านป้ายกํากับ
- ทําให้เป้าหมายใช้งานได้
ก่อนเริ่มต้น
ติดตั้ง Bazel
หากต้องการเตรียมพร้อมสําหรับบทแนะนํา ให้ติดตั้ง Bazel ก่อนหากยังไม่ได้ติดตั้ง
ติดตั้ง JDK
ติดตั้ง Java JDK (เวอร์ชันที่แนะนําคือ 11 แต่รองรับเวอร์ชันระหว่าง 8 ถึง 15 รายการ)
ตั้งค่าตัวแปรสภาพแวดล้อม JAVA_HOME ให้ชี้ไปที่ JDK
ใน Linux/macOS
export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
บน Windows:
- เปิดแผงควบคุม
- ไปที่ "ระบบและความปลอดภัย" > "ระบบ" > "การตั้งค่าระบบขั้นสูง" > แท็บ "ขั้นสูง" > "ตัวแปรสภาพแวดล้อม..."
- คลิก "ใหม่..." ในส่วน "ตัวแปรผู้ใช้" (ตัวแปรด้านบนสุด)
- ป้อน
JAVA_HOME
ในช่อง "ชื่อตัวแปร" - คลิก "เรียกดูไดเรกทอรี..."
- ไปที่ไดเรกทอรี JDK (เช่น
C:\Program Files\Java\jdk1.8.0_152
) - คลิก "ตกลง" ในหน้าต่างกล่องโต้ตอบทั้งหมด
รับโปรเจ็กต์ตัวอย่าง
เรียกข้อมูลโปรเจ็กต์ตัวอย่างจากที่เก็บ GitHub ของ Bazel
git clone https://github.com/bazelbuild/examples
โปรเจ็กต์ตัวอย่างสําหรับบทแนะนํานี้อยู่ในไดเรกทอรี examples/java-tutorial
และมีโครงสร้างดังนี้
java-tutorial
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── WORKSPACE
สร้างด้วย Bazel
ตั้งค่าพื้นที่ทํางาน
คุณต้องตั้งค่าพื้นที่ทํางานก่อนจึงจะสร้างโปรเจ็กต์ได้ พื้นที่ทํางานคือไดเรกทอรีที่ใช้เก็บไฟล์แหล่งที่มาของโปรเจ็กต์และเอาต์พุตบิวด์ของ Bazel และยังมีไฟล์ที่ Bazel ระบุว่าพิเศษอีกด้วย
ไฟล์
WORKSPACE
ที่ระบุไดเรกทอรีและเนื้อหาในไดเรกทอรีเป็นพื้นที่ทํางาน Bazel และอยู่ที่รากของโครงสร้างไดเรกทอรีของโปรเจ็กต์ไฟล์
BUILD
อย่างน้อย 1 ไฟล์ซึ่งบอก Bazel เกี่ยวกับวิธีสร้างส่วนต่างๆ ของโปรเจ็กต์ (ไดเรกทอรีภายในพื้นที่ทํางานที่มีไฟล์BUILD
คือแพ็กเกจ คุณจะได้ดูข้อมูลเกี่ยวกับแพ็กเกจในภายหลังในบทแนะนํานี้)
หากต้องการกําหนดไดเรกทอรีเป็นพื้นที่ทํางาน Bazel ให้สร้างไฟล์เปล่าที่ชื่อ WORKSPACE
ในไดเรกทอรีนั้น
เมื่อ Bazel สร้างโปรเจ็กต์ อินพุตและทรัพยากร Dependency ทั้งหมดต้องอยู่ในพื้นที่ทํางานเดียวกัน ไฟล์ที่อาศัยอยู่ในพื้นที่ทํางานที่แตกต่างกันจะอิสระจากกัน เว้นแต่จะลิงก์อยู่ ซึ่งอยู่นอกเหนือขอบเขตของบทแนะนํานี้
ทําความเข้าใจไฟล์ BUILD
ไฟล์ BUILD
ประกอบด้วยวิธีการต่างๆ สําหรับ Bazel
ประเภทที่สําคัญที่สุดคือกฎการสร้าง ซึ่งบอก Bazel ในการสร้างเอาต์พุตที่ต้องการ เช่น ไบนารีปฏิบัติการหรือไลบรารี อินสแตนซ์แต่ละรายการของกฎบิวด์ในไฟล์ BUILD
เรียกว่าเป้าหมาย และชี้ไปยังชุดไฟล์ต้นฉบับและทรัพยากร Dependency เป้าหมายยังชี้ไปที่เป้าหมายอื่นๆ ได้ด้วย
ดูที่ไฟล์ java-tutorial/BUILD
:
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
ในตัวอย่างของเรา ProjectRunner
เป้าหมายจะกลายเป็นคําสั่งในตัวของ Bazel
java_binary
กฎ กฎจะบอก Bazel ในการสร้างไฟล์ .jar
และสคริปต์เชือก Wrapper (ทั้งคู่ตั้งชื่อตามเป้าหมาย)
แอตทริบิวต์ในเป้าหมายจะระบุทรัพยากร Dependency และตัวเลือกไว้อย่างชัดเจน
แม้ว่าคุณจะต้องระบุแอตทริบิวต์ name
แต่แอตทริบิวต์หลายรายการก็ไม่บังคับ ตัวอย่างเช่น ในเป้าหมาย ProjectRunner
ของกฎ name
คือชื่อของเป้าหมาย srcs
จะระบุไฟล์แหล่งที่มาที่ Bazel ใช้เพื่อสร้างเป้าหมาย และ main_class
จะระบุคลาสที่มีเมธอดหลัก (คุณอาจสังเกตเห็นว่าตัวอย่างของเราใช้ glob เพื่อส่งชุดไฟล์ต้นฉบับไปยัง Bazel แทนที่จะแสดงทีละไฟล์)
สร้างโครงการ
หากต้องการสร้างโปรเจ็กต์ตัวอย่าง ให้ไปที่ไดเรกทอรี java-tutorial
แล้วเรียกใช้
bazel build //:ProjectRunner
ในป้ายกํากับเป้าหมาย ส่วน //
คือตําแหน่งของไฟล์ BUILD
ที่เกี่ยวข้องกับรากของพื้นที่ทํางาน (ในกรณีนี้คือรากเอง) และ ProjectRunner
เป็นชื่อเป้าหมายในไฟล์ BUILD
(ดูข้อมูลเกี่ยวกับป้ายกํากับเป้าหมายได้โดยละเอียดที่ตอนท้ายของบทแนะนํานี้)
Bazel จะสร้างเอาต์พุตในลักษณะนี้
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 1.021s, Critical Path: 0.83s
ขอแสดงความยินดี คุณเพิ่งสร้างเป้าหมายแรกของ Bazel Bazel จะสร้างเอาต์พุต
ในไดเรกทอรี bazel-bin
ที่รูทของพื้นที่ทํางาน เรียกดูเนื้อหาเพื่อดูแนวคิดเกี่ยวกับโครงสร้างเอาต์พุตของ Bazel
คราวนี้ทดสอบไบนารีที่สร้างขึ้นใหม่
bazel-bin/ProjectRunner
ตรวจสอบกราฟทรัพยากร Dependency
Bazel ต้องมีการประกาศทรัพยากร Dependency สําหรับการประกาศในไฟล์ BUILD Bazel ใช้คําสั่งเหล่านั้นเพื่อสร้างกราฟทรัพยากร Dependency ของโปรเจ็กต์ ซึ่งช่วยให้บิลด์ที่เพิ่มขึ้นที่แม่นยํา
หากต้องการดูภาพทรัพยากร Dependency ของโปรเจ็กต์ตัวอย่าง คุณสามารถสร้างการนําเสนอข้อความในรูปแบบกราฟทรัพยากร Dependency ได้โดยเรียกใช้คําสั่งนี้ที่รูทของพื้นที่ทํางาน
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph
คําสั่งข้างต้นจะบอก Bazel ให้หาทรัพยากร Dependency ทั้งหมดสําหรับเป้าหมาย
//:ProjectRunner
(ยกเว้นทรัพยากร Dependency ของโฮสต์และโดยนัย) แล้วจัดรูปแบบเอาต์พุตเป็นกราฟ
จากนั้นวางข้อความใน GraphViz
คุณจะเห็นได้ว่าโปรเจ็กต์มีเป้าหมายเดียวในการสร้างไฟล์แหล่งที่มา 2 ไฟล์ โดยไม่มีทรัพยากร Dependency เพิ่มเติม ซึ่งประกอบด้วย
หลังจากตั้งค่าพื้นที่ทํางาน สร้างโปรเจ็กต์ และตรวจสอบทรัพยากร Dependency แล้ว คุณจะเพิ่มความซับซ้อนได้
ปรับแต่งบิลด์ Bazel
แม้ว่าเป้าหมายเดียวจะเพียงพอสําหรับโปรเจ็กต์เล็ก แต่คุณอาจต้องการแบ่งโปรเจ็กต์ขนาดใหญ่ออกเป็นเป้าหมายและแพ็กเกจหลายรายการ เพื่อให้สามารถสร้างเพิ่มได้อย่างรวดเร็ว (กล่าวคือ สร้างอีกครั้งเฉพาะสิ่งที่เปลี่ยนแปลง) และเร่งความเร็วบิลด์โดยสร้างหลายส่วนของโปรเจ็กต์พร้อมกัน
ระบุเป้าหมายบิลด์หลายรายการ
คุณสามารถแบ่งโครงสร้างโปรเจ็กต์ตัวอย่างออกเป็น 2 เป้าหมาย แทนที่เนื้อหาของไฟล์ java-tutorial/BUILD
ด้วยข้อมูลต่อไปนี้
java_binary(
name = "ProjectRunner",
srcs = ["src/main/java/com/example/ProjectRunner.java"],
main_class = "com.example.ProjectRunner",
deps = [":greeter"],
)
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
)
เมื่อกําหนดค่านี้ Bazel จะสร้างไลบรารี greeter
ก่อน ตามด้วยไบนารี ProjectRunner
แอตทริบิวต์ deps
ใน java_binary
จะบอก Bazel ว่า
ต้องมีไลบรารี greeter
เพื่อสร้างไบนารี ProjectRunner
ในการสร้างโปรเจ็กต์เวอร์ชันใหม่นี้ ให้เรียกใช้คําสั่งต่อไปนี้
bazel build //:ProjectRunner
Bazel จะสร้างเอาต์พุตในลักษณะนี้
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s
คราวนี้ทดสอบไบนารีที่สร้างขึ้นใหม่
bazel-bin/ProjectRunner
หากคุณแก้ไข ProjectRunner.java
และสร้างโปรเจ็กต์อีกครั้ง Bazel จะรวบรวมไฟล์ดังกล่าวอีกครั้งเท่านั้น
เมื่อดูกราฟทรัพยากร Dependency คุณจะพบว่า ProjectRunner
จะขึ้นอยู่กับอินพุตเดิมเหมือนที่ผ่านมา แต่โครงสร้างของโครงสร้างนั้นต่างออกไป ดังนี้
ตอนนี้คุณสร้างโปรเจ็กต์ที่มีเป้าหมาย 2 ข้อแล้ว เป้าหมาย ProjectRunner
จะสร้างไฟล์แหล่งที่มา 2 ไฟล์ และขึ้นอยู่กับเป้าหมายอื่น (:greeter
) ซึ่งจะสร้างไฟล์ต้นฉบับเพิ่มเติม 1 ไฟล์
ใช้แพ็กเกจหลายรายการ
ตอนนี้เราจะแบ่งโปรเจ็กต์ออกเป็นหลายแพ็กเกจ หากดูไดเรกทอรี src/main/java/com/example/cmdline
คุณจะเห็นว่ามีไฟล์ BUILD
รวมถึงไฟล์ต้นฉบับบางไฟล์ด้วย ดังนั้น สําหรับ Bazel ตอนนี้พื้นที่ทํางานจึงมี 2 แพ็กเกจ คือ //src/main/java/com/example/cmdline
และ //
(เนื่องจากมีไฟล์ BUILD
ตั้งอยู่ที่รูทของพื้นที่ทํางาน)
ดูที่ไฟล์ src/main/java/com/example/cmdline/BUILD
:
java_binary(
name = "runner",
srcs = ["Runner.java"],
main_class = "com.example.cmdline.Runner",
deps = ["//:greeter"],
)
เป้าหมาย runner
จะขึ้นอยู่กับเป้าหมาย greeter
ในแพ็กเกจ //
(ป้ายกํากับเป้าหมาย //:greeter
) - Bazel จะทราบข้อมูลนี้ผ่านแอตทริบิวต์ deps
โปรดดูกราฟทรัพยากร Dependency
อย่างไรก็ตาม คุณต้องกําหนดเป้าหมาย runner
ใน//src/main/java/com/example/cmdline/BUILD
ให้กับเป้าหมายใน //BUILD
อย่างชัดเจนโดยใช้แอตทริบิวต์ visibility
เพื่อให้บิลด์ประสบความสําเร็จ เนื่องจากเป้าหมายเริ่มต้นจะแสดงต่อเป้าหมายอื่นๆ ในไฟล์ BUILD
เดียวกันเท่านั้น (Bazel ใช้ระดับการเข้าถึงเป้าหมายเพื่อป้องกันปัญหาต่างๆ เช่น ไลบรารีที่มีรายละเอียดการนําไปใช้งาน API สาธารณะ)
โดยเพิ่มแอตทริบิวต์ visibility
ลงในเป้าหมาย greeter
ใน java-tutorial/BUILD
ตามที่แสดงด้านล่าง
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)
ตอนนี้คุณสามารถสร้างแพ็กเกจใหม่ได้โดยเรียกใช้คําสั่งต่อไปนี้ที่รูทของพื้นที่ทํางาน
bazel build //src/main/java/com/example/cmdline:runner
Bazel จะสร้างเอาต์พุตในลักษณะนี้
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner.jar
bazel-bin/src/main/java/com/example/cmdline/runner
INFO: Elapsed time: 1.576s, Critical Path: 0.81s
คราวนี้ทดสอบไบนารีที่สร้างขึ้นใหม่
./bazel-bin/src/main/java/com/example/cmdline/runner
ตอนนี้คุณสามารถแก้ไขโปรเจ็กต์เพื่อสร้างเป็นแพ็กเกจ 2 แพ็กเกจ โดยแต่ละแพ็กเกจมีเป้าหมายเดียว และเข้าใจทรัพยากร Dependency ระหว่างแพ็กเกจเหล่านั้น
ใช้ป้ายกํากับเพื่ออ้างอิงเป้าหมาย
ในไฟล์ BUILD
และที่บรรทัดคําสั่ง Bazel จะใช้ป้ายกํากับเป้าหมายเพื่ออ้างอิงเป้าหมาย เช่น //:ProjectRunner
หรือ //src/main/java/com/example/cmdline:runner
ชุดคําสั่งตามรูปแบบไวยากรณ์มีดังนี้
//path/to/package:target-name
หากเป้าหมายเป็นเป้าหมายของกฎ path/to/package
คือเส้นทางไปยังไดเรกทอรีที่มีไฟล์ BUILD
และ target-name
คือสิ่งที่คุณตั้งชื่อเป้าหมายในไฟล์ BUILD
(แอตทริบิวต์ name
) หากเป้าหมายคือไฟล์เป้าหมาย path/to/package
จะเป็นเส้นทางไปยังรากของแพ็กเกจ และ target-name
คือชื่อของไฟล์เป้าหมาย รวมถึงเส้นทางแบบเต็ม
เมื่ออ้างอิงเป้าหมายที่รูทของที่เก็บ เส้นทางแพ็กเกจว่างเปล่า ให้ใช้ //:target-name
เมื่ออ้างอิงเป้าหมายภายในไฟล์ BUILD
เดียวกัน คุณสามารถข้ามตัวระบุรากของพื้นที่ทํางาน //
และจะใช้เพียง :target-name
เช่น สําหรับเป้าหมายในไฟล์ java-tutorial/BUILD
คุณไม่จําเป็นต้องระบุเส้นทางแพ็กเกจ เนื่องจากรูทของพื้นที่ทํางานเป็นแพ็กเกจ (//
) และป้ายกํากับเป้าหมาย 2 รายการเป็นเพียง //:ProjectRunner
และ //:greeter
แต่สําหรับเป้าหมายในไฟล์ //src/main/java/com/example/cmdline/BUILD
คุณจะต้องระบุเส้นทางแพ็กเกจแบบเต็มของ //src/main/java/com/example/cmdline
และป้ายกํากับเป้าหมายคือ //src/main/java/com/example/cmdline:runner
จัดแพ็กเกจเป้าหมาย Java สําหรับการติดตั้งใช้งาน
มาเริ่มจัดกลุ่มเป้าหมาย Java เพื่อการทําให้ใช้งานได้โดยการสร้างไบนารีด้วยทรัพยากร Dependency แบบรันไทม์ทั้งหมด ซึ่งจะช่วยให้คุณเรียกใช้ไบนารีนอกสภาพแวดล้อมการพัฒนาได้
โดยอย่าลืมว่ากฎบิวด์ java_binary จะสร้าง .jar
และสคริปต์เชือก Wrapper ดูเนื้อหาของ runner.jar
โดยใช้คําสั่งนี้
jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar
เนื้อหามีดังต่อไปนี้
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
คุณจะเห็นได้ว่า runner.jar
มี Runner.class
แต่ไม่ใช่ทรัพยากร Dependency อีกต่อไป
Greeting.class
สคริปต์ runner
ที่ Bazel สร้างจะเพิ่ม greeter.jar
ลงในคลาสเส้นทาง ดังนั้นหากคุณปล่อยไว้แบบนี้ สคริปต์ก็จะทํางานในเครื่อง แต่จะไม่เรียกใช้แบบสแตนด์อโลนในเครื่องอื่น แต่โชคดีที่กฎ java_binary
ช่วยให้คุณสร้างไบนารีแบบใช้งานได้ในตัวซึ่งสามารถทําให้ใช้งานได้ ในการสร้าง ให้เพิ่ม _deploy.jar
ต่อท้ายชื่อเป้าหมาย
bazel build //src/main/java/com/example/cmdline:runner_deploy.jar
Bazel จะสร้างเอาต์พุตในลักษณะนี้
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s
คุณเพิ่งสร้าง runner_deploy.jar
ซึ่งสามารถเรียกใช้แบบสแตนด์อโลนจากสภาพแวดล้อมการพัฒนาได้เนื่องจากมีทรัพยากร Dependency ที่จําเป็นอยู่ ลองดูเนื้อหาของ JAR แบบสแตนด์อโลนนี้โดยใช้คําสั่งเดียวกันเหมือนก่อนหน้านี้
jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
เนื้อหามีชั้นเรียนที่จําเป็นทั้งหมดให้ทํางานดังนี้
META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class
อ่านเพิ่มเติม
โปรดดูรายละเอียดเพิ่มเติมได้จากหัวข้อต่อไปนี้
rules_jvm_external สําหรับกฎที่ใช้จัดการทรัพยากร Dependency ของ Maven
ทรัพยากร Dependency ภายนอกเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับการทํางานกับที่เก็บในเครื่องและระยะไกล
กฎอื่นๆ เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับ Bazel
บทแนะนําการสร้าง C++ เพื่อเริ่มต้นใช้งานโปรเจ็กต์ C++ ด้วย Bazel
บทแนะนําแอปพลิเคชัน Android และบทแนะนําแอปพลิเคชัน iOS) เพื่อเริ่มต้นใช้งานการสร้างแอปพลิเคชันบนอุปกรณ์เคลื่อนที่สําหรับ Android และ iOS ด้วย Bazel
ขอให้สนุกกับการสร้าง