बाहरी डिपेंडेंसी के साथ काम करना

Bazel, दूसरे प्रोजेक्ट के टारगेट पर निर्भर हो सकता है. इन अन्य प्रोजेक्ट की डिपेंडेंसी को बाहरी डिपेंडेंसी कहा जाता है.

वर्कस्पेस डायरेक्ट्री में मौजूद WORKSPACE फ़ाइल (या WORKSPACE.bazel फ़ाइल) से Bazel को यह पता चलता है कि अन्य प्रोजेक्ट के सोर्स कैसे पाएं. इन अन्य प्रोजेक्ट में, एक या उससे ज़्यादा BUILD फ़ाइलें हो सकती हैं. इनके अपने टारगेट होते हैं. मुख्य प्रोजेक्ट में मौजूद BUILD फ़ाइलें, इन बाहरी टारगेट पर निर्भर हो सकती हैं. इसके लिए, WORKSPACE फ़ाइल से उनके नाम का इस्तेमाल किया जाता है.

उदाहरण के लिए, मान लें कि किसी सिस्टम पर दो प्रोजेक्ट हैं:

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

अगर project1 को /home/user/project2/BUILD में तय किए गए :foo टारगेट पर निर्भर रहना है, तो वह यह तय कर सकता है कि project2 नाम का डेटाबेस /home/user/project2 पर मौजूद है. इसके बाद, /home/user/project1/BUILD में मौजूद टारगेट, @project2//:foo पर निर्भर हो सकते हैं.

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

बाहरी डिपेंडेंसी के साथ काम करने वाले टाइप

कुछ बुनियादी तरह की बाहरी डिपेंडेंसी का इस्तेमाल किया जा सकता है:

अन्य Bazel प्रोजेक्ट पर निर्भर होना

अगर आपको किसी दूसरे Bazel प्रोजेक्ट के टारगेट इस्तेमाल करने हैं, तो आपके पास इन्हें इस्तेमाल करने का विकल्प है: local_repository, git_repository या http_archive इनका इस्तेमाल करके, लोकल फ़ाइल सिस्टम से इसे सिमलिंक किया जा सकता है, किसी Git रिपॉज़िटरी को रेफ़रंस किया जा सकता है या इसे डाउनलोड किया जा सकता है.

उदाहरण के लिए, मान लें कि आपको my-project/ प्रोजेक्ट पर काम करना है और आपको अपने सहकर्मी के coworkers-project/ प्रोजेक्ट के टारगेट का इस्तेमाल करना है. दोनों प्रोजेक्ट Bazel का इस्तेमाल करते हैं. इसलिए, अपने सहकर्मी के प्रोजेक्ट को बाहरी डिपेंडेंसी के तौर पर जोड़ा जा सकता है. इसके बाद, अपने BUILD फ़ाइलों से, अपने सहकर्मी के तय किए गए किसी भी टारगेट का इस्तेमाल किया जा सकता है. आपको my_project/WORKSPACE में यह जानकारी जोड़नी होगी:

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

अगर आपके सहकर्मी के पास टारगेट //foo:bar है, तो आपका प्रोजेक्ट इसे //foo:bar के तौर पर रेफ़र कर सकता है.@coworkers_project//foo:bar बाहरी प्रोजेक्ट के नाम, मान्य वर्कस्पेस के नाम होने चाहिए.

Bazel के अलावा अन्य प्रोजेक्ट पर निर्भर होना

new_ से शुरू होने वाले नियम, जैसे कि new_local_repository, आपको ऐसे प्रोजेक्ट से टारगेट बनाने की अनुमति देते हैं जो Bazel का इस्तेमाल नहीं करते.

