فا نیکسی

4.7. در حال ساخت

همان‌طور که در صفحه اصلی درباره درایویشن‌ها بحث شد:

یک درایویشن مشخصاتی برای اجرای یک فایل اجرایی روی ورودی‌های دقیقاً تعریف‌شده به‌منظور تولید یک یا چند شیء انبار است.

این صفحه به توضیح ساختن (building) یک درایویشن می‌پردازد؛ یعنی پیروی از دستورالعمل‌های موجود در درایویشن برای اجرای واقعی فایل اجرایی. برخی از عناصر درایویشن‌ها خودتوضیح هستند. برای مثال، آرگومان‌های مشخص‌شده در درایویشن واقعاً همان آرگومان‌هایی هستند که به فایل اجرایی منتقل می‌شوند. با این حال، در موارد دیگر، مراحل رایج اضافی توسط Nix برای تمام درایویشن‌ها انجام می‌شود که بیشتر مربوط به راه‌اندازی محیط ساخت و جمع‌آوری خروجی‌های ساخته‌شده است.

مهم‌ترین ملاحظه طراحی در فرآیند ساخت، قطعی بودن (determinism) است. سیستم‌عامل‌های مرسوم معمولاً با در نظر گرفتن قطعی بودن طراحی نمی‌شوند. اما قطعی بودن برای تبدیل کردن کش ساخت Nix به یک انتزاع شفاف ضروری است.

توضیح

برای مثال، هیچ‌کس نمی‌خواهد یک درایویشن را کمی تغییر دهد و سپس متوجه شود که به دلیلی نامرتبط دیگر ساخته نمی‌شود، زیرا درایویشن اصلی نیز دیگر ساخته نمی‌شود، اما برخورد کش (cache hit) روی درایویشن اصلی این موضوع را پنهان کرده بود. ما می‌خواهیم ساخت‌هایی که یک‌بار با موفقیت انجام شده‌اند، همچنان به موفقیت خود ادامه دهند تا ترسی از تغییر دادن دستورالعمل‌های ساخت قدیمی نداشته باشیم. قطعی بودن همان چیزی است که باعث می‌شود کارهایی که یک‌بار کار می‌کردند، همچنان به کار کردن خود ادامه دهند.

چرخه عمر یک ساخت را می‌توان به ۳ بخش تقسیم کرد:

  1. راه‌اندازی فرآیند سازنده با محیط مناسب، از جمله آرگومان‌های صحیح فرآیند، متغیرهای محیطی و وضعیت سیستم‌فایل.

  2. انتظار برای خروج فرآیند سازنده و جمع‌آوری وضعیت خروج آن. کد خروج 0 به معنای موفقیت است؛ هر چیز دیگری یک شکست ساخت محسوب می‌شود. (به بیان دقیق، Nix با انتظار برای بسته‌شدن جریان‌های خروجی استاندارد و خطا، خروج فرآیند را تشخیص می‌دهد. اگر یک سازنده به‌طور صریح این جریان‌ها را بدون خروج ببندد، Nix آن را می‌کشد و ساخت را شکست‌خورده تلقی می‌کند. بنابراین فرآیندها باید بدون بستن صریح آن جریان‌های استاندارد خارج شوند و اجازه دهند خروج فرآیند آن‌ها را به‌طور ضمنی ببندد.)

    Nix همچنین خروجی استاندارد و خطای فرآیند را ثبت (log) می‌کند، اما این کار صرفاً برای راحتی انسان است و تاثیری بر رفتار سیستم ندارد. (فرآیندهای سازنده هیچ ایده‌ای ندارند که مصرف‌کننده خروجی استاندارد و خطای آن‌ها با مستر شبه‌پایانه (pseudo-terminal master) چه می‌کند، فقط می‌دانند که آن‌ها واقعاً مصرف می‌شوند تا بافرها پر نشوند و غیره، و نوشتن روی هر جریان خروجی استاندارد به موفقیت ادامه خواهد داد. در عمل، Nix لاگ را در /nix/var/log/nix ذخیره خواهد کرد)

  3. پردازش خروجی‌ها.

    به طور سنتی، این کار تنها پس از خروج سازنده اتفاق می‌افتد: فرآیند سازنده باید برای هر خروجی که درایویشن قرار است تولید کند، فایل‌هایی را باقی گذاشته باشد و آن فایل‌ها پردازش شوند تا به اشیاء انبار واقعی تبدیل شوند. به عنوان جایگزین، سازنده ممکن است در حین اجرا پیام‌هایی را به Nix ارسال کند و اشیاء سیستم‌فایل را ایجاد کرده و آن‌ها را به نام خروجی پیوند دهد. این امر به خروجی‌ها اجازه می‌دهد تا به صورت همزمان در طول ساخت پردازش شوند، به خروجی‌ها اجازه می‌دهد به سایر اشیاء انبار تازه ایجاد شده وابسته باشند، و همچنین برخی مسائل پیچیده مربوط به آدرس‌دهی محتوا (content-addressing) و ارجاعات خروجی به خروجی را حل می‌کند. اگر پردازش موفقیت‌آمیز باشد، اشیاء انبار حاصل به عنوان (نتایج) یک ساخت موفق با درایویشن مرتبط می‌شوند.

