Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
Ebben az oktatóanyagban létrehoz egy egyéni feladatot az MSBuild in C# nyelven, amely kezeli a kódgenerálást, majd a feladatot egy buildben fogja használni. Ez a példa bemutatja, hogyan használható az MSBuild a tiszta és az újraépítési műveletek kezelésére. A példa azt is bemutatja, hogyan támogathatja a növekményes buildelést, így a kód csak a bemeneti fájlok módosításakor jön létre. A bemutatott technikák a kódgenerálási forgatókönyvek széles körére alkalmazhatók. A lépések azt is mutatják, hogy a NuGet használatával csomagolja be a feladatot a terjesztéshez, és az oktatóanyag tartalmaz egy opcionális lépést a BinLog megjelenítő használatával a hibaelhárítási élmény javítása érdekében.
Előfeltételek
Ismernie kell az MSBuild fogalmait, például a feladatokat, a célokat és a tulajdonságokat. Lásd MSBuild fogalmakat.
A példákhoz az MSBuild szükséges, amely a Visual Studióval van telepítve, de külön is telepíthető. Lásd: Az MSBuild letöltése Visual Studionélkül.
Bevezetés a példakódba
A példa egy olyan bemeneti szövegfájlt vesz, amely beállítandó értékeket tartalmaz, és létrehoz egy C# kódfájlt, amely ezen értékek létrehozását végző kódot tartalmaz. Bár ez egy egyszerű példa, ugyanezek az alapvető technikák alkalmazhatók összetettebb kódgenerálási forgatókönyvekre is.
Ebben az oktatóanyagban létrehozod az AppSettingStronglyTyped nevű MSBuild egyéni feladatot. A feladat beolvassa a szövegfájlok egy készletét, és minden fájl sorait a következő formátumban:
propertyName:type:defaultValue
A kód létrehoz egy C# osztályt az összes állandóval. A probléma leállítja a buildet, és elegendő információt ad a felhasználónak a probléma diagnosztizálásához.
Az oktatóanyag teljes mintakódja a Egyéni feladat – kódgenerálás mintánál található a(z) .NET minták adattárában a GitHubon.
Az AppSettingStronglyTyped projekt létrehozása
Hozzon létre egy .NET standard osztálytárat. A keretrendszernek a .NET Standard 2.0-nak kell lennie.
Figyelje meg a különbséget a teljes MSBuild (a Visual Studio által használt) és a .NET Core parancssorba csomagolt hordozható MSBuild között.
- Teljes MSBuild: Az MSBuild ezen verziója általában a Visual Studióban található. .NET-keretrendszeren fut. A Visual Studio ezt akkor használja, amikor a Buildet hajt végre a megoldáson vagy projekten. Ez a verzió parancssori környezetből is elérhető, például a Visual Studio fejlesztői parancssorából vagy a PowerShellből.
- .NET MSBuild: Az MSBuild ezen verziója a .NET Core parancssorba van csomagolva. .NET Core-on fut. A Visual Studio nem hívja meg közvetlenül az MSBuild ezen verzióját. Csak a Microsoft.NET.Sdk használatával buildelt projekteket támogatja.
Ha kódot szeretne megosztani a .NET-keretrendszer és bármely más .NET-implementáció, például a .NET Core között, a kódtárnak meg kell céloznia .NET Standard 2.0, és a .NET-keretrendszeren futó Visual Studióban kell futtatnia. A .NET-keretrendszer nem támogatja a .NET Standard 2.1-et.
Válassza ki a hivatkozni kívánt MSBuild API-verziót
Egyéni feladat összeállításakor az MSBuild API (Microsoft.Build.*) azon verziójára kell hivatkoznia, amely megfelel a Visual Studio minimális verziójának és/vagy a támogatni kívánt .NET SDK-nak. Például, ha a Visual Studio 2019 felhasználóit szeretné támogatni, az MSBuild 16.11 verzióval kell fordítania.
Az AppSettingStronglyTyped MSBuild egyéni feladat létrehozása
Az első lépés az MSBuild egyéni feladat létrehozása. Az MSBuild egyéni feladat írásával kapcsolatos információ segíthet megérteni az alábbi lépéseket. Az MSBuild egyéni feladat egy olyan osztály, amely megvalósítja a ITask interfészt.
Adjon hozzá egy hivatkozást a Microsoft.Build.Utilities.Core NuGet-csomagra, majd hozzon létre egy AppSettingStronglyTyped nevű osztályt, amely a Microsoft.Build.Utilities.Task osztályból származik.
Adjon hozzá három tulajdonságot. Ezek a tulajdonságok határozzák meg annak a feladatnak a paramétereit, amelyeket a felhasználók a feladat ügyfélprojektben való használatakor állítottak be:
//The name of the class which is going to be generated [Required] public string SettingClassName { get; set; } //The name of the namespace where the class is going to be generated [Required] public string SettingNamespaceName { get; set; } //List of files which we need to read with the defined format: 'propertyName:type:defaultValue' per line [Required] public ITaskItem[] SettingFiles { get; set; }A feladat feldolgozza a SettingFiles, és létrehoz egy osztály
SettingNamespaceName.SettingClassName. A létrehozott osztály a szövegfájl tartalma alapján konstanskészlettel rendelkezik.A feladat kimenetének olyan sztringnek kell lennie, amely megadja a létrehozott kód fájlnevét:
// The filename where the class was generated [Output] public string ClassNameFile { get; set; }Egyéni feladat létrehozásakor a Microsoft.Build.Utilities.Task-ból örökölsz. A feladat végrehajtásához felül kell írnia a Execute() metódust. A
Executemetódustruead vissza, ha a feladat sikeres, és ellenkező esetbenfalse.Taskimplementálja a Microsoft.Build.Framework.ITask, és néhányITasktag alapértelmezett implementációit is biztosítja, valamint néhány naplózási funkciót is biztosít. A feladat diagnosztizálásához és hibaelhárításához fontos a napló állapotának megadása, különösen akkor, ha probléma merül fel, és a feladatnak hibaeredményt (false) kell visszaadnia. Hiba miatt az osztály TaskLoggingHelper.LogErrormeghívásával jelzi a hibát.public override bool Execute() { //Read the input files and return a IDictionary<string, object> with the properties to be created. //Any format error it will return false and log an error var (success, settings) = ReadProjectSettingFiles(); if (!success) { return !Log.HasLoggedErrors; } //Create the class based on the Dictionary success = CreateSettingClass(settings); return !Log.HasLoggedErrors; }A Task API lehetővé teszi, hogy a hiba jelzése nélkül hamis eredményt adjon vissza a felhasználónak. A legjobb, ha logikai kód helyett
!Log.HasLoggedErrorsad vissza, és hiba esetén naplózza a hibát.
Naplóhibák
A naplózási hibák esetén ajánlott olyan részleteket megadni, mint a sorszám és egy különálló hibakód a hibanaplózás során. Az alábbi kód elemzi a szövegbeviteli fájlt, és a TaskLoggingHelper.LogError metódust használja a hibát okozó szövegfájl sorszámával.
private (bool, IDictionary<string, object>) ReadProjectSettingFiles()
{
var values = new Dictionary<string, object>();
foreach (var item in SettingFiles)
{
int lineNumber = 0;
var settingFile = item.GetMetadata("FullPath");
foreach (string line in File.ReadLines(settingFile))
{
lineNumber++;
var lineParse = line.Split(':');
if (lineParse.Length != 3)
{
Log.LogError(subcategory: null,
errorCode: "APPS0001",
helpKeyword: null,
file: settingFile,
lineNumber: lineNumber,
columnNumber: 0,
endLineNumber: 0,
endColumnNumber: 0,
message: "Incorrect line format. Valid format prop:type:defaultvalue");
return (false, null);
}
var value = GetValue(lineParse[1], lineParse[2]);
if (!value.Item1)
{
return (value.Item1, null);
}
values[lineParse[0]] = value.Item2;
}
}
return (true, values);
}
Az előző kódban bemutatott technikák használatával a szövegbeviteli fájl szintaxisában megjelenő hibák összeállítási hibákként jelennek meg hasznos diagnosztikai információkkal:
Microsoft (R) Build Engine version 17.2.0 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 2/16/2022 10:23:24 AM.
Project "S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild" on node 1 (default targets).
S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\error-prop.setting(1): error APPS0001: Incorrect line format. Valid format prop:type:defaultvalue [S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild]
Done Building Project "S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild" (default targets) -- FAILED.
Build FAILED.
"S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild" (default target) (1) ->
(generateSettingClass target) ->
S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\error-prop.setting(1): error APPS0001: Incorrect line format. Valid format prop:type:defaultvalue [S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild]
0 Warning(s)
1 Error(s)
Amikor kivételeket észlel a feladatban, használja a TaskLoggingHelper.LogErrorFromException metódust. Ez javítja a hibakimenetet, például a hívásverem megismerésével, ahol a kivétel keletkezett.
catch (Exception ex)
{
// This logging helper method is designed to capture and display information
// from arbitrary exceptions in a standard way.
Log.LogErrorFromException(ex, showStackTrace: true);
return false;
}
Itt nem jelenik meg azoknak a módszereknek a megvalósítása, amelyek ezeket a bemeneteket használják a létrehozott kódfájl szövegének összeállításához; lásd AppSettingStronglyTyped.cs a mintaadattárban.
A példakód C#-kódot hoz létre a buildelési folyamat során. A feladat olyan, mint bármely más C#-osztály, így ha végzett ezzel az oktatóanyaggal, testre szabhatja, és bármilyen funkciót hozzáadhat a saját forgatókönyvéhez.
Konzolalkalmazás létrehozása és az egyéni feladat használata
Ebben a szakaszban egy szabványos .NET Core-konzolalkalmazást fog létrehozni, amely a feladatot használja.
Fontos
Fontos, hogy ne hozzon létre egy MSBuild egyéni feladatot ugyanabban az MSBuild folyamatban, amely használni fogja azt. Az új projektnek egy teljesen más Visual Studio-megoldásban kell lennie, vagy az új projekt egy előre létrehozott és a standard kimenetből újra elhelyezett DLL-t használ.
Hozza létre az MSBuildConsoleExample .NET-konzolprojektet egy új Visual Studio-megoldásban.
A tevékenységek terjesztésének szokásos módja egy NuGet-csomag, de a fejlesztés és a hibakeresés során a
.propsés.targetsösszes információját közvetlenül az alkalmazás projektfájljába helyezheti, majd a feladat másoknak való terjesztésekor a NuGet formátumra válthat.Módosítsa a projektfájlt a kódgenerálási feladat felhasználásához. Az ebben a szakaszban található kódlista a tevékenységre való hivatkozás, a tevékenység bemeneti paramétereinek beállítása és a tiszta és újraépítési műveletek kezelésére szolgáló célok megírása után a módosított projektfájlt jeleníti meg, hogy a létrehozott kódfájl a várt módon törlődjön.
A feladatok a UsingTask elem (MSBuild)használatával vannak regisztrálva. A
UsingTaskelem regisztrálja a feladatot; a tájékoztatja az MSBuildet a feladat nevéről, valamint arról, hogyan keresse meg és futtassa a feladatosztályt tartalmazó szerelvényt. A könyvtár elérési útja a projektfájlhoz képest van megadva.A
PropertyGroupa tevékenységben meghatározott tulajdonságoknak megfelelő tulajdonságdefiníciókat tartalmazza. Ezek a tulajdonságok attribútumok használatával vannak beállítva, és a rendszer a feladat nevét használja elemnévként.TaskNamea szerelvényen belül hivatkozott feladat neve. Ennek az attribútumnak mindig teljes névtereket kell használnia. AAssemblyFileaz assembly fájlútvonala.A feladat meghívásához adja hozzá a feladatot a megfelelő célhelyhez, ebben az esetben
GenerateSetting.A cél
ForceGenerateOnRebuilda létrehozott fájl törlésével kezeli a tisztítási és újraépítési műveleteket. Úgy van beállítva, hogy aCoreCleancél után fusson úgy, hogy aAfterTargetsattribútumotCoreCleanértékre állítja.<Project Sdk="Microsoft.NET.Sdk"> <UsingTask TaskName="AppSettingStronglyTyped.AppSettingStronglyTyped" AssemblyFile="..\..\AppSettingStronglyTyped\AppSettingStronglyTyped\bin\Debug\netstandard2.0\AppSettingStronglyTyped.dll"/> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <RootFolder>$(MSBuildProjectDirectory)</RootFolder> <SettingClass>MySetting</SettingClass> <SettingNamespace>MSBuildConsoleExample</SettingNamespace> <SettingExtensionFile>mysettings</SettingExtensionFile> </PropertyGroup> <ItemGroup> <SettingFiles Include="$(RootFolder)\*.mysettings" /> </ItemGroup> <Target Name="GenerateSetting" BeforeTargets="CoreCompile" Inputs="@(SettingFiles)" Outputs="$(RootFolder)\$(SettingClass).generated.cs"> <AppSettingStronglyTyped SettingClassName="$(SettingClass)" SettingNamespaceName="$(SettingNamespace)" SettingFiles="@(SettingFiles)"> <Output TaskParameter="ClassNameFile" PropertyName="SettingClassFileName" /> </AppSettingStronglyTyped> <ItemGroup> <Compile Remove="$(SettingClassFileName)" /> <Compile Include="$(SettingClassFileName)" /> </ItemGroup> </Target> <Target Name="ForceReGenerateOnRebuild" AfterTargets="CoreClean"> <Delete Files="$(RootFolder)\$(SettingClass).generated.cs" /> </Target> </Project>Jegyzet
Ahelyett, hogy felülírna egy célt, például
CoreClean, ez a kód más módot használ a célok rendezésére, mint a (BeforeTarget és AfterTarget). Az SDK-stílusú projektek a projektfájl utolsó sora után automatikusan importálják a célokat; Ez azt jelenti, hogy csak akkor bírálhatja felül az alapértelmezett célokat, ha az importokat manuálisan adja meg. Lásd: Előre definiált célok felülbírálása.A
InputsésOutputsattribútumok segítségével az MSBuild hatékonyabb lehet a növekményes buildek információinak biztosításával. A bemenetek dátumait összehasonlítjuk a kimenetekkel, hogy megállapítsuk, futtatni kell-e a célértéket, vagy az előző build kimenete újra felhasználható-e.Hozza létre a felderítendő bővítményt tartalmazó bemeneti szövegfájlt. Az alapértelmezett bővítmény használatával hozzon létre
MyValues.mysettingsa gyökéren a következő tartalommal:Greeting:string:Hello World!Építse újra, és létre kell jönnie a generált fájlnak is. Ellenőrizze a projektmappában a MySetting.generated.cs fájlt.
Az osztály MySetting helytelen névtérben van, ezért most változtassuk meg az alkalmazásunk névterére. Nyissa meg a projektfájlt, és adja hozzá a következő kódot:
<PropertyGroup> <SettingNamespace>MSBuildConsoleExample</SettingNamespace> </PropertyGroup>Újraépítse újra, és figyelje meg, hogy az osztály a
MSBuildConsoleExamplenévtérben van. Ily módon újradefinitálhatja a létrehozott osztálynevet (SettingClass), a bemenetként használni kívánt szövegkiterjesztési fájlokat (SettingExtensionFile), és tetszés szerint a helyüket (RootFolder).Nyissa meg
Program.cs, és módosítsa a keménykódolt "Hello World!!" a felhasználó által megadott állandóra:static void Main(string[] args) { Console.WriteLine(MySetting.Greeting); }
Hajtsa végre a programot; a létrehozott osztályból fogja kinyomtatni a köszöntést.
(Nem kötelező) Események naplózása a buildelési folyamat során
Parancssori futtatással lefordítható. Navigáljon a projekt mappába. A bináris naplók létrehozásához használja a -bl (bináris napló) lehetőséget. A bináris napló hasznos információkkal szolgál a buildelési folyamat során történt eseményekről.
# Using dotnet MSBuild (run core environment)
dotnet build -bl
# or full MSBuild (run on net framework environment; this is used by Visual Studio)
msbuild -bl
Mindkét parancs létrehoz egy naplófájlt msbuild.binlog, amely MSBuild Binary és Structured Log Viewersegítségével nyitható meg. Az /t:rebuild lehetőség az újjáépítési cél elindítását jelenti. Ez kényszeríti a létrehozott kódfájl újragenerálását.
Gratulálok! Ön létrehozott egy feladatot, amely kódot generál, és ezt használta egy buildben.
A feladat becsomagolása a terjesztéshez
Ha csak néhány projektben vagy egyetlen megoldásban kell használnia az egyéni feladatot, akkor elegendő lehet a feladat nyers összeállításként történő felhasználása. Azonban a legjobb módja annak, hogy a feladatot máshol használhassa vagy másokkal megossza, ha NuGet-csomagként készíti el.
Az MSBuild feladatcsomagok néhány kulcsfontosságú különbséget mutatnak a NuGet csomagokkal szemben:
- Saját összetevőfüggőségeiket kell csomagolniuk, ahelyett, hogy ezeket a függőségeket kiteszik a fogyasztói projektnek.
- Nem csomagolják be a szükséges szerelvényeket egy
lib/<target framework>mappába, mert ez azt eredményezné, hogy a NuGet a feladatokat használó csomagba belefoglalja a szerelvényeket - Csak a Microsoft.Build összeállításokkal kell kompilálniuk - futásidőben ezeket a tényleges MSBuild motor biztosítja, ezért nem kell belefoglalni a csomagba.
- Létrehoznak egy speciális
.deps.jsonfájlt, amely segít az MSBuildnek a tevékenység függőségeinek (különösen a natív függőségeknek) konzisztens betöltésében
Ezeknek a céloknak a eléréséhez néhány módosítást kell végrehajtania a standard projektfájlon felül és azon túl is, amelyeket esetleg ismer.
NuGet-csomag létrehozása
A NuGet-csomag létrehozása az egyéni feladat másoknak való terjesztésének ajánlott módja.
Felkészülés a csomag létrehozására
A NuGet-csomag létrehozására való felkészüléshez végezze el a projektfájl néhány módosítását a csomag leírását leíró részletek megadásához. A létrehozott kezdeti projektfájl a következő kódhoz hasonlít:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" />
</ItemGroup>
</Project>
NuGet-csomag létrehozásához adja hozzá a következő kódot a csomag tulajdonságainak beállításához. A támogatott MSBuild tulajdonságok teljes listáját a Pack dokumentációjábanláthatja:
<PropertyGroup>
...
<IsPackable>true</IsPackable>
<Version>1.0.0</Version>
<Title>AppSettingStronglyTyped</Title>
<Authors>Your author name</Authors>
<Description>Generates a strongly typed setting class base on a text file.</Description>
<PackageTags>MyTags</PackageTags>
<Copyright>Copyright ©Contoso 2022</Copyright>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
...
</PropertyGroup>
A CopyLocalLockFileAssemblies tulajdonságra azért van szükség, hogy a függőségek át legyenek másolva a kimeneti könyvtárba.
Függőségek megjelölése magánjellegűként
Az MSBuild tevékenység függőségeit a csomagba kell csomagolni; nem fejezhetők ki normál csomaghivatkozásként. A csomag nem teszi elérhetővé a külső felhasználók számára a szokásos függőségeket. Ehhez két lépésre van szükség: az assembly-k privátként való megjelölése és tényleges beágyazásuk a generált csomagba. Ebben a példában feltételezzük, hogy a feladat a Microsoft.Extensions.DependencyInjection működésétől függ, ezért adjon hozzá egy PackageReference-et a Microsoft.Extensions.DependencyInjection-höz a 6.0.0verzióban.
<ItemGroup>
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0" />
<PackageReference
Include="Microsoft.Extensions.DependencyInjection"
Version="6.0.0" />
</ItemGroup>
Most jelölje meg ennek a feladat projektnek minden függőségét, mind a PackageReference, mind a ProjectReference a PrivateAssets="all" attribútummal. Ez azt fogja jelezni a NuGetnek, hogy ezekhez a függőségekhez a használatban lévő projektek számára semmiképpen ne nyújtson hozzáférést. A függőségi eszközök szabályozásáról a NuGet dokumentációjábanolvashat bővebben.
<ItemGroup>
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0"
PrivateAssets="all"
/>
<PackageReference
Include="Microsoft.Extensions.DependencyInjection"
Version="6.0.0"
PrivateAssets="all"
/>
</ItemGroup>
Csomagoljuk egybe a függőségeket a csomagba.
A függőségeink futtatásidejű elemeit is be kell ágyaznia a Feladatcsomagba. Ennek két része van: egy MSBuild-cél, amely hozzáadja a függőségeinket a BuildOutputInPackage ItemGroup-hoz, valamint néhány tulajdonság, amely szabályozza ezeknek a BuildOutputInPackage elemeknek az elrendezését. Erről a folyamatról további információt találhat a NuGet dokumentációjában.
<PropertyGroup>
...
<!-- This target will run when MSBuild is collecting the files to be packaged, and we'll implement it below. This property controls the dependency list for this packaging process, so by adding our custom property we hook ourselves into the process in a supported way. -->
<TargetsForTfmSpecificBuildOutput>
$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
</TargetsForTfmSpecificBuildOutput>
<!-- This property tells MSBuild where the root folder of the package's build assets should be. Because we are not a library package, we should not pack to 'lib'. Instead, we choose 'tasks' by convention. -->
<BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
<!-- NuGet does validation that libraries in a package are exposed as dependencies, but we _explicitly_ do not want that behavior for MSBuild tasks. They are isolated by design. Therefore we ignore this specific warning. -->
<NoWarn>NU5100</NoWarn>
<!-- Suppress NuGet warning NU5128. -->
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
...
</PropertyGroup>
...
<!-- This is the target we defined above. It's purpose is to add all of our PackageReference and ProjectReference's runtime assets to our package output. -->
<Target
Name="CopyProjectReferencesToPackage"
DependsOnTargets="ResolveReferences">
<ItemGroup>
<!-- The TargetPath is the path inside the package that the source file will be placed. This is already precomputed in the ReferenceCopyLocalPaths items' DestinationSubPath, so reuse it here. -->
<BuildOutputInPackage
Include="@(ReferenceCopyLocalPaths)"
TargetPath="%(ReferenceCopyLocalPaths.DestinationSubPath)" />
</ItemGroup>
</Target>
Ne csomagolja be a Microsoft.Build.Utilities.Core szerelvényt
Ahogy fentebb említettük, ezt a függőséget az MSBuild maga biztosítja futásidőben, így nem kell a csomagba csomagolnunk. Ehhez adja hozzá a ExcludeAssets="Runtime" attribútumot a PackageReference-hez.
...
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0"
PrivateAssets="all"
ExcludeAssets="Runtime"
/>
...
deps.json-fájl létrehozása és beágyazása
A deps.json fájlt az MSBuild használhatja a függőségek megfelelő verzióinak betöltéséhez. Hozzá kell adnia néhány MSBuild tulajdonságot a fájl létrehozásához, mivel a kódtárak esetében alapértelmezés szerint nem jön létre. Ezután adjon hozzá egy célértéket, hogy a csomag kimenetében szerepeljen, hasonlóan a csomagfüggőségeinkhez.
<PropertyGroup>
...
<!-- Tell the SDK to generate a deps.json file -->
<GenerateDependencyFile>true</GenerateDependencyFile>
...
</PropertyGroup>
...
<!-- This target adds the generated deps.json file to our package output -->
<Target
Name="AddBuildDependencyFileToBuiltProjectOutputGroupOutput"
BeforeTargets="BuiltProjectOutputGroup"
Condition=" '$(GenerateDependencyFile)' == 'true'">
<ItemGroup>
<BuiltProjectOutputGroupOutput
Include="$(ProjectDepsFilePath)"
TargetPath="$(ProjectDepsFileName)"
FinalOutputPath="$(ProjectDepsFilePath)" />
</ItemGroup>
</Target>
MSBuild tulajdonságok és célok belefoglalása egy csomagba
A szakasz hátterének megértéséhez először olvassa el a tulajdonságokat és célokat, majd ismerje meg, hogyan lehet a tulajdonságokat és célokat belefoglalni egy NuGet-csomagba.
Bizonyos esetekben előfordulhat, hogy egyéni buildcélokat vagy tulajdonságokat szeretne hozzáadni a csomagot használó projektekhez, például egyéni eszköz vagy folyamat futtatásához a buildelés során. Úgy teheti meg, hogy a fájlokat <package_id>.targets vagy <package_id>.props formában helyezi a projekt build mappájába.
A projekt gyökérmappájában található fájlok a build mappában minden célkeretrendszerhez megfelelőnek minősülnek.
Ebben a szakaszban a .props és .targets fájlokban fogja összekapcsolni a feladat megvalósítását, amelyet a NuGet csomag tartalmaz, és automatikusan betöltésre kerül egy hivatkozási projektből.
A feladat projektfájljában AppSettingStronglyTyped.csprojadja hozzá a következő kódot:
<ItemGroup> <!-- these lines pack the build props/targets files to the `build` folder in the generated package. by convention, the .NET SDK will look for build\<Package Id>.props and build\<Package Id>.targets for automatic inclusion in the build. --> <Content Include="build\AppSettingStronglyTyped.props" PackagePath="build\" /> <Content Include="build\AppSettingStronglyTyped.targets" PackagePath="build\" /> </ItemGroup>Hozzon létre egy mappát, és abban a mappában adjon hozzá két szövegfájlt:
AppSettingStronglyTyped.propsés AppSettingStronglyTyped.targets. AAppSettingStronglyTyped.propsa Microsoft.Common.props korai szakaszában van importálva, és a később definiált tulajdonságok nem érhetők el. Ezért ne hivatkozz olyan tulajdonságokra, amelyek még nincsenek definiálva; üresnek értékelik.A Directory.Build.targets a Microsoft.Common.targets importálása a
.targetsfájloknak a NuGet-csomagokból történő importálása után történik. Így felülbírálhatja a legtöbb buildlogikában definiált tulajdonságokat és célokat, vagy beállíthatja az összes projekt tulajdonságait, függetlenül attól, hogy az egyes projektek mit állítottak be. Lásd: importálási rendelés.AppSettingStronglyTyped.props tartalmazza a feladatot, és meghatároz néhány alapértelmezett értéket tartalmazó tulajdonságot:
<?xml version="1.0" encoding="utf-8" ?> <Project> <!--defining properties interesting for my task--> <PropertyGroup> <!--The folder where the custom task will be present. It points to inside the nuget package. --> <_AppSettingsStronglyTyped_TaskFolder>$(MSBuildThisFileDirectory)..\tasks\netstandard2.0</_AppSettingsStronglyTyped_TaskFolder> <!--Reference to the assembly which contains the MSBuild Task--> <CustomTasksAssembly>$(_AppSettingsStronglyTyped_TaskFolder)\$(MSBuildThisFileName).dll</CustomTasksAssembly> </PropertyGroup> <!--Register our custom task--> <UsingTask TaskName="$(MSBuildThisFileName).AppSettingStronglyTyped" AssemblyFile="$(CustomTasksAssembly)"/> <!--Task parameters default values, this can be overridden--> <PropertyGroup> <RootFolder Condition="'$(RootFolder)' == ''">$(MSBuildProjectDirectory)</RootFolder> <SettingClass Condition="'$(SettingClass)' == ''">MySetting</SettingClass> <SettingNamespace Condition="'$(SettingNamespace)' == ''">example</SettingNamespace> <SettingExtensionFile Condition="'$(SettingExtensionFile)' == ''">mysettings</SettingExtensionFile> </PropertyGroup> </Project>A
AppSettingStronglyTyped.propsfájl automatikusan megjelenik a csomag telepítésekor. Ezután az ügyfél rendelkezik a rendelkezésre álló feladatokkal és néhány alapértelmezett értékkel. Azonban soha nem használják. A kód működésbe lépéséhez definiáljon néhány célt aAppSettingStronglyTyped.targetsfájlban, amely szintén automatikusan szerepelni fog a csomag telepítésekor:<?xml version="1.0" encoding="utf-8" ?> <Project> <!--Defining all the text files input parameters--> <ItemGroup> <SettingFiles Include="$(RootFolder)\*.$(SettingExtensionFile)" /> </ItemGroup> <!--A target that generates code, which is executed before the compilation--> <Target Name="BeforeCompile" Inputs="@(SettingFiles)" Outputs="$(RootFolder)\$(SettingClass).generated.cs"> <!--Calling our custom task--> <AppSettingStronglyTyped SettingClassName="$(SettingClass)" SettingNamespaceName="$(SettingNamespace)" SettingFiles="@(SettingFiles)"> <Output TaskParameter="ClassNameFile" PropertyName="SettingClassFileName" /> </AppSettingStronglyTyped> <!--Our generated file is included to be compiled--> <ItemGroup> <Compile Remove="$(SettingClassFileName)" /> <Compile Include="$(SettingClassFileName)" /> </ItemGroup> </Target> <!--The generated file is deleted after a general clean. It will force the regeneration on rebuild--> <Target Name="AfterClean"> <Delete Files="$(RootFolder)\$(SettingClass).generated.cs" /> </Target> </Project>Az első lépés egy ItemGrouplétrehozása, amely az elolvasni kívánt szövegfájlokat (több is lehet) jelöli, és ez lesz a tevékenységparaméterünk egy része. Vannak alapértelmezett értékek a helyhez és a bővítményhez, ahol keresünk, de felülbírálhatja azokat az értékeket, amelyek meghatározzák a tulajdonságokat az ügyfél MSBuild projektfájljában.
Ezután definiáljon két MSBuild-célt. Bővítjük az MSBuild folyamatot , felülírva az előre definiált célokat:
BeforeCompile: A cél az, hogy meghívja az egyéni feladatot az osztály létrehozására, és foglalja magában a lefordítandó osztályt. Az ebben a célban szereplő feladatok az összesítés befejezése előtt lesznek beszúrva. A bemeneti és kimeneti mezők az inkrementális építési folyamathoz kapcsolódnak. Ha az összes kimeneti elem up-to-date, az MSBuild kihagyja a célértéket. A cél fokozatos felépítése jelentősen javíthatja az építések teljesítményét. Az elemek akkor tekinthetők up-todátumnak, ha a kimeneti fájl ugyanolyan korú vagy újabb, mint a bemeneti fájl vagy fájlok.AfterClean: A cél a létrehozott osztályfájl törlése az általános tisztítás után. A feladatok ebben a célkitűzésben kerülnek beszúrásra, miután a fő tisztítási funkciót meghívták. A kódgenerálási lépést az újraépítési cél végrehajtásakor meg kell ismételni.
A NuGet-csomag létrehozása
A NuGet-csomag létrehozásához használhatja a Visual Studiót (kattintson a jobb gombbal a projektcsomópontra Megoldáskezelő, és válassza Csomag). Ezt a parancssor használatával is megteheti. Lépjen arra a mappára, amelyben a feladatprojekt-fájl AppSettingStronglyTyped.csproj található, és hajtsa végre a következő parancsot:
// -o is to define the output; the following command chooses the current folder.
dotnet pack -o .
Gratulálok! Létrehozott egy NuGet-csomagot \AppSettingStronglyTyped\AppSettingStronglyTyped\AppSettingStronglyTyped.1.0.0.nupkg.
A csomag egy .nupkg kiterjesztéssel rendelkezik, és tömörített zip-fájl. Tömörítő programmal megnyithatja. A .target és .props fájlok a build mappában találhatók. A .dll fájl a lib\netstandard2.0\ mappában található. A AppSettingStronglyTyped.nuspec fájl a gyökérszinten van.
(Nem kötelező) Több célplatform támogatása
Fontolja meg a Full (.NET-keretrendszer) és Core (beleértve a .NET 5-ös és újabb verziójú) MSBuild disztribúciókat is a lehető legszélesebb felhasználói bázis támogatása érdekében.
A "normál" .NET SDK-projektek esetében a többtargeting több TargetFrameworks beállítását jelenti a projektfájlban. Ha ezt teszi, a buildek mindkét TargetFrameworkMonikerhez aktiválódnak, és az összesített eredmények egyetlen összetevőként csomagolhatók.
Ez nem az MSBuild teljes története. Az MSBuild két elsődleges szállítójárműve van: a Visual Studio és a .NET SDK. Ezek nagyon különböző futtatókörnyezetek; az egyik a .NET-keretrendszer futtatókörnyezetében, a másik a CoreCLR-en fut. Ez azt jelenti, hogy bár a kód meg tudja célozni a netstandard2.0-t, a feladatlogika eltérhet attól függően, hogy milyen MSBuild futtatókörnyezettípus van jelenleg használatban. Gyakorlatilag, mivel a .NET 5.0-s és újabb verzióban olyan sok új API található, érdemes az MSBuild feladat forráskódját egyszerre több TargetFrameworkMonikerhez használni, valamint az MSBuild céllogikát több MSBuild futtatókörnyezet-típushoz.
A multitargethez szükséges módosítások
Több TargetFrameworkMoniker (TFM) megcélzása:
Módosítsa a projektfájlt úgy, hogy a
net472ésnet6.0TFM-eket használja (ez utóbbi változhat attól függően, hogy melyik SDK-szintet szeretné megcélzni). Érdemes lehetnetcoreapp3.1megcélzni, amíg a .NET Core 3.1 nem lesz támogatott. Ha ezt teszi, a csomagmappa struktúrájatasks/-rőltasks/<TFM>/-ra változik.<TargetFrameworks>net472;net6.0</TargetFrameworks>Frissítse a
.targetsfájlokat, hogy a megfelelő TFM-et használja a feladatok betöltéséhez. A szükséges TFM a fent kiválasztott .NET TFM alapján változik, de egynet472ésnet6.0célzó projekt esetében a következő tulajdonságot használhatja:
<AppSettingStronglyTyped_TFM Condition=" '$(MSBuildRuntimeType)' != 'Core' ">net472</AppSettingStronglyTyped_TFM>
<AppSettingStronglyTyped_TFM Condition=" '$(MSBuildRuntimeType)' == 'Core' ">net6.0</AppSettingStronglyTyped_TFM>
Ez a kód a MSBuildRuntimeType tulajdonságot használja proxyként az aktív üzemeltetési környezethez. Miután beállította ezt a tulajdonságot, használhatja a UsingTask-ban a megfelelő AssemblyFilebetöltéséhez.
<UsingTask
AssemblyFile="$(MSBuildThisFileDirectory)../tasks/$(AppSettingStronglyTyped_TFM)/AppSettingStronglyTyped.dll"
TaskName="AppSettingStrongTyped.AppSettingStronglyTyped" />
Következő lépések
Számos feladat magában foglalja egy végrehajtható fájl hívását. Bizonyos esetekben használhatja az Exec-feladatot, de ha az Exec-tevékenység korlátozásai problémát jelentenek, létrehozhat egy egyéni feladatot is. Az alábbi oktatóanyag mindkét lehetőséget egy reálisabb kódgenerálási forgatókönyvvel mutatja be: egyéni feladat létrehozása a REST API ügyfélkódjának létrehozásához.
Vagy megtudhatja, hogyan tesztelhet egyéni feladatokat.