उदाहरण के लिए, मान लें कि आपको my-project/ प्रोजेक्ट पर काम करना है और आपको अपने सहकर्मी के coworkers-project/ प्रोजेक्ट पर निर्भर रहना है. आपके सहकर्मी का प्रोजेक्ट, बनाने के लिए make का इस्तेमाल करता है. हालांकि, आपको जनरेट की गई किसी .so फ़ाइल पर निर्भर रहना है. इसके लिए, my_project/WORKSPACE में यह जोड़ें:

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file, मौजूदा प्रोजेक्ट पर ओवरले करने के लिए BUILD फ़ाइल के बारे में बताता है. उदाहरण के लिए:

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

इसके बाद, अपने प्रोजेक्ट की BUILD फ़ाइलों से @coworkers_project//:some-lib का इस्तेमाल किया जा सकता है.

बाहरी पैकेज पर निर्भर होना

Maven आर्टफ़ैक्ट और रिपॉज़िटरी

Maven के डेटा को स्टोर करने की जगहों से आर्टफ़ैक्ट डाउनलोड करने और उन्हें Java डिपेंडेंसी के तौर पर उपलब्ध कराने के लिए, rules_jvm_external नियमों के सेट का इस्तेमाल करें.

डिपेंडेंसी फ़ेच की जा रही हैं

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

शैडोइंग डिपेंडेंसी

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

myproject/WORKSPACE

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/WORKSPACE

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

दोनों डिपेंडेंसी A और B, testrunner पर निर्भर करती हैं. हालांकि, ये testrunner के अलग-अलग वर्शन पर निर्भर करती हैं. इन टेस्ट रनर के myproject में एक साथ काम न करने की कोई वजह नहीं है. हालांकि, ये एक-दूसरे से टकराएंगे, क्योंकि इनका नाम एक ही है. दोनों डिपेंडेंसी का एलान करने के लिए, myproject/WORKSPACE फ़ाइल को अपडेट करें:

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

इस तरीके का इस्तेमाल, डायमंड जोड़ने के लिए भी किया जा सकता है. उदाहरण के लिए, अगर A और B की एक ही डिपेंडेंसी है, लेकिन उसे अलग-अलग नामों से कॉल किया जाता है, तो उन डिपेंडेंसी को myproject/WORKSPACE में जोड़ा जा सकता है.

कमांड लाइन से रिपॉज़िटरी को बदलना

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

उदाहरण के लिए, @foo को स्थानीय डायरेक्ट्री /path/to/local/foo पर बदलने के लिए, --override_repository=foo=/path/to/local/foo फ़्लैग पास करें.

इन्हें इन कामों के लिए इस्तेमाल किया जा सकता है:

  • समस्याओं को डीबग करना. उदाहरण के लिए, http_archive रिपॉज़िटरी को किसी ऐसी लोकल डायरेक्ट्री से बदला जा सकता है जहां आसानी से बदलाव किए जा सकते हैं.
  • वेंडरिंग. अगर आप ऐसे माहौल में हैं जहां नेटवर्क कॉल नहीं किए जा सकते, तो नेटवर्क पर आधारित रिपॉज़िटरी के नियमों को बदलकर, स्थानीय डायरेक्ट्री की ओर पॉइंट करें.

प्रॉक्सी का इस्तेमाल करना

Bazel, HTTPS_PROXY और HTTP_PROXY एनवायरमेंट वैरिएबल से प्रॉक्सी पते चुनता है. साथ ही, इनका इस्तेमाल एचटीटीपी/एचटीटीपीएस फ़ाइलें डाउनलोड करने के लिए करता है (अगर बताया गया हो).

IPv6 के साथ काम करने वाला वर्शन

