Merge remote-tracking branch 'upstream/main' into project-cache-vnext

This commit is contained in:
David Federman 2023-08-14 13:35:02 -07:00
Родитель 9861d878bf 0cf89d3b27
Коммит 6222f76864
81 изменённых файлов: 3401 добавлений и 754 удалений

Просмотреть файл

@ -6,7 +6,6 @@
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
<add key="msbuild17.7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-DotNet-msbuild-Trusted-5785ed5c/nuget/v3/index.json" />
<add key="dotnet8" value="https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet8/nuget/v3/index.json" />
<add key="dotnet8-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json" />
<add key="BuildXL" value="https://pkgs.dev.azure.com/ms/BuildXL/_packaging/BuildXL/nuget/v3/index.json" />

Просмотреть файл

@ -68,6 +68,9 @@
<ItemGroup>
<InstalledVersionedExtensions Include="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\**\*.targets" />
<InstalledVersionedExtensions Include="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\**\*.props" />
<InstalledVersionedExtensions Include="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\**\Tracker*.dll" />
<InstalledVersionedExtensions Include="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\**\Tracker*.exe" />
<InstalledVersionedExtensions Include="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\**\FileTracker*.dll" />
<SdkResolverFiles Include="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.DotNet.MSBuildSdkResolver\**\*.*" />
<NuGetSdkResolverManifest Include= "$(RepoRoot)src\MSBuild\SdkResolvers\VS\Microsoft.Build.NuGetSdkResolver.xml" Condition="'$(MonoBuild)' != 'true'" />
<NuGetSdkResolverManifest Include= "$(RepoRoot)src\MSBuild\SdkResolvers\Standalone\Microsoft.Build.NuGetSdkResolver.xml" Condition="'$(MonoBuild)' == 'true'" />

Просмотреть файл

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<ProductDependencies>
<Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="8.0.0-alpha.1.23381.3">
<Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="8.0.0-alpha.1.23408.2">
<Uri>https://github.com/dotnet/source-build-reference-packages</Uri>
<Sha>5a1492557c8717b428b69fd4b7ca8c91d5d18cd3</Sha>
<Sha>41f1a158d460e11ded6cffd6340f9e671e2b0a5c</Sha>
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
</Dependency>
<!-- Necessary for source-build due to being a transitive dependency of System.Reflection.MetadataLoadContext.
@ -43,33 +43,28 @@
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="8.0.0-beta.23378.2">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="8.0.0-beta.23404.2">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>54dd37d44a2adfb8b966fac466c2ece40f8b20dd</Sha>
<Sha>1d39647dd408f7afd99cce01f26bba1d6bdeb248</Sha>
<SourceBuild RepoName="arcade" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.SourceLink.GitHub" Version="8.0.0-beta.23211.2" CoherentParentDependency="Microsoft.DotNet.Arcade.Sdk">
<Uri>https://github.com/dotnet/sourcelink</Uri>
<Sha>4cf2eb17c295905edeca76a9afe6dda42988359e</Sha>
<SourceBuild RepoName="sourcelink" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.DotNet.XliffTasks" Version="1.0.0-beta.23374.1" CoherentParentDependency="Microsoft.DotNet.Arcade.Sdk">
<Dependency Name="Microsoft.DotNet.XliffTasks" Version="1.0.0-beta.23381.1" CoherentParentDependency="Microsoft.DotNet.Arcade.Sdk">
<Uri>https://github.com/dotnet/xliff-tasks</Uri>
<Sha>a61cdec7a7f96c654b8c92bea0167df0427cc26c</Sha>
<Sha>d3553ca27fb1c128f302f52b73c0079e65d62ea8</Sha>
<SourceBuild RepoName="xliff-tasks" ManagedOnly="true" />
</Dependency>
<Dependency Name="NuGet.Build.Tasks" Version="6.8.0-preview.1.44">
<Dependency Name="NuGet.Build.Tasks" Version="6.8.0-preview.1.56">
<Uri>https://github.com/nuget/nuget.client</Uri>
<Sha>c7035e0564fc33c43bf3f17b612a052e0a01c95b</Sha>
<Sha>a39baac1e0fc3126a767b7261beb3804a28e4a97</Sha>
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.8.0-1.23378.8">
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.8.0-1.23406.1">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>f5b6c715a742c56b7cc672e47385508fb4df98cc</Sha>
<Sha>e3ede0e8fee242f6bf988f3c71a6ba5e8217faa3</Sha>
<SourceBuild RepoName="roslyn" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.DotNet.XUnitExtensions" Version="8.0.0-beta.23378.2">
<Dependency Name="Microsoft.DotNet.XUnitExtensions" Version="8.0.0-beta.23404.2">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>54dd37d44a2adfb8b966fac466c2ece40f8b20dd</Sha>
<Sha>1d39647dd408f7afd99cce01f26bba1d6bdeb248</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>

Просмотреть файл

@ -48,11 +48,11 @@
Otherwise, this version of dotnet will not be installed and the build will error out. -->
<DotNetCliVersion>$([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1))</DotNetCliVersion>
<MicrosoftCodeAnalysisCollectionsVersion>4.2.0-1.22102.8</MicrosoftCodeAnalysisCollectionsVersion>
<MicrosoftDotNetXUnitExtensionsVersion>8.0.0-beta.23378.2</MicrosoftDotNetXUnitExtensionsVersion>
<MicrosoftDotNetXUnitExtensionsVersion>8.0.0-beta.23404.2</MicrosoftDotNetXUnitExtensionsVersion>
<MicrosoftExtensionsDependencyModelVersion>7.0.0</MicrosoftExtensionsDependencyModelVersion>
<MicrosoftIORedistVersion>6.0.0</MicrosoftIORedistVersion>
<MicrosoftNetCompilersToolsetVersion>4.8.0-1.23378.8</MicrosoftNetCompilersToolsetVersion>
<NuGetBuildTasksVersion>6.8.0-preview.1.44</NuGetBuildTasksVersion>
<MicrosoftNetCompilersToolsetVersion>4.8.0-1.23406.1</MicrosoftNetCompilersToolsetVersion>
<NuGetBuildTasksVersion>6.8.0-preview.1.56</NuGetBuildTasksVersion>
<SystemRuntimeCompilerServicesUnsafeVersion>6.0.0</SystemRuntimeCompilerServicesUnsafeVersion>
<SystemTextJsonVersion>7.0.3</SystemTextJsonVersion>
<SystemThreadingTasksDataflowVersion>7.0.0</SystemThreadingTasksDataflowVersion>

Двоичные данные
eng/common/loc/P22DotNetHtmlLocalization.lss сгенерированный поставляемый

Двоичный файл не отображается.

20
eng/common/sdl/extract-artifact-packages.ps1 сгенерированный поставляемый
Просмотреть файл

@ -35,31 +35,33 @@ try {
param(
[string] $PackagePath # Full path to a NuGet package
)
if (!(Test-Path $PackagePath)) {
Write-PipelineTelemetryError -Category 'Build' -Message "Input file does not exist: $PackagePath"
ExitWithExitCode 1
}
$RelevantExtensions = @('.dll', '.exe', '.pdb')
Write-Host -NoNewLine 'Extracting ' ([System.IO.Path]::GetFileName($PackagePath)) '...'
$PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath)
$ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageId
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Directory]::CreateDirectory($ExtractPath);
try {
$zip = [System.IO.Compression.ZipFile]::OpenRead($PackagePath)
$zip.Entries |
Where-Object {$RelevantExtensions -contains [System.IO.Path]::GetExtension($_.Name)} |
ForEach-Object {
$TargetFile = Join-Path -Path $ExtractPath -ChildPath $_.Name
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile, $true)
$TargetPath = Join-Path -Path $ExtractPath -ChildPath (Split-Path -Path $_.FullName)
[System.IO.Directory]::CreateDirectory($TargetPath);
$TargetFile = Join-Path -Path $ExtractPath -ChildPath $_.FullName
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile)
}
}
catch {

9
eng/common/templates/steps/source-build.yml сгенерированный поставляемый
Просмотреть файл

@ -118,3 +118,12 @@ steps:
artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt)
continueOnError: true
condition: succeededOrFailed()
# Manually inject component detection so that we can ignore the source build upstream cache, which contains
# a nupkg cache of input packages (a local feed).
# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir'
# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets
- task: ComponentGovernanceComponentDetection@0
displayName: Component Detection (Exclude upstream cache)
inputs:
ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache'

Просмотреть файл

@ -13,12 +13,15 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.13.1" />
<PackageVersion Update="BenchmarkDotNet" Condition="'$(BenchmarkDotNetVersion)' != ''" Version="$(BenchmarkDotNetVersion)" />
<PackageVersion Include="Microsoft.BuildXL.Processes" Version="0.1.0-20230727.4.2" />
<PackageVersion Update="Microsoft.BuildXL.Processes" Condition="'$(BuildXLProcessesVersion)' != ''" Version="$(BuildXLProcessesVersion)" />
<PackageVersion Include="FluentAssertions" Version="6.11.0" />
<PackageVersion Update="FluentAssertions" Condition="'$(FluentAssertionsVersion)' != ''" Version="$(FluentAssertionsVersion)" />
<PackageVersion Include="LargeAddressAware" Version="1.0.5" />
<PackageVersion Update="LargeAddressAware" Condition="'$(LargeAddressAwareVersion)' != ''" Version="$(LargeAddressAwareVersion)" />
<PackageVersion Include="Microsoft.BuildXL.Processes" Version="0.1.0-20230727.4.2" />
<PackageVersion Update="Microsoft.BuildXL.Processes" Condition="'$(BuildXLProcessesVersion)' != ''" Version="$(BuildXLProcessesVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="3.2.2146" PrivateAssets="All" />
<PackageVersion Update="Microsoft.VisualStudio.Setup.Configuration.Interop" Condition="'$(MicrosoftVisualStudioSetupConfigurationInteropVersion)' != ''" Version="$(MicrosoftVisualStudioSetupConfigurationInteropVersion)" PrivateAssets="All" />

Просмотреть файл

@ -10,6 +10,6 @@
"xcopy-msbuild": "17.6.0-2"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23378.2"
"Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23404.2"
}
}

Просмотреть файл

