зеркало из https://github.com/dotnet/msbuild.git
Merge pull request #8726 from dfederm/project-cache-vnext
This change add "cache add" functionality to project caching. Today project caching exposes a hook to plugins for "I'm about to build this thing, do you want to take over instead?" and that's it. This change adds a few more hooks which report file accesses and processes (via Detours) and also for when a project finishes building. This allows a plugin to collect the file accesses and add entries to the cache for future replayability.
This commit is contained in:
Коммит
462a9175d2
|
@ -8,6 +8,7 @@
|
|||
<add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/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" />
|
||||
</packageSources>
|
||||
<disabledPackageSources />
|
||||
</configuration>
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
<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" />
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
using Microsoft.Build.Shared;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Xunit;
|
||||
|
@ -25,21 +26,67 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
[Fact]
|
||||
public void TestConstructors()
|
||||
{
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success), null);
|
||||
TaskHostTaskComplete complete2 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure), null);
|
||||
TaskHostTaskComplete complete3 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringInitialization, new ArgumentOutOfRangeException()), null);
|
||||
TaskHostTaskComplete complete4 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringExecution, new ArgumentNullException()), null);
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
var fileAccessData = new List<FileAccessData>()
|
||||
{
|
||||
new FileAccessData(
|
||||
ReportedFileOperation.CreateFile,
|
||||
RequestedAccess.Read,
|
||||
0,
|
||||
0,
|
||||
DesiredAccess.GENERIC_READ,
|
||||
FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL,
|
||||
"foo",
|
||||
null,
|
||||
true),
|
||||
};
|
||||
#endif
|
||||
|
||||
_ = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
fileAccessData,
|
||||
#endif
|
||||
null);
|
||||
_ = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
fileAccessData,
|
||||
#endif
|
||||
null);
|
||||
_ = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringInitialization,
|
||||
new ArgumentOutOfRangeException()),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
fileAccessData,
|
||||
#endif
|
||||
null);
|
||||
_ = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringExecution, new ArgumentNullException()),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
fileAccessData,
|
||||
#endif
|
||||
null);
|
||||
|
||||
IDictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
TaskHostTaskComplete complete5 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null);
|
||||
_ = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
|
||||
IDictionary<string, object> parameters2 = new Dictionary<string, object>();
|
||||
parameters2.Add("Text", "Hello!");
|
||||
parameters2.Add("MyBoolValue", true);
|
||||
parameters2.Add("MyITaskItem", new TaskItem("ABC"));
|
||||
parameters2.Add("ItemArray", new ITaskItem[] { new TaskItem("DEF"), new TaskItem("GHI"), new TaskItem("JKL") });
|
||||
|
||||
TaskHostTaskComplete complete6 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters2), null);
|
||||
_ = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters2),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,7 +107,12 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
[Fact]
|
||||
public void TestTranslationWithNullDictionary()
|
||||
{
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success), null);
|
||||
TaskHostTaskComplete complete = new(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
|
||||
((ITranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator());
|
||||
INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
|
||||
|
@ -78,7 +130,12 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
[Fact]
|
||||
public void TestTranslationWithEmptyDictionary()
|
||||
{
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, new Dictionary<string, object>()), null);
|
||||
TaskHostTaskComplete complete = new(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, new Dictionary<string, object>()),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
|
||||
((ITranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator());
|
||||
INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
|
||||
|
@ -99,7 +156,12 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
IDictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
parameters.Add("Text", "Foo");
|
||||
parameters.Add("BoolValue", false);
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null);
|
||||
TaskHostTaskComplete complete = new(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
|
||||
((ITranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator());
|
||||
INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
|
||||
|
@ -121,7 +183,12 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
{
|
||||
IDictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
parameters.Add("TaskItemValue", new TaskItem("Foo"));
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null);
|
||||
TaskHostTaskComplete complete = new(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
|
||||
((ITranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator());
|
||||
INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
|
||||
|
@ -142,7 +209,12 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
{
|
||||
IDictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
parameters.Add("TaskItemArrayValue", new ITaskItem[] { new TaskItem("Foo"), new TaskItem("Baz") });
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null);
|
||||
TaskHostTaskComplete complete = new(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
null);
|
||||
|
||||
((ITranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator());
|
||||
INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
|
||||
|
@ -168,7 +240,12 @@ namespace Microsoft.Build.UnitTests.BackEnd
|
|||
|
||||
try
|
||||
{
|
||||
TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(taskResult, taskOutputParameters, taskException, taskExceptionMessage, taskExceptionMessageArgs), buildProcessEnvironment);
|
||||
TaskHostTaskComplete complete = new(
|
||||
new OutOfProcTaskHostTaskResult(taskResult, taskOutputParameters, taskException, taskExceptionMessage, taskExceptionMessageArgs),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
null,
|
||||
#endif
|
||||
buildProcessEnvironment);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ using Microsoft.Build.Eventing;
|
|||
using Microsoft.Build.Exceptions;
|
||||
using Microsoft.Build.Experimental;
|
||||
using Microsoft.Build.Experimental.ProjectCache;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Framework.Telemetry;
|
||||
using Microsoft.Build.Graph;
|
||||
|
@ -558,6 +559,19 @@ namespace Microsoft.Build.Execution
|
|||
_buildParameters.OutputResultsCacheFile = FileUtilities.NormalizePath("msbuild-cache");
|
||||
}
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (_buildParameters.ReportFileAccesses)
|
||||
{
|
||||
// To properly report file access, we need to disable the in-proc node which won't be detoured.
|
||||
_buildParameters.DisableInProcNode = true;
|
||||
|
||||
// Node reuse must be disabled as future builds will not be able to listen to events raised by detours.
|
||||
_buildParameters.EnableNodeReuse = false;
|
||||
|
||||
_componentFactories.ReplaceFactory(BuildComponentType.NodeLauncher, DetouredNodeLauncher.CreateComponent);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize components.
|
||||
_nodeManager = ((IBuildComponentHost)this).GetComponent(BuildComponentType.NodeManager) as INodeManager;
|
||||
|
||||
|
@ -572,9 +586,17 @@ namespace Microsoft.Build.Execution
|
|||
|
||||
InitializeCaches();
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
var fileAccessManager = ((IBuildComponentHost)this).GetComponent(BuildComponentType.FileAccessManager) as IFileAccessManager;
|
||||
#endif
|
||||
|
||||
_projectCacheService = new ProjectCacheService(
|
||||
this,
|
||||
loggingService,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
fileAccessManager,
|
||||
#endif
|
||||
_configCache,
|
||||
_buildParameters.ProjectCacheDescriptor);
|
||||
|
||||
_taskHostNodeManager = ((IBuildComponentHost)this).GetComponent(BuildComponentType.TaskHostNodeManager) as INodeManager;
|
||||
|
@ -584,7 +606,9 @@ namespace Microsoft.Build.Execution
|
|||
_nodeManager.RegisterPacketHandler(NodePacketType.BuildRequestConfiguration, BuildRequestConfiguration.FactoryForDeserialization, this);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.BuildRequestConfigurationResponse, BuildRequestConfigurationResponse.FactoryForDeserialization, this);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.BuildResult, BuildResult.FactoryForDeserialization, this);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.FileAccessReport, FileAccessReport.FactoryForDeserialization, this);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.NodeShutdown, NodeShutdown.FactoryForDeserialization, this);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.ProcessReport, ProcessReport.FactoryForDeserialization, this);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.ResolveSdkRequest, SdkResolverRequest.FactoryForDeserialization, SdkResolverService as INodePacketHandler);
|
||||
_nodeManager.RegisterPacketHandler(NodePacketType.ResourceRequest, ResourceRequest.FactoryForDeserialization, this);
|
||||
|
||||
|
@ -1564,6 +1588,16 @@ namespace Microsoft.Build.Execution
|
|||
HandleNodeShutdown(node, shutdownPacket);
|
||||
break;
|
||||
|
||||
case NodePacketType.FileAccessReport:
|
||||
FileAccessReport fileAccessReport = ExpectPacketType<FileAccessReport>(packet, NodePacketType.FileAccessReport);
|
||||
HandleFileAccessReport(node, fileAccessReport);
|
||||
break;
|
||||
|
||||
case NodePacketType.ProcessReport:
|
||||
ProcessReport processReport = ExpectPacketType<ProcessReport>(packet, NodePacketType.ProcessReport);
|
||||
HandleProcessReport(node, processReport);
|
||||
break;
|
||||
|
||||
default:
|
||||
ErrorUtilities.ThrowInternalError("Unexpected packet received by BuildManager: {0}", packet.Type);
|
||||
break;
|
||||
|
@ -2371,6 +2405,39 @@ namespace Microsoft.Build.Execution
|
|||
configuration.ProjectTargets ??= result.ProjectTargets;
|
||||
}
|
||||
|
||||
// Only report results to the project cache services if it's the result for a build submission.
|
||||
// Note that graph builds create a submission for each node in the graph, so each node in the graph will be
|
||||
// handled here. This intentionally mirrors the behavior for cache requests, as it doesn't make sense to
|
||||
// report for projects which aren't going to be requested. Ideally, *any* request could be handled, but that
|
||||
// would require moving the cache service interactions to the Scheduler.
|
||||
if (_buildSubmissions.TryGetValue(result.SubmissionId, out BuildSubmission buildSubmission))
|
||||
{
|
||||
// The result may be associated with the build submission due to it being the submission which
|
||||
// caused the build, but not the actual request which was originally used with the build submission.
|
||||
// ie. it may be a dependency of the "root-level" project which is associated with this submission, which
|
||||
// isn't what we're looking for. Ensure only the actual submission's request is considered.
|
||||
if (buildSubmission.BuildRequest != null
|
||||
&& buildSubmission.BuildRequest.ConfigurationId == configuration.ConfigurationId
|
||||
&& _projectCacheService.ShouldUseCache(configuration))
|
||||
{
|
||||
BuildEventContext buildEventContext = _projectStartedEvents.TryGetValue(result.SubmissionId, out BuildEventArgs buildEventArgs)
|
||||
? buildEventArgs.BuildEventContext
|
||||
: new BuildEventContext(result.SubmissionId, node, configuration.Project?.EvaluationId ?? BuildEventContext.InvalidEvaluationId, configuration.ConfigurationId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidTaskId);
|
||||
try
|
||||
{
|
||||
_projectCacheService.HandleBuildResultAsync(configuration, result, buildEventContext, _executionCancellationTokenSource.Token).Wait();
|
||||
}
|
||||
catch (AggregateException ex) when (ex.InnerExceptions.All(inner => inner is OperationCanceledException))
|
||||
{
|
||||
// The build is being cancelled. Swallow any exceptions related specifically to cancellation.
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// The build is being cancelled. Swallow any exceptions related specifically to cancellation.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<ScheduleResponse> response = _scheduler.ReportResult(node, result);
|
||||
PerformSchedulingActions(response);
|
||||
}
|
||||
|
@ -2437,6 +2504,36 @@ namespace Microsoft.Build.Execution
|
|||
CheckForActiveNodesAndCleanUpSubmissions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Report the received <paramref name="fileAccessReport"/> to the file access manager.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The id of the node from which the <paramref name="fileAccessReport"/> was received.</param>
|
||||
/// <param name="fileAccessReport">The file access report.</param>
|
||||
private void HandleFileAccessReport(int nodeId, FileAccessReport fileAccessReport)
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (_buildParameters.ReportFileAccesses)
|
||||
{
|
||||
((FileAccessManager)((IBuildComponentHost)this).GetComponent(BuildComponentType.FileAccessManager)).ReportFileAccess(fileAccessReport.FileAccessData, nodeId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Report the received <paramref name="processReport"/> to the file access manager.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The id of the node from which the <paramref name="processReport"/> was received.</param>
|
||||
/// <param name="processReport">The process data report.</param>
|
||||
private void HandleProcessReport(int nodeId, ProcessReport processReport)
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (_buildParameters.ReportFileAccesses)
|
||||
{
|
||||
((FileAccessManager)((IBuildComponentHost)this).GetComponent(BuildComponentType.FileAccessManager)).ReportProcess(processReport.ProcessData, nodeId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there are no more active nodes, cleans up any remaining submissions.
|
||||
/// </summary>
|
||||
|
|
|
@ -220,6 +220,8 @@ namespace Microsoft.Build.Execution
|
|||
|
||||
private string _outputResultsCacheFile;
|
||||
|
||||
private bool _reportFileAccesses;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for those who intend to set all properties themselves.
|
||||
/// </summary>
|
||||
|
@ -303,6 +305,7 @@ namespace Microsoft.Build.Execution
|
|||
_projectIsolationMode = other.ProjectIsolationMode;
|
||||
_inputResultsCacheFiles = other._inputResultsCacheFiles;
|
||||
_outputResultsCacheFile = other._outputResultsCacheFile;
|
||||
_reportFileAccesses = other._reportFileAccesses;
|
||||
DiscardBuildResults = other.DiscardBuildResults;
|
||||
LowPriority = other.LowPriority;
|
||||
Question = other.Question;
|
||||
|
@ -801,6 +804,17 @@ namespace Microsoft.Build.Execution
|
|||
set => _outputResultsCacheFile = value;
|
||||
}
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether file accesses should be reported to any configured project cache plugins.
|
||||
/// </summary>
|
||||
public bool ReportFileAccesses
|
||||
{
|
||||
get => _reportFileAccesses;
|
||||
set => _reportFileAccesses = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether MSBuild will save the results of builds after EndBuild to speed up future builds.
|
||||
/// </summary>
|
||||
|
@ -885,6 +899,7 @@ namespace Microsoft.Build.Execution
|
|||
translator.Translate(ref _interactive);
|
||||
translator.Translate(ref _question);
|
||||
translator.TranslateEnum(ref _projectIsolationMode, (int)_projectIsolationMode);
|
||||
translator.Translate(ref _reportFileAccesses);
|
||||
|
||||
// ProjectRootElementCache is not transmitted.
|
||||
// ResetCaches is not transmitted.
|
||||
|
|
|
@ -473,7 +473,7 @@ namespace Microsoft.Build.Experimental
|
|||
};
|
||||
NodeLauncher nodeLauncher = new NodeLauncher();
|
||||
CommunicationsUtilities.Trace("Starting Server...");
|
||||
Process msbuildProcess = nodeLauncher.Start(_msbuildLocation, string.Join(" ", msBuildServerOptions));
|
||||
Process msbuildProcess = nodeLauncher.Start(_msbuildLocation, string.Join(" ", msBuildServerOptions), nodeId: 0);
|
||||
CommunicationsUtilities.Trace("Server started with PID: {0}", msbuildProcess?.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Build.BackEnd.Components.Caching;
|
||||
using Microsoft.Build.BackEnd.SdkResolution;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.Shared;
|
||||
|
||||
#nullable disable
|
||||
|
@ -61,6 +62,7 @@ namespace Microsoft.Build.BackEnd
|
|||
_componentEntriesByType[BuildComponentType.NodeManager] = new BuildComponentEntry(BuildComponentType.NodeManager, NodeManager.CreateComponent, CreationPattern.Singleton);
|
||||
_componentEntriesByType[BuildComponentType.TaskHostNodeManager] = new BuildComponentEntry(BuildComponentType.TaskHostNodeManager, TaskHostNodeManager.CreateComponent, CreationPattern.Singleton);
|
||||
|
||||
_componentEntriesByType[BuildComponentType.NodeLauncher] = new BuildComponentEntry(BuildComponentType.NodeLauncher, NodeLauncher.CreateComponent, CreationPattern.Singleton);
|
||||
_componentEntriesByType[BuildComponentType.InProcNodeProvider] = new BuildComponentEntry(BuildComponentType.InProcNodeProvider, NodeProviderInProc.CreateComponent, CreationPattern.Singleton);
|
||||
_componentEntriesByType[BuildComponentType.OutOfProcNodeProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcNodeProvider, NodeProviderOutOfProc.CreateComponent, CreationPattern.Singleton);
|
||||
_componentEntriesByType[BuildComponentType.OutOfProcTaskHostNodeProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcTaskHostNodeProvider, NodeProviderOutOfProcTaskHost.CreateComponent, CreationPattern.Singleton);
|
||||
|
@ -80,6 +82,10 @@ namespace Microsoft.Build.BackEnd
|
|||
|
||||
// SDK resolution
|
||||
_componentEntriesByType[BuildComponentType.SdkResolverService] = new BuildComponentEntry(BuildComponentType.SdkResolverService, MainNodeSdkResolverService.CreateComponent, CreationPattern.Singleton);
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_componentEntriesByType[BuildComponentType.FileAccessManager] = new BuildComponentEntry(BuildComponentType.FileAccessManager, FileAccessManager.CreateComponent, CreationPattern.Singleton);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using BuildXL.Processes;
|
||||
using BuildXL.Utilities.Core;
|
||||
using Microsoft.Build.Exceptions;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.Internal;
|
||||
using Microsoft.Build.Shared;
|
||||
using Microsoft.Build.Shared.FileSystem;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.Build.BackEnd
|
||||
{
|
||||
internal sealed class DetouredNodeLauncher : INodeLauncher, IBuildComponent
|
||||
{
|
||||
private readonly List<ISandboxedProcess> _sandboxedProcesses = new();
|
||||
|
||||
private IFileAccessManager _fileAccessManager;
|
||||
|
||||
public static IBuildComponent CreateComponent(BuildComponentType type)
|
||||
{
|
||||
ErrorUtilities.VerifyThrowArgumentOutOfRange(type == BuildComponentType.NodeLauncher, nameof(type));
|
||||
return new DetouredNodeLauncher();
|
||||
}
|
||||
|
||||
public void InitializeComponent(IBuildComponentHost host)
|
||||
{
|
||||
_fileAccessManager = (IFileAccessManager)host.GetComponent(BuildComponentType.FileAccessManager);
|
||||
}
|
||||
|
||||
public void ShutdownComponent()
|
||||
{
|
||||
_fileAccessManager = null;
|
||||
|
||||
foreach (ISandboxedProcess sandboxedProcess in _sandboxedProcesses)
|
||||
{
|
||||
sandboxedProcess.Dispose();
|
||||
}
|
||||
|
||||
_sandboxedProcesses.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new MSBuild process
|
||||
/// </summary>
|
||||
public Process Start(string msbuildLocation, string commandLineArgs, int nodeId)
|
||||
{
|
||||
// Should always have been set already.
|
||||
ErrorUtilities.VerifyThrowInternalLength(msbuildLocation, nameof(msbuildLocation));
|
||||
|
||||
ErrorUtilities.VerifyThrowInternalNull(_fileAccessManager, nameof(_fileAccessManager));
|
||||
|
||||
if (!FileSystems.Default.FileExists(msbuildLocation))
|
||||
{
|
||||
throw new BuildAbortedException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("CouldNotFindMSBuildExe", msbuildLocation));
|
||||
}
|
||||
|
||||
// Repeat the executable name as the first token of the command line because the command line
|
||||
// parser logic expects it and will otherwise skip the first argument
|
||||
commandLineArgs = $"\"{msbuildLocation}\" {commandLineArgs}";
|
||||
|
||||
CommunicationsUtilities.Trace("Launching node from {0}", msbuildLocation);
|
||||
|
||||
string exeName = msbuildLocation;
|
||||
|
||||
#if RUNTIME_TYPE_NETCORE
|
||||
// Run the child process with the same host as the currently-running process.
|
||||
exeName = CurrentHost.GetCurrentHost();
|
||||
#endif
|
||||
|
||||
var eventListener = new DetoursEventListener(_fileAccessManager, nodeId);
|
||||
eventListener.SetMessageHandlingFlags(MessageHandlingFlags.DebugMessageNotify | MessageHandlingFlags.FileAccessNotify | MessageHandlingFlags.ProcessDataNotify | MessageHandlingFlags.ProcessDetoursStatusNotify);
|
||||
|
||||
var info = new SandboxedProcessInfo(
|
||||
fileStorage: null, // Don't write stdout/stderr to files
|
||||
fileName: exeName,
|
||||
disableConHostSharing: false,
|
||||
detoursEventListener: eventListener,
|
||||
createJobObjectForCurrentProcess: false)
|
||||
{
|
||||
SandboxKind = SandboxKind.Default,
|
||||
PipDescription = "MSBuild",
|
||||
PipSemiStableHash = 0,
|
||||
Arguments = commandLineArgs,
|
||||
EnvironmentVariables = EnvironmentalBuildParameters.Instance,
|
||||
MaxLengthInMemory = 0, // Don't buffer any output
|
||||
};
|
||||
|
||||
// FileAccessManifest.AddScope is used to define the list of files which the running process is allowed to access and what kinds of file accesses are allowed
|
||||
// Tracker internally uses AbsolutePath.Invalid to represent the root, just like Unix '/' root.
|
||||
// this code allows all types of accesses for all files
|
||||
info.FileAccessManifest.AddScope(
|
||||
AbsolutePath.Invalid,
|
||||
FileAccessPolicy.MaskNothing,
|
||||
FileAccessPolicy.AllowAll | FileAccessPolicy.ReportAccess);
|
||||
|
||||
// Support shared compilation
|
||||
info.FileAccessManifest.ChildProcessesToBreakawayFromSandbox = new string[] { NativeMethodsShared.IsWindows ? "VBCSCompiler.exe" : "VBCSCompiler" };
|
||||
info.FileAccessManifest.MonitorChildProcesses = true;
|
||||
info.FileAccessManifest.IgnoreReparsePoints = true;
|
||||
info.FileAccessManifest.UseExtraThreadToDrainNtClose = false;
|
||||
info.FileAccessManifest.UseLargeNtClosePreallocatedList = true;
|
||||
info.FileAccessManifest.LogProcessData = true;
|
||||
|
||||
// needed for logging process arguments when a new process is invoked; see DetoursEventListener.cs
|
||||
info.FileAccessManifest.ReportProcessArgs = true;
|
||||
|
||||
// By default, BuildXL sets the timestamp of all input files to January 1, 1970
|
||||
// This breaks some tools like Robocopy which will not copy a file to the destination if the file exists at the destination and has a timestamp that is more recent than the source file
|
||||
info.FileAccessManifest.NormalizeReadTimestamps = false;
|
||||
|
||||
// If a process exits but its child processes survive, Tracker waits 30 seconds by default to wait for this process to exit.
|
||||
// This slows down C++ builds in which mspdbsrv.exe doesn't exit when it's parent exits. Set this time to 0.
|
||||
info.NestedProcessTerminationTimeout = TimeSpan.Zero;
|
||||
|
||||
ISandboxedProcess sp = SandboxedProcessFactory.StartAsync(info, forceSandboxing: false).GetAwaiter().GetResult();
|
||||
lock (_sandboxedProcesses)
|
||||
{
|
||||
_sandboxedProcesses.Add(sp);
|
||||
}
|
||||
|
||||
CommunicationsUtilities.Trace("Successfully launched {1} node with PID {0}", sp.ProcessId, exeName);
|
||||
return Process.GetProcessById(sp.ProcessId);
|
||||
}
|
||||
|
||||
private sealed class EnvironmentalBuildParameters : BuildParameters.IBuildParameters
|
||||
{
|
||||
private readonly Dictionary<string, string> _envVars;
|
||||
|
||||
private EnvironmentalBuildParameters()
|
||||
{
|
||||
var envVars = new Dictionary<string, string>();
|
||||
foreach (DictionaryEntry baseVar in Environment.GetEnvironmentVariables())
|
||||
{
|
||||
envVars.Add((string)baseVar.Key, (string)baseVar.Value);
|
||||
}
|
||||
|
||||
_envVars = envVars;
|
||||
}
|
||||
|
||||
private EnvironmentalBuildParameters(Dictionary<string, string> envVars)
|
||||
{
|
||||
_envVars = envVars;
|
||||
}
|
||||
|
||||
public static EnvironmentalBuildParameters Instance { get; } = new EnvironmentalBuildParameters();
|
||||
|
||||
public string this[string key] => _envVars[key];
|
||||
|
||||
public BuildParameters.IBuildParameters Select(IEnumerable<string> keys)
|
||||
=> new EnvironmentalBuildParameters(keys.ToDictionary(key => key, key => _envVars[key]));
|
||||
|
||||
public BuildParameters.IBuildParameters Override(IEnumerable<KeyValuePair<string, string>> parameters)
|
||||
{
|
||||
var copy = new Dictionary<string, string>(_envVars);
|
||||
foreach (KeyValuePair<string, string> param in parameters)
|
||||
{
|
||||
copy[param.Key] = param.Value;
|
||||
}
|
||||
|
||||
return new EnvironmentalBuildParameters(copy);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, string> ToDictionary() => _envVars;
|
||||
|
||||
public bool ContainsKey(string key) => _envVars.ContainsKey(key);
|
||||
}
|
||||
|
||||
private sealed class DetoursEventListener : IDetoursEventListener
|
||||
{
|
||||
private readonly IFileAccessManager _fileAccessManager;
|
||||
private readonly int _nodeId;
|
||||
|
||||
public DetoursEventListener(IFileAccessManager fileAccessManager, int nodeId)
|
||||
{
|
||||
_fileAccessManager = fileAccessManager;
|
||||
_nodeId = nodeId;
|
||||
}
|
||||
|
||||
public override void HandleDebugMessage(DebugData debugData)
|
||||
{
|
||||
}
|
||||
|
||||
public override void HandleFileAccess(FileAccessData fileAccessData) => _fileAccessManager.ReportFileAccess(
|
||||
new Framework.FileAccess.FileAccessData(
|
||||
(Framework.FileAccess.ReportedFileOperation)fileAccessData.Operation,
|
||||
(Framework.FileAccess.RequestedAccess)fileAccessData.RequestedAccess,
|
||||
fileAccessData.ProcessId,
|
||||
fileAccessData.Error,
|
||||
(Framework.FileAccess.DesiredAccess)fileAccessData.DesiredAccess,
|
||||
(Framework.FileAccess.FlagsAndAttributes)fileAccessData.FlagsAndAttributes,
|
||||
fileAccessData.Path,
|
||||
fileAccessData.ProcessArgs,
|
||||
fileAccessData.IsAnAugmentedFileAccess),
|
||||
_nodeId);
|
||||
|
||||
public override void HandleProcessData(ProcessData processData) => _fileAccessManager.ReportProcess(
|
||||
new Framework.FileAccess.ProcessData(
|
||||
processData.ProcessName,
|
||||
processData.ProcessId,
|
||||
processData.ParentProcessId,
|
||||
processData.CreationDateTime,
|
||||
processData.ExitDateTime,
|
||||
processData.ExitCode),
|
||||
_nodeId);
|
||||
|
||||
public override void HandleProcessDetouringStatus(ProcessDetouringStatusData data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Build.BackEnd
|
||||
{
|
||||
internal interface INodeLauncher
|
||||
{
|
||||
Process Start(string msbuildLocation, string commandLineArgs, int nodeId);
|
||||
}
|
||||
}
|
|
@ -16,12 +16,26 @@ using BackendNativeMethods = Microsoft.Build.BackEnd.NativeMethods;
|
|||
|
||||
namespace Microsoft.Build.BackEnd
|
||||
{
|
||||
internal class NodeLauncher
|
||||
internal sealed class NodeLauncher : INodeLauncher, IBuildComponent
|
||||
{
|
||||
public static IBuildComponent CreateComponent(BuildComponentType type)
|
||||
{
|
||||
ErrorUtilities.VerifyThrowArgumentOutOfRange(type == BuildComponentType.NodeLauncher, nameof(type));
|
||||
return new NodeLauncher();
|
||||
}
|
||||
|
||||
public void InitializeComponent(IBuildComponentHost host)
|
||||
{
|
||||
}
|
||||
|
||||
public void ShutdownComponent()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new MSBuild process
|
||||
/// </summary>
|
||||
public Process Start(string msbuildLocation, string commandLineArgs)
|
||||
public Process Start(string msbuildLocation, string commandLineArgs, int nodeId)
|
||||
{
|
||||
// Disable MSBuild server for a child process.
|
||||
// In case of starting msbuild server it prevents an infinite recurson. In case of starting msbuild node we also do not want this variable to be set.
|
||||
|
@ -181,7 +195,7 @@ namespace Microsoft.Build.BackEnd
|
|||
}
|
||||
}
|
||||
|
||||
private Process DisableMSBuildServer(Func<Process> func)
|
||||
private static Process DisableMSBuildServer(Func<Process> func)
|
||||
{
|
||||
string useMSBuildServerEnvVarValue = Environment.GetEnvironmentVariable(Traits.UseMSBuildServerEnvVarName);
|
||||
try
|
||||
|
|
|
@ -332,8 +332,8 @@ namespace Microsoft.Build.BackEnd
|
|||
}
|
||||
#endif
|
||||
// Create the node process
|
||||
NodeLauncher nodeLauncher = new NodeLauncher();
|
||||
Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs);
|
||||
INodeLauncher nodeLauncher = (INodeLauncher)_componentHost.GetComponent(BuildComponentType.NodeLauncher);
|
||||
Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs, nodeId);
|
||||
_processesToIgnore.TryAdd(GetProcessesToIgnoreKey(hostHandshake, msbuildProcess.Id), default);
|
||||
|
||||
// Note, when running under IMAGEFILEEXECUTIONOPTIONS registry key to debug, the process ID
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
using Microsoft.Build.Shared;
|
||||
|
||||
namespace Microsoft.Build.FileAccesses
|
||||
{
|
||||
internal sealed class FileAccessManager : IFileAccessManager, IBuildComponent
|
||||
{
|
||||
private record Handlers(Action<BuildRequest, FileAccessData> FileAccessHander, Action<BuildRequest, ProcessData> ProcessHandler);
|
||||
|
||||
// In order to synchronize between the node communication and the file access reporting, a special file access
|
||||
// is used to mark when the file accesses should be considered complete. Only after both this special file access is seen
|
||||
// and the build result is reported can plugins be notified about project completion.
|
||||
// NOTE! This is currently Windows-specific and will need to change once this feature is opened up to more scenarios.
|
||||
private static readonly string FileAccessCompletionPrefix = BuildParameters.StartupDirectory[0] + @":\{MSBuildFileAccessCompletion}\";
|
||||
|
||||
private IScheduler? _scheduler;
|
||||
private IConfigCache? _configCache;
|
||||
|
||||
private object _handlersWriteLock = new object();
|
||||
private Handlers[] _handlers = Array.Empty<Handlers>();
|
||||
private string? _tempDirectory;
|
||||
|
||||
// Keyed on global request id
|
||||
private readonly ConcurrentDictionary<int, ManualResetEventSlim> _fileAccessCompletionWaitHandles = new();
|
||||
|
||||
public static IBuildComponent CreateComponent(BuildComponentType type)
|
||||
{
|
||||
ErrorUtilities.VerifyThrowArgumentOutOfRange(type == BuildComponentType.FileAccessManager, nameof(type));
|
||||
return new FileAccessManager();
|
||||
}
|
||||
|
||||
public void InitializeComponent(IBuildComponentHost host)
|
||||
{
|
||||
_scheduler = host.GetComponent(BuildComponentType.Scheduler) as IScheduler;
|
||||
_configCache = host.GetComponent(BuildComponentType.ConfigCache) as IConfigCache;
|
||||
_tempDirectory = FileUtilities.EnsureNoTrailingSlash(FileUtilities.TempFileDirectory);
|
||||
}
|
||||
|
||||
public void ShutdownComponent()
|
||||
{
|
||||
_scheduler = null;
|
||||
_configCache = null;
|
||||
_tempDirectory = null;
|
||||
_fileAccessCompletionWaitHandles.Clear();
|
||||
}
|
||||
|
||||
public void ReportFileAccess(FileAccessData fileAccessData, int nodeId)
|
||||
{
|
||||
string fileAccessPath = fileAccessData.Path;
|
||||
|
||||
// Intercept and avoid forwarding the file access completion
|
||||
if (fileAccessPath.StartsWith(FileAccessCompletionPrefix, StringComparison.Ordinal))
|
||||
{
|
||||
// Parse out the global request id. Note, this must match what NotifyFileAccessCompletion does.
|
||||
int globalRequestId = int.Parse(fileAccessPath.Substring(FileAccessCompletionPrefix.Length));
|
||||
|
||||
ManualResetEventSlim handle = _fileAccessCompletionWaitHandles.GetOrAdd(globalRequestId, static _ => new ManualResetEventSlim());
|
||||
handle.Set();
|
||||
}
|
||||
else if (_tempDirectory != null && fileAccessPath.StartsWith(_tempDirectory))
|
||||
{
|
||||
// Ignore MSBuild's temp directory as these are related to internal MSBuild functionality and not always directly related to the execution of the project itself,
|
||||
// so should not be exposed to handlers. Note that this is not %TEMP% but instead a subdir under %TEMP% which is only expected to be used by MSBuild.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Forward the file access to handlers.
|
||||
BuildRequest? buildRequest = GetBuildRequest(nodeId);
|
||||
if (buildRequest != null)
|
||||
{
|
||||
Handlers[] localHandlers = _handlers;
|
||||
foreach (Handlers handlers in localHandlers)
|
||||
{
|
||||
handlers.FileAccessHander.Invoke(buildRequest, fileAccessData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReportProcess(ProcessData processData, int nodeId)
|
||||
{
|
||||
BuildRequest? buildRequest = GetBuildRequest(nodeId);
|
||||
if (buildRequest != null)
|
||||
{
|
||||
Handlers[] localHandlers = _handlers;
|
||||
foreach (Handlers handlers in localHandlers)
|
||||
{
|
||||
handlers.ProcessHandler.Invoke(buildRequest, processData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HandlerRegistration RegisterHandlers(Action<BuildRequest, FileAccessData> fileAccessHandler, Action<BuildRequest, ProcessData> processHandler)
|
||||
{
|
||||
lock (_handlersWriteLock)
|
||||
{
|
||||
Handlers[] newHandlers = new Handlers[_handlers.Length + 1];
|
||||
_handlers.CopyTo(newHandlers, 0);
|
||||
|
||||
Handlers addedHandlers = new(fileAccessHandler, processHandler);
|
||||
newHandlers[_handlers.Length] = addedHandlers;
|
||||
|
||||
_handlers = newHandlers;
|
||||
|
||||
return new HandlerRegistration(() => UnregisterHandlers(addedHandlers));
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterHandlers(Handlers handlersToRemove)
|
||||
{
|
||||
lock (_handlersWriteLock)
|
||||
{
|
||||
Handlers[] newHandlers = new Handlers[_handlers.Length - 1];
|
||||
int newHandlersIdx = 0;
|
||||
for (int handlersIdx = 0; handlersIdx < _handlers.Length; handlersIdx++)
|
||||
{
|
||||
if (_handlers[handlersIdx] != handlersToRemove)
|
||||
{
|
||||
newHandlers[newHandlersIdx] = _handlers[handlersIdx];
|
||||
newHandlersIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
_handlers = newHandlers;
|
||||
}
|
||||
}
|
||||
|
||||
// The [SupportedOSPlatform] attribute is a safeguard to ensure that the comment on FileAccessCompletionPrefix regarding being Windows-only gets addressed.
|
||||
// [SupportedOSPlatform] doesn't apply to fields, so using it here as a reasonable proxy.
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static void NotifyFileAccessCompletion(int globalRequestId)
|
||||
{
|
||||
// Make a dummy file access to use as a notification that the file accesses should be completed for a project.
|
||||
string filePath = FileAccessCompletionPrefix + globalRequestId.ToString();
|
||||
_ = File.Exists(filePath);
|
||||
}
|
||||
|
||||
public void WaitForFileAccessReportCompletion(int globalRequestId, CancellationToken cancellationToken)
|
||||
{
|
||||
ManualResetEventSlim handle = _fileAccessCompletionWaitHandles.GetOrAdd(globalRequestId, static _ => new ManualResetEventSlim());
|
||||
if (!handle.IsSet)
|
||||
{
|
||||
handle.Wait(cancellationToken);
|
||||
}
|
||||
|
||||
// Try to keep the collection clean. A request should not need to be completed twice.
|
||||
_fileAccessCompletionWaitHandles.TryRemove(globalRequestId, out _);
|
||||
}
|
||||
|
||||
private BuildRequest? GetBuildRequest(int nodeId)
|
||||
{
|
||||
ErrorUtilities.VerifyThrow(
|
||||
_scheduler != null && _configCache != null,
|
||||
"Component has not been initialized");
|
||||
|
||||
// Note: If the node isn't executing anything it may be accessing binaries required to run, eg. the MSBuild binaries
|
||||
return _scheduler!.GetExecutingRequestByNode(nodeId);
|
||||
}
|
||||
|
||||
internal readonly struct HandlerRegistration : IDisposable
|
||||
{
|
||||
private readonly Action _unregisterAction;
|
||||
|
||||
public HandlerRegistration(Action unregisterAction)
|
||||
{
|
||||
_unregisterAction = unregisterAction;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_unregisterAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
|
||||
namespace Microsoft.Build.FileAccesses
|
||||
{
|
||||
internal sealed class FileAccessReport : INodePacket
|
||||
{
|
||||
private FileAccessData _fileAccessData;
|
||||
|
||||
internal FileAccessReport(FileAccessData fileAccessData) => _fileAccessData = fileAccessData;
|
||||
|
||||
private FileAccessReport(ITranslator translator) => Translate(translator);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NodePacketType Type => NodePacketType.FileAccessReport;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ITranslator translator) => translator.Translate(ref _fileAccessData);
|
||||
|
||||
internal FileAccessData FileAccessData => _fileAccessData;
|
||||
|
||||
internal static INodePacket FactoryForDeserialization(ITranslator translator) => new FileAccessReport(translator);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
|
||||
namespace Microsoft.Build.FileAccesses
|
||||
{
|
||||
internal interface IFileAccessManager
|
||||
{
|
||||
void ReportFileAccess(FileAccessData fileAccessData, int nodeId);
|
||||
|
||||
void ReportProcess(ProcessData processData, int nodeId);
|
||||
|
||||
// Note: The return type of FileAccessManager.HandlerRegistration is exposed directly instead of IDisposable to avoid boxing.
|
||||
FileAccessManager.HandlerRegistration RegisterHandlers(
|
||||
Action<BuildRequest, FileAccessData> fileAccessHandler,
|
||||
Action<BuildRequest, ProcessData> processHandler);
|
||||
|
||||
void WaitForFileAccessReportCompletion(int globalRequestId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
using Microsoft.Build.Shared;
|
||||
|
||||
namespace Microsoft.Build.FileAccesses
|
||||
{
|
||||
/// <summary>
|
||||
/// Reports file accesses and process data to the in-proc node.
|
||||
/// </summary>
|
||||
internal sealed class OutOfProcNodeFileAccessManager : IFileAccessManager, IBuildComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Action"/> to report file accesses and process
|
||||
/// data to the in-proc node.
|
||||
/// </summary>
|
||||
private readonly Action<INodePacket> _sendPacket;
|
||||
|
||||
private OutOfProcNodeFileAccessManager(Action<INodePacket> sendPacket) => _sendPacket = sendPacket;
|
||||
|
||||
public static IBuildComponent CreateComponent(BuildComponentType type, Action<INodePacket> sendPacket)
|
||||
{
|
||||
ErrorUtilities.VerifyThrowArgumentOutOfRange(type == BuildComponentType.FileAccessManager, nameof(type));
|
||||
return new OutOfProcNodeFileAccessManager(sendPacket);
|
||||
}
|
||||
|
||||
public void InitializeComponent(IBuildComponentHost host)
|
||||
{
|
||||
}
|
||||
|
||||
public void ShutdownComponent()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reports a file access to the in-proc node.
|
||||
/// </summary>
|
||||
/// <param name="fileAccessData">The file access to report to the in-proc node.</param>
|
||||
/// <param name="nodeId">The id of the reporting out-of-proc node.</param>
|
||||
public void ReportFileAccess(FileAccessData fileAccessData, int nodeId) => _sendPacket(new FileAccessReport(fileAccessData));
|
||||
|
||||
/// <summary>
|
||||
/// Reports process data to the in-proc node.
|
||||
/// </summary>
|
||||
/// <param name="processData">The process data to report to the in-proc node.</param>
|
||||
/// <param name="nodeId">The id of the reporting out-of-proc node.</param>
|
||||
public void ReportProcess(ProcessData processData, int nodeId) => _sendPacket(new ProcessReport(processData));
|
||||
|
||||
public FileAccessManager.HandlerRegistration RegisterHandlers(
|
||||
Action<BuildRequest, FileAccessData> fileAccessHandler,
|
||||
Action<BuildRequest, ProcessData> processHandler) =>
|
||||
throw new NotImplementedException("This method should not be called in OOP nodes.");
|
||||
|
||||
public void WaitForFileAccessReportCompletion(int globalRequestId, CancellationToken cancellationToken) =>
|
||||
throw new NotImplementedException("This method should not be called in OOP nodes.");
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
|
||||
namespace Microsoft.Build.FileAccesses
|
||||
{
|
||||
internal sealed class ProcessReport : INodePacket
|
||||
{
|
||||
private ProcessData _processData;
|
||||
|
||||
internal ProcessReport(ProcessData processData) => _processData = processData;
|
||||
|
||||
private ProcessReport(ITranslator translator) => Translate(translator);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NodePacketType Type => NodePacketType.ProcessReport;
|
||||
|
||||
internal ProcessData ProcessData => _processData;
|
||||
|
||||
internal static INodePacket FactoryForDeserialization(ITranslator translator) => new ProcessReport(translator);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ITranslator translator) => translator.Translate(ref _processData);
|
||||
}
|
||||
}
|
|
@ -130,6 +130,18 @@ namespace Microsoft.Build.BackEnd
|
|||
/// The SDK resolution service.
|
||||
/// </summary>
|
||||
SdkResolverService,
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
/// <summary>
|
||||
/// The component which is the sink for file access reports and forwards reports to other components.
|
||||
/// </summary>
|
||||
FileAccessManager,
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The component which launches new MSBuild nodes.
|
||||
/// </summary>
|
||||
NodeLauncher,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// 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.Experimental.ProjectCache
|
||||
{
|
||||
public readonly struct FileAccessContext
|
||||
{
|
||||
public FileAccessContext(
|
||||
string projectFullPath,
|
||||
IReadOnlyDictionary<string, string> globalProperties,
|
||||
IReadOnlyList<string> targets)
|
||||
{
|
||||
ProjectFullPath = projectFullPath;
|
||||
GlobalProperties = globalProperties;
|
||||
Targets = targets;
|
||||
}
|
||||
|
||||
public string ProjectFullPath { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, string> GlobalProperties { get; }
|
||||
|
||||
public IReadOnlyList<string> Targets { get; }
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
|
||||
namespace Microsoft.Build.Experimental.ProjectCache
|
||||
{
|
||||
|
@ -39,5 +41,32 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
/// Errors are checked via <see cref="PluginLoggerBase.HasLoggedErrors" />.
|
||||
/// </summary>
|
||||
public abstract Task EndBuildAsync(PluginLoggerBase logger, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Called for each file access from an MSBuild node or one of its children.
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public virtual void HandleFileAccess(FileAccessContext fileAccessContext, FileAccessData fileAccessData)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called for each new child process created by an MSBuild node or one of its children.
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public virtual void HandleProcess(FileAccessContext fileAccessContext, ProcessData processData)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a build request finishes execution. This provides an opportunity for the plugin to take action on the
|
||||
/// aggregated file access reports from <see cref="HandleFileAccess(FileAccessContext, FileAccessData)"/>.
|
||||
/// Errors are checked via <see cref="PluginLoggerBase.HasLoggedErrors" />.
|
||||
/// </summary>
|
||||
public virtual Task HandleProjectFinishedAsync(
|
||||
FileAccessContext fileAccessContext,
|
||||
BuildResult buildResult,
|
||||
PluginLoggerBase logger,
|
||||
CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ using Microsoft.Build.BackEnd.Logging;
|
|||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Eventing;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.FileSystem;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Graph;
|
||||
|
@ -33,17 +34,31 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
private static HashSet<string> s_projectSpecificPropertyNames = new(StringComparer.OrdinalIgnoreCase) { "TargetFramework", "Configuration", "Platform", "TargetPlatform", "OutputType" };
|
||||
|
||||
private readonly BuildManager _buildManager;
|
||||
private readonly IBuildComponentHost _componentHost;
|
||||
private readonly ILoggingService _loggingService;
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
private readonly IFileAccessManager _fileAccessManager;
|
||||
#endif
|
||||
private readonly IConfigCache _configCache;
|
||||
|
||||
private readonly ProjectCacheDescriptor? _globalProjectCacheDescriptor;
|
||||
|
||||
private readonly ConcurrentDictionary<ProjectCacheDescriptor, Lazy<Task<ProjectCachePlugin>>> _projectCachePlugins = new(ProjectCacheDescriptorEqualityComparer.Instance);
|
||||
|
||||
// Helps to avoid excessive allocation since BuildRequestConfiguration doesn't expose global properties in a way the plugins can consume (PropertyDictionary<ProjectPropertyInstance> vs IReadOnlyDictionary<string, string>).
|
||||
private readonly ConcurrentDictionary<BuildRequestConfiguration, IReadOnlyDictionary<string, string>> _globalPropertiesPerConfiguration = new();
|
||||
|
||||
private bool _isVsScenario;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
private record struct ProjectCachePlugin(string Name, ProjectCachePluginBase? Instance, ExceptionDispatchInfo? InitializationException = null);
|
||||
private record struct ProjectCachePlugin(
|
||||
string Name,
|
||||
ProjectCachePluginBase? Instance,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
FileAccessManager.HandlerRegistration? HandlerRegistration,
|
||||
#endif
|
||||
ExceptionDispatchInfo? InitializationException = null);
|
||||
|
||||
/// <summary>
|
||||
/// An instanatiable version of MSBuildFileSystemBase not overriding any methods,
|
||||
|
@ -61,10 +76,19 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
public ProjectCacheService(
|
||||
BuildManager buildManager,
|
||||
ILoggingService loggingService,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
IFileAccessManager fileAccessManager,
|
||||
#endif
|
||||
IConfigCache configCache,
|
||||
ProjectCacheDescriptor? globalProjectCacheDescriptor)
|
||||
{
|
||||
_buildManager = buildManager;
|
||||
_componentHost = buildManager;
|
||||
_loggingService = loggingService;
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_fileAccessManager = fileAccessManager;
|
||||
#endif
|
||||
_configCache = configCache;
|
||||
_globalProjectCacheDescriptor = globalProjectCacheDescriptor;
|
||||
}
|
||||
|
||||
|
@ -187,7 +211,13 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ProjectCachePlugin(pluginTypeName, Instance: null, ExceptionDispatchInfo.Capture(e));
|
||||
return new ProjectCachePlugin(
|
||||
pluginTypeName,
|
||||
Instance: null,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
HandlerRegistration: null,
|
||||
#endif
|
||||
ExceptionDispatchInfo.Capture(e));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -218,11 +248,43 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
ProjectCacheException.ThrowForErrorLoggedInsideTheProjectCache("ProjectCacheInitializationFailed");
|
||||
}
|
||||
|
||||
return new ProjectCachePlugin(pluginTypeName, pluginInstance);
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
FileAccessManager.HandlerRegistration? handlerRegistration = null;
|
||||
if (_componentHost.BuildParameters.ReportFileAccesses)
|
||||
{
|
||||
handlerRegistration = _fileAccessManager.RegisterHandlers(
|
||||
(buildRequest, fileAccessData) =>
|
||||
{
|
||||
// TODO: Filter out projects which do not configure this plugin
|
||||
FileAccessContext fileAccessContext = GetFileAccessContext(buildRequest);
|
||||
pluginInstance.HandleFileAccess(fileAccessContext, fileAccessData);
|
||||
},
|
||||
(buildRequest, processData) =>
|
||||
{
|
||||
// TODO: Filter out projects which do not configure this plugin
|
||||
FileAccessContext fileAccessContext = GetFileAccessContext(buildRequest);
|
||||
pluginInstance.HandleProcess(fileAccessContext, processData);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
return new ProjectCachePlugin(
|
||||
pluginTypeName,
|
||||
pluginInstance,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
handlerRegistration,
|
||||
#endif
|
||||
InitializationException: null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ProjectCachePlugin(pluginTypeName, Instance: null, ExceptionDispatchInfo.Capture(e));
|
||||
return new ProjectCachePlugin(
|
||||
pluginTypeName,
|
||||
Instance: null,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
HandlerRegistration: null,
|
||||
#endif
|
||||
ExceptionDispatchInfo.Capture(e));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -230,6 +292,27 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
}
|
||||
}
|
||||
|
||||
private FileAccessContext GetFileAccessContext(BuildRequest buildRequest)
|
||||
{
|
||||
BuildRequestConfiguration configuration = _configCache[buildRequest.ConfigurationId];
|
||||
IReadOnlyDictionary<string, string> globalProperties = GetGlobalProperties(configuration);
|
||||
return new FileAccessContext(configuration.ProjectFullPath, globalProperties, buildRequest.Targets);
|
||||
}
|
||||
|
||||
private IReadOnlyDictionary<string, string> GetGlobalProperties(BuildRequestConfiguration configuration)
|
||||
=> _globalPropertiesPerConfiguration.GetOrAdd(
|
||||
configuration,
|
||||
static configuration =>
|
||||
{
|
||||
Dictionary<string, string> globalProperties = new(configuration.GlobalProperties.Count, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (ProjectPropertyInstance property in configuration.GlobalProperties)
|
||||
{
|
||||
globalProperties.Add(property.Name, property.EvaluatedValue);
|
||||
}
|
||||
|
||||
return globalProperties;
|
||||
});
|
||||
|
||||
private static ProjectCachePluginBase GetPluginInstanceFromType(Type pluginType)
|
||||
{
|
||||
try
|
||||
|
@ -306,6 +389,12 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
return false;
|
||||
}
|
||||
|
||||
// We need to retrieve the configuration if it's already loaded in order to access the Project property below.
|
||||
if (buildRequestConfiguration.IsCached)
|
||||
{
|
||||
buildRequestConfiguration.RetrieveFromCache();
|
||||
}
|
||||
|
||||
// Check if there are any project cache items defined in the project
|
||||
return GetProjectCacheDescriptors(buildRequestConfiguration.Project).Any();
|
||||
}
|
||||
|
@ -587,6 +676,98 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
}
|
||||
}
|
||||
|
||||
public async Task HandleBuildResultAsync(
|
||||
BuildRequestConfiguration requestConfiguration,
|
||||
BuildResult buildResult,
|
||||
BuildEventContext buildEventContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ErrorUtilities.VerifyThrowInternalNull(requestConfiguration.Project, nameof(requestConfiguration.Project));
|
||||
|
||||
if (_projectCachePlugins.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to retrieve the configuration if it's already loaded in order to access the Project property below.
|
||||
if (requestConfiguration.IsCached)
|
||||
{
|
||||
requestConfiguration.RetrieveFromCache();
|
||||
}
|
||||
|
||||
// Filter to plugins which apply to the project, if any
|
||||
List<ProjectCacheDescriptor> projectCacheDescriptors = GetProjectCacheDescriptors(requestConfiguration.Project).ToList();
|
||||
if (projectCacheDescriptors.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (_componentHost.BuildParameters.ReportFileAccesses)
|
||||
{
|
||||
_fileAccessManager.WaitForFileAccessReportCompletion(buildResult.GlobalRequestId, cancellationToken);
|
||||
}
|
||||
#endif
|
||||
|
||||
IReadOnlyDictionary<string, string> globalProperties = GetGlobalProperties(requestConfiguration);
|
||||
|
||||
List<string> targets = buildResult.ResultsByTarget.Keys.ToList();
|
||||
string? targetNames = string.Join(", ", targets);
|
||||
|
||||
FileAccessContext fileAccessContext = new(requestConfiguration.ProjectFullPath, globalProperties, targets);
|
||||
|
||||
var buildEventFileInfo = new BuildEventFileInfo(requestConfiguration.ProjectFullPath);
|
||||
var pluginLogger = new LoggingServiceToPluginLoggerAdapter(
|
||||
_loggingService,
|
||||
buildEventContext,
|
||||
buildEventFileInfo);
|
||||
|
||||
Task[] tasks = new Task[projectCacheDescriptors.Count];
|
||||
int idx = 0;
|
||||
foreach (ProjectCacheDescriptor projectCacheDescriptor in projectCacheDescriptors)
|
||||
{
|
||||
tasks[idx++] = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
if (!_projectCachePlugins.TryGetValue(projectCacheDescriptor, out Lazy<Task<ProjectCachePlugin>>? pluginLazyTask))
|
||||
{
|
||||
// The plugin might not be in the collection if it was never initialized, which can happen if there are multiple plugins
|
||||
// and the first one(s) always handles the cache request so the subsequent one(s) never get lazy initialized.
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectCachePlugin plugin = await pluginLazyTask.Value;
|
||||
|
||||
// Rethrow any initialization exception.
|
||||
plugin.InitializationException?.Throw();
|
||||
|
||||
ErrorUtilities.VerifyThrow(plugin.Instance != null, "Plugin '{0}' instance is null", plugin.Name);
|
||||
|
||||
MSBuildEventSource.Log.ProjectCacheHandleBuildResultStart(plugin.Name, fileAccessContext.ProjectFullPath, targetNames);
|
||||
try
|
||||
{
|
||||
await plugin.Instance!.HandleProjectFinishedAsync(fileAccessContext, buildResult, pluginLogger, cancellationToken);
|
||||
}
|
||||
catch (Exception e) when (e is not ProjectCacheException)
|
||||
{
|
||||
HandlePluginException(e, nameof(ProjectCachePluginBase.HandleProjectFinishedAsync));
|
||||
}
|
||||
finally
|
||||
{
|
||||
MSBuildEventSource.Log.ProjectCacheHandleBuildResultStop(plugin.Name, fileAccessContext.ProjectFullPath, targetNames);
|
||||
}
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
if (pluginLogger.HasLoggedErrors)
|
||||
{
|
||||
ProjectCacheException.ThrowForErrorLoggedInsideTheProjectCache("ProjectCacheHandleBuildResultFailed", fileAccessContext.ProjectFullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
|
@ -624,6 +805,13 @@ namespace Microsoft.Build.Experimental.ProjectCache
|
|||
return;
|
||||
}
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (plugin.HandlerRegistration.HasValue)
|
||||
{
|
||||
plugin.HandlerRegistration.Value.Dispose();
|
||||
}
|
||||
#endif
|
||||
|
||||
MSBuildEventSource.Log.ProjectCacheEndBuildStart(plugin.Name);
|
||||
try
|
||||
{
|
||||
|
|
|
@ -6,22 +6,24 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
#if FEATURE_APPDOMAIN
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
using System.Runtime.Remoting;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
#endif
|
||||
using System.Threading;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Shared;
|
||||
using Microsoft.Build.Execution;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.BackEnd.Components.Caching;
|
||||
using Microsoft.Build.Collections;
|
||||
using Microsoft.Build.Eventing;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
using Microsoft.Build.Shared;
|
||||
using ElementLocation = Microsoft.Build.Construction.ElementLocation;
|
||||
using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem;
|
||||
using TaskLoggingContext = Microsoft.Build.BackEnd.Logging.TaskLoggingContext;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.BackEnd.Components.Caching;
|
||||
using System.Reflection;
|
||||
using Microsoft.Build.Eventing;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -343,6 +345,14 @@ namespace Microsoft.Build.BackEnd
|
|||
/// </summary>
|
||||
public void Yield()
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
// If file accesses are being reported we should not yield as file access will be attributed to the wrong project.
|
||||
if (_host.BuildParameters.ReportFileAccesses)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
lock (_callbackMonitor)
|
||||
{
|
||||
IRequestBuilderCallback builderCallback = _requestEntry.Builder as IRequestBuilderCallback;
|
||||
|
@ -364,6 +374,14 @@ namespace Microsoft.Build.BackEnd
|
|||
// to release explicitly granted cores when reacquiring the node may lead to deadlocks.
|
||||
ReleaseAllCores();
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
// If file accesses are being reported yielding is a no-op so reacquire should be too.
|
||||
if (_host.BuildParameters.ReportFileAccesses)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
lock (_callbackMonitor)
|
||||
{
|
||||
IRequestBuilderCallback builderCallback = _requestEntry.Builder as IRequestBuilderCallback;
|
||||
|
@ -377,7 +395,7 @@ namespace Microsoft.Build.BackEnd
|
|||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region IBuildEngine Members
|
||||
|
||||
|
@ -920,11 +938,23 @@ namespace Microsoft.Build.BackEnd
|
|||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsTaskInputLoggingEnabled => _taskHost._host.BuildParameters.LogTaskInputs;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReportFileAccess(FileAccessData fileAccessData)
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
IBuildComponentHost buildComponentHost = _taskHost._host;
|
||||
if (buildComponentHost.BuildParameters.ReportFileAccesses)
|
||||
{
|
||||
((IFileAccessManager)buildComponentHost.GetComponent(BuildComponentType.FileAccessManager)).ReportFileAccess(fileAccessData, buildComponentHost.BuildParameters.NodeId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public EngineServices EngineServices { get; }
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Called by the internal MSBuild task.
|
||||
|
|
|
@ -33,6 +33,11 @@ namespace Microsoft.Build.BackEnd
|
|||
/// <returns>A positive configuration id if one exists in the plan, 0 otherwise.</returns>
|
||||
int GetConfigurationIdFromPlan(string configurationPath);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the request executing on a node.
|
||||
/// </summary>
|
||||
BuildRequest GetExecutingRequestByNode(int nodeId);
|
||||
|
||||
/// <summary>
|
||||
/// Reports to the scheduler that a request is blocked.
|
||||
/// </summary>
|
||||
|
|
|
@ -283,6 +283,20 @@ namespace Microsoft.Build.BackEnd
|
|||
return _schedulingPlan.GetConfigIdForPath(configPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the request executing on a node.
|
||||
/// </summary>
|
||||
public BuildRequest GetExecutingRequestByNode(int nodeId)
|
||||
{
|
||||
if (!_schedulingData.IsNodeWorking(nodeId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SchedulableRequest request = _schedulingData.GetExecutingRequestByNode(nodeId);
|
||||
return request.BuildRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reports that the specified request has become blocked and cannot proceed.
|
||||
/// </summary>
|
||||
|
|
|
@ -14,6 +14,7 @@ using Microsoft.Build.BackEnd.Components.Caching;
|
|||
using Microsoft.Build.BackEnd.Logging;
|
||||
using Microsoft.Build.BackEnd.SdkResolution;
|
||||
using Microsoft.Build.Evaluation;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Internal;
|
||||
using Microsoft.Build.Shared;
|
||||
|
@ -153,11 +154,15 @@ namespace Microsoft.Build.Execution
|
|||
|
||||
// Create a factory for the out-of-proc SDK resolver service which can pass our SendPacket delegate to be used for sending packets to the main node
|
||||
OutOfProcNodeSdkResolverServiceFactory sdkResolverServiceFactory = new OutOfProcNodeSdkResolverServiceFactory(SendPacket);
|
||||
|
||||
((IBuildComponentHost)this).RegisterFactory(BuildComponentType.SdkResolverService, sdkResolverServiceFactory.CreateInstance);
|
||||
|
||||
_sdkResolverService = (this as IBuildComponentHost).GetComponent(BuildComponentType.SdkResolverService) as ISdkResolverService;
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
((IBuildComponentHost)this).RegisterFactory(
|
||||
BuildComponentType.FileAccessManager,
|
||||
(componentType) => OutOfProcNodeFileAccessManager.CreateComponent(componentType, SendPacket));
|
||||
#endif
|
||||
|
||||
if (s_projectRootElementCacheBase == null)
|
||||
{
|
||||
s_projectRootElementCacheBase = new ProjectRootElementCache(true /* automatically reload any changes from disk */);
|
||||
|
@ -369,6 +374,13 @@ namespace Microsoft.Build.Execution
|
|||
{
|
||||
_nodeEndpoint.SendData(result);
|
||||
}
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (_buildParameters.ReportFileAccesses)
|
||||
{
|
||||
FileAccessManager.NotifyFileAccessCompletion(result.GlobalRequestId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -32,6 +32,11 @@ using Microsoft.Build.Shared;
|
|||
|
||||
#nullable disable
|
||||
|
||||
// The BuildXL package causes an indirect dependency on the RuntimeContracts package, which adds an analyzer which forbids the use of System.Diagnostics.Contract.
|
||||
// So effectively if your dependencies use RuntimeContracts, it attempts to force itself on your as well.
|
||||
// See: https://github.com/SergeyTeplyakov/RuntimeContracts/issues/12
|
||||
#pragma warning disable RA001 // Do not use System.Diagnostics.Contract class.
|
||||
|
||||
namespace Microsoft.Build.Collections
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -9,7 +9,9 @@ using System.Reflection;
|
|||
using System.Threading;
|
||||
using Microsoft.Build.BackEnd.Logging;
|
||||
using Microsoft.Build.Exceptions;
|
||||
using Microsoft.Build.FileAccesses;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
using Microsoft.Build.Internal;
|
||||
using Microsoft.Build.Shared;
|
||||
|
||||
|
@ -433,6 +435,17 @@ namespace Microsoft.Build.BackEnd
|
|||
/// </summary>
|
||||
private void HandleTaskHostTaskComplete(TaskHostTaskComplete taskHostTaskComplete)
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (taskHostTaskComplete.FileAccessData.Count > 0)
|
||||
{
|
||||
IFileAccessManager fileAccessManager = ((IFileAccessManager)_buildComponentHost.GetComponent(BuildComponentType.FileAccessManager));
|
||||
foreach (FileAccessData fileAccessData in taskHostTaskComplete.FileAccessData)
|
||||
{
|
||||
fileAccessManager.ReportFileAccess(fileAccessData, _buildComponentHost.BuildParameters.NodeId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If it crashed, or if it failed, it didn't succeed.
|
||||
_taskExecutionSucceeded = taskHostTaskComplete.TaskResult == TaskCompleteType.Success ? true : false;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\Shared\FileSystemSources.proj" />
|
||||
<Import Project="..\Shared\DebuggingSources.proj" />
|
||||
|
@ -32,12 +32,15 @@
|
|||
|
||||
<PackageReference Include="System.Collections.Immutable" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" />
|
||||
<PackageReference Include="System.Security.Principal.Windows" />
|
||||
<PackageReference Include="System.Threading.Tasks.Dataflow" />
|
||||
|
||||
<PackageReference Include="System.Reflection.Metadata" Condition="'$(MonoBuild)' == 'true'" />
|
||||
<PackageReference Include="System.Reflection.MetadataLoadContext" />
|
||||
|
||||
<PackageReference Include="Microsoft.IO.Redist" Condition="'$(FeatureMSIORedist)' == 'true'" />
|
||||
|
||||
<PackageReference Include="Microsoft.BuildXL.Processes" Condition="'$(FeatureReportFileAccesses)' == 'true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework' and '$(DotNetBuildFromSource)' != 'true'">
|
||||
|
@ -51,7 +54,6 @@
|
|||
|
||||
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'">
|
||||
<PackageReference Include="System.Reflection.Metadata" />
|
||||
<PackageReference Include="System.Security.Principal.Windows" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -152,8 +154,14 @@
|
|||
<Compile Include="BackEnd\Components\Caching\ResultsCacheWithOverride.cs" />
|
||||
<Compile Include="BackEnd\Components\ProjectCache\*.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\CurrentHost.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\DetouredNodeLauncher.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\SerializationContractInitializer.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\ServerNodeEndpointOutOfProc.cs" />
|
||||
<Compile Include="BackEnd\Components\FileAccesses\IFileAccessManager.cs" />
|
||||
<Compile Include="BackEnd\Components\FileAccesses\FileAccessManager.cs" />
|
||||
<Compile Include="BackEnd\Components\FileAccesses\FileAccessReport.cs" />
|
||||
<Compile Include="BackEnd\Components\FileAccesses\OutOfProcNodeFileAccessManager.cs" />
|
||||
<Compile Include="BackEnd\Components\FileAccesses\ProcessReport.cs" />
|
||||
<Compile Include="BackEnd\Components\RequestBuilder\AssemblyLoadsTracker.cs" />
|
||||
<Compile Include="BackEnd\Components\SdkResolution\SdkResolverException.cs" />
|
||||
<Compile Include="BackEnd\Components\SdkResolution\TranslationHelpers.cs" />
|
||||
|
@ -361,6 +369,7 @@
|
|||
<Compile Include="BackEnd\Shared\TargetResult.cs" />
|
||||
<Compile Include="BackEnd\Shared\WorkUnitResult.cs" />
|
||||
<Compile Include="BackEnd\Components\BuildRequestEngine\IBuildRequestEngine.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\INodeLauncher.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\INodeManager.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\INodeProvider.cs" />
|
||||
<Compile Include="BackEnd\Components\Communications\NodeEndpointInProc.cs" />
|
||||
|
|
|
@ -1906,6 +1906,9 @@ Utilization: {0} Average Utilization: {1:###.0}</value>
|
|||
<data name="ProjectCacheShutdownFailed" xml:space="preserve">
|
||||
<value>MSB4268: The project cache failed to shut down properly.</value>
|
||||
</data>
|
||||
<data name="ProjectCacheHandleBuildResultFailed" xml:space="preserve">
|
||||
<value>MSB4269: The project cache failed while handling a build result for the following project: {0}.</value>
|
||||
</data>
|
||||
<data name="NoProjectCachePluginFoundInAssembly" xml:space="preserve">
|
||||
<value>MSB4270: No project cache plugins found in assembly "{0}". Expected one.</value>
|
||||
</data>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: Mezipaměť projektu vyvolala neošetřenou výjimku z metody {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Přístup do mezipaměti projektu pro „{0}“ (výchozí cíle).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: Der Projektcache hat über die Methode {0} eine unbehandelte Ausnahme ausgelöst.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Projektcachetreffer für „{0}“ (Standardziele).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: la caché del proyecto inició una excepción no controlada desde el método {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Acierto de caché de proyecto para "{0}" (destinos predeterminados).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: le cache de projet a levé une exception non gérée à partir de la méthode {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Le cache de projet a été atteint pour « {0} » (cibles par défaut).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: la cache del progetto ha generato un'eccezione non gestita dal metodo {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Riscontro nella cache del progetto per "{0}" (destinazioni predefinite).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: プロジェクト キャッシュが {0} メソッドで処理されていない例外が返されました。</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">"{0}" のプロジェクト キャッシュ ヒット (既定のターゲット)。</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: 프로젝트 캐시는 {0} 메서드에서 처리되지 않은 예외를 발생시켰습니다.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">"{0}"(기본 대상)에 대한 프로젝트 캐시 적중입니다.</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: pamięć podręczna projektu zgłosiła nieobsługiwany wyjątek z metody {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Trafienie pamięci podręcznej projektu dla „{0}” (domyślne elementy docelowe).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: O cache do projeto lançou uma exceção sem tratamento do método {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Acerto de cache do projeto para "{0}" (destinos padrão).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: в кэше проектов возникло необработанное исключение из метода {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">Попадание в кэше проекта для "{0}" (целевые объекты по умолчанию).</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: Proje önbelleği {0} yönteminden yakalanamayan özel durum oluşturdu.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">"{0}" (varsayılan hedefler) için proje önbelleği isabeti.</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: 项目缓存从 {0} 方法引发了未经处理的异常。</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">项目缓存命中 "{0}" (默认目标)。</target>
|
||||
|
|
|
@ -264,6 +264,11 @@
|
|||
<target state="translated">MSB4273: 專案快取從 {0} 方法擲回未處理的例外狀況。</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHandleBuildResultFailed">
|
||||
<source>MSB4269: The project cache failed while handling a build result for the following project: {0}.</source>
|
||||
<target state="new">MSB4269: The project cache failed while handling a build result for the following project: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="ProjectCacheHitWithDefaultTargets">
|
||||
<source>Project cache hit for "{0}" (default targets).</source>
|
||||
<target state="translated">"{0}" 的專案快取命中 (預設目標)。</target>
|
||||
|
|
|
@ -117,4 +117,9 @@
|
|||
<FeatureMSIORedist>true</FeatureMSIORedist>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == '$(FullFrameworkTFM)'">
|
||||
<DefineConstants>$(DefineConstants);FEATURE_REPORTFILEACCESSES</DefineConstants>
|
||||
<FeatureReportFileAccesses>true</FeatureReportFileAccesses>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -9,6 +9,9 @@ using System.IO;
|
|||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Framework.BuildException;
|
||||
#if !CLR2COMPATIBILITY
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
#endif
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -172,6 +175,9 @@ namespace Microsoft.Build.BackEnd
|
|||
value = _reader.ReadInt32();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref uint unsignedInteger) => unsignedInteger = _reader.ReadUInt32();
|
||||
|
||||
/// <summary>
|
||||
/// Translates an <see langword="int"/> array.
|
||||
/// </summary>
|
||||
|
@ -421,6 +427,81 @@ namespace Microsoft.Build.BackEnd
|
|||
_reader.ReadInt32());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref FileAccessData fileAccessData)
|
||||
{
|
||||
ReportedFileOperation reportedFileOperation = default;
|
||||
RequestedAccess requestedAccess = default;
|
||||
uint processId = default;
|
||||
uint error = default;
|
||||
DesiredAccess desiredAccess = default;
|
||||
FlagsAndAttributes flagsAndAttributes = default;
|
||||
string path = default;
|
||||
string processArgs = default;
|
||||
bool isAnAugmentedFileAccess = default;
|
||||
TranslateEnum(ref reportedFileOperation, (int)reportedFileOperation);
|
||||
TranslateEnum(ref requestedAccess, (int)requestedAccess);
|
||||
Translate(ref processId);
|
||||
Translate(ref error);
|
||||
TranslateEnum(ref desiredAccess, (int)desiredAccess);
|
||||
TranslateEnum(ref flagsAndAttributes, (int)flagsAndAttributes);
|
||||
Translate(ref path);
|
||||
Translate(ref processArgs);
|
||||
Translate(ref isAnAugmentedFileAccess);
|
||||
fileAccessData = new FileAccessData(
|
||||
reportedFileOperation,
|
||||
requestedAccess,
|
||||
processId,
|
||||
error,
|
||||
desiredAccess,
|
||||
flagsAndAttributes,
|
||||
path,
|
||||
processArgs,
|
||||
isAnAugmentedFileAccess);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref List<FileAccessData> fileAccessDataList)
|
||||
{
|
||||
if (!TranslateNullable(fileAccessDataList))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = default;
|
||||
Translate(ref count);
|
||||
fileAccessDataList = new List<FileAccessData>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
FileAccessData fileAccessData = default;
|
||||
Translate(ref fileAccessData);
|
||||
fileAccessDataList.Add(fileAccessData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref ProcessData processData)
|
||||
{
|
||||
string processName = default;
|
||||
uint processId = default;
|
||||
uint parentProcessId = default;
|
||||
DateTime creationDateTime = default;
|
||||
DateTime exitDateTime = default;
|
||||
uint exitCode = default;
|
||||
Translate(ref processName);
|
||||
Translate(ref processId);
|
||||
Translate(ref parentProcessId);
|
||||
Translate(ref creationDateTime);
|
||||
Translate(ref exitDateTime);
|
||||
Translate(ref exitCode);
|
||||
processData = new ProcessData(
|
||||
processName,
|
||||
processId,
|
||||
parentProcessId,
|
||||
creationDateTime,
|
||||
exitDateTime,
|
||||
exitCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
@ -884,6 +965,9 @@ namespace Microsoft.Build.BackEnd
|
|||
_writer.Write(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref uint unsignedInteger) => _writer.Write(unsignedInteger);
|
||||
|
||||
/// <summary>
|
||||
/// Translates an <see langword="int"/> array.
|
||||
/// </summary>
|
||||
|
@ -1109,6 +1193,58 @@ namespace Microsoft.Build.BackEnd
|
|||
_writer.Write(value.TaskId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref FileAccessData fileAccessData)
|
||||
{
|
||||
ReportedFileOperation reportedFileOperation = fileAccessData.Operation;
|
||||
RequestedAccess requestedAccess = fileAccessData.RequestedAccess;
|
||||
uint processId = fileAccessData.ProcessId;
|
||||
uint error = fileAccessData.Error;
|
||||
DesiredAccess desiredAccess = fileAccessData.DesiredAccess;
|
||||
FlagsAndAttributes flagsAndAttributes = fileAccessData.FlagsAndAttributes;
|
||||
string path = fileAccessData.Path;
|
||||
string processArgs = fileAccessData.ProcessArgs;
|
||||
bool isAnAugmentedFileAccess = fileAccessData.IsAnAugmentedFileAccess;
|
||||
TranslateEnum(ref reportedFileOperation, (int)reportedFileOperation);
|
||||
TranslateEnum(ref requestedAccess, (int)requestedAccess);
|
||||
Translate(ref processId);
|
||||
Translate(ref error);
|
||||
TranslateEnum(ref desiredAccess, (int)desiredAccess);
|
||||
TranslateEnum(ref flagsAndAttributes, (int)flagsAndAttributes);
|
||||
Translate(ref path);
|
||||
Translate(ref processArgs);
|
||||
Translate(ref isAnAugmentedFileAccess);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref List<FileAccessData> fileAccessDataList)
|
||||
{
|
||||
if (!TranslateNullable(fileAccessDataList))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = fileAccessDataList.Count;
|
||||
Translate(ref count);
|
||||
fileAccessDataList.ForEach(fileAccessData => Translate(ref fileAccessData));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Translate(ref ProcessData processData)
|
||||
{
|
||||
string processName = processData.ProcessName;
|
||||
uint processId = processData.ProcessId;
|
||||
uint parentProcessId = processData.ParentProcessId;
|
||||
DateTime creationDateTime = processData.CreationDateTime;
|
||||
DateTime exitDateTime = processData.ExitDateTime;
|
||||
uint exitCode = processData.ExitCode;
|
||||
Translate(ref processName);
|
||||
Translate(ref processId);
|
||||
Translate(ref parentProcessId);
|
||||
Translate(ref creationDateTime);
|
||||
Translate(ref exitDateTime);
|
||||
Translate(ref exitCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
|
||||
namespace Microsoft.Build.Framework
|
||||
{
|
||||
|
@ -21,10 +22,18 @@ namespace Microsoft.Build.Framework
|
|||
public const int Version1 = 1;
|
||||
|
||||
/// <summary>
|
||||
/// An explicit version of this class. Must be incremented whenever new members are added. Derived classes should override
|
||||
/// the property to return the version actually being implemented.
|
||||
/// Includes <see cref="ReportFileAccess(FileAccessData)"/>.
|
||||
/// </summary>
|
||||
public virtual int Version => Version1; // Not updated since we have not shipped 17.0 yet. This comment is meant to bypass RequiredVersionBumps check in build.ps1 for PR #470646. If the changes in the file are cosmetic, change PR# in this comment to silence the build error on CI build.
|
||||
public const int Version2 = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an explicit version of this class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be incremented whenever new members are added. Derived classes should override
|
||||
/// the property to return the version actually being implemented.
|
||||
/// </remarks>
|
||||
public virtual int Version => Version2;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the given message importance is not guaranteed to be ignored by registered loggers.
|
||||
|
@ -45,5 +54,12 @@ namespace Microsoft.Build.Framework
|
|||
/// This is a performance optimization allowing tasks to skip expensive double-logging.
|
||||
/// </remarks>
|
||||
public virtual bool IsTaskInputLoggingEnabled => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Reports a file access from a task.
|
||||
/// </summary>
|
||||
/// <param name="fileAccessData">The file access to report.</param>
|
||||
[CLSCompliant(false)]
|
||||
public virtual void ReportFileAccess(FileAccessData fileAccessData) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Build.Framework.FileAccess
|
||||
{
|
||||
/*
|
||||
* Implementation note: This is a copy of BuildXL.Processes.DesiredAccess.
|
||||
* The purpose of the copy is because this is part of the public MSBuild API and it's not desirable to
|
||||
* expose BuildXL types directly.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// The requested access to the file or device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants for a full list of values.
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
[CLSCompliant(false)]
|
||||
public enum DesiredAccess : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// For a directory, the right to list the contents of the directory.
|
||||
/// </summary>
|
||||
FILE_LIST_DIRECTORY = 0x00000001,
|
||||
|
||||
/// <summary>
|
||||
/// For a directory, the right to create a file in the directory.
|
||||
/// </summary>
|
||||
FILE_ADD_FILE = 0x00000002,
|
||||
|
||||
/// <summary>
|
||||
/// For a directory, the right to create a subdirectory.
|
||||
/// </summary>
|
||||
FILE_ADD_SUBDIRECTORY = 0x00000004,
|
||||
|
||||
/// <summary>
|
||||
/// The right to read extended file attributes.
|
||||
/// </summary>
|
||||
FILE_READ_EA = 0x00000008,
|
||||
|
||||
/// <summary>
|
||||
/// Right to delete an object.
|
||||
/// </summary>
|
||||
DELETE = 0x00010000,
|
||||
|
||||
/// <summary>
|
||||
/// Right to wait on a handle.
|
||||
/// </summary>
|
||||
SYNCHRONIZE = 0x00100000,
|
||||
|
||||
/// <summary>
|
||||
/// For a file object, the right to append data to the file. (For local files, write operations will not overwrite existing
|
||||
/// data if this flag is specified without <see cref="FILE_WRITE_DATA"/>.) For a directory object, the right to create a subdirectory
|
||||
/// (<see cref="FILE_ADD_SUBDIRECTORY"/>).
|
||||
/// </summary>
|
||||
FILE_APPEND_DATA = 0x00000004,
|
||||
|
||||
/// <summary>
|
||||
/// The right to write extended file attributes.
|
||||
/// </summary>
|
||||
FILE_WRITE_EA = 0x00000010,
|
||||
|
||||
/// <summary>
|
||||
/// For a native code file, the right to execute the file. This access right given to scripts may cause the script to be executable, depending on the script interpreter.
|
||||
/// </summary>
|
||||
FILE_EXECUTE = 0x00000020,
|
||||
|
||||
/// <summary>
|
||||
/// For a directory, the right to delete a directory and all the files it contains, including read-only files.
|
||||
/// </summary>
|
||||
FILE_DELETE_CHILD = 0x00000040,
|
||||
|
||||
/// <summary>
|
||||
/// The right to read file attributes.
|
||||
/// </summary>
|
||||
FILE_READ_ATTRIBUTES = 0x00000080,
|
||||
|
||||
/// <summary>
|
||||
/// The right to write file attributes.
|
||||
/// </summary>
|
||||
FILE_WRITE_ATTRIBUTES = 0x00000100,
|
||||
|
||||
/// <summary>
|
||||
/// For a file object, the right to write data to the file. For a directory object, the right to create a file in the
|
||||
/// directory (<see cref="FILE_ADD_FILE"/>).
|
||||
/// </summary>
|
||||
FILE_WRITE_DATA = 0x00000002,
|
||||
|
||||
/// <summary>
|
||||
/// All possible access rights.
|
||||
/// </summary>
|
||||
GENERIC_ALL = 0x10000000,
|
||||
|
||||
/// <summary>
|
||||
/// Execute access.
|
||||
/// </summary>
|
||||
GENERIC_EXECUTE = 0x20000000,
|
||||
|
||||
/// <summary>
|
||||
/// Write access.
|
||||
/// </summary>
|
||||
GENERIC_WRITE = 0x40000000,
|
||||
|
||||
/// <summary>
|
||||
/// Read access.
|
||||
/// </summary>
|
||||
GENERIC_READ = 0x80000000,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Build.Framework.FileAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// File access data.
|
||||
/// </summary>
|
||||
/// <param name="Operation">The operation that performed the file access.</param>
|
||||
/// <param name="RequestedAccess">The requested access.</param>
|
||||
/// <param name="ProcessId">The process id.</param>
|
||||
/// <param name="Error">The error code of the operation.</param>
|
||||
/// <param name="DesiredAccess">The desired access.</param>
|
||||
/// <param name="FlagsAndAttributes">The file flags and attributes.</param>
|
||||
/// <param name="Path">The path being accessed.</param>
|
||||
/// <param name="ProcessArgs">The process arguments.</param>
|
||||
/// <param name="IsAnAugmentedFileAccess">Whether the file access is augmented.</param>
|
||||
[CLSCompliant(false)]
|
||||
public readonly record struct FileAccessData(
|
||||
ReportedFileOperation Operation,
|
||||
RequestedAccess RequestedAccess,
|
||||
uint ProcessId,
|
||||
uint Error,
|
||||
DesiredAccess DesiredAccess,
|
||||
FlagsAndAttributes FlagsAndAttributes,
|
||||
string Path,
|
||||
string? ProcessArgs,
|
||||
bool IsAnAugmentedFileAccess);
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Build.Framework.FileAccess
|
||||
{
|
||||
/*
|
||||
* Implementation note: This is a copy of BuildXL.Processes.FlagsAndAttributes.
|
||||
* The purpose of the copy is because this is part of the public MSBuild API and it's not desirable to
|
||||
* expose BuildXL types directly.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// The file or device attributes and flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
[CLSCompliant(false)]
|
||||
public enum FlagsAndAttributes : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// The file is read only. Applications can read the file but cannot write to or delete it.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_READONLY = 0x00000001,
|
||||
|
||||
/// <summary>
|
||||
/// The file is hidden. Do not include it in an ordinary directory listing.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_HIDDEN = 0x00000002,
|
||||
|
||||
/// <summary>
|
||||
/// The file is part of or used exclusively by an operating system.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_SYSTEM = 0x00000004,
|
||||
|
||||
/// <summary>
|
||||
/// The path is a directory.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_DIRECTORY = 0x00000010,
|
||||
|
||||
/// <summary>
|
||||
/// The file should be archived. Applications use this attribute to mark files for backup or removal.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
|
||||
|
||||
/// <summary>
|
||||
/// The file does not have other attributes set. This attribute is valid only if used alone.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_NORMAL = 0x00000080,
|
||||
|
||||
/// <summary>
|
||||
/// The file is being used for temporary storage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For more information, see the Caching Behavior section of this topic.
|
||||
/// </remarks>
|
||||
FILE_ATTRIBUTE_TEMPORARY = 0x00000100,
|
||||
|
||||
/// <summary>
|
||||
/// The data of a file is not immediately available. This attribute indicates that file data is physically moved to offline
|
||||
/// storage. This attribute is used by Remote Storage, the hierarchical storage management software. Applications should
|
||||
/// not arbitrarily change this attribute.
|
||||
/// </summary>
|
||||
FILE_ATTRIBUTE_OFFLINE = 0x00001000,
|
||||
|
||||
/// <summary>
|
||||
/// The file or directory is encrypted. For a file, this means that all data in the file is encrypted. For a directory,
|
||||
/// this means that encryption is the default for newly created files and subdirectories. For more information, see File
|
||||
/// Encryption.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This flag has no effect if <see cref="FILE_ATTRIBUTE_SYSTEM"/> is also specified.
|
||||
/// This flag is not supported on Home, Home Premium, Starter, or ARM editions of Windows.
|
||||
/// </remarks>
|
||||
FILE_ATTRIBUTE_ENCRYPED = 0x00004000,
|
||||
|
||||
/// <summary>
|
||||
/// The file data is requested, but it should continue to be located in remote storage. It should not be transported back
|
||||
/// to local storage. This flag is for use by remote storage systems.
|
||||
/// </summary>
|
||||
FILE_FLAG_OPEN_NO_RECALL = 0x00100000,
|
||||
|
||||
/// <summary>
|
||||
/// Normal reparse point processing will not occur; CreateFile will attempt to open the reparse point. When a file is
|
||||
/// opened, a file handle is returned, whether or not the filter that controls the reparse point is operational.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This flag cannot be used with the CREATE_ALWAYS flag.
|
||||
/// If the file is not a reparse point, then this flag is ignored.
|
||||
/// For more information, see the Remarks section.
|
||||
/// </remarks>
|
||||
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000,
|
||||
|
||||
/// <summary>
|
||||
/// The file or device is being opened with session awareness. If this flag is not specified, then per-session devices
|
||||
/// (such as a redirected USB device) cannot be opened by processes running in session 0. This flag has no effect for
|
||||
/// callers not in session 0. This flag is supported only on server editions of Windows.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Windows Server 2008 R2, Windows Server 2008, and Windows Server 2003: This flag is not supported before Windows Server
|
||||
/// 2012.
|
||||
/// </remarks>
|
||||
FILE_FLAG_SESSION_AWARE = 0x00800000,
|
||||
|
||||
/// <summary>
|
||||
/// Access will occur according to POSIX rules. This includes allowing multiple files with names, differing only in case,
|
||||
/// for file systems that support that naming. Use care when using this option, because files created with this flag may
|
||||
/// not be accessible by applications that are written for MS-DOS or 16-bit Windows.
|
||||
/// </summary>
|
||||
FILE_FLAG_POSIX_SEMANTICS = 0x01000000,
|
||||
|
||||
/// <summary>
|
||||
/// The file is being opened or created for a backup or restore operation. The system ensures that the calling process
|
||||
/// overrides file security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges. For more
|
||||
/// information, see Changing Privileges in a Token.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must set this flag to obtain a handle to a directory. A directory handle can be passed to some functions instead of
|
||||
/// a file handle. For more information, see the Remarks section.
|
||||
/// </remarks>
|
||||
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000,
|
||||
|
||||
/// <summary>
|
||||
/// The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any
|
||||
/// other open or duplicated handles.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE
|
||||
/// share mode.
|
||||
/// Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified.
|
||||
/// </remarks>
|
||||
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000,
|
||||
|
||||
/// <summary>
|
||||
/// Access is intended to be sequential from beginning to end. The system can use this as a hint to optimize file caching.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This flag should not be used if read-behind (that is, reverse scans) will be used.
|
||||
/// This flag has no effect if the file system does not support cached I/O and <see cref="FILE_FLAG_NO_BUFFERING"/> .
|
||||
/// For more information, see the Caching Behavior section of this topic.
|
||||
/// </remarks>
|
||||
FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000,
|
||||
|
||||
/// <summary>
|
||||
/// Access is intended to be random. The system can use this as a hint to optimize file caching.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This flag has no effect if the file system does not support cached I/O and <see cref="FILE_FLAG_NO_BUFFERING"/>.
|
||||
/// For more information, see the Caching Behavior section of this topic.
|
||||
/// </remarks>
|
||||
FILE_FLAG_RANDOM_ACCESS = 0x10000000,
|
||||
|
||||
/// <summary>
|
||||
/// The file or device is being opened with no system caching for data reads and writes. This flag does not affect hard
|
||||
/// disk caching or memory mapped files.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are strict requirements for successfully working with files opened with CreateFile using this
|
||||
/// flag; for details, see File Buffering.
|
||||
/// </remarks>
|
||||
FILE_FLAG_NO_BUFFERING = 0x20000000,
|
||||
|
||||
/// <summary>
|
||||
/// The file or device is being opened or created for asynchronous I/O.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When subsequent I/O operations are completed on this handle, the event specified in the OVERLAPPED structure will be
|
||||
/// set to the signaled state.
|
||||
/// If this flag is specified, the file can be used for simultaneous read and write operations.
|
||||
/// If this flag is not specified, then I/O operations are serialized, even if the calls to the read and write functions
|
||||
/// specify an OVERLAPPED structure.
|
||||
/// For information about considerations when using a file handle created with this flag, see the Synchronous and
|
||||
/// Asynchronous I/O Handles section of this topic.
|
||||
/// </remarks>
|
||||
FILE_FLAG_OVERLAPPED = 0x40000000,
|
||||
|
||||
/// <summary>
|
||||
/// Write operations will not go through any intermediate cache; they will go directly to disk.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For additional information, see the Caching Behavior section of this topic.
|
||||
/// </remarks>
|
||||
FILE_FLAG_WRITE_THROUGH = 0x80000000,
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
namespace Microsoft.Build.Framework.FileAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Process data.
|
||||
/// </summary>
|
||||
/// <param name="ProcessName">The process name.</param>
|
||||
/// <param name="ProcessId">The process id.</param>
|
||||
/// <param name="ParentProcessId">The parent process id.</param>
|
||||
/// <param name="CreationDateTime">The creation date time.</param>
|
||||
/// <param name="ExitDateTime">The exit date time.</param>
|
||||
/// <param name="ExitCode">The exit code.</param>
|
||||
[CLSCompliant(false)]
|
||||
public readonly record struct ProcessData(
|
||||
string ProcessName,
|
||||
uint ProcessId,
|
||||
uint ParentProcessId,
|
||||
DateTime CreationDateTime,
|
||||
DateTime ExitDateTime,
|
||||
uint ExitCode);
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
namespace Microsoft.Build.Framework.FileAccess
|
||||
{
|
||||
/*
|
||||
* Implementation note: This is a copy of BuildXL.Processes.ReportedFileOperation.
|
||||
* The purpose of the copy is because this is part of the public MSBuild API and it's not desirable to
|
||||
* expose BuildXL types directly.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Which operation resulted in a reported file access.
|
||||
/// </summary>
|
||||
public enum ReportedFileOperation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown operation.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// CreateFile.
|
||||
/// </summary>
|
||||
CreateFile,
|
||||
|
||||
/// <summary>
|
||||
/// CreateProcess.
|
||||
/// </summary>
|
||||
CreateProcess,
|
||||
|
||||
/// <summary>
|
||||
/// GetFileAttributes.
|
||||
/// </summary>
|
||||
GetFileAttributes,
|
||||
|
||||
/// <summary>
|
||||
/// GetFileAttributesEx.
|
||||
/// </summary>
|
||||
GetFileAttributesEx,
|
||||
|
||||
/// <summary>
|
||||
/// Process forked.
|
||||
/// </summary>
|
||||
Process,
|
||||
|
||||
/// <summary>
|
||||
/// FindFirstFileEx.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// FindFirstFile also indicates this op, since we implement it in terms of FindFirstFileEx.
|
||||
/// </remarks>
|
||||
FindFirstFileEx,
|
||||
|
||||
/// <summary>
|
||||
/// FindNextFile.
|
||||
/// </summary>
|
||||
FindNextFile,
|
||||
|
||||
/// <summary>
|
||||
/// CreateDirectory.
|
||||
/// </summary>
|
||||
CreateDirectory,
|
||||
|
||||
/// <summary>
|
||||
/// DeleteFile.
|
||||
/// </summary>
|
||||
DeleteFile,
|
||||
|
||||
/// <summary>
|
||||
/// MoveFile (source; read and deleted).
|
||||
/// </summary>
|
||||
MoveFileSource,
|
||||
|
||||
/// <summary>
|
||||
/// MoveFile (destination; written).
|
||||
/// </summary>
|
||||
MoveFileDestination,
|
||||
|
||||
/// <summary>
|
||||
/// SetFileInformationByHandleSource (source; read and deleted).
|
||||
/// </summary>
|
||||
SetFileInformationByHandleSource,
|
||||
|
||||
/// <summary>
|
||||
/// SetFileInformationByHandleDest (destination; written).
|
||||
/// </summary>
|
||||
SetFileInformationByHandleDest,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetRenameInformationFileSource (source; read and deleted).
|
||||
/// </summary>
|
||||
ZwSetRenameInformationFileSource,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetRenameInformationFileDest (destination; written).
|
||||
/// </summary>
|
||||
ZwSetRenameInformationFileDest,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetLinkInformationFileDest.
|
||||
/// </summary>
|
||||
ZwSetLinkInformationFile,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetDispositionInformationFile (delete-on-close; deleted).
|
||||
/// </summary>
|
||||
ZwSetDispositionInformationFile,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetModeInformationFile (delete-on-close; deleted).
|
||||
/// </summary>
|
||||
ZwSetModeInformationFile,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetFileNameInformationFile (source; read and written).
|
||||
/// </summary>
|
||||
ZwSetFileNameInformationFileSource,
|
||||
|
||||
/// <summary>
|
||||
/// ZwSetFileNameInformationFile (destination; written).
|
||||
/// </summary>
|
||||
ZwSetFileNameInformationFileDest,
|
||||
|
||||
/// <summary>
|
||||
/// CopyFile (source; read).
|
||||
/// </summary>
|
||||
CopyFileSource,
|
||||
|
||||
/// <summary>
|
||||
/// CopyFile (destination; written).
|
||||
/// </summary>
|
||||
CopyFileDestination,
|
||||
|
||||
/// <summary>
|
||||
/// CreateHardLink (source; read).
|
||||
/// </summary>
|
||||
CreateHardLinkSource,
|
||||
|
||||
/// <summary>
|
||||
/// CreateHardLink (destination; written).
|
||||
/// </summary>
|
||||
CreateHardLinkDestination,
|
||||
|
||||
/// <summary>
|
||||
/// RemoveDirectory.
|
||||
/// </summary>
|
||||
RemoveDirectory,
|
||||
|
||||
/// <summary>
|
||||
/// RemoveDirectory (source; written).
|
||||
/// </summary>
|
||||
RemoveDirectorySource,
|
||||
|
||||
/// <summary>
|
||||
/// NtQueryDirectoryFile.
|
||||
/// </summary>
|
||||
NtQueryDirectoryFile,
|
||||
|
||||
/// <summary>
|
||||
/// ZwQueryDirectoryFile.
|
||||
/// </summary>
|
||||
ZwQueryDirectoryFile,
|
||||
|
||||
/// <summary>
|
||||
/// NtCreateFile.
|
||||
/// </summary>
|
||||
NtCreateFile,
|
||||
|
||||
/// <summary>
|
||||
/// ZwCreateFile.
|
||||
/// </summary>
|
||||
ZwCreateFile,
|
||||
|
||||
/// <summary>
|
||||
/// ZwOpenFile.
|
||||
/// </summary>
|
||||
ZwOpenFile,
|
||||
|
||||
/// <summary>
|
||||
/// This is a quasi operation. We issue this
|
||||
/// report when Detours is changing file open
|
||||
/// request with Read/Write access to Read access only.
|
||||
/// </summary>
|
||||
ChangedReadWriteToReadAccess,
|
||||
|
||||
/// <summary>
|
||||
/// This is a quasi operation. The sandbox issues this only when FileAccessPolicy.OverrideAllowWriteForExistingFiles is set, representing
|
||||
/// that an allow for write check was performed for a given path for the first time (in the scope of a process, another process in the same process
|
||||
/// tree may also report this for the same path).
|
||||
/// </summary>
|
||||
FirstAllowWriteCheckInProcess,
|
||||
|
||||
/// <summary>
|
||||
/// This operation used to indicate to the engine by the Linux sandbox that a process being executed statically links libc
|
||||
/// and may have missing file observations.
|
||||
/// </summary>
|
||||
StaticallyLinkedProcess,
|
||||
|
||||
/// <summary>
|
||||
/// Access of reparse point target.
|
||||
/// </summary>
|
||||
ReparsePointTarget,
|
||||
|
||||
/// <summary>
|
||||
/// Access of reparse point target, cached by Detours.
|
||||
/// </summary>
|
||||
ReparsePointTargetCached,
|
||||
|
||||
/// <summary>
|
||||
/// Access checks for source of CreateSymbolicLink API.
|
||||
/// </summary>
|
||||
CreateSymbolicLinkSource,
|
||||
|
||||
/// <summary>
|
||||
/// Access check for MoveFileWithgProgress source target.
|
||||
/// </summary>
|
||||
MoveFileWithProgressSource,
|
||||
|
||||
/// <summary>
|
||||
/// Access check for MoveFileWithProgress dest target.
|
||||
/// </summary>
|
||||
MoveFileWithProgressDest,
|
||||
|
||||
/// <summary>
|
||||
/// Multiple operations lumped into one.
|
||||
/// </summary>
|
||||
MultipleOperations,
|
||||
|
||||
/// <summary>
|
||||
/// Process exited.
|
||||
/// </summary>
|
||||
ProcessExit,
|
||||
|
||||
#region Operation Names Reported by BuildXLSandbox (macOS sandbox implementation)
|
||||
MacLookup,
|
||||
MacReadlink,
|
||||
MacVNodeCreate,
|
||||
KAuthMoveSource,
|
||||
KAuthMoveDest,
|
||||
KAuthCreateHardlinkSource,
|
||||
KAuthCreateHardlinkDest,
|
||||
KAuthCopySource,
|
||||
KAuthCopyDest,
|
||||
KAuthDeleteDir,
|
||||
KAuthDeleteFile,
|
||||
KAuthOpenDir,
|
||||
KAuthReadFile,
|
||||
KAuthCreateDir,
|
||||
KAuthWriteFile,
|
||||
KAuthClose,
|
||||
KAuthCloseModified,
|
||||
KAuthGetAttributes,
|
||||
KAuthVNodeExecute,
|
||||
KAuthVNodeWrite,
|
||||
KAuthVNodeRead,
|
||||
KAuthVNodeProbe,
|
||||
MacVNodeWrite,
|
||||
MacVNodeCloneSource,
|
||||
MacVNodeCloneDest,
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Build.Framework.FileAccess
|
||||
{
|
||||
/*
|
||||
* Implementation note: This is a copy of BuildXL.Processes.RequestedAccess.
|
||||
* The purpose of the copy is because this is part of the public MSBuild API and it's not desirable to
|
||||
* expose BuildXL types directly.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Level of access requested by a reported file operation.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RequestedAccess : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No access requested.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Read access requested.
|
||||
/// </summary>
|
||||
Read = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Write access requested.
|
||||
/// </summary>
|
||||
Write = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Metadata-only probe access requested (e.g. <see cref="ReportedFileOperation.GetFileAttributes"/>).
|
||||
/// </summary>
|
||||
Probe = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Directory enumeration access requested (on the directory itself; immediate children will be enumerated).
|
||||
/// </summary>
|
||||
Enumerate = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Metadata-only probe access requested; probed as part of a directory enumeration (e.g. <see cref="ReportedFileOperation.FindNextFile"/>).
|
||||
/// </summary>
|
||||
EnumerationProbe = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Both read and write access requested.
|
||||
/// </summary>
|
||||
ReadWrite = Read | Write,
|
||||
|
||||
/// <summary>
|
||||
/// All defined access levels requested.
|
||||
/// </summary>
|
||||
All = Read | Write | Probe | Enumerate | EnumerationProbe,
|
||||
}
|
||||
}
|
|
@ -6,6 +6,9 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.Build.Framework;
|
||||
#if !CLR2COMPATIBILITY
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
#endif
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -134,6 +137,12 @@ namespace Microsoft.Build.BackEnd
|
|||
/// <param name="value">The value to be translated.</param>
|
||||
void Translate(ref int value);
|
||||
|
||||
/// <summary>
|
||||
/// Translates an unsigned integer.
|
||||
/// </summary>
|
||||
/// <param name="unsignedInteger">The unsigned integer to translate.</param>
|
||||
void Translate(ref uint unsignedInteger);
|
||||
|
||||
/// <summary>
|
||||
/// Translates an <see langword="int"/> array.
|
||||
/// </summary>
|
||||
|
@ -234,6 +243,23 @@ namespace Microsoft.Build.BackEnd
|
|||
/// <param name="value">The context to be translated.</param>
|
||||
void Translate(ref BuildEventContext value);
|
||||
|
||||
/// <summary>
|
||||
/// Translates <paramref name="fileAccessData"/>.
|
||||
/// </summary>
|
||||
/// <param name="fileAccessData">The <see cref="FileAccessData"/> to translate.</param>
|
||||
void Translate(ref FileAccessData fileAccessData);
|
||||
|
||||
/// <summary>
|
||||
/// Translates <paramref name="fileAccessDataList"/>.
|
||||
/// </summary>
|
||||
/// <param name="fileAccessDataList">The file accesses to translate.</param>
|
||||
void Translate(ref List<FileAccessData> fileAccessDataList);
|
||||
|
||||
/// <summary>
|
||||
/// Translates <paramref name="processData"/>.
|
||||
/// </summary>
|
||||
/// <param name="processData">The <see cref="ProcessData"/> to translate.</param>
|
||||
void Translate(ref ProcessData processData);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -661,6 +661,18 @@ namespace Microsoft.Build.Eventing
|
|||
{
|
||||
WriteEvent(90, commandLine, countOfConsoleMessages, sumSizeOfConsoleMessages, clientExitType, serverExitType);
|
||||
}
|
||||
|
||||
[Event(91, Keywords = Keywords.All)]
|
||||
public void ProjectCacheHandleBuildResultStart(string pluginTypeName, string projectPath, string targets)
|
||||
{
|
||||
WriteEvent(91, pluginTypeName, projectPath, targets);
|
||||
}
|
||||
|
||||
[Event(92, Keywords = Keywords.All)]
|
||||
public void ProjectCacheHandleBuildResultStop(string pluginTypeName, string projectPath, string targets)
|
||||
{
|
||||
WriteEvent(92, pluginTypeName, projectPath, targets);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1300,7 +1300,7 @@ internal static class NativeMethods
|
|||
// using (var r = FileUtilities.OpenRead("/proc/" + processId + "/stat"))
|
||||
// and could be again when FileUtilities moves to Framework
|
||||
|
||||
using var fileStream = new FileStream("/proc/" + processId + "/stat", FileMode.Open, FileAccess.Read);
|
||||
using var fileStream = new FileStream("/proc/" + processId + "/stat", FileMode.Open, System.IO.FileAccess.Read);
|
||||
using StreamReader r = new(fileStream);
|
||||
|
||||
line = r.ReadLine();
|
||||
|
|
|
@ -1073,6 +1073,9 @@ namespace Microsoft.Build.UnitTests
|
|||
question: false,
|
||||
inputResultsCaches: null,
|
||||
outputResultsCache: null,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
reportFileAccesses: false,
|
||||
#endif
|
||||
commandLine: null);
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -106,6 +106,9 @@ namespace Microsoft.Build.CommandLine
|
|||
GraphBuild,
|
||||
InputResultsCaches,
|
||||
OutputResultsCache,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
ReportFileAccesses,
|
||||
#endif
|
||||
LowPriority,
|
||||
Question,
|
||||
DetailedSummary,
|
||||
|
@ -265,6 +268,9 @@ namespace Microsoft.Build.CommandLine
|
|||
new ParameterizedSwitchInfo( new string[] { "graphbuild", "graph" }, ParameterizedSwitch.GraphBuild, null, true, null, true, false),
|
||||
new ParameterizedSwitchInfo( new string[] { "inputResultsCaches", "irc" }, ParameterizedSwitch.InputResultsCaches, null, true, null, true, true),
|
||||
new ParameterizedSwitchInfo( new string[] { "outputResultsCache", "orc" }, ParameterizedSwitch.OutputResultsCache, "DuplicateOutputResultsCache", false, null, true, true),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
new ParameterizedSwitchInfo( new string[] { "reportfileaccesses" }, ParameterizedSwitch.ReportFileAccesses, null, false, null, true, false),
|
||||
#endif
|
||||
new ParameterizedSwitchInfo( new string[] { "lowpriority", "low" }, ParameterizedSwitch.LowPriority, null, false, null, true, false),
|
||||
new ParameterizedSwitchInfo( new string[] { "question", "q" }, ParameterizedSwitch.Question, null, false, null, true, false),
|
||||
new ParameterizedSwitchInfo( new string[] { "detailedsummary", "ds" }, ParameterizedSwitch.DetailedSummary, null, false, null, true, false),
|
||||
|
|
|
@ -5,14 +5,16 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using System.Threading;
|
||||
using Microsoft.Build.BackEnd;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.Framework;
|
||||
#if !CLR2COMPATIBILITY
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
#endif
|
||||
using Microsoft.Build.Internal;
|
||||
using Microsoft.Build.Shared;
|
||||
#if FEATURE_APPDOMAIN
|
||||
|
@ -165,6 +167,13 @@ namespace Microsoft.Build.CommandLine
|
|||
private RegisteredTaskObjectCacheBase _registeredTaskObjectCache;
|
||||
#endif
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
/// <summary>
|
||||
/// The file accesses reported by the most recently completed task.
|
||||
/// </summary>
|
||||
private List<FileAccessData> _fileAccessData = new List<FileAccessData>();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
|
@ -531,11 +540,19 @@ namespace Microsoft.Build.CommandLine
|
|||
return _taskHost._currentConfiguration.IsTaskInputLoggingEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReportFileAccess(FileAccessData fileAccessData)
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_taskHost._fileAccessData.Add(fileAccessData);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public EngineServices EngineServices { get; }
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -936,8 +953,11 @@ namespace Microsoft.Build.CommandLine
|
|||
lock (_taskCompleteLock)
|
||||
{
|
||||
_taskCompletePacket = new TaskHostTaskComplete(
|
||||
taskResult,
|
||||
currentEnvironment);
|
||||
taskResult,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_fileAccessData,
|
||||
#endif
|
||||
currentEnvironment);
|
||||
}
|
||||
|
||||
#if FEATURE_APPDOMAIN
|
||||
|
@ -956,11 +976,20 @@ namespace Microsoft.Build.CommandLine
|
|||
lock (_taskCompleteLock)
|
||||
{
|
||||
// Create a minimal taskCompletePacket to carry the exception so that the TaskHostTask does not hang while waiting
|
||||
_taskCompletePacket = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedAfterExecution, e), null);
|
||||
_taskCompletePacket = new TaskHostTaskComplete(
|
||||
new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedAfterExecution, e),
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_fileAccessData,
|
||||
#endif
|
||||
null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_fileAccessData = new List<FileAccessData>();
|
||||
#endif
|
||||
|
||||
// Call CleanupTask to unload any domains and other necessary cleanup in the taskWrapper
|
||||
_taskWrapper.CleanupTask();
|
||||
|
||||
|
|
|
@ -865,6 +865,18 @@
|
|||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</comment>
|
||||
</data>
|
||||
<data name="HelpMessage_42_ReportFileAccessesSwitch" Visibility="Public">
|
||||
<value> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</value>
|
||||
<comment>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</comment>
|
||||
</data>
|
||||
<data name="InvalidConfigurationFile" Visibility="Public">
|
||||
<value>MSBUILD : Configuration error MSB1043: The application could not start. {0}</value>
|
||||
<comment>
|
||||
|
@ -1305,6 +1317,15 @@
|
|||
<value>MSBUILD : error MSB1049: The {0} parameter must be specified</value>
|
||||
<comment>{StrBegin="MSBUILD : error MSB1049: "}</comment>
|
||||
</data>
|
||||
<data name="InvalidReportFileAccessesValue" UESanitized="true" Visibility="Public">
|
||||
<value>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</value>
|
||||
<comment>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</comment>
|
||||
</data>
|
||||
<!-- #################################################################################################-->
|
||||
<!-- ######################################## Shared strings #########################################-->
|
||||
<!-- ###### These belong in Strings.shared.resx, but have been copied here for performance ###########-->
|
||||
|
|
|
@ -92,6 +92,24 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
@ -112,6 +130,16 @@
|
|||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value for the lowPriority parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="MSBuildVersionMessage">
|
||||
|
|
|
@ -91,6 +91,24 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
@ -111,6 +129,16 @@
|
|||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value for the lowPriority parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="MSBuildVersionMessage">
|
||||
|
|
|
@ -91,6 +91,24 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
@ -111,6 +129,16 @@
|
|||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value for the lowPriority parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="MSBuildVersionMessage">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -91,6 +91,34 @@
|
|||
LOCALIZATION: "MSBuild" should not be localized.
|
||||
LOCALIZATION: "-question" and "-q" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HelpMessage_42_ReportFileAccessesSwitch">
|
||||
<source> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</source>
|
||||
<target state="new"> -reportFileAccesses[:True|False]
|
||||
Causes MSBuild to report file accesses to any configured
|
||||
project cache plugins.
|
||||
|
||||
This flag is experimental and may not work as intended.
|
||||
</target>
|
||||
<note>
|
||||
LOCALIZATION: "-reportFileAccesses" should not be localized.
|
||||
LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidReportFileAccessesValue">
|
||||
<source>MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</source>
|
||||
<target state="new">MSBUILD : error MSB1063: Report file accesses value is not valid. {0}</target>
|
||||
<note>
|
||||
{StrBegin="MSBUILD : error MSB1063: "}
|
||||
UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg.
|
||||
This error is shown when a user specifies a value that is not equivalent to Boolean.TrueString or Boolean.FalseString.
|
||||
LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized.
|
||||
</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidLowPriorityValue">
|
||||
|
|
|
@ -707,6 +707,9 @@ namespace Microsoft.Build.CommandLine
|
|||
string[] inputResultsCaches = null;
|
||||
string outputResultsCache = null;
|
||||
bool question = false;
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
bool reportFileAccesses = false;
|
||||
#endif
|
||||
|
||||
GatherAllSwitches(commandLine, out var switchesFromAutoResponseFile, out var switchesNotFromAutoResponseFile, out _);
|
||||
bool buildCanBeInvoked = ProcessCommandLineSwitches(
|
||||
|
@ -741,6 +744,9 @@ namespace Microsoft.Build.CommandLine
|
|||
ref graphBuildOptions,
|
||||
ref inputResultsCaches,
|
||||
ref outputResultsCache,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
ref reportFileAccesses,
|
||||
#endif
|
||||
ref lowPriority,
|
||||
ref question,
|
||||
recursing: false,
|
||||
|
@ -816,6 +822,9 @@ namespace Microsoft.Build.CommandLine
|
|||
question,
|
||||
inputResultsCaches,
|
||||
outputResultsCache,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
reportFileAccesses,
|
||||
#endif
|
||||
commandLine))
|
||||
{
|
||||
exitType = ExitType.BuildError;
|
||||
|
@ -1137,6 +1146,9 @@ namespace Microsoft.Build.CommandLine
|
|||
bool question,
|
||||
string[] inputResultsCaches,
|
||||
string outputResultsCache,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
bool reportFileAccesses,
|
||||
#endif
|
||||
#if FEATURE_GET_COMMANDLINE
|
||||
string commandLine)
|
||||
#else
|
||||
|
@ -1328,6 +1340,9 @@ namespace Microsoft.Build.CommandLine
|
|||
parameters.InputResultsCacheFiles = inputResultsCaches;
|
||||
parameters.OutputResultsCacheFile = outputResultsCache;
|
||||
parameters.Question = question;
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
parameters.ReportFileAccesses = reportFileAccesses;
|
||||
#endif
|
||||
|
||||
// Propagate the profiler flag into the project load settings so the evaluator
|
||||
// can pick it up
|
||||
|
@ -2259,6 +2274,9 @@ namespace Microsoft.Build.CommandLine
|
|||
ref GraphBuildOptions graphBuild,
|
||||
ref string[] inputResultsCaches,
|
||||
ref string outputResultsCache,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
ref bool reportFileAccesses,
|
||||
#endif
|
||||
ref bool lowPriority,
|
||||
ref bool question,
|
||||
bool recursing,
|
||||
|
@ -2313,6 +2331,13 @@ namespace Microsoft.Build.CommandLine
|
|||
// leave priority where it was.
|
||||
catch (Win32Exception) { }
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
if (commandLineSwitches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.ReportFileAccesses))
|
||||
{
|
||||
reportFileAccesses = ProcessBooleanSwitch(commandLineSwitches[CommandLineSwitches.ParameterizedSwitch.ReportFileAccesses], defaultValue: true, resourceName: "");
|
||||
}
|
||||
#endif
|
||||
|
||||
// if help switch is set (regardless of switch errors), show the help message and ignore the other switches
|
||||
if (commandLineSwitches[CommandLineSwitches.ParameterlessSwitch.Help])
|
||||
{
|
||||
|
@ -2375,6 +2400,9 @@ namespace Microsoft.Build.CommandLine
|
|||
ref graphBuild,
|
||||
ref inputResultsCaches,
|
||||
ref outputResultsCache,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
ref reportFileAccesses,
|
||||
#endif
|
||||
ref lowPriority,
|
||||
ref question,
|
||||
recursing: true,
|
||||
|
@ -4269,6 +4297,9 @@ namespace Microsoft.Build.CommandLine
|
|||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_InputCachesFiles"));
|
||||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_OutputCacheFile"));
|
||||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_36_GraphBuildSwitch"));
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_42_ReportFileAccessesSwitch"));
|
||||
#endif
|
||||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_39_LowPrioritySwitch"));
|
||||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_41_QuestionSwitch"));
|
||||
Console.WriteLine(AssemblyResources.GetString("HelpMessage_7_ResponseFile"));
|
||||
|
|
|
@ -190,6 +190,16 @@ namespace Microsoft.Build.BackEnd
|
|||
/// </summary>
|
||||
ResourceResponse,
|
||||
|
||||
/// <summary>
|
||||
/// Message sent from a node reporting a file access.
|
||||
/// </summary>
|
||||
FileAccessReport,
|
||||
|
||||
/// <summary>
|
||||
/// Message sent from a node reporting process data.
|
||||
/// </summary>
|
||||
ProcessReport,
|
||||
|
||||
/// <summary>
|
||||
/// Command in form of MSBuild command line for server node - MSBuild Server.
|
||||
/// Keep this enum value constant intact as this is part of contract with dotnet CLI
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
#if !CLR2COMPATIBILITY
|
||||
using Microsoft.Build.Framework.FileAccess;
|
||||
#endif
|
||||
using Microsoft.Build.Shared;
|
||||
|
||||
#nullable disable
|
||||
|
@ -49,6 +52,10 @@ namespace Microsoft.Build.BackEnd
|
|||
/// </summary>
|
||||
internal class TaskHostTaskComplete : INodePacket
|
||||
{
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
private List<FileAccessData> _fileAccessData;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Result of the task's execution.
|
||||
/// </summary>
|
||||
|
@ -82,12 +89,21 @@ namespace Microsoft.Build.BackEnd
|
|||
/// </summary>
|
||||
private Dictionary<string, string> _buildProcessEnvironment = null;
|
||||
|
||||
|
||||
#pragma warning disable CS1572 // XML comment has a param tag, but there is no parameter by that name. Justification: xmldoc doesn't seem to interact well with #ifdef of params.
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// Initializes a new instance of the <see cref="TaskHostTaskComplete"/> class.
|
||||
/// </summary>
|
||||
/// <param name="result">Result of the task's execution.</param>
|
||||
/// <param name="result">The result of the task's execution.</param>
|
||||
/// <param name="fileAccessData">The file accesses reported by the task.</param>
|
||||
/// <param name="buildProcessEnvironment">The build process environment as it was at the end of the task's execution.</param>
|
||||
public TaskHostTaskComplete(OutOfProcTaskHostTaskResult result, IDictionary<string, string> buildProcessEnvironment)
|
||||
#pragma warning restore CS1572 // XML comment has a param tag, but there is no parameter by that name
|
||||
public TaskHostTaskComplete(
|
||||
OutOfProcTaskHostTaskResult result,
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
List<FileAccessData> fileAccessData,
|
||||
#endif
|
||||
IDictionary<string, string> buildProcessEnvironment)
|
||||
{
|
||||
ErrorUtilities.VerifyThrowInternalNull(result, nameof(result));
|
||||
|
||||
|
@ -95,6 +111,9 @@ namespace Microsoft.Build.BackEnd
|
|||
_taskException = result.TaskException;
|
||||
_taskExceptionMessage = result.ExceptionMessage;
|
||||
_taskExceptionMessageArgs = result.ExceptionMessageArgs;
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
_fileAccessData = fileAccessData;
|
||||
#endif
|
||||
|
||||
if (result.FinalParameterValues != null)
|
||||
{
|
||||
|
@ -201,6 +220,17 @@ namespace Microsoft.Build.BackEnd
|
|||
get { return NodePacketType.TaskHostTaskComplete; }
|
||||
}
|
||||
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
/// <summary>
|
||||
/// Gets the file accesses reported by the task.
|
||||
/// </summary>
|
||||
public List<FileAccessData> FileAccessData
|
||||
{
|
||||
[DebuggerStepThrough]
|
||||
get => _fileAccessData;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Translates the packet to/from binary form.
|
||||
/// </summary>
|
||||
|
@ -213,6 +243,9 @@ namespace Microsoft.Build.BackEnd
|
|||
translator.Translate(ref _taskExceptionMessageArgs);
|
||||
translator.TranslateDictionary(ref _taskOutputParameters, StringComparer.OrdinalIgnoreCase, TaskParameter.FactoryForDeserialization);
|
||||
translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase);
|
||||
#if FEATURE_REPORTFILEACCESSES
|
||||
translator.Translate(ref _fileAccessData);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
Загрузка…
Ссылка в новой задаче