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

समस्या की शिकायत करें सोर्स देखें Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

परिचय

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

Bazel एक बिल्ड सिस्टम है, जो कई भाषाओं में बिल्ड करने की सुविधा देता है. हालांकि, इस ट्यूटोरियल में C++ प्रोजेक्ट का इस्तेमाल किया गया है. साथ ही, इसमें सामान्य दिशा-निर्देश और फ़्लो दिए गए हैं, जो ज़्यादातर भाषाओं पर लागू होते हैं.

पूरा होने में लगने वाला अनुमानित समय: 30 मिनट.

ज़रूरी शर्तें

अगर आपने अब तक Bazel इंस्टॉल नहीं किया है, तो सबसे पहले इसे इंस्टॉल करें. इस ट्यूटोरियल में, सोर्स कंट्रोल के लिए Git का इस्तेमाल किया गया है. इसलिए, सबसे अच्छे नतीजे पाने के लिए, Git इंस्टॉल करें.

इसके बाद, Bazel की GitHub रिपॉज़िटरी से सैंपल प्रोजेक्ट पाएं. इसके लिए, अपनी पसंद की कमांड-लाइन टूल में यह कमांड चलाएं:

git clone https://github.com/bazelbuild/examples

इस ट्यूटोरियल के लिए सैंपल प्रोजेक्ट, examples/cpp-tutorial डायरेक्ट्री में है.

नीचे देखें कि इसे कैसे स्ट्रक्चर किया गया है:

examples
└── cpp-tutorial
    ├──stage1
      ├── main
         ├── BUILD
         └── hello-world.cc
      └── WORKSPACE
    ├──stage2
      ├── main
         ├── BUILD
         ├── hello-world.cc
         ├── hello-greet.cc
         └── hello-greet.h
      └── WORKSPACE
    └──stage3
       ├── main
          ├── BUILD
          ├── hello-world.cc
          ├── hello-greet.cc
          └── hello-greet.h
       ├── lib
          ├── BUILD
          ├── hello-time.cc
          └── hello-time.h
       └── WORKSPACE

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

खास जानकारी: शुरुआती जानकारी

Bazel और Git को इंस्टॉल करके और इस ट्यूटोरियल के लिए रिपॉज़िटरी को क्लोन करके, आपने Bazel के साथ अपनी पहली बिल्ड के लिए बुनियादी ढांचा तैयार कर लिया है. कुछ शब्दों को तय करने और अपना वर्कस्पेस सेट अप करने के लिए, अगले सेक्शन पर जाएं.

शुरू करना

वर्कस्पेस सेट अप करना

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

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

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

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

BUILD फ़ाइल के बारे में जानकारी

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

cpp-tutorial/stage1/main डायरेक्ट्री में मौजूद BUILD फ़ाइल देखें:

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

हमारे उदाहरण में, hello-world टारगेट, Bazel के बिल्ट-इन cc_binary rule को इंस्टैंशिएट करता है. इस नियम से Bazel को यह निर्देश मिलता है कि वह hello-world.cc सोर्स फ़ाइल से, बिना किसी डिपेंडेंसी के एक एक्ज़ीक्यूटेबल बाइनरी बनाए.

खास जानकारी: शुरू करना

अब आपको कुछ मुख्य शब्दों के बारे में पता चल गया है. साथ ही, आपको यह भी पता चल गया है कि इस प्रोजेक्ट और Bazel के संदर्भ में उनका क्या मतलब है. अगले सेक्शन में, प्रोजेक्ट के पहले चरण को बनाया और टेस्ट किया जाएगा.

पहला चरण: एक टारगेट, एक पैकेज

अब प्रोजेक्ट का पहला हिस्सा बनाने का समय है. विज़ुअल रेफ़रंस के लिए, प्रोजेक्ट के पहले चरण के सेक्शन का स्ट्रक्चर यह है:

examples
└── cpp-tutorial
    └──stage1
       ├── main
          ├── BUILD
          └── hello-world.cc
       └── WORKSPACE

