Upgrade Microsoft.Build.Prediction to 0.2.0 (#480)

Upgrade Microsoft.Build.Prediction to 0.2.0
This commit is contained in:
David Federman 2019-06-17 16:35:50 -07:00 коммит произвёл GitHub
Родитель e0b0ceabf8
Коммит a725108f10
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 485 добавлений и 174 удалений

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

@ -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" },