@ -181,36 +181,6 @@ namespace Microsoft.Build.UnitTests.BackEnd
Assert.Equal(value, deserializedValue);
}
/// <summary>
/// Tests serializing using the DotNet serializer.
/// </summary>
[Fact]
public void TestSerializeDotNet()
{
ArgumentNullException value = new ArgumentNullException("The argument was null", new InsufficientMemoryException());
TranslationHelpers.GetWriteTranslator().TranslateDotNet(ref value);
ArgumentNullException deserializedValue = null;
TranslationHelpers.GetReadTranslator().TranslateDotNet(ref deserializedValue);
Assert.True(TranslationHelpers.CompareExceptions(value, deserializedValue, out string diffReason), diffReason);
}
/// <summary>
/// Tests serializing using the DotNet serializer passing in null.
/// </summary>
[Fact]
public void TestSerializeDotNetNull()
{
ArgumentNullException value = null;
TranslationHelpers.GetWriteTranslator().TranslateDotNet(ref value);
ArgumentNullException deserializedValue = null;
TranslationHelpers.GetReadTranslator().TranslateDotNet(ref deserializedValue);
Assert.True(TranslationHelpers.CompareExceptions(value, deserializedValue, out string diffReason), diffReason);
}
[Fact]
public void TestSerializeException()
{

Просмотреть файл

@ -0,0 +1,163 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
using Xunit.NetCore.Extensions;
using static Microsoft.Build.UnitTests.ObjectModelHelpers;
#nullable disable
namespace Microsoft.Build.Engine.UnitTests.BackEnd
{
public class BuildManager_Logging_Tests : IDisposable
{
private string _mainProject = @"
<Project>
<Target Name=`MainTarget`>
<MSBuild Projects=`{0}` Targets=`ChildTarget` />
</Target>
</Project>";
private string _childProjectWithCustomBuildEvent = $@"
<Project>
<UsingTask TaskName=""CustomBuildEventTask"" AssemblyFile=""{Assembly.GetExecutingAssembly().Location}"" />
<Target Name=`ChildTarget`>
<CustomBuildEventTask />
</Target>
</Project>";
/// <summary>
/// The mock logger for testing.
/// </summary>
private readonly MockLogger _logger;
/// <summary>
/// The standard build manager for each test.
/// </summary>
private readonly BuildManager _buildManager;
/// <summary>
/// The project collection used.
/// </summary>
private readonly ProjectCollection _projectCollection;
private readonly TestEnvironment _env;
private readonly ITestOutputHelper _output;
/// <summary>
/// SetUp
/// </summary>
public BuildManager_Logging_Tests(ITestOutputHelper output)
{
_output = output;
// Ensure that any previous tests which may have been using the default BuildManager do not conflict with us.
BuildManager.DefaultBuildManager.Dispose();
_logger = new MockLogger(output);
_buildManager = new BuildManager();
_projectCollection = new ProjectCollection();
_env = TestEnvironment.Create(output);
}
[DotNetOnlyTheory]
[InlineData("1", true)]
[InlineData("0", false)]
[InlineData(null, true)]
public void Build_WithCustomBuildArgs_NetCore(string envVariableValue, bool isWarningExpected)
=> TestCustomEventWarning<BuildErrorEventArgs>(envVariableValue, isWarningExpected);
[WindowsFullFrameworkOnlyTheory]
[InlineData("1", true)]
[InlineData("0", false)]
[InlineData(null, false)]
public void Build_WithCustomBuildArgs_Framework(string envVariableValue, bool isWarningExpected) =>
TestCustomEventWarning<BuildWarningEventArgs>(envVariableValue, isWarningExpected);
private void TestCustomEventWarning<T>(string envVariableValue, bool isWarningExpected) where T : LazyFormattedBuildEventArgs
{
var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] { "main", "child1" }, string.Empty);
ILoggingService service = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 1);
service.RegisterLogger(_logger);
_env.SetEnvironmentVariable("MSBUILDCUSTOMBUILDEVENTWARNING", envVariableValue);
_env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
_buildManager.BeginBuild(BuildParameters);
try
{
var child1ProjectPath = testFiles.CreatedFiles[1];
var cleanedUpChildContents = CleanupFileContents(_childProjectWithCustomBuildEvent);
File.WriteAllText(child1ProjectPath, cleanedUpChildContents);
var mainProjectPath = testFiles.CreatedFiles[0];
var cleanedUpMainContents = CleanupFileContents(string.Format(_mainProject, child1ProjectPath));
File.WriteAllText(mainProjectPath, cleanedUpMainContents);
var buildRequestData = new BuildRequestData(
mainProjectPath,
new Dictionary<string, string>(),
MSBuildConstants.CurrentToolsVersion,
new[] { "MainTarget" },
null);
var submission = _buildManager.PendBuildRequest(buildRequestData);
var result = submission.Execute();
var allEvents = _logger.AllBuildEvents;
if (isWarningExpected)
{
allEvents.OfType<T>().ShouldHaveSingleItem();
allEvents.First(x => x is T).Message.ShouldContain(
string.Format(ResourceUtilities.GetResourceString("DeprecatedEventSerialization"),
"MyCustomBuildEventArgs"));
}
else
{
allEvents.OfType<T>().ShouldBeEmpty();
}
}
finally
{
_buildManager.EndBuild();
}
}
private BuildParameters BuildParameters => new BuildParameters(_projectCollection)
{
DisableInProcNode = true,
EnableNodeReuse = false,
Loggers = new ILogger[] { _logger }
};
/// <summary>
/// TearDown
/// </summary>
public void Dispose()
{
_buildManager.Dispose();
_projectCollection.Dispose();
_env.Dispose();
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
#nullable disable
namespace Microsoft.Build.UnitTests
{
public class CustomBuildEventTask : Task
{
public override bool Execute()
{
MyCustomBuildEventArgs customBuildEvent = new() { RawMessage = "A message from MyCustomBuildEventArgs" };
BuildEngine.LogCustomEvent(customBuildEvent);
return true;
}
[Serializable]
public sealed class MyCustomBuildEventArgs : CustomBuildEventArgs { }
}
}

Просмотреть файл

@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
@ -57,8 +58,21 @@ namespace Microsoft.Build.UnitTests.BackEnd
ProjectStartedEventArgs projectStarted = new ProjectStartedEventArgs(-1, "message", "help", "ProjectFile", "targetNames", null, null, null);
ProjectFinishedEventArgs projectFinished = new ProjectFinishedEventArgs("message", "help", "ProjectFile", true);
ExternalProjectStartedEventArgs externalStartedEvent = new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames");
ExternalProjectFinishedEventArgs externalFinishedEvent = new("message", "help", "senderName", "projectFile", true);
ProjectEvaluationStartedEventArgs evaluationStarted = new ProjectEvaluationStartedEventArgs();
ProjectEvaluationFinishedEventArgs evaluationFinished = new ProjectEvaluationFinishedEventArgs();
AssemblyLoadBuildEventArgs assemblyLoad = new(AssemblyLoadingContext.Evaluation, null, null, "path", Guid.NewGuid(), null);
ExtendedBuildErrorEventArgs extError = new("extError", "SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender");
ExtendedBuildWarningEventArgs extWarning = new("extWarn", "SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender");
ExtendedBuildMessageEventArgs extMessage = new("extMsg", "SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender", MessageImportance.Normal);
ExtendedCustomBuildEventArgs extCustom = new("extCustom", "message", "help", "sender");
CriticalBuildMessageEventArgs criticalMessage = new("Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "Sender", DateTime.Now, "arg1");
PropertyInitialValueSetEventArgs propInit = new("prop", "val", "propsource", "message", "help", "sender", MessageImportance.Normal);
MetaprojectGeneratedEventArgs metaProjectGenerated = new("metaName", "path", "message");
PropertyReassignmentEventArgs propReassign = new("prop", "prevValue", "newValue", "loc", "message", "help", "sender", MessageImportance.Normal);
ResponseFileUsedEventArgs responseFileUsed = new("path");
UninitializedPropertyReadEventArgs uninitializedPropertyRead = new("prop", "message", "help", "sender", MessageImportance.Normal);
EnvironmentVariableReadEventArgs environmentVariableRead = new("env", "message", "help", "sender", MessageImportance.Normal);
VerifyLoggingPacket(buildFinished, LoggingEventType.BuildFinishedEvent);
VerifyLoggingPacket(buildStarted, LoggingEventType.BuildStartedEvent);
@ -76,7 +90,20 @@ namespace Microsoft.Build.UnitTests.BackEnd
VerifyLoggingPacket(projectFinished, LoggingEventType.ProjectFinishedEvent);
VerifyLoggingPacket(evaluationStarted, LoggingEventType.ProjectEvaluationStartedEvent);
VerifyLoggingPacket(evaluationFinished, LoggingEventType.ProjectEvaluationFinishedEvent);
VerifyLoggingPacket(externalStartedEvent, LoggingEventType.CustomEvent);
VerifyLoggingPacket(externalStartedEvent, LoggingEventType.ExternalProjectStartedEvent);
VerifyLoggingPacket(externalFinishedEvent, LoggingEventType.ExternalProjectFinishedEvent);
VerifyLoggingPacket(assemblyLoad, LoggingEventType.AssemblyLoadEvent);
VerifyLoggingPacket(extError, LoggingEventType.ExtendedBuildErrorEvent);
VerifyLoggingPacket(extWarning, LoggingEventType.ExtendedBuildWarningEvent);
VerifyLoggingPacket(extMessage, LoggingEventType.ExtendedBuildMessageEvent);
VerifyLoggingPacket(extCustom, LoggingEventType.ExtendedCustomEvent);
VerifyLoggingPacket(criticalMessage, LoggingEventType.CriticalBuildMessage);
VerifyLoggingPacket(propInit, LoggingEventType.PropertyInitialValueSet);
VerifyLoggingPacket(metaProjectGenerated, LoggingEventType.MetaprojectGenerated);
VerifyLoggingPacket(propReassign, LoggingEventType.PropertyReassignment);
VerifyLoggingPacket(responseFileUsed, LoggingEventType.ResponseFileUsedEvent);
VerifyLoggingPacket(uninitializedPropertyRead, LoggingEventType.UninitializedPropertyRead);
VerifyLoggingPacket(environmentVariableRead, LoggingEventType.EnvironmentVariableReadEvent);
}
private static BuildEventContext CreateBuildEventContext()
@ -218,6 +245,13 @@ namespace Microsoft.Build.UnitTests.BackEnd
{
BuildEventArgs[] testArgs = new BuildEventArgs[]
{
new ResponseFileUsedEventArgs("path"),
new UninitializedPropertyReadEventArgs("prop", "message", "help", "sender", MessageImportance.Normal),
new EnvironmentVariableReadEventArgs("env", "message", "help", "sender", MessageImportance.Normal) { BuildEventContext = new BuildEventContext(1, 2, 3, 4, 5, 6) },
new PropertyReassignmentEventArgs("prop", "prevValue", "newValue", "loc", "message", "help", "sender", MessageImportance.Normal),
new PropertyInitialValueSetEventArgs("prop", "val", "propsource", "message", "help", "sender", MessageImportance.Normal),
new MetaprojectGeneratedEventArgs("metaName", "path", "message"),
new CriticalBuildMessageEventArgs("Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "Sender", DateTime.Now, "arg1"),
new BuildFinishedEventArgs("Message", "Keyword", true),
new BuildStartedEventArgs("Message", "Help"),
new BuildMessageEventArgs("Message", "help", "sender", MessageImportance.Low),
@ -236,11 +270,36 @@ namespace Microsoft.Build.UnitTests.BackEnd
new ProjectStartedEventArgs(-1, "message", "help", "ProjectFile", "targetNames", null, null, null),
new ProjectFinishedEventArgs("message", "help", "ProjectFile", true),
new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames"),
new ExternalProjectFinishedEventArgs("message", "help", "senderName", "projectFile", true),
CreateProjectEvaluationStarted(),
CreateProjectEvaluationFinished(),
CreateTargetSkipped()
new AssemblyLoadBuildEventArgs(AssemblyLoadingContext.Evaluation, "init", "aname", "path", Guid.NewGuid(), "domain", MessageImportance.Normal),
CreateTargetSkipped(),
new ExtendedBuildErrorEventArgs("extError", "SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender", DateTime.UtcNow, "arg1")
{
ExtendedData = "{'long-json':'mostly-strings'}",
ExtendedMetadata = new Dictionary<string, string> { { "m1", "v1" }, { "m2", "v2" } },
BuildEventContext = new BuildEventContext(1, 2, 3, 4, 5, 6, 7)
},
new ExtendedBuildWarningEventArgs("extWarn", "SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender", DateTime.UtcNow, "arg1")
{
ExtendedData = "{'long-json':'mostly-strings'}",
ExtendedMetadata = new Dictionary<string, string> { { "m1", "v1" }, { "m2", "v2" } },
BuildEventContext = new BuildEventContext(1, 2, 3, 4, 5, 6, 7)
},
new ExtendedBuildMessageEventArgs("extWarn", "SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender", MessageImportance.Normal, DateTime.UtcNow, "arg1")
{
ExtendedData = "{'long-json':'mostly-strings'}",
ExtendedMetadata = new Dictionary<string, string> { { "m1", "v1" }, { "m2", "v2" } },
BuildEventContext = new BuildEventContext(1, 2, 3, 4, 5, 6, 7)
},
new ExtendedCustomBuildEventArgs("extCustom", "message", "help", "sender", DateTime.UtcNow, "arg1")
{
ExtendedData = "{'long-json':'mostly-strings'}",
ExtendedMetadata = new Dictionary<string, string> { { "m1", "v1" }, { "m2", "v2" } },
BuildEventContext = new BuildEventContext(1, 2, 3, 4, 5, 6, 7)
},
};
foreach (BuildEventArgs arg in testArgs)
{
LogMessagePacket packet = new LogMessagePacket(new KeyValuePair<int, BuildEventArgs>(0, arg));
@ -250,7 +309,17 @@ namespace Microsoft.Build.UnitTests.BackEnd
LogMessagePacket deserializedPacket = tempPacket as LogMessagePacket;
CompareLogMessagePackets(packet, deserializedPacket);
packet.Should().BeEquivalentTo(deserializedPacket, options => options
.RespectingRuntimeTypes());
BuildEventArgs args = packet.NodeBuildEvent?.Value;
BuildEventArgs desArgs = deserializedPacket?.NodeBuildEvent?.Value;
desArgs.Should().BeEquivalentTo(args, options => options
.RespectingRuntimeTypes()
// Since we use struct DictionaryEntry of class TaskItemData, generated DictionaryEntry.Equals compare TaskItemData by references.
// Bellow will instruct equivalency test to not use DictionaryEntry.Equals but its public members for equivalency tests.
.ComparingByMembers<DictionaryEntry>()
.WithTracing(), "Roundtrip deserialization of message type {0} should be equivalent", args.GetType().Name);
}
}
finally
@ -273,249 +342,6 @@ namespace Microsoft.Build.UnitTests.BackEnd
Assert.True(Object.ReferenceEquals(buildEvent, packet.NodeBuildEvent.Value.Value)); // "Expected buildEvent to have the same object reference as packet.BuildEvent"
}
/// <summary>
/// Compares two BuildEventArgs objects for equivalence.
/// </summary>
private void CompareNodeBuildEventArgs(KeyValuePair<int, BuildEventArgs> leftTuple, KeyValuePair<int, BuildEventArgs> rightTuple, bool expectInvalidBuildEventContext)
{
BuildEventArgs left = leftTuple.Value;
BuildEventArgs right = rightTuple.Value;
if (expectInvalidBuildEventContext)
{
Assert.Equal(BuildEventContext.Invalid, right.BuildEventContext);
}
else
{
Assert.Equal(left.BuildEventContext, right.BuildEventContext);
}
Assert.Equal(leftTuple.Key, rightTuple.Key);
Assert.Equal(left.HelpKeyword, right.HelpKeyword);
Assert.Equal(left.Message, right.Message);
Assert.Equal(left.SenderName, right.SenderName);
Assert.Equal(left.ThreadId, right.ThreadId);
Assert.Equal(left.Timestamp, right.Timestamp);
}
/// <summary>
/// Compares two LogMessagePacket objects for equivalence.
/// </summary>
private void CompareLogMessagePackets(LogMessagePacket left, LogMessagePacket right)
{
Assert.Equal(left.EventType, right.EventType);
Assert.Equal(left.NodeBuildEvent.Value.Value.GetType(), right.NodeBuildEvent.Value.Value.GetType());
CompareNodeBuildEventArgs(left.NodeBuildEvent.Value, right.NodeBuildEvent.Value, left.EventType == LoggingEventType.CustomEvent /* expectInvalidBuildEventContext */);
switch (left.EventType)
{
case LoggingEventType.BuildErrorEvent:
BuildErrorEventArgs leftError = left.NodeBuildEvent.Value.Value as BuildErrorEventArgs;
BuildErrorEventArgs rightError = right.NodeBuildEvent.Value.Value as BuildErrorEventArgs;
Assert.NotNull(leftError);
Assert.NotNull(rightError);
Assert.Equal(leftError.Code, rightError.Code);
Assert.Equal(leftError.ColumnNumber, rightError.ColumnNumber);
Assert.Equal(leftError.EndColumnNumber, rightError.EndColumnNumber);
Assert.Equal(leftError.EndLineNumber, rightError.EndLineNumber);
Assert.Equal(leftError.File, rightError.File);
Assert.Equal(leftError.LineNumber, rightError.LineNumber);
Assert.Equal(leftError.Message, rightError.Message);
Assert.Equal(leftError.Subcategory, rightError.Subcategory);
break;
case LoggingEventType.BuildFinishedEvent:
BuildFinishedEventArgs leftFinished = left.NodeBuildEvent.Value.Value as BuildFinishedEventArgs;
BuildFinishedEventArgs rightFinished = right.NodeBuildEvent.Value.Value as BuildFinishedEventArgs;
Assert.NotNull(leftFinished);
Assert.NotNull(rightFinished);
Assert.Equal(leftFinished.Succeeded, rightFinished.Succeeded);
break;
case LoggingEventType.BuildMessageEvent:
BuildMessageEventArgs leftMessage = left.NodeBuildEvent.Value.Value as BuildMessageEventArgs;
BuildMessageEventArgs rightMessage = right.NodeBuildEvent.Value.Value as BuildMessageEventArgs;
Assert.NotNull(leftMessage);
Assert.NotNull(rightMessage);
Assert.Equal(leftMessage.Importance, rightMessage.Importance);
break;
case LoggingEventType.BuildStartedEvent:
BuildStartedEventArgs leftBuildStart = left.NodeBuildEvent.Value.Value as BuildStartedEventArgs;
BuildStartedEventArgs rightBuildStart = right.NodeBuildEvent.Value.Value as BuildStartedEventArgs;
Assert.NotNull(leftBuildStart);
Assert.NotNull(rightBuildStart);
break;
case LoggingEventType.BuildWarningEvent:
BuildWarningEventArgs leftBuildWarn = left.NodeBuildEvent.Value.Value as BuildWarningEventArgs;
BuildWarningEventArgs rightBuildWarn = right.NodeBuildEvent.Value.Value as BuildWarningEventArgs;
Assert.NotNull(leftBuildWarn);
Assert.NotNull(rightBuildWarn);
Assert.Equal(leftBuildWarn.Code, rightBuildWarn.Code);
Assert.Equal(leftBuildWarn.ColumnNumber, rightBuildWarn.ColumnNumber);
Assert.Equal(leftBuildWarn.EndColumnNumber, rightBuildWarn.EndColumnNumber);
Assert.Equal(leftBuildWarn.EndLineNumber, rightBuildWarn.EndLineNumber);
Assert.Equal(leftBuildWarn.File, rightBuildWarn.File);
Assert.Equal(leftBuildWarn.LineNumber, rightBuildWarn.LineNumber);
Assert.Equal(leftBuildWarn.Subcategory, rightBuildWarn.Subcategory);
break;
case LoggingEventType.CustomEvent:
ExternalProjectStartedEventArgs leftCustom = left.NodeBuildEvent.Value.Value as ExternalProjectStartedEventArgs;
ExternalProjectStartedEventArgs rightCustom = right.NodeBuildEvent.Value.Value as ExternalProjectStartedEventArgs;
Assert.NotNull(leftCustom);
Assert.NotNull(rightCustom);
Assert.Equal(leftCustom.ProjectFile, rightCustom.ProjectFile);
Assert.Equal(leftCustom.TargetNames, rightCustom.TargetNames);
break;
case LoggingEventType.ProjectFinishedEvent:
ProjectFinishedEventArgs leftProjectFinished = left.NodeBuildEvent.Value.Value as ProjectFinishedEventArgs;
ProjectFinishedEventArgs rightProjectFinished = right.NodeBuildEvent.Value.Value as ProjectFinishedEventArgs;
Assert.NotNull(leftProjectFinished);
Assert.NotNull(rightProjectFinished);
Assert.Equal(leftProjectFinished.ProjectFile, rightProjectFinished.ProjectFile);
Assert.Equal(leftProjectFinished.Succeeded, rightProjectFinished.Succeeded);
break;
case LoggingEventType.ProjectStartedEvent:
ProjectStartedEventArgs leftProjectStarted = left.NodeBuildEvent.Value.Value as ProjectStartedEventArgs;
ProjectStartedEventArgs rightProjectStarted = right.NodeBuildEvent.Value.Value as ProjectStartedEventArgs;
Assert.NotNull(leftProjectStarted);
Assert.NotNull(rightProjectStarted);
Assert.Equal(leftProjectStarted.ParentProjectBuildEventContext, rightProjectStarted.ParentProjectBuildEventContext);
Assert.Equal(leftProjectStarted.ProjectFile, rightProjectStarted.ProjectFile);
Assert.Equal(leftProjectStarted.ProjectId, rightProjectStarted.ProjectId);
Assert.Equal(leftProjectStarted.TargetNames, rightProjectStarted.TargetNames);
// UNDONE: (Serialization.) We don't actually serialize the items at this time.
// Assert.AreEqual(leftProjectStarted.Items, rightProjectStarted.Items);
// UNDONE: (Serialization.) We don't actually serialize properties at this time.
// Assert.AreEqual(leftProjectStarted.Properties, rightProjectStarted.Properties);
break;
case LoggingEventType.ProjectEvaluationStartedEvent:
ProjectEvaluationStartedEventArgs leftEvaluationStarted = left.NodeBuildEvent.Value.Value as ProjectEvaluationStartedEventArgs;
ProjectEvaluationStartedEventArgs rightEvaluationStarted = right.NodeBuildEvent.Value.Value as ProjectEvaluationStartedEventArgs;
Assert.NotNull(leftEvaluationStarted);
Assert.NotNull(rightEvaluationStarted);
Assert.Equal(leftEvaluationStarted.ProjectFile, rightEvaluationStarted.ProjectFile);
break;
case LoggingEventType.ProjectEvaluationFinishedEvent:
ProjectEvaluationFinishedEventArgs leftEvaluationFinished = left.NodeBuildEvent.Value.Value as ProjectEvaluationFinishedEventArgs;
ProjectEvaluationFinishedEventArgs rightEvaluationFinished = right.NodeBuildEvent.Value.Value as ProjectEvaluationFinishedEventArgs;
Assert.NotNull(leftEvaluationFinished);
Assert.NotNull(rightEvaluationFinished);
Assert.Equal(leftEvaluationFinished.ProjectFile, rightEvaluationFinished.ProjectFile);
Assert.Equal(leftEvaluationFinished.ProfilerResult, rightEvaluationFinished.ProfilerResult);
Assert.Equal(
TranslationHelpers.GetPropertiesString(leftEvaluationFinished.GlobalProperties),
TranslationHelpers.GetPropertiesString(rightEvaluationFinished.GlobalProperties));
Assert.Equal(
TranslationHelpers.GetPropertiesString(leftEvaluationFinished.Properties),
TranslationHelpers.GetPropertiesString(rightEvaluationFinished.Properties));
Assert.Equal(
TranslationHelpers.GetMultiItemsString(leftEvaluationFinished.Items),
TranslationHelpers.GetMultiItemsString(rightEvaluationFinished.Items));
break;
case LoggingEventType.TargetFinishedEvent:
TargetFinishedEventArgs leftTargetFinished = left.NodeBuildEvent.Value.Value as TargetFinishedEventArgs;
TargetFinishedEventArgs rightTargetFinished = right.NodeBuildEvent.Value.Value as TargetFinishedEventArgs;
Assert.NotNull(leftTargetFinished);
Assert.NotNull(rightTargetFinished);
Assert.Equal(leftTargetFinished.ProjectFile, rightTargetFinished.ProjectFile);
Assert.Equal(leftTargetFinished.Succeeded, rightTargetFinished.Succeeded);
Assert.Equal(leftTargetFinished.TargetFile, rightTargetFinished.TargetFile);
Assert.Equal(leftTargetFinished.TargetName, rightTargetFinished.TargetName);
// TODO: target output translation is a special case and is done in TranslateTargetFinishedEvent
// Assert.Equal(leftTargetFinished.TargetOutputs, rightTargetFinished.TargetOutputs);
break;
case LoggingEventType.TargetStartedEvent:
TargetStartedEventArgs leftTargetStarted = left.NodeBuildEvent.Value.Value as TargetStartedEventArgs;
TargetStartedEventArgs rightTargetStarted = right.NodeBuildEvent.Value.Value as TargetStartedEventArgs;
Assert.NotNull(leftTargetStarted);
Assert.NotNull(rightTargetStarted);
Assert.Equal(leftTargetStarted.ProjectFile, rightTargetStarted.ProjectFile);
Assert.Equal(leftTargetStarted.TargetFile, rightTargetStarted.TargetFile);
Assert.Equal(leftTargetStarted.TargetName, rightTargetStarted.TargetName);
break;
case LoggingEventType.TargetSkipped:
TargetSkippedEventArgs leftTargetSkipped = left.NodeBuildEvent.Value.Value as TargetSkippedEventArgs;
TargetSkippedEventArgs rightTargetSkipped = right.NodeBuildEvent.Value.Value as TargetSkippedEventArgs;
Assert.Equal(leftTargetSkipped.BuildReason, rightTargetSkipped.BuildReason);
Assert.Equal(leftTargetSkipped.SkipReason, rightTargetSkipped.SkipReason);
Assert.Equal(leftTargetSkipped.BuildEventContext, rightTargetSkipped.BuildEventContext);
Assert.Equal(leftTargetSkipped.OriginalBuildEventContext, rightTargetSkipped.OriginalBuildEventContext);
Assert.Equal(leftTargetSkipped.Condition, rightTargetSkipped.Condition);
Assert.Equal(leftTargetSkipped.EvaluatedCondition, rightTargetSkipped.EvaluatedCondition);
Assert.Equal(leftTargetSkipped.Importance, rightTargetSkipped.Importance);
Assert.Equal(leftTargetSkipped.OriginallySucceeded, rightTargetSkipped.OriginallySucceeded);
Assert.Equal(leftTargetSkipped.ProjectFile, rightTargetSkipped.ProjectFile);
Assert.Equal(leftTargetSkipped.TargetFile, rightTargetSkipped.TargetFile);
Assert.Equal(leftTargetSkipped.TargetName, rightTargetSkipped.TargetName);
Assert.Equal(leftTargetSkipped.ParentTarget, rightTargetSkipped.ParentTarget);
break;
case LoggingEventType.TaskCommandLineEvent:
TaskCommandLineEventArgs leftCommand = left.NodeBuildEvent.Value.Value as TaskCommandLineEventArgs;
TaskCommandLineEventArgs rightCommand = right.NodeBuildEvent.Value.Value as TaskCommandLineEventArgs;
Assert.NotNull(leftCommand);
Assert.NotNull(rightCommand);
Assert.Equal(leftCommand.CommandLine, rightCommand.CommandLine);
Assert.Equal(leftCommand.Importance, rightCommand.Importance);
Assert.Equal(leftCommand.TaskName, rightCommand.TaskName);
break;
case LoggingEventType.TaskParameterEvent:
var leftTaskParameter = left.NodeBuildEvent.Value.Value as TaskParameterEventArgs;
var rightTaskParameter = right.NodeBuildEvent.Value.Value as TaskParameterEventArgs;
Assert.NotNull(leftTaskParameter);
Assert.NotNull(rightTaskParameter);
Assert.Equal(leftTaskParameter.Kind, rightTaskParameter.Kind);
Assert.Equal(leftTaskParameter.ItemType, rightTaskParameter.ItemType);
Assert.Equal(leftTaskParameter.Items.Count, rightTaskParameter.Items.Count);
Assert.Equal(leftTaskParameter.Message, rightTaskParameter.Message);
Assert.Equal(leftTaskParameter.BuildEventContext, rightTaskParameter.BuildEventContext);
Assert.Equal(leftTaskParameter.Timestamp, rightTaskParameter.Timestamp);
Assert.Equal(leftTaskParameter.LineNumber, rightTaskParameter.LineNumber);
Assert.Equal(leftTaskParameter.ColumnNumber, rightTaskParameter.ColumnNumber);
break;
case LoggingEventType.TaskFinishedEvent:
TaskFinishedEventArgs leftTaskFinished = left.NodeBuildEvent.Value.Value as TaskFinishedEventArgs;
TaskFinishedEventArgs rightTaskFinished = right.NodeBuildEvent.Value.Value as TaskFinishedEventArgs;
Assert.NotNull(leftTaskFinished);
Assert.NotNull(rightTaskFinished);
Assert.Equal(leftTaskFinished.ProjectFile, rightTaskFinished.ProjectFile);
Assert.Equal(leftTaskFinished.Succeeded, rightTaskFinished.Succeeded);
Assert.Equal(leftTaskFinished.TaskFile, rightTaskFinished.TaskFile);
Assert.Equal(leftTaskFinished.TaskName, rightTaskFinished.TaskName);
break;
case LoggingEventType.TaskStartedEvent:
TaskStartedEventArgs leftTaskStarted = left.NodeBuildEvent.Value.Value as TaskStartedEventArgs;
TaskStartedEventArgs rightTaskStarted = right.NodeBuildEvent.Value.Value as TaskStartedEventArgs;
Assert.NotNull(leftTaskStarted);
Assert.NotNull(rightTaskStarted);
Assert.Equal(leftTaskStarted.ProjectFile, rightTaskStarted.ProjectFile);
Assert.Equal(leftTaskStarted.TaskFile, rightTaskStarted.TaskFile);
Assert.Equal(leftTaskStarted.TaskName, rightTaskStarted.TaskName);
Assert.Equal(leftTaskStarted.LineNumber, rightTaskStarted.LineNumber);
Assert.Equal(leftTaskStarted.ColumnNumber, rightTaskStarted.ColumnNumber);
break;
default:
Assert.True(false, string.Format("Unexpected logging event type {0}", left.EventType));
break;
}
}
#endregion
}
}

Просмотреть файл