گام (۳) توسط نیکس انجام می‌شود؛ یا به صورت خارجی نسبت به ساخت (در حالت سنتی، که روی داده‌های غیرفعالی که پس از خروج یا متوقف شدن سازنده باقی مانده‌اند کار می‌کند) یا به صورت همزمان با آن (در حالت IPC). با این حال، گام (۱) را بهتر است نه از دیدگاه نیکس، بلکه از دیدگاه فرآیند ساخت توصیف کرد.

توضیح

در نهایت، آنچه برای قطعی بودن (deterministic) اهمیت دارد، چیزهایی است که فرآیند ساخت می‌تواند مشاهده کند: چه منابعی (فایل‌ها، شبکه و غیره) را می‌تواند ببیند، چه سیستمکال‌هایی (syscalls) موفق یا ناموفق می‌شوند، و غیره. نیکس می‌تواند این کار را از طریق استراتژی‌های مختلف ایزوله کردن (namespaces، ماشین‌های مجازی، chroots و غیره) به دست آورد، اما فرآیند نباید بتواند تفاوت آن‌ها را تشخیص دهد. بنابراین ما ساخت را از دیدگاه فرآیند مشخص می‌کنیم، نه دیدگاه نیکس، تا روی چه چیزی تمرکز کنیم، نه چگونه.

چه درایویشن‌هایی را می‌توان ساخت

در واقع تنها برخی از درایویشن‌ها آماده‌ی ساخت هستند. به طور خاص، تنها درایویشن‌های حل‌شده را می‌توان ساخت. به عبارت دیگر، درایویشنی که به درایویشن‌های دیگر وابسته است، هنوز آماده‌ی ساخت نیست، زیرا ممکن است برخی از آن درایویشن‌های دیگر هنوز ساخته نشده باشند. اگر درایویشن‌های دیگر واقعاً همگی ساخته شده باشند، می‌توانیم با حل کردن درایویشن و تبدیل تمام ارجاعات ورودی درایویشن به مسیرهای انبار ساده، این واقعیت را مشاهده کنیم.

نکته

توجه داشته باشید که درایویشن‌های مبتنی بر آدرس ورودی به طور نادرستی حل می‌شوند. همان‌طور که در صفحه پیوند داده شده بحث شد، الگوریتم فعلی آدرس‌دهی ورودی، معادل بودن حل درایویشن‌ها (\(\sim_\mathrm{Drv}\)) را رعایت نمی‌کند. این بدان معناست که اگر نیکس یک درایویشن مبتنی بر آدرس ورودی را به درستی حل می‌کرد، درایویشن حل‌شده آدرس‌های ورودی متفاوتی داشت که انتظارات را نقض می‌کرد. بنابراین نیکس درایویشن را به طور نادرستی حل می‌کند، مسیرهای خروجی مبتنی بر آدرس ورودی اصلی خود را حفظ می‌کند و یک درایویشن نامعتبر ایجاد می‌کند که هم حل‌شده است و هم دستور دارد خروجی‌ها را در مسیرهای مورد انتظار اولیه ایجاد کند.

محیط فرآیند سازنده

این بخش نحوه اجرای builder را توصیف می‌کند.

جزئیات پیاده‌سازی

