इस ट्यूटोरियल में Bazel के साथ Java ऐप्लिकेशन बनाने की बुनियादी बातें बताई गई हैं. आपको अपना फ़ाइल फ़ोल्डर सेट अप करना होगा और एक आसान Java प्रोजेक्ट बनाना होगा, जो
टारगेट और BUILD
फ़ाइलों जैसे खास Bazel कॉन्सेप्ट को दिखाता हो.
पूरा होने का अनुमानित समय: 30 मिनट.
आप इन चीज़ों के बारे में जानेंगे
इस ट्यूटोरियल में, ये काम करने के तरीके बताए गए हैं:
- कोई टारगेट बनाएं
- प्रोजेक्ट की डिपेंडेंसी विज़ुअलाइज़ करें
- प्रोजेक्ट को एक से ज़्यादा टारगेट और पैकेज में बांटें
- तय करें कि सभी पैकेज में, टारगेट किसको दिखे
- लेबल के ज़रिए टारगेट का रेफ़रंस देना
- टारगेट डिप्लॉय करना
शुरू करने से पहले
Bazel इंस्टॉल करें
ट्यूटोरियल के लिए तैयार होने के लिए, अगर आपने पहले से {1/} class="ph-1-1"> इंस्टॉल नहीं किया है, तो पहले इंस्टॉल करें.
JDK इंस्टॉल करें
Java JDK इंस्टॉल करें (पसंदीदा वर्शन 11 है. हालांकि, 8 से 15 के बीच के वर्शन काम करते हैं).
JDK पर पॉइंट करने के लिए, JAVA_HOME के एनवायरमेंट वैरिएबल को सेट करें.
Linux/macOS पर:
export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
Windows पर:
- कंट्रोल पैनल खोलें.
- "सिस्टम और सुरक्षा" > "सिस्टम" > "ऐडवांस सिस्टम सेटिंग" > "बेहतर" टैब > "एनवायरमेंट वैरिएबल..." पर जाएं .
- सबसे ऊपर मौजूद "उपयोगकर्ता वैरिएबल" सूची में, "नया..." पर क्लिक करें.
- "वैरिएबल नाम" फ़ील्ड में,
JAVA_HOME
डालें. - "डायरेक्ट्री ब्राउज़ करें..." पर क्लिक करें.
- JDK डायरेक्ट्री पर जाएं (उदाहरण के लिए,
C:\Program Files\Java\jdk1.8.0_152
). - सभी डायलॉग विंडो में "ठीक है" पर क्लिक करें.
सैंपल प्रोजेक्ट पाएं
Bazel के GitHub रिपॉज़िटरी से सैंपल प्रोजेक्ट वापस पाएं:
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 Workspace के तौर पर करती है. यह प्रोजेक्ट के डायरेक्ट्री स्ट्रक्चर के रूट में होती है,एक या इससे ज़्यादा
BUILD
फ़ाइलें, जो 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
फ़ाइल और एक रैपर शेल स्क्रिप्ट (दोनों का नाम टारगेट पर रखने के लिए) बनाने के लिए कहता है.
टारगेट के एट्रिब्यूट, इसकी डिपेंडेंसी और विकल्पों के बारे में साफ़ तौर पर बताते हैं.
name
एट्रिब्यूट ज़रूरी हैं, लेकिन इनमें से कई एट्रिब्यूट ज़रूरी नहीं हैं. उदाहरण के लिए, ProjectRunner
नियम के टारगेट में, name
टारगेट का नाम है, srcs
उन सोर्स फ़ाइलों के बारे में बताता है जिनका इस्तेमाल बैजल, टारगेट बनाने के लिए करता है, और main_class
उस क्लास के बारे में बताता है जिसमें मुख्य तरीका शामिल है. (आपने देखा होगा कि हमारा उदाहरण, सोर्स फ़ाइलों को एक-एक करके दिखाने के बजाय, सोर्स फ़ाइलों के एक सेट को पास करने के लिए ग्लोब का इस्तेमाल करता है.)
प्रोजेक्ट बनाएं
अपना सैंपल प्रोजेक्ट बनाने के लिए, 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 उन स्टेटमेंट का इस्तेमाल करके, प्रोजेक्ट का डिपेंडेंसी ग्राफ़ बनाता है. इससे सटीक इंक्रीमेंटल (बढ़ने वाले) बिल्ड चालू करने में मदद मिलती है.
सैंपल प्रोजेक्ट की डिपेंडेंसी को विज़ुअलाइज़ करने के लिए, वर्कस्पेस के रूट पर इस कमांड को चलाकर, डिपेंडेंसी ग्राफ़ का टेक्स्ट प्रज़ेंटेशन जनरेट किया जा सकता है:
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph
ऊपर दिए गए निर्देश की मदद से, Bazel, टारगेट //:ProjectRunner
(होस्ट और इंप्लिसिट डिपेंडेंसी के अलावा) के लिए सभी डिपेंडेंसी का पता लगाता है और आउटपुट को ग्राफ़ के तौर पर फ़ॉर्मैट करता है.
इसके बाद, टेक्स्ट को GraphViz में चिपकाएं.
जैसा कि आपको दिख रहा है, प्रोजेक्ट में एक ही टारगेट है जो बिना किसी अतिरिक्त डिपेंडेंसी के दो सोर्स फ़ाइलें बनाता है:
अपना वर्कस्पेस सेट अप करने, प्रोजेक्ट बनाने, और उसकी डिपेंडेंसी की जांच करने के बाद, कुछ जटिलता जोड़ी जा सकती है.
अपने Bazel बिल्ड को बेहतर बनाएं
छोटे प्रोजेक्ट के लिए एक ही टारगेट काफ़ी होता है. हालांकि, बड़े प्रोजेक्ट को कई टारगेट और पैकेज में बांटा जा सकता है, ताकि बड़े प्रोजेक्ट को एक साथ कई टारगेट और पैकेज में बांटा जा सके. इसका मतलब है कि जो बदलाव किए गए हैं उन्हें फिर से बनाएं. साथ ही, प्रोजेक्ट के कई हिस्सों को एक साथ बनाकर अपने बिल्ड को तेज़ करें.
कई बिल्ड टारगेट तय करें
सैंपल प्रोजेक्ट के बिल्ड को दो टारगेट में बांटा जा सकता है. 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
बाइनरी बनाता है. java_binary
का deps
एट्रिब्यूट, बैजल को बताता है कि
ProjectRunner
बाइनरी बनाने के लिए greeter
लाइब्रेरी की ज़रूरत है.
प्रोजेक्ट का यह नया वर्शन बनाने के लिए, नीचे दिया गया कमांड चलाएं:
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
पहले की तरह ही
इनपुट पर निर्भर करता है. हालांकि, बिल्ड का स्ट्रक्चर अलग है:
अब आपने दो टारगेट के साथ प्रोजेक्ट बना लिया है. ProjectRunner
टारगेट, दो सोर्स फ़ाइलें बनाता है और एक अन्य टारगेट (:greeter
) पर निर्भर करता है, जो एक और सोर्स फ़ाइल बनाता है.
एक से ज़्यादा पैकेज इस्तेमाल करें
अब प्रोजेक्ट को अलग-अलग पैकेज में बांटें. अगर आप src/main/java/com/example/cmdline
डायरेक्ट्री पर एक नज़र डालें, तो आपको दिखेगा कि इसमें एक BUILD
फ़ाइल और कुछ सोर्स फ़ाइलें भी हैं. इसलिए, Bazel के लिए, अब फ़ाइल फ़ोल्डर में दो पैकेज शामिल हैं, //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
एट्रिब्यूट से यह पता चलता है.
डिपेंडेंसी ग्राफ़ देखें:
हालांकि, बिल्ड को सफल बनाने के लिए, आपको visibility
एट्रिब्यूट का इस्तेमाल करके, यह साफ़ तौर पर बताना होगा कि //BUILD
में मौजूद टारगेट के लिए, //src/main/java/com/example/cmdline/BUILD
में runner
टारगेट को कैसे दिखाया जाए. ऐसा इसलिए होता है, क्योंकि डिफ़ॉल्ट तौर पर टारगेट
एक ही BUILD
फ़ाइल में मौजूद अन्य टारगेट को ही दिखते हैं. (Bazel 'टारगेट की जानकारी' देखने की सुविधा का इस्तेमाल करके, लाइब्रेरी जैसी समस्याओं को रोकता है.
जैसे, लागू करने के तरीके की जानकारी वाली लाइब्रेरी, जो सार्वजनिक एपीआई में लीक होती है.)
ऐसा करने के लिए, java-tutorial/BUILD
में मौजूद greeter
टारगेट में visibility
एट्रिब्यूट जोड़ें, जैसा कि यहां दिखाया गया है:
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
फ़ाइल में टारगेट का रेफ़रंस देते समय, //
फ़ाइल फ़ोल्डर के रूट आइडेंटिफ़ायर को छोड़ा जा सकता है और सिर्फ़ :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 टारगेट को पैकेज करते हैं. इससे आपको अपने डेवलपमेंट एनवायरमेंट के बाहर बाइनरी चलाने में मदद मिलती है.
जैसा कि आपको याद है, java_binary बिल्ड नियम
बनाता है और .jar
और रैपर शेल स्क्रिप्ट बनाता है. इस निर्देश का इस्तेमाल करके, 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
नहीं है. Bazel ने जो runner
स्क्रिप्ट जनरेट की है वह क्लासपाथ में 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 के बारे में ज़्यादा जानने के लिए, दूसरे नियम.
Bazel के साथ C++ प्रोजेक्ट बनाना शुरू करने के लिए, C++ बिल्ड ट्यूटोरियल देखें.
Bazel के साथ Android और iOS के लिए मोबाइल ऐप्लिकेशन बनाने के बारे में जानने के लिए, Android ऐप्लिकेशन ट्यूटोरियल और iOS ऐप्लिकेशन ट्यूटोरियल देखें.
इमारत बनाने के लिए शुभकामनाएं!