@ -17,53 +17,224 @@ namespace Microsoft.Build.UnitTests.BackEnd
/// </summary>
public class TaskBuilderTestTask : IGeneratedTask
{
/// <summary>
/// A custom <see cref="IConvertible"/> value type.
/// </summary>
/// <remarks>
/// Types like this one can be used only as Output parameter types because they can be converted to string
/// but not from string.
/// </remarks>
[Serializable]
public struct CustomStruct : IConvertible
{
private readonly object _value;
/// <summary>
/// Using <see cref="IConvertible"/> as the type of the <see cref="_value"/> field triggers a BinaryFormatter bug.
/// </summary>
private IConvertible Value => (IConvertible)_value;
public CustomStruct(IConvertible value)
{
_value = value;
}
public TypeCode GetTypeCode() => Value.GetTypeCode();
public bool ToBoolean(IFormatProvider provider) => Value.ToBoolean(provider);
public byte ToByte(IFormatProvider provider) => Value.ToByte(provider);
public char ToChar(IFormatProvider provider) => Value.ToChar(provider);
public DateTime ToDateTime(IFormatProvider provider) => Value.ToDateTime(provider);
public decimal ToDecimal(IFormatProvider provider) => Value.ToDecimal(provider);
public double ToDouble(IFormatProvider provider) => Value.ToDouble(provider);
public short ToInt16(IFormatProvider provider) => Value.ToInt16(provider);
public int ToInt32(IFormatProvider provider) => Value.ToInt32(provider);
public long ToInt64(IFormatProvider provider) => Value.ToInt64(provider);
public sbyte ToSByte(IFormatProvider provider) => Value.ToSByte(provider);
public float ToSingle(IFormatProvider provider) => Value.ToSingle(provider);
public string ToString(IFormatProvider provider) => Value.ToString(provider);
public object ToType(Type conversionType, IFormatProvider provider) => Value.ToType(conversionType, provider);
public ushort ToUInt16(IFormatProvider provider) => Value.ToUInt16(provider);
public uint ToUInt32(IFormatProvider provider) => Value.ToUInt32(provider);
public ulong ToUInt64(IFormatProvider provider) => Value.ToUInt64(provider);
}
/// <summary>
/// The <see cref="CustomStruct"/> value returned from <see cref="CustomStructOutput"/>.
/// </summary>
internal static readonly CustomStruct s_customStruct = new CustomStruct(42);
/// <summary>
/// The <see cref="CustomStruct[]"/> value returned from <see cref="CustomStructArrayOutput"/>.
/// </summary>
internal static readonly CustomStruct[] s_customStructArray = new CustomStruct[] { new CustomStruct(43), new CustomStruct(44) };
/// <summary>
/// The task host.
/// </summary>
private ITestTaskHost _testTaskHost;
/// <summary>
/// The value to return from Execute
/// The value to return from Execute.
/// </summary>
private bool _executeReturnValue;
/// <summary>
/// The value for the BoolOutput
/// The value for the BoolOutput.
/// </summary>
private bool _boolOutput;
/// <summary>
/// The value for the BoolArrayOutput
/// The value for the BoolArrayOutput.
/// </summary>
private bool[] _boolArrayOutput;
/// <summary>
/// The value for the IntOutput
/// The value for the ByteOutput.
/// </summary>
private byte _byteOutput;
/// <summary>
/// The value for the ByteArrayOutput.
/// </summary>
private byte[] _byteArrayOutput;
/// <summary>
/// The value for the SByteOutput.
/// </summary>
private sbyte _sbyteOutput;
/// <summary>
/// The value for the SByteArrayOutput.
/// </summary>
private sbyte[] _sbyteArrayOutput;
/// <summary>
/// The value for the DoubleOutput.
/// </summary>
private double _doubleOutput;
/// <summary>
/// The value for the DoubleArrayOutput.
/// </summary>
private double[] _doubleArrayOutput;
/// <summary>
/// The value for the FloatOutput.
/// </summary>
private float _floatOutput;
/// <summary>
/// The value for the FloatArrayOutput.
/// </summary>
private float[] _floatArrayOutput;
/// <summary>
/// The value for the ShortOutput.
/// </summary>
private short _shortOutput;
/// <summary>
/// The value for the ShortArrayOutput.
/// </summary>
private short[] _shortArrayOutput;
/// <summary>
/// The value for the UShortOutput.
/// </summary>
private ushort _ushortOutput;
/// <summary>
/// The value for the UShortArrayOutput.
/// </summary>
private ushort[] _ushortArrayOutput;
/// <summary>
/// The value for the IntOutput.
/// </summary>
private int _intOutput;
/// <summary>
/// The value for the IntArrayOutput
/// The value for the IntArrayOutput.
/// </summary>
private int[] _intArrayOutput;
/// <summary>
/// The value for the StringOutput
/// The value for the UIntOutput.
/// </summary>
private uint _uintOutput;
/// <summary>
/// The value for the UIntArrayOutput.
/// </summary>
private uint[] _uintArrayOutput;
/// <summary>
/// The value for the LongOutput.
/// </summary>
private long _longOutput;
/// <summary>
/// The value for the LongArrayOutput.
/// </summary>
private long[] _longArrayOutput;
/// <summary>
/// The value for the ULongOutput.
/// </summary>
private ulong _ulongOutput;
/// <summary>
/// The value for the ULongArrayOutput.
/// </summary>
private ulong[] _ulongArrayOutput;
/// <summary>
/// The value for the DecimalOutput.
/// </summary>
private decimal _decimalOutput;
/// <summary>
/// The value for the DecimalArrayOutput.
/// </summary>
private decimal[] _decimalArrayOutput;
/// <summary>
/// The value for the CharOutput.
/// </summary>
private char _charOutput;
/// <summary>
/// The value for the CharArrayOutput.
/// </summary>
private char[] _charArrayOutput;
/// <summary>
/// The value for the StringOutput.
/// </summary>
private string _stringOutput;
/// <summary>
/// The value for the StringArrayOutput
/// The value for the StringArrayOutput.
/// </summary>
private string[] _stringArrayOutput;
/// <summary>
/// The value for the ItemOutput
/// The value for the DateTimeOutput.
/// </summary>
private DateTime _dateTimeOutput;
/// <summary>
/// The value for the DateTimeArrayOutput.
/// </summary>
private DateTime[] _dateTimeArrayOutput;
/// <summary>
/// The value for the ItemOutput.
/// </summary>
private ITaskItem _itemOutput;
/// <summary>
/// The value for the ItemArrayOutput
/// The value for the ItemArrayOutput.
/// </summary>
private ITaskItem[] _itemArrayOutput;
@ -77,7 +248,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A boolean parameter
/// A boolean parameter.
/// </summary>
public bool BoolParam
{
@ -89,7 +260,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A boolean array parameter
/// A boolean array parameter.
/// </summary>
public bool[] BoolArrayParam
{
@ -101,7 +272,151 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// An integer parameter
/// A byte parameter.
/// </summary>
public byte ByteParam
{
set
{
_byteOutput = value;
_testTaskHost?.ParameterSet("ByteParam", value);
}
}
/// <summary>
/// A byte array parameter.
/// </summary>
public byte[] ByteArrayParam
{
set
{
_byteArrayOutput = value;
_testTaskHost?.ParameterSet("ByteArrayParam", value);
}
}
/// <summary>
/// An sbyte parameter.
/// </summary>
public sbyte SByteParam
{
set
{
_sbyteOutput = value;
_testTaskHost?.ParameterSet("SByteParam", value);
}
}
/// <summary>
/// An sbyte array parameter.
/// </summary>
public sbyte[] SByteArrayParam
{
set
{
_sbyteArrayOutput = value;
_testTaskHost?.ParameterSet("SByteArrayParam", value);
}
}
/// <summary>
/// A double parameter.
/// </summary>
public double DoubleParam
{
set
{
_doubleOutput = value;
_testTaskHost?.ParameterSet("DoubleParam", value);
}
}
/// <summary>
/// A double array parameter.
/// </summary>
public double[] DoubleArrayParam
{
set
{
_doubleArrayOutput = value;
_testTaskHost?.ParameterSet("DoubleArrayParam", value);
}
}
/// <summary>
/// A float parameter.
/// </summary>
public float FloatParam
{
set
{
_floatOutput = value;
_testTaskHost?.ParameterSet("FloatParam", value);
}
}
/// <summary>
/// A float array parameter.
/// </summary>
public float[] FloatArrayParam
{
set
{
_floatArrayOutput = value;
_testTaskHost?.ParameterSet("FloatArrayParam", value);
}
}
/// <summary>
/// A short parameter.
/// </summary>
public short ShortParam
{
set
{
_shortOutput = value;
_testTaskHost?.ParameterSet("ShortParam", value);
}
}
/// <summary>
/// A short array parameter.
/// </summary>
public short[] ShortArrayParam
{
set
{
_shortArrayOutput = value;
_testTaskHost?.ParameterSet("ShortArrayParam", value);
}
}
/// <summary>
/// A ushort parameter.
/// </summary>
public ushort UShortParam
{
set
{
_ushortOutput = value;
_testTaskHost?.ParameterSet("UShortParam", value);
}
}
/// <summary>
/// A ushort array parameter.
/// </summary>
public ushort[] UShortArrayParam
{
set
{
_ushortArrayOutput = value;
_testTaskHost?.ParameterSet("UShortArrayParam", value);
}
}
/// <summary>
/// An integer parameter.
/// </summary>
public int IntParam
{
@ -124,6 +439,126 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
}
/// <summary>
/// A uint parameter.
/// </summary>
public uint UIntParam
{
set
{
_uintOutput = value;
_testTaskHost?.ParameterSet("UIntParam", value);
}
}
/// <summary>
/// A uint array parameter.
/// </summary>
public uint[] UIntArrayParam
{
set
{
_uintArrayOutput = value;
_testTaskHost?.ParameterSet("UIntArrayParam", value);
}
}
/// <summary>
/// A long parameter.
/// </summary>
public long LongParam
{
set
{
_longOutput = value;
_testTaskHost?.ParameterSet("LongParam", value);
}
}
/// <summary>
/// A long array parameter.
/// </summary>
public long[] LongArrayParam
{
set
{
_longArrayOutput = value;
_testTaskHost?.ParameterSet("LongArrayParam", value);
}
}
/// <summary>
/// A ulong parameter.
/// </summary>
public ulong ULongParam
{
set
{
_ulongOutput = value;
_testTaskHost?.ParameterSet("ULongParam", value);
}
}
/// <summary>
/// A ulong array parameter.
/// </summary>
public ulong[] ULongArrayParam
{
set
{
_ulongArrayOutput = value;
_testTaskHost?.ParameterSet("ULongArrayParam", value);
}
}
/// <summary>
/// A decimal parameter.
/// </summary>
public decimal DecimalParam
{
set
{
_decimalOutput = value;
_testTaskHost?.ParameterSet("DecimalParam", value);
}
}
/// <summary>
/// A decimal array parameter.
/// </summary>
public decimal[] DecimalArrayParam
{
set
{
_decimalArrayOutput = value;
_testTaskHost?.ParameterSet("DecimalArrayParam", value);
}
}
/// <summary>
/// A char parameter.
/// </summary>
public char CharParam
{
set
{
_charOutput = value;
_testTaskHost?.ParameterSet("CharParam", value);
}
}
/// <summary>
/// A char array parameter.
/// </summary>
public char[] CharArrayParam
{
set
{
_charArrayOutput = value;
_testTaskHost?.ParameterSet("CharArrayParam", value);
}
}
/// <summary>
/// A string parameter.
/// </summary>
@ -148,6 +583,30 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
}
/// <summary>
/// A DateTime parameter.
/// </summary>
public DateTime DateTimeParam
{
set
{
_dateTimeOutput = value;
_testTaskHost?.ParameterSet("DateTimeParam", value);
}
}
/// <summary>
/// A DateTime array parameter.
/// </summary>
public DateTime[] DateTimeArrayParam
{
set
{
_dateTimeArrayOutput = value;
_testTaskHost?.ParameterSet("DateTimeArrayParam", value);
}
}
/// <summary>
/// An item parameter.
/// </summary>
@ -199,7 +658,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A boolean array output
/// A boolean array output.
/// </summary>
[Output]
public bool[] BoolArrayOutput
@ -212,7 +671,163 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// An integer output
/// A byte output.
/// </summary>
[Output]
public byte ByteOutput
{
get
{
_testTaskHost?.OutputRead("ByteOutput", _byteOutput);
return _byteOutput;
}
}
/// <summary>
/// A byte array output.
/// </summary>
[Output]
public byte[] ByteArrayOutput
{
get
{
_testTaskHost?.OutputRead("ByteArrayOutput", _byteArrayOutput);
return _byteArrayOutput;
}
}
/// <summary>
/// An sbyte output.
/// </summary>
[Output]
public sbyte SByteOutput
{
get
{
_testTaskHost?.OutputRead("SByteOutput", _sbyteOutput);
return _sbyteOutput;
}
}
/// <summary>
/// An sbyte array output.
/// </summary>
[Output]
public sbyte[] SByteArrayOutput
{
get
{
_testTaskHost?.OutputRead("SByteArrayOutput", _sbyteArrayOutput);
return _sbyteArrayOutput;
}
}
/// <summary>
/// A double output.
/// </summary>
[Output]
public double DoubleOutput
{
get
{
_testTaskHost?.OutputRead("DoubleOutput", _doubleOutput);
return _doubleOutput;
}
}
/// <summary>
/// A double array output.
/// </summary>
[Output]
public double[] DoubleArrayOutput
{
get
{
_testTaskHost?.OutputRead("DoubleArrayOutput", _doubleArrayOutput);
return _doubleArrayOutput;
}
}
/// <summary>
/// A float output.
/// </summary>
[Output]
public float FloatOutput
{
get
{
_testTaskHost?.OutputRead("FloatOutput", _floatOutput);
return _floatOutput;
}
}
/// <summary>
/// A float array output.
/// </summary>
[Output]
public float[] FloatArrayOutput
{
get
{
_testTaskHost?.OutputRead("FloatArrayOutput", _floatArrayOutput);
return _floatArrayOutput;
}
}
/// <summary>
/// A short output.
/// </summary>
[Output]
public short ShortOutput
{
get
{
_testTaskHost?.OutputRead("ShortOutput", _shortOutput);
return _shortOutput;
}
}
/// <summary>
/// A short array output.
/// </summary>
[Output]
public short[] ShortArrayOutput
{
get
{
_testTaskHost?.OutputRead("ShortArrayOutput", _shortArrayOutput);
return _shortArrayOutput;
}
}
/// <summary>
/// A ushort output.
/// </summary>
[Output]
public ushort UShortOutput
{
get
{
_testTaskHost?.OutputRead("UShortOutput", _ushortOutput);
return _ushortOutput;
}
}
/// <summary>
/// A ushort array output.
/// </summary>
[Output]
public ushort[] UShortArrayOutput
{
get
{
_testTaskHost?.OutputRead("UShortArrayOutput", _ushortArrayOutput);
return _ushortArrayOutput;
}
}
/// <summary>
/// An integer output.
/// </summary>
[Output]
public int IntOutput
@ -225,7 +840,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// An integer array output
/// An integer array output.
/// </summary>
[Output]
public int[] IntArrayOutput
@ -238,7 +853,137 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A string output
/// A uint output.
/// </summary>
[Output]
public uint UIntOutput
{
get
{
_testTaskHost?.OutputRead("UIntOutput", _uintOutput);
return _uintOutput;
}
}
/// <summary>
/// A uint array output.
/// </summary>
[Output]
public uint[] UIntArrayOutput
{
get
{
_testTaskHost?.OutputRead("UIntArrayOutput", _uintArrayOutput);
return _uintArrayOutput;
}
}
/// <summary>
/// A long output.
/// </summary>
[Output]
public long LongOutput
{
get
{
_testTaskHost?.OutputRead("LongOutput", _longOutput);
return _longOutput;
}
}
/// <summary>
/// A long array output.
/// </summary>
[Output]
public long[] LongArrayOutput
{
get
{
_testTaskHost?.OutputRead("LongArrayOutput", _longArrayOutput);
return _longArrayOutput;
}
}
/// <summary>
/// A ulong output.
/// </summary>
[Output]
public ulong ULongOutput
{
get
{
_testTaskHost?.OutputRead("ULongOutput", _ulongOutput);
return _ulongOutput;
}
}
/// <summary>
/// A ulong array output.
/// </summary>
[Output]
public ulong[] ULongArrayOutput
{
get
{
_testTaskHost?.OutputRead("ULongArrayOutput", _ulongArrayOutput);
return _ulongArrayOutput;
}
}
/// <summary>
/// A decimal output.
/// </summary>
[Output]
public decimal DecimalOutput
{
get
{
_testTaskHost?.OutputRead("DecimalOutput", _decimalOutput);
return _decimalOutput;
}
}
/// <summary>
/// A decimal array output.
/// </summary>
[Output]
public decimal[] DecimalArrayOutput
{
get
{
_testTaskHost?.OutputRead("DecimalArrayOutput", _decimalArrayOutput);
return _decimalArrayOutput;
}
}
/// <summary>
/// A char output.
/// </summary>
[Output]
public char CharOutput
{
get
{
_testTaskHost?.OutputRead("CharOutput", _charOutput);
return _charOutput;
}
}
/// <summary>
/// A char array output.
/// </summary>
[Output]
public char[] CharArrayOutput
{
get
{
_testTaskHost?.OutputRead("CharArrayOutput", _charArrayOutput);
return _charArrayOutput;
}
}
/// <summary>
/// A string output.
/// </summary>
[Output]
public string StringOutput
@ -290,7 +1035,59 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A null ITaskItem output
/// A DateTime output
/// </summary>
[Output]
public DateTime DateTimeOutput
{
get
{
_testTaskHost?.OutputRead("DateTimeOutput", _dateTimeOutput);
return _dateTimeOutput;
}
}
/// <summary>
/// A DateTime array output.
/// </summary>
[Output]
public DateTime[] DateTimeArrayOutput
{
get
{
_testTaskHost?.OutputRead("DateTimeArrayOutput", _dateTimeArrayOutput);
return _dateTimeArrayOutput;
}
}
/// <summary>
/// A CustomStruct output.
/// </summary>
[Output]
public CustomStruct CustomStructOutput
{
get
{
_testTaskHost?.OutputRead("CustomStructOutput", s_customStruct);
return s_customStruct;
}
}
/// <summary>
/// A CustomStruct array output.
/// </summary>
[Output]
public CustomStruct[] CustomStructArrayOutput
{
get
{
_testTaskHost?.OutputRead("CustomStructArrayOutput", s_customStructArray);
return s_customStructArray;
}
}
/// <summary>
/// A null ITaskItem output.
/// </summary>
[Output]
public ITaskItem NullITaskItemOutput
@ -303,7 +1100,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A null string array output
/// A null string array output.
/// </summary>
[Output]
public string[] NullStringArrayOutput
@ -316,7 +1113,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A null ITaskItem array output
/// A null ITaskItem array output.
/// </summary>
[Output]
public ITaskItem[] NullITaskItemArrayOutput
@ -329,7 +1126,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A string array output
/// A string array output.
/// </summary>
[Output]
public string[] StringArrayOutput
@ -342,7 +1139,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A task item output
/// A task item output.
/// </summary>
[Output]
public ITaskItem ItemOutput
@ -355,7 +1152,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A task item array output
/// A task item array output.
/// </summary>
[Output]
public ITaskItem[] ItemArrayOutput
@ -368,7 +1165,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
}
/// <summary>
/// A task item array output that is null
/// A task item array output that is null.
/// </summary>
[Output]
public ITaskItem[] ItemArrayNullOutput

Просмотреть файл

@ -3,7 +3,10 @@
using System;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests;
using Microsoft.Build.UnitTests.BackEnd;
@ -58,37 +61,159 @@ namespace Microsoft.Build.Engine.UnitTests.BackEnd
}
}
[Fact]
public void VariousParameterTypesCanBeTransmittedToAndRecievedFromTaskHost()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void VariousParameterTypesCanBeTransmittedToAndReceivedFromTaskHost(bool testLegacyImplementation)
{
using TestEnvironment env = TestEnvironment.Create(_output);
ChangeWaves.ResetStateForTests();
if (testLegacyImplementation)
{
env.SetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION", ChangeWaves.Wave17_6.ToString());
BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly();
}
string boolParam = "True";
string boolArrayParam = "False;True;False";
string byteParam = "42";
string byteArrayParam = "11;22;33";
string sbyteParam = "-42";
string sbyteArrayParam = "-11;-22;-33";
string doubleParam = "3.14";
string doubleArrayParam = "3.14;2.72";
string floatParam = "0.5";
string floatArrayParam = "0.6;0.7;0.8";
string shortParam = "-100";
string shortArrayParam = "-200;-300;999";
string ushortParam = "100";
string ushortArrayParam = "200;300;999";
string intParam = "-314";
string intArrayParam = "42;-67;98";
string uintParam = "314";
string uintArrayParam = "4200000;67;98";
string longParam = "-120000000000";
string longArrayParam = "-120000000000;0;1";
string ulongParam = "120000000000";
string ulongArrayParam = "120000000000;0;1";
string decimalParam = "0.999999999999";
string decimalArrayParam = "-0.999999999999";
string charParam = "A";
string charArrayParam = "A;b;2";
string stringParam = "stringParamInput";
string stringArrayParam = "stringArrayParamInput1;stringArrayParamInput2;stringArrayParamInput3";
string dateTimeParam = "01/01/2001 10:15:00";
string dateTimeArrayParam = "01/01/2001 10:15:00;02/02/2002 11:30:00;03/03/2003 12:45:00";
string projectContents = $@"
<Project>
<UsingTask TaskName=""{nameof(TaskBuilderTestTask)}"" AssemblyFile=""{typeof(TaskBuilderTestTask).Assembly.Location}"" TaskFactory=""TaskHostFactory"" />
<Target Name='{nameof(VariousParameterTypesCanBeTransmittedToAndRecievedFromTaskHost)}'>
<Target Name='{nameof(VariousParameterTypesCanBeTransmittedToAndReceivedFromTaskHost)}'>
<{nameof(TaskBuilderTestTask)}
ExecuteReturnParam=""true""
BoolParam=""true""
BoolArrayParam=""false;true;false""
IntParam=""314""
IntArrayParam=""42;67;98""
StringParam=""stringParamInput""
StringArrayParam=""stringArrayParamInput1;stringArrayParamInput2;stringArrayParamInput3"">
BoolParam=""{boolParam}""
BoolArrayParam=""{boolArrayParam}""
ByteParam=""{byteParam}""
ByteArrayParam=""{byteArrayParam}""
SByteParam=""{sbyteParam}""
SByteArrayParam=""{sbyteArrayParam}""
DoubleParam=""{doubleParam}""
DoubleArrayParam=""{doubleArrayParam}""
FloatParam=""{floatParam}""
FloatArrayParam=""{floatArrayParam}""
ShortParam=""{shortParam}""
ShortArrayParam=""{shortArrayParam}""
UShortParam=""{ushortParam}""
UShortArrayParam=""{ushortArrayParam}""
IntParam=""{intParam}""
IntArrayParam=""{intArrayParam}""
UIntParam=""{uintParam}""
UIntArrayParam=""{uintArrayParam}""
LongParam=""{longParam}""
LongArrayParam=""{longArrayParam}""
ULongParam=""{ulongParam}""
ULongArrayParam=""{ulongArrayParam}""
DecimalParam=""{decimalParam}""
DecimalArrayParam=""{decimalArrayParam}""
CharParam=""{charParam}""
CharArrayParam=""{charArrayParam}""
StringParam=""{stringParam}""
StringArrayParam=""{stringArrayParam}""
DateTimeParam=""{dateTimeParam}""
DateTimeArrayParam=""{dateTimeArrayParam}"">
<Output PropertyName=""BoolOutput"" TaskParameter=""BoolOutput"" />
<Output PropertyName=""BoolArrayOutput"" TaskParameter=""BoolArrayOutput"" />
<Output PropertyName=""ByteOutput"" TaskParameter=""ByteOutput"" />
<Output PropertyName=""ByteArrayOutput"" TaskParameter=""ByteArrayOutput"" />
<Output PropertyName=""SByteOutput"" TaskParameter=""SByteOutput"" />
<Output PropertyName=""SByteArrayOutput"" TaskParameter=""SByteArrayOutput"" />
<Output PropertyName=""DoubleOutput"" TaskParameter=""DoubleOutput"" />
<Output PropertyName=""DoubleArrayOutput"" TaskParameter=""DoubleArrayOutput"" />
<Output PropertyName=""FloatOutput"" TaskParameter=""FloatOutput"" />
<Output PropertyName=""FloatArrayOutput"" TaskParameter=""FloatArrayOutput"" />
<Output PropertyName=""ShortOutput"" TaskParameter=""ShortOutput"" />
<Output PropertyName=""ShortArrayOutput"" TaskParameter=""ShortArrayOutput"" />
<Output PropertyName=""UShortOutput"" TaskParameter=""UShortOutput"" />
<Output PropertyName=""UShortArrayOutput"" TaskParameter=""UShortArrayOutput"" />
<Output PropertyName=""IntOutput"" TaskParameter=""IntOutput"" />
<Output PropertyName=""IntArrayOutput"" TaskParameter=""IntArrayOutput"" />
<Output PropertyName=""EnumOutput"" TaskParameter=""EnumOutput"" />
<Output PropertyName=""UIntOutput"" TaskParameter=""UIntOutput"" />
<Output PropertyName=""UIntArrayOutput"" TaskParameter=""UIntArrayOutput"" />
<Output PropertyName=""LongOutput"" TaskParameter=""LongOutput"" />
<Output PropertyName=""LongArrayOutput"" TaskParameter=""LongArrayOutput"" />
<Output PropertyName=""ULongOutput"" TaskParameter=""ULongOutput"" />
<Output PropertyName=""ULongArrayOutput"" TaskParameter=""ULongArrayOutput"" />
<Output PropertyName=""DecimalOutput"" TaskParameter=""DecimalOutput"" />
<Output PropertyName=""DecimalArrayOutput"" TaskParameter=""DecimalArrayOutput"" />
<Output PropertyName=""CharOutput"" TaskParameter=""CharOutput"" />
<Output PropertyName=""CharArrayOutput"" TaskParameter=""CharArrayOutput"" />
<Output PropertyName=""StringOutput"" TaskParameter=""StringOutput"" />
<Output PropertyName=""StringArrayOutput"" TaskParameter=""StringArrayOutput"" />
<Output PropertyName=""DateTimeOutput"" TaskParameter=""DateTimeOutput"" />
<Output PropertyName=""DateTimeArrayOutput"" TaskParameter=""DateTimeArrayOutput"" />
<Output PropertyName=""CustomStructOutput"" TaskParameter=""CustomStructOutput"" />
<Output PropertyName=""EnumOutput"" TaskParameter=""EnumOutput"" />
</{nameof(TaskBuilderTestTask)}>
</Target>
</Project>";
TransientTestProjectWithFiles project = env.CreateTestProjectWithFiles(projectContents);
ProjectInstance projectInstance = new(project.ProjectFile);
projectInstance.Build(new[] { new MockLogger(env.Output) }).ShouldBeTrue();
projectInstance.GetPropertyValue("BoolOutput").ShouldBe(boolParam);
projectInstance.GetPropertyValue("BoolArrayOutput").ShouldBe(boolArrayParam);
projectInstance.GetPropertyValue("ByteOutput").ShouldBe(byteParam);
projectInstance.GetPropertyValue("ByteArrayOutput").ShouldBe(byteArrayParam);
projectInstance.GetPropertyValue("SByteOutput").ShouldBe(sbyteParam);
projectInstance.GetPropertyValue("SByteArrayOutput").ShouldBe(sbyteArrayParam);
projectInstance.GetPropertyValue("DoubleOutput").ShouldBe(doubleParam);
projectInstance.GetPropertyValue("DoubleArrayOutput").ShouldBe(doubleArrayParam);
projectInstance.GetPropertyValue("FloatOutput").ShouldBe(floatParam);
projectInstance.GetPropertyValue("FloatArrayOutput").ShouldBe(floatArrayParam);
projectInstance.GetPropertyValue("ShortOutput").ShouldBe(shortParam);
projectInstance.GetPropertyValue("ShortArrayOutput").ShouldBe(shortArrayParam);
projectInstance.GetPropertyValue("UShortOutput").ShouldBe(ushortParam);
projectInstance.GetPropertyValue("UShortArrayOutput").ShouldBe(ushortArrayParam);
projectInstance.GetPropertyValue("IntOutput").ShouldBe(intParam);
projectInstance.GetPropertyValue("IntArrayOutput").ShouldBe(intArrayParam);
projectInstance.GetPropertyValue("UIntOutput").ShouldBe(uintParam);
projectInstance.GetPropertyValue("UIntArrayOutput").ShouldBe(uintArrayParam);
projectInstance.GetPropertyValue("LongOutput").ShouldBe(longParam);
projectInstance.GetPropertyValue("LongArrayOutput").ShouldBe(longArrayParam);
projectInstance.GetPropertyValue("ULongOutput").ShouldBe(ulongParam);
projectInstance.GetPropertyValue("ULongArrayOutput").ShouldBe(ulongArrayParam);
projectInstance.GetPropertyValue("DecimalOutput").ShouldBe(decimalParam);
projectInstance.GetPropertyValue("DecimalArrayOutput").ShouldBe(decimalArrayParam);
projectInstance.GetPropertyValue("CharOutput").ShouldBe(charParam);
projectInstance.GetPropertyValue("CharArrayOutput").ShouldBe(charArrayParam);
projectInstance.GetPropertyValue("StringOutput").ShouldBe(stringParam);
projectInstance.GetPropertyValue("StringArrayOutput").ShouldBe(stringArrayParam);
projectInstance.GetPropertyValue("DateTimeOutput").ShouldBe(dateTimeParam);
projectInstance.GetPropertyValue("DateTimeArrayOutput").ShouldBe(dateTimeArrayParam);
projectInstance.GetPropertyValue("CustomStructOutput").ShouldBe(TaskBuilderTestTask.s_customStruct.ToString(CultureInfo.InvariantCulture));
projectInstance.GetPropertyValue("EnumOutput").ShouldBe(TargetBuiltReason.BeforeTargets.ToString());
}
}
}

