Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu öğreticide, C# dilinde MSBuild'de kod oluşturmayı işleyen özel bir görev oluşturacak ve ardından görevi bir derlemede kullanacaksınız. Bu örnekte, temizleme ve yeniden oluşturma işlemlerini işlemek için MSBuild'in nasıl kullanılacağı gösterilmektedir. Örnek ayrıca artımlı derlemenin nasıl desteklendiğini de gösterir, böylece kod yalnızca giriş dosyaları değiştiğinde oluşturulur. Belirtilen teknikler, çok çeşitli kod oluşturma senaryoları için geçerlidir. Bu adımlarda, görevi dağıtım için paketlemek için NuGet kullanımı da gösterilir ve öğreticide, sorun giderme deneyimini geliştirmek için BinLog görüntüleyicisini kullanmak için isteğe bağlı bir adım yer alır.
Önkoşullar
Görevler, hedefler ve özellikler gibi MSBuild kavramlarını anlamanız gerekir. bkz. MSBuild kavramları.
Örnekler, Visual Studio ile birlikte yüklenen ancak ayrı olarak da yüklenebilen MSBuild gerektirir. Bkz. Visual Studio olmadan MSBuild'i indirme.
Kod örneğine giriş
Örnek, ayarlanacak değerleri içeren bir giriş metin dosyası alır ve bu değerleri oluşturan koda sahip bir C# kod dosyası oluşturur. Bu basit bir örnek olsa da, aynı temel teknikler daha karmaşık kod oluşturma senaryolarına uygulanabilir.
Bu öğreticide AppSettingStronglyTyped adlı bir MSBuild özel görevi oluşturacaksınız. Görev, bir dizi metin dosyasını ve her dosyayı aşağıdaki biçime sahip satırlarla okur:
propertyName:type:defaultValue
Kod, tüm sabitleri içeren bir C# sınıfı oluşturur. Bir sorun derlemeyi durdurmalı ve kullanıcıya sorunu tanılamak için yeterli bilgi vermelidir.
Bu öğretici için tam örnek kod, GitHub'daki .NET örnekleri deposundaki Özel görev - kod oluşturma'de bulunabilir.
AppSettingStronglyTyped projesini oluşturma
.NET Standart Sınıf Kitaplığı oluşturun. Çerçeve .NET Standard 2.0 olmalıdır.
.NET Core Komut Satırı'nda paketlenmiş olan tam MSBuild (Visual Studio tarafından kullanılan) ile taşınabilir MSBuild arasındaki farka dikkat edin.
- Tam MSBuild: MSBuild'in bu sürümü genellikle Visual Studio'da bulunur. .NET Framework üzerinde çalışır. Visual Studio, çözümünüzde veya projenizde Derleme çalıştırdığınızda bunu kullanır. Bu sürüm, Visual Studio Geliştirici Komut İstemi veya PowerShell gibi bir komut satırı ortamında da kullanılabilir.
- .NET MSBuild: MSBuild'in bu sürümü .NET Core Komut Satırı'nda paketlenmiştir. .NET Core üzerinde çalışır. Visual Studio bu MSBuild sürümünü doğrudan çağırmaz. Yalnızca Microsoft.NET.Sdk kullanılarak derleyen projeleri destekler.
.NET Framework ile .NET Core gibi başka bir .NET uygulaması arasında kod paylaşmak istiyorsanız, kitaplığınız .NET Standard 2.0'ı hedeflemeli ve .NET Framework üzerinde çalışan Visual Studio'da çalıştırmak istiyorsunuz. .NET Framework, .NET Standard 2.1'i desteklemez.
Başvurulacak MSBuild API sürümünü seçin
Özel bir görevi derlerken, Visual Studio'nun en düşük sürümüyle ve/veya desteklemeyi beklediğiniz .NET SDK'sı ile eşleşen MSBuild API'sinin (Microsoft.Build.*
) sürümüne başvurmanız gerekir. Visual Studio 2019 kullanıcılarını desteklemek için MSBuild 16.11 üzerinde derlemeniz gerekir.
AppSettingStronglyTyped MSBuild özel görevini oluşturma
İlk adım, MSBuild özel görevini oluşturmaktır. MSBuild özel görev oluşturma konusunda bilgi, aşağıdaki adımları anlamanıza katkıda bulunabilir. MSBuild özel görevi, ITask arabirimini devreye sokan bir sınıftır.
Microsoft.Build.Utilities.Core NuGet paketine bir başvuru ekleyin ve ardından Microsoft.Build.Utilities.Task'ten türetilen AppSettingStronglyTyped adlı bir sınıf oluşturun.
Üç özellik ekleyin. Bu özellikler, kullanıcıların görevi bir istemci projesinde kullanırken ayarladıkları görevin parametrelerini tanımlar:
//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; }
Görev, SettingFiles işler ve bir sınıf
SettingNamespaceName.SettingClassName
oluşturur. Oluşturulan sınıf, metin dosyasının içeriğine göre bir dizi sabite sahip olur.Görev çıkışı, oluşturulan kodun dosya adını veren bir dize olmalıdır:
// The filename where the class was generated [Output] public string ClassNameFile { get; set; }
Özel bir görev oluşturduğunuzda, Microsoft.Build.Utilities.Task'den devralırsınız. Görevi uygulamak için Execute() yöntemini geçersiz kılarsınız.
Execute
yöntemi, görev başarılı olursatrue
döndürür ve aksi takdirdefalse
.Task
Microsoft.Build.Framework.ITask uygular ve bazıITask
üyelerinin varsayılan uygulamalarını sağlar ve ayrıca bazı günlük tutma işlevleri sağlar. Bir sorun meydana geldiğinde ve görevin bir hata sonucu döndürmesi gerektiğinde, özellikle görevi tanılamak ve sorun gidermek için durumu günlüğe kaydetmek önemlidir. (false
) Hata oluştuğunda, sınıf TaskLoggingHelper.LogError'ı çağırarak hataya işaret eder.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; }
Görev API'si, kullanıcıya neyin yanlış gittiğini belirtmeden hatayı döndürmeye izin verir. Boole kodu yerine
!Log.HasLoggedErrors
döndürmek ve bir sorun olduğunda hata kaydetmek en iyisidir.
Kayıt hataları
Hataları günlüğe kaydetmenin en iyi yöntemi, hata günlüğe kaydetme sırasında satır numarası ve ayrı bir hata kodu gibi ayrıntıları sağlamaktır. Aşağıdaki kod, metin giriş dosyasını ayrıştırmaktadır ve hatayı oluşturan metin dosyasındaki satır numarasıyla TaskLoggingHelper.LogError yöntemini kullanır.
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);
}
Önceki kodda gösterilen teknikleri kullanarak, metin giriş dosyasının söz dizimindeki hatalar yararlı tanılama bilgileriyle derleme hataları olarak gösterilir:
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)
Görevinizdeki istisnaları yakaladığınızda TaskLoggingHelper.LogErrorFromException yöntemini kullanın. Bu, örneğin özel durumun atıldığı çağrı yığınını alarak hata çıkışını geliştirir.
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;
}
Oluşturulan kod dosyasının metnini oluşturmak için bu girişleri kullanan diğer yöntemlerin uygulanması burada gösterilmez; Örnek depodaki AppSettingStronglyTyped.cs bakın.
Örnek kod, derleme işlemi sırasında C# kodu oluşturur. Görev diğer tüm C# sınıflarında olduğu gibi, bu öğreticiyi tamamladığınızda özelleştirebilir ve kendi senaryonuz için gereken işlevleri ekleyebilirsiniz.
Bir konsol uygulaması oluşturun ve özel görevi kullanın
Bu bölümde, görevi kullanan standart bir .NET Core Konsol Uygulaması oluşturacaksınız.
Önemli
Aynı MSBuild işleminde tüketilecek olan bir MSBuild özel görevi oluşturmaktan kaçınmak önemlidir. Yeni proje tamamen farklı bir Visual Studio çözümünde olmalıdır veya yeni proje önceden oluşturulmuş ve standart çıktıdan yeniden yerleştirilmiş bir dll kullanmalıdır.
Yeni bir Visual Studio Çözümünde .NET Konsol projesi MSBuildConsoleExample oluşturun.
Görevi dağıtmanın normal yolu NuGet paketinden geçer, ancak geliştirme ve hata ayıklama sırasında,
.props
ve.targets
hakkındaki tüm bilgileri doğrudan uygulamanızın proje dosyasına ekleyebilir ve görevi başkalarına dağıttığınızda NuGet biçimine geçebilirsiniz.Kod oluşturma görevini kullanmak için proje dosyasını değiştirin. Bu bölümdeki kod listesi, göreve başvurduktan, görevin giriş parametrelerini ayarladıktan ve oluşturulan kod dosyasının beklediğiniz gibi kaldırılması için temiz ve yeniden derleme işlemlerini işlemek için hedefleri yazdıktan sonra değiştirilen proje dosyasını gösterir.
Görevler UsingTask öğesi (MSBuild)kullanılarak kaydedilir.
UsingTask
öğesi görevi kaydeder; MSBuild'e görevin adını ve görev sınıfını içeren derlemenin nasıl bulunup çalıştırıldığını bildirir. Derleme yolu proje dosyasına göredir.PropertyGroup
, görevde tanımlanan özelliklere karşılık gelen özellik tanımlarını içerir. Bu özellikler öznitelikler kullanılarak ayarlanır ve görev adı öğe adı olarak kullanılır.TaskName
, meclisten başvurulacak görevin adıdır. Bu öznitelik her zaman tam olarak belirtilen ad alanlarını kullanmalıdır.AssemblyFile
derlemenin dosya yoludur.Görevi başlatmak için, görevi uygun hedefe, bu durumda
GenerateSetting
'a ekleyin.Hedef
ForceGenerateOnRebuild
, oluşturulan dosyayı silerek temizleme ve yeniden derleme işlemlerini işler.CoreClean
özniteliğiniAfterTargets
olarak ayarlayarakCoreClean
hedefinin ardından çalışacak şekilde ayarlanır.<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>
Not
CoreClean
gibi bir hedefi geçersiz kılmak yerine, bu kod, hedefleri ,(BeforeTarget ve AfterTarget) sıralamak için farklı bir yöntem kullanır. SDK stilindeki projeler, proje dosyasının son satırından sonra hedefleri örtük olarak içeri aktarır; bu, içeri aktarmalarınızı el ile belirtmediğiniz sürece varsayılan hedefleri geçersiz kılamadığınız anlamına gelir. bkz. önceden tanımlanmış hedefleri geçersiz kılma.Inputs
veOutputs
öznitelikleri, artımlı derlemeler için bilgi sağlayarak MSBuild'in daha verimli olmasını sağlar. Girişlerin tarihleri, hedefin çalıştırılması gerekip gerekmediğini veya önceki derlemenin çıkışının yeniden kullanılıp kullanılamayabileceğini görmek için çıkışlarla karşılaştırılır.Keşfedilmesi için tanımlanan uzantıyla giriş metin dosyasını oluşturun. Varsayılan uzantıyı kullanarak kökte aşağıdaki içerikle
MyValues.mysettings
oluşturun:Greeting:string:Hello World!
Yeniden derleyin, böylece oluşturulan dosya yaratılıp derlenir. MySetting.generated.cs dosyasının proje klasörünü denetleyin.
MySetting sınıfı yanlış ad alanında, bu yüzden uygulama ad alanımızı kullanmak için bir değişiklik yapın. Proje dosyasını açın ve aşağıdaki kodu ekleyin:
<PropertyGroup> <SettingNamespace>MSBuildConsoleExample</SettingNamespace> </PropertyGroup>
Yeniden derleyin ve sınıfın
MSBuildConsoleExample
ad alanında olduğunu gözlemleyin. Bu şekilde, giriş olarak kullanılacak oluşturulan sınıf adını (SettingClass
), metin uzantısı dosyalarını (SettingExtensionFile
) ve isterseniz bunların konumunu (RootFolder
) yeniden tanımlayabilirsiniz.Program.cs
'ı açın ve sabit kodlanmış 'Merhaba Dünya!!' ifadesini değiştirin. kullanıcı tanımlı sabiteye:static void Main(string[] args) { Console.WriteLine(MySetting.Greeting); }
Programı çalıştır ve oluşturulan sınıftan selamlamayı yazdır.
(İsteğe bağlı) Derleme sürecinde olayları günlüğe kaydetme
Bir komut satırı komutu kullanarak derlemek mümkündür. Proje klasörüne gidin. İkili günlük oluşturmak için -bl
(ikili günlük) seçeneğini kullanacaksınız. İkili kayıt, derleme işlemi sırasında neler olduğunu bilmek için yararlı bilgiler içerir.
# 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
Her iki komut da msbuild.binlog
ile açilebilen bir günlük dosyası oluşturur.
/t:rebuild
seçeneği, yeniden oluşturma hedefini çalıştırma anlamına gelir. Oluşturulan kod dosyasının yeniden oluşturulmasını zorlar.
Tebrikler! Kod oluşturan bir görev oluşturdunuz ve bunu bir derlemede kullandınız.
Görevi dağıtıma paketle
Özel görevinizi yalnızca birkaç projede veya tek bir çözümde kullanmanız gerekiyorsa, görevi ham derleme olarak kullanmak tek ihtiyacınız olan şey olabilir, ancak görevinizi başka bir yerde kullanmak veya başkalarıyla paylaşmak için hazırlamanın en iyi yolu NuGet paketidir.
MSBuild Görev paketleri, NuGet kitaplık paketlerinden birkaç önemli farka sahiptir:
- Bu bağımlılıkları tüketen projeye ifşa etmek yerine kendi derleme bağımlılıklarını paketlemeleri gerekir
- Gerekli derlemeleri bir
lib/<target framework>
klasörüne paketlemezler, çünkü bu NuGet'in görevi kullanan herhangi bir pakete derlemeleri eklemesine neden olur - Yalnızca Microsoft.Build derlemelerine karşı derlemek gerekir - çalışma zamanında bunlar gerçek MSBuild motoru tarafından sağlanacaktır ve bu nedenle pakete dahil edilmesine gerek yoktur.
- MSBuild'in Görevin bağımlılıklarını (özellikle yerel bağımlılıkları) tutarlı bir şekilde yüklemesine yardımcı olan özel bir
.deps.json
dosyası oluştururlar
Bu hedeflerin tümünü gerçekleştirmek için, standart proje dosyasında aşina olabileceğiniz değerlerin üzerinde ve ötesinde birkaç değişiklik yapmanız gerekir.
NuGet paketi oluşturma
NuGet paketi oluşturmak, özel görevinizi başkalarına dağıtmanın önerilen yoludur.
Paketi oluşturmaya hazırlanma
NuGet paketi oluşturmaya hazırlanmak için proje dosyasında bazı değişiklikler yapıp paketi açıklayan ayrıntıları belirtin. Oluşturduğunuz ilk proje dosyası aşağıdaki koda benzer:
<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 paketi oluşturmak için aşağıdaki kodu ekleyerek paketin özelliklerini ayarlayın. desteklenen MSBuild özelliklerinin tam listesini Paketi belgelerindegörebilirsiniz:
<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>
Bağımlılıkların çıkış dizinine kopyalandığından emin olmak için CopyLocalLockFileAssemblies özelliği gereklidir.
Bağımlılıkları özel olarak işaretleme
MSBuild görevinizin bağımlılıkları paket içinde paketlenmelidir; bunlar normal paket başvuruları olarak ifade edilemez. Paket, dış kullanıcılara normal bağımlılıklar sunmaz. Bunu yapmak için iki adım gerekir: derlemelerinizi özel olarak işaretleme ve bunları oluşturulan pakete ekleme. Bu örnekte, görevinizin çalışması için Microsoft.Extensions.DependencyInjection
'a bağlı olduğunu varsayacağız, bu yüzden PackageReference
sürümünde Microsoft.Extensions.DependencyInjection
'ye bir 6.0.0
ekleyin.
<ItemGroup>
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0" />
<PackageReference
Include="Microsoft.Extensions.DependencyInjection"
Version="6.0.0" />
</ItemGroup>
Şimdi, bu Görev projesinin tüm bağımlılıklarını hem PackageReference
hem de ProjectReference
olarak, PrivateAssets="all"
özniteliğiyle işaretleyin. Bu, NuGet'e bu bağımlılıkları tüketen projelere hiçbir şekilde göstermemelerini söyler.
NuGet belgelerindebağımlılık varlıklarını denetleme hakkında daha fazla bilgi edinebilirsiniz.
<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>
Bağımlılıkları pakete dahil et
Bağımlılıklarımızın çalışma zamanı bileşenlerini Görev paketine de eklemelisiniz. Bunun iki bölümü vardır: bağımlılıklarımızı BuildOutputInPackage
ItemGroup'a ekleyen bir MSBuild hedefi ve bu BuildOutputInPackage
öğelerinin düzenini denetleyen birkaç özellik.
NuGet belgelerinde bu işlemhakkında daha fazla bilgi edinebilirsiniz.
<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>
Microsoft.Build.Utilities.Core derlemesini paketlemeyin
Yukarıda açıklandığı gibi, bu bağımlılık çalışma zamanında MSBuild tarafından sağlanacaktır, bu nedenle paketi paketlememiz gerekmez. Bunu yapmak için ExcludeAssets="Runtime"
özniteliğini PackageReference
ekleyin
...
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0"
PrivateAssets="all"
ExcludeAssets="Runtime"
/>
...
deps.json dosyası oluşturma ve ekleme
deps.json
dosyası, bağımlılıklarınızın doğru sürümlerinin yüklendiğinden emin olmak için MSBuild tarafından kullanılabilir. Dosyanın oluşturulmasını sağlamak için bazı MSBuild özelliklerini eklemeniz gerekecek, çünkü kitaplıklar için bu varsayılan olarak oluşturulmaz. Ardından bunu paket çıkışımıza eklemek için paket bağımlılıklarımız için yaptığınıza benzer bir hedef ekleyin.
<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 özelliklerini ve hedeflerini bir pakete ekleme
Bu bölümle ilgili arka plan bilgisi için, önce özellikleri ve hedefleri hakkında okuyun ve ardından bir NuGet paketineözellik ve hedefleri nasıl dahil edeceğinizi öğrenin.
Bazı durumlarda, paketinizi kullanan projelere özel derleme hedefleri veya özellikler eklemek isteyebilirsiniz; örneğin derleme sırasında özel bir araç veya işlem çalıştırma. Bunu, dosyaları projedeki <package_id>.targets
klasörüne <package_id>.props
veya build
biçiminde yerleştirerek yaparsınız.
Proje kök derleme klasöründeki dosyalar tüm hedef çerçeveler için uygun olarak kabul edilir.
Bu bölümde, .props
ve .targets
dosyalarını NuGet paketimize dahil edilecek ve referans alan bir projeden otomatik olarak yüklenecek şekilde görev uygulama sürecine bağlayacaksınız.
Görevin proje dosyasına AppSettingStronglyTyped.csproj aşağıdaki kodu ekleyin:
<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>
bir derleme klasörü oluşturun ve bu klasöre iki metin dosyası ekleyin:
AppSettingStronglyTyped.props
ve AppSettingStronglyTyped.targets.AppSettingStronglyTyped.props
, Microsoft.Common.propserken içe aktarılır ve daha sonra tanımlanan özellikler o sırada kullanılamaz. Bu nedenle, henüz tanımlanmamış özelliklere başvurmaktan kaçının; boş olarak değerlendirilir.Directory.Build.targets, NuGet paketlerinden dosyalar içe aktarıldıktan sonra
.targets
dosyasından içe aktarılır. Bu nedenle, derleme mantığının çoğunda tanımlanan özellikleri ve hedefleri geçersiz kılabilir veya tek tek projelerin ayarladığından bağımsız olarak tüm projeleriniz için özellikleri ayarlayabilir. Bakınız ithalat siparişi.AppSettingStronglyTyped.props görevi içerir ve bazı özellikleri varsayılan değerlerle tanımlar:
<?xml version="1.0" encoding="utf-8" ?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!--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>
paket yüklendiğinde
AppSettingStronglyTyped.props
dosyası otomatik olarak eklenir. Ardından, istemcide kullanılabilir görev ve bazı varsayılan değerler bulunur. Ancak, hiçbir zaman kullanılmaz. Bu kodu uygulamaya koymak içinAppSettingStronglyTyped.targets
dosyasında bazı hedefler tanımlayın; paket yüklendiğinde otomatik olarak da eklenecektir:<?xml version="1.0" encoding="utf-8" ?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!--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>
İlk adım, okunacak metin dosyalarını (birden fazla olabilir) temsil eden bir ItemGroupoluşturulmasıdır ve görev parametremizden bazıları olacaktır. Arama yaptığımız konum ve uzantı için varsayılan değerler vardır, ancak istemci MSBuild proje dosyasındaki özellikleri tanımlayan değerleri geçersiz kılabilirsiniz.
Ardından iki MSBuild hedefi veolarak tanımlayın. MSBuild işlemini önceden tanımlanmış hedefleri geçersiz kılarak genişletmeyi:
BeforeCompile
: Amaç, sınıfı oluşturmak ve derlenecek sınıfı dahil etmek için özel görevi çağırmaktır. Bu hedefteki görevler çekirdek derleme yapılmadan önce eklenir. Giriş ve Çıkış alanları artımlı derlemeile ilgilidir. Tüm çıkış öğeleri up-to-date durumundaysa, MSBuild hedefi atlar. Bu hedefin artımlı yapısı, derlemelerinizin performansını önemli ölçüde artırabilir. Çıkış dosyası giriş dosyası veya dosyalarından aynı yaş veya daha yeniyse, öğe up-to-date olarak kabul edilir.AfterClean
: Amaç, genel temizleme işlemi tamamlandıktan sonra oluşturulan sınıf dosyasını silmektir. Bu hedefteki görevler, çekirdek temizleme işlevi çağrıldıktan sonra eklenir. Yeniden oluşturma hedefi yürütüldüğünde, kod oluşturma adımının yinelenmesine zorlar.
NuGet paketini oluşturma
NuGet paketini oluşturmak için Visual Studio'yu kullanabilirsiniz (Çözüm Gezgini'ndeproje düğümüne sağ tıklayın ve Packöğesini seçin). Bunu komut satırını kullanarak da yapabilirsiniz. Görev projesi dosyasının AppSettingStronglyTyped.csproj
bulunduğu klasöre gidin ve aşağıdaki komutu yürütür:
// -o is to define the output; the following command chooses the current folder.
dotnet pack -o .
Tebrikler! \AppSettingStronglyTyped\AppSettingStronglyTyped\AppSettingStronglyTyped.1.0.0.nupkgadlı bir NuGet paketi oluşturdunuz.
Paketin bir uzantı .nupkg
vardır ve sıkıştırılmış bir zip dosyasıdır. Bir zip aracıyla açabilirsiniz.
.target
ve .props
dosyaları build
klasöründedir.
.dll
dosyası lib\netstandard2.0\
klasöründedir.
AppSettingStronglyTyped.nuspec
dosyası kök düzeydedir.
(İsteğe bağlı) Çoklu hedeflemeyi destekleme
Mümkün olan en geniş kullanıcı tabanını desteklemek için hem Full
(.NET Framework) hem de Core
(.NET 5 ve üzeri dahil) MSBuild dağıtımlarını desteklemeyi düşünmelisiniz.
'Normal' .NET SDK projeleri için çoklu hedefleme, proje dosyanızda birden çok TargetFrameworks ayarlanması anlamına gelir. Bunu yaptığınızda, her iki "TargetFrameworkMonikers" için derlemeler tetiklenir ve sonuçların tamamı tek bir çıktı olarak paketlenebilir.
MSBuild için tam hikaye bu değildir. MSBuild'in iki birincil sevkiyat aracı vardır: Visual Studio ve .NET SDK. Bunlar çok farklı çalışma zamanı ortamlarıdır; biri .NET Framework çalışma zamanında, diğer çalıştırmalar ise CoreCLR'de çalışır. Bunun anlamı, kodunuz netstandard2.0'ı hedefleyebilirken, görev mantığınızda şu anda kullanılan MSBuild çalışma zamanı türüne bağlı olarak farklılıklar olabileceği anlamına gelir. Pratikte, .NET 5.0 ve sonrasında çok sayıda yeni API bulunduğu için, hem birden fazla TargetFrameworkMoniker'a yönelik MSBuild görev kaynak kodunuzu, hem de birden çok MSBuild çalışma zamanı türü için MSBuild hedef mantığınızı çoklu hedefleme yapacak şekilde tasarlamak mantıklıdır.
Çoklu hedef için gereken değişiklikler
Birden çok TargetFrameworkMoniker'i (TFM) hedeflemek için:
proje dosyanızı
net472
venet6.0
TFM'leri kullanacak şekilde değiştirin (ikincisi hedeflemek istediğiniz SDK düzeyine göre değişebilir). .NET Core 3.1 destekten çıkana kadarnetcoreapp3.1
hedeflemek isteyebilirsiniz. Bunu yaptığınızda, paket klasörü yapısıtasks/
olantasks/<TFM>/
olarak değişir.<TargetFrameworks>net472;net6.0</TargetFrameworks>
.targets
dosyalarınızı görevlerinizi yüklemek için doğru TFM'yi kullanacak şekilde güncelleştirin. Gerekli TFM, yukarıda seçtiğiniz .NET TFM'ye göre değişir, ancaknet472
venet6.0
hedefleyen bir proje için aşağıdaki gibi bir özelliğiniz olur:
<AppSettingStronglyTyped_TFM Condition=" '$(MSBuildRuntimeType)' != 'Core' ">net472</AppSettingStronglyTyped_TFM>
<AppSettingStronglyTyped_TFM Condition=" '$(MSBuildRuntimeType)' == 'Core' ">net6.0</AppSettingStronglyTyped_TFM>
Bu kod, etkin barındırma ortamı için ara sunucu olarak MSBuildRuntimeType
özelliğini kullanır. Bu özellik ayarlandıktan sonra, UsingTask
içinde doğru AssemblyFile
yüklemek için kullanabilirsiniz.
<UsingTask
AssemblyFile="$(MSBuildThisFileDirectory)../tasks/$(AppSettingStronglyTyped_TFM)/AppSettingStronglyTyped.dll"
TaskName="AppSettingStrongTyped.AppSettingStronglyTyped" />
Sonraki adımlar
Birçok görev yürütülebilir dosyayı çağırmayı içerir. Bazı senaryolarda, Exec görevini kullanabilirsiniz, ancak Exec görevinin sınırlamaları bir sorunsa, özel bir görev de oluşturabilirsiniz. Aşağıdaki öğretici, daha gerçekçi bir kod oluşturma senaryosuyla her iki seçenekte de yol gösterir: REST API için istemci kodu oluşturmak üzere özel bir görev oluşturma.
İsterseniz özel bir görevi test etmeyi de öğrenebilirsiniz.