सिर्फ़ IPv6 वाले कंप्यूटरों पर, Bazel बिना किसी बदलाव के डिपेंडेंसी डाउनलोड कर पाएगा. हालांकि, ड्यूअल-स्टैक IPv4/IPv6 मशीनों पर, Bazel उसी कन्वेंशन का पालन करता है जिसका पालन Java करता है: अगर IPv4 चालू है, तो IPv4 को प्राथमिकता दी जाती है. कुछ मामलों में, जैसे कि जब IPv4 नेटवर्क बाहरी पतों को हल/पहुंच नहीं कर पाता है, तो इससे Network unreachable अपवाद हो सकते हैं और बिल्ड फ़ेल हो सकते हैं. इन मामलों में, java.net.preferIPv6Addresses=true सिस्टम प्रॉपर्टी का इस्तेमाल करके, Bazel के डिफ़ॉल्ट व्यवहार को बदला जा सकता है, ताकि वह आईपीवी6 को प्राथमिकता दे. खास तौर पर, इस बारे में जानकारी मिलती है:

  • --host_jvm_args=-Djava.net.preferIPv6Addresses=true स्टार्टअप विकल्प का इस्तेमाल करें. उदाहरण के लिए, अपनी .bazelrc फ़ाइल में यह लाइन जोड़ें:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • अगर आपको ऐसे Java बिल्ड टारगेट चलाने हैं जिन्हें इंटरनेट से कनेक्ट करने की भी ज़रूरत है (कभी-कभी इंटिग्रेशन टेस्ट के लिए इसकी ज़रूरत होती है), तो --jvmopt=-Djava.net.preferIPv6Addresses=true टूल फ़्लैग का भी इस्तेमाल करें. उदाहरण के लिए, अपनी .bazelrc फ़ाइल में यह लाइन जोड़ें:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • अगर rules_jvm_external का इस्तेमाल किया जा रहा है, तो COURSIER_OPTS एनवायरमेंट वैरिएबल में -Djava.net.preferIPv6Addresses=true भी जोड़ें. उदाहरण के लिए, डिपेंडेंसी वर्शन रिज़ॉल्यूशन के लिए, Coursier के लिए JVM विकल्प उपलब्ध कराएं

ट्रांज़िटिव डिपेंडेंसी

Bazel, सिर्फ़ आपकी WORKSPACE फ़ाइल में दी गई डिपेंडेंसी को पढ़ता है. अगर आपका प्रोजेक्ट (A) किसी ऐसे प्रोजेक्ट (B) पर निर्भर करता है जिसकी WORKSPACE फ़ाइल में किसी तीसरे प्रोजेक्ट (C) पर निर्भरता की जानकारी दी गई है, तो आपको अपने प्रोजेक्ट की WORKSPACE फ़ाइल में B और C, दोनों को जोड़ना होगा. इस ज़रूरी शर्त की वजह से, WORKSPACE फ़ाइल का साइज़ बढ़ सकता है. हालांकि, इससे इस बात की संभावना कम हो जाती है कि एक लाइब्रेरी में C का वर्शन 1.0 और दूसरी लाइब्रेरी में C का वर्शन 2.0 शामिल हो.

बाहरी डिपेंडेंसी को कैश मेमोरी में सेव करना

डिफ़ॉल्ट रूप से, Bazel सिर्फ़ तब बाहरी डिपेंडेंसी को फिर से डाउनलोड करेगा, जब उनकी परिभाषा में बदलाव होगा. Bazel, परिभाषा में शामिल फ़ाइलों (जैसे कि पैच या BUILD फ़ाइलें) में किए गए बदलावों को भी ध्यान में रखता है.

फिर से डाउनलोड करने के लिए, bazel sync का इस्तेमाल करें.

लेआउट

सभी बाहरी डिपेंडेंसी, output base में मौजूद सबडायरेक्ट्री external के तहत किसी डायरेक्ट्री में डाउनलोड की जाती हैं. लोकल रिपॉज़िटरी के मामले में, नई डायरेक्ट्री बनाने के बजाय वहां एक सिमलंक बनाया जाता है. external डायरेक्ट्री देखने के लिए, यह कमांड चलाएं:

ls $(bazel info output_base)/external