Просмотреть файл

@ -385,6 +385,61 @@ namespace Microsoft.Build.UnitTests.BackEnd
Assert.Equal("testCustomBuildEvent", _customLogger.LastCustom.Message);
}
/// <summary>
/// Test that extended custom events are logged properly
/// </summary>
[Fact]
public void TestLogExtendedCustomEventNotSerializableMP()
{
_mockHost.BuildParameters.MaxNodeCount = 4;
// Log the custom event args. (Pretend that the task actually did this.)
_taskHost.LogCustomEvent(new ExtendedCustomBuildEventArgs("testExtCustomBuildEvent", "ext message", null, null));
// Make sure our custom logger received the actual custom event and not some fake.
Assert.True(_customLogger.LastCustom is ExtendedCustomBuildEventArgs); // "Expected custom build Event"
Assert.Equal("ext message", _customLogger.LastCustom.Message);
}
[Fact]
public void TestLogExtendedCustomErrorNotSerializableMP()
{
_mockHost.BuildParameters.MaxNodeCount = 4;
// Log the custom event args. (Pretend that the task actually did this.)
_taskHost.LogErrorEvent(new ExtendedBuildErrorEventArgs("testExtCustomBuildError", null, null, null, 0, 0, 0, 0,"ext err message", null, null));
// Make sure our custom logger received the actual custom event and not some fake.
Assert.True(_customLogger.LastError is ExtendedBuildErrorEventArgs); // "Expected custom build Event"
Assert.Equal("ext err message", _customLogger.LastError.Message);
}
[Fact]
public void TestLogExtendedCustomWarningNotSerializableMP()
{
_mockHost.BuildParameters.MaxNodeCount = 4;
// Log the custom event args. (Pretend that the task actually did this.)
_taskHost.LogWarningEvent(new ExtendedBuildWarningEventArgs("testExtCustomBuildWarning", null, null, null, 0, 0, 0, 0, "ext warn message", null, null));
// Make sure our custom logger received the actual custom event and not some fake.
Assert.True(_customLogger.LastWarning is ExtendedBuildWarningEventArgs); // "Expected custom build Event"
Assert.Equal("ext warn message", _customLogger.LastWarning.Message);
}
[Fact]
public void TestLogExtendedCustomMessageNotSerializableMP()
{
_mockHost.BuildParameters.MaxNodeCount = 4;
// Log the custom event args. (Pretend that the task actually did this.)
_taskHost.LogMessageEvent(new ExtendedBuildMessageEventArgs("testExtCustomBuildMessage", "ext message", null, null, MessageImportance.Normal));
// Make sure our custom logger received the actual custom event and not some fake.
Assert.True(_customLogger.LastMessage is ExtendedBuildMessageEventArgs); // "Expected custom build Event"
Assert.Equal("ext message", _customLogger.LastMessage.Message);
}
/// <summary>
/// Test that errors are logged properly
/// </summary>

Просмотреть файл

@ -13,6 +13,7 @@ using Microsoft.Build.Framework.Profiler;
using Microsoft.Build.Logging;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests.BackEnd;
using Shouldly;
using Xunit;
#nullable disable
@ -262,6 +263,40 @@ namespace Microsoft.Build.UnitTests
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void RoundtripExtendedErrorEventArgs_SerializedAsError(bool useArguments)
{
var args = new ExtendedBuildErrorEventArgs(
"extendedDataType",
"Subcategory",
"Code",
"File",
1,
2,
3,
4,
"Message with arguments: '{0}'",
"Help",
"SenderName",
DateTime.Parse("9/1/2021 12:02:07 PM"),
useArguments ? new object[] { "argument0" } : null);
// For now we don't serialize extended data into binary log
Roundtrip<BuildErrorEventArgs>(args,
e => e.Code,
e => e.ColumnNumber.ToString(),
e => e.EndColumnNumber.ToString(),
e => e.EndLineNumber.ToString(),
e => e.File,
e => e.LineNumber.ToString(),
e => e.Message,
e => e.ProjectFile,
e => e.Subcategory,
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
@ -294,6 +329,40 @@ namespace Microsoft.Build.UnitTests
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void RoundtripExtendedWarningEventArgs_SerializedAsWarning(bool useArguments)
{
var args = new ExtendedBuildWarningEventArgs(
"extendedDataType",
"Subcategory",
"Code",
"File",
1,
2,
3,
4,
"Message with arguments: '{0}'",
"Help",
"SenderName",
DateTime.Parse("9/1/2021 12:02:07 PM"),
useArguments ? new object[] { "argument0" } : null);
// For now we don't serialize extended data into binary log
Roundtrip<BuildWarningEventArgs>(args,
e => e.Code,
e => e.ColumnNumber.ToString(),
e => e.EndColumnNumber.ToString(),
e => e.EndLineNumber.ToString(),
e => e.File,
e => e.LineNumber.ToString(),
e => e.Message,
e => e.ProjectFile,
e => e.Subcategory,
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
@ -328,6 +397,122 @@ namespace Microsoft.Build.UnitTests
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void RoundtripExtendedBuildMessageEventArgs_SerializedAsMessage(bool useArguments)
{
var args = new ExtendedBuildMessageEventArgs(
"extendedDataType",
"Subcategory",
"Code",
"File",
1,
2,
3,
4,
"Message",
"Help",
"SenderName",
MessageImportance.High,
DateTime.Parse("12/12/2015 06:11:56 PM"),
useArguments ? new object[] { "argument0" } : null);
Roundtrip<BuildMessageEventArgs>(args,
e => e.Code,
e => e.ColumnNumber.ToString(),
e => e.EndColumnNumber.ToString(),
e => e.EndLineNumber.ToString(),
e => e.File,
e => e.LineNumber.ToString(),
e => e.Message,
e => e.Importance.ToString(),
e => e.ProjectFile,
e => e.Subcategory,
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Fact]
public void RoundtripAssemblyLoadBuild()
{
string assemblyName = Guid.NewGuid().ToString();
string assemblyPath = Guid.NewGuid().ToString();
Guid mvid = Guid.NewGuid();
string loadingInitiator = Guid.NewGuid().ToString();
string appDomainName = Guid.NewGuid().ToString();
AssemblyLoadingContext context =
(AssemblyLoadingContext)(new Random().Next(Enum.GetNames(typeof(AssemblyLoadingContext)).Length));
AssemblyLoadBuildEventArgs args = new(context, loadingInitiator, assemblyName, assemblyPath, mvid, appDomainName);
Roundtrip(args,
e => e.Code,
e => e.ColumnNumber.ToString(),
e => e.EndColumnNumber.ToString(),
e => e.EndLineNumber.ToString(),
e => e.File,
e => e.LineNumber.ToString(),
e => e.Message,
e => e.Importance.ToString(),
e => e.ProjectFile,
e => e.Subcategory,
e => e.LoadingContext.ToString(),
e => e.AssemblyName,
e => e.AssemblyPath,
e => e.MVID.ToString(),
e => e.AppDomainDescriptor,
e => string.Join(", ", e.RawArguments ?? Array.Empty<object>()));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void ExtendedCustomBuildEventArgs_SerializedAsMessage(bool withOptionalData)
{
ExtendedCustomBuildEventArgs args = new(
type: "TypeOfExtendedCustom",
message: withOptionalData ? "a message with args {0} {1}" : null,
helpKeyword: withOptionalData ? "MSBT123" : null,
senderName: withOptionalData ? $"UnitTest {Guid.NewGuid()}" : null,
eventTimestamp: withOptionalData ? DateTime.Parse("3/1/2017 11:11:56 AM") : DateTime.Now,
messageArgs: withOptionalData ? new object[] { "arg0val", "arg1val" } : null)
{
ExtendedData = withOptionalData ? "{'long-json':'mostly-strings'}" : null,
ExtendedMetadata = withOptionalData ? new Dictionary<string, string> { { "m1", "v1" }, { "m2", "v2" } } : null,
BuildEventContext = withOptionalData ? new BuildEventContext(1, 2, 3, 4, 5, 6, 7) : null,
};
var memoryStream = new MemoryStream();
var binaryWriter = new BinaryWriter(memoryStream);
var buildEventArgsWriter = new BuildEventArgsWriter(binaryWriter);
buildEventArgsWriter.Write(args);
memoryStream.Position = 0;
var binaryReader = new BinaryReader(memoryStream);
using var buildEventArgsReader = new BuildEventArgsReader(binaryReader, BinaryLogger.FileFormatVersion);
var deserialized = buildEventArgsReader.Read();
BuildMessageEventArgs desArgs = (BuildMessageEventArgs)deserialized;
desArgs.ShouldBeOfType(typeof(BuildMessageEventArgs));
desArgs.Message.ShouldBe(args.Message);
desArgs.HelpKeyword.ShouldBe(args.HelpKeyword);
desArgs.SenderName.ShouldBe(args.SenderName);
desArgs.Importance.ShouldBe(MessageImportance.Normal);
desArgs.Timestamp.ShouldBe(args.Timestamp);
if (withOptionalData)
{
desArgs.BuildEventContext.ShouldBe(args.BuildEventContext);
}
else
{
desArgs.BuildEventContext.ShouldBe(BuildEventContext.Invalid);
}
}
[Fact]
public void RoundtripResponseFileUsedEventArgs()
{
@ -689,7 +874,7 @@ namespace Microsoft.Build.UnitTests
Assert.Equal(length, memoryStream.Position);
Assert.NotNull(deserializedArgs);
Assert.Equal(args.GetType(), deserializedArgs.GetType());
Assert.Equal(typeof(T), deserializedArgs.GetType());
foreach (var field in fieldsToCompare)
{

Просмотреть файл

@ -695,8 +695,9 @@ namespace Microsoft.Build.Graph.UnitTests
var globalProperties = currentSolutionConfiguration != null
? new Dictionary<string, string>
{
["Configuration"] = currentSolutionConfiguration.ConfigurationName,
["Platform"] = currentSolutionConfiguration.PlatformName
// Intentionally use mismatched casing to ensure it's properly normalized.
["Configuration"] = currentSolutionConfiguration.ConfigurationName.ToUpperInvariant(),
["Platform"] = currentSolutionConfiguration.PlatformName.ToUpperInvariant()
}
: new Dictionary<string, string>();
@ -706,6 +707,9 @@ namespace Microsoft.Build.Graph.UnitTests
globalProperties),
_env.CreateProjectCollection().Collection);
// Exactly 1 node per project
graph.ProjectNodes.Count.ShouldBe(graph.ProjectNodes.Select(GetProjectPath).Distinct().Count());
// in the solution, all nodes are entry points
graphFromSolution.EntryPointNodes.Select(GetProjectPath)
.ShouldBeSetEquivalentTo(graph.ProjectNodes.Select(GetProjectPath));
@ -724,19 +728,9 @@ namespace Microsoft.Build.Graph.UnitTests
foreach (var node in graphFromSolution.ProjectNodes)
{
// Project references get duplicated, once as entry points from the solution (handled in the if block) and once as nodes
// produced by ProjectReference items (handled in the else block).
if (node.ReferencingProjects.Count == 0)
{
var expectedProjectConfiguration = actualProjectConfigurations[GetProjectNumber(node).ToString()][expectedCurrentConfiguration];
GetConfiguration(node).ShouldBe(expectedProjectConfiguration.ConfigurationName);
GetPlatform(node).ShouldBe(expectedProjectConfiguration.PlatformName);
}
else
{
GetConfiguration(node).ShouldBe(GetConfiguration(node.ReferencingProjects.First()));
GetPlatform(node).ShouldBe(GetPlatform(node.ReferencingProjects.First()));
}
var expectedProjectConfiguration = actualProjectConfigurations[GetProjectNumber(node).ToString()][expectedCurrentConfiguration];
GetConfiguration(node).ShouldBe(expectedProjectConfiguration.ConfigurationName);
GetPlatform(node).ShouldBe(expectedProjectConfiguration.PlatformName);
}
}

Просмотреть файл

@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="System.IO.Compression" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="System.Configuration.ConfigurationManager" />
<PackageReference Include="Shouldly" />
<PackageReference Include="System.Net.Http" />

Просмотреть файл

@ -1,68 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework.Profiler;
using Microsoft.Build.UnitTests.BackEnd;
using Xunit;
#nullable disable
namespace Microsoft.Build.UnitTests
{
// Although this tests the ProfilerResult API from Microsoft.Build.Framework, it uses the
// construction APIs in Microsoft.Build in the test, so this test is in the Microsoft.Build tests
public class ProjectEvaluationFinishedEventArgs_Tests
{
/// <summary>
/// Roundtrip serialization tests for <see cref="ProfilerResult"/>
/// </summary>
[MemberData(nameof(GetProfilerResults))]
[Theory]
public void ProfilerResultRoundTrip(ProfilerResult profilerResult)
{
var writeTranslator = TranslationHelpers.GetWriteTranslator();
ProfilerResult deserializedResult = default;
writeTranslator.TranslateDotNet(ref profilerResult);
var readTranslator = TranslationHelpers.GetReadTranslator();
readTranslator.TranslateDotNet(ref deserializedResult);
Assert.Equal(deserializedResult, profilerResult);
}
public static IEnumerable<object[]> GetProfilerResults()
{
yield return new object[] { new ProfilerResult(new Dictionary<EvaluationLocation, ProfiledLocation>()) };
yield return new object[] { new ProfilerResult(new Dictionary<EvaluationLocation, ProfiledLocation>
{
{new EvaluationLocation(0, null, EvaluationPass.TotalEvaluation, "1", "myFile", 42, "elementName", "description", EvaluationLocationKind.Condition), new ProfiledLocation(TimeSpan.MaxValue, TimeSpan.MinValue, 2) },
{new EvaluationLocation(1, 0, EvaluationPass.Targets, "1", null, null, null, null, EvaluationLocationKind.Glob), new ProfiledLocation(TimeSpan.MaxValue, TimeSpan.MinValue, 2) },
{new EvaluationLocation(2, 0, EvaluationPass.LazyItems, "2", null, null, null, null, EvaluationLocationKind.Element), new ProfiledLocation(TimeSpan.Zero, TimeSpan.Zero, 0) }
}) };
var element = new ProjectRootElement(
XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes(
"<Project />"))),
new ProjectRootElementCache(false), false, false);
yield return new object[] { new ProfilerResult(new Dictionary<EvaluationLocation, ProfiledLocation>
{
{EvaluationLocation.CreateLocationForCondition(null, EvaluationPass.UsingTasks, "1", "myFile", 42, "conditionCase"), new ProfiledLocation(TimeSpan.MaxValue, TimeSpan.MinValue, 2) },
{EvaluationLocation.CreateLocationForProject(null, EvaluationPass.InitialProperties, "1", "myFile", 42, element),
new ProfiledLocation(TimeSpan.MaxValue, TimeSpan.MinValue, 2) },
{EvaluationLocation.CreateLocationForGlob(null, EvaluationPass.InitialProperties, "1", "myFile", 42, "glob description"),
new ProfiledLocation(TimeSpan.MaxValue, TimeSpan.MinValue, 2) }
}) };
}
}
}

Просмотреть файл

@ -31,7 +31,7 @@ namespace Microsoft.Build.BackEnd
/// Constructor for deserialization
/// </summary>
private LogMessagePacket(ITranslator translator)
: base(translator)
: base(translator, new TargetFinishedTranslator(TranslateTargetFinishedEvent))
{
}

Просмотреть файл

@ -901,9 +901,60 @@ namespace Microsoft.Build.BackEnd.Logging
LogMessagePacket loggingPacket = (LogMessagePacket)packet;
InjectNonSerializedData(loggingPacket);
WarnOnDeprecatedCustomArgsSerialization(loggingPacket);
ProcessLoggingEvent(loggingPacket.NodeBuildEvent);
}
/// <summary>
/// Serializing unknown CustomEvent which has to use unsecure BinaryFormatter by TranslateDotNet.
/// Since BinaryFormatter is going to be deprecated, log warning so users can use new Extended*EventArgs instead of custom
/// EventArgs derived from existing EventArgs.
/// </summary>
private void WarnOnDeprecatedCustomArgsSerialization(LogMessagePacket loggingPacket)
{
if (loggingPacket.EventType == LoggingEventType.CustomEvent
&& ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_8)
&& Traits.Instance.EscapeHatches.EnableWarningOnCustomBuildEvent)
{
BuildEventArgs buildEvent = loggingPacket.NodeBuildEvent.Value.Value;
BuildEventContext buildEventContext = buildEvent?.BuildEventContext ?? BuildEventContext.Invalid;
string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword(
out string warningCode,
out string helpKeyword,
"DeprecatedEventSerialization",
buildEvent?.GetType().Name ?? string.Empty);
BuildWarningEventArgs warning = new(
null,
warningCode,
BuildEventFileInfo.Empty.File,
BuildEventFileInfo.Empty.Line,
BuildEventFileInfo.Empty.Column,
BuildEventFileInfo.Empty.EndLine,
BuildEventFileInfo.Empty.EndColumn,
message,
helpKeyword,
"MSBuild");
warning.BuildEventContext = buildEventContext;
if (warning.ProjectFile == null && buildEventContext.ProjectContextId != BuildEventContext.InvalidProjectContextId)
{
warning.ProjectFile = buildEvent switch
{
BuildMessageEventArgs buildMessageEvent => buildMessageEvent.ProjectFile,
BuildErrorEventArgs buildErrorEvent => buildErrorEvent.ProjectFile,
BuildWarningEventArgs buildWarningEvent => buildWarningEvent.ProjectFile,
_ => null,
};
}
ProcessLoggingEvent(warning);
}
}
/// <summary>
/// Register an instantiated logger which implements the ILogger interface. This logger will be registered to a specific event
/// source (the central logger event source) which will receive all logging messages for a given build.
@ -1438,42 +1489,93 @@ namespace Microsoft.Build.BackEnd.Logging
{
if (ShouldTreatWarningAsMessage(warningEvent))
{
buildEventArgs = new BuildMessageEventArgs(
warningEvent.Subcategory,
warningEvent.Code,
warningEvent.File,
warningEvent.LineNumber,
warningEvent.ColumnNumber,
warningEvent.EndLineNumber,
warningEvent.EndColumnNumber,
warningEvent.Message,
warningEvent.HelpKeyword,
warningEvent.SenderName,
MessageImportance.Low,
warningEvent.Timestamp)
if (buildEventArgs is ExtendedBuildWarningEventArgs extWarningEvent)
{
BuildEventContext = warningEvent.BuildEventContext,
ProjectFile = warningEvent.ProjectFile,
};
buildEventArgs = new ExtendedBuildMessageEventArgs(
extWarningEvent.ExtendedType,
extWarningEvent.Subcategory,
extWarningEvent.Code,
extWarningEvent.File,
extWarningEvent.LineNumber,
extWarningEvent.ColumnNumber,
extWarningEvent.EndLineNumber,
extWarningEvent.EndColumnNumber,
extWarningEvent.Message,
extWarningEvent.HelpKeyword,
extWarningEvent.SenderName,
MessageImportance.Low,
extWarningEvent.Timestamp)
{
BuildEventContext = warningEvent.BuildEventContext,
ProjectFile = warningEvent.ProjectFile,
ExtendedMetadata = extWarningEvent.ExtendedMetadata,
ExtendedData = extWarningEvent.ExtendedData,
};
}
else
{
buildEventArgs = new BuildMessageEventArgs(
warningEvent.Subcategory,
warningEvent.Code,
warningEvent.File,
warningEvent.LineNumber,
warningEvent.ColumnNumber,
warningEvent.EndLineNumber,
warningEvent.EndColumnNumber,
warningEvent.Message,
warningEvent.HelpKeyword,
warningEvent.SenderName,
MessageImportance.Low,
warningEvent.Timestamp)
{
BuildEventContext = warningEvent.BuildEventContext,
ProjectFile = warningEvent.ProjectFile,
};
}
}
else if (ShouldTreatWarningAsError(warningEvent))
{
buildEventArgs = new BuildErrorEventArgs(
warningEvent.Subcategory,
warningEvent.Code,
warningEvent.File,
warningEvent.LineNumber,
warningEvent.ColumnNumber,
warningEvent.EndLineNumber,
warningEvent.EndColumnNumber,
warningEvent.Message,
warningEvent.HelpKeyword,
warningEvent.SenderName,
warningEvent.Timestamp)
if (warningEvent is ExtendedBuildWarningEventArgs extWarningEvent)
{
BuildEventContext = warningEvent.BuildEventContext,
ProjectFile = warningEvent.ProjectFile,
};
buildEventArgs = new ExtendedBuildErrorEventArgs(
extWarningEvent.ExtendedType,
extWarningEvent.Subcategory,
extWarningEvent.Code,
extWarningEvent.File,
extWarningEvent.LineNumber,
extWarningEvent.ColumnNumber,
extWarningEvent.EndLineNumber,
extWarningEvent.EndColumnNumber,
extWarningEvent.Message,
extWarningEvent.HelpKeyword,
extWarningEvent.SenderName,
extWarningEvent.Timestamp)
{
BuildEventContext = warningEvent.BuildEventContext,
ProjectFile = warningEvent.ProjectFile,
ExtendedMetadata = extWarningEvent.ExtendedMetadata,
ExtendedData = extWarningEvent.ExtendedData,
};
}
else
{
buildEventArgs = new BuildErrorEventArgs(
warningEvent.Subcategory,
warningEvent.Code,
warningEvent.File,
warningEvent.LineNumber,
warningEvent.ColumnNumber,
warningEvent.EndLineNumber,
warningEvent.EndColumnNumber,
warningEvent.Message,
warningEvent.HelpKeyword,
warningEvent.SenderName,
warningEvent.Timestamp)
{
BuildEventContext = warningEvent.BuildEventContext,
ProjectFile = warningEvent.ProjectFile,
};
}
}
}

