บทแนะนํานี้จะอธิบายพื้นฐานการสร้างแอปพลิเคชัน 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
└── MODULE.bazel
บิลด์ด้วย Bazel
ตั้งค่าพื้นที่ทํางาน
คุณต้องตั้งค่าพื้นที่ทํางานก่อนจึงจะสร้างโปรเจ็กต์ได้ เวิร์กช็อปคือไดเรกทอรีที่มีไฟล์ต้นฉบับของโปรเจ็กต์และเอาต์พุตการสร้างของ Bazel รวมถึงไฟล์ที่ Bazel รู้จักว่าเป็นไฟล์พิเศษ
ไฟล์
MODULE.bazelซึ่งระบุไดเรกทอรีและเนื้อหาว่าเป็นเวิร์กสเปซ Bazel และอยู่ในรูทของโครงสร้างไดเรกทอรีของโปรเจ็กต์ไฟล์
BUILDอย่างน้อย 1 ไฟล์ ซึ่งบอก Bazel ว่าจะสร้างส่วนต่างๆ ของโปรเจ็กต์อย่างไร (ไดเรกทอรีภายในพื้นที่ทำงานที่มีไฟล์BUILDแพ็กเกจ คุณจะได้เรียนรู้เกี่ยวกับแพ็กเกจในบทแนะนำนี้ในภายหลัง)
หากต้องการกำหนดไดเรกทอรีเป็นพื้นที่ทำงาน Bazel ให้สร้างไฟล์ว่างชื่อ MODULE.bazel ในไดเรกทอรีนั้น
เมื่อ Bazel บิลด์โปรเจ็กต์ อินพุตและทรัพยากร Dependency ทั้งหมดต้องอยู่ในเวิร์กスペースเดียวกัน ไฟล์ที่อยู่ในพื้นที่ทำงานต่างๆ จะแยกจากกัน เว้นแต่จะมีการลิงก์ ซึ่งอยู่นอกเหนือขอบเขตของบทแนะนำนี้
ทำความเข้าใจไฟล์ BUILD
ไฟล์ BUILD มีคำสั่งหลายประเภทสำหรับ Bazel
ประเภทที่สำคัญที่สุดคือกฎการสร้าง ซึ่งบอก Bazel ว่าจะสร้างเอาต์พุตที่ต้องการอย่างไร เช่น ไฟล์ปฏิบัติการหรือไลบรารี อินสแตนซ์แต่ละรายการของกฎการสร้างในไฟล์ BUILD เรียกว่าเป้าหมายและชี้ไปยังชุดไฟล์ต้นทางและทรัพยากรที่เกี่ยวข้อง เป้าหมายหนึ่งๆ ยังชี้ไปยังเป้าหมายอื่นๆ ได้ด้วย
โปรดดูไฟล์ java-tutorial/BUILD
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
ในตัวอย่างของเรา เป้าหมาย ProjectRunner จะสร้างอินสแตนซ์กฎ java_binary ในตัวของ Bazel กฎนี้บอกให้ Bazel สร้างขึ้นไฟล์ .jar และสคริปต์เชลล์ที่ใช้ห่อ (ทั้ง 2 รายการตั้งชื่อตามเป้าหมาย)
แอตทริบิวต์ในเป้าหมายจะระบุการพึ่งพาและตัวเลือกอย่างชัดเจน
แม้ว่าแอตทริบิวต์ name จะเป็นสิ่งที่ต้องระบุ แต่แอตทริบิวต์อื่นๆ หลายรายการเป็นค่าที่ไม่บังคับ ตัวอย่างเช่น ใน ProjectRunner rule target 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 กำหนดให้ต้องประกาศการพึ่งพาของบิลด์อย่างชัดเจนในไฟล์ BUILD Bazel ใช้คำสั่งเหล่านั้นเพื่อสร้างกราฟความเกี่ยวข้องของโปรเจ็กต์ ซึ่งช่วยให้การบิลด์แบบเพิ่มประสิทธิภาพแม่นยำ
หากต้องการแสดงภาพทรัพยากร Dependency ของโปรเจ็กต์ตัวอย่าง ให้สร้างการนําเสนอแบบข้อความของกราฟทรัพยากร Dependency โดยเรียกใช้คําสั่งนี้ที่รูทของเวิร์กสเปซ
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graphคำสั่งข้างต้นบอกให้ Bazel ค้นหาไลบรารีทั้งหมดของเป้าหมาย //:ProjectRunner (ยกเว้นไลบรารีของโฮสต์และไลบรารีโดยนัย) และจัดรูปแบบเอาต์พุตเป็นกราฟ
จากนั้นวางข้อความลงใน 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 //:ProjectRunnerBazel จะสร้างเอาต์พุตที่คล้ายกับตัวอย่างต่อไปนี้
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 จะสร้างไฟล์ต้นฉบับ 1 ไฟล์และขึ้นอยู่กับเป้าหมายอื่น 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:runnerBazel จะสร้างเอาต์พุตที่คล้ายกับตัวอย่างต่อไปนี้
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 รายการ และเข้าใจความสัมพันธ์ระหว่างแพ็กเกจ
ใช้ป้ายกำกับเพื่ออ้างอิงเป้าหมาย
ในไฟล์ 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 สําหรับการติดตั้งใช้งานโดยการสร้างไบนารีที่มีการพึ่งพารันไทม์ทั้งหมดกัน ซึ่งจะช่วยให้คุณเรียกใช้ไบนารีนอกสภาพแวดล้อมการพัฒนาได้
ดังที่คุณจำได้ กฎการสร้าง 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.jarBazel จะสร้างเอาต์พุตที่คล้ายกับตัวอย่างต่อไปนี้
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 สำหรับกฎเพื่อจัดการการอ้างอิง Maven แบบทรานซิทีฟ
Dependency ภายนอกเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับการทำงานกับที่เก็บข้อมูลในเครื่องและที่เก็บข้อมูลระยะไกล
กฎอื่นๆ เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับ Bazel
บทแนะนำการสร้าง C++ เพื่อเริ่มต้นสร้างโปรเจ็กต์ C++ ด้วย Bazel
บทแนะนำแอปพลิเคชัน Android และบทแนะนำแอปพลิเคชัน iOS เพื่อเริ่มต้นสร้างแอปพลิเคชันบนอุปกรณ์เคลื่อนที่สำหรับ Android และ iOS ด้วย Bazel
ขอให้สนุกกับการสร้าง