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

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 टारगेट है, तो आपका प्रोजेक्ट इसे @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 में जोड़ा जा सकता है.

कमांड लाइन से डेटा स्टोर करने की जगहों को बदलना

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

उदाहरण के लिए, लोकल डायरेक्ट्री /path/to/local/foo से @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 अपवाद और बिल्ड में गड़बड़ियां हो सकती हैं. इन मामलों में, Bazel के व्यवहार को बदलकर, IPv6 को प्राथमिकता दी जा सकती है. इसके लिए, java.net.preferIPv6Addresses=true सिस्टम प्रॉपर्टी का इस्तेमाल करें. खास तौर पर, इस बारे में जानकारी मिलती है:

  • स्टार्टअप विकल्प --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 का इस्तेमाल करें.

लेआउट

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

ls $(bazel info output_base)/external

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

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

कभी-कभी ऑफ़लाइन तरीके से बिल्ड चलाना ज़रूरी होता है या ऐसा करना फ़ायदेमंद होता है. आसान इस्तेमाल के उदाहरणों के लिए, जैसे कि हवाई जहाज़ में यात्रा करना, prefetching `--nofetch` विकल्प का इस्तेमाल करके, बिल्ड के दौरान अन्य डेटा स्टोर करने की जगहों को फ़ेच करने की सुविधा बंद की जा सकती है.--nofetch

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

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

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

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

डेटा स्टोर करने की जगह के नियम

डेटा स्टोर करने की जगह के नियम की ज़िम्मेदारी आम तौर पर यह होनी चाहिए:

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

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

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

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