Просмотреть файл

@ -1110,7 +1110,9 @@ namespace Microsoft.Build.BackEnd
internal bool IsEventSerializable(BuildEventArgs e)
{
#pragma warning disable SYSLIB0050
if (!e.GetType().GetTypeInfo().IsSerializable)
// Types which are not serializable and are not IExtendedBuildEventArgs as
// those always implement custom serialization by WriteToStream and CreateFromStream.
if (!e.GetType().GetTypeInfo().IsSerializable && e is not IExtendedBuildEventArgs)
#pragma warning restore SYSLIB0050
{
_taskLoggingContext.LogWarning(null, new BuildEventFileInfo(string.Empty), "ExpectedEventToBeSerializable", e.GetType().Name);

Просмотреть файл

@ -581,7 +581,28 @@ namespace Microsoft.Build.Execution
{
if (_nodeEndpoint.LinkStatus == LinkStatus.Active)
{
#if RUNTIME_TYPE_NETCORE
if (packet is LogMessagePacketBase logMessage
&& logMessage.EventType == LoggingEventType.CustomEvent
&& ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_8)
&& Traits.Instance.EscapeHatches.EnableWarningOnCustomBuildEvent)
{
BuildEventArgs buildEvent = logMessage.NodeBuildEvent.Value.Value;
// Serializing unknown CustomEvent which has to use unsecure BinaryFormatter by TranslateDotNet<T>
// Since BinaryFormatter is deprecated in dotnet 8+, log error so users discover root cause easier
// then by reading CommTrace where it would be otherwise logged as critical infra error.
_loggingService.LogError(_loggingContext?.BuildEventContext ?? BuildEventContext.Invalid, null, BuildEventFileInfo.Empty,
"DeprecatedEventSerialization",
buildEvent?.GetType().Name ?? string.Empty);
}
else
{
_nodeEndpoint.SendData(packet);
}
#else
_nodeEndpoint.SendData(packet);
#endif
}
}

Просмотреть файл

@ -258,7 +258,7 @@ namespace Microsoft.Build.Graph
ProjectGraphEntryPoint solutionEntryPoint = entryPoints.Single();
ImmutableDictionary<string, string>.Builder solutionGlobalPropertiesBuilder = ImmutableDictionary.CreateBuilder(
keyComparer: StringComparer.OrdinalIgnoreCase,
valueComparer: StringComparer.OrdinalIgnoreCase);
valueComparer: StringComparer.Ordinal);
if (solutionEntryPoint.GlobalProperties != null)
{
@ -279,9 +279,11 @@ namespace Microsoft.Build.Graph
IReadOnlyCollection<ProjectInSolution> projectsInSolution = GetBuildableProjects(solution);
SolutionConfigurationInSolution currentSolutionConfiguration = SelectSolutionConfiguration(solution, solutionEntryPoint.GlobalProperties);
// Mimic behavior of SolutionProjectGenerator
SolutionConfigurationInSolution currentSolutionConfiguration = SelectSolutionConfiguration(solution, solutionEntryPoint.GlobalProperties);
solutionGlobalPropertiesBuilder["Configuration"] = currentSolutionConfiguration.ConfigurationName;
solutionGlobalPropertiesBuilder["Platform"] = currentSolutionConfiguration.PlatformName;
string solutionConfigurationXml = SolutionProjectGenerator.GetSolutionConfiguration(solution, currentSolutionConfiguration);
solutionGlobalPropertiesBuilder["CurrentSolutionConfigurationContents"] = solutionConfigurationXml;
solutionGlobalPropertiesBuilder["BuildingSolutionFile"] = "true";

Просмотреть файл

@ -338,7 +338,9 @@ namespace Microsoft.Build.BackEnd
internal bool IsEventSerializable(BuildEventArgs e)
{
#pragma warning disable SYSLIB0050
if (!e.GetType().GetTypeInfo().IsSerializable)
// Types which are not serializable and are not IExtendedBuildEventArgs as
// those always implement custom serialization by WriteToStream and CreateFromStream.
if (!e.GetType().GetTypeInfo().IsSerializable && e is not IExtendedBuildEventArgs)
#pragma warning restore SYSLIB0050
{
_loggingContext.LogWarning(null, new BuildEventFileInfo(string.Empty), "ExpectedEventToBeSerializable", e.GetType().Name);

Просмотреть файл

@ -1479,7 +1479,7 @@ namespace Microsoft.Build.Execution
initialized = factory.Initialize(RegisteredName, ParameterGroupAndTaskBody.UsingTaskParameters, ParameterGroupAndTaskBody.InlineTaskXmlBody, taskFactoryLoggingHost);
// TaskFactoryParameters will always be null unless specifically created to have runtime and architecture parameters.
if (TaskFactoryParameters != null)
if (initialized && TaskFactoryParameters != null)
{
targetLoggingContext.LogWarning(
null,
@ -1493,7 +1493,7 @@ namespace Microsoft.Build.Execution
}
// Throw an error if the ITaskFactory did not set the TaskType property. If the property is null, it can cause NullReferenceExceptions in our code
if (factory.TaskType == null)
if (initialized && factory.TaskType == null)
{
throw new InvalidOperationException(AssemblyResources.GetString("TaskFactoryTaskTypeIsNotSet"));
}

Просмотреть файл

@ -156,6 +156,7 @@ namespace Microsoft.Build.Logging
TaskCommandLine
TaskParameter
UninitializedPropertyRead
ExtendedMessage
BuildStatus
TaskStarted
TaskFinished
@ -168,11 +169,13 @@ namespace Microsoft.Build.Logging
ProjectEvaluationStarted
ProjectEvaluationFinished
BuildError
ExtendedBuildError
BuildWarning
ExtendedBuildWarning
CustomBuild
ExternalProjectStarted
ExternalProjectFinished
ExtendedCustomBuild
*/
private void WriteCore(BuildEventArgs e)

Просмотреть файл

@ -31,7 +31,6 @@ namespace Microsoft.Build.Framework.UnitTests
AssemblyLoadBuildEventArgs argDeserialized = new();
int packetVersion = (Environment.Version.Major * 10) + Environment.Version.Minor;
argDeserialized.CreateFromStream(br, packetVersion);
argDeserialized.LoadingInitiator.ShouldBe(loadingInitiator);
argDeserialized.AssemblyName.ShouldBe(assemblyName);
argDeserialized.AssemblyPath.ShouldBe(assemblyPath);

Просмотреть файл

@ -0,0 +1,218 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using FluentAssertions;
using Xunit;
namespace Microsoft.Build.Framework.UnitTests;
public class ExtendedBuildEventArgs_Tests
{
[InlineData(true)]
[InlineData(false)]
[Theory]
public void ExtendedCustomBuildEventArgs_SerializationDeserialization(bool withOptionalData)
{
ExtendedCustomBuildEventArgs arg = new(
type: "TypeOfExtendedCustom",
message: withOptionalData ? "a message with args {0} {1}" : null,
helpKeyword: withOptionalData ? "MSBT123" : null,
senderName: withOptionalData ? $"UnitTest {Guid.NewGuid()}" : null,
eventTimestamp: withOptionalData ? DateTime.Parse("3/1/2017 11:11:56 AM") : DateTime.Now,
messageArgs: withOptionalData ? new object[] { "arg0val", "arg1val" } : null)
{
ExtendedData = withOptionalData ? "{'long-json':'mostly-strings'}" : null,
ExtendedMetadata = withOptionalData ? new Dictionary<string, string?> { {"m1", "v1" }, { "m2", "v2" } } : null,
BuildEventContext = withOptionalData ? new BuildEventContext(1, 2, 3, 4, 5, 6, 7) : null,
};
using MemoryStream stream = new MemoryStream();
using BinaryWriter bw = new BinaryWriter(stream);
arg.WriteToStream(bw);
stream.Position = 0;
using BinaryReader br = new BinaryReader(stream);
ExtendedCustomBuildEventArgs argDeserialized = new();
argDeserialized.CreateFromStream(br, 80);
argDeserialized.Should().BeEquivalentTo(arg);
}
[InlineData(true)]
[InlineData(false)]
[Theory]
public void ExtendedErrorEventArgs_SerializationDeserialization(bool withOptionalData)
{
ExtendedBuildErrorEventArgs arg = new(
type: "TypeOfExtendedCustom",
subcategory: withOptionalData ? "sub-type" : null,
code: withOptionalData ? "a-code" : null,
file: withOptionalData ? ".\\dev\\my.csproj" : null,
lineNumber: withOptionalData ? 1 : default,
columnNumber: withOptionalData ? 2 : default,
endLineNumber: withOptionalData ? 3 : default,
endColumnNumber: withOptionalData ? 4 : default,
message: withOptionalData ? "a message with args {0} {1}" : null,
helpKeyword: withOptionalData ? "MSBT123" : null,
senderName: withOptionalData ? $"UnitTest {Guid.NewGuid()}" : null,
helpLink: withOptionalData ? "(001)2234456" : null,
eventTimestamp: withOptionalData ? DateTime.Parse("3/1/2017 11:11:56 AM") : DateTime.Now,
messageArgs: withOptionalData ? new object[] { "arg0val", "arg1val" } : null)
{
ExtendedData = withOptionalData ? "{'long-json':'mostly-strings'}" : null,
ExtendedMetadata = withOptionalData ? new Dictionary<string, string?> { { "m1", "v1" }, { "m2", "v2" } } : null,
BuildEventContext = withOptionalData ? new BuildEventContext(1, 2, 3, 4, 5, 6, 7) : null,
};
using MemoryStream stream = new MemoryStream();
using BinaryWriter bw = new BinaryWriter(stream);
arg.WriteToStream(bw);
stream.Position = 0;
using BinaryReader br = new BinaryReader(stream);
ExtendedBuildErrorEventArgs argDeserialized = new();
argDeserialized.CreateFromStream(br, 80);
argDeserialized.Should().BeEquivalentTo(arg);
}
[InlineData(true)]
[InlineData(false)]
[Theory]
public void ExtendedWarningEventArgs_SerializationDeserialization(bool withOptionalData)
{
ExtendedBuildWarningEventArgs arg = new(
type: "TypeOfExtendedCustom",
subcategory: withOptionalData ? "sub-type" : null,
code: withOptionalData ? "a-code" : null,
file: withOptionalData ? ".\\dev\\my.csproj" : null,
lineNumber: withOptionalData ? 1 : default,
columnNumber: withOptionalData ? 2 : default,
endLineNumber: withOptionalData ? 3 : default,
endColumnNumber: withOptionalData ? 4 : default,
message: withOptionalData ? "a message with args {0} {1}" : null,
helpKeyword: withOptionalData ? "MSBT123" : null,
senderName: withOptionalData ? $"UnitTest {Guid.NewGuid()}" : null,
helpLink: withOptionalData ? "(001)2234456" : null,
eventTimestamp: withOptionalData ? DateTime.Parse("3/1/2017 11:11:56 AM") : DateTime.Now,
messageArgs: withOptionalData ? new object[] { "arg0val", "arg1val" } : null)
{
ExtendedData = withOptionalData ? "{'long-json':'mostly-strings'}" : null,
ExtendedMetadata = withOptionalData ? new Dictionary<string, string?> { { "m1", "v1" }, { "m2", "v2" } } : null,
BuildEventContext = withOptionalData ? new BuildEventContext(1, 2, 3, 4, 5, 6, 7) : null,
};
using MemoryStream stream = new MemoryStream();
using BinaryWriter bw = new BinaryWriter(stream);
arg.WriteToStream(bw);
stream.Position = 0;
using BinaryReader br = new BinaryReader(stream);
ExtendedBuildWarningEventArgs argDeserialized = new();
argDeserialized.CreateFromStream(br, 80);
argDeserialized.Should().BeEquivalentTo(arg);
}
[InlineData(true)]
[InlineData(false)]
[Theory]
public void ExtendedMessageEventArgs_SerializationDeserialization(bool withOptionalData)
{
ExtendedBuildMessageEventArgs arg = new(
type: "TypeOfExtendedCustom",
subcategory: withOptionalData ? "sub-type" : null,
code: withOptionalData ? "a-code" : null,
file: withOptionalData ? ".\\dev\\my.csproj" : null,
lineNumber: withOptionalData ? 1 : default,
columnNumber: withOptionalData ? 2 : default,
endLineNumber: withOptionalData ? 3 : default,
endColumnNumber: withOptionalData ? 4 : default,
message: withOptionalData ? "a message with args {0} {1}" : null,
helpKeyword: withOptionalData ? "MSBT123" : null,
senderName: withOptionalData ? $"UnitTest {Guid.NewGuid()}" : null,
importance: withOptionalData ? MessageImportance.Normal : default,
eventTimestamp: withOptionalData ? DateTime.Parse("3/1/2017 11:11:56 AM") : DateTime.Now,
messageArgs: withOptionalData ? new object[] { "arg0val", "arg1val" } : null)
{
ExtendedData = withOptionalData ? "{'long-json':'mostly-strings'}" : null,
ExtendedMetadata = withOptionalData ? new Dictionary<string, string?> { { "m1", "v1" }, { "m2", "v2" } } : null,
BuildEventContext = withOptionalData ? new BuildEventContext(1, 2, 3, 4, 5, 6, 7) : null,
};
using MemoryStream stream = new MemoryStream();
using BinaryWriter bw = new BinaryWriter(stream);
arg.WriteToStream(bw);
stream.Position = 0;
using BinaryReader br = new BinaryReader(stream);
ExtendedBuildMessageEventArgs argDeserialized = new();
argDeserialized.CreateFromStream(br, 80);
argDeserialized.Should().BeEquivalentTo(arg);
}
[Fact]
public void ExtendedCustomBuildEventArgs_Ctors()
{
var ea = new ExtendedCustomBuildEventArgs();
ea = new ExtendedCustomBuildEventArgs("type");
ea = new ExtendedCustomBuildEventArgs("type", "Message {0}", "Help", "sender");
ea = new ExtendedCustomBuildEventArgs("type", "Message {0}", "Help", "sender", DateTime.Now);
ea = new ExtendedCustomBuildEventArgs("type", "Message {0}", "Help", "sender", DateTime.Now, "arg1");
ea = new ExtendedCustomBuildEventArgs("type");
ea = new ExtendedCustomBuildEventArgs("type", null, null, null);
ea = new ExtendedCustomBuildEventArgs("type", null, null, null, default(DateTime));
ea = new ExtendedCustomBuildEventArgs("type", null, null, null, default(DateTime), null);
}
[Fact]
public void ExtendedBuildErrorEventArgs_Ctors()
{
var ea = new ExtendedBuildErrorEventArgs();
ea = new ExtendedBuildErrorEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "Message", "HelpKeyword", "sender");
ea = new ExtendedBuildErrorEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "Message", "HelpKeyword", "sender", DateTime.Now);
ea = new ExtendedBuildErrorEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "sender", DateTime.Now, "Message");
ea = new ExtendedBuildErrorEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "sender", "HelpLink", DateTime.Now, "Message");
ea = new ExtendedBuildErrorEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null);
ea = new ExtendedBuildErrorEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, DateTime.Now);
ea = new ExtendedBuildErrorEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, null, DateTime.Now, null);
}
[Fact]
public void ExtendedBuildWarningEventArgs_Ctors()
{
var ea = new ExtendedBuildWarningEventArgs();
ea = new ExtendedBuildWarningEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "Message", "HelpKeyword", "sender");
ea = new ExtendedBuildWarningEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "Message", "HelpKeyword", "sender", DateTime.Now);
ea = new ExtendedBuildWarningEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "sender", DateTime.Now, "Message");
ea = new ExtendedBuildWarningEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "sender", "HelpLink", DateTime.Now, "Message");
ea = new ExtendedBuildWarningEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null);
ea = new ExtendedBuildWarningEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, DateTime.Now);
ea = new ExtendedBuildWarningEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, null, DateTime.Now, null);
}
[Fact]
public void ExtendedBuildMessageEventArgs_Ctors()
{
var ea = new ExtendedBuildMessageEventArgs();
ea = new ExtendedBuildMessageEventArgs("type");
ea = new ExtendedBuildMessageEventArgs("type", "Message", "HelpKeyword", "sender", MessageImportance.High);
ea = new ExtendedBuildMessageEventArgs("type", "Message", "HelpKeyword", "sender", MessageImportance.High, DateTime.Now);
ea = new ExtendedBuildMessageEventArgs("type", "Message", "HelpKeyword", "sender", MessageImportance.High, DateTime.Now, "arg1");
ea = new ExtendedBuildMessageEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "Message", "HelpKeyword", "sender", MessageImportance.High);
ea = new ExtendedBuildMessageEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "Message", "HelpKeyword", "sender", MessageImportance.High, DateTime.Now);
ea = new ExtendedBuildMessageEventArgs("type", "Subcategory", "Code", "File", 1, 2, 3, 4, "{0}", "HelpKeyword", "sender", MessageImportance.High, DateTime.Now, "Message");
ea = new ExtendedBuildMessageEventArgs("type");
ea = new ExtendedBuildMessageEventArgs("type", null, null, null, default);
ea = new ExtendedBuildMessageEventArgs("type", null, null, null, default, DateTime.Now);
ea = new ExtendedBuildMessageEventArgs("type", null, null, null, default, default, null);
ea = new ExtendedBuildMessageEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, default);
ea = new ExtendedBuildMessageEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, default, DateTime.Now);
ea = new ExtendedBuildMessageEventArgs("type", null, null, null, 1, 2, 3, 4, null, null, null, default, DateTime.Now, null);
}
}

Просмотреть файл

@ -11,6 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.IO.Redist" Condition="'$(FeatureMSIORedist)' == 'true'" />
<PackageReference Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
<PackageReference Include="Shouldly" />

Просмотреть файл

@ -119,6 +119,26 @@ namespace Microsoft.Build.BackEnd
value = _reader.ReadBoolean();
}
/// <summary>
/// Translates an <see langword="bool"/> array.
/// </summary>
/// <param name="array">The array to be translated.</param>
public void Translate(ref bool[] array)
{
if (!TranslateNullable(array))
{
return;
}
int count = _reader.ReadInt32();
array = new bool[count];
for (int i = 0; i < count; i++)
{
array[i] = _reader.ReadBoolean();
}
}
/// <summary>
/// Translates a byte.
/// </summary>
@ -891,6 +911,26 @@ namespace Microsoft.Build.BackEnd
_writer.Write(value);
}
/// <summary>
/// Translates an <see langword="bool"/> array.
/// </summary>
/// <param name="array">The array to be translated.</param>
public void Translate(ref bool[] array)
{
if (!TranslateNullable(array))
{
return;
}
int count = array.Length;
_writer.Write(count);
for (int i = 0; i < count; i++)
{
_writer.Write(array[i]);
}
}
/// <summary>
/// Translates a byte.
/// </summary>

Просмотреть файл

@ -0,0 +1,126 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Shared;
namespace Microsoft.Build.Framework;
/// <summary>
/// Generic custom error events including extended data for event enriching.
/// Extended data are implemented by <see cref="IExtendedBuildEventArgs"/>
/// </summary>
public sealed class ExtendedBuildErrorEventArgs : BuildErrorEventArgs, IExtendedBuildEventArgs
{
/// <inheritdoc />
public string ExtendedType { get; set; }
/// <inheritdoc />
public Dictionary<string, string?>? ExtendedMetadata { get; set; }
/// <inheritdoc />
public string? ExtendedData { get; set; }
/// <summary>
/// Default constructor. Used for deserialization.
/// </summary>
internal ExtendedBuildErrorEventArgs() : this("undefined") { }
/// <summary>
/// This constructor specifies only type of extended data.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
public ExtendedBuildErrorEventArgs(string type) => ExtendedType = type;
/// <summary>
/// This constructor allows all event data to be initialized
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">message arguments</param>
public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp, params object[]? messageArgs)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp, messageArgs) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="helpLink">A link pointing to more information about the error </param>
/// <param name="senderName">name of event sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">message arguments</param>
public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, string? helpLink, DateTime eventTimestamp, params object[]? messageArgs)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, helpLink, eventTimestamp, messageArgs) => ExtendedType = type;
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteExtendedBuildEventData(this);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
reader.ReadExtendedBuildEventData(this);
}
}

Просмотреть файл

