Bazel लॉकफ़ाइल

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

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

Lockfile Generation

लॉकफ़ाइल, वर्कस्पेस रूट में MODULE.bazel.lock नाम से जनरेट होती है. इसे बिल्ड प्रोसेस के दौरान बनाया या अपडेट किया जाता है. खास तौर पर, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के बाद. खास तौर पर, इसमें सिर्फ़ वे डिपेंडेंसी शामिल होती हैं जो बिल्ड के मौजूदा इनवोकेशन में शामिल होती हैं.

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

लॉकफ़ाइल का इस्तेमाल

लॉकफ़ाइल को फ़्लैग --lockfile_mode से कंट्रोल किया जा सकता है. इससे, प्रोजेक्ट की स्थिति लॉकफ़ाइल से अलग होने पर Bazel के व्यवहार को पसंद के मुताबिक बनाया जा सकता है. ये मोड उपलब्ध हैं:

  • update (डिफ़ॉल्ट): लॉकफ़ाइल में मौजूद जानकारी का इस्तेमाल करके, जानी-पहचानी रजिस्ट्री फ़ाइलों को डाउनलोड करने से बचें. साथ ही, उन एक्सटेंशन का फिर से आकलन करने से बचें जिनके नतीजे अब भी अप-टू-डेट हैं. अगर जानकारी मौजूद नहीं है, तो उसे लॉकफ़ाइल में जोड़ दिया जाएगा. इस मोड में Bazel, उन डिपेंडेंसी के लिए भी बदलाव की जा सकने वाली जानकारी को रीफ़्रेश नहीं करता जिनमें कोई बदलाव नहीं हुआ है. जैसे, हटाए गए वर्शन.
  • refresh: यह update की तरह ही होता है. हालांकि, इस मोड पर स्विच करने पर, बदलाव की जा सकने वाली जानकारी हमेशा रीफ़्रेश होती है. साथ ही, इस मोड में हर घंटे में एक बार रीफ़्रेश होती है.
  • error: update की तरह, लेकिन अगर कोई जानकारी मौजूद नहीं है या पुरानी है, तो Bazel गड़बड़ी के साथ काम नहीं करेगा. यह मोड, लॉकफ़ाइल में कभी बदलाव नहीं करता. साथ ही, रिज़ॉल्यूशन के दौरान नेटवर्क अनुरोध नहीं करता. reproducible के तौर पर मार्क किए गए मॉड्यूल एक्सटेंशन अब भी नेटवर्क अनुरोध कर सकते हैं. हालाँकि, उनसे हमेशा एक जैसा नतीजा मिलने की उम्मीद की जाती है.
  • off: लॉकफ़ाइल की न तो जांच की जाती है और न ही उसे अपडेट किया जाता है.

लॉकफ़ाइल के फ़ायदे

लॉकफ़ाइल से कई फ़ायदे मिलते हैं. साथ ही, इसे कई तरीकों से इस्तेमाल किया जा सकता है:

  • फिर से बनाए जा सकने वाले बिल्ड. सॉफ़्टवेयर लाइब्रेरी के खास वर्शन या डिपेंडेंसी कैप्चर करके, लॉकफ़ाइल यह पक्का करती है कि अलग-अलग एनवायरमेंट और समय के साथ, बिल्ड को फिर से बनाया जा सके. डेवलपर अपने प्रोजेक्ट बनाते समय, लगातार और अनुमान के मुताबिक नतीजे पा सकते हैं.

  • तेज़ी से इंक्रीमेंटल रिज़ॉल्यूशन. लॉकफ़ाइल की मदद से Bazel, रजिस्ट्री की उन फ़ाइलों को डाउनलोड करने से बचता है जिनका इस्तेमाल पिछली बिल्ड में किया जा चुका है. इससे बिल्ड की प्रोसेस काफ़ी बेहतर हो जाती है. खास तौर पर, उन स्थितियों में जहां रिज़ॉल्यूशन में ज़्यादा समय लग सकता है.

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

    अपडेट के काम न करने या उनकी जांच न होने की वजह से, परफ़ॉर्मेंस में गिरावट नहीं आती.

छिपी हुई लॉकफ़ाइल

