इस ट्यूटोरियल में, Bazel की मदद से Java ऐप्लिकेशन बनाने की बुनियादी बातें बताई गई हैं. इसमें आपको अपना वर्कस्पेस सेट अप करना होगा और एक आसान Java प्रोजेक्ट बनाना होगा. इस प्रोजेक्ट में, टारगेट और BUILD
फ़ाइलों जैसे Bazel के मुख्य कॉन्सेप्ट के बारे में बताया गया है.
पूरा होने का अनुमानित समय: 30 मिनट.
आपको क्या सीखने को मिलेगा
इस ट्यूटोरियल में, आपको ये काम करने का तरीका पता चलेगा:
- कोई टारगेट बनाएं
- प्रोजेक्ट की डिपेंडेंसी को विज़ुअलाइज़ करना
- प्रोजेक्ट को कई टारगेट और पैकेज में बांटना
- सभी पैकेज में टारगेट की विज़िबिलिटी कंट्रोल करना
- लेबल के ज़रिए संदर्भ लक्ष्य
- टारगेट डिप्लॉय करना
शुरू करने से पहले
Basel इंस्टॉल करें
ट्यूटोरियल के लिए तैयारी करने के लिए, अगर आपने Bazel पहले से इंस्टॉल नहीं किया है, तो पहले उसे इंस्टॉल करें.
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
) पर जाएं. - सभी डायलॉग विंडो में "ठीक है" पर क्लिक करें.
सैंपल प्रोजेक्ट डाउनलोड करें
Baज़ल के 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
फ़ाइल, डायरेक्ट्री और उसके कॉन्टेंट की पहचान बेज़ल वर्कस्पेस के तौर पर करती है. साथ ही, यह प्रोजेक्ट की डायरेक्ट्री स्ट्रक्चर के रूट में रहती है,एक या एक से ज़्यादा
BUILD
फ़ाइलें, जो Basel को प्रोजेक्ट के अलग-अलग हिस्से बनाने का तरीका बताती हैं. (फ़ाइल फ़ोल्डर में मौजूद वह डायरेक्ट्री जिसमेंBUILD
फ़ाइल होती है, वह एक पैकेज होता है. इस ट्यूटोरियल में आपको पैकेज के बारे में बाद में जानकारी मिलेगी.)
किसी डायरेक्ट्री को Bazel फ़ाइल फ़ोल्डर के तौर पर सेट करने के लिए, उस डायरेक्ट्री में WORKSPACE
नाम की खाली फ़ाइल बनाएं.
जब Bazel प्रोजेक्ट बनाता है, तो सभी इनपुट और डिपेंडेंसी एक ही वर्कस्पेस में होनी चाहिए. अलग-अलग फ़ाइल फ़ोल्डर में मौजूद फ़ाइलें, एक-दूसरे से अलग होती हैं. हालांकि, इन्हें लिंक किया जा सकता है. हालांकि, इस ट्यूटोरियल में इस बारे में नहीं बताया गया है.
BUILD फ़ाइल को समझना
BUILD
फ़ाइल में Basel के लिए कई अलग-अलग तरह के निर्देश होते हैं.
सबसे ज़रूरी टाइप बिल्ड नियम है. इससे 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
फ़ाइल में टारगेट का नाम है. (इस ट्यूटोरियल के आखिर में, टारगेट लेबल के बारे में ज़्यादा जानकारी मिलेगी.)
Baज़ल, इस तरह के आउटपुट देता है:
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
बधाई हो, आपने अभी-अभी अपना पहला Bagel टारगेट बनाया है! 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
है. - Baze, इस बात को 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
फ़ाइलों और कमांड लाइन में, Baze, टारगेट को रेफ़रंस देने के लिए टारगेट लेबल का इस्तेमाल करता है - उदाहरण के लिए, //: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
इसके बारे में और पढ़ें
ज़्यादा जानकारी के लिए, यह देखें:
ट्रांज़िटिव Maven डिपेंडेंसी को मैनेज करने के नियमों के लिए rules_jvm_external का तरीका.
बाहरी डिपेंडेंसी. इस लेख में, लोकल और रिमोट डेटा स्टोर करने की जगहों के साथ काम करने के बारे में ज़्यादा जानकारी दी गई है.
Bazel के बारे में ज़्यादा जानने के लिए, अन्य नियम.
Basel के साथ C++ प्रोजेक्ट बनाना शुरू करने के लिए, C++ बिल्ड ट्यूटोरियल.
Android ऐप्लिकेशन ट्यूटोरियल और iOS ऐप्लिकेशन ट्यूटोरियल, ताकि Bazel की मदद से Android और iOS के लिए मोबाइल ऐप्लिकेशन बनाए जा सकें.
मज़े से बनाएं!