نیکس از انجام همزمان یک ساخت توسط چندین نمونه نیکس جلوگیری می‌کند، به عنوان مثال با به دست آوردن قفل‌های انحصاری فایل.

سیستم‌فایل

سازنده باید به یک سیستم‌فایل محدود دسترسی داشته باشد که در آن فقط اشیاء خاصی در دسترس هستند. مهم‌ترین فایل‌های در معرض دید، ورودی‌ها (سایر اشیاء انبار) درایویشن (حل‌شده) هستند. علاوه بر این، برخی فایل‌های دیگر نیز در معرض دید قرار می‌گیرند.

ورودی‌های انبار

سازنده در برابر یک سیستم‌فایل اجرا خواهد شد که در آن پوشه انبار شامل [بستار] ورودی‌ها است. به طور خاص، انباری را در نظر بگیرید که فقط حاوی همین بستار است. آن انبار مطابق با قوانین مشخص‌شده در مستندات قرار دادن اشیاء انبار در معرض دید در سیستم‌فایل‌های سیستم‌عامل در معرض سیستم‌فایل قرار می‌گیرد. این کار، چیدمان سیستم‌فایل انبار را که باید برای فرآیند سازنده قابل مشاهده باشد، به دقت تعریف می‌کند.

نکته

از نظر تاریخی، Nix حداقل محتویات انبار زیر را در اختیار سازنده (Builder) قرار می‌داد، اما به دلیل محدودیت‌های مربوط به قابلیت‌های مجازی‌سازی سیستم‌فایل سیستم‌عامل‌ها و تمایل به اجتناب از کپی کردن یا جابه‌جایی فایل‌ها، سایر اشیاء انبار را نیز به صورت دلخواه در معرض دید قرار می‌داد. این ابزار همچنان می‌تواند در ساخت‌های اصطلاحاً ایزوله‌نشده (unsandboxed) چنین کاری را انجام دهد.

این‌گونه ساخت‌ها ناامیدکننده و ناپایدار در نظر گرفته می‌شوند، اما در برابر درایویشن‌های غیرمخرب، کمتر از آنچه انتظار می‌رود عملکرد ضعیفی از خود نشان می‌دهند. دلیل این امر آن است که مسیرهای انبار نسبتاً غیرقابل‌پیش‌بینی هستند؛ بنابراین، یک برنامه خوش‌رفتار به‌طور اتفاقی با شیء انباری که نباید از آن مطلع می‌بود، مواجه نخواهد شد.

با تکامل سیستم‌عامل‌ها و توسعه‌ی پریمیتیوهای سیستم‌فایل بهتر، نیاز به غیرفعال کردن ایزوله‌سازی (sandboxing) طی سالیان متمادی به شدت کاهش یافته است و این روند در آینده نیز ادامه خواهد داشت.

خروجی‌ها باید در آن پوشه انبار به گونه‌ای ایجاد شوند که گویی اشیاء انبار معتبری هستند. (آن‌ها صرفاً در طول اجرای سازنده فایل هستند، اما در حین پردازش خروجی‌ها به اشیاء انبار مناسبی تبدیل خواهند شد.) متغیرهای محیطی برای هر خروجی مشخص می‌کنند که سازنده باید آن‌ها را کجا بنویسد؛ Nix تضمین می‌کند که این مسیرها هنگام اجرای سازنده هنوز وجود ندارند.

نکته

در ساخت‌های ایزوله (sandboxed)، اطمینان حاصل کردن از اینکه خروجی‌ها در پوشه انبار وجود ندارند، امری بدیهی است. در ساخت‌های ایزوله‌نشده (unsandboxed)، به طور کلی این کار دشوارتر است. در بدترین حالت، درایویشن در واقع بازنویسی می‌شود تا در عوض از مسیرهای خروجی متفاوتی استفاده شود و سپس خروجی‌ها پس از آن به مسیرهای خروجی مورد نظر بازگردانده می‌شوند. در حالت آدرس‌دهی محتوا (content-addressing)، بازنویسی در هر صورت لازم خواهد بود، اما در حالت آدرس‌دهی ورودی (input-addressing)، این یک افت قابل‌توجه است، زیرا هدف از آدرس‌دهی ورودی این است که با دانستن قبلی مسیرهای خروجی، از بازنویسی‌ها جلوگیری شود.

