บทแนะนำนี้ครอบคลุมพื้นฐานการสร้างแอปพลิเคชัน 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
จะสร้างอินสแตนซ์กฎ java_binary
ในตัวของ Bazel กฎนี้จะบอก 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 ไฟล์โดยไม่มีไลบรารีเพิ่มเติม
หลังจากตั้งค่าพื้นที่ทำงานแล้ว ให้สร้างโปรเจ็กต์และตรวจสอบการขึ้นต่อกัน จากนั้นคุณอาจเพิ่มความซับซ้อนบางอย่างได้
ปรับแต่งบิลด์ 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 จะคอมไพล์เฉพาะไฟล์นั้นซ้ำเท่านั้น
เมื่อดูที่กราฟความเกี่ยวข้อง คุณจะเห็นได้ว่า ProjectRunner
ขึ้นอยู่กับอินพุตเดียวกันกับก่อนหน้านี้ แต่โครงสร้างของบิลด์จะแตกต่างกัน
ตอนนี้คุณสร้างโปรเจ็กต์ที่มีเป้าหมาย 2 รายการแล้ว เป้าหมาย ProjectRunner
จะสร้างไฟล์ต้นฉบับ 2 ไฟล์และขึ้นอยู่กับเป้าหมายอื่น 1 รายการ (: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 แพ็กเกจ โดยแต่ละแพ็กเกจมี 1 เป้าหมาย และทำความเข้าใจทรัพยากร 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
แต่ไม่มี Greeting.class
ซึ่งเป็นข้อกำหนด สคริปต์ runner
ที่ Bazel สร้างขึ้นจะเพิ่ม greeter.jar
ลงใน classpath ดังนั้นหากปล่อยไว้เช่นนี้ สคริปต์จะทำงานในเครื่อง แต่จะไม่ทำงานแบบสแตนด์อโลนในเครื่องอื่น โชคดีที่กฎ 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
ซึ่งสามารถเรียกใช้ได้แบบสแตนด์อโลนจากสภาพแวดล้อมการพัฒนา เนื่องจากมีไลบรารีรันไทม์ที่จำเป็น ดูเนื้อหาของ 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
ขอให้สนุกกับการสร้าง