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