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