سایر وضعیت‌های سیستم‌فایل

  • پوشه کاری فعلی فرآیند سازنده یک پوشه موقت تازه خواهد بود. هنگامی که فرآیند شروع می‌شود، در ابتدا خالی است مگر برای چند فایل ورودی:

    • اگر __structuredAttrs فعال باشد: فایل .attrs.json (صفات درایویشن به صورت JSON) و فایل .attrs.sh (نسخه‌ای سازگار با Bash از همان). متغیرهای محیطی NIX_ATTRS_JSON_FILE و NIX_ATTRS_SH_FILE به ترتیب به این فایل‌ها اشاره می‌کنند.

    • اگر از passAsFile استفاده شود (فقط بدون __structuredAttrs): برای هر نام صفت فهرست‌شده، یک فایل .attr-<hash> که در آن <hash> هش SHA-256 رمزگذاری‌شده با Nix32 از نام صفت است. متغیر محیطی <name>Path به فایلی که حاوی مقدار صفت است اشاره می‌کند.

  • در ساخت‌های ایزوله، این پوشه در یک مسیر قطعی درون محیط ایزوله قرار دارد (که توسط تنظیمات sandbox-build-dir کنترل می‌شود، پیش‌فرض /build است). همچنین تنظیمات build-dir مختص هر انبار را برای مکان سمت هاست مشاهده کنید.

  • گره‌های دستگاه پایه برای عملیات ضروری (دستگاه تهی (null)، تولید اعداد تصادفی، جریان‌های استاندارد به عنوان یک شبه‌ترمینال)

(یک شبه‌پایانه (pseudo terminal) الزاماً ضروری نیست، زیرا جریان‌های استاندارد در حال ثبت گزارش (logging) غیرفعال هستند و برای تسهیل تعامل وجود ندارند. اما همچنان برای ترغیب برنامه‌ها به ثبت گزارش‌های زیباتر با استفاده از رنگ‌ها و غیره مفید است.)

  • در لینوکس: اطلاعات فرآیند از طریق /proc

  • اطلاعات حداقلی هویت کاربر و گروه

  • یک پیکربندی شبکه صرفاً لوپ‌بک با نام هاست تنظیم‌شده روی localhost

نکته

درایویشن‌های با خروجی ثابت به وضعیت‌های اضافی سیستم‌عامل دسترسی دارند تا ارتباط با دنیای خارج، مانند تفکیک نام شبکه و تأیید گواهی TLS را تسهیل کنند. این امر ضروری است زیرا برخلاف درایویشن‌های معمولی که کاملاً ایزوله (sandboxed) هستند، این درایویشن‌ها اجازه دسترسی به شبکه را دارند.

متغیرهای محیطی

محیط پاکسازی شده و مطابق موارد ذکر شده در بالا، روی صفت‌های درایویشن تنظیم می‌شود.

برای اکثر انواع درایویشن، این مورد باید حداقل شامل موارد زیر باشد:

  • برای هر خروجی اعلام‌شده در outputs، متغیر محیطی مربوطه طوری تنظیم می‌شود که به مسیر مورد نظر در انبار Nix برای آن خروجی اشاره کند. هر مسیر خروجی، حاصل به هم چسباندن هش رمزنگاری‌شده‌ی تمام ورودی‌های ساخت، صفت name و نام خروجی است. (اگر نام خروجی out باشد، از آن صرف‌نظر می‌شود.)

علاوه بر این، متغیرهای زیر تنظیم می‌شوند:

  • متغیر NIX_BUILD_TOP حاوی مسیر پوشه موقت برای این ساخت است.

  • همچنین، TMPDIR، TEMPDIR، TMP و TEMP به گونه‌ای تنظیم می‌شوند که به پوشه موقت اشاره کنند. هدف از این کار جلوگیری از نوشتن تصادفی فایل‌های موقت توسط سازنده (builder) در هر جای دیگری است. انجام این کار ممکن است باعث تداخل با سایر فرآیندها شود.

  • متغیر PATH روی /path-not-set تنظیم می‌شود تا از مقداردهی اولیه آن توسط شل‌ها به مقدار پیش‌فرض توکارشان جلوگیری شود.

  • متغیر HOME روی /homeless-shelter تنظیم می‌شود. (بدون ایزوله‌سازی (sandboxing)، این کار برنامه‌ها را از استفاده از /etc/passwd یا موارد مشابه برای پیدا کردن پوشه خانگی کاربر بازمی‌دارد، که می‌تواند باعث ایجاد ناخالصی شود.) معمولاً وقتی HOME تنظیم می‌شود، حتی اگر به یک مسیر ناموجود اشاره کند، به عنوان مکان پوشه خانگی استفاده می‌شود.

  • متغیر NIX_STORE روی مسیر [مسیر پوشه انبار] سطح بالای Nix (معمولاً /nix/store) تنظیم می‌شود.

  • متغیرهای NIX_ATTRS_JSON_FILE و NIX_ATTRS_SH_FILE (اگر __structuredAttrs برای درایویشن روی true تنظیم شده باشد). توضیح مفصلی درباره‌ی این رفتار را می‌توانید در بخش مربوط به صفت‌های ساختاریافته بیابید.

