מדריך זה סוקר את העקרונות הבסיסיים של בניית אפליקציות 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:
- פתיחת לוח הבקרה.
- "System and Security" > "System" > " Advanced System Settings" > "Advanced" tab > "Environment Variables..." .
- ברשימה "משתנים ו&משתמש; (הרשימה שבחלק העליון), לוחצים על "חדש...".
- בשדה "Variable name", מזינים
JAVA_HOME
. - לוחצים על &מירכאות. מעיינים בספרייה...
- עוברים לספריית JDK (לדוגמה
C:\Program Files\Java\jdk1.8.0_152
). - לוחצים על "OK"בכל חלונות הדו-שיח.
קבלת הפרויקט לדוגמה
אחזור פרויקט לדוגמה ממאגר GitHub של Bazel&39:
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
Build עם Bazel
הגדרת סביבת העבודה
כדי ליצור פרויקט, צריך להגדיר את סביבת העבודה שלו. 'סביבת עבודה' היא ספרייה ששומרת את קובצי המקור של הפרויקט ואת תוצרי ה-build של Bazel&39. הוא גם מכיל קבצים ש-Bazel מזהה כמיוחדים:
הקובץ
WORKSPACE
, שמזהה את הספרייה והתוכן שלה כסביבת עבודה על בסיס ושוכן בבסיס מבנה הספרייה של הפרויקט,קובץ אחד (
BUILD
) או יותר, שמסביר ל-Bazel איך לבנות חלקים שונים של הפרויקט. (ספרייה בתוך סביבת העבודה שמכילה קובץBUILD
היא חבילה. בהמשך המדריך תקבלו מידע נוסף על חבילות.)
כדי להגדיר ספרייה כסביבת עבודה של Bazel, יש ליצור בספרייה הזו קובץ ריק בשם WORKSPACE
.
כאשר בונים בבזל את הפרויקט, כל הקלט והתלויות חייבים להיות באותו סביבת עבודה. קבצים השוכנים בסביבות עבודה שונות אינם תלויים זה בזה, אלא אם כן הם מקושרים, מחוץ לטווח של המדריך.
הבנת קובץ ה-BUILD
קובץ BUILD
מכיל מספר סוגים שונים של הוראות עבור Bazel.
הסוג החשוב ביותר הוא כלל הבנייה, שמסביר ל-Bazel איך ליצור את הפלט הרצוי, למשל קובצי בינאריים או קובצי הפעלה. כל מופע של כלל build בקובץ BUILD
נקרא target ומצביע על קבוצה ספציפית של קובצי מקור ותלויות. טירגוט יכול גם להצביע על יעדים אחרים.
מומלץ לעיין בקובץ java-tutorial/BUILD
:
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
בדוגמה שלנו, היעד ProjectRunner
משקף את הכלל המובנה של Bazel'java_binary
. הכלל מורה ל-Bazel לבנות קובץ .jar
וסקריפט מעטפת של wrapper (שניהם נקראים על שם היעד).
המאפיינים ביעד מציינים במפורש את האפשרויות והתלויות שלו.
המאפיין 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-bin
שבבסיס סביבת העבודה. מעיינים בתוכן שלו כדי לקבל מושג לגבי מבנה הפלט של Bazel&39.
עכשיו יש לבדוק את הקובץ הבינארי שבנה לאחרונה:
bazel-bin/ProjectRunner
בדיקת תרשים התלות
ל-Bazel נדרשת הצהרה מפורשת על יחסי תלות של build, בקובצי BUILD. מאחר ש-Bazel משתמשת בהצהרות האלה כדי ליצור את תרשים התלות של הפרויקט, שמאפשרת ליצור גרסאות מצטברות מדויקות.
כדי להמחיש את התלות של הפרויקט לדוגמה, תוכלו ליצור ייצוג טקסט של תרשים תלות על ידי הרצת הפקודה בשורש העבודה:
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph
הפקודה שלמעלה מורה ל-Bazel לחפש את כל יחסי התלות של היעד
//:ProjectRunner
(לא כולל יחסי תלות של מראים ומשתמעים) ולעצב את הפלט כתרשים.
לאחר מכן מדביקים את הטקסט ב-GraphViz.
כפי שניתן לראות, לפרויקט יש יעד יחיד שיוצר שני קובצי מקור ללא תלות נוספות:
אחרי שמגדירים את סביבת העבודה, בונים את הפרויקט ובודקים את התלויות שלו, אפשר להוסיף מורכבות.
צמצום גרסת ה-Bazel
בעוד שיעד אחד מספיק לפרויקטים קטנים, יכול להיות שתרצו לפצל פרויקטים גדולים לכמה יעדים וחבילות כדי לאפשר בנייה מהירה ומצטברת (כלומר, לבנות מחדש מה ששונה) ולהאיץ את גרסאות ה-build על ידי בניית מספר חלקים של פרויקט בבת אחת.
ציון יעדי build מרובים
אפשר לפצל את גרסת הפרויקט לדוגמה לשני יעדים. אתם צריכים להחליף את התוכן של הקובץ 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"],
)
בתצורה הזו, בזל בונה תחילה את הספרייה greeter
, ואז את הקובץ הבינארי של ProjectRunner
. המאפיין deps
ב-java_binary
מיידע את בזל על כך שספריית 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
תלוי באותם הנתונים להזין, אבל המבנה של ה-build שונה:
סיימת ליצור את הפרויקט עם שני יעדים. היעד ProjectRunner
יוצר שני קובצי מקור ותלוי ביעד אחד נוסף (:greeter
), שיוצר קובץ מקור אחד נוסף.
שימוש בחבילות מרובות
עכשיו נפצל את הפרויקט לכמה חבילות. אם תעיינו בספרייה של src/main/java/com/example/cmdline
, תוכלו לראות שהיא מכילה גם קובץ BUILD
, וגם קובצי מקור מסוימים. לכן, בבזל, סביבת העבודה כוללת עכשיו שתי חבילות: //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
.
אנחנו מציגים את תרשים התלות:
עם זאת, כדי שה-build יצליח, עליך לתת באופן מפורש את היעד 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
עכשיו שיניתם את הפרויקט כדי לבנות אותו כשתי חבילות, וכל אחת מהן מכילה יעד אחד ולהבין את התלויות ביניהן.
אפשר להשתמש בתוויות כדי לציין יעדים
בקבצים מסוג 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
, אפשר גם לדלג על מזהה הבסיס של //
Workspace ולהשתמש רק ב-:target-name
.
לדוגמה, לגבי יעדים בקובץ java-tutorial/BUILD
לא היה צורך לציין נתיב חבילה, כי שורש העבודה הוא עצמו חבילה (//
) ושתי תוויות היעד שלכם היו //:ProjectRunner
ו-//:greeter
.
עם זאת, עבור היעדים בקובץ //src/main/java/com/example/cmdline/BUILD
היה עליכם לציין את מסלול החבילה המלא, //src/main/java/com/example/cmdline
, ותווית היעד שלכם הייתה //src/main/java/com/example/cmdline:runner
.
חבילת יעד של Java לפריסה
עכשיו נכלול יעד ל-Java לפריסה על ידי בניית הקובץ הבינארי עם כל יחסי תלות בזמן הריצה. כך תוכלו להריץ את הקובץ הבינארי מחוץ לסביבת הפיתוח שלכם.
חשוב לזכור שכלל ה-build של 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
, וניתן להריץ אותו בנפרד מסביבת הפיתוח, כי הוא מכיל את זמני הריצה הנדרשים. חשוב לצפות בתוכן של ה-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 לכללים לניהול יחסי תלות חולפים.
תלויות חיצוניות למידע נוסף על עבודה עם מאגרים מקומיים ומרוחקים.
הכללים האחרים לקבלת מידע נוסף על Bazel.
במדריך ליצירת C++ תוכלו להתחיל בבניית פרויקטים של C++ ב-Bazel.
המדריך לאפליקציות ל-Android ומדריך לאפליקציות ל-iOS כדי להתחיל ליצור אפליקציות לנייד ל-Android ול-iOS בעזרת Bazel.
בניין שמח!