@ -0,0 +1,144 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Shared;
namespace Microsoft.Build.Framework;
/// <summary>
/// Generic custom build events including extended data for event enriching.
/// Extended data are implemented by <see cref="IExtendedBuildEventArgs"/>
/// </summary>
public sealed class ExtendedBuildMessageEventArgs : BuildMessageEventArgs, IExtendedBuildEventArgs
{
/// <inheritdoc />
public string ExtendedType { get; set; }
/// <inheritdoc />
public Dictionary<string, string?>? ExtendedMetadata { get; set; }
/// <inheritdoc />
public string? ExtendedData { get; set; }
/// <summary>
/// Default constructor. Used for deserialization.
/// </summary>
internal ExtendedBuildMessageEventArgs() : this("undefined") { }
/// <summary>
/// This constructor specifies only type of extended data.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
public ExtendedBuildMessageEventArgs(string type) => ExtendedType = type;
/// <summary>
/// This constructor allows all event data to be initialized
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="importance">importance of the message</param>
public ExtendedBuildMessageEventArgs(string type, string? message, string? helpKeyword, string? senderName, MessageImportance importance)
: base(message, helpKeyword, senderName, importance) => ExtendedType = type;
/// <summary>
/// This constructor allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="importance">importance of the message</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
public ExtendedBuildMessageEventArgs(string type, string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp)
: base(message, helpKeyword, senderName, importance, eventTimestamp) => ExtendedType = type;
/// <summary>
/// This constructor allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="importance">importance of the message</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">message arguments</param>
public ExtendedBuildMessageEventArgs(string type, string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp, params object[]? messageArgs)
: base(message, helpKeyword, senderName, importance, eventTimestamp, messageArgs) => ExtendedType = type;
/// <summary>
/// This constructor allows all event data to be initialized
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="importance">importance of the message</param>
public ExtendedBuildMessageEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, MessageImportance importance)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, importance) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="importance">importance of the message</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
public ExtendedBuildMessageEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, importance, eventTimestamp) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="importance">importance of the message</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">message arguments</param>
public ExtendedBuildMessageEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp, params object[]? messageArgs)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, importance, eventTimestamp, messageArgs) => ExtendedType = type;
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteExtendedBuildEventData(this);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
reader.ReadExtendedBuildEventData(this);
}
}

Просмотреть файл

@ -0,0 +1,126 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Shared;
namespace Microsoft.Build.Framework;
/// <summary>
/// Generic custom warning events including extended data for event enriching.
/// Extended data are implemented by <see cref="IExtendedBuildEventArgs"/>
/// </summary>
public sealed class ExtendedBuildWarningEventArgs : BuildWarningEventArgs, IExtendedBuildEventArgs
{
/// <inheritdoc />
public string ExtendedType { get; set; }
/// <inheritdoc />
public Dictionary<string, string?>? ExtendedMetadata { get; set; }
/// <inheritdoc />
public string? ExtendedData { get; set; }
/// <summary>
/// Default constructor. Used for deserialization.
/// </summary>
internal ExtendedBuildWarningEventArgs() : this("undefined") { }
/// <summary>
/// This constructor specifies only type of extended data.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
public ExtendedBuildWarningEventArgs(string type) => ExtendedType = type;
/// <summary>
/// This constructor allows all event data to be initialized
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of event sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">message arguments</param>
public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp, params object[]? messageArgs)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp, messageArgs) => ExtendedType = type;
/// <summary>
/// This constructor which allows a timestamp to be set
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="subcategory">event sub-category</param>
/// <param name="code">event code</param>
/// <param name="file">file associated with the event</param>
/// <param name="lineNumber">line number (0 if not applicable)</param>
/// <param name="columnNumber">column number (0 if not applicable)</param>
/// <param name="endLineNumber">end line number (0 if not applicable)</param>
/// <param name="endColumnNumber">end column number (0 if not applicable)</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="helpLink">A link pointing to more information about the error </param>
/// <param name="senderName">name of event sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">message arguments</param>
public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber,
string? message, string? helpKeyword, string? senderName, string? helpLink, DateTime eventTimestamp, params object[]? messageArgs)
: base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, helpLink, eventTimestamp, messageArgs) => ExtendedType = type;
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteExtendedBuildEventData(this);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
reader.ReadExtendedBuildEventData(this);
}
}

Просмотреть файл

@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Shared;
namespace Microsoft.Build.Framework;
/// <summary>
/// Generic custom event.
/// Extended data are implemented by <see cref="IExtendedBuildEventArgs"/>
/// </summary>
public sealed class ExtendedCustomBuildEventArgs : CustomBuildEventArgs, IExtendedBuildEventArgs
{
/// <inheritdoc />
public string ExtendedType { get; set; }
/// <inheritdoc />
public Dictionary<string, string?>? ExtendedMetadata { get; set; }
/// <inheritdoc />
public string? ExtendedData { get; set; }
/// <summary>
/// This constructor allows event data to be initialized.
/// </summary>
/// <seealso cref="IExtendedBuildEventArgs.ExtendedType"/>
internal ExtendedCustomBuildEventArgs() : this("undefined") {}
/// <summary>
/// This constructor allows event data to be initialized.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <seealso cref="IExtendedBuildEventArgs.ExtendedType"/>
public ExtendedCustomBuildEventArgs(string type) => ExtendedType = type;
/// <summary>
/// This constructor allows event data to be initialized.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of sender</param>
public ExtendedCustomBuildEventArgs(string type, string? message, string? helpKeyword, string? senderName) : base(message, helpKeyword, senderName) => ExtendedType = type;
/// <summary>
/// This constructor allows event data to be initialized including timestamp.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
public ExtendedCustomBuildEventArgs(string type, string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp) : base(message, helpKeyword, senderName, eventTimestamp) => ExtendedType = type;
/// <summary>
/// This constructor allows event data to be initialized including timestamp.
/// </summary>
/// <param name="type">Type of <see cref="IExtendedBuildEventArgs.ExtendedType"/>.</param>
/// <param name="message">text message</param>
/// <param name="helpKeyword">help keyword </param>
/// <param name="senderName">name of sender</param>
/// <param name="eventTimestamp">Timestamp when event was created</param>
/// <param name="messageArgs">Message arguments</param>
public ExtendedCustomBuildEventArgs(string type, string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp, params object[]? messageArgs) : base(message, helpKeyword, senderName, eventTimestamp, messageArgs) => ExtendedType = type;
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteExtendedBuildEventData(this);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
reader.ReadExtendedBuildEventData(this);
}
}

Просмотреть файл

@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using Microsoft.Build.Shared;
#nullable disable
@ -16,6 +18,8 @@ namespace Microsoft.Build.Framework
// immutable; adding new fields in the next version of the type
// without following certain special FX guidelines, can break both
// forward and backward compatibility
// NOTE: Although this class has been modified and do not longer relay on [Serializable]
// and BinaryFormatter. We have left it [Serializable] for backward compatibility reasons.
[Serializable]
public class ExternalProjectFinishedEventArgs : CustomBuildEventArgs
{
@ -93,5 +97,19 @@ namespace Microsoft.Build.Framework
return succeeded;
}
}
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteOptionalString(projectFile);
writer.Write(succeeded);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
projectFile = reader.ReadOptionalString();
succeeded = reader.ReadBoolean();
}
}
}

Просмотреть файл

@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using Microsoft.Build.Shared;
#nullable disable
@ -16,6 +18,8 @@ namespace Microsoft.Build.Framework
// immutable; adding new fields in the next version of the type
// without following certain special FX guidelines, can break both
// forward and backward compatibility
// NOTE: Although this class has been modified and do not longer relay on [Serializable]
// and BinaryFormatter. We have left it [Serializable] for backward compatibility reasons.
[Serializable]
public class ExternalProjectStartedEventArgs : CustomBuildEventArgs
{
@ -95,5 +99,19 @@ namespace Microsoft.Build.Framework
return targetNames;
}
}
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteOptionalString(projectFile);
writer.WriteOptionalString(targetNames);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
projectFile = reader.ReadOptionalString();
targetNames = reader.ReadOptionalString();
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
namespace Microsoft.Build.Framework;
/// <summary>
/// Interface for Extended EventArgs to allow enriching particular events with extended data.
/// Deriving from EventArgs will be deprecated soon and using Extended EventArgs is recommended for custom Event Args.
/// </summary>
public interface IExtendedBuildEventArgs
{
/// <summary>
/// Unique string identifying type of extended data so receiver side knows how to interpret, deserialize and handle <see cref="ExtendedData"/>.
/// </summary>
string ExtendedType { get; set; }
/// <summary>
/// Metadata of <see cref="ExtendedData"/>.
/// Example usage:
/// - data which needed in custom code to properly routing this message without interpreting/deserializing <see cref="ExtendedData"/>.
/// - simple extended data can be transferred in form of dictionary key-value per one extended property.
/// </summary>
Dictionary<string, string?>? ExtendedMetadata { get; set; }
/// <summary>
/// Transparent data as string.
/// Custom code is responsible to serialize and deserialize this string to structured data - if needed.
/// Custom code can use any serialization they deem safe - e.g. json for textual data, base64 for binary data...
/// </summary>
string? ExtendedData { get; set; }
}

Просмотреть файл

@ -107,6 +107,12 @@ namespace Microsoft.Build.BackEnd
/// <param name="value">The value to be translated.</param>
void Translate(ref bool value);
/// <summary>
/// Translates an <see langword="bool"/> array.
/// </summary>
/// <param name="array">The array to be translated.</param>
void Translate(ref bool[] array);
/// <summary>
/// Translates a byte.
/// </summary>

Просмотреть файл

@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using Microsoft.Build.Shared;
using System.IO;
#nullable disable
@ -27,5 +29,19 @@ namespace Microsoft.Build.Framework
this.metaprojectXml = metaprojectXml;
this.ProjectFile = metaprojectPath;
}
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteOptionalString(metaprojectXml);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
metaprojectXml = reader.ReadOptionalString();
}
}
}

Просмотреть файл

@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using Microsoft.Build.Shared;
#nullable disable
@ -56,5 +58,23 @@ namespace Microsoft.Build.Framework
/// The source of the property.
/// </summary>
public string PropertySource { get; set; }
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteOptionalString(PropertyName);
writer.WriteOptionalString(PropertyValue);
writer.WriteOptionalString(PropertySource);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
PropertyName = reader.ReadOptionalString();
PropertyValue = reader.ReadOptionalString();
PropertySource = reader.ReadOptionalString();
}
}
}

Просмотреть файл

@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using Microsoft.Build.Shared;
#nullable disable
@ -79,5 +81,25 @@ namespace Microsoft.Build.Framework
return RawMessage;
}
}
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteOptionalString(PropertyName);
writer.WriteOptionalString(NewValue);
writer.WriteOptionalString(PreviousValue);
writer.WriteOptionalString(Location);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
PropertyName = reader.ReadOptionalString();
NewValue = reader.ReadOptionalString();
PreviousValue = reader.ReadOptionalString();
Location = reader.ReadOptionalString();
}
}
}

Просмотреть файл

@ -372,6 +372,29 @@ namespace Microsoft.Build.Framework
}
}
/// <summary>
/// Allows displaying the deprecation warning for BinaryFormatter in your current environment.
/// </summary>
public bool EnableWarningOnCustomBuildEvent
{
get
{
var value = Environment.GetEnvironmentVariable("MSBUILDCUSTOMBUILDEVENTWARNING");
if (value == null)
{
// If variable is not set explicitly, for .NETCORE warning appears.
#if RUNTIME_TYPE_NETCORE
return true;
#else
return false;
#endif
}
return value == "1";
}
}
private static bool? ParseNullableBoolFromEnvironmentVariable(string environmentVariable)
{
var value = Environment.GetEnvironmentVariable(environmentVariable);

Просмотреть файл

@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using Microsoft.Build.Shared;
#nullable disable
@ -42,5 +44,18 @@ namespace Microsoft.Build.Framework
/// The name of the uninitialized property that was read.
/// </summary>
public string PropertyName { get; set; }
internal override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
writer.WriteOptionalString(PropertyName);
}
internal override void CreateFromStream(BinaryReader reader, int version)
{
base.CreateFromStream(reader, version);
PropertyName = reader.ReadOptionalString();
}
}
}

Просмотреть файл

@ -247,7 +247,7 @@
<!-- Xsds are not TF or arch-specific so copy once them in the outer build -->
<Target Name="CopyXsds" BeforeTargets="Build" Condition="'$(IsInnerBuild)' != 'true'">
<Copy SourceFiles="@(XsdsForVS)" DestinationFiles="@(XsdsForVS-&gt;'$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'xsd'))%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" />
<Copy SourceFiles="@(XsdsForVS)" DestinationFiles="@(XsdsForVS-&gt;'$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'xsd'))%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
</Target>
<!-- Include MSBuild.deps.json and MSBuild.runtimeconfig.json in ContentWithTargetPath so they will be copied to the output folder of projects

Просмотреть файл

@ -1179,7 +1179,9 @@ namespace Microsoft.Build.CommandLine
if (_nodeEndpoint?.LinkStatus == LinkStatus.Active)
{
#pragma warning disable SYSLIB0050
if (!e.GetType().GetTypeInfo().IsSerializable)
// Types which are not serializable and are not IExtendedBuildEventArgs as
// those always implement custom serialization by WriteToStream and CreateFromStream.
if (!e.GetType().GetTypeInfo().IsSerializable && e is not IExtendedBuildEventArgs)
#pragma warning disable SYSLIB0050
{
// log a warning and bail. This will end up re-calling SendBuildEvent, but we know for a fact
@ -1188,7 +1190,8 @@ namespace Microsoft.Build.CommandLine
return;
}
_nodeEndpoint.SendData(new LogMessagePacket(new KeyValuePair<int, BuildEventArgs>(_currentConfiguration.NodeId, e)));
LogMessagePacket logMessage = new LogMessagePacket(new KeyValuePair<int, BuildEventArgs>(_currentConfiguration.NodeId, e));
_nodeEndpoint.SendData(logMessage);
}
}

2
src/MSBuild/Resources/xlf/Strings.cs.xlf сгенерированный
Просмотреть файл

@ -1413,7 +1413,7 @@ Když se nastaví na MessageUponIsolationViolation (nebo jeho krátký
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: Název {0} obsahuje neplatný znak {1}.</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.de.xlf сгенерированный
Просмотреть файл

@ -1400,7 +1400,7 @@ Dieses Protokollierungsformat ist standardmäßig aktiviert.
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: Der Name "{0}" enthält ein ungültiges Zeichen "{1}".</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.es.xlf сгенерированный
Просмотреть файл

@ -1407,7 +1407,7 @@
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: El nombre "{0}" contiene un carácter no válido "{1}".</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.fr.xlf сгенерированный
Просмотреть файл

@ -1400,7 +1400,7 @@ Remarque : verbosité des enregistreurs dévénements de fichiers
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: le nom «{0}» contient un caractère non valide «{1}».</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.ko.xlf сгенерированный
Просмотреть файл

@ -1400,7 +1400,7 @@
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: "{0}" 이름에 잘못된 문자 "{1}"이(가) 사용되었습니다.</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.pt-BR.xlf сгенерированный
Просмотреть файл

@ -1401,7 +1401,7 @@ arquivo de resposta.
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: O nome "{0}" contém um caractere inválido "{1}".</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.tr.xlf сгенерированный
Просмотреть файл

@ -1404,7 +1404,7 @@
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: "{0}" adı "{1}" geçersiz karakterini içeriyor.</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

2
src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf сгенерированный
Просмотреть файл

@ -1400,7 +1400,7 @@
</trans-unit>
<trans-unit id="NameInvalid">
<source>MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</source>
<target state="new">MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}".</target>
<target state="translated">MSBUILD : error MSB5016: 名称“{0}”包含无效字符“{1}”。</target>
<note>
{StrBegin="MSBUILD : error MSB5016: "}
</note>

Просмотреть файл

@ -61,6 +61,9 @@
<Compile Include="..\Framework\ITaskItem2.cs">
<Link>ITaskItem2.cs</Link>
</Compile>
<Compile Include="..\Framework\IExtendedBuildEventArgs.cs">
<Link>IExtendedBuildEventArgs.cs</Link>
</Compile>
<Compile Include="..\Framework\AssemblyUtilities.cs" />
<Compile Include="..\Framework\ResponseFileUsedEventArgs.cs" />
<Compile Include="..\Shared\BufferedReadStream.cs" />

Просмотреть файл

@ -18,6 +18,6 @@
<!-- This is only needed for a test in the MSBuild repo; it is unrelated to the PortableTask sample itself. -->
<Target Name="CopyMSBuildUtilitiesToNewFolder" BeforeTargets="CopyFilesToOutputDirectory">
<Copy SourceFiles="$(PkgMicrosoft_Build_Utilities_Core)\lib\net46\Microsoft.Build.Utilities.Core.dll" DestinationFiles="$(OutDir)\OldMSBuild\Microsoft.Build.Utilities.Core.dll" />
<Copy SourceFiles="$(PkgMicrosoft_Build_Utilities_Core)\lib\net46\Microsoft.Build.Utilities.Core.dll" DestinationFiles="$(OutDir)\OldMSBuild\Microsoft.Build.Utilities.Core.dll" SkipUnchangedFiles="true"/>
</Target>
</Project>

Просмотреть файл

@ -99,5 +99,30 @@ namespace Microsoft.Build.Shared
{
return new Guid(reader.ReadBytes(sizeof(Guid)));
}
public static void ReadExtendedBuildEventData(this BinaryReader reader, IExtendedBuildEventArgs data)
{
data.ExtendedType = reader.ReadString();
data.ExtendedData = reader.ReadOptionalString();
bool haveMetadata = reader.ReadBoolean();
if (haveMetadata)
{
data.ExtendedMetadata = new();
int count = reader.Read7BitEncodedInt();
for (int i = 0; i < count; i++)
{
string key = reader.ReadString();
string? value = reader.ReadOptionalString();
data.ExtendedMetadata.Add(key, value);
}
}
else
{
data.ExtendedMetadata = null;
}
}
}
}

Просмотреть файл

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.Build.Framework;
@ -111,5 +112,22 @@ namespace Microsoft.Build.Shared
}
}
}
public static void WriteExtendedBuildEventData(this BinaryWriter writer, IExtendedBuildEventArgs data)
{
writer.Write(data.ExtendedType);
writer.WriteOptionalString(data.ExtendedData);
writer.Write(data.ExtendedMetadata != null);
if (data.ExtendedMetadata != null)
{
writer.Write7BitEncodedInt(data.ExtendedMetadata.Count);
foreach (KeyValuePair<string, string?> kvp in data.ExtendedMetadata)
{
writer.Write(kvp.Key);
writer.WriteOptionalString(kvp.Value);
}
}
}
}
}

Просмотреть файл

