Bazel में लॉकफ़ाइल की सुविधा की मदद से, किसी प्रोजेक्ट के लिए ज़रूरी सॉफ़्टवेयर लाइब्रेरी या पैकेज के खास वर्शन या डिपेंडेंसी रिकॉर्ड की जा सकती हैं. यह कुकी, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के नतीजे को सेव करके ऐसा करती है. लॉकफ़ाइल से, दोबारा बनाए जा सकने वाले बिल्ड को बढ़ावा मिलता है. इससे डेवलपमेंट एनवायरमेंट में एक जैसा माहौल बना रहता है. इसके अलावा, यह Bazel को प्रोजेक्ट की डिपेंडेंसी में हुए बदलावों से प्रभावित न होने वाले रिज़ॉल्यूशन प्रोसेस के हिस्सों को स्किप करने की अनुमति देकर, बिल्ड की क्षमता को बढ़ाता है. इसके अलावा, लॉकफ़ाइल बाहरी लाइब्रेरी में अचानक होने वाले अपडेट या बड़े बदलावों को रोककर, स्थिरता को बेहतर बनाती है. इससे गड़बड़ियां होने का खतरा कम हो जाता है.
लॉकफ़ाइल जनरेट करना
लॉकफ़ाइल, वर्कस्पेस रूट में MODULE.bazel.lock नाम से जनरेट होती है. इसे बिल्ड प्रोसेस के दौरान बनाया या अपडेट किया जाता है. खास तौर पर, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के बाद. खास तौर पर, इसमें सिर्फ़ वे डिपेंडेंसी शामिल होती हैं जो बिल्ड के मौजूदा इनवोकेशन में शामिल हैं.
जब प्रोजेक्ट में ऐसे बदलाव होते हैं जिनसे उसकी डिपेंडेंसी पर असर पड़ता है, तो लॉकफ़ाइल अपने-आप अपडेट हो जाती है. इससे नई स्थिति दिखती है. इससे यह पक्का होता है कि लॉकफ़ाइल, मौजूदा बिल्ड के लिए ज़रूरी डिपेंडेंसी के खास सेट पर फ़ोकस करती है. साथ ही, प्रोजेक्ट की हल की गई डिपेंडेंसी के बारे में सटीक जानकारी देती है.
Lockfile का इस्तेमाल
लॉकफ़ाइल को फ़्लैग --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 या किसी मॉड्यूल एक्सटेंशन में कोई गड़बड़ी हो.
लॉकफ़ाइल का कॉन्टेंट
लॉकफ़ाइल में यह तय करने के लिए सभी ज़रूरी जानकारी होती है कि प्रोजेक्ट की स्थिति में बदलाव हुआ है या नहीं. इसमें प्रोजेक्ट को मौजूदा स्थिति में बनाने का नतीजा भी शामिल होता है. लॉकफ़ाइल में दो मुख्य हिस्से होते हैं:
- मॉड्यूल रिज़ॉल्यूशन के लिए इनपुट के तौर पर इस्तेमाल की जाने वाली सभी रिमोट फ़ाइलों के हैश.
- हर मॉड्यूल एक्सटेंशन के लिए, लॉकफ़ाइल में ऐसे इनपुट शामिल होते हैं जो इसे प्रभावित करते हैं. इन्हें
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 मैप से हटा दिया जाता है.
अगर कोई एक्सटेंशन ऑपरेटिंग सिस्टम या आर्किटेक्चर टाइप से अलग है, तो इस सेक्शन में सिर्फ़ एक "सामान्य" एंट्री दिखेगी. इसके अलावा, ओएस, आर्किटेक्चर या दोनों के नाम से कई एंट्री शामिल की जाती हैं. इनमें से हर एंट्री, इन खास बातों के आधार पर एक्सटेंशन का आकलन करने के नतीजे से मेल खाती है.
एक्सटेंशन मैप में मौजूद हर एंट्री, इस्तेमाल किए गए एक्सटेंशन से मेल खाती है. इसे इसकी फ़ाइल और नाम से पहचाना जाता है. हर एंट्री की वैल्यू में, उस एक्सटेंशन से जुड़ी ज़रूरी जानकारी होती है:
bzlTransitiveDigest, एक्सटेंशन लागू करने और इसके ज़रिए ट्रांज़िटिव तरीके से लोड की गई .bzl फ़ाइलों का डाइजेस्ट है.usagesDigest, डिपेंडेंसी ग्राफ़ में एक्सटेंशन के इस्तेमाल की खास जानकारी है. इसमें सभी टैग शामिल होते हैं.- इसके अलावा, ऐसे फ़ील्ड जिनकी जानकारी नहीं दी गई है. ये एक्सटेंशन के अन्य इनपुट को ट्रैक करते हैं. जैसे, एक्सटेंशन जिन फ़ाइलों या डायरेक्ट्री को पढ़ता है उनका कॉन्टेंट या एक्सटेंशन जिन एनवायरमेंट वैरिएबल का इस्तेमाल करता है.
generatedRepoSpecs, एक्सटेंशन से बनाई गई रिपॉज़िटरी को मौजूदा इनपुट के साथ एन्कोड करता है.- ज़रूरी नहीं है कि
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
इसके बाद, ड्राइवर का इस्तेमाल करने वाले हर डेवलपर को इसे एक बार रजिस्टर करना होगा. इसके लिए, यह तरीका अपनाएं:
- jq (1.5 या इसके बाद का वर्शन) इंस्टॉल करें.
- ये कमांड चलाएं:
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 फ़ील्ड में मौजूद डेटा को मर्ज करने से जुड़ी सामान्य समस्याओं को आसानी से हल किया जा सकता है. इसके लिए, आपको सिर्फ़ यह करना होगा कि दोनों फ़ील्ड में मौजूद सभी एंट्री को सेव कर लें.
मर्ज करने से जुड़े अन्य विवादों को मैन्युअल तरीके से हल नहीं किया जाना चाहिए. इसके बजाय:
git reset MODULE.bazel.lock && git checkout MODULE.bazel.lockकी मदद से, लॉकफ़ाइल की पिछली स्थिति को वापस लाएं.MODULE.bazelफ़ाइल में मौजूद किसी भी तरह के विवाद को हल करें.- लॉकफ़ाइल को अपडेट करने के लिए,
bazel mod depsचलाएं.