बेज़ेल ट्यूटोरियल: Java प्रोजेक्ट बनाएं

संग्रह की मदद से व्यवस्थित रहें अपनी प्राथमिकताओं के आधार पर, कॉन्टेंट को सेव करें और कैटगरी में बांटें.
समस्या की शिकायत करें स्रोत देखें

इस ट्यूटोरियल में बेज़ल की मदद से Java ऐप्लिकेशन बनाने की बुनियादी बातें बताई गई हैं. आपको अपने फ़ाइल फ़ोल्डर को सेट अप करना होगा और एक आसान Java प्रोजेक्ट बनाना होगा, जो टारगेट करने और BUILD फ़ाइलों जैसे अहम बेज़ल कॉन्सेप्ट दिखाता है.

परफ़ॉर्मेंस की जांच पूरी होने का अनुमानित समय: 30 मिनट.

आप इन चीज़ों के बारे में जानेंगे

इस ट्यूटोरियल में, आप ये सीखते हैं:

  • टारगेट बनाना
  • प्रोजेक्ट की डिपेंडेंसी विज़ुअलाइज़ करना
  • प्रोजेक्ट को एक से ज़्यादा टारगेट और पैकेज में बांटना
  • सभी पैकेज पर टारगेट विज़िबिलिटी को कंट्रोल करना
  • लेबल के ज़रिए रेफ़रंस टारगेट करना
  • टारगेट डिप्लॉय करें

शुरू करने से पहले

Bazel इंस्टॉल करें

ट्यूटोरियल की तैयारी के लिए, पहले Bzel इंस्टॉल करें. अगर आपने इसे पहले से इंस्टॉल नहीं किया है, तो

JDK इंस्टॉल करें

  1. Java JDK इंस्टॉल करें (पसंदीदा वर्शन 11 है, हालांकि 8 से 15 के बीच के वर्शन काम करते हैं).

  2. JDVA_HOME एनवायरमेंट वैरिएबल को JDK पर ले जाने के लिए सेट करें.

    • Linux/macOS पर:

      export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
      
    • Windows पर:

      1. कंट्रोल पैनल खोलें.
      2. "सिस्टम और सुरक्षा" > "सिस्टम" > "बेहतर सिस्टम सेटिंग" > "बेहतर" टैब > "एनवायरमेंट वैरिएबल..." पर जाएं .
      3. "उपयोगकर्ता वैरिएबल" सूची (सबसे ऊपर मौजूद वैरिएबल) सूची में, "नया..." पर क्लिक करें.
      4. "वैरिएबल का नाम" फ़ील्ड में, JAVA_HOME डालें.
      5. "डायरेक्ट्री ब्राउज़ करें..." पर क्लिक करें.
      6. JDK डायरेक्ट्री पर जाएं. उदाहरण के लिए, C:\Program Files\Java\jdk1.8.0_152.
      7. सभी डायलॉग विंडो में, "ठीक है" पर क्लिक करें.

सैंपल प्रोजेक्ट डाउनलोड करें

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 के बिल्ड आउटपुट शामिल होते हैं. इसमें वे फ़ाइलें भी शामिल हैं जिन्हें बेज़ल ने खास तौर पर पहचाना है:

  • WORKSPACE फ़ाइल, जिसमें डायरेक्ट्री और उसके कॉन्टेंट की पहचान बेज़ल फ़ाइल फ़ोल्डर के तौर पर की जाती है. यह प्रोजेक्ट की डायरेक्ट्री के रूट के रूट में होती है,

  • एक या एक से ज़्यादा BUILD फ़ाइलें, जो Bazel को बताती हैं कि प्रोजेक्ट के अलग-अलग हिस्सों को कैसे बनाया जाता है. (Workspace में से, BUILD फ़ाइल वाला कोई डायरेक्ट्री पैकेज होती है. आप इस ट्यूटोरियल में बाद में पैकेज के बारे में जानेंगे.)

किसी डायरेक्ट्री को बेज़ल फ़ाइल फ़ोल्डर के तौर पर बताने के लिए, उस डायरेक्ट्री में WORKSPACE नाम की एक खाली फ़ाइल बनाएं.

जब बेज़ल प्रोजेक्ट बनाता है, तो सभी इनपुट और डिपेंडेंसी एक ही फ़ाइल फ़ोल्डर में होने चाहिए. अलग-अलग फ़ाइल फ़ोल्डर में रहने वाली फ़ाइलें एक-दूसरे से अलग होती हैं, जब तक कि उन्हें लिंक नहीं किया जाता, जो इस ट्यूटोरियल के दायरे से बाहर हैं.

