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

समस्या की शिकायत करें सोर्स देखें

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

पूरा होने का अनुमानित समय: 30 मिनट.

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

इस ट्यूटोरियल में आपको ये काम करने का तरीका पता चलेगा:

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

वेब कंटेनर इंस्टॉल करने से पहले

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

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

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

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

  2. JDK पर पॉइंट करने के लिए, JAVA_HOME एनवायरमेंट वैरिएबल को सेट करें.

    • 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. सभी डायलॉग विंडो में "ठीक है" पर क्लिक करें.

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

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

बेज़ल के साथ बिल्ड

फ़ाइल फ़ोल्डर सेट अप करना

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

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

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

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

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

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

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

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

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

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

टारगेट में मौजूद एट्रिब्यूट, इसकी निर्भरता और विकल्पों के बारे में साफ़ तौर पर बताते हैं. name एट्रिब्यूट ज़रूरी है, लेकिन कई एट्रिब्यूट ज़रूरी नहीं हैं. उदाहरण के लिए, ProjectRunner नियम के टारगेट में name, टारगेट का नाम है. srcs उन सोर्स फ़ाइलों के बारे में बताता है जिनका इस्तेमाल Baze टारगेट बनाने के लिए करता है. main_class, मुख्य तरीके वाली क्लास के बारे में बताता है. (आपने देखा होगा कि हमारे उदाहरण में, सोर्स फ़ाइलों को एक-एक करके दिखाने के बजाय, सोर्स फ़ाइलों के एक सेट को Baze में भेजने के लिए ग्लोब का इस्तेमाल किया गया है.

प्रोजेक्ट बनाएं

सैंपल प्रोजेक्ट बनाने के लिए, java-tutorial डायरेक्ट्री पर जाएं और यह चलाकर देखें:

bazel build //:ProjectRunner

टारगेट लेबल में, // वाला हिस्सा, फ़ाइल फ़ोल्डर के रूट के मुताबिक BUILD फ़ाइल की जगह होता है (इस मामले में, रूट खुद) और BUILD फ़ाइल में टारगेट का नाम ProjectRunner है. (इस ट्यूटोरियल के आखिर में, आप टारगेट लेबल के बारे में ज़्यादा जानेंगे.)

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 टारगेट बनाया है! Baज़र, बिल्ड आउटपुट को फ़ाइल फ़ोल्डर के रूट में मौजूद bazel-bin डायरेक्ट्री में सेव करता है. इसके कॉन्टेंट को ब्राउज़ करें और इसके आउटपुट के बारे में ज़्यादा जानकारी पाएं.

अब अपनी हाल ही में बनाई गई बाइनरी की जांच करें:

bazel-bin/ProjectRunner

डिपेंडेंसी ग्राफ़ देखें

Basel के लिए बिल्ड डिपेंडेंसी का इस्तेमाल, BUILD फ़ाइलों में साफ़ तौर पर करना ज़रूरी होता है. Baज़ल, प्रोजेक्ट का डिपेंडेंसी ग्राफ़ बनाने के लिए इन स्टेटमेंट का इस्तेमाल करता है. इससे इंक्रीमेंटल बिल्ड सही तरीके से चालू होता है.

सैंपल प्रोजेक्ट की डिपेंडेंसी को विज़ुअलाइज़ करने के लिए, Workspace के रूट पर इस कमांड को चलाकर, डिपेंडेंसी ग्राफ़ को टेक्स्ट के तौर पर दिखाया जा सकता है:

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

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

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

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

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

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

अपने Basel के बिल्ड को बेहतर बनाएं

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

कई बिल्ड टारगेट तय करें

सैंपल प्रोजेक्ट के बिल्ड को दो टारगेट में बांटा जा सकता है. 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"],
)

इस कॉन्फ़िगरेशन के साथ, Bagel पहले greeter लाइब्रेरी बनाता है. इसके बाद, ProjectRunner बाइनरी बनाता है. java_binary में deps एट्रिब्यूट से बेज़ल को पता चलता है कि ProjectRunner बाइनरी बनाने के लिए greeter लाइब्रेरी ज़रूरी है.

प्रोजेक्ट के इस नए वर्शन को बनाने के लिए, यह कमांड चलाएं:

bazel build //:ProjectRunner

Baज़ल, इस तरह के आउटपुट देता है:

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 में बदलाव किया जाता है और प्रोजेक्ट को फिर से बनाया जाता है, तो Baze, सिर्फ़ उस फ़ाइल को फिर से कंपाइल करता है.

डिपेंडेंसी ग्राफ़ में देखने पर, पता चलता है कि ProjectRunner उन इनपुट पर निर्भर है जो पहले थे. हालांकि, बिल्ड का स्ट्रक्चर अलग है:

डिपेंडेंसी जोड़ने के बाद टारगेट 'ProjectRunner' का डिपेंडेंसी ग्राफ़

अब आपने दो टारगेट के साथ प्रोजेक्ट बना लिया है. ProjectRunner टारगेट, दो सोर्स फ़ाइलें बनाता है और एक अन्य टारगेट (:greeter) पर निर्भर करता है. यह टारगेट एक अतिरिक्त सोर्स फ़ाइल बनाता है.

एक से ज़्यादा पैकेज का इस्तेमाल करें

अब प्रोजेक्ट को कई पैकेज में बांट लें. src/main/java/com/example/cmdline डायरेक्ट्री को देखने पर, आपको पता चल जाएगा कि इसमें एक BUILD फ़ाइल के साथ-साथ, कुछ सोर्स फ़ाइलें भी मौजूद हैं. इसलिए, Basel के लिए, फ़ाइल फ़ोल्डर में अब दो पैकेज शामिल हैं, //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 फ़ाइल में मौजूद दूसरे टारगेट को दिखते हैं. (Bazz, टारगेट की मदद से विज़िबिलिटी का इस्तेमाल करता है. इससे पब्लिक एपीआई में ले जाने वाली लाइब्रेरी जैसी समस्याओं से बचा जा सकता है. जैसे, लागू करने की जानकारी वाली लाइब्रेरी.)

ऐसा करने के लिए, 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

Baज़ल, इस तरह के आउटपुट देता है:

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 फ़ाइल होती है. आपने BUILD फ़ाइल में टारगेट को target-name कहा है (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. Basel की जनरेट की गई runner स्क्रिप्ट, क्लासपाथ में greeter.jar जोड़ती है. इसलिए, अगर उसे ऐसे ही छोड़ा जाता है, तो यह लोकल तौर पर चलेगा, लेकिन किसी दूसरी मशीन पर स्टैंडअलोन नहीं चलेगा. अच्छी बात यह है कि java_binary नियम की मदद से, अपने-आप में पूरी और डिप्लॉय की जा सकने वाली बाइनरी बनाई जा सकती है. इसे बनाने के लिए, टारगेट के नाम में _deploy.jar को जोड़ें:

bazel build //src/main/java/com/example/cmdline:runner_deploy.jar

Baज़ल, इस तरह के आउटपुट देता है:

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

इसके बारे में और पढ़ें

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

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