इस ट्यूटोरियल में, Bazel के साथ Java ऐप्लिकेशन बनाने की बुनियादी जानकारी दी गई है. आपको अपना वर्कस्पेस सेट अप करना होगा और एक आसान Java प्रोजेक्ट बनाना होगा, जो Bazel के मुख्य सिद्धांतों, जैसे कि टारगेट और BUILD
फ़ाइलों को दिखाता है.
पूरा होने का अनुमानित समय: 30 मिनट.
आप इन चीज़ों के बारे में जानेंगे
इस ट्यूटोरियल में, यह जाना जा सकता है कि:
- कोई टारगेट बनाएं
- प्रोजेक्ट की डिपेंडेंसी विज़ुअलाइज़ करना
- प्रोजेक्ट को एक से ज़्यादा टारगेट और पैकेज में बांटें
- सभी पैकेज में टारगेट की विज़िबिलिटी कंट्रोल करना
- लेबल के ज़रिए रेफ़रंस टारगेट
- कोई टारगेट डिप्लॉय करना
शुरू करने से पहले
Bazel इंस्टॉल करें
ट्यूटोरियल के लिए तैयार होने के लिए, अगर आपने पहले से Bzel इंस्टॉल नहीं किया है, तो सबसे पहले इसे इंस्टॉल करें.
जेडीके इंस्टॉल करें
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
└── MODULE.bazel
Bazel के साथ बनाएं
वर्कस्पेस सेट अप करना
प्रोजेक्ट बनाने से पहले, आपको इसका फ़ाइल फ़ोल्डर सेट अप करना होगा. वर्कस्पेस एक डायरेक्ट्री है, जिसमें आपके प्रोजेक्ट की सोर्स फ़ाइलें और Bazel के बिल्ड आउटपुट होते हैं. इसमें ऐसी फ़ाइलें भी शामिल हैं जिन्हें Bazel खास मानता है:
MODULE.bazel
फ़ाइल, जो डायरेक्ट्री और उसके कॉन्टेंट की पहचान, Bazel फ़ाइल फ़ोल्डर के तौर पर करती है. यह प्रोजेक्ट की डायरेक्ट्री के स्ट्रक्चर के मूल में होती है,एक या इससे ज़्यादा
BUILD
फ़ाइलें, जो 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
टारगेट, Bazel के पहले से मौजूद
java_binary
नियम को इंस्टैंशिएट करता है. यह नियम, बैजल को .jar
फ़ाइल और रैपर शेल स्क्रिप्ट (दोनों के नाम टारगेट के नाम पर) बनाने के लिए कहता है.
टारगेट के एट्रिब्यूट, इसकी डिपेंडेंसी और विकल्पों के बारे में साफ़ तौर पर बताते हैं.
name
एट्रिब्यूट ज़रूरी है, लेकिन कई एट्रिब्यूट ज़रूरी नहीं हैं. उदाहरण के लिए, ProjectRunner
नियम के टारगेट में, name
टारगेट का नाम है, srcs
उन सोर्स फ़ाइलों के बारे में बताता है जिनका इस्तेमाल Bazel, टारगेट बनाने के लिए करता है. साथ ही, main_class
उस क्लास के बारे में बताता है जिसमें मुख्य तरीका होता है. (आपने देखा होगा कि हमारे उदाहरण में सोर्स फ़ाइलों के एक सेट को एक-एक करके दिखाने के बजाय,
Bzel को पास करने के लिए ग्लोब का इस्तेमाल किया गया है.)
प्रोजेक्ट बनाएं
सैंपल प्रोजेक्ट बनाने के लिए, 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
एट्रिब्यूट, Bazel को बताता है कि
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
और //
.
ऐसा इसलिए, क्योंकि
Workspace के रूट में एक 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
फ़ाइल वाली डायरेक्ट्री का पाथ होगा. साथ ही, BUILD
फ़ाइल (name
एट्रिब्यूट) में आपने टारगेट को target-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 अस्थायी Maven डिपेंडेंसी मैनेज करने के नियमों के लिए है.
Bazel के बारे में ज़्यादा जानने के लिए, दूसरे नियम.
Bazel के साथ C++ प्रोजेक्ट बनाना शुरू करने के लिए, C++ बिल्ड ट्यूटोरियल.
Android ऐप्लिकेशन ट्यूटोरियल और iOS ऐप्लिकेशन ट्यूटोरियल) देखें. इसमें, Bazel के साथ Android और iOS के लिए मोबाइल ऐप्लिकेशन बनाने का तरीका बताया गया है.
बिल्डिंग मुबारक!