BUILD फ़ाइल को समझें

BUILD फ़ाइल में Bazel के लिए, कई अलग-अलग तरह के निर्देश दिए गए हैं. सबसे अहम तरीका बिल्ड नियम है, जो बेज़ल को आपकी पसंद के मुताबिक आउटपुट बनाने का तरीका बताता है, जैसे कि एक्ज़ीक्यूटेबल बाइनरी या लाइब्रेरी. BUILD फ़ाइल में बिल्ड नियम के हर इंस्टेंस को टारगेट कहा जाता है. साथ ही, यह सोर्स फ़ाइलों और डिपेंडेंसी के खास सेट पर ले जाता है. एक टारगेट, दूसरे टारगेट पर भी ले जा सकता है.

java-tutorial/BUILD फ़ाइल पर एक नज़र डालें:

java_binary(
    name = "ProjectRunner",
    srcs = glob(["src/main/java/com/example/*.java"]),
)

हमारे उदाहरण में, ProjectRunner टारगेट, बेज़ल के बिल्ट-इन java_binary नियम को लागू करता है. यह नियम, बेज़ैल को .jar फ़ाइल और रैपर शेल स्क्रिप्ट (दोनों का नाम टारगेट करने के नाम से) बनाने के लिए कहता है.

टारगेट के एट्रिब्यूट में, इसकी डिपेंडेंसी और विकल्प साफ़ तौर पर दिखते हैं. हालांकि, name एट्रिब्यूट ज़रूरी है, लेकिन कई एट्रिब्यूट ज़रूरी नहीं हैं. उदाहरण के लिए, ProjectRunner नियम टारगेट में, name टारगेट का नाम होता है. srcs, उन सोर्स फ़ाइलों के बारे में बताता है जिनका इस्तेमाल Bazel ने टारगेट बनाने के लिए किया है. साथ ही, 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-bin डायरेक्ट्री में आउटपुट बनाता है. Bazel की आउटपुट संरचना के बारे में जानने के लिए, उसके कॉन्टेंट को ब्राउज़ करें.

अब आपकी नई बनी बाइनरी की जांच करें:

bazel-bin/ProjectRunner

डिपेंडेंसी ग्राफ़ की समीक्षा करना

Bazel को बिल्ड डिपेंडेंसी का इस्तेमाल, BUILD फ़ाइलों में साफ़ तौर पर करना होता है. बेज़ेल, उन स्टेटमेंट का इस्तेमाल करके प्रोजेक्ट का डिपेंडेंसी ग्राफ़ बनाता है. इससे इंक्रीमेंटल बिल्ड तय होते हैं.

सैंपल प्रोजेक्ट पर निर्भरता को विज़ुअलाइज़ करने के लिए, आप फ़ाइल फ़ोल्डर पर यह निर्देश चलाकर निर्भरता ग्राफ़ के लिए एक टेक्स्ट प्रज़ेंटेशन बना सकते हैं:

bazel query  --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph

ऊपर दिया गया निर्देश, बेज़ैल को टारगेट //:ProjectRunner के लिए सभी डिपेंडेंसी ढूंढने की अनुमति देता है. यह होस्ट और इंप्लिसिट डिपेंडेंसी को छोड़कर, आउटपुट के ग्राफ़ को फ़ॉर्मैट करता है.

इसके बाद, टेक्स्ट को GraphViz पर चिपकाएं.

जैसा कि आपको देखा जा सकता है कि प्रोजेक्ट में एक ही टारगेट है, जो दो सोर्स फ़ाइलों को बनाता है. इनके लिए कोई अन्य डिपेंडेंसी नहीं होती हैं:

टारगेट 'ProjectRunner' का डिपेंडेंसी ग्राफ़

अपना फ़ाइल फ़ोल्डर सेट अप करने, अपना प्रोजेक्ट बनाने, और उसकी डिपेंडेंसी की जांच करने के बाद, आप कुछ मुश्किलें जोड़ सकते हैं.

अपने 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"],
)

इस कॉन्फ़िगरेशन के साथ, बेज़ल ने पहले 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' का डिपेंडेंसी ग्राफ़

अब आपने दो टारगेट के साथ प्रोजेक्ट बनाया है. 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) पर निर्भर करता है - बैज़ल को 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

आगे पढ़ना

ज़्यादा जानकारी के लिए, ये देखें:

बिल्डिंग की शुभकामनाएं!