Bazel, "$(bazel info output_base)"/MODULE.bazel.lock पर एक और लॉकफ़ाइल भी सेव करता है. इस लॉकफ़ाइल के फ़ॉर्मैट और कॉन्टेंट के बारे में साफ़ तौर पर नहीं बताया गया है. इसका इस्तेमाल सिर्फ़ परफ़ॉर्मेंस ऑप्टिमाइज़ेशन के लिए किया जाता है. bazel clean --expunge के ज़रिए, इसे आउटपुट बेस के साथ मिटाया जा सकता है. हालांकि, ऐसा करने की ज़रूरत तब पड़ती है, जब Bazel या मॉड्यूल एक्सटेंशन में कोई गड़बड़ी हो.

लॉकफ़ाइल का कॉन्टेंट

लॉकफ़ाइल में, यह तय करने के लिए ज़रूरी जानकारी होती है कि प्रोजेक्ट की स्थिति में बदलाव हुआ है या नहीं. इसमें प्रोजेक्ट को मौजूदा स्थिति में बनाने का नतीजा भी शामिल होता है. लॉकफ़ाइल के दो मुख्य हिस्से होते हैं:

  1. मॉड्यूल रिज़ॉल्यूशन के लिए इनपुट के तौर पर इस्तेमाल की जाने वाली सभी रिमोट फ़ाइलों के हैश.
  2. हर मॉड्यूल एक्सटेंशन के लिए, लॉकफ़ाइल में ऐसे इनपुट शामिल होते हैं जो इसे प्रभावित करते हैं. इन्हें bzlTransitiveDigest, usagesDigest, और अन्य फ़ील्ड के तौर पर दिखाया जाता है. साथ ही, इसमें उस एक्सटेंशन को चलाने का आउटपुट भी शामिल होता है. इसे generatedRepoSpecs कहा जाता है

यहां एक उदाहरण दिया गया है, जिसमें लॉकफ़ाइल का स्ट्रक्चर दिखाया गया है. साथ ही, हर सेक्शन के बारे में जानकारी दी गई है:

{
  "lockFileVersion": 10,
  "registryFileHashes": {
    "https://bcr.bazel.build/bazel_registry.json": "8a28e4af...5d5b3497",
    "https://bcr.bazel.build/modules/foo/1.0/MODULE.bazel": "7cd0312e...5c96ace2",
    "https://bcr.bazel.build/modules/foo/2.0/MODULE.bazel": "70390338... 9fc57589",
    "https://bcr.bazel.build/modules/foo/2.0/source.json": "7e3a9adf...170d94ad",
    "https://registry.mycorp.com/modules/foo/1.0/MODULE.bazel": "not found",
    ...
  },
  "selectedYankedVersions": {
    "foo@2.0": "Yanked for demo purposes"
  },
  "moduleExtensions": {
    "//:extension.bzl%lockfile_ext": {
      "general": {
        "bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
        "usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
        ...,
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      }
    },
    "//:extension.bzl%lockfile_ext2": {
      "os:macos": {
        "bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
        "usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
        ...,
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      },
      "os:linux": {
        "bzlTransitiveDigest": "eWDzxG/aLsyY3Ubrto....+Jp4maQvEPxn0pLK=",
        "usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
        ...,
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      }
    }
  }
}

रजिस्ट्री फ़ाइल के हैश

registryFileHashes सेक्शन में, मॉड्यूल रिज़ॉल्यूशन के दौरान ऐक्सेस की गई रिमोट रजिस्ट्री की सभी फ़ाइलों के हैश शामिल होते हैं. एक जैसे इनपुट और सभी रिमोट इनपुट हैश किए जाने पर, रिज़ॉल्यूशन एल्गोरिदम पूरी तरह से डिटरमिनिस्टिक होता है. इससे, रिज़ॉल्यूशन के नतीजे को पूरी तरह से दोहराया जा सकता है. साथ ही, लॉकफ़ाइल में रिमोट जानकारी के बहुत ज़्यादा डुप्लीकेट होने से बचा जा सकता है. ध्यान दें कि इसके लिए, यह रिकॉर्ड करना भी ज़रूरी है कि जब किसी खास रजिस्ट्री में कोई मॉड्यूल मौजूद न हो, लेकिन कम प्राथमिकता वाली रजिस्ट्री में मौजूद हो. उदाहरण में "नहीं मिला" एंट्री देखें. बदली जा सकने वाली इस जानकारी को bazel mod deps --lockfile_mode=refresh के ज़रिए अपडेट किया जा सकता है.

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