آرگومان‌ها

آرگومان‌های مشخص‌شده توسط صفت درایویشن args به سازنده ارسال می‌شوند.

پردازش خروجی‌ها

دو روش برای پردازش خروجی‌ها وجود دارد. اما ابتدا، بیایید الزامات مشترک برای هر دو روش را بررسی کنیم.

صرف‌نظر از اینکه از چه روشی استفاده می‌شود، هر خروجی باید به یک شیء انبار معتبر تبدیل شود. این کار شامل دو مرحله است:

  • نرمال‌سازی مجوزهای فایل

    فایل‌ها باید با مدل توصیف‌شده در بخش نمایش در سیستم‌فایل‌های سیستم‌عامل مطابقت داشته باشند. برای مثال، زمان‌سنج‌ها (timestamps) و مجوزها قانون‌مند (canonicalised) می‌شوند.

  • محاسبه ارجاع‌ها

    Nix هر مسیر خروجی را برای یافتن [ارجاع‌ها] به شیء‌های انبار ورودی، با جستجوی خلاصه (digest) هر ورودی اسکن می‌کند. (بخش نام و [مسیر پوشه انبار] هنگام اسکن نادیده گرفته می‌شوند؛ بخش هش یک ورودی که نه توسط یک - دنبال شده و نه توسط یک / پیشوند گرفته شده است، همچنان به عنوان یک ارجاع اسکن می‌شود.) از آنجا که این موارد وابستگی‌های بالقوه زمان اجرا هستند، Nix آن‌ها را به عنوان ارجاع‌های شیء انبار خروجی که در آن رخ می‌دهند، ثبت خواهد کرد.

پردازش سنتی (پس از ساخت)

با روش سنتی، فرآیند سازنده پس از خروج باید فایل‌هایی را به ازای هر خروجی که درایویشن قرار است تولید کند، به جا گذاشته باشد. این فایل‌ها باید پردازش شوند تا به آبجکت‌های انبار معتبری تبدیل گردند. اگر پردازش با موفقیت انجام شود، آن آبجکت‌های انبار به عنوان (نتایج) یک ساخت موفقیت‌آمیز به درایویشن مرتبط می‌شوند.

نیکس همچنین به‌طور مشابه برای ارجاعات از یک خروجی به خروجی دیگر اسکن می‌کند، زیرا خروجی‌ها مجاز به ارجاع به یکدیگر هستند. ارجاعات خروجی‌ها باید یک گراف جهت‌دار بدون دور تشکیل دهند. (این یک محدودیت ویژه برای خروجی‌ها نیست؛ بلکه برای ارجاعات تمام آبجکت‌های انبار به‌طور کلی صادق است.)

در مورد درایویشن‌هایی با مسیرهای خروجی که از پیش تعیین شده‌اند (یعنی درایویشن‌های مبتنی بر آدرس ورودی یا درایویشن‌های مبتنی بر محتوا با آدرس ثابت)، در صورت امکان، مسیر انبار نهایی واقعی برای هر خروجی در طول ساخت استفاده می‌شود. با این حال، برای درایویشن‌های مبتنی بر محتوا با آدرس شناور، مسیر انبار نهایی بنا به تعریف از پیش مشخص نیست. بنابراین باید به جای آن از مسیرهای انبار موقت (Scratch) استفاده شود. اسکن کردن از این مسیرهای موقت استفاده خواهد کرد، اما پس از آن، هر خروجی آینده که حاوی چنین مسیر موقت اسکن‌شده‌ای باشد، باید بازنویسی شود تا به جای آن از مسیر نهایی (مبتنی بر محتوا) خروجی مورد نظر استفاده کند.