cpp-tutorial/stage1 डायरेक्ट्री पर जाने के लिए, यह कमांड चलाएं:

cd cpp-tutorial/stage1

इसके बाद, यह कमांड चलाएं:

bazel build //main:hello-world

टारगेट लेबल में, //main: हिस्सा, Workspace के रूट के हिसाब से BUILD फ़ाइल की जगह की जानकारी देता है. वहीं, hello-world, BUILD फ़ाइल में टारगेट का नाम होता है.

Bazel, इस तरह का कुछ आउटपुट जनरेट करता है:

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s

आपने अभी-अभी अपना पहला Bazel टारगेट बनाया है. Bazel, बिल्ड आउटपुट को वर्कस्पेस के रूट में मौजूद bazel-bin डायरेक्ट्री में रखता है.

अब अपने नए बाइनरी की जांच करें. यह बाइनरी:

bazel-bin/main/hello-world

इससे “Hello world” मैसेज प्रिंट होता है.

यहां स्टेज 1 का डिपेंडेंसी ग्राफ़ दिया गया है:

hello-world के लिए डिपेंडेंसी ग्राफ़ में, एक सोर्स फ़ाइल के साथ एक टारगेट दिखाया गया है.

खास जानकारी: पहला चरण

अब आपने पहला बिल्ड पूरा कर लिया है. इसलिए, आपको बिल्ड के स्ट्रक्चर के बारे में बुनियादी जानकारी मिल गई है. अगले चरण में, एक और टारगेट जोड़कर इसे और मुश्किल बनाया जाएगा.

दूसरा चरण: एक से ज़्यादा बिल्ड टारगेट

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

यह वह डायरेक्ट्री है जिसका इस्तेमाल आपको दूसरे चरण के लिए करना है:

    ├──stage2
      ├── main
         ├── BUILD
         ├── hello-world.cc
         ├── hello-greet.cc
         └── hello-greet.h
      └── WORKSPACE

cpp-tutorial/stage2/main डायरेक्ट्री में मौजूद BUILD फ़ाइल को यहां देखें:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

इस BUILD फ़ाइल की मदद से, Bazel पहले hello-greet लाइब्रेरी बनाता है. इसके लिए, Bazel में पहले से मौजूद cc_library rule का इस्तेमाल किया जाता है. इसके बाद, hello-world बाइनरी बनाई जाती है. hello-world टारगेट में मौजूद deps एट्रिब्यूट, Bazel को बताता है कि hello-world बाइनरी बनाने के लिए hello-greet लाइब्रेरी की ज़रूरत है.

प्रोजेक्ट का नया वर्शन बनाने से पहले, आपको डायरेक्ट्री बदलनी होगी. इसके लिए, यह कमांड चलाकर cpp-tutorial/stage2 डायरेक्ट्री पर स्विच करें:

cd ../stage2

अब इस जानी-पहचानी कमांड का इस्तेमाल करके, नया बाइनरी बनाया जा सकता है:

bazel build //main:hello-world

Bazel, एक बार फिर इस तरह का कुछ आउटपुट जनरेट करता है:

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s

अब नई बाइनरी को टेस्ट किया जा सकता है. इससे एक और “Hello world” मिलता है:

bazel-bin/main/hello-world

अब अगर hello-greet.cc में बदलाव किया जाता है और प्रोजेक्ट को फिर से बनाया जाता है, तो Bazel सिर्फ़ उस फ़ाइल को फिर से कंपाइल करता है.

डिपेंडेंसी ग्राफ़ को देखने पर, आपको पता चलेगा कि hello-world, hello-greet नाम के एक अतिरिक्त इनपुट पर निर्भर करता है:

`hello-world` के लिए डिपेंडेंसी ग्राफ़, फ़ाइल में बदलाव करने के बाद डिपेंडेंसी में हुए बदलावों को दिखाता है.

खास जानकारी: दूसरा चरण