ध्यान दें कि bazel clean चलाने से, बाहरी डायरेक्ट्री असल में नहीं मिटेगी. सभी बाहरी आर्टफ़ैक्ट हटाने के लिए, bazel clean --expunge का इस्तेमाल करें.

ऑफ़लाइन बिल्ड

कभी-कभी, ऑफ़लाइन तरीके से बिल्ड चलाना ज़रूरी या बेहतर होता है. फ़्लाइट से यात्रा करने जैसे सामान्य इस्तेमाल के मामलों में, bazel fetch या bazel sync की मदद से ज़रूरी रिपॉज़िटरी को prefetching करना काफ़ी हो सकता है. इसके अलावा, --nofetch विकल्प का इस्तेमाल करके, बिल्ड के दौरान अन्य रिपॉज़िटरी को फ़ेच करने की सुविधा बंद की जा सकती है.

अगर आपको पूरी तरह से ऑफ़लाइन बिल्ड बनाने हैं, तो ज़रूरी फ़ाइलें Bazel के अलावा किसी अन्य इकाई को उपलब्ध करानी होंगी. इसके लिए, Bazel --distdir विकल्प का इस्तेमाल किया जा सकता है. जब भी कोई रिपॉज़िटरी नियम, Bazel से ctx.download या ctx.download_and_extract के ज़रिए कोई फ़ाइल फ़ेच करने के लिए कहता है और ज़रूरी फ़ाइल का हैश सम उपलब्ध कराता है, तो Bazel सबसे पहले उस विकल्प के ज़रिए बताई गई डायरेक्ट्री में, दिए गए पहले यूआरएल के बेसनेम से मेल खाने वाली फ़ाइल की तलाश करेगा. अगर हैश मेल खाता है, तो Bazel उस लोकल कॉपी का इस्तेमाल करेगा.

Bazel खुद इस तकनीक का इस्तेमाल करके, डिस्ट्रिब्यूशन आर्टफ़ैक्ट से ऑफ़लाइन बूटस्ट्रैप करता है. यह ऐसा, ज़रूरी सभी बाहरी डिपेंडेंसी को इकट्ठा करके करता है. इसके बाद, उन्हें किसी इंटरनल distdir_tar में सेव करता है.

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

सबसे सही तरीके

रिपॉज़िटरी के नियम

आम तौर पर, रिपॉज़िटरी के नियम में ये बातें शामिल होनी चाहिए:

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

जब भी हो सके, repository_ctx.execute का इस्तेमाल न करें. उदाहरण के लिए, अगर आपको Bazel के अलावा किसी अन्य C++ लाइब्रेरी का इस्तेमाल करना है, जिसे Make का इस्तेमाल करके बनाया गया है, तो repository_ctx.download() चलाने के बजाय, repository_ctx.download() का इस्तेमाल करना बेहतर होता है. इसके बाद, एक BUILD फ़ाइल लिखें, जो इसे बनाए.ctx.execute(["make"])

git_repository और new_git_repository के बजाय, http_archive को प्राथमिकता दें. इसकी ये वजहें हैं:

  • Git रिपॉज़िटरी के नियम, सिस्टम git(1) पर निर्भर करते हैं. वहीं, एचटीटीपी डाउनलोडर को Bazel में बनाया गया है और इसकी कोई सिस्टम डिपेंडेंसी नहीं है.
  • http_archive में, urls की सूची को मिरर के तौर पर इस्तेमाल किया जा सकता है. वहीं, git_repository में सिर्फ़ एक remote का इस्तेमाल किया जा सकता है.
  • http_archive, रिपॉज़िटरी कैश मेमोरी के साथ काम करता है, लेकिन git_repository के साथ नहीं. ज़्यादा जानकारी के लिए, #5116 देखें.

bind() का इस्तेमाल न करें. इसकी समस्याओं और विकल्पों के बारे में ज़्यादा जानने के लिए, "बाइंड हटाने पर विचार करें" लेख पढ़ें.