علاوه بر ارجاعات خروجی به خروجی، بازنویسی برای پشتیبانی از خودارجاعی‌ها در حالت آدرس‌دهی مبتنی بر محتوا نیز ضروری است. یک خروجی ممکن است حاوی خلاصه‌ساز (Digest) مسیر انبار خودش باشد که یک خودارجاعی است. توابع هش امن نمی‌توانند محاسبه آسان نقاط ثابت شبه‌ثابت مورد نیاز برای پشتیبانی «بومی» از خودارجاعی‌ها را فراهم کنند، بنابراین به جای آن، ما تمام خودارجاعی‌های احتمالی را با یک مقدار نشانگر (Sentinel) جایگزین می‌کنیم، و سپس مقدار نشانگر را بازنویسی می‌کنیم تا به خلاصه‌ساز مسیر انبار نهایی تبدیل شود. به طور سطحی، این بازنویسی پس از هش کردن، آدرس محتوا را می‌شکند، اما از آنجایی که خودارجاعی‌ها به راحتی قابل شناسایی هستند، بازنویسی را می‌توان معکوس کرد تا داده‌های هش‌شده اصلی به دست آید و در نتیجه امکان تأیید آدرس محتوا پس از همه این مراحل فراهم شود.

در این مرحله، داده‌های سیستم‌فایل در فرم مناسبی قرار دارند و داده‌های ارجاع غیرحلقه‌ای معتبر برای هر خروجی نیز محاسبه می‌شوند، بنابراین خروجی‌ها به عنوان آبجکت‌های انبار مناسب به انبار اضافه می‌شوند. علاوه بر این، آن آبجکت‌های انبار (حداقل در صورتی که مبتنی بر محتوا باشند) می‌توانند در ردیابی ساخت در رکورد مربوط به یک ساخت موفق، به درایویشن مرتبط شوند.

جزئیات پیاده‌سازی

نیکس به‌طور معمول پس از هر ساخت، چه موفق و چه ناموفق، پوشه ساخت موقت را پاک‌سازی و حذف می‌کند. با این حال، سازنده نمی‌داند که آیا نیکس این کار را انجام می‌دهد یا خیر، زیرا پیش از پاک‌سازی پوشه ساخت خارج خواهد شد، و اگر (پس از یک ساخت ناموفق) دوباره اجرا شود، هیچ پوشه ساخت قدیمی را نخواهد دید. گزینه --keep-failed را می‌توان برای حفظ پوشه ساخت در صورت شکست ساخت مشخص کرد.

پردازش هم‌زمان از طریق ارتباط بین فرآیندی (IPC)

با این روش، سازنده در طول ساخت با استفاده از ارتباط بین فرآیندی (IPC) با نیکس ارتباط برقرار می‌کند.

جزئیات پیاده‌سازی

پیاده‌سازی فعلی، یعنی builder-rpc-v0، رابط کاربری خود را از طریق شکل محدودی از سوکت خدمت پس‌زمینه (daemon) Nix در معرض نمایش قرار می‌دهد. سازنده‌ها (Builders) ممکن است از آن یا با پیاده‌سازی خودشان از پروتکل Nix استفاده کنند، یا با دستورات nix store add و nix store submit-output.

درایویشن‌های دارای builder-rpc-v0 در مجموعه requiredSystemFeatures خود مسیرهای خروجی را در متغیر محیطی خود دریافت نخواهند کرد، و انتظار می‌رود که تمام خروجی‌ها را با دستورات یا پروتکل ذکر شده ارسال کنند.

به‌جای اینکه فایل‌ها برای پردازش توسط Nix پس از خروج باقی بمانند، سازنده صراحتاً از خدمت پس‌زمینه (daemon) می‌خواهد که اشیاء انبار (store objects) را یکی‌یکی ایجاد کند، سپس دستوراتی را ارسال می‌کند که نام‌های خروجی را به اشیاء انبار تازه‌ساخته‌شده اختصاص می‌دهند.