चुने गए यांक किए गए वर्शन

selectedYankedVersions सेक्शन में, मॉड्यूल के वे वर्शन शामिल होते हैं जिन्हें मॉड्यूल रिज़ॉल्यूशन ने चुना था. आम तौर पर, इस वजह से पैकेज बनाने में गड़बड़ी होती है. इसलिए, यह सेक्शन सिर्फ़ तब दिखता है, जब --allow_yanked_versions या BZLMOD_ALLOW_YANKED_VERSIONS के ज़रिए, हटाए गए वर्शन को इस्तेमाल करने की अनुमति दी गई हो.

इस फ़ील्ड की ज़रूरत इसलिए होती है, क्योंकि मॉड्यूल फ़ाइलों की तुलना में, यांक किए गए वर्शन की जानकारी में बदलाव किया जा सकता है. इसलिए, इसे हैश से रेफ़रंस नहीं किया जा सकता. इस जानकारी को bazel mod deps --lockfile_mode=refresh के ज़रिए अपडेट किया जा सकता है.

मॉड्यूल एक्सटेंशन

moduleExtensions सेक्शन एक मैप होता है. इसमें सिर्फ़ वे एक्सटेंशन शामिल होते हैं जिनका इस्तेमाल मौजूदा इनवोकेशन में किया गया है या पहले किया गया था. इसमें उन एक्सटेंशन को शामिल नहीं किया जाता जिनका अब इस्तेमाल नहीं किया जाता. दूसरे शब्दों में कहें, तो अगर किसी एक्सटेंशन का इस्तेमाल अब डिपेंडेंसी ग्राफ़ में नहीं किया जा रहा है, तो उसे moduleExtensions मैप से हटा दिया जाता है.

अगर कोई एक्सटेंशन ऑपरेटिंग सिस्टम या आर्किटेक्चर टाइप से अलग है, तो इस सेक्शन में सिर्फ़ एक "सामान्य" एंट्री दिखती है. इसके अलावा, ओएस, आर्किटेक्चर या दोनों के नाम से कई एंट्री शामिल की जाती हैं. इनमें से हर एंट्री, इन खास बातों के आधार पर एक्सटेंशन का आकलन करने के नतीजे से जुड़ी होती है.

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

  1. bzlTransitiveDigest, एक्सटेंशन लागू करने और इसके ज़रिए ट्रांज़िटिव तरीके से लोड की गई .bzl फ़ाइलों का डाइजेस्ट होता है.
  2. usagesDigest, डिपेंडेंसी ग्राफ़ में एक्सटेंशन के इस्तेमाल की खास जानकारी है. इसमें सभी टैग शामिल होते हैं.
  3. इसके अलावा, ऐसे फ़ील्ड जिनके बारे में नहीं बताया गया है. ये एक्सटेंशन के अन्य इनपुट को ट्रैक करते हैं. जैसे, एक्सटेंशन जिन फ़ाइलों या डायरेक्ट्री को पढ़ता है उनका कॉन्टेंट या एक्सटेंशन जिन एनवायरमेंट वैरिएबल का इस्तेमाल करता है.
  4. generatedRepoSpecs, एक्सटेंशन से बनाई गई रिपॉज़िटरी को मौजूदा इनपुट के साथ एन्कोड करता है.
  5. ज़रूरी नहीं है कि moduleExtensionMetadata फ़ील्ड में एक्सटेंशन से मिला मेटाडेटा शामिल हो. जैसे, एक्सटेंशन से बनाई गई कुछ रिपॉज़िटरी को रूट मॉड्यूल से use_repo के ज़रिए इंपोर्ट किया जाना चाहिए या नहीं. इस जानकारी का इस्तेमाल bazel mod tidy कमांड के लिए किया जाता है.

मॉड्यूल एक्सटेंशन, लॉकफ़ाइल में शामिल होने से ऑप्ट आउट कर सकते हैं. इसके लिए, उन्हें reproducible = True के साथ मेटाडेटा भेजना होगा. ऐसा करके, वे यह वादा करते हैं कि एक जैसे इनपुट मिलने पर, वे हमेशा एक जैसी रिपॉज़िटरी बनाएंगे.

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