अब आपने दो टारगेट वाला प्रोजेक्ट बना लिया है. hello-world टारगेट, एक सोर्स फ़ाइल बनाता है. साथ ही, यह एक अन्य टारगेट (//main:hello-greet) पर निर्भर करता है. //main:hello-greet टारगेट, दो अतिरिक्त सोर्स फ़ाइलें बनाता है. अगले सेक्शन में, एक और पैकेज जोड़ें.

तीसरा चरण: एक से ज़्यादा पैकेज

इस अगले चरण में, एक और लेयर जोड़ी जाती है. साथ ही, कई पैकेज वाला प्रोजेक्ट बनाया जाता है. cpp-tutorial/stage3 डायरेक्ट्री का स्ट्रक्चर और कॉन्टेंट यहां देखें:

└──stage3
   ├── main
      ├── BUILD
      ├── hello-world.cc
      ├── hello-greet.cc
      └── hello-greet.h
   ├── lib
      ├── BUILD
      ├── hello-time.cc
      └── hello-time.h
   └── WORKSPACE

अब आपको दिखेगा कि दो सब-डायरेक्ट्री हैं और हर एक में BUILD फ़ाइल है. इसलिए, Bazel के लिए अब वर्कस्पेस में दो पैकेज हैं: lib और main.

lib/BUILD फ़ाइल देखें:

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

साथ ही, main/BUILD फ़ाइल में:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "//lib:hello-time",
    ],
)

मुख्य पैकेज में मौजूद hello-world टारगेट, lib पैकेज में मौजूद hello-time टारगेट पर निर्भर करता है. इसलिए, टारगेट का लेबल //lib:hello-time है. Bazel को इस बारे में deps एट्रिब्यूट से पता चलता है. इसे डिपेंडेंसी ग्राफ़ में देखा जा सकता है:

`hello-world` के लिए डिपेंडेंसी ग्राफ़ दिखाता है कि मुख्य पैकेज में मौजूद टारगेट, `lib` पैकेज में मौजूद टारगेट पर कैसे निर्भर करता है.

बिल्ड को पूरा करने के लिए, विज़िबिलिटी एट्रिब्यूट का इस्तेमाल करके, lib/BUILD में मौजूद //lib:hello-time टारगेट को main/BUILD में मौजूद टारगेट के लिए साफ़ तौर पर दिखाया जाता है. ऐसा इसलिए है, क्योंकि डिफ़ॉल्ट रूप से टारगेट सिर्फ़ उसी BUILD फ़ाइल में मौजूद अन्य टारगेट को दिखते हैं. Bazel, टारगेट विज़िबिलिटी का इस्तेमाल करता है. इससे, सार्वजनिक एपीआई में लागू करने से जुड़ी जानकारी वाली लाइब्रेरी जैसी समस्याओं को रोकने में मदद मिलती है.

अब प्रोजेक्ट का यह फ़ाइनल वर्शन बनाएं. cpp-tutorial/stage3 डायरेक्ट्री पर स्विच करने के लिए, यह कमांड चलाएं:

cd  ../stage3

यहां दिया गया कमांड फिर से चलाएं:

bazel build //main:hello-world

Bazel, इस तरह का कुछ आउटपुट जनरेट करता है:

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s

अब इस ट्यूटोरियल के आखिरी बाइनरी को फ़ाइनल Hello world मैसेज के लिए टेस्ट करें:

bazel-bin/main/hello-world

खास जानकारी: तीसरा चरण

अब आपने प्रोजेक्ट को दो पैकेज के तौर पर बनाया है. इनमें तीन टारगेट हैं. साथ ही, आपने इनके बीच की डिपेंडेंसी को समझ लिया है. इससे आपको Bazel का इस्तेमाल करके, आने वाले समय में प्रोजेक्ट बनाने में मदद मिलेगी. अगले सेक्शन में, Bazel का इस्तेमाल जारी रखने का तरीका जानें.

अगले चरण

आपने Bazel की मदद से, पहला बुनियादी बिल्ड तैयार कर लिया है. हालांकि, यह सिर्फ़ शुरुआत है. Bazel के बारे में ज़्यादा जानने के लिए, यहां कुछ और संसाधन दिए गए हैं:

ऐप बनाने का आनंद लें!