پویش (scanning) برای ارجاعات طبق روال عادی برای هر درخواست ایجاد شیء انبار ادامه می‌یابد، اما مجموعه ارجاعات بالقوه برای پویش بزرگ‌تر است: این مجموعه شامل تمام ورودی‌ها (مانند قبل) و همچنین تمام اشیاء انبار اضافه‌شده‌ی قبلی می‌شود. این یعنی اگر خروجی bar قرار است به خروجی foo ارجاع دهد، ابتدا باید foo ایجاد شود و سپس bar.

تمام اشیاء انباری که در حال ایجاد هستند، آدرس‌دهی‌شده بر اساس محتوا (content-addressed) هستند (هیچ پشتیبانی از خروجی‌های آدرس‌دهی‌شده بر اساس ورودی با رویکرد IPC وجود ندارد). هنگامی که یک شیء انبار ایجاد می‌شود، مسیر انبار آدرس محتوای آن توسط Nix محاسبه شده و سپس در پیام پاسخ IPC بازگردانده می‌شود. سپس سازنده می‌داند که چه مسیر انباری را در اشیاء انبار بعدی استفاده کند تا پویشگر ارجاع بتواند آن‌ها را شناسایی کند.

این رویکرد کلی چندین مزیت دارد:

  • عدم بازنویسی در سمت Nix

    برای خروجی‌های آدرس‌دهی‌شده بر اساس محتوا، سازنده مسئول افزودن خروجی‌ها به ترتیب ارجاع است، و در افزودنی‌های بعدی از مسیرهای انبارِ افزودنی‌های قبلی استفاده می‌کند. این کار از بازنویسی شکننده‌ای که در غیر این صورت برای اصلاح ارجاعات خروجی به خروجی توصیف‌شده در بالا مورد نیاز بود، جلوگیری می‌کند. سازنده، برخلاف خود Nix، آزاد است از دانش مخصوص حوزه برای انجام کار به شکلی بهتر بهره ببرد. برای مثال، می‌تواند

    • صفحات راهنما (man pages) را از حالت فشرده خارج کرده، بازنویسی کند و سپس دوباره فشرده سازد تا ارجاعات پنهان‌شده توسط فشرده‌سازی را از دست ندهد.

    • مطمئن شود داده‌هایی را که قرار است امضا شوند، مانند باینری‌های Apple، قبل از امضا کردن بازنویسی می‌کند تا به اشتباه هیچ امضایی را بی‌اعتبار نکند.

  • خط لوله (Pipelining)

    ساخت‌های پایین‌دستی (downstream builds) که فقط به برخی خروجی‌ها نیاز دارند (به‌عنوان مثال، یک خروجی "dev" یا "headers") می‌توانند بدون انتظار برای آماده شدن تمام خروجی‌ها شروع شوند. Nix هنوز این مورد را پیاده‌سازی نکرده است، اما می‌تواند و باید بکند.

اشکال عمده این رویکرد این است که هنوز از خودارجاعی‌ها پشتیبانی نمی‌کند. برخلاف ارجاعات غیرمدورِ خروجی به خروجی، خودارجاعی‌ها اساساً به بازنویسی نیاز دارند. موردِ ارجاع خروجی به خروجی فقط در حالت سنتی یک چالش بود زیرا تمام خروجی‌ها به‌طور هم‌زمان ارسال می‌شدند، در حالی که موردِ خودارجاعی اساساً به خاطر معنای ایمن بودن یک تابع هش، همان‌طور که در بالا توصیف شد، چالش‌برانگیز است. نه ارسال دسته‌ای (سنتی) و نه ارسال ترتیبی (IPC) خروجی‌ها نمی‌توانند از این ویژگی اساسی توابع هش ایمن جلوگیری کنند. ما می‌توانیم پشتیبانی از چنین بازنویسی را فقط برای خودارجاعی‌ها اضافه کنیم، همان‌طور که برای پردازش سنتی پس از ساخت انجام می‌شود، اما هنوز این کار را نکرده‌ایم؛ زیرا هدف اصلی رویکرد IPC رها کردن Nix از هرگونه تعهد به بازنویسی داده‌های جعبه‌سیاه (black-box) به روش‌های ناایمن است.

nix.dev/manual/nix/stable/store/building.html

نیکسی · یادداشت‌های فارسی Nix local fonts