लॉकफ़ाइल की सुविधा का ज़्यादा से ज़्यादा फ़ायदा पाने के लिए, इन सबसे सही तरीकों को अपनाएं:

  • प्रोजेक्ट की डिपेंडेंसी या कॉन्फ़िगरेशन में हुए बदलावों को दिखाने के लिए, लॉकफ़ाइल को समय-समय पर अपडेट करें. इससे यह पक्का होता है कि बाद के बिल्ड, डिपेंडेंसी के सबसे अप-टू-डेट और सटीक सेट पर आधारित हों. सभी एक्सटेंशन को एक साथ लॉक करने के लिए, bazel mod deps --lockfile_mode=update चलाएं.

  • साथ मिलकर काम करने के लिए, वर्शन कंट्रोल में लॉकफ़ाइल शामिल करें. इससे यह पक्का किया जा सकेगा कि टीम के सभी सदस्यों के पास एक ही लॉकफ़ाइल का ऐक्सेस हो. इससे पूरे प्रोजेक्ट में डेवलपमेंट एनवायरमेंट एक जैसा रहेगा.

  • Bazel को चलाने के लिए, bazelisk का इस्तेमाल करें. साथ ही, वर्शन कंट्रोल में .bazelversion फ़ाइल शामिल करें. यह फ़ाइल, लॉकफ़ाइल से जुड़े Bazel वर्शन के बारे में बताती है. Bazel, आपके बिल्ड की डिपेंडेंसी है. इसलिए, लॉकफ़ाइल Bazel के वर्शन के हिसाब से होती है. साथ ही, पिछले वर्शन के साथ काम करने वाले Bazel के रिलीज़ होने पर भी यह बदल जाएगी. bazelisk का इस्तेमाल करने से यह पक्का किया जा सकता है कि सभी डेवलपर, Bazel के ऐसे वर्शन का इस्तेमाल कर रहे हैं जो लॉकफ़ाइल से मेल खाता हो.

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

मर्ज करने से जुड़ी समस्याएं

लॉकफ़ाइल फ़ॉर्मैट को इस तरह से डिज़ाइन किया गया है कि मर्ज करने से जुड़ी समस्याएं कम से कम हों. हालांकि, ऐसा अब भी हो सकता है.

अपने-आप ठीक होने की सुविधा

Bazel, इन विवादों को अपने-आप हल करने के लिए, कस्टम git merge driver उपलब्ध कराता है.

ड्राइवर को सेट अप करने के लिए, इस लाइन को अपनी Git रिपॉज़िटरी के रूट में मौजूद .gitattributes फ़ाइल में जोड़ें:

# A custom merge driver for the Bazel lockfile.
# https://bazel.build/external/lockfile#automatic-resolution
MODULE.bazel.lock merge=bazel-lockfile-merge

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

  1. jq (1.5 या इसके बाद का वर्शन) इंस्टॉल करें.
  2. ये कमांड चलाएं:
jq_script=$(curl https://raw.githubusercontent.com/bazelbuild/bazel/master/scripts/bazel-lockfile-merge.jq)
printf '%s\n' "${jq_script}" | less # to optionally inspect the jq script
git config --global merge.bazel-lockfile-merge.name   "Merge driver for the Bazel lockfile (MODULE.bazel.lock)"
git config --global merge.bazel-lockfile-merge.driver "jq -s '${jq_script}' -- %O %A %B > %A.jq_tmp && mv %A.jq_tmp %A"

मैन्युअल तरीके से समस्या हल करना

registryFileHashes और selectedYankedVersions फ़ील्ड में मौजूद डेटा को मर्ज करने से जुड़ी सामान्य समस्याओं को आसानी से हल किया जा सकता है. इसके लिए, आपको सिर्फ़ यह करना होगा कि दोनों फ़ील्ड में मौजूद सभी एंट्री को सेव कर लें.

मर्ज करने से जुड़े अन्य विवादों को मैन्युअल तरीके से हल नहीं किया जाना चाहिए. इसके बजाय:

  1. git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock का इस्तेमाल करके, लॉकफ़ाइल की पिछली स्थिति को वापस लाएं.
  2. MODULE.bazel फ़ाइल में मौजूद किसी भी तरह के विवाद को हल करें.
  3. लॉकफ़ाइल को अपडेट करने के लिए, bazel mod deps चलाएं.