Browse Source

Address feedback about how we do evaluation

Fixes: #362

This change splits up our evaluation code-path into a few steps:

- Load project
- Do a restore
- Mark dirty
- Run targets and read properties

The key is that we need to do a restore separately from other targets
because restore will bring in nuget packages that contain targets. You
need to let MSBuild load those before continuing. The code here is
pretty similar to what dotnet does.

Additionally blocking the NBGV scenario, there are some additional
assemblies that need to be provided by MSBuild. NBGV also loads
Microsoft.DotNet.Platform.Abstractions which is part of the SDK. Since
we had an allow-list of just nuget assemblies that wasn't resolved.
davidfowl/local-ip-range
Ryan Nowak 7 years ago
parent
commit
2878c7ffe8
  1. 62
      src/Microsoft.Tye.Core/ProjectReader.cs

62
src/Microsoft.Tye.Core/ProjectReader.cs

@ -145,16 +145,24 @@ namespace Microsoft.Tye
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
// Currently we only log at debug level.
var logger = new ConsoleLogger(
verbosity: LoggerVerbosity.Normal,
write: message => output.WriteDebug(message),
colorSet: null,
colorReset: null);
// We need to isolate projects from each other for testing. MSBuild does not support // We need to isolate projects from each other for testing. MSBuild does not support
// loading the same project twice in the same collection. // loading the same project twice in the same collection.
var projectCollection = new ProjectCollection(); var projectCollection = new ProjectCollection();
ProjectInstance projectInstance; ProjectInstance projectInstance;
Microsoft.Build.Evaluation.Project msbuildProject;
try try
{ {
output.WriteDebugLine($"Loading project '{project.ProjectFile.FullName}'."); output.WriteDebugLine($"Loading project '{project.ProjectFile.FullName}'.");
var msbuildProject = Microsoft.Build.Evaluation.Project.FromFile(project.ProjectFile.FullName, new ProjectOptions() msbuildProject = Microsoft.Build.Evaluation.Project.FromFile(project.ProjectFile.FullName, new ProjectOptions()
{ {
ProjectCollection = projectCollection, ProjectCollection = projectCollection,
}); });
@ -166,18 +174,45 @@ namespace Microsoft.Tye
throw new CommandException($"Failed to load project: '{project.ProjectFile.FullName}'.", ex); throw new CommandException($"Failed to load project: '{project.ProjectFile.FullName}'.", ex);
} }
// Currently we only log at debug level.
var logger = new ConsoleLogger(
verbosity: LoggerVerbosity.Normal,
write: message => output.WriteDebug(message),
colorSet: null,
colorReset: null);
try try
{ {
AssemblyLoadContext.Default.Resolving += ResolveAssembly; AssemblyLoadContext.Default.Resolving += ResolveAssembly;
output.WriteDebugLine($"Restoring project '{project.ProjectFile.FullName}'.");
// Similar to what MSBuild does for restore:
// https://github.com/microsoft/msbuild/blob/3453beee039fb6f5ccc54ac783ebeced31fec472/src/MSBuild/XMake.cs#L1417
//
// We need to do restore as a separate operation
var restoreRequest = new BuildRequestData(
projectInstance,
targetsToBuild: new[] { "Restore" },
hostServices: null,
flags: BuildRequestDataFlags.ClearCachesAfterBuild | BuildRequestDataFlags.SkipNonexistentTargets | BuildRequestDataFlags.IgnoreMissingEmptyAndInvalidImports);
var parameters = new BuildParameters(projectCollection)
{
Loggers = new[] { logger, },
};
// We don't really look at the result, because it's not clear we should halt totally
// if restore fails.
var restoreResult = BuildManager.DefaultBuildManager.Build(parameters, restoreRequest);
output.WriteDebugLine($"Restored project '{project.ProjectFile.FullName}'.");
msbuildProject.MarkDirty();
projectInstance = msbuildProject.CreateProjectInstance();
var targets = new List<string>()
{
"ResolveReferences",
"ResolvePackageDependenciesDesignTime",
"PrepareResources",
"GetAssemblyAttributes",
};
var result = projectInstance.Build( var result = projectInstance.Build(
targets: new[] { "Restore", "ResolveReferences", "ResolvePackageDependenciesDesignTime", "PrepareResources", "GetAssemblyAttributes", }, targets: targets.ToArray(),
loggers: new[] { logger, }); loggers: new[] { logger, });
// If the build fails, we're not really blocked from doing our work. // If the build fails, we're not really blocked from doing our work.
@ -189,8 +224,11 @@ namespace Microsoft.Tye
AssemblyLoadContext.Default.Resolving -= ResolveAssembly; AssemblyLoadContext.Default.Resolving -= ResolveAssembly;
} }
// Reading both InformationalVersion and Version is more resilient in the face of build failures. // Reading a few different version properties to be more resilient.
var version = projectInstance.GetProperty("InformationalVersion")?.EvaluatedValue ?? projectInstance.GetProperty("Version").EvaluatedValue; var version =
projectInstance.GetProperty("AssemblyInformationalVersion")?.EvaluatedValue ??
projectInstance.GetProperty("InformationalVersion")?.EvaluatedValue ??
projectInstance.GetProperty("Version").EvaluatedValue;
project.Version = version; project.Version = version;
output.WriteDebugLine($"Found application version: {version}"); output.WriteDebugLine($"Found application version: {version}");
@ -254,7 +292,7 @@ namespace Microsoft.Tye
// See: https://github.com/microsoft/MSBuildLocator/issues/86 // See: https://github.com/microsoft/MSBuildLocator/issues/86
Assembly? ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName) Assembly? ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
{ {
if (assemblyName.Name is object && assemblyName.Name.StartsWith("NuGet.")) if (assemblyName.Name is object)
{ {
var msbuildDirectory = Environment.GetEnvironmentVariable("MSBuildExtensionsPath")!; var msbuildDirectory = Environment.GetEnvironmentVariable("MSBuildExtensionsPath")!;
var assemblyFilePath = Path.Combine(msbuildDirectory, assemblyName.Name + ".dll"); var assemblyFilePath = Path.Combine(msbuildDirectory, assemblyName.Name + ".dll");

Loading…
Cancel
Save