зеркало из https://github.com/microsoft/BuildXL.git
Upgrade Microsoft.Build.Prediction to 0.2.0 (#480)
Upgrade Microsoft.Build.Prediction to 0.2.0
This commit is contained in:
Родитель
e0b0ceabf8
Коммит
a725108f10
|
@ -14,11 +14,6 @@ namespace BuildXL.FrontEnd.MsBuild.Serialization
|
|||
[JsonObject(IsReference = false)]
|
||||
public sealed class MSBuildGraphBuilderArguments
|
||||
{
|
||||
/// <summary>
|
||||
/// Root of the enlistment, required by BuildPrediction
|
||||
/// </summary>
|
||||
public string EnlistmentRoot { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional file paths for the projects or solutions that should be used to start parsing. These are relative
|
||||
/// paths with respect to the enlistment root.
|
||||
|
@ -66,16 +61,14 @@ namespace BuildXL.FrontEnd.MsBuild.Serialization
|
|||
|
||||
/// <nodoc/>
|
||||
public MSBuildGraphBuilderArguments(
|
||||
string enlistmentRoot,
|
||||
IReadOnlyCollection<string> projectsToParse,
|
||||
string outputPath,
|
||||
IReadOnlyCollection<string> projectsToParse,
|
||||
string outputPath,
|
||||
GlobalProperties globalProperties,
|
||||
IReadOnlyCollection<string> mSBuildSearchLocations,
|
||||
IReadOnlyCollection<string> mSBuildSearchLocations,
|
||||
IReadOnlyCollection<string> entryPointTargets,
|
||||
IReadOnlyCollection<GlobalProperties> requestedQualifiers,
|
||||
bool allowProjectsWithoutTargetProtocol)
|
||||
{
|
||||
Contract.Requires(!string.IsNullOrEmpty(enlistmentRoot));
|
||||
Contract.Requires(projectsToParse?.Count > 0);
|
||||
Contract.Requires(!string.IsNullOrEmpty(outputPath));
|
||||
Contract.Requires(globalProperties != null);
|
||||
|
@ -83,7 +76,6 @@ namespace BuildXL.FrontEnd.MsBuild.Serialization
|
|||
Contract.Requires(entryPointTargets != null);
|
||||
Contract.Requires(requestedQualifiers?.Count > 0);
|
||||
|
||||
EnlistmentRoot = enlistmentRoot;
|
||||
ProjectsToParse = projectsToParse;
|
||||
OutputPath = outputPath;
|
||||
GlobalProperties = globalProperties;
|
||||
|
@ -96,9 +88,8 @@ namespace BuildXL.FrontEnd.MsBuild.Serialization
|
|||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$@"Enlistment root: {EnlistmentRoot}
|
||||
Project entry points: [{string.Join(", ", ProjectsToParse)}]
|
||||
return
|
||||
$@"Project entry points: [{string.Join(", ", ProjectsToParse)}]
|
||||
Serialized graph path: {OutputPath}
|
||||
Global properties: {string.Join(" ", GlobalProperties.Select(kvp => $"[{kvp.Key}]={kvp.Value}"))}
|
||||
Search locations: {string.Join(" ", MSBuildSearchLocations)}
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
/// <summary>
|
||||
/// Collection of environment variables that are allowed to the graph construction process to see (in addition to the ones specified by the user)
|
||||
/// </summary>
|
||||
private static readonly string[] s_environmentVariableWhitelist = new[]
|
||||
private static readonly string[] s_environmentVariableWhitelist = new[]
|
||||
{
|
||||
"ComSpec",
|
||||
"PATH",
|
||||
|
@ -346,7 +346,7 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
{
|
||||
Tracing.Logger.Log.TooManyParsingEntryPointCandidates(m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), m_resolverSettings.RootTraversal.ToString(m_context.PathTable));
|
||||
}
|
||||
|
||||
|
||||
parsingEntryPoints = null;
|
||||
return false;
|
||||
|
||||
|
@ -458,9 +458,9 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
|
||||
private async Task<Possible<ProjectGraphWithPredictionsResult<AbsolutePath>>> ComputeBuildGraphAsync(
|
||||
AbsolutePath responseFile,
|
||||
IEnumerable<AbsolutePath> projectEntryPoints,
|
||||
AbsolutePath outputFile,
|
||||
IEnumerable<AbsolutePath> searchLocations,
|
||||
IEnumerable<AbsolutePath> projectEntryPoints,
|
||||
AbsolutePath outputFile,
|
||||
IEnumerable<AbsolutePath> searchLocations,
|
||||
BuildParameters.IBuildParameters buildParameters)
|
||||
{
|
||||
SandboxedProcessResult result = await RunMsBuildGraphBuilderAsync(responseFile, projectEntryPoints, outputFile, searchLocations, buildParameters);
|
||||
|
@ -483,7 +483,7 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
return new MsBuildGraphConstructionFailure(m_resolverSettings, m_context.PathTable);
|
||||
}
|
||||
|
||||
// If the tool exited gracefully, but standard error is not empty, that
|
||||
// If the tool exited gracefully, but standard error is not empty, that
|
||||
// is interpreted as a warning. We propagate that to the BuildXL log
|
||||
if (!string.IsNullOrEmpty(standardError))
|
||||
{
|
||||
|
@ -513,8 +513,8 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
|
||||
// Let's log the paths to the used MsBuild assemblies, just for debugging purposes
|
||||
Tracing.Logger.Log.GraphConstructionToolCompleted(
|
||||
m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable),
|
||||
string.Join(",\n", projectGraphWithPredictionsResult.MsBuildAssemblyPaths.Select(kvp => I($"[{kvp.Key}]:{kvp.Value.ToString(m_context.PathTable)}"))),
|
||||
m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable),
|
||||
string.Join(",\n", projectGraphWithPredictionsResult.MsBuildAssemblyPaths.Select(kvp => I($"[{kvp.Key}]:{kvp.Value.ToString(m_context.PathTable)}"))),
|
||||
projectGraphWithPredictionsResult.PathToMsBuildExe.ToString(m_context.PathTable));
|
||||
|
||||
return projectGraphWithPredictionsResult;
|
||||
|
@ -537,22 +537,20 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
|
||||
private Task<SandboxedProcessResult> RunMsBuildGraphBuilderAsync(
|
||||
AbsolutePath responseFile,
|
||||
IEnumerable<AbsolutePath> projectEntryPoints,
|
||||
AbsolutePath outputFile,
|
||||
IEnumerable<AbsolutePath> searchLocations,
|
||||
IEnumerable<AbsolutePath> projectEntryPoints,
|
||||
AbsolutePath outputFile,
|
||||
IEnumerable<AbsolutePath> searchLocations,
|
||||
BuildParameters.IBuildParameters buildParameters)
|
||||
{
|
||||
AbsolutePath toolDirectory = m_configuration.Layout.BuildEngineDirectory.Combine(m_context.PathTable, RelativePathToGraphConstructionTool).GetParent(m_context.PathTable);
|
||||
string pathToTool = m_configuration.Layout.BuildEngineDirectory.Combine(m_context.PathTable, RelativePathToGraphConstructionTool).ToString(m_context.PathTable);
|
||||
string outputDirectory = outputFile.GetParent(m_context.PathTable).ToString(m_context.PathTable);
|
||||
string outputFileString = outputFile.ToString(m_context.PathTable);
|
||||
string enlistmentRoot = m_resolverSettings.Root.ToString(m_context.PathTable);
|
||||
IReadOnlyCollection<string> entryPointTargets = m_resolverSettings.InitialTargets ?? CollectionUtilities.EmptyArray<string>();
|
||||
|
||||
var requestedQualifiers = m_requestedQualifiers.Select(qualifierId => MsBuildResolverUtils.CreateQualifierAsGlobalProperties(qualifierId, m_context)).ToList();
|
||||
|
||||
var arguments = new MSBuildGraphBuilderArguments(
|
||||
enlistmentRoot,
|
||||
projectEntryPoints.Select(entryPoint => entryPoint.ToString(m_context.PathTable)).ToList(),
|
||||
outputFileString,
|
||||
new GlobalProperties(m_resolverSettings.GlobalProperties ?? CollectionUtilities.EmptyDictionary<string, string>()),
|
||||
|
@ -601,7 +599,7 @@ namespace BuildXL.FrontEnd.MsBuild
|
|||
{
|
||||
try
|
||||
{
|
||||
// The name of the pipe is the filename of the output file
|
||||
// The name of the pipe is the filename of the output file
|
||||
using (var pipeClient = new NamedPipeClientStream(
|
||||
".",
|
||||
Path.GetFileName(outputFileString),
|
||||
|
|
|
@ -33,8 +33,6 @@ namespace MsBuildGraphBuilderTool
|
|||
/// </summary>
|
||||
public static class MsBuildGraphBuilder
|
||||
{
|
||||
private static readonly (IReadOnlyCollection<string> predictedBy, string failure) s_noPredictionFailure = (new string[] { }, string.Empty);
|
||||
|
||||
// Well-known item that defines the protocol static targets.
|
||||
// See https://github.com/Microsoft/msbuild/blob/master/documentation/specs/static-graph.md#inferring-which-targets-to-run-for-a-project-within-the-graph
|
||||
// TODO: maybe the static graph API can provide this information in the future in a more native way
|
||||
|
@ -67,7 +65,7 @@ namespace MsBuildGraphBuilderTool
|
|||
IMsBuildAssemblyLoader assemblyLoader,
|
||||
GraphBuilderReporter reporter,
|
||||
MSBuildGraphBuilderArguments arguments,
|
||||
IReadOnlyCollection<IProjectStaticPredictor> projectPredictorsForTesting = null)
|
||||
IReadOnlyCollection<IProjectPredictor> projectPredictorsForTesting = null)
|
||||
{
|
||||
DoBuildGraphAndSerialize(
|
||||
assemblyLoader,
|
||||
|
@ -80,7 +78,7 @@ namespace MsBuildGraphBuilderTool
|
|||
IMsBuildAssemblyLoader assemblyLoader,
|
||||
GraphBuilderReporter reporter,
|
||||
MSBuildGraphBuilderArguments arguments,
|
||||
IReadOnlyCollection<IProjectStaticPredictor> projectPredictorsForTesting = null)
|
||||
IReadOnlyCollection<IProjectPredictor> projectPredictorsForTesting = null)
|
||||
{
|
||||
reporter.ReportMessage("Starting MSBuild graph construction process...");
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
@ -95,7 +93,7 @@ namespace MsBuildGraphBuilderTool
|
|||
IMsBuildAssemblyLoader assemblyLoader,
|
||||
GraphBuilderReporter reporter,
|
||||
MSBuildGraphBuilderArguments arguments,
|
||||
IReadOnlyCollection<IProjectStaticPredictor> projectPredictorsForTesting)
|
||||
IReadOnlyCollection<IProjectPredictor> projectPredictorsForTesting)
|
||||
{
|
||||
reporter.ReportMessage("Looking for MSBuild toolset...");
|
||||
|
||||
|
@ -125,7 +123,7 @@ namespace MsBuildGraphBuilderTool
|
|||
IReadOnlyDictionary<string, string> assemblyPathsToLoad,
|
||||
string locatedMsBuildPath,
|
||||
MSBuildGraphBuilderArguments graphBuildArguments,
|
||||
IReadOnlyCollection<IProjectStaticPredictor> projectPredictorsForTesting)
|
||||
IReadOnlyCollection<IProjectPredictor> projectPredictorsForTesting)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -170,7 +168,6 @@ namespace MsBuildGraphBuilderTool
|
|||
|
||||
if (!TryConstructGraph(
|
||||
projectGraph,
|
||||
graphBuildArguments.EnlistmentRoot,
|
||||
reporter,
|
||||
projectInstanceToProjectCache,
|
||||
graphBuildArguments.EntryPointTargets,
|
||||
|
@ -299,11 +296,10 @@ namespace MsBuildGraphBuilderTool
|
|||
|
||||
private static bool TryConstructGraph(
|
||||
ProjectGraph projectGraph,
|
||||
string enlistmentRoot,
|
||||
GraphBuilderReporter reporter,
|
||||
ConcurrentDictionary<ProjectInstance, Project> projectInstanceToProjectCache,
|
||||
IReadOnlyCollection<string> entryPointTargets,
|
||||
IReadOnlyCollection<IProjectStaticPredictor> projectPredictorsForTesting,
|
||||
IReadOnlyCollection<IProjectPredictor> projectPredictorsForTesting,
|
||||
bool allowProjectsWithoutTargetProtocol,
|
||||
out ProjectGraphWithPredictions projectGraphWithPredictions,
|
||||
out string failure)
|
||||
|
@ -329,10 +325,10 @@ namespace MsBuildGraphBuilderTool
|
|||
|
||||
// Create the registered predictors and initialize the prediction executor
|
||||
// The prediction executor potentially initializes third-party predictors, which may contain bugs. So let's be very defensive here
|
||||
IReadOnlyCollection<IProjectStaticPredictor> predictors;
|
||||
IReadOnlyCollection<IProjectPredictor> predictors;
|
||||
try
|
||||
{
|
||||
predictors = projectPredictorsForTesting ?? ProjectStaticPredictors.BasicPredictors;
|
||||
predictors = projectPredictorsForTesting ?? ProjectPredictors.AllPredictors;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
|
@ -341,10 +337,11 @@ namespace MsBuildGraphBuilderTool
|
|||
return false;
|
||||
}
|
||||
|
||||
var predictionExecutor = new ProjectStaticPredictionExecutor(enlistmentRoot, predictors);
|
||||
// Using single-threaded prediction since we're parallelizing on project nodes instead.
|
||||
var predictionExecutor = new ProjectPredictionExecutor(predictors, new ProjectPredictionOptions { MaxDegreeOfParallelism = 1 });
|
||||
|
||||
// Each predictor may return unexpected/incorrect results and targets may not be able to be predicted. We put those failures here for post-processing.
|
||||
ConcurrentQueue<(IReadOnlyCollection<string> predictedBy, string failure)> predictedIOFailures = new ConcurrentQueue<(IReadOnlyCollection<string>, string)>();
|
||||
ConcurrentQueue<(string predictorName, string failure)> predictionFailures = new ConcurrentQueue<(string, string)>();
|
||||
var predictedTargetFailures = new ConcurrentQueue<string>();
|
||||
|
||||
// The predicted targets to execute (per project) go here
|
||||
|
@ -360,31 +357,20 @@ namespace MsBuildGraphBuilderTool
|
|||
ProjectInstance projectInstance = msBuildNode.ProjectInstance;
|
||||
Project project = projectInstanceToProjectCache[projectInstance];
|
||||
|
||||
StaticPredictions predictions;
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
|
||||
var predictionCollector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
try
|
||||
{
|
||||
// Again, be defensive when using arbitrary predictors
|
||||
predictions = predictionExecutor.PredictInputsAndOutputs(project);
|
||||
predictionExecutor.PredictInputsAndOutputs(project, predictionCollector);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
predictedIOFailures.Enqueue((
|
||||
new string[] { "Unknown predictor" },
|
||||
predictionFailures.Enqueue((
|
||||
"Unknown predictor",
|
||||
$"Cannot run static predictor on project '{project.FullPath ?? "Unknown project"}'. An unexpected error occurred. Please contact BuildPrediction project owners with this stack trace: {ex.ToString()}"));
|
||||
|
||||
// Stick an empty prediction. The error will be caught anyway after all predictors are done.
|
||||
predictions = new StaticPredictions(new BuildInput[] { }, new BuildOutputDirectory[] { });
|
||||
}
|
||||
|
||||
// Let's validate the predicted inputs and outputs, in case the predictor is not working properly
|
||||
if (!TryGetInputPredictions(predictions, out IReadOnlyCollection<string> inputPredictions, out (IReadOnlyCollection<string> predictedBy, string failure) inputPredictionFailure))
|
||||
{
|
||||
predictedIOFailures.Enqueue(inputPredictionFailure);
|
||||
}
|
||||
|
||||
if (!TryGetOutputPredictions(predictions, out IReadOnlyCollection<string> outputPredictions, out (IReadOnlyCollection<string> predictedBy, string failure) outputPredictionFailure))
|
||||
{
|
||||
predictedIOFailures.Enqueue(outputPredictionFailure);
|
||||
}
|
||||
|
||||
if (!TryGetPredictedTargetsAndPropertiesToExecute(
|
||||
|
@ -405,8 +391,8 @@ namespace MsBuildGraphBuilderTool
|
|||
projectInstance.FullPath,
|
||||
projectInstance.GetItems(ProjectReferenceTargets).Count > 0,
|
||||
globalProperties,
|
||||
inputPredictions,
|
||||
outputPredictions);
|
||||
inputFilePredictions,
|
||||
outputFolderPredictions);
|
||||
|
||||
// If projects not implementing the target protocol are blocked, then the list of computed targets is final. So we set it right here
|
||||
// to avoid wasted allocations
|
||||
|
@ -422,11 +408,11 @@ namespace MsBuildGraphBuilderTool
|
|||
});
|
||||
|
||||
// There were IO prediction errors.
|
||||
if (!predictedIOFailures.IsEmpty)
|
||||
if (!predictionFailures.IsEmpty)
|
||||
{
|
||||
projectGraphWithPredictions = new ProjectGraphWithPredictions(new ProjectWithPredictions<string>[] { });
|
||||
failure = $"Errors found during static prediction of inputs and outputs. " +
|
||||
$"{string.Join(", ", predictedIOFailures.Select(failureWithCulprit => $"[Predicted by: {string.Join(", ", failureWithCulprit.predictedBy)}] {failureWithCulprit.failure}"))}";
|
||||
$"{string.Join(", ", predictionFailures.Select(failureWithCulprit => $"[Predicted by: {failureWithCulprit.predictorName}] {failureWithCulprit.failure}"))}";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -524,82 +510,6 @@ namespace MsBuildGraphBuilderTool
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetInputPredictions(StaticPredictions predictions, out IReadOnlyCollection<string> inputPredictions, out (IReadOnlyCollection<string> predictedBy, string failure) inputPredictionFailure)
|
||||
{
|
||||
var inputs = new List<string>(predictions.BuildInputs.Count);
|
||||
|
||||
foreach (BuildInput input in predictions.BuildInputs)
|
||||
{
|
||||
var folder = input.Path;
|
||||
|
||||
if (!IsStringValidAbsolutePath(folder, out string invalidPathFailure))
|
||||
{
|
||||
inputPredictionFailure = (input.PredictedBy, invalidPathFailure);
|
||||
inputPredictions = inputs;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.IsDirectory)
|
||||
{
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
inputs.AddRange(Directory.EnumerateFiles(folder));
|
||||
}
|
||||
// TODO: Can we do anything to flag that the input prediction is not going to be used?
|
||||
}
|
||||
else
|
||||
{
|
||||
inputs.Add(folder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inputPredictions = inputs;
|
||||
inputPredictionFailure = s_noPredictionFailure;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryGetOutputPredictions(StaticPredictions predictions, out IReadOnlyCollection<string> outputPredictions, out (IReadOnlyCollection<string> predictedBy, string failure) outputPredictionFailure)
|
||||
{
|
||||
var items = new List<string>();
|
||||
foreach (var output in predictions.BuildOutputDirectories)
|
||||
{
|
||||
var path = output.Path;
|
||||
if (!IsStringValidAbsolutePath(path, out string invalidPathFailure))
|
||||
{
|
||||
outputPredictionFailure = (output.PredictedBy, invalidPathFailure);
|
||||
outputPredictions = items;
|
||||
return false;
|
||||
}
|
||||
|
||||
items.Add(path);
|
||||
}
|
||||
outputPredictions = items;
|
||||
outputPredictionFailure = s_noPredictionFailure;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsStringValidAbsolutePath(string path, out string failure)
|
||||
{
|
||||
try
|
||||
{
|
||||
var isRooted = Path.IsPathRooted(path);
|
||||
if (!isRooted)
|
||||
{
|
||||
failure = $"The predicted path '{path}' is not absolute.";
|
||||
return false;
|
||||
}
|
||||
failure = string.Empty;
|
||||
return true;
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
failure = $"The predicted path '{path}' is malformed: {e.Message}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SerializeGraph(ProjectGraphWithPredictionsResult projectGraphWithPredictions, string outputFile, GraphBuilderReporter reporter)
|
||||
{
|
||||
reporter.ReportMessage("Serializing graph...");
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.ContractsLight;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BuildXL.Native.IO;
|
||||
using Microsoft.Build.Prediction;
|
||||
|
||||
namespace MsBuildGraphBuilderTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Collects predictions from the MSBuild prediction library.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This validates the predicted inputs and outputs, in case the predictor is not working properly
|
||||
/// </remarks>
|
||||
public sealed class MsBuildPredictionCollector : IProjectPredictionCollector
|
||||
{
|
||||
private readonly ConcurrentQueue<(string predictorName, string failure)> m_predictionFailures;
|
||||
|
||||
private readonly ICollection<string> m_inputFilePredictions;
|
||||
|
||||
private readonly ICollection<string> m_outputFolderPredictions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MsBuildPredictionCollector"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The provided collections will be added to by this collector as predictions (or prediction errors) happen.
|
||||
/// </remarks>
|
||||
/// <param name="inputFilePredictions">A collection of input file predictions the collector should add to as needed.</param>
|
||||
/// <param name="outputFolderPredictions">A collection of output folder predictions the collector should add to as needed.</param>
|
||||
/// <param name="predictionFailures">A collection of prediction failures the collector should add to as needed.</param>
|
||||
public MsBuildPredictionCollector(
|
||||
ICollection<string> inputFilePredictions,
|
||||
ICollection<string> outputFolderPredictions,
|
||||
ConcurrentQueue<(string predictorName, string failure)> predictionFailures)
|
||||
{
|
||||
Contract.Assert(inputFilePredictions != null);
|
||||
Contract.Assert(outputFolderPredictions != null);
|
||||
Contract.Assert(predictionFailures != null);
|
||||
|
||||
m_inputFilePredictions = inputFilePredictions;
|
||||
m_outputFolderPredictions = outputFolderPredictions;
|
||||
m_predictionFailures = predictionFailures;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddInputFile(string path, string projectDirectory, string predictorName)
|
||||
{
|
||||
if (!TryValidatePrediction(path, projectDirectory, predictorName, out string absolutePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_inputFilePredictions.Add(absolutePath);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddInputDirectory(string path, string projectDirectory, string predictorName)
|
||||
{
|
||||
if (!TryValidatePrediction(path, projectDirectory, predictorName, out string absolutePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Directory.Exists(absolutePath))
|
||||
{
|
||||
FileUtilities.EnumerateFiles(
|
||||
absolutePath,
|
||||
false,
|
||||
"*",
|
||||
(dir, fileName, attrs, length) => m_inputFilePredictions.Add(Path.Combine(dir, fileName)));
|
||||
}
|
||||
// TODO: Can we do anything to flag that the input prediction is not going to be used?
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddOutputFile(string path, string projectDirectory, string predictorName)
|
||||
{
|
||||
if (!TryValidatePrediction(path, projectDirectory, predictorName, out string absolutePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string folder = Path.GetDirectoryName(absolutePath);
|
||||
m_outputFolderPredictions.Add(folder);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddOutputDirectory(string path, string projectDirectory, string predictorName)
|
||||
{
|
||||
if (!TryValidatePrediction(path, projectDirectory, predictorName, out string absolutePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_outputFolderPredictions.Add(absolutePath);
|
||||
}
|
||||
|
||||
private bool TryValidatePrediction(string path, string projectDirectory, string predictorName, out string absolutePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
absolutePath = Path.IsPathRooted(path)
|
||||
? path
|
||||
: Path.GetFullPath(Path.Combine(projectDirectory, path));
|
||||
return true;
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
absolutePath = null;
|
||||
m_predictionFailures.Enqueue((predictorName, $"The predicted path '{path}' is malformed: {e.Message}"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ namespace MsBuildGraphBuilder {
|
|||
importFrom("Newtonsoft.Json").pkg,
|
||||
importFrom("BuildXL.FrontEnd").MsBuild.Serialization.dll,
|
||||
importFrom("BuildXL.Utilities").Collections.dll,
|
||||
importFrom("BuildXL.Utilities").Native.dll,
|
||||
importFrom("BuildXL.Utilities.Instrumentation").Common.dll,
|
||||
importFrom("System.Collections.Immutable").pkg,
|
||||
importFrom("DataflowForMSBuildRuntime").pkg,
|
||||
|
|
|
@ -11,10 +11,10 @@ namespace Test.Tool.ProjectGraphBuilder.Mocks
|
|||
/// <summary>
|
||||
/// A predictor that throws on prediction
|
||||
/// </summary>
|
||||
public class ThrowOnPredictionPredictor : IProjectStaticPredictor
|
||||
public class ThrowOnPredictionPredictor : IProjectPredictor
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool TryPredictInputsAndOutputs(Project project, ProjectInstance projectInstance, string repositoryRootDirectory, out StaticPredictions predictions)
|
||||
public void PredictInputsAndOutputs(Project project, ProjectInstance projectInstance, ProjectPredictionReporter predictionReporter)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
|
|
@ -27,25 +27,25 @@ namespace Test.ProjectGraphBuilder
|
|||
// One isolated project
|
||||
[InlineData(true, "[A]")]
|
||||
// Two isolated projects
|
||||
[InlineData(false, "[A]",
|
||||
[InlineData(false, "[A]",
|
||||
"B")]
|
||||
// Simple dependency
|
||||
[InlineData(true, "[A] -> B")]
|
||||
[InlineData(true, "[A] -> B")]
|
||||
// Transitive dependency
|
||||
[InlineData(true, "[A] -> B -> C",
|
||||
[InlineData(true, "[A] -> B -> C",
|
||||
"A -> C")]
|
||||
// Fan-out test
|
||||
[InlineData(true, "[A] -> B",
|
||||
[InlineData(true, "[A] -> B",
|
||||
"A -> C")]
|
||||
// Fan-in test
|
||||
[InlineData(false, "[A] -> B",
|
||||
[InlineData(false, "[A] -> B",
|
||||
"C -> B")]
|
||||
// Diamond test
|
||||
[InlineData(true, "[A] -> B1 -> C",
|
||||
[InlineData(true, "[A] -> B1 -> C",
|
||||
"A -> B2 -> C")]
|
||||
// Two connected components
|
||||
[InlineData(false, "A -> B",
|
||||
"[C] -> D")]
|
||||
[InlineData(false, "A -> B",
|
||||
"[C] -> D")]
|
||||
public void ValidateRoundtripProjects(bool exactMatch, params string[] projectChains)
|
||||
{
|
||||
// Write to disk the set of projects that the project chains represent
|
||||
|
@ -67,17 +67,17 @@ namespace Test.ProjectGraphBuilder
|
|||
const string DoubleReferenceProject =
|
||||
@"
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<InnerBuildProperty>InnerBuild</InnerBuildProperty>
|
||||
<InnerBuildPropertyValues>InnerBuildProperties</InnerBuildPropertyValues>
|
||||
<InnerBuildProperties>A;A</InnerBuildProperties>
|
||||
<PropertyGroup>
|
||||
<InnerBuildProperty>InnerBuild</InnerBuildProperty>
|
||||
<InnerBuildPropertyValues>InnerBuildProperties</InnerBuildPropertyValues>
|
||||
<InnerBuildProperties>A;A</InnerBuildProperties>
|
||||
</PropertyGroup>
|
||||
</Project>";
|
||||
|
||||
var entryPointPath = m_builder.WriteProjectsWithReferences(("A", DoubleReferenceProject));
|
||||
|
||||
// Parse the projects, build the graph, serialize it to disk and deserialize it back
|
||||
var projectGraphWithPredictionsResult = BuildGraphAndDeserialize(new[] { entryPointPath });
|
||||
var projectGraphWithPredictionsResult = BuildGraphAndDeserialize(new[] { entryPointPath });
|
||||
|
||||
Assert.True(projectGraphWithPredictionsResult.Succeeded);
|
||||
|
||||
|
@ -125,7 +125,7 @@ namespace Test.ProjectGraphBuilder
|
|||
|
||||
Assert.True(projectGraphWithPredictionsResult.Succeeded);
|
||||
var projectProperties = projectGraphWithPredictionsResult.Result.ProjectNodes.Single().GlobalProperties;
|
||||
|
||||
|
||||
Assert.Equal("x64", projectProperties["platform"]);
|
||||
Assert.Equal("release", projectProperties["configuration"]);
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ namespace Test.ProjectGraphBuilder
|
|||
<ProjectReference Include='B.proj'/>
|
||||
</ItemGroup>
|
||||
<Target Name='Build'/>
|
||||
</Project>"),
|
||||
</Project>"),
|
||||
("B.proj", @"
|
||||
<Project DefaultTargets='Build;Pack'>
|
||||
<Target Name='Build'/>
|
||||
|
@ -210,7 +210,7 @@ namespace Test.ProjectGraphBuilder
|
|||
Assert.True(projectGraphWithPredictionsResult.Succeeded);
|
||||
var projectB = projectGraphWithPredictionsResult.Result.ProjectNodes.First(projectNode => projectNode.FullPath.Contains("B.proj"));
|
||||
|
||||
// The targets of B should be flagged so we know default targets were appended,
|
||||
// The targets of B should be flagged so we know default targets were appended,
|
||||
// and B targets should contain the default targets
|
||||
Assert.True(projectB.PredictedTargetsToExecute.IsDefaultTargetsAppended);
|
||||
Assert.Contains("Build", projectB.PredictedTargetsToExecute.Targets);
|
||||
|
@ -234,7 +234,6 @@ namespace Test.ProjectGraphBuilder
|
|||
string outputFile = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
|
||||
var arguments = new MSBuildGraphBuilderArguments(
|
||||
TestOutputDirectory,
|
||||
projectEntryPoints,
|
||||
outputFile,
|
||||
globalProperties: GlobalProperties.Empty,
|
||||
|
@ -244,14 +243,13 @@ namespace Test.ProjectGraphBuilder
|
|||
allowProjectsWithoutTargetProtocol: true);
|
||||
|
||||
return BuildGraphAndDeserialize(arguments);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private MSBuildGraphBuilderArguments CreateBuilderArguments(string entryPointPath, GlobalProperties[] requestedQualifiers = null, GlobalProperties globalProperties = null, bool allowProjectsWithoutTargetProtocol = true)
|
||||
{
|
||||
string outputFile = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
var arguments = new MSBuildGraphBuilderArguments(
|
||||
TestOutputDirectory,
|
||||
new string[] { entryPointPath },
|
||||
outputFile,
|
||||
globalProperties: globalProperties ?? GlobalProperties.Empty,
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace Test.ProjectGraphBuilder
|
|||
public void ValidProjectFileSucceedsAndAssemblyLocationsAreSetProperly()
|
||||
{
|
||||
// Write an empty project
|
||||
string entryPoint =
|
||||
string entryPoint =
|
||||
@"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""/>";
|
||||
|
||||
|
@ -78,7 +78,6 @@ namespace Test.ProjectGraphBuilder
|
|||
using (var reporter = new GraphBuilderReporter(Guid.NewGuid().ToString()))
|
||||
{
|
||||
var arguments = new MSBuildGraphBuilderArguments(
|
||||
TemporaryDirectory,
|
||||
new[] { entryPoint },
|
||||
outputFile,
|
||||
globalProperties: GlobalProperties.Empty,
|
||||
|
|
|
@ -70,7 +70,6 @@ namespace Test.ProjectGraphBuilder
|
|||
{
|
||||
string outputFile = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
var arguments = new MSBuildGraphBuilderArguments(
|
||||
TestOutputDirectory,
|
||||
new[] { m_entryPoint },
|
||||
outputFile,
|
||||
globalProperties: GlobalProperties.Empty,
|
||||
|
|
|
@ -17,16 +17,16 @@ using Xunit.Abstractions;
|
|||
namespace Test.ProjectGraphBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes sure that static predictions are plumbed through and serialized into the project graph. The actual predictions are not tested here.
|
||||
/// Makes sure that project predictions are plumbed through and serialized into the project graph. The actual predictions are not tested here.
|
||||
/// </summary>
|
||||
public class MsBuildGraphStaticPredictionTests : TemporaryStorageTestBase
|
||||
public class MsBuildGraphProjectPredictionTests : TemporaryStorageTestBase
|
||||
{
|
||||
public MsBuildGraphStaticPredictionTests(ITestOutputHelper output): base(output)
|
||||
public MsBuildGraphProjectPredictionTests(ITestOutputHelper output): base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticPredictionsAreSerialized()
|
||||
public void ProjectPredictionsAreSerialized()
|
||||
{
|
||||
string outputFile = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
string entryPoint = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
|
@ -45,7 +45,6 @@ namespace Test.ProjectGraphBuilder
|
|||
|
||||
MsBuildGraphBuilder.BuildGraphAndSerialize(
|
||||
new MSBuildGraphBuilderArguments(
|
||||
TestOutputDirectory,
|
||||
new[] { entryPoint },
|
||||
outputFile,
|
||||
globalProperties: GlobalProperties.Empty,
|
||||
|
@ -74,7 +73,6 @@ namespace Test.ProjectGraphBuilder
|
|||
using (var reporter = new GraphBuilderReporter(Guid.NewGuid().ToString()))
|
||||
{
|
||||
var arguments = new MSBuildGraphBuilderArguments(
|
||||
TestOutputDirectory,
|
||||
new[] { entryPoint },
|
||||
outputFile,
|
||||
globalProperties: GlobalProperties.Empty,
|
||||
|
@ -84,12 +82,12 @@ namespace Test.ProjectGraphBuilder
|
|||
allowProjectsWithoutTargetProtocol: false);
|
||||
|
||||
MsBuildGraphBuilder.BuildGraphAndSerializeForTesting(
|
||||
MsBuildAssemblyLoader.Instance,
|
||||
reporter,
|
||||
MsBuildAssemblyLoader.Instance,
|
||||
reporter,
|
||||
arguments,
|
||||
new IProjectStaticPredictor[] { new ThrowOnPredictionPredictor() });
|
||||
new IProjectPredictor[] { new ThrowOnPredictionPredictor() });
|
||||
}
|
||||
|
||||
|
||||
var result = SimpleDeserializer.Instance.DeserializeGraph(outputFile);
|
||||
|
||||
// The result should gracefully fail, with some error message.
|
|
@ -0,0 +1,296 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MsBuildGraphBuilderTool;
|
||||
using Test.BuildXL.TestUtilities.Xunit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Test.ProjectGraphBuilder
|
||||
{
|
||||
public class MsBuildPredictionCollectorTests : TemporaryStorageTestBase
|
||||
{
|
||||
public MsBuildPredictionCollectorTests(ITestOutputHelper output): base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputFileHandlesAbsolutePaths()
|
||||
{
|
||||
string absolutePath = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputFile(absolutePath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(1, inputFilePredictions.Count);
|
||||
Assert.Contains(absolutePath, inputFilePredictions);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputFileHandlesRelativePaths()
|
||||
{
|
||||
string relativePath = Path.Combine(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
|
||||
string absolutePath = Path.Combine(TemporaryDirectory, relativePath);
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputFile(relativePath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(1, inputFilePredictions.Count);
|
||||
Assert.Contains(absolutePath, inputFilePredictions);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputFileHandlesBadPaths()
|
||||
{
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputFile("!@#$%^&*()", TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(1, predictionFailures.Count);
|
||||
Assert.Equal("Mock", predictionFailures.Single().predictorName);
|
||||
Assert.Contains("!@#$%^&*()", predictionFailures.Single().failure);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputDirectoryHandlesAbsolutePaths()
|
||||
{
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
string absoluteFilePath1 = Path.Combine(absoluteDirectoryPath, Guid.NewGuid().ToString());
|
||||
string absoluteFilePath2 = Path.Combine(absoluteDirectoryPath, Guid.NewGuid().ToString());
|
||||
|
||||
Directory.CreateDirectory(absoluteDirectoryPath);
|
||||
File.WriteAllText(absoluteFilePath1, Guid.NewGuid().ToString());
|
||||
File.WriteAllText(absoluteFilePath2, Guid.NewGuid().ToString());
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputDirectory(absoluteDirectoryPath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(2, inputFilePredictions.Count);
|
||||
Assert.Contains(absoluteFilePath1, inputFilePredictions);
|
||||
Assert.Contains(absoluteFilePath2, inputFilePredictions);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputDirectoryHandlesRelativePaths()
|
||||
{
|
||||
string relativeDirectoryPath = Path.Combine(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, relativeDirectoryPath);
|
||||
string absoluteFilePath1 = Path.Combine(absoluteDirectoryPath, Guid.NewGuid().ToString());
|
||||
string absoluteFilePath2 = Path.Combine(absoluteDirectoryPath, Guid.NewGuid().ToString());
|
||||
|
||||
Directory.CreateDirectory(absoluteDirectoryPath);
|
||||
File.WriteAllText(absoluteFilePath1, Guid.NewGuid().ToString());
|
||||
File.WriteAllText(absoluteFilePath2, Guid.NewGuid().ToString());
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputDirectory(relativeDirectoryPath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(2, inputFilePredictions.Count);
|
||||
Assert.Contains(absoluteFilePath1, inputFilePredictions);
|
||||
Assert.Contains(absoluteFilePath2, inputFilePredictions);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputDirectoryHandlesMissingPaths()
|
||||
{
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputDirectory(absoluteDirectoryPath, TemporaryDirectory, "Mock");
|
||||
|
||||
// The folder didn't exist, so ignore the prediction
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddInputDirectoryHandlesBadPaths()
|
||||
{
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddInputDirectory("!@#$%^&*()", TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(1, predictionFailures.Count);
|
||||
Assert.Equal("Mock", predictionFailures.Single().predictorName);
|
||||
Assert.Contains("!@#$%^&*()", predictionFailures.Single().failure);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOutputFileHandlesAbsolutePaths()
|
||||
{
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
string absoluteFilePath = Path.Combine(absoluteDirectoryPath, Guid.NewGuid().ToString());
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddOutputFile(absoluteFilePath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(1, outputFolderPredictions.Count);
|
||||
Assert.Contains(absoluteDirectoryPath, outputFolderPredictions);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOutputFileHandlesRelativePaths()
|
||||
{
|
||||
string relativeDirectoryPath = Guid.NewGuid().ToString();
|
||||
string relativeFilePath = Path.Combine(relativeDirectoryPath, Guid.NewGuid().ToString());
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, relativeDirectoryPath);
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddOutputFile(relativeFilePath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(1, outputFolderPredictions.Count);
|
||||
Assert.Contains(absoluteDirectoryPath, outputFolderPredictions);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOutputFileHandlesBadPaths()
|
||||
{
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddOutputFile("!@#$%^&*()", TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(1, predictionFailures.Count);
|
||||
Assert.Equal("Mock", predictionFailures.Single().predictorName);
|
||||
Assert.Contains("!@#$%^&*()", predictionFailures.Single().failure);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOutputDirectoryHandlesAbsolutePaths()
|
||||
{
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, Guid.NewGuid().ToString());
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddOutputDirectory(absoluteDirectoryPath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(1, outputFolderPredictions.Count);
|
||||
Assert.Contains(absoluteDirectoryPath, outputFolderPredictions);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOutputDirectoryHandlesRelativePaths()
|
||||
{
|
||||
string relativeDirectoryPath = Path.Combine(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
|
||||
string absoluteDirectoryPath = Path.Combine(TemporaryDirectory, relativeDirectoryPath);
|
||||
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddOutputDirectory(relativeDirectoryPath, TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(1, outputFolderPredictions.Count);
|
||||
Assert.Contains(absoluteDirectoryPath, outputFolderPredictions);
|
||||
|
||||
Assert.Equal(0, predictionFailures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOutputDirectoryHandlesBadPaths()
|
||||
{
|
||||
var inputFilePredictions = new List<string>();
|
||||
var outputFolderPredictions = new List<string>();
|
||||
var predictionFailures = new ConcurrentQueue<(string predictorName, string failure)>();
|
||||
var collector = new MsBuildPredictionCollector(inputFilePredictions, outputFolderPredictions, predictionFailures);
|
||||
|
||||
collector.AddOutputDirectory("!@#$%^&*()", TemporaryDirectory, "Mock");
|
||||
|
||||
Assert.Equal(0, inputFilePredictions.Count);
|
||||
|
||||
Assert.Equal(0, outputFolderPredictions.Count);
|
||||
|
||||
Assert.Equal(1, predictionFailures.Count);
|
||||
Assert.Equal("Mock", predictionFailures.Single().predictorName);
|
||||
Assert.Contains("!@#$%^&*()", predictionFailures.Single().failure);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -445,7 +445,7 @@ config({
|
|||
{ id: "System.CodeDom", version: "4.4.0"},
|
||||
|
||||
// Used for MSBuild input/output prediction
|
||||
{ id: "Microsoft.Build.Prediction", version: "0.1.0" },
|
||||
{ id: "Microsoft.Build.Prediction", version: "0.2.0" },
|
||||
|
||||
{ id: "SharpZipLib", version: "1.1.0" },
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче