Dotnet
گردش کار توسعه محلی
برای توسعه محلی، توصیه میشود از nix-shell برای ایجاد یک محیط dotnet استفاده کنید:
# shell.nix
with import <nixpkgs> { };
mkShell {
name = "dotnet-env";
packages = [ dotnet-sdk ];
} استفاده از چند SDK در یک گردش کار
بسیار محتمل است که در یک پروژه به بیش از یک SDK نیاز باشد. Dotnet چند چارچوب مختلف (مانند dotnetcore، aspnetcore و غیره) و همچنین نسخههای متعددی را برای یک چارچوب مشخص ارائه میدهد. به طور معمول، dotnet میتواند یک چارچوب را دریافت کرده و آن را نسبت به فایل اجرایی نصب کند. با این حال، این کار به معنای نوشتن در انبار نیکس (Nix store) در Nixpkgs خواهد بود که فقطخواندنی است. برای پشتیبانی از مورد استفاده چند SDK، میتوان با استفاده از dotnetCorePackages.combinePackages یک محیط ایجاد کرد:
with import <nixpkgs> { };
mkShell {
name = "dotnet-env";
packages = [
(
with dotnetCorePackages;
combinePackages [
sdk_8_0
sdk_9_0
]
)
];
} این کار یک نصب dotnet ایجاد خواهد کرد که دارای SDKهای dotnet 8.0 و 9.0 است. اولین SDK فهرستشده، ابزار رابط خط فرمان (CLI) خود را در محیط حاصل خواهد داشت. خروجی نمونه info:
$ dotnet --info
.NET SDK:
Version: 9.0.100
Commit: 59db016f11
Workload version: 9.0.100-manifests.3068a692
MSBuild version: 17.12.7+5b8665660
Runtime Environment:
OS Name: nixos
OS Version: 25.05
OS Platform: Linux
RID: linux-x64
Base Path: /nix/store/a03c70i7x6rjdr6vikczsp5ck3v6rixh-dotnet-sdk-9.0.100/share/dotnet/sdk/9.0.100/
.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.
Host:
Version: 9.0.0
Architecture: x64
Commit: 9d5a6a9aa4
.NET SDKs installed:
8.0.404 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/sdk]
9.0.100 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.11 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.0 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 8.0.11 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.0 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.NETCore.App]
Other architectures found:
None
Environment variables:
Not set
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download dotnet-sdk در برابر dotnetCorePackages.sdk
عبارت dotnetCorePackages.sdk_X_Y نسبت به dotnet-sdk قدیمی ترجیح داده میشود، زیرا هر دو نسخه اصلی (major) و فرعی (minor) برای یک محیط dotnet بسیار مهم هستند. اگر یک نسخه فرعی مشخص وجود نداشته باشد (یا تغییر کرده باشد)، به احتمال زیاد توانایی شما را برای ساخت یک پروژه مختل خواهد کرد.
dotnetCorePackages.sdk در برابر dotnetCorePackages.runtime در برابر dotnetCorePackages.aspnetcore
عبارت dotnetCorePackages.sdk شامل هر دو زمان اجرا (runtime) و sdk کامل از یک نسخه مشخص است. بستههای runtime و aspnetcore به منظور ارائه حداقل زمان اجرا جهت استقرار در کنار برنامههای ازقبل ساختهشده طراحی شدهاند.
بستهبندی یک برنامه Dotnet
برای بستهبندی برنامههای Dotnet، میتوانید از buildDotnetModule استفاده کنید. این تابع آرگومانهای مشابهی با stdenv.mkDerivation دارد، همراه با موارد افزوده شده زیر:
projectFileبرای مشخص کردن فایل پروژه dotnet، نسبت به ریشه کد منبع استفاده میشود. این فایلها دارای پسوند.sln(کل راهکار) یا.csproj(یک پروژه) هستند. این مورد میتواند لیستی از چندین پروژه نیز باشد. در صورت حذف، سعی خواهد شد راهکار (.sln) پیدا و ساخته شود. اگر با مشکل مواجه شدید، مطمئن شوید که آن را روی یک فایل (یا لیستی از فایلها) با پسوند.csprojتنظیم کردهاید - ساخت برنامهها به عنوان کل راهکار به طور کامل توسط CLI مربوط به .NET پشتیبانی نمیشود.nugetDepsباید یک مسیر به یک فایل JSON، یک مسیر به یک فایل nix (منسوخشده)، یک درایویشن، یا لیستی از درایویشنها باشد. یک فایلdeps.jsonرا میتوان با استفاده از اسکریپت متصل بهpassthru.fetch-depsتولید کرد که روش ترجیحی است. تمام بستههایnugetDepsبهbuildInputsاضافه میشوند.نکته
برای جزییات بیشتر درباره مدیریت فایل
deps.json، بخش تولید و بهروزرسانی وابستگیهای NuGet را ببینید.packNupkgبرای بستهبندی پروژه به عنوان یکnupkgاستفاده میشود و آن را در$out/shareنصب میکند. در صورت تنظیم رویtrue، درایویشن میتواند با افزودن بهbuildInputsبه عنوان یک وابستگی برای پروژه dotnet دیگری استفاده شود.buildInputsمیتواند برای حل موارد پروژهProjectReferenceاستفاده شود. پروژههای مورد ارجاع میتوانند باbuildDotnetModuleبا تنظیم صفتpackNupkg = trueو پاس دادن لیستی از درایویشنها بهbuildInputsبستهبندی شوند. از آنجا که ما پروژههای مورد ارجاع را به عنوان NuGet به اشتراک میگذاریم، آنها باید به فایلهای csproj/fsproj به عنوانPackageReferenceنیز اضافه شوند. به عنوان مثال، پروژه شما یک وابستگی محلی دارد:
<ProjectReference Include="../foo/bar.fsproj" /> برای فعالسازی شناسایی از طریق buildInputs باید موارد زیر را اضافه کنید:
<ProjectReference Include="../foo/bar.fsproj" />
<PackageReference Include="bar" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/> executablesبرای مشخص کردن اینکه کدام فایلهای اجرایی نسبت به$out/lib/$pnameدر$out/binقرار میگیرند (wrap میشوند) استفاده میشود. اگر این گزینه تنظیم نشود، تمام فایلهای اجرایی تولیدشده نصب خواهند شد. اگر نمیخواهید هیچکدام نصب شوند، آن را برابر[]قرار دهید. این کار در فازpreFixupانجام میشود.runtimeDepsبرای قرار دادن کتابخانهها درLD_LIBRARY_PATHاستفاده میشود. این روشی است که dotnet معمولاً وابستگیهای زمان اجرا را مدیریت میکند.buildTypeبرای تغییر نوع ساخت استفاده میشود. مقادیر ممکن عبارتند ازRelease،Debugو غیره. به طور پیشفرض، این مقدار رویReleaseتنظیم شده است.selfContainedBuildامکان فعالسازی پرچم ساخت self-contained را فراهم میکند. به طور پیشفرض، روی false تنظیم شده است و برنامههای تولیدشده به زمان اجرای dotnet انتخابشده وابستگی دارند. در صورت فعال شدن، زمان اجرای dotnet همراه با فایل اجرایی بستهبندی میشود و برنامه ساختهشده هیچ وابستگی به .NET ندارد.useAppHostایجاد یک فایل اجرایی باینری را فعال میکند که برنامه .NET را با استفاده از ریشه مشخصشده اجرا میکند. اطلاعات بیشتر در مستندات مایکروسافت. به طور پیشفرض فعال است.useDotnetFromEnvوراپر باینری را تغییر میدهد تا از .NET موجود در محیط استفاده کند. زمان اجرای مشخصشده توسطdotnet-runtimeبه عنوان حالت پشتیبان ارائه میشود تا در صورتی که هیچ .NETی در محیط کاربر نصب نشده باشد، استفاده شود. این گزینه بیشتر برای ابزارهای سراسری .NET و سرورهای LSP کاربرد دارد که اغلب CLI مربوط به .NET را گسترش میدهند و زمان اجرای آنها باید با زمان اجرای .NET کاربر مطابقت داشته باشد.dotnet-sdkدر مواردی مفید است که نیاز به تغییر SDK مورد استفاده dotnet دارید. همچنین اگر پروژه برای ساخت از چندین SDK استفاده میکند، میتوانید این صفت را روی نتیجهdotnetSdkPackages.combinePackagesتنظیم کنید.dotnet-runtimeدر مواردی مفید است که نیاز به تغییر زمان اجرای dotnet مورد استفاده دارید. این میتواند یک زمان اجرای معمولی dotnet یا aspnetcore باشد.testProjectFileدر مواردی مفید است که فایل پروژه معمولی شامل تستهای واحد نیست. این فایل بازیابی و ساخته میشود، اما نصب نمیگردد. ممکن است لازم باشد پس از تنظیم این صفت، فایل lockfile مربوط به nuget خود را دوباره تولید کنید. توجه داشته باشید که در صورت تنظیم، فقط تستهای همین پروژه اجرا میشوند.testFiltersبرای غیرفعال کردن اجرای تستهای واحد بر اساس فیلترهای مختلف استفاده میشود. این مقدار به صورتdotnet test --filter "{'{'}'{'{'}'{'}'}{'{'}'{'}'}'{'}'}"ارسال میشود و هر فیلتر با استفاده از"&"الحاق میگردد.disabledTestsبرای غیرفعال کردن اجرای تستهای واحد خاص استفاده میشود. این مقدار به صورتdotnet test --filter "FullyQualifiedName!={'{'}'{'{'}'{'}'}{'{'}'{'}'}'{'}'}"ارسال میشود تا از سازگاری با تمامی چارچوبهای تست واحد اطمینان حاصل شود.dotnetRestoreFlagsمیتواند برای ارسال پرچمها بهdotnet restoreاستفاده شود.dotnetBuildFlagsمیتواند برای ارسال پرچمها بهdotnet buildاستفاده شود.dotnetTestFlagsمیتواند برای ارسال پرچمها بهdotnet testاستفاده شود. تنها در صورتی استفاده میشود کهdoCheckرویtrueتنظیم شده باشد.dotnetInstallFlagsمیتواند برای ارسال پرچمها بهdotnet installاستفاده شود.dotnetPackFlagsمیتواند برای ارسال پرچمها بهdotnet packاستفاده شود. تنها در صورتی استفاده میشود کهpackNupkgرویtrueتنظیم شده باشد.dotnetFlagsمیتواند برای ارسال پرچمها به تمام فازهای بالا استفاده شود.
هنگام بستهبندی یک برنامه جدید، باید وابستگیهای آن را دریافت کنید. یک deps.json خالی ایجاد کنید، nugetDeps = ./deps.json را تنظیم نمایید، سپس nix-build -A package.fetch-deps را اجرا کنید تا اسکریپتی تولید شود که lockfile را برای شما میسازد.
در ادامه یک نمونه default.nix آمده است که از برخی از آرگومانهای بررسیشده در بالا استفاده میکند:
{
lib,
buildDotnetModule,
dotnetCorePackages,
ffmpeg,
}:
let
referencedProject = import ../../bar {
# ...
};
in
buildDotnetModule rec {
pname = "someDotnetApplication";
version = "0.1";
src = ./.;
projectFile = "src/project.sln";
nugetDeps = ./deps.json; # see "Generating and updating NuGet dependencies" section for details
buildInputs = [
referencedProject
]; # `referencedProject` must contain `nupkg` in the folder structure.
dotnet-sdk = dotnetCorePackages.sdk_8_0;
dotnet-runtime = dotnetCorePackages.runtime_8_0;
executables = [ "foo" ]; # This wraps "$out/lib/$pname/foo" to `$out/bin/foo`.
executables = [ ]; # Don't install any executables.
packNupkg = true; # This packs the project as "foo-0.1.nupkg" at `$out/share`.
runtimeDeps = [ ffmpeg ]; # This will wrap ffmpeg's library path into `LD_LIBRARY_PATH`.
} به یاد داشته باشید که میتوانید برای دریافت کمک و بررسی کد، تیم @NixOS/dotnet را تگ کنید.
ابزارهای جهانی Dotnet
ابزارهای جهانی .NET مکانیزمی ارائهشده توسط CLI مربوط به dotnet برای نصب باینریهای .NET از بستههای Nuget هستند.
آنها میتوانند هم به عنوان یک ابزار جهانی برای کل سیستم، یا به عنوان یک ابزار محلی مخصوص به پروژه نصب شوند.
نصب محلی آسانترین روش است و روی NixOS به همان روش سایر توزیعهای Linux کار میکند. برای اطلاعات بیشتر مستندات dotnet را ببینید.
روش نصب جهانی نیز بیشتر اوقات باید کار کند. باید به یاد داشته باشید که مقدار PATH را به محلی که ابزارها در آن نصب شدهاند بهروزرسانی کنید (CLI در طول نصب این موضوع را به شما اطلاع میدهد) و همچنین
مقدار DOTNET_ROOT را تنظیم کنید تا ابزار بتواند بسته .NET SDK را پیدا کند.
میتوانید با اجرای nix eval --raw nixpkgs#dotnet-sdk مسیر SDK را پیدا کنید (در صورت نیاز به نسخه متفاوتی از SDK، بسته dotnet-sdk را با بسته دیگری جایگزین کنید).
این روش در NixOS توصیه نمیشود، زیرا اعلانی (declarative) نیست و شامل نصب باینریهایی است که برای NixOS ساخته نشدهاند، که همیشه کار نخواهند کرد.
روش سوم و ترجیح داده شده، بستهبندی ابزار در یک derivation نیکس است.
بستهبندی ابزارهای جهانی Dotnet
ابزارهای جهانی Dotnet باینریهای استاندارد .NET هستند که فقط از طریق یک بسته ویژه
NuGet در دسترس قرار گرفتهاند. بنابراین، آنها مانند هر برنامه .NET دیگری میتوانند
با استفاده از buildDotnetModule ساخته و بستهبندی شوند.
اگر با این حال کد منبع در دسترس نباشد یا ساخت آن دشوار باشد، میتوان از
کمکرسان buildDotnetGlobalTool استفاده کرد که ابزار را
مستقیماً از بسته NuGet آن بستهبندی میکند.
این کمکرسان همان آرگومانهای buildDotnetModule را دارد، با چند تفاوت:
pnameوversionالزامی هستند و برای پیدا کردن بسته NuGet ابزار استفاده خواهند شدnugetNameمیتواند برای بازنشانی نام بسته NuGet که دانلود میشود (در صورتی که باpnameمتفاوت باشد) استفاده شودnugetHashهش بسته NuGet دریافتشده است.nugetSha256نیز پشتیبانی میشود، اما توصیه نمیشود. برای اولین ساخت، این مقدار را رویlib.fakeHashقرار دهید تا با خطا مواجه شده و هش مناسب را به شما بدهد. همچنین به یاد داشته باشید که در زمان ارتقای نسخه آن را بهروزرسانی کنید (اگر فقط نسخه را تغییر دهید در حالی که بسته دریافتشده در/nix/storeموجود باشد، خطایی رخ نخواهد داد)dotnet-runtimeبه طور پیشفرض رویdotnet-sdkتنظیم شده است. هنگام تغییر این مقدار، به یاد داشته باشید که ابزارهای .NET دریافتشده از NuGet به یک SDK نیاز دارند.
در ادامه یک نمونه از بستهبندی pbm (یک باینری غیرآزاد که کد منبع آن در دسترس نیست) آورده شده است:
{ buildDotnetGlobalTool, lib }:
buildDotnetGlobalTool {
pname = "pbm";
version = "1.3.1";
nugetHash = "sha256-ZG2HFyKYhVNVYd2kRlkbAjZJq88OADe3yjxmLuxXDUo=";
meta = {
homepage = "https://cmd.petabridge.com/index.html";
changelog = "https://cmd.petabridge.com/articles/RELEASE_NOTES.html";
license = lib.licenses.unfree;
platforms = lib.platforms.linux;
};
} تولید و بهروزرسانی وابستگیهای NuGet
هنگام نوشتن یک عبارت جدید، میتوانید از اسکریپت تولیدشدهی fetch-deps برای مقداردهی اولیه lockfile استفاده کنید.
پس از تنظیم nugetDeps روی مسیر دلخواه lockfile (برای مثال ./deps.json)،
اسکریپت را با nix-build -A package.fetch-deps بسازید و سپس نتیجه را اجرا کنید.
(وقتی صفت ریشه، بسته شما باشد، این دستور بهسادگی nix-build -A fetch-deps است.)
یک روش دستی نیز وجود دارد:
نخست، بستهها را در پوشه out بازیابی کنید، مطمئن شوید که مخزن بالادستی را کلون کردهاید و داخل آن هستید.
$ dotnet restore --packages out
Determining projects to restore...
Restored /home/ggg/git-credential-manager/src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj (in 1.21 sec). در ادامه، از ابزار nuget-to-json ارائهشده در Nixpkgs برای تولید لاکفایل در deps.json از بستههای داخل پوشهی out استفاده کنید.
$ nuget-to-json out > deps.json ابزار nuget-to-json خروجی مشابه نمونه زیر تولید خواهد کرد.
[
{
"pname": "Avalonia",
"version": "11.1.3",
"hash": "sha256-kz+k/vkuWoL0XBvRT8SadMOmmRCFk9W/J4k/IM6oYX0="
},
{
"pname": "Avalonia.Angle.Windows.Natives",
"version": "2.1.22045.20230930",
"hash": "sha256-RxPcWUT3b/+R3Tu5E5ftpr5ppCLZrhm+OTsi0SwW3pc="
},
{
"pname": "Avalonia.BuildServices",
"version": "0.0.29",
"hash": "sha256-WPHRMNowRnYSCh88DWNBCltWsLPyOfzXGzBqLYE7tRY="
},
// ...
{
"pname": "System.Runtime.CompilerServices.Unsafe",
"version": "6.0.0",
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
},
{
"pname": "System.Security.Cryptography.ProtectedData",
"version": "4.5.0",
"hash": "sha256-Z+X1Z2lErLL7Ynt2jFszku6/IgrngO3V1bSfZTBiFIc="
},
{
"pname": "Tmds.DBus.Protocol",
"version": "0.16.0",
"hash": "sha256-vKYEaa1EszR7alHj48R8G3uYArhI+zh2ZgiBv955E98="
}
]
در نهایت، فایل deps.json را به مکان مناسب منتقل میکنید تا توسط nugetDeps استفاده شود، و کار تمام است!
اگر زمانی نیاز به بهروزرسانی وابستگیهای یک بسته داشته باشید، در عوض این کارها را انجام میدهید:
- اجرای
nix-build -A package.fetch-depsبرای تولید اسکریپت بهروزرسانی برایpackage - اجرای
./resultبرای تولید مجدد فایل قفل در مسیری که بهnugetDepsداده شده است (در نظر داشته باشید اگر نتوان آن را به یک مسیر محلی ارزیابی کرد، اسکریپت در عوض در$1یا یک مسیر موقت خواهد نوشت) - در نهایت، اطمینان حاصل کنید که فایل درست نوشته شده است و derivation قابل ساخت است.