บทแนะนำนี้ครอบคลุมพื้นฐานการสร้างแอปพลิเคชัน 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 สร้างโปรเจ็กต์ อินพุตและการอ้างอิงทั้งหมดต้องอยู่ในเวิร์กสเปซเดียวกัน ไฟล์ที่อยู่ในพื้นที่ทำงานต่างๆ จะแยกจากกัน เว้นแต่จะมีการลิงก์ ซึ่งอยู่นอกเหนือขอบเขตของบทแนะนำนี้
ทำความเข้าใจไฟล์ 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
และสคริปต์ Shell Wrapper (ทั้ง 2 รายการตั้งชื่อตามเป้าหมาย)
แอตทริบิวต์ในเป้าหมายจะระบุการขึ้นต่อกันและตัวเลือกอย่างชัดเจน
แม้ว่าแอตทริบิวต์ 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 กำหนดให้ประกาศการขึ้นต่อกันของบิลด์อย่างชัดเจนในไฟล์ 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 //: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 ไฟล์และขึ้นอยู่กับเป้าหมายอื่น (: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 รายการ และเข้าใจการขึ้นต่อกันระหว่างแพ็กเกจทั้ง 2
ใช้ป้ายกำกับเพื่ออ้างอิงเป้าหมาย
ในไฟล์ 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
ลงในเส้นทางคลาส ดังนั้นหากปล่อยไว้เช่นนี้ สคริปต์จะทำงานในเครื่อง แต่จะไม่ทำงานแบบสแตนด์อโลนในเครื่องอื่น
โชคดีที่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 สำหรับ กฎในการจัดการการอ้างอิง Maven แบบทรานซิทีฟ
การขึ้นต่อกันภายนอกเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับการทำงานกับ ที่เก็บข้อมูลในเครื่องและที่เก็บข้อมูลระยะไกล
กฎอื่นๆ เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับ Bazel
บทแนะนำการสร้าง C++ เพื่อเริ่มต้นสร้างโปรเจ็กต์ C++ ด้วย Bazel
บทแนะนำเกี่ยวกับแอปพลิเคชัน Android และ บทแนะนำเกี่ยวกับแอปพลิเคชัน iOS) เพื่อเริ่มต้นใช้งาน การสร้างแอปพลิเคชันบนอุปกรณ์เคลื่อนที่สำหรับ Android และ iOS ด้วย Bazel
ขอให้สนุกกับการสร้าง