@ -145,6 +145,61 @@ namespace Microsoft.Build.Shared
/// Event is an AssemblyLoadBuildEventArgs
/// </summary>
AssemblyLoadEvent = 21,
/// <summary>
/// Event is <see cref="ExternalProjectStartedEventArgs"/>
/// </summary>
ExternalProjectStartedEvent = 22,
/// <summary>
/// Event is <see cref="ExternalProjectFinishedEventArgs"/>
/// </summary>
ExternalProjectFinishedEvent = 23,
/// <summary>
/// Event is <see cref="ExtendedCustomBuildEventArgs"/>
/// </summary>
ExtendedCustomEvent = 24,
/// <summary>
/// Event is <see cref="ExtendedBuildErrorEventArgs"/>
/// </summary>
ExtendedBuildErrorEvent = 25,
/// <summary>
/// Event is <see cref="ExtendedBuildWarningEventArgs"/>
/// </summary>
ExtendedBuildWarningEvent = 26,
/// <summary>
/// Event is <see cref="ExtendedBuildMessageEventArgs"/>
/// </summary>
ExtendedBuildMessageEvent = 27,
/// <summary>
/// Event is <see cref="CriticalBuildMessageEventArgs"/>
/// </summary>
CriticalBuildMessage = 28,
/// <summary>
/// Event is <see cref="MetaprojectGeneratedEventArgs"/>
/// </summary>
MetaprojectGenerated = 29,
/// <summary>
/// Event is <see cref="PropertyInitialValueSetEventArgs"/>
/// </summary>
PropertyInitialValueSet = 30,
/// <summary>
/// Event is <see cref="PropertyReassignmentEventArgs"/>
/// </summary>
PropertyReassignment = 31,
/// <summary>
/// Event is <see cref="UninitializedPropertyReadEventArgs"/>
/// </summary>
UninitializedPropertyRead = 32
}
#endregion
@ -230,8 +285,9 @@ namespace Microsoft.Build.Shared
/// <summary>
/// Constructor for deserialization
/// </summary>
protected LogMessagePacketBase(ITranslator translator)
protected LogMessagePacketBase(ITranslator translator, TargetFinishedTranslator targetFinishedTranslator = null)
{
_targetFinishedTranslator = targetFinishedTranslator;
Translate(translator);
}
@ -339,7 +395,8 @@ namespace Microsoft.Build.Shared
#if !TASKHOST && !MSBUILDENTRYPOINTEXE
if (_buildEvent is ProjectEvaluationStartedEventArgs
or ProjectEvaluationFinishedEventArgs
or EnvironmentVariableReadEventArgs)
or EnvironmentVariableReadEventArgs
or ResponseFileUsedEventArgs)
{
// switch to serialization methods that we provide in this file
// and don't use the WriteToStream inherited from LazyFormattedBuildEventArgs
@ -536,6 +593,17 @@ namespace Microsoft.Build.Shared
LoggingEventType.ProjectImportedEvent => new ProjectImportedEventArgs(),
LoggingEventType.TargetSkipped => new TargetSkippedEventArgs(),
LoggingEventType.Telemetry => new TelemetryEventArgs(),
LoggingEventType.ExtendedCustomEvent => new ExtendedCustomBuildEventArgs(),
LoggingEventType.ExtendedBuildErrorEvent => new ExtendedBuildErrorEventArgs(),
LoggingEventType.ExtendedBuildWarningEvent => new ExtendedBuildWarningEventArgs(),
LoggingEventType.ExtendedBuildMessageEvent => new ExtendedBuildMessageEventArgs(),
LoggingEventType.ExternalProjectStartedEvent => new ExternalProjectStartedEventArgs(null, null, null, null, null),
LoggingEventType.ExternalProjectFinishedEvent => new ExternalProjectFinishedEventArgs(null, null, null, null, false),
LoggingEventType.CriticalBuildMessage => new CriticalBuildMessageEventArgs(null, null, null, -1, -1, -1, -1, null, null, null),
LoggingEventType.MetaprojectGenerated => new MetaprojectGeneratedEventArgs(null, null, null),
LoggingEventType.PropertyInitialValueSet => new PropertyInitialValueSetEventArgs(),
LoggingEventType.PropertyReassignment => new PropertyReassignmentEventArgs(),
LoggingEventType.UninitializedPropertyRead => new UninitializedPropertyReadEventArgs(),
#endif
_ => throw new InternalErrorException("Should not get to the default of GetBuildEventArgFromId ID: " + _eventType)
};
@ -573,6 +641,15 @@ namespace Microsoft.Build.Shared
{
return LoggingEventType.ProjectStartedEvent;
}
else if (eventType == typeof(ExternalProjectStartedEventArgs))
{
return LoggingEventType.ExternalProjectStartedEvent;
}
else if (eventType == typeof(ExternalProjectFinishedEventArgs))
{
return LoggingEventType.ExternalProjectFinishedEvent;
}
#if !TASKHOST
else if (eventType == typeof(ProjectEvaluationFinishedEventArgs))
{
@ -598,6 +675,42 @@ namespace Microsoft.Build.Shared
{
return LoggingEventType.AssemblyLoadEvent;
}
else if (eventType == typeof(ExtendedCustomBuildEventArgs))
{
return LoggingEventType.ExtendedCustomEvent;
}
else if (eventType == typeof(ExtendedBuildErrorEventArgs))
{
return LoggingEventType.ExtendedBuildErrorEvent;
}
else if (eventType == typeof(ExtendedBuildWarningEventArgs))
{
return LoggingEventType.ExtendedBuildWarningEvent;
}
else if (eventType == typeof(ExtendedBuildMessageEventArgs))
{
return LoggingEventType.ExtendedBuildMessageEvent;
}
else if (eventType == typeof(CriticalBuildMessageEventArgs))
{
return LoggingEventType.CriticalBuildMessage;
}
else if (eventType == typeof(MetaprojectGeneratedEventArgs))
{
return LoggingEventType.MetaprojectGenerated;
}
else if (eventType == typeof(PropertyInitialValueSetEventArgs))
{
return LoggingEventType.PropertyInitialValueSet;
}
else if (eventType == typeof(PropertyReassignmentEventArgs))
{
return LoggingEventType.PropertyReassignment;
}
else if (eventType == typeof(UninitializedPropertyReadEventArgs))
{
return LoggingEventType.UninitializedPropertyRead;
}
#endif
else if (eventType == typeof(TargetStartedEventArgs))
{
@ -690,12 +803,6 @@ namespace Microsoft.Build.Shared
case LoggingEventType.BuildWarningEvent:
WriteBuildWarningEventToStream((BuildWarningEventArgs)buildEvent, translator);
break;
case LoggingEventType.ProjectStartedEvent:
WriteExternalProjectStartedEventToStream((ExternalProjectStartedEventArgs)buildEvent, translator);
break;
case LoggingEventType.ProjectFinishedEvent:
WriteExternalProjectFinishedEventToStream((ExternalProjectFinishedEventArgs)buildEvent, translator);
break;
case LoggingEventType.EnvironmentVariableReadEvent:
WriteEnvironmentVariableReadEventArgs((EnvironmentVariableReadEventArgs)buildEvent, translator);
break;
@ -712,37 +819,20 @@ namespace Microsoft.Build.Shared
private void WriteEnvironmentVariableReadEventArgs(EnvironmentVariableReadEventArgs environmentVariableReadEventArgs, ITranslator translator)
{
string name = environmentVariableReadEventArgs.EnvironmentVariableName;
MessageImportance importance = environmentVariableReadEventArgs.Importance;
translator.Translate(ref name);
BuildEventContext context = environmentVariableReadEventArgs.BuildEventContext;
translator.TranslateEnum(ref importance, (int)importance);
#if !CLR2COMPATIBILITY
DateTime timestamp = environmentVariableReadEventArgs.RawTimestamp;
BuildEventContext context = environmentVariableReadEventArgs.BuildEventContext;
translator.Translate(ref timestamp);
translator.Translate(ref context);
#endif
}
/// <summary>
/// Serialize ExternalProjectFinished Event Argument to the stream
/// </summary>
private void WriteExternalProjectFinishedEventToStream(ExternalProjectFinishedEventArgs externalProjectFinishedEventArgs, ITranslator translator)
{
string projectFile = externalProjectFinishedEventArgs.ProjectFile;
translator.Translate(ref projectFile);
bool succeeded = externalProjectFinishedEventArgs.Succeeded;
translator.Translate(ref succeeded);
}
/// <summary>
/// ExternalProjectStartedEvent
/// </summary>
private void WriteExternalProjectStartedEventToStream(ExternalProjectStartedEventArgs externalProjectStartedEventArgs, ITranslator translator)
{
string projectFile = externalProjectStartedEventArgs.ProjectFile;
translator.Translate(ref projectFile);
string targetNames = externalProjectStartedEventArgs.TargetNames;
translator.Translate(ref targetNames);
}
#region Writes to Stream
/// <summary>
@ -829,7 +919,13 @@ namespace Microsoft.Build.Shared
private void WriteResponseFileUsedEventToStream(ResponseFileUsedEventArgs responseFileUsedEventArgs, ITranslator translator)
{
string filePath = responseFileUsedEventArgs.ResponseFilePath;
translator.Translate(ref filePath);
#if !CLR2COMPATIBILITY
DateTime timestamp = responseFileUsedEventArgs.RawTimestamp;
translator.Translate(ref timestamp);
#endif
}
#if !TASKHOST && !MSBUILDENTRYPOINTEXE
@ -1066,8 +1162,6 @@ namespace Microsoft.Build.Shared
{
LoggingEventType.TaskCommandLineEvent => ReadTaskCommandLineEventFromStream(translator, message, helpKeyword, senderName),
LoggingEventType.BuildErrorEvent => ReadTaskBuildErrorEventFromStream(translator, message, helpKeyword, senderName),
LoggingEventType.ProjectStartedEvent => ReadExternalProjectStartedEventFromStream(translator, message, helpKeyword, senderName),
LoggingEventType.ProjectFinishedEvent => ReadExternalProjectFinishedEventFromStream(translator, message, helpKeyword, senderName),
LoggingEventType.BuildMessageEvent => ReadBuildMessageEventFromStream(translator, message, helpKeyword, senderName),
LoggingEventType.ResponseFileUsedEvent => ReadResponseFileUsedEventFromStream(translator, message, helpKeyword, senderName),
LoggingEventType.BuildWarningEvent => ReadBuildWarningEventFromStream(translator, message, helpKeyword, senderName),
@ -1082,60 +1176,24 @@ namespace Microsoft.Build.Shared
private EnvironmentVariableReadEventArgs ReadEnvironmentVariableReadEventFromStream(ITranslator translator, string message, string helpKeyword, string senderName)
{
string environmentVariableName = null;
MessageImportance importance = default;
translator.Translate(ref environmentVariableName);
BuildEventContext context = null;
translator.TranslateEnum(ref importance, (int)importance);
EnvironmentVariableReadEventArgs args = new(environmentVariableName, message, helpKeyword, senderName, importance);
#if !CLR2COMPATIBILITY
DateTime timestamp = default;
BuildEventContext context = null;
translator.Translate(ref timestamp);
translator.Translate(ref context);
#endif
EnvironmentVariableReadEventArgs args = new(environmentVariableName, message);
args.RawTimestamp = timestamp;
args.BuildEventContext = context;
#endif
return args;
}
/// <summary>
/// Read and reconstruct a ProjectFinishedEventArgs from the stream
/// </summary>
private ExternalProjectFinishedEventArgs ReadExternalProjectFinishedEventFromStream(ITranslator translator, string message, string helpKeyword, string senderName)
{
string projectFile = null;
translator.Translate(ref projectFile);
bool succeeded = true;
translator.Translate(ref succeeded);
ExternalProjectFinishedEventArgs buildEvent =
new ExternalProjectFinishedEventArgs(
message,
helpKeyword,
senderName,
projectFile,
succeeded);
return buildEvent;
}
/// <summary>
/// Read and reconstruct a ProjectStartedEventArgs from the stream
/// </summary>
private ExternalProjectStartedEventArgs ReadExternalProjectStartedEventFromStream(ITranslator translator, string message, string helpKeyword, string senderName)
{
string projectFile = null;
translator.Translate(ref projectFile);
string targetNames = null;
translator.Translate(ref targetNames);
ExternalProjectStartedEventArgs buildEvent =
new ExternalProjectStartedEventArgs(
message,
helpKeyword,
senderName,
projectFile,
targetNames);
return buildEvent;
}
/// <summary>
/// Read and reconstruct a BuildWarningEventArgs from the stream
/// </summary>
@ -1256,6 +1314,14 @@ namespace Microsoft.Build.Shared
string responseFilePath = String.Empty;
translator.Translate(ref responseFilePath);
ResponseFileUsedEventArgs buildEvent = new ResponseFileUsedEventArgs(responseFilePath);
#if !CLR2COMPATIBILITY
DateTime timestamp = default;
translator.Translate(ref timestamp);
buildEvent.RawTimestamp = timestamp;
#endif
return buildEvent;
}

Просмотреть файл

@ -66,6 +66,9 @@
<data name="ExpectedEventToBeSerializable" Visibility="Public">
<value>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</value>
</data>
<data name="DeprecatedEventSerialization" Visibility="Public">
<value>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</value>
</data>
<data name="FileLocation" Visibility="Public">
<value>{0} ({1},{2})</value>
<comment>A file location to be embedded in a string.</comment>

5
src/Shared/Resources/xlf/Strings.shared.cs.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: Bylo nalezeno konfliktní sestavení pro sestavení úlohy {0} v umístění {1}.</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Očekávalo se, že typ události {0} bude možné serializovat pomocí serializátoru .NET. Událost nebylo možné serializovat a byla ignorována.</target>

5
src/Shared/Resources/xlf/Strings.shared.de.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: Eine mit der Aufgabenassembly "{0}" in Konflikt stehende Assembly wurde in "{1}" gefunden.</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Es wurde erwartet, dass der Ereignistyp "{0}" mithilfe des .NET-Serialisierers serialisierbar ist. Das Ereignis war nicht serialisierbar und wurde ignoriert.</target>

5
src/Shared/Resources/xlf/Strings.shared.es.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: Se detectó un ensamblado conflictivo para el ensamblado de tarea "{0}" en "{1}".</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Se esperaba que el tipo de evento "{0}" fuera serializable con el serializador .NET. El evento no era serializable y se ha omitido.</target>

5
src/Shared/Resources/xlf/Strings.shared.fr.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: un assembly en conflit avec l'assembly de tâche "{0}" a été trouvé sur "{1}".</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Le type d'événement "{0}" devait être sérialisable à l'aide du sérialiseur .NET. L'événement n'était pas sérialisable et a été ignoré.</target>

5
src/Shared/Resources/xlf/Strings.shared.it.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: rilevato un assembly in conflitto per l'assembly dell'attività "{0}" in "{1}".</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">È previsto un tipo di evento "{0}" serializzabile con il serializzatore .NET. L'evento non era serializzabile ed è stato ignorato.</target>

5
src/Shared/Resources/xlf/Strings.shared.ja.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: タスク アセンブリ "{0}" に対して競合しているアセンブリが "{1}" で見つかりました。</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">イベントの種類 "{0}" は .NET シリアライザーを使用してシリアル化可能であることが想定されていましたが、シリアル化可能でなかったため無視されました。</target>

5
src/Shared/Resources/xlf/Strings.shared.ko.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: 작업 어셈블리 "{0}"과(와) 충돌하는 어셈블리가 "{1}"에 있습니다.</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">이벤트 유형 "{0}"은(는) .NET serializer를 사용하여 serialize할 수 있어야 합니다. 이 이벤트는 serialize할 수 없으므로 무시되었습니다.</target>

5
src/Shared/Resources/xlf/Strings.shared.pl.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: Zestaw, który wywołuje konflikt z zestawem zadania „{0}”, został znaleziony w „{1}”.</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Oczekiwano, że zdarzenie typu „{0}” będzie uszeregowane przy użyciu serializatora platformy .NET. Zdarzenie nie może podlegać szeregowaniu, dlatego zostało zignorowane.</target>

5
src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: Foi encontrado um assembly conflitante no assembly da tarefa "{0}" em "{1}".</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Era esperado que o tipo de evento "{0}" fosse serializável usando o serializador .NET. O evento não era serializável e foi ignorado.</target>

5
src/Shared/Resources/xlf/Strings.shared.ru.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: в "{1}" обнаружена сборка, конфликтующая со сборкой задачи "{0}".</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">Необходимо, чтобы тип события "{0}" был сериализуемым с помощью сериализатора .NET. Событие не было сериализуемым и было пропущено.</target>

5
src/Shared/Resources/xlf/Strings.shared.tr.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: "{0}" görev derlemesi için "{1}" konumunda çakışan derleme bulundu.</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">"{0}" olay türünün .NET serileştiricisi kullanılarak serileştirilebilir olması bekleniyordu. Olay serileştirilebilir değildi ve yoksayıldı.</target>

5
src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: 在“{1}”处发现了与任务程序集“{0}”冲突的程序集。</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">事件类型“{0}”应可以使用 .NET 序列化程序进行序列化。此事件不可序列化,已忽略它。</target>

5
src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf сгенерированный
Просмотреть файл

@ -22,6 +22,11 @@
<target state="translated">MSB4008: 已在 "{1}" 中發現工作組件 "{0}" 的衝突組件。</target>
<note>{StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly.</note>
</trans-unit>
<trans-unit id="DeprecatedEventSerialization">
<source>Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</source>
<target state="new">Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs</target>
<note />
</trans-unit>
<trans-unit id="ExpectedEventToBeSerializable">
<source>Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored.</source>
<target state="translated">事件類型 "{0}" 應該可以使用 .NET 序列化程式序列化。此事件不可序列化,已略過。</target>

Просмотреть файл

@ -24,47 +24,37 @@ namespace Microsoft.Build.BackEnd
internal enum TaskParameterType
{
/// <summary>
/// Parameter is null
/// Parameter is null.
/// </summary>
Null,
/// <summary>
/// Parameter is a string
/// Parameter is of a type described by a <see cref="TypeCode"/>.
/// </summary>
String,
PrimitiveType,
/// <summary>
/// Parameter is an array of strings
/// Parameter is an array of a type described by a <see cref="TypeCode"/>.
/// </summary>
StringArray,
PrimitiveTypeArray,
/// <summary>
/// Parameter is <c>true</c> or <c>false</c>.
/// </summary>
Bool,
/// <summary>
/// Parameter is an <see langword="int"/>.
/// </summary>
Int,
/// <summary>
/// Parameter is a value type. Note: Must be serializable
/// Parameter is a value type. Note: Must be <see cref="IConvertible"/>.
/// </summary>
ValueType,
/// <summary>
/// Parameter is an array of value types. Note: Must be serializable.
/// Parameter is an array of value types. Note: Must be <see cref="IConvertible"/>.
/// </summary>
ValueTypeArray,
/// <summary>
/// Parameter is an ITaskItem
/// Parameter is an ITaskItem.
/// </summary>
ITaskItem,
/// <summary>
/// Parameter is an array of ITaskItems
/// Parameter is an array of ITaskItems.
/// </summary>
ITaskItemArray,
@ -72,7 +62,7 @@ namespace Microsoft.Build.BackEnd
/// An invalid parameter -- the value of this parameter contains the exception
/// that is thrown when trying to access it.
/// </summary>
Invalid
Invalid,
}
/// <summary>
@ -86,10 +76,15 @@ namespace Microsoft.Build.BackEnd
ITranslatable
{
/// <summary>
/// The TaskParameterType of the wrapped parameter
/// The TaskParameterType of the wrapped parameter.
/// </summary>
private TaskParameterType _parameterType;
/// <summary>
/// The <see cref="TypeCode"/> of the wrapped parameter if it's a primitive type.
/// </summary>
private TypeCode _parameterTypeCode;
/// <summary>
/// The actual task parameter that we're wrapping
/// </summary>
@ -124,9 +119,11 @@ namespace Microsoft.Build.BackEnd
if (wrappedParameterType.IsArray)
{
if (wrappedParameterType == typeof(string[]))
TypeCode typeCode = Type.GetTypeCode(wrappedParameterType.GetElementType());
if (typeCode != TypeCode.Object && typeCode != TypeCode.DBNull)
{
_parameterType = TaskParameterType.StringArray;
_parameterType = TaskParameterType.PrimitiveTypeArray;
_parameterTypeCode = typeCode;
_wrappedParameter = wrappedParameter;
}
else if (typeof(ITaskItem[]).GetTypeInfo().IsAssignableFrom(wrappedParameterType.GetTypeInfo()))
@ -158,9 +155,21 @@ namespace Microsoft.Build.BackEnd
else
{
// scalar parameter
if (wrappedParameterType == typeof(string))
// Preserve enums as strings: the enum type itself may not
// be loaded on the other side of the serialization, but
// we would convert to string anyway after pulling the
// task output into a property or item.
if (wrappedParameterType.IsEnum)
{
_parameterType = TaskParameterType.String;
wrappedParameter = (string)Convert.ChangeType(wrappedParameter, typeof(string), CultureInfo.InvariantCulture);
wrappedParameterType = typeof(string);
}
TypeCode typeCode = Type.GetTypeCode(wrappedParameterType);
if (typeCode != TypeCode.Object && typeCode != TypeCode.DBNull)
{
_parameterType = TaskParameterType.PrimitiveType;
_parameterTypeCode = typeCode;
_wrappedParameter = wrappedParameter;
}
else if (typeof(ITaskItem).IsAssignableFrom(wrappedParameterType))
@ -168,28 +177,6 @@ namespace Microsoft.Build.BackEnd
_parameterType = TaskParameterType.ITaskItem;
_wrappedParameter = CreateNewTaskItemFrom((ITaskItem)wrappedParameter);
}
// Preserve enums as strings: the enum type itself may not
// be loaded on the other side of the serialization, but
// we would convert to string anyway after pulling the
// task output into a property or item.
else if (wrappedParameterType.IsEnum)
{
_parameterType = TaskParameterType.String;
_wrappedParameter = (string)Convert.ChangeType(wrappedParameter, typeof(string), CultureInfo.InvariantCulture);
}
// Also stringify known common value types, to avoid calling
// TranslateDotNet when they'll just be stringified on the
// output side
else if (wrappedParameterType == typeof(bool))
{
_parameterType = TaskParameterType.Bool;
_wrappedParameter = wrappedParameter;
}
else if (wrappedParameterType == typeof(int))
{
_parameterType = TaskParameterType.Int;
_wrappedParameter = wrappedParameter;
}
else if (wrappedParameterType.GetTypeInfo().IsValueType)
{
_parameterType = TaskParameterType.ValueType;
@ -203,31 +190,26 @@ namespace Microsoft.Build.BackEnd
}
/// <summary>
/// Constructor for deserialization
/// Constructor for deserialization.
/// </summary>
private TaskParameter()
{
}
/// <summary>
/// The TaskParameterType of the wrapped parameter
/// The TaskParameterType of the wrapped parameter.
/// </summary>
public TaskParameterType ParameterType
{
[DebuggerStepThrough]
get
{ return _parameterType; }
}
public TaskParameterType ParameterType => _parameterType;
/// <summary>
/// The actual task parameter that we're wrapping
/// The <see cref="TypeCode"/> of the wrapper parameter if it's a primitive or array of primitives.
/// </summary>
public object WrappedParameter
{
[DebuggerStepThrough]
get
{ return _wrappedParameter; }
}
public TypeCode ParameterTypeCode => _parameterTypeCode;
/// <summary>
/// The actual task parameter that we're wrapping.
/// </summary>
public object WrappedParameter => _wrappedParameter;
/// <summary>
/// TaskParameter's ToString should just pass through to whatever it's wrapping.
@ -242,44 +224,38 @@ namespace Microsoft.Build.BackEnd
/// </summary>
public void Translate(ITranslator translator)
{
translator.TranslateEnum<TaskParameterType>(ref _parameterType, (int)_parameterType);
translator.TranslateEnum(ref _parameterType, (int)_parameterType);
switch (_parameterType)
{
case TaskParameterType.Null:
_wrappedParameter = null;
break;
case TaskParameterType.String:
string stringParam = (string)_wrappedParameter;
translator.Translate(ref stringParam);
_wrappedParameter = stringParam;
case TaskParameterType.PrimitiveType:
TranslatePrimitiveType(translator);
break;
case TaskParameterType.StringArray:
string[] stringArrayParam = (string[])_wrappedParameter;
translator.Translate(ref stringArrayParam);
_wrappedParameter = stringArrayParam;
break;
case TaskParameterType.Bool:
bool boolParam = _wrappedParameter switch
{
bool hadValue => hadValue,
_ => default,
};
translator.Translate(ref boolParam);
_wrappedParameter = boolParam;
break;
case TaskParameterType.Int:
int intParam = _wrappedParameter switch
{
int hadValue => hadValue,
_ => default,
};
translator.Translate(ref intParam);
_wrappedParameter = intParam;
case TaskParameterType.PrimitiveTypeArray:
TranslatePrimitiveTypeArray(translator);
break;
case TaskParameterType.ValueType:
if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_8))
{
TranslateValueType(translator);
}
else
{
translator.TranslateDotNet(ref _wrappedParameter);
}
break;
case TaskParameterType.ValueTypeArray:
translator.TranslateDotNet(ref _wrappedParameter);
if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_8))
{
TranslateValueTypeArray(translator);
}
else
{
translator.TranslateDotNet(ref _wrappedParameter);
}
break;
case TaskParameterType.ITaskItem:
TranslateITaskItem(translator);
@ -537,6 +513,223 @@ namespace Microsoft.Build.BackEnd
return haveRef;
}
/// <summary>
/// Serializes or deserializes a primitive type value wrapped by this <see cref="TaskParameter"/>.
/// </summary>
private void TranslatePrimitiveType(ITranslator translator)
{
translator.TranslateEnum(ref _parameterTypeCode, (int)_parameterTypeCode);
switch (_parameterTypeCode)
{
case TypeCode.Boolean:
bool boolParam = _wrappedParameter is bool wrappedBool ? wrappedBool : default;
translator.Translate(ref boolParam);
_wrappedParameter = boolParam;
break;
case TypeCode.Byte:
byte byteParam = _wrappedParameter is byte wrappedByte ? wrappedByte : default;
translator.Translate(ref byteParam);
_wrappedParameter = byteParam;
break;
case TypeCode.Int16:
short shortParam = _wrappedParameter is short wrappedShort ? wrappedShort : default;
translator.Translate(ref shortParam);
_wrappedParameter = shortParam;
break;
case TypeCode.UInt16:
ushort ushortParam = _wrappedParameter is ushort wrappedUShort ? wrappedUShort : default;
translator.Translate(ref ushortParam);
_wrappedParameter = ushortParam;
break;
case TypeCode.Int64:
long longParam = _wrappedParameter is long wrappedLong ? wrappedLong : default;
translator.Translate(ref longParam);
_wrappedParameter = longParam;
break;
case TypeCode.Double:
double doubleParam = _wrappedParameter is double wrappedDouble ? wrappedDouble : default;
translator.Translate(ref doubleParam);
_wrappedParameter = doubleParam;
break;
case TypeCode.String:
string stringParam = (string)_wrappedParameter;
translator.Translate(ref stringParam);
_wrappedParameter = stringParam;
break;
case TypeCode.DateTime:
DateTime dateTimeParam = _wrappedParameter is DateTime wrappedDateTime ? wrappedDateTime : default;
translator.Translate(ref dateTimeParam);
_wrappedParameter = dateTimeParam;
break;
default:
// Fall back to converting to/from string for types that don't have ITranslator support.
string stringValue = null;
if (translator.Mode == TranslationDirection.WriteToStream)
{
stringValue = (string)Convert.ChangeType(_wrappedParameter, typeof(string), CultureInfo.InvariantCulture);
}
translator.Translate(ref stringValue);
if (translator.Mode == TranslationDirection.ReadFromStream)
{
_wrappedParameter = Convert.ChangeType(stringValue, _parameterTypeCode, CultureInfo.InvariantCulture);
}
break;
}
}
/// <summary>
/// Serializes or deserializes an array of primitive type values wrapped by this <see cref="TaskParameter"/>.
/// </summary>
private void TranslatePrimitiveTypeArray(ITranslator translator)
{
translator.TranslateEnum(ref _parameterTypeCode, (int)_parameterTypeCode);
switch (_parameterTypeCode)
{
case TypeCode.Boolean:
bool[] boolArrayParam = (bool[])_wrappedParameter;
translator.Translate(ref boolArrayParam);
_wrappedParameter = boolArrayParam;
break;
case TypeCode.Int32:
int[] intArrayParam = (int[])_wrappedParameter;
translator.Translate(ref intArrayParam);
_wrappedParameter = intArrayParam;
break;
case TypeCode.String:
string[] stringArrayParam = (string[])_wrappedParameter;
translator.Translate(ref stringArrayParam);
_wrappedParameter = stringArrayParam;
break;
default:
// Fall back to converting to/from string for types that don't have ITranslator support.
if (translator.Mode == TranslationDirection.WriteToStream)
{
Array array = (Array)_wrappedParameter;
int length = array.Length;
translator.Translate(ref length);
for (int i = 0; i < length; i++)
{
string valueString = Convert.ToString(array.GetValue(i), CultureInfo.InvariantCulture);
translator.Translate(ref valueString);
}
}
else
{
Type elementType = _parameterTypeCode switch
{
TypeCode.Char => typeof(char),
TypeCode.SByte => typeof(sbyte),
TypeCode.Byte => typeof(byte),
TypeCode.Int16 => typeof(short),
TypeCode.UInt16 => typeof(ushort),
TypeCode.UInt32 => typeof(uint),
TypeCode.Int64 => typeof(long),
TypeCode.UInt64 => typeof(ulong),
TypeCode.Single => typeof(float),
TypeCode.Double => typeof(double),
TypeCode.Decimal => typeof(decimal),
TypeCode.DateTime => typeof(DateTime),
_ => throw new NotImplementedException(),
};
int length = 0;
translator.Translate(ref length);
Array array = Array.CreateInstance(elementType, length);
for (int i = 0; i < length; i++)
{
string valueString = null;
translator.Translate(ref valueString);
array.SetValue(Convert.ChangeType(valueString, _parameterTypeCode, CultureInfo.InvariantCulture), i);
}
_wrappedParameter = array;
}
break;
}
}
/// <summary>
/// Serializes or deserializes the value type instance wrapped by this <see cref="TaskParameter"/>.
/// </summary>
/// <remarks>
/// The value type is converted to/from string using the <see cref="Convert"/> class. Note that we require
/// task parameter types to be <see cref="IConvertible"/> so this conversion is guaranteed to work for parameters
/// that have made it this far.
/// </remarks>
private void TranslateValueType(ITranslator translator)
{
string valueString = null;
if (translator.Mode == TranslationDirection.WriteToStream)
{
valueString = (string)Convert.ChangeType(_wrappedParameter, typeof(string), CultureInfo.InvariantCulture);
}
translator.Translate(ref valueString);
if (translator.Mode == TranslationDirection.ReadFromStream)
{
// We don't know how to convert the string back to the original value type. This is fine because output
// task parameters are anyway converted to strings by the engine (see TaskExecutionHost.GetValueOutputs)
// and input task parameters of custom value types are not supported.
_wrappedParameter = valueString;
}
}
/// <summary>
/// Serializes or deserializes the value type array instance wrapped by this <see cref="TaskParameter"/>.
/// </summary>
/// <remarks>
/// The array is assumed to be non-null.
/// </remarks>
private void TranslateValueTypeArray(ITranslator translator)
{
if (translator.Mode == TranslationDirection.WriteToStream)
{
Array array = (Array)_wrappedParameter;
int length = array.Length;
translator.Translate(ref length);
for (int i = 0; i < length; i++)
{
string valueString = Convert.ToString(array.GetValue(i), CultureInfo.InvariantCulture);
translator.Translate(ref valueString);
}
}
else
{
int length = 0;
translator.Translate(ref length);
string[] stringArray = new string[length];
for (int i = 0; i < length; i++)
{
translator.Translate(ref stringArray[i]);
}
// We don't know how to convert the string array back to the original value type array.
// This is fine because the engine would eventually convert it to strings anyway.
_wrappedParameter = stringArray;
}
}
/// <summary>
/// Super simple ITaskItem derivative that we can use as a container for read items.
/// </summary>

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Framework;
@ -39,96 +40,124 @@ namespace Microsoft.Build.UnitTests
Assert.Equal(TaskParameterType.Null, t2.ParameterType);
}
/// <summary>
/// Verifies that construction and serialization with a string parameter is OK.
/// </summary>
[Fact]
public void StringParameter()
[Theory]
[InlineData(typeof(bool), (int)TypeCode.Boolean, "True")]
[InlineData(typeof(byte), (int)TypeCode.Byte, "127")]
[InlineData(typeof(sbyte), (int)TypeCode.SByte, "-127")]
[InlineData(typeof(double), (int)TypeCode.Double, "3.14")]
[InlineData(typeof(float), (int)TypeCode.Single, "3.14")]
[InlineData(typeof(short), (int)TypeCode.Int16, "-20000")]
[InlineData(typeof(ushort), (int)TypeCode.UInt16, "30000")]
[InlineData(typeof(int), (int)TypeCode.Int32, "-1")]
[InlineData(typeof(uint), (int)TypeCode.UInt32, "1")]
[InlineData(typeof(long), (int)TypeCode.Int64, "-1000000000000")]
[InlineData(typeof(ulong), (int)TypeCode.UInt64, "1000000000000")]
[InlineData(typeof(decimal), (int)TypeCode.Decimal, "29.99")]
[InlineData(typeof(char), (int)TypeCode.Char, "q")]
[InlineData(typeof(string), (int)TypeCode.String, "foo")]
[InlineData(typeof(DateTime), (int)TypeCode.DateTime, "1/1/2000 12:12:12")]
public void PrimitiveParameter(Type type, int expectedTypeCodeAsInt, string testValueAsString)
{
TaskParameter t = new TaskParameter("foo");
TypeCode expectedTypeCode = (TypeCode)expectedTypeCodeAsInt;
Assert.Equal("foo", t.WrappedParameter);
Assert.Equal(TaskParameterType.String, t.ParameterType);
object value = Convert.ChangeType(testValueAsString, type, CultureInfo.InvariantCulture);
TaskParameter t = new TaskParameter(value);
Assert.Equal(value, t.WrappedParameter);
Assert.Equal(TaskParameterType.PrimitiveType, t.ParameterType);
Assert.Equal(expectedTypeCode, t.ParameterTypeCode);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
Assert.Equal("foo", t2.WrappedParameter);
Assert.Equal(TaskParameterType.String, t2.ParameterType);
Assert.Equal(value, t2.WrappedParameter);
Assert.Equal(TaskParameterType.PrimitiveType, t2.ParameterType);
Assert.Equal(expectedTypeCode, t2.ParameterTypeCode);
}
/// <summary>
/// Verifies that construction and serialization with a string array parameter is OK.
/// </summary>
[Fact]
public void StringArrayParameter()
[Theory]
[InlineData(typeof(bool), (int)TypeCode.Boolean, "True;False;True")]
[InlineData(typeof(byte), (int)TypeCode.Byte, "127;100;0")]
[InlineData(typeof(sbyte), (int)TypeCode.SByte, "-127;-126;12")]
[InlineData(typeof(double), (int)TypeCode.Double, "3.14;3.15")]
[InlineData(typeof(float), (int)TypeCode.Single, "3.14;3.15")]
[InlineData(typeof(short), (int)TypeCode.Int16, "-20000;0;-1")]
[InlineData(typeof(ushort), (int)TypeCode.UInt16, "30000;20000;10")]
[InlineData(typeof(int), (int)TypeCode.Int32, "-1;-2")]
[InlineData(typeof(uint), (int)TypeCode.UInt32, "1;5;6")]
[InlineData(typeof(long), (int)TypeCode.Int64, "-1000000000000;0")]
[InlineData(typeof(ulong), (int)TypeCode.UInt64, "1000000000000;0")]
[InlineData(typeof(decimal), (int)TypeCode.Decimal, "29.99;0.88")]
[InlineData(typeof(char), (int)TypeCode.Char, "q;r;c")]
[InlineData(typeof(string), (int)TypeCode.String, "foo;bar")]
[InlineData(typeof(DateTime), (int)TypeCode.DateTime, "1/1/2000 12:12:12;2/2/2000 13:13:13")]
public void PrimitiveArrayParameter(Type type, int expectedTypeCodeAsInt, string testValueAsString)
{
TaskParameter t = new TaskParameter(new string[] { "foo", "bar" });
TypeCode expectedTypeCode = (TypeCode)expectedTypeCodeAsInt;
Assert.Equal(TaskParameterType.StringArray, t.ParameterType);
string[] values = testValueAsString.Split(';');
Array array = Array.CreateInstance(type, values.Length);
for (int i = 0; i < values.Length; i++)
{
object value = Convert.ChangeType(values[i], type, CultureInfo.InvariantCulture);
array.SetValue(value, i);
}
string[] wrappedParameter = t.WrappedParameter as string[];
Assert.NotNull(wrappedParameter);
Assert.Equal(2, wrappedParameter.Length);
Assert.Equal("foo", wrappedParameter[0]);
Assert.Equal("bar", wrappedParameter[1]);
TaskParameter t = new TaskParameter(array);
Assert.Equal(array, t.WrappedParameter);
Assert.Equal(TaskParameterType.PrimitiveTypeArray, t.ParameterType);
Assert.Equal(expectedTypeCode, t.ParameterTypeCode);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
Assert.Equal(TaskParameterType.StringArray, t2.ParameterType);
string[] wrappedParameter2 = t2.WrappedParameter as string[];
Assert.NotNull(wrappedParameter2);
Assert.Equal(2, wrappedParameter2.Length);
Assert.Equal("foo", wrappedParameter2[0]);
Assert.Equal("bar", wrappedParameter2[1]);
Assert.Equal(array, t2.WrappedParameter);
Assert.Equal(TaskParameterType.PrimitiveTypeArray, t2.ParameterType);
Assert.Equal(expectedTypeCode, t2.ParameterTypeCode);
}
/// <summary>
/// Verifies that construction and serialization with a value type (integer) parameter is OK.
/// </summary>
[Fact]
public void IntParameter()
public void ValueTypeParameter()
{
TaskParameter t = new TaskParameter(1);
TaskBuilderTestTask.CustomStruct value = new TaskBuilderTestTask.CustomStruct(3.14);
TaskParameter t = new TaskParameter(value);
Assert.Equal(1, t.WrappedParameter);
Assert.Equal(TaskParameterType.Int, t.ParameterType);
Assert.Equal(value, t.WrappedParameter);
Assert.Equal(TaskParameterType.ValueType, t.ParameterType);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
Assert.Equal(1, t2.WrappedParameter);
Assert.Equal(TaskParameterType.Int, t2.ParameterType);
// Custom IConvertible structs are deserialized into strings.
Assert.Equal(value.ToString(CultureInfo.InvariantCulture), t2.WrappedParameter);
Assert.Equal(TaskParameterType.ValueType, t2.ParameterType);
}
/// <summary>
/// Verifies that construction and serialization with a parameter that is an array of value types (ints) is OK.
/// </summary>
[Fact]
public void IntArrayParameter()
public void ValueTypeArrayParameter()
{
TaskParameter t = new TaskParameter(new int[] { 2, 15 });
TaskBuilderTestTask.CustomStruct[] value = new TaskBuilderTestTask.CustomStruct[]
{
new TaskBuilderTestTask.CustomStruct(3.14),
new TaskBuilderTestTask.CustomStruct(2.72),
};
TaskParameter t = new TaskParameter(value);
Assert.Equal(value, t.WrappedParameter);
Assert.Equal(TaskParameterType.ValueTypeArray, t.ParameterType);
int[] wrappedParameter = t.WrappedParameter as int[];
Assert.NotNull(wrappedParameter);
Assert.Equal(2, wrappedParameter.Length);
Assert.Equal(2, wrappedParameter[0]);
Assert.Equal(15, wrappedParameter[1]);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
// Custom IConvertible structs are deserialized into strings.
Assert.True(t2.WrappedParameter is string[]);
Assert.Equal(TaskParameterType.ValueTypeArray, t2.ParameterType);
int[] wrappedParameter2 = t2.WrappedParameter as int[];
Assert.NotNull(wrappedParameter2);
Assert.Equal(2, wrappedParameter2.Length);
Assert.Equal(2, wrappedParameter2[0]);
Assert.Equal(15, wrappedParameter2[1]);
string[] stringArray = (string[])t2.WrappedParameter;
Assert.Equal(2, stringArray.Length);
Assert.Equal(value[0].ToString(CultureInfo.InvariantCulture), stringArray[0]);
Assert.Equal(value[1].ToString(CultureInfo.InvariantCulture), stringArray[1]);
}
private enum TestEnumForParameter
@ -143,55 +172,15 @@ namespace Microsoft.Build.UnitTests
TaskParameter t = new TaskParameter(TestEnumForParameter.SomethingElse);
Assert.Equal("SomethingElse", t.WrappedParameter);
Assert.Equal(TaskParameterType.String, t.ParameterType);
Assert.Equal(TaskParameterType.PrimitiveType, t.ParameterType);
Assert.Equal(TypeCode.String, t.ParameterTypeCode);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
Assert.Equal("SomethingElse", t2.WrappedParameter);
Assert.Equal(TaskParameterType.String, t2.ParameterType);
}
[Fact]
public void BoolParameter()
{
TaskParameter t = new TaskParameter(true);
Assert.Equal(true, t.WrappedParameter);
Assert.Equal(TaskParameterType.Bool, t.ParameterType);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
Assert.Equal(true, t2.WrappedParameter);
Assert.Equal(TaskParameterType.Bool, t2.ParameterType);
}
/// <summary>
/// Verifies that construction and serialization with a parameter that is an array of value types (ints) is OK.
/// </summary>
[Fact]
public void BoolArrayParameter()
{
TaskParameter t = new TaskParameter(new bool[] { false, true });
Assert.Equal(TaskParameterType.ValueTypeArray, t.ParameterType);
bool[] wrappedParameter = t.WrappedParameter as bool[];
Assert.NotNull(wrappedParameter);
Assert.Equal(2, wrappedParameter.Length);
Assert.False(wrappedParameter[0]);
Assert.True(wrappedParameter[1]);
((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
TaskParameter t2 = TaskParameter.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
Assert.Equal(TaskParameterType.ValueTypeArray, t2.ParameterType);
bool[] wrappedParameter2 = Assert.IsType<bool[]>(t2.WrappedParameter);
Assert.Equal(2, wrappedParameter2.Length);
Assert.False(wrappedParameter2[0]);
Assert.True(wrappedParameter2[1]);
Assert.Equal(TaskParameterType.PrimitiveType, t2.ParameterType);
Assert.Equal(TypeCode.String, t2.ParameterTypeCode);
}
/// <summary>

Просмотреть файл

@ -0,0 +1,3 @@
# Files that have inline expected results that include trailing whitespace
[RoslynCodeTaskFactory_Tests.cs]
trim_trailing_whitespace = false

Просмотреть файл

@ -450,6 +450,9 @@ End Namespace
TryLoadTaskBodyAndExpectSuccess("<Code Language=\"vb\">code</Code>", expectedCodeLanguage: "VB");
TryLoadTaskBodyAndExpectSuccess("<Code Language=\"visualbasic\">code</Code>", expectedCodeLanguage: "VB");
TryLoadTaskBodyAndExpectSuccess("<Code Language=\"ViSuAl BaSic\">code</Code>", expectedCodeLanguage: "VB");
// Default when the Language attribute is not present.
TryLoadTaskBodyAndExpectSuccess("<Code>code</Code>", expectedCodeLanguage: "CS");
}
[Fact]
@ -474,6 +477,31 @@ End Namespace
}
}
[Fact]
public void CSharpClass()
{
const string taskClassSourceCode = @"namespace InlineTask
{
using Microsoft.Build.Utilities;
public class HelloWorld : Task
{
public override bool Execute()
{
Log.LogMessage(""Hello, world!"");
return !Log.HasLoggedErrors;
}
}
}
";
TryLoadTaskBodyAndExpectSuccess(
$"<Code Type=\"Class\">{taskClassSourceCode}</Code>",
expectedSourceCode: taskClassSourceCode,
expectedCodeType: RoslynCodeTaskFactoryCodeType.Class,
expectedCodeLanguage: "CS");
}
[Fact]
public void CSharpFragment()
{
@ -690,6 +718,36 @@ namespace InlineCode {{
expectedCodeType: RoslynCodeTaskFactoryCodeType.Method);
}
[Fact]
public void CSharpClassSourceCodeFromFile()
{
const string taskClassSourceCode = @"namespace InlineTask
{
using Microsoft.Build.Utilities;
public class HelloWorld : Task
{
public override bool Execute()
{
Log.LogMessage(""Hello, world!"");
return !Log.HasLoggedErrors;
}
}
}
";
using (TestEnvironment env = TestEnvironment.Create())
{
TransientTestFile file = env.CreateFile(fileName: "CSharpClassSourceCodeFromFile.tmp", contents: taskClassSourceCode);
TryLoadTaskBodyAndExpectSuccess(
$"<Code Source=\"{file.Path}\" />",
expectedSourceCode: taskClassSourceCode,
expectedCodeType: RoslynCodeTaskFactoryCodeType.Class,
expectedCodeLanguage: "CS");
}
}
[Fact]
public void CSharpFragmentSourceCodeFromFile()
{
@ -969,6 +1027,47 @@ namespace InlineCode {{
}
}
[Fact]
public void MismatchedTaskNameAndTaskClassName()
{
const string taskName = "SayHello";
const string className = "HelloWorld";
taskName.ShouldNotBe(className, "The test is misconfigured.");
string errorMessage = string.Format(ResourceUtilities.GetResourceString("CodeTaskFactory.CouldNotFindTaskInAssembly"), taskName);
const string projectContent = @"<Project>
<UsingTask TaskName=""" + taskName + @""" TaskFactory=""RoslynCodeTaskFactory"" AssemblyFile=""$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"">
<Task>
<Code Type=""Class"">
namespace InlineTask
{
using Microsoft.Build.Utilities;
public class " + className + @" : Task
{
public override bool Execute()
{
Log.LogMessage(""Hello, world!"");
return !Log.HasLoggedErrors;
}
}
}
</Code>
</Task>
</UsingTask>
<Target Name=""Build"">
<" + taskName + @" />
</Target>
</Project>";
using (TestEnvironment env = TestEnvironment.Create())
{
TransientTestProjectWithFiles proj = env.CreateTestProjectWithFiles(projectContent);
var logger = proj.BuildProjectExpectFailure();
logger.AssertLogContains(errorMessage);
}
}
private void TryLoadTaskBodyAndExpectFailure(string taskBody, string expectedErrorMessage)
{
if (expectedErrorMessage == null)

Просмотреть файл

@ -238,6 +238,57 @@ namespace Microsoft.Build.Tasks.UnitTests
}
}
/// <summary>
/// Question WriteLines to return true when Lines are empty.
/// </summary>
[Fact]
public void QuestionWriteLinesWhenLinesAreEmpty()
{
// Test the combination of:
// 1) File exists
// 2) Overwrite
// 3) WriteOnlyWhenDifferent
var fileExists = FileUtilities.GetTemporaryFile();
var fileNotExists = FileUtilities.GetTemporaryFileName();
try
{
TestWriteLines(fileExists, fileNotExists, Overwrite: true, WriteOnlyWhenDifferent: true);
TestWriteLines(fileExists, fileNotExists, Overwrite: false, WriteOnlyWhenDifferent: true);
TestWriteLines(fileExists, fileNotExists, Overwrite: true, WriteOnlyWhenDifferent: false);
TestWriteLines(fileExists, fileNotExists, Overwrite: false, WriteOnlyWhenDifferent: false);
}
finally
{
File.Delete(fileExists);
}
void TestWriteLines(string fileExists, string fileNotExists, bool Overwrite, bool WriteOnlyWhenDifferent)
{
var test1 = new WriteLinesToFile
{
Overwrite = Overwrite,
BuildEngine = new MockEngine(_output),
File = new TaskItem(fileExists),
WriteOnlyWhenDifferent = WriteOnlyWhenDifferent,
FailIfNotIncremental = true,
// Tests Lines = null.
};
test1.Execute().ShouldBeTrue();
var test2 = new WriteLinesToFile
{
Overwrite = Overwrite,
BuildEngine = new MockEngine(_output),
File = new TaskItem(fileNotExists),
WriteOnlyWhenDifferent = WriteOnlyWhenDifferent,
FailIfNotIncremental = true,
Lines = Array.Empty<ITaskItem>(), // Test empty.
};
test2.Execute().ShouldBeTrue();
}
}
/// <summary>
/// Should create directory structure when target <see cref="WriteLinesToFile.File"/> does not exist.
/// </summary>

Просмотреть файл

@ -133,8 +133,11 @@ namespace Microsoft.Build.Tasks
if (FailIfNotIncremental)
{
Log.LogErrorWithCodeFromResources("WriteLinesToFile.ErrorReadingFile", File.ItemSpec);
return false;
if (Lines?.Length > 0)
{
Log.LogErrorWithCodeFromResources("WriteLinesToFile.ErrorReadingFile", File.ItemSpec);
return false;
}
}
else
{
@ -143,7 +146,7 @@ namespace Microsoft.Build.Tasks
}
else
{
if (FailIfNotIncremental)
if (FailIfNotIncremental && Lines?.Length > 0)
{
Log.LogErrorWithCodeFromResources("WriteLinesToFile.ErrorOrWarning", File.ItemSpec, string.Empty);
return false;

Просмотреть файл

@ -181,6 +181,12 @@ namespace Microsoft.Build.Tasks
TaskType = exportedTypes.FirstOrDefault(type => type.Name.Equals(taskName, StringComparison.OrdinalIgnoreCase))
?? exportedTypes.Where(i => i.FullName != null).FirstOrDefault(type => type.FullName.Equals(taskName, StringComparison.OrdinalIgnoreCase) || type.FullName.EndsWith(taskName, StringComparison.OrdinalIgnoreCase));
if (TaskType == null)
{
_log.LogErrorWithCodeFromResources("CodeTaskFactory.CouldNotFindTaskInAssembly", taskName);
return false;
}
if (taskInfo.CodeType == RoslynCodeTaskFactoryCodeType.Class && parameterGroup.Count == 0)
{
// If the user specified a whole class but nothing in <ParameterGroup />, automatically derive