refactor WorkspaceContext (#2736)
this removes a regression in ProjectModelServer behavior also added a test to cover target framework change in DTH
This commit is contained in:
parent
2e22c3ac4b
commit
f32683cbab
35 changed files with 821 additions and 635 deletions
|
@ -230,11 +230,11 @@ namespace Microsoft.DotNet.Cli.Build
|
|||
var projectFile = Path.Combine(Dirs.RepoRoot, "src", projectName, "project.json");
|
||||
|
||||
dotnet.Pack(
|
||||
projectFile,
|
||||
"--no-build",
|
||||
"--build-base-path", packagingBuildBasePath,
|
||||
"--output", Dirs.PackagesIntermediate,
|
||||
"--configuration", configuration,
|
||||
projectFile,
|
||||
"--no-build",
|
||||
"--build-base-path", packagingBuildBasePath,
|
||||
"--output", Dirs.PackagesIntermediate,
|
||||
"--configuration", configuration,
|
||||
"--version-suffix", versionSuffix)
|
||||
.Execute()
|
||||
.EnsureSuccessful();
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
}
|
||||
}
|
||||
|
||||
private static void WaitForDebugger()
|
||||
public static void WaitForDebugger()
|
||||
{
|
||||
Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue");
|
||||
Console.WriteLine($"Process ID: {Process.GetCurrentProcess().Id}");
|
||||
|
|
|
@ -17,9 +17,11 @@ using Microsoft.DotNet.ProjectModel.Compilation;
|
|||
using Microsoft.DotNet.ProjectModel.Files;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
using RoslynWorkspace = Microsoft.CodeAnalysis.Workspace;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel.Workspaces
|
||||
{
|
||||
public class ProjectJsonWorkspace : Workspace
|
||||
public class ProjectJsonWorkspace : RoslynWorkspace
|
||||
{
|
||||
private Dictionary<string, AssemblyMetadata> _cache = new Dictionary<string, AssemblyMetadata>();
|
||||
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using RoslynWorkspace = Microsoft.CodeAnalysis.Workspace;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel.Workspaces
|
||||
{
|
||||
public static class WorkspaceProjectContextExtensions
|
||||
{
|
||||
public static Workspace CreateWorkspace(this ProjectContext context)
|
||||
{
|
||||
return new ProjectJsonWorkspace(context);
|
||||
}
|
||||
public static RoslynWorkspace CreateRoslynWorkspace(this ProjectContext context) => new ProjectJsonWorkspace(context);
|
||||
}
|
||||
}
|
81
src/Microsoft.DotNet.ProjectModel/BuildWorkspace.cs
Normal file
81
src/Microsoft.DotNet.ProjectModel/BuildWorkspace.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel
|
||||
{
|
||||
public class BuildWorkspace : Workspace
|
||||
{
|
||||
public BuildWorkspace(ProjectReaderSettings settings) : base(settings) { }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty <see cref="WorkspaceContext" /> using the default <see cref="ProjectReaderSettings" />
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static BuildWorkspace Create() => Create(versionSuffix: string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty <see cref="WorkspaceContext" /> using the default <see cref="ProjectReaderSettings" />, with the specified Version Suffix
|
||||
/// </summary>
|
||||
/// <param name="versionSuffix">The suffix to use to replace any '-*' snapshot tokens in Project versions.</param>
|
||||
/// <returns></returns>
|
||||
public static BuildWorkspace Create(string versionSuffix)
|
||||
{
|
||||
var settings = ProjectReaderSettings.ReadFromEnvironment();
|
||||
if (!string.IsNullOrEmpty(versionSuffix))
|
||||
{
|
||||
settings.VersionSuffix = versionSuffix;
|
||||
}
|
||||
return new BuildWorkspace(settings);
|
||||
}
|
||||
|
||||
public ProjectContext GetRuntimeContext(ProjectContext context, IEnumerable<string> runtimeIdentifiers)
|
||||
{
|
||||
if (!runtimeIdentifiers.Any())
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
var contexts = GetProjectContextCollection(context.ProjectDirectory);
|
||||
if (contexts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var runtimeContext = runtimeIdentifiers
|
||||
.Select(r => contexts.GetTarget(context.TargetFramework, r))
|
||||
.FirstOrDefault(c => c != null);
|
||||
|
||||
if (runtimeContext == null)
|
||||
{
|
||||
if (context.IsPortable)
|
||||
{
|
||||
// We're specializing a portable target, so synthesize a runtime target manually
|
||||
// We don't cache this project context, but we'll still use the cached Project and LockFile
|
||||
return CreateBaseProjectBuilder(context.ProjectFile)
|
||||
.WithTargetFramework(context.TargetFramework)
|
||||
.WithRuntimeIdentifiers(runtimeIdentifiers)
|
||||
.AsDesignTime()
|
||||
.Build();
|
||||
}
|
||||
|
||||
// We are standalone, but don't support this runtime
|
||||
var rids = string.Join(", ", runtimeIdentifiers);
|
||||
throw new InvalidOperationException($"Can not find runtime target for framework '{context.TargetFramework}' compatible with one of the target runtimes: '{rids}'. " +
|
||||
"Possible causes:" + Environment.NewLine +
|
||||
"1. The project has not been restored or restore failed - run `dotnet restore`" + Environment.NewLine +
|
||||
$"2. The project does not list one of '{rids}' in the 'runtimes' section.");
|
||||
}
|
||||
|
||||
return runtimeContext;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ProjectContext> BuildProjectContexts(Project project)
|
||||
{
|
||||
return CreateBaseProjectBuilder(project).BuildAllTargets();
|
||||
}
|
||||
}
|
||||
}
|
148
src/Microsoft.DotNet.ProjectModel/DesignTimeWorkspace.cs
Normal file
148
src/Microsoft.DotNet.ProjectModel/DesignTimeWorkspace.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.ProjectModel.Graph;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel
|
||||
{
|
||||
public class DesignTimeWorkspace : Workspace
|
||||
{
|
||||
private readonly HashSet<string> _projects = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private bool _needRefresh;
|
||||
|
||||
public DesignTimeWorkspace(ProjectReaderSettings settings) : base(settings) { }
|
||||
|
||||
public void AddProject(string path)
|
||||
{
|
||||
var projectPath = NormalizeProjectPath(path);
|
||||
|
||||
if (projectPath != null)
|
||||
{
|
||||
_needRefresh = _projects.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveProject(string path)
|
||||
{
|
||||
_needRefresh = _projects.Remove(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh all cached projects in the Workspace
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
if (!_needRefresh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var basePaths = new List<string>(_projects);
|
||||
_projects.Clear();
|
||||
|
||||
foreach (var projectDirectory in basePaths)
|
||||
{
|
||||
var project = GetProject(projectDirectory);
|
||||
if (project == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_projects.Add(project.ProjectDirectory);
|
||||
|
||||
foreach (var projectContext in GetProjectContextCollection(project.ProjectDirectory).ProjectContexts)
|
||||
{
|
||||
foreach (var reference in GetProjectReferences(projectContext))
|
||||
{
|
||||
var referencedProject = GetProject(reference.Path);
|
||||
if (referencedProject != null)
|
||||
{
|
||||
_projects.Add(referencedProject.ProjectDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_needRefresh = false;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ProjectContext> BuildProjectContexts(Project project)
|
||||
{
|
||||
foreach (var framework in project.GetTargetFrameworks())
|
||||
{
|
||||
yield return CreateBaseProjectBuilder(project)
|
||||
.AsDesignTime()
|
||||
.WithTargetFramework(framework.FrameworkName)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> ResolveProjectPath(string projectPath)
|
||||
{
|
||||
if (File.Exists(projectPath))
|
||||
{
|
||||
var filename = Path.GetFileName(projectPath);
|
||||
if (!Project.FileName.Equals(filename, StringComparison.OrdinalIgnoreCase) &&
|
||||
!GlobalSettings.FileName.Equals(filename, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
projectPath = Path.GetDirectoryName(projectPath);
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(projectPath, Project.FileName)))
|
||||
{
|
||||
return new List<string> { projectPath };
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(projectPath, GlobalSettings.FileName)))
|
||||
{
|
||||
var root = ProjectRootResolver.ResolveRootDirectory(projectPath);
|
||||
GlobalSettings globalSettings;
|
||||
if (GlobalSettings.TryGetGlobalSettings(projectPath, out globalSettings))
|
||||
{
|
||||
return globalSettings.ProjectSearchPaths
|
||||
.Select(searchPath => Path.Combine(globalSettings.DirectoryPath, searchPath))
|
||||
.Where(actualPath => Directory.Exists(actualPath))
|
||||
.SelectMany(actualPath => Directory.GetDirectories(actualPath))
|
||||
.Where(actualPath => File.Exists(Path.Combine(actualPath, Project.FileName)))
|
||||
.Select(path => Path.GetFullPath(path))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<ProjectDescription> GetProjectReferences(ProjectContext context)
|
||||
{
|
||||
var projectDescriptions = context.LibraryManager
|
||||
.GetLibraries()
|
||||
.Where(lib => lib.Identity.Type == LibraryType.Project)
|
||||
.OfType<ProjectDescription>();
|
||||
|
||||
foreach (var description in projectDescriptions)
|
||||
{
|
||||
if (description.Identity.Name == context.ProjectFile.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this is an assembly reference then don't threat it as project reference
|
||||
if (!string.IsNullOrEmpty(description.TargetFrameworkInfo?.AssemblyPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -135,7 +135,7 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
yield return new ProjectContextBuilder()
|
||||
.WithProject(project)
|
||||
.WithTargetFramework(framework.FrameworkName)
|
||||
.WithReaderSettings(settings)
|
||||
.WithProjectReaderSettings(settings)
|
||||
.WithRuntimeIdentifiers(runtimeIdentifiers ?? Enumerable.Empty<string>())
|
||||
.Build();
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
var project = ProjectReader.GetProject(projectPath);
|
||||
|
||||
return new ProjectContextBuilder()
|
||||
.WithReaderSettings(settings)
|
||||
.WithProjectReaderSettings(settings)
|
||||
.WithProject(project)
|
||||
.BuildAllTargets();
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.DotNet.InternalAbstractions;
|
||||
using Microsoft.DotNet.ProjectModel.Graph;
|
||||
|
@ -15,12 +17,13 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
{
|
||||
public class ProjectContextBuilder
|
||||
{
|
||||
// Note: When adding a property, make sure to add it to Clone below. You'll also need to update the CloneTest in
|
||||
// Microsoft.DotNet.ProjectModel.Tests.ProjectContextBuilderTests
|
||||
|
||||
private Project Project { get; set; }
|
||||
|
||||
private LockFile LockFile { get; set; }
|
||||
|
||||
private GlobalSettings GlobalSettings { get; set; }
|
||||
|
||||
private NuGetFramework TargetFramework { get; set; }
|
||||
|
||||
private IEnumerable<string> RuntimeIdentifiers { get; set; } = Enumerable.Empty<string>();
|
||||
|
@ -39,7 +42,7 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
|
||||
private Func<string, LockFile> LockFileResolver { get; set; }
|
||||
|
||||
private ProjectReaderSettings Settings { get; set; } = ProjectReaderSettings.ReadFromEnvironment();
|
||||
private ProjectReaderSettings ProjectReaderSettings { get; set; } = ProjectReaderSettings.ReadFromEnvironment();
|
||||
|
||||
public ProjectContextBuilder()
|
||||
{
|
||||
|
@ -47,6 +50,28 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
LockFileResolver = ResolveLockFile;
|
||||
}
|
||||
|
||||
public ProjectContextBuilder Clone()
|
||||
{
|
||||
var builder = new ProjectContextBuilder()
|
||||
.WithLockFile(LockFile)
|
||||
.WithProject(Project)
|
||||
.WithProjectDirectory(ProjectDirectory)
|
||||
.WithTargetFramework(TargetFramework)
|
||||
.WithRuntimeIdentifiers(RuntimeIdentifiers)
|
||||
.WithReferenceAssembliesPath(ReferenceAssembliesPath)
|
||||
.WithPackagesDirectory(PackagesDirectory)
|
||||
.WithRootDirectory(RootDirectory)
|
||||
.WithProjectResolver(ProjectResolver)
|
||||
.WithLockFileResolver(LockFileResolver)
|
||||
.WithProjectReaderSettings(ProjectReaderSettings);
|
||||
if(IsDesignTime)
|
||||
{
|
||||
builder.AsDesignTime();
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public ProjectContextBuilder WithLockFile(LockFile lockFile)
|
||||
{
|
||||
LockFile = lockFile;
|
||||
|
@ -113,9 +138,9 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
return this;
|
||||
}
|
||||
|
||||
public ProjectContextBuilder WithReaderSettings(ProjectReaderSettings settings)
|
||||
public ProjectContextBuilder WithProjectReaderSettings(ProjectReaderSettings projectReaderSettings)
|
||||
{
|
||||
Settings = settings;
|
||||
ProjectReaderSettings = projectReaderSettings;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -125,13 +150,20 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produce all targets found in the lock file associated with this builder.
|
||||
/// Returns an empty enumerable if there is no lock file
|
||||
/// (making this unsuitable for scenarios where the lock file may not be present,
|
||||
/// such as at design-time)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ProjectContext> BuildAllTargets()
|
||||
{
|
||||
ProjectDirectory = Project?.ProjectDirectory ?? ProjectDirectory;
|
||||
EnsureProjectLoaded();
|
||||
LockFile = LockFile ?? LockFileResolver(ProjectDirectory);
|
||||
|
||||
if (LockFile != null && LockFile.Targets.Any())
|
||||
if (LockFile != null)
|
||||
{
|
||||
var deduper = new HashSet<string>();
|
||||
foreach (var target in LockFile.Targets)
|
||||
|
@ -139,15 +171,9 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
var id = $"{target.TargetFramework}/{target.RuntimeIdentifier}";
|
||||
if (deduper.Add(id))
|
||||
{
|
||||
var builder = new ProjectContextBuilder()
|
||||
.WithProject(Project)
|
||||
.WithLockFile(LockFile)
|
||||
var builder = Clone()
|
||||
.WithTargetFramework(target.TargetFramework)
|
||||
.WithRuntimeIdentifiers(new[] { target.RuntimeIdentifier });
|
||||
if (IsDesignTime)
|
||||
{
|
||||
builder.AsDesignTime();
|
||||
}
|
||||
|
||||
yield return builder.Build();
|
||||
}
|
||||
|
@ -155,8 +181,7 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
}
|
||||
else
|
||||
{
|
||||
// Build a context for each framework. It won't be fully valid, since it won't have resolved data or runtime data, but the diagnostics will show that
|
||||
// (Project Model Server needs this)
|
||||
// Build a context for each framework. It won't be fully valid, since it won't have resolved data or runtime data, but the diagnostics will show that.
|
||||
foreach (var framework in Project.GetTargetFrameworks())
|
||||
{
|
||||
var builder = new ProjectContextBuilder()
|
||||
|
@ -177,19 +202,15 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
|
||||
ProjectDirectory = Project?.ProjectDirectory ?? ProjectDirectory;
|
||||
|
||||
if (GlobalSettings == null && ProjectDirectory != null)
|
||||
GlobalSettings globalSettings = null;
|
||||
if (ProjectDirectory != null)
|
||||
{
|
||||
RootDirectory = ProjectRootResolver.ResolveRootDirectory(ProjectDirectory);
|
||||
|
||||
GlobalSettings globalSettings;
|
||||
if (GlobalSettings.TryGetGlobalSettings(RootDirectory, out globalSettings))
|
||||
{
|
||||
GlobalSettings = globalSettings;
|
||||
}
|
||||
GlobalSettings.TryGetGlobalSettings(RootDirectory, out globalSettings);
|
||||
}
|
||||
|
||||
RootDirectory = GlobalSettings?.DirectoryPath ?? RootDirectory;
|
||||
PackagesDirectory = PackagesDirectory ?? PackageDependencyProvider.ResolvePackagesPath(RootDirectory, GlobalSettings);
|
||||
RootDirectory = globalSettings?.DirectoryPath ?? RootDirectory;
|
||||
PackagesDirectory = PackagesDirectory ?? PackageDependencyProvider.ResolvePackagesPath(RootDirectory, globalSettings);
|
||||
|
||||
FrameworkReferenceResolver frameworkReferenceResolver;
|
||||
if (string.IsNullOrEmpty(ReferenceAssembliesPath))
|
||||
|
@ -343,7 +364,7 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
var libraryManager = new LibraryManager(libraries.Values.ToList(), diagnostics, Project?.ProjectFilePath);
|
||||
|
||||
return new ProjectContext(
|
||||
GlobalSettings,
|
||||
globalSettings,
|
||||
mainProject,
|
||||
platformLibrary,
|
||||
TargetFramework,
|
||||
|
@ -570,7 +591,7 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
private Project ResolveProject(string projectDirectory)
|
||||
{
|
||||
Project project;
|
||||
if (ProjectReader.TryGetProject(projectDirectory, out project, settings: Settings))
|
||||
if (ProjectReader.TryGetProject(projectDirectory, out project, settings: ProjectReaderSettings))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,14 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
{
|
||||
public Project Project { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the ProjectContexts in this collection
|
||||
/// </summary>
|
||||
public List<ProjectContext> ProjectContexts { get; } = new List<ProjectContext>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ProjectContexts in this collection which are not runtime-specific (i.e. the ones used for compilation)
|
||||
/// </summary>
|
||||
public IEnumerable<ProjectContext> FrameworkOnlyContexts => ProjectContexts.Where(c => string.IsNullOrEmpty(c.RuntimeIdentifier));
|
||||
|
||||
public List<DiagnosticMessage> ProjectDiagnostics { get; } = new List<DiagnosticMessage>();
|
||||
|
|
300
src/Microsoft.DotNet.ProjectModel/Workspace.cs
Normal file
300
src/Microsoft.DotNet.ProjectModel/Workspace.cs
Normal file
|
@ -0,0 +1,300 @@
|
|||
// Copyright (c) .NET Foundation and contributors. 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 Microsoft.DotNet.ProjectModel.Graph;
|
||||
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache of Projects, LockFiles, and ProjectContexts
|
||||
/// </summary>
|
||||
public abstract class Workspace
|
||||
{
|
||||
// key: project directory
|
||||
private readonly ConcurrentDictionary<string, FileModelEntry<Project>> _projectsCache
|
||||
= new ConcurrentDictionary<string, FileModelEntry<Project>>();
|
||||
|
||||
// key: project directory
|
||||
private readonly ConcurrentDictionary<string, FileModelEntry<LockFile>> _lockFileCache
|
||||
= new ConcurrentDictionary<string, FileModelEntry<LockFile>>();
|
||||
|
||||
// key: project directory, target framework
|
||||
private readonly ConcurrentDictionary<string, ProjectContextCollection> _projectContextsCache
|
||||
= new ConcurrentDictionary<string, ProjectContextCollection>();
|
||||
|
||||
private readonly ProjectReaderSettings _settings;
|
||||
private readonly LockFileReader _lockFileReader;
|
||||
|
||||
protected Workspace(ProjectReaderSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_lockFileReader = new LockFileReader();
|
||||
}
|
||||
|
||||
public ProjectContext GetProjectContext(string projectPath, NuGetFramework framework)
|
||||
{
|
||||
var contexts = GetProjectContextCollection(projectPath);
|
||||
if (contexts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return contexts
|
||||
.ProjectContexts
|
||||
.FirstOrDefault(c => Equals(c.TargetFramework, framework) && string.IsNullOrEmpty(c.RuntimeIdentifier));
|
||||
}
|
||||
|
||||
public ProjectContextCollection GetProjectContextCollection(string projectPath)
|
||||
{
|
||||
var normalizedPath = NormalizeProjectPath(projectPath);
|
||||
if (normalizedPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _projectContextsCache.AddOrUpdate(
|
||||
normalizedPath,
|
||||
key => AddProjectContextEntry(key, null),
|
||||
(key, oldEntry) => AddProjectContextEntry(key, oldEntry));
|
||||
}
|
||||
|
||||
public Project GetProject(string projectDirectory) => GetProjectCore(projectDirectory)?.Model;
|
||||
|
||||
private LockFile GetLockFile(string projectDirectory)
|
||||
{
|
||||
var normalizedPath = NormalizeProjectPath(projectDirectory);
|
||||
if (normalizedPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _lockFileCache.AddOrUpdate(
|
||||
normalizedPath,
|
||||
key => AddLockFileEntry(key, null),
|
||||
(key, oldEntry) => AddLockFileEntry(key, oldEntry)).Model;
|
||||
}
|
||||
|
||||
|
||||
private FileModelEntry<Project> GetProjectCore(string projectDirectory)
|
||||
{
|
||||
var normalizedPath = NormalizeProjectPath(projectDirectory);
|
||||
if (normalizedPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _projectsCache.AddOrUpdate(
|
||||
normalizedPath,
|
||||
key => AddProjectEntry(key, null),
|
||||
(key, oldEntry) => AddProjectEntry(key, oldEntry));
|
||||
}
|
||||
|
||||
|
||||
protected static string NormalizeProjectPath(string path)
|
||||
{
|
||||
if (File.Exists(path) &&
|
||||
string.Equals(Path.GetFileName(path), Project.FileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Path.GetDirectoryName(Path.GetFullPath(path));
|
||||
}
|
||||
else if (Directory.Exists(path) &&
|
||||
File.Exists(Path.Combine(path, Project.FileName)))
|
||||
{
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private FileModelEntry<Project> AddProjectEntry(string projectDirectory, FileModelEntry<Project> currentEntry)
|
||||
{
|
||||
if (currentEntry == null)
|
||||
{
|
||||
currentEntry = new FileModelEntry<Project>();
|
||||
}
|
||||
else if (!File.Exists(Path.Combine(projectDirectory, Project.FileName)))
|
||||
{
|
||||
// project was deleted
|
||||
currentEntry.Reset();
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
if (currentEntry.IsInvalid)
|
||||
{
|
||||
Project project;
|
||||
if (!ProjectReader.TryGetProject(projectDirectory, out project, _settings))
|
||||
{
|
||||
currentEntry.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentEntry.Diagnostics.AddRange(project.Diagnostics);
|
||||
currentEntry.Model = project;
|
||||
currentEntry.FilePath = project.ProjectFilePath;
|
||||
currentEntry.UpdateLastWriteTimeUtc();
|
||||
}
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
private FileModelEntry<LockFile> AddLockFileEntry(string projectDirectory, FileModelEntry<LockFile> currentEntry)
|
||||
{
|
||||
if (currentEntry == null)
|
||||
{
|
||||
currentEntry = new FileModelEntry<LockFile>();
|
||||
}
|
||||
|
||||
if (currentEntry.IsInvalid)
|
||||
{
|
||||
currentEntry.Reset();
|
||||
|
||||
if (!File.Exists(Path.Combine(projectDirectory, LockFile.FileName)))
|
||||
{
|
||||
return currentEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentEntry.FilePath = Path.Combine(projectDirectory, LockFile.FileName);
|
||||
|
||||
using (var fs = ResilientFileStreamOpener.OpenFile(currentEntry.FilePath, retry: 2))
|
||||
{
|
||||
try
|
||||
{
|
||||
currentEntry.Model = _lockFileReader.ReadLockFile(currentEntry.FilePath, fs, designTime: true);
|
||||
currentEntry.UpdateLastWriteTimeUtc();
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
throw ex.WithFilePath(currentEntry.FilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw FileFormatException.Create(ex, currentEntry.FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
private ProjectContextCollection AddProjectContextEntry(string projectDirectory,
|
||||
ProjectContextCollection currentEntry)
|
||||
{
|
||||
if (currentEntry == null)
|
||||
{
|
||||
// new entry required
|
||||
currentEntry = new ProjectContextCollection();
|
||||
}
|
||||
|
||||
var projectEntry = GetProjectCore(projectDirectory);
|
||||
|
||||
if (projectEntry?.Model == null)
|
||||
{
|
||||
// project doesn't exist anymore
|
||||
currentEntry.Reset();
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
var project = projectEntry.Model;
|
||||
if (currentEntry.HasChanged)
|
||||
{
|
||||
currentEntry.Reset();
|
||||
|
||||
var contexts = BuildProjectContexts(project);
|
||||
|
||||
currentEntry.ProjectContexts.AddRange(contexts);
|
||||
|
||||
currentEntry.Project = project;
|
||||
currentEntry.ProjectFilePath = project.ProjectFilePath;
|
||||
currentEntry.LastProjectFileWriteTimeUtc = File.GetLastWriteTimeUtc(currentEntry.ProjectFilePath);
|
||||
|
||||
var lockFilePath = Path.Combine(project.ProjectDirectory, LockFile.FileName);
|
||||
if (File.Exists(lockFilePath))
|
||||
{
|
||||
currentEntry.LockFilePath = lockFilePath;
|
||||
currentEntry.LastLockFileWriteTimeUtc = File.GetLastWriteTimeUtc(lockFilePath);
|
||||
}
|
||||
|
||||
currentEntry.ProjectDiagnostics.AddRange(projectEntry.Diagnostics);
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<ProjectContext> BuildProjectContexts(Project project);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ProjectContextBuilder configured to use the Workspace caches.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected ProjectContextBuilder CreateBaseProjectBuilder()
|
||||
{
|
||||
return new ProjectContextBuilder()
|
||||
.WithProjectReaderSettings(_settings)
|
||||
.WithProjectResolver(path => GetProjectCore(path)?.Model)
|
||||
.WithLockFileResolver(path => GetLockFile(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ProjectContextBuilder configured to use the Workspace caches, and the specified root project.
|
||||
/// </summary>
|
||||
/// <param name="root">The root project</param>
|
||||
/// <returns></returns>
|
||||
protected ProjectContextBuilder CreateBaseProjectBuilder(Project root)
|
||||
{
|
||||
return CreateBaseProjectBuilder().WithProject(root);
|
||||
}
|
||||
|
||||
protected class FileModelEntry<TModel> where TModel : class
|
||||
{
|
||||
private DateTime _lastWriteTimeUtc;
|
||||
|
||||
public TModel Model { get; set; }
|
||||
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public List<DiagnosticMessage> Diagnostics { get; } = new List<DiagnosticMessage>();
|
||||
|
||||
public void UpdateLastWriteTimeUtc()
|
||||
{
|
||||
_lastWriteTimeUtc = File.GetLastWriteTimeUtc(FilePath);
|
||||
}
|
||||
|
||||
public bool IsInvalid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Model == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _lastWriteTimeUtc < File.GetLastWriteTimeUtc(FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Model = null;
|
||||
FilePath = null;
|
||||
Diagnostics.Clear();
|
||||
_lastWriteTimeUtc = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,507 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. 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 Microsoft.DotNet.ProjectModel.Graph;
|
||||
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel
|
||||
{
|
||||
public class WorkspaceContext
|
||||
{
|
||||
// key: project directory
|
||||
private readonly ConcurrentDictionary<string, FileModelEntry<Project>> _projectsCache
|
||||
= new ConcurrentDictionary<string, FileModelEntry<Project>>();
|
||||
|
||||
// key: project directory
|
||||
private readonly ConcurrentDictionary<string, FileModelEntry<LockFile>> _lockFileCache
|
||||
= new ConcurrentDictionary<string, FileModelEntry<LockFile>>();
|
||||
|
||||
// key: project directory, target framework
|
||||
private readonly ConcurrentDictionary<string, ProjectContextCollection> _projectContextsCache
|
||||
= new ConcurrentDictionary<string, ProjectContextCollection>();
|
||||
|
||||
private readonly HashSet<string> _projects = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private bool _needRefresh;
|
||||
private readonly ProjectReaderSettings _settings;
|
||||
private readonly bool _designTime;
|
||||
private readonly LockFileReader _lockFileReader;
|
||||
|
||||
private WorkspaceContext(IEnumerable<string> projectPaths, ProjectReaderSettings settings, bool designTime)
|
||||
{
|
||||
_settings = settings;
|
||||
_designTime = designTime;
|
||||
_lockFileReader = new LockFileReader();
|
||||
|
||||
foreach (var path in projectPaths)
|
||||
{
|
||||
AddProject(path);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a WorkspaceContext from a given path.
|
||||
///
|
||||
/// If the given path points to a global.json, all the projects found under the search paths
|
||||
/// are added to the WorkspaceContext.
|
||||
///
|
||||
/// If the given path points to a project.json, all the projects it referenced as well as itself
|
||||
/// are added to the WorkspaceContext.
|
||||
///
|
||||
/// If no path is provided, the workspace context is created empty and projects must be manually added
|
||||
/// to it using <see cref="AddProject(string)"/>.
|
||||
/// </summary>
|
||||
public static WorkspaceContext CreateFrom(string projectPath, bool designTime)
|
||||
{
|
||||
var projectPaths = ResolveProjectPath(projectPath);
|
||||
if (projectPaths == null || !projectPaths.Any())
|
||||
{
|
||||
return new WorkspaceContext(Enumerable.Empty<string>(), ProjectReaderSettings.ReadFromEnvironment(), designTime);
|
||||
}
|
||||
|
||||
var context = new WorkspaceContext(projectPaths, ProjectReaderSettings.ReadFromEnvironment(), designTime);
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty <see cref="WorkspaceContext" /> using the default <see cref="ProjectReaderSettings" />
|
||||
/// </summary>
|
||||
/// <param name="designTime">A boolean indicating if the workspace should be created in Design-Time mode</param>
|
||||
/// <returns></returns>
|
||||
public static WorkspaceContext Create(bool designTime) => Create(ProjectReaderSettings.ReadFromEnvironment(), designTime);
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty <see cref="WorkspaceContext" /> using the default <see cref="ProjectReaderSettings" />, with the specified Version Suffix
|
||||
/// </summary>
|
||||
/// <param name="versionSuffix">The suffix to use to replace any '-*' snapshot tokens in Project versions.</param>
|
||||
/// <param name="designTime">A boolean indicating if the workspace should be created in Design-Time mode</param>
|
||||
/// <returns></returns>
|
||||
public static WorkspaceContext Create(string versionSuffix, bool designTime)
|
||||
{
|
||||
var settings = ProjectReaderSettings.ReadFromEnvironment();
|
||||
if (!string.IsNullOrEmpty(versionSuffix))
|
||||
{
|
||||
settings.VersionSuffix = versionSuffix;
|
||||
}
|
||||
return Create(settings, designTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty <see cref="WorkspaceContext" /> using the provided <see cref="ProjectReaderSettings" />.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings to use when reading projects</param>
|
||||
/// <param name="designTime">A boolean indicating if the workspace should be created in Design-Time mode</param>
|
||||
/// <returns></returns>
|
||||
public static WorkspaceContext Create(ProjectReaderSettings settings, bool designTime)
|
||||
{
|
||||
return new WorkspaceContext(Enumerable.Empty<string>(), settings, designTime);
|
||||
}
|
||||
|
||||
public void AddProject(string path)
|
||||
{
|
||||
var projectPath = ProjectPathHelper.NormalizeProjectDirectoryPath(path);
|
||||
|
||||
if (projectPath != null)
|
||||
{
|
||||
_needRefresh = _projects.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveProject(string path)
|
||||
{
|
||||
_needRefresh = _projects.Remove(path);
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetAllProjects()
|
||||
{
|
||||
Refresh();
|
||||
return _projects.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the WorkspaceContext to update projects collection
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
if (!_needRefresh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var basePaths = new List<string>(_projects);
|
||||
_projects.Clear();
|
||||
|
||||
foreach (var projectDirectory in basePaths)
|
||||
{
|
||||
var project = GetProjectCore(projectDirectory).Model;
|
||||
if (project == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_projects.Add(project.ProjectDirectory);
|
||||
|
||||
foreach (var projectContext in GetProjectContexts(project.ProjectDirectory))
|
||||
{
|
||||
foreach (var reference in GetProjectReferences(projectContext))
|
||||
{
|
||||
var referencedProject = GetProjectCore(reference.Path).Model;
|
||||
if (referencedProject != null)
|
||||
{
|
||||
_projects.Add(referencedProject.ProjectDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_needRefresh = false;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ProjectContext> GetProjectContexts(string projectPath)
|
||||
{
|
||||
return (IReadOnlyList<ProjectContext>)GetProjectContextCollection(projectPath)?.ProjectContexts.AsReadOnly() ??
|
||||
EmptyArray<ProjectContext>.Value;
|
||||
}
|
||||
|
||||
public ProjectContext GetProjectContext(string projectPath, NuGetFramework framework)
|
||||
{
|
||||
var contexts = GetProjectContextCollection(projectPath);
|
||||
if (contexts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return contexts
|
||||
.ProjectContexts
|
||||
.FirstOrDefault(c => Equals(c.TargetFramework, framework) && string.IsNullOrEmpty(c.RuntimeIdentifier));
|
||||
}
|
||||
|
||||
public ProjectContext GetRuntimeContext(ProjectContext context, IEnumerable<string> runtimeIdentifiers)
|
||||
{
|
||||
if(!runtimeIdentifiers.Any())
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
var contexts = GetProjectContextCollection(context.ProjectDirectory);
|
||||
if (contexts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var runtimeContext = runtimeIdentifiers
|
||||
.Select(r => contexts.GetTarget(context.TargetFramework, r))
|
||||
.FirstOrDefault(c => c != null);
|
||||
|
||||
if (runtimeContext == null)
|
||||
{
|
||||
if (context.IsPortable)
|
||||
{
|
||||
// We're specializing a portable target, so synthesize a runtime target manually
|
||||
// We don't cache this project context, but we'll still use the cached Project and LockFile
|
||||
return InitializeProjectContextBuilder(context.ProjectFile)
|
||||
.WithTargetFramework(context.TargetFramework)
|
||||
.WithRuntimeIdentifiers(runtimeIdentifiers)
|
||||
.Build();
|
||||
}
|
||||
|
||||
// We are standalone, but don't support this runtime
|
||||
var rids = string.Join(", ", runtimeIdentifiers);
|
||||
throw new InvalidOperationException($"Can not find runtime target for framework '{context.TargetFramework}' compatible with one of the target runtimes: '{rids}'. " +
|
||||
"Possible causes:" + Environment.NewLine +
|
||||
"1. The project has not been restored or restore failed - run `dotnet restore`" + Environment.NewLine +
|
||||
$"2. The project does not list one of '{rids}' in the 'runtimes' section.");
|
||||
}
|
||||
|
||||
return runtimeContext;
|
||||
}
|
||||
|
||||
public Project GetProject(string projectDirectory) => GetProjectCore(projectDirectory)?.Model;
|
||||
|
||||
public ProjectContextCollection GetProjectContextCollection(string projectPath)
|
||||
{
|
||||
var normalizedPath = ProjectPathHelper.NormalizeProjectDirectoryPath(projectPath);
|
||||
if (normalizedPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _projectContextsCache.AddOrUpdate(
|
||||
normalizedPath,
|
||||
key => AddProjectContextEntry(key, null),
|
||||
(key, oldEntry) => AddProjectContextEntry(key, oldEntry));
|
||||
}
|
||||
|
||||
private FileModelEntry<Project> GetProjectCore(string projectDirectory)
|
||||
{
|
||||
var normalizedPath = ProjectPathHelper.NormalizeProjectDirectoryPath(projectDirectory);
|
||||
if (normalizedPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _projectsCache.AddOrUpdate(
|
||||
normalizedPath,
|
||||
key => AddProjectEntry(key, null),
|
||||
(key, oldEntry) => AddProjectEntry(key, oldEntry));
|
||||
}
|
||||
|
||||
private LockFile GetLockFile(string projectDirectory)
|
||||
{
|
||||
var normalizedPath = ProjectPathHelper.NormalizeProjectDirectoryPath(projectDirectory);
|
||||
if (normalizedPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _lockFileCache.AddOrUpdate(
|
||||
normalizedPath,
|
||||
key => AddLockFileEntry(key, null),
|
||||
(key, oldEntry) => AddLockFileEntry(key, oldEntry)).Model;
|
||||
}
|
||||
|
||||
private FileModelEntry<Project> AddProjectEntry(string projectDirectory, FileModelEntry<Project> currentEntry)
|
||||
{
|
||||
if (currentEntry == null)
|
||||
{
|
||||
currentEntry = new FileModelEntry<Project>();
|
||||
}
|
||||
else if (!File.Exists(Path.Combine(projectDirectory, Project.FileName)))
|
||||
{
|
||||
// project was deleted
|
||||
currentEntry.Reset();
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
if (currentEntry.IsInvalid)
|
||||
{
|
||||
Project project;
|
||||
if (!ProjectReader.TryGetProject(projectDirectory, out project, _settings))
|
||||
{
|
||||
currentEntry.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentEntry.Model = project;
|
||||
currentEntry.Diagnostics.AddRange(project.Diagnostics);
|
||||
currentEntry.FilePath = project.ProjectFilePath;
|
||||
currentEntry.UpdateLastWriteTimeUtc();
|
||||
}
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
private FileModelEntry<LockFile> AddLockFileEntry(string projectDirectory, FileModelEntry<LockFile> currentEntry)
|
||||
{
|
||||
if (currentEntry == null)
|
||||
{
|
||||
currentEntry = new FileModelEntry<LockFile>();
|
||||
}
|
||||
|
||||
if (currentEntry.IsInvalid)
|
||||
{
|
||||
currentEntry.Reset();
|
||||
|
||||
if (!File.Exists(Path.Combine(projectDirectory, LockFile.FileName)))
|
||||
{
|
||||
return currentEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentEntry.FilePath = Path.Combine(projectDirectory, LockFile.FileName);
|
||||
|
||||
using (var fs = ResilientFileStreamOpener.OpenFile(currentEntry.FilePath, retry: 2))
|
||||
{
|
||||
try
|
||||
{
|
||||
currentEntry.Model = _lockFileReader.ReadLockFile(currentEntry.FilePath, fs, designTime: true);
|
||||
currentEntry.UpdateLastWriteTimeUtc();
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
throw ex.WithFilePath(currentEntry.FilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw FileFormatException.Create(ex, currentEntry.FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
private ProjectContextCollection AddProjectContextEntry(string projectDirectory,
|
||||
ProjectContextCollection currentEntry)
|
||||
{
|
||||
if (currentEntry == null)
|
||||
{
|
||||
// new entry required
|
||||
currentEntry = new ProjectContextCollection();
|
||||
}
|
||||
|
||||
var projectEntry = GetProjectCore(projectDirectory);
|
||||
|
||||
if (projectEntry?.Model == null)
|
||||
{
|
||||
// project doesn't exist anymore
|
||||
currentEntry.Reset();
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
var project = projectEntry.Model;
|
||||
if (currentEntry.HasChanged)
|
||||
{
|
||||
currentEntry.Reset();
|
||||
|
||||
var builder = InitializeProjectContextBuilder(project);
|
||||
|
||||
currentEntry.ProjectContexts.AddRange(builder.BuildAllTargets());
|
||||
|
||||
currentEntry.Project = project;
|
||||
currentEntry.ProjectFilePath = project.ProjectFilePath;
|
||||
currentEntry.LastProjectFileWriteTimeUtc = File.GetLastWriteTimeUtc(currentEntry.ProjectFilePath);
|
||||
|
||||
var lockFilePath = Path.Combine(project.ProjectDirectory, LockFile.FileName);
|
||||
if (File.Exists(lockFilePath))
|
||||
{
|
||||
currentEntry.LockFilePath = lockFilePath;
|
||||
currentEntry.LastLockFileWriteTimeUtc = File.GetLastWriteTimeUtc(lockFilePath);
|
||||
}
|
||||
|
||||
currentEntry.ProjectDiagnostics.AddRange(projectEntry.Diagnostics);
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
private ProjectContextBuilder InitializeProjectContextBuilder(Project project)
|
||||
{
|
||||
var builder = new ProjectContextBuilder()
|
||||
.WithProjectResolver(path => GetProjectCore(path)?.Model)
|
||||
.WithLockFileResolver(path => GetLockFile(path))
|
||||
.WithProject(project);
|
||||
if (_designTime)
|
||||
{
|
||||
builder.AsDesignTime();
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private class FileModelEntry<TModel> where TModel : class
|
||||
{
|
||||
private DateTime _lastWriteTimeUtc;
|
||||
|
||||
public TModel Model { get; set; }
|
||||
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public List<DiagnosticMessage> Diagnostics { get; } = new List<DiagnosticMessage>();
|
||||
|
||||
public void UpdateLastWriteTimeUtc()
|
||||
{
|
||||
_lastWriteTimeUtc = File.GetLastWriteTimeUtc(FilePath);
|
||||
}
|
||||
|
||||
public bool IsInvalid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Model == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _lastWriteTimeUtc < File.GetLastWriteTimeUtc(FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Model = null;
|
||||
FilePath = null;
|
||||
Diagnostics.Clear();
|
||||
_lastWriteTimeUtc = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> ResolveProjectPath(string projectPath)
|
||||
{
|
||||
if (File.Exists(projectPath))
|
||||
{
|
||||
var filename = Path.GetFileName(projectPath);
|
||||
if (!Project.FileName.Equals(filename, StringComparison.OrdinalIgnoreCase) &&
|
||||
!GlobalSettings.FileName.Equals(filename, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
projectPath = Path.GetDirectoryName(projectPath);
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(projectPath, Project.FileName)))
|
||||
{
|
||||
return new List<string> { projectPath };
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(projectPath, GlobalSettings.FileName)))
|
||||
{
|
||||
var root = ProjectRootResolver.ResolveRootDirectory(projectPath);
|
||||
GlobalSettings globalSettings;
|
||||
if (GlobalSettings.TryGetGlobalSettings(projectPath, out globalSettings))
|
||||
{
|
||||
return globalSettings.ProjectSearchPaths
|
||||
.Select(searchPath => Path.Combine(globalSettings.DirectoryPath, searchPath))
|
||||
.Where(actualPath => Directory.Exists(actualPath))
|
||||
.SelectMany(actualPath => Directory.GetDirectories(actualPath))
|
||||
.Where(actualPath => File.Exists(Path.Combine(actualPath, Project.FileName)))
|
||||
.Select(path => Path.GetFullPath(path))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<ProjectDescription> GetProjectReferences(ProjectContext context)
|
||||
{
|
||||
var projectDescriptions = context.LibraryManager
|
||||
.GetLibraries()
|
||||
.Where(lib => lib.Identity.Type == LibraryType.Project)
|
||||
.OfType<ProjectDescription>();
|
||||
|
||||
foreach (var description in projectDescriptions)
|
||||
{
|
||||
if (description.Identity.Name == context.ProjectFile.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this is an assembly reference then don't threat it as project reference
|
||||
if (!string.IsNullOrEmpty(description.TargetFrameworkInfo?.AssemblyPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,14 +45,14 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
public bool ShouldNotUseIncrementality { get; set; }
|
||||
public bool ShouldSkipDependencies { get; set; }
|
||||
|
||||
public WorkspaceContext Workspace { get; private set; }
|
||||
public BuildWorkspace Workspace { get; private set; }
|
||||
|
||||
// workaround: CommandLineApplication is internal therefore I cannot make _app protected so baseclasses can add their own params
|
||||
private readonly Dictionary<string, CommandOption> baseClassOptions;
|
||||
|
||||
public BuildCommandApp(string name, string fullName, string description) : this(name, fullName, description, workspace: null) { }
|
||||
|
||||
public BuildCommandApp(string name, string fullName, string description, WorkspaceContext workspace)
|
||||
public BuildCommandApp(string name, string fullName, string description, BuildWorkspace workspace)
|
||||
{
|
||||
Workspace = workspace;
|
||||
_app = new CommandLineApplication
|
||||
|
@ -108,13 +108,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
// Set defaults based on the environment
|
||||
if (Workspace == null)
|
||||
{
|
||||
var settings = ProjectReaderSettings.ReadFromEnvironment();
|
||||
|
||||
if (!string.IsNullOrEmpty(VersionSuffixValue))
|
||||
{
|
||||
settings.VersionSuffix = VersionSuffixValue;
|
||||
}
|
||||
Workspace = WorkspaceContext.Create(settings, designTime: false);
|
||||
Workspace = BuildWorkspace.Create(VersionSuffixValue);
|
||||
}
|
||||
|
||||
var files = new ProjectGlobbingResolver().Resolve(_projectArgument.Values);
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Cli.Compiler.Common;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Build
|
||||
{
|
||||
|
@ -19,21 +18,21 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
private readonly string _outputPath;
|
||||
private readonly string _buildBasePath;
|
||||
private readonly IList<string> _runtimes;
|
||||
private readonly WorkspaceContext _workspace;
|
||||
private readonly BuildWorkspace _workspace;
|
||||
private readonly ConcurrentDictionary<ProjectContextIdentity, CompilerIO> _cache;
|
||||
|
||||
public CompilerIOManager(string configuration,
|
||||
string outputPath,
|
||||
string buildBasePath,
|
||||
IEnumerable<string> runtimes,
|
||||
WorkspaceContext workspace)
|
||||
BuildWorkspace workspace)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_outputPath = outputPath;
|
||||
_buildBasePath = buildBasePath;
|
||||
_runtimes = runtimes.ToList();
|
||||
_workspace = workspace;
|
||||
|
||||
|
||||
_cache = new ConcurrentDictionary<ProjectContextIdentity, CompilerIO>();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Build
|
||||
|
@ -17,7 +16,7 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
{
|
||||
public static int Run(string[] args) => Run(args, null);
|
||||
|
||||
public static int Run(string[] args, WorkspaceContext workspace)
|
||||
public static int Run(string[] args, BuildWorkspace workspace)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
|
|
|
@ -15,18 +15,18 @@ namespace Microsoft.DotNet.Tools.Pack
|
|||
private readonly string _buildBasePath;
|
||||
private readonly string _configuration;
|
||||
|
||||
private readonly string _versionSuffix;
|
||||
private readonly BuildWorkspace _workspace;
|
||||
|
||||
public BuildProjectCommand(
|
||||
Project project,
|
||||
string buildBasePath,
|
||||
string configuration,
|
||||
string versionSuffix)
|
||||
BuildWorkspace workspace)
|
||||
{
|
||||
_project = project;
|
||||
_buildBasePath = buildBasePath;
|
||||
_configuration = configuration;
|
||||
_versionSuffix = versionSuffix;
|
||||
_workspace = workspace;
|
||||
}
|
||||
|
||||
public int Execute()
|
||||
|
@ -37,11 +37,8 @@ namespace Microsoft.DotNet.Tools.Pack
|
|||
argsBuilder.Add("--configuration");
|
||||
argsBuilder.Add($"{_configuration}");
|
||||
|
||||
if (!string.IsNullOrEmpty(_versionSuffix))
|
||||
{
|
||||
argsBuilder.Add("--version-suffix");
|
||||
argsBuilder.Add(_versionSuffix);
|
||||
}
|
||||
// Passing the Workspace along will flow the version suffix,
|
||||
// so we don't need to pass it as an argument.
|
||||
|
||||
if (!string.IsNullOrEmpty(_buildBasePath))
|
||||
{
|
||||
|
@ -51,7 +48,7 @@ namespace Microsoft.DotNet.Tools.Pack
|
|||
|
||||
argsBuilder.Add($"{_project.ProjectFilePath}");
|
||||
|
||||
var result = Build.BuildCommand.Run(argsBuilder.ToArray());
|
||||
var result = Build.BuildCommand.Run(argsBuilder.ToArray(), _workspace);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Microsoft.DotNet.Tools.Pack
|
|||
var packDiagnostics = new List<DiagnosticMessage>();
|
||||
|
||||
var mainPackageGenerator = new PackageGenerator(project, _configuration, _artifactPathsCalculator);
|
||||
var symbolsPackageGenerator =
|
||||
var symbolsPackageGenerator =
|
||||
new SymbolPackageGenerator(project, _configuration, _artifactPathsCalculator);
|
||||
|
||||
return mainPackageGenerator.BuildPackage(_contexts, packDiagnostics) &&
|
||||
|
|
|
@ -51,19 +51,13 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
}
|
||||
|
||||
// Set defaults based on the environment
|
||||
var settings = ProjectReaderSettings.ReadFromEnvironment();
|
||||
var versionSuffixValue = versionSuffix.Value();
|
||||
|
||||
if (!string.IsNullOrEmpty(versionSuffixValue))
|
||||
{
|
||||
settings.VersionSuffix = versionSuffixValue;
|
||||
}
|
||||
var workspace = BuildWorkspace.Create(versionSuffix.Value());
|
||||
|
||||
var configValue = configuration.Value() ?? Cli.Utils.Constants.DefaultConfiguration;
|
||||
var outputValue = output.Value();
|
||||
var buildBasePathValue = buildBasePath.Value();
|
||||
|
||||
var contexts = ProjectContext.CreateContextForEachFramework(pathValue, settings);
|
||||
var contexts = workspace.GetProjectContextCollection(pathValue).FrameworkOnlyContexts;
|
||||
var project = contexts.First().ProjectFile;
|
||||
|
||||
var artifactPathsCalculator = new ArtifactPathsCalculator(project, buildBasePathValue, outputValue, configValue);
|
||||
|
@ -72,7 +66,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
int buildResult = 0;
|
||||
if (!noBuild.HasValue())
|
||||
{
|
||||
var buildProjectCommand = new BuildProjectCommand(project, buildBasePathValue, configValue, versionSuffixValue);
|
||||
var buildProjectCommand = new BuildProjectCommand(project, buildBasePathValue, configValue, workspace);
|
||||
buildResult = buildProjectCommand.Execute();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
public ConnectionContext(Socket acceptedSocket,
|
||||
string hostName,
|
||||
ProtocolManager protocolManager,
|
||||
WorkspaceContext workspaceContext,
|
||||
DesignTimeWorkspace workspaceContext,
|
||||
IDictionary<int, ProjectManager> projects)
|
||||
{
|
||||
_hostName = hostName;
|
||||
|
@ -59,4 +59,4 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
return _queue.Send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.ProjectModel.Server.Helpers;
|
||||
using Microsoft.DotNet.ProjectModel.Server.Models;
|
||||
|
@ -20,7 +19,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
public ErrorMessage GlobalErrorMessage { get; set; }
|
||||
public Dictionary<NuGetFramework, ProjectContextSnapshot> ProjectContexts { get; } = new Dictionary<NuGetFramework, ProjectContextSnapshot>();
|
||||
|
||||
public static ProjectSnapshot Create(string projectDirectory, string configuration, WorkspaceContext workspaceContext, IReadOnlyList<string> projectSearchPaths)
|
||||
public static ProjectSnapshot Create(string projectDirectory, string configuration, DesignTimeWorkspace workspaceContext, IReadOnlyList<string> projectSearchPaths)
|
||||
{
|
||||
var projectContextsCollection = workspaceContext.GetProjectContextCollection(projectDirectory);
|
||||
if (!projectContextsCollection.ProjectContexts.Any())
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
public class ProjectModelServerCommand
|
||||
{
|
||||
private readonly Dictionary<int, ProjectManager> _projects;
|
||||
private readonly WorkspaceContext _workspaceContext;
|
||||
private readonly DesignTimeWorkspace _workspaceContext;
|
||||
private readonly ProtocolManager _protocolManager;
|
||||
private readonly string _hostName;
|
||||
private readonly int _port;
|
||||
|
@ -25,7 +25,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
_port = port;
|
||||
_hostName = hostName;
|
||||
_protocolManager = new ProtocolManager(maxVersion: 4);
|
||||
_workspaceContext = WorkspaceContext.Create(designTime: true);
|
||||
_workspaceContext = new DesignTimeWorkspace(ProjectReaderSettings.ReadFromEnvironment());
|
||||
_projects = new Dictionary<int, ProjectManager>();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
private ProjectSnapshot _local = new ProjectSnapshot();
|
||||
private ProjectSnapshot _remote = new ProjectSnapshot();
|
||||
|
||||
private readonly WorkspaceContext _workspaceContext;
|
||||
private readonly DesignTimeWorkspace _workspaceContext;
|
||||
private int? _contextProtocolVersion;
|
||||
|
||||
private readonly List<Messenger<ProjectContextSnapshot>> _messengers;
|
||||
|
@ -42,7 +42,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
|
|||
|
||||
public ProjectManager(
|
||||
int contextId,
|
||||
WorkspaceContext workspaceContext,
|
||||
DesignTimeWorkspace workspaceContext,
|
||||
ProtocolManager protocolManager)
|
||||
{
|
||||
Id = contextId;
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace Microsoft.DotNet.Tools.Publish
|
|||
publish.VersionSuffix = versionSuffix.Value();
|
||||
publish.ShouldBuild = !noBuild.HasValue();
|
||||
|
||||
publish.Workspace = WorkspaceContext.Create(versionSuffix.Value(), designTime: false);
|
||||
publish.Workspace = BuildWorkspace.Create(versionSuffix.Value());
|
||||
|
||||
if (string.IsNullOrEmpty(publish.ProjectPath))
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Microsoft.DotNet.Tools.Publish
|
|||
public string Runtime { get; set; }
|
||||
public bool NativeSubdirectories { get; set; }
|
||||
public NuGetFramework NugetFramework { get; set; }
|
||||
public WorkspaceContext Workspace { get; set; }
|
||||
public BuildWorkspace Workspace { get; set; }
|
||||
public IList<ProjectContext> ProjectContexts { get; set; }
|
||||
public string VersionSuffix { get; set; }
|
||||
public int NumberOfProjects { get; private set; }
|
||||
|
@ -253,7 +253,7 @@ namespace Microsoft.DotNet.Tools.Publish
|
|||
args.Add(buildBasePath);
|
||||
}
|
||||
|
||||
var result = Build.BuildCommand.Run(args.ToArray());
|
||||
var result = Build.BuildCommand.Run(args.ToArray(), Workspace);
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Microsoft.DotNet.Tools.Run
|
|||
|
||||
ProjectContext _context;
|
||||
List<string> _args;
|
||||
private WorkspaceContext _workspace;
|
||||
private BuildWorkspace _workspace;
|
||||
|
||||
public int Start()
|
||||
{
|
||||
|
@ -113,7 +113,7 @@ namespace Microsoft.DotNet.Tools.Run
|
|||
private int RunExecutable()
|
||||
{
|
||||
// Set up the workspace
|
||||
_workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
|
||||
_workspace = new BuildWorkspace(ProjectReaderSettings.ReadFromEnvironment());
|
||||
|
||||
CalculateDefaultsForNonAssigned();
|
||||
|
||||
|
@ -125,7 +125,7 @@ namespace Microsoft.DotNet.Tools.Run
|
|||
$"--configuration",
|
||||
Configuration,
|
||||
$"{_context.ProjectFile.ProjectDirectory}"
|
||||
});
|
||||
}, _workspace);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
|
|
|
@ -8,26 +8,26 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
{
|
||||
public abstract class BaseDotnetTestRunner : IDotnetTestRunner
|
||||
{
|
||||
public int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
|
||||
public int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams, BuildWorkspace workspace)
|
||||
{
|
||||
var result = BuildTestProject(projectContext, dotnetTestParams);
|
||||
var result = BuildTestProject(projectContext, dotnetTestParams, workspace);
|
||||
|
||||
return result == 0 ? DoRunTests(projectContext, dotnetTestParams) : result;
|
||||
}
|
||||
|
||||
internal abstract int DoRunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams);
|
||||
|
||||
private int BuildTestProject(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
|
||||
private int BuildTestProject(ProjectContext projectContext, DotnetTestParams dotnetTestParams, BuildWorkspace workspace)
|
||||
{
|
||||
if (dotnetTestParams.NoBuild)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DoBuildTestProject(projectContext, dotnetTestParams);
|
||||
return DoBuildTestProject(projectContext, dotnetTestParams, workspace);
|
||||
}
|
||||
|
||||
private int DoBuildTestProject(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
|
||||
private int DoBuildTestProject(ProjectContext projectContext, DotnetTestParams dotnetTestParams, BuildWorkspace workspace)
|
||||
{
|
||||
var strings = new List<string>
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
strings.Add(projectContext.RuntimeIdentifier);
|
||||
}
|
||||
|
||||
var result = Build.BuildCommand.Run(strings.ToArray());
|
||||
var result = Build.BuildCommand.Run(strings.ToArray(), workspace);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
|
||||
public NuGetFramework Framework { get; set; }
|
||||
|
||||
public string UnparsedFramework { get; set; }
|
||||
|
||||
public List<string> RemainingArguments { get; set; }
|
||||
|
||||
public bool NoBuild { get; set; }
|
||||
|
@ -97,6 +99,7 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
Port = port;
|
||||
}
|
||||
|
||||
UnparsedFramework = _frameworkOption.Value();
|
||||
if (_frameworkOption.HasValue())
|
||||
{
|
||||
Framework = NuGetFramework.Parse(_frameworkOption.Value());
|
||||
|
|
|
@ -8,6 +8,6 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
{
|
||||
public interface IDotnetTestRunner
|
||||
{
|
||||
int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams);
|
||||
int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams, BuildWorkspace workspace);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,12 +48,19 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
var exitCode = 0;
|
||||
|
||||
// Create a workspace
|
||||
var workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
|
||||
var workspace = new BuildWorkspace(ProjectReaderSettings.ReadFromEnvironment());
|
||||
|
||||
if (dotnetTestParams.Framework != null)
|
||||
{
|
||||
var projectContext = ProjectContext.Create(projectPath, dotnetTestParams.Framework, runtimeIdentifiers);
|
||||
exitCode = RunTest(projectContext, dotnetTestParams);
|
||||
var projectContext = workspace.GetProjectContext(projectPath, dotnetTestParams.Framework);
|
||||
if (projectContext == null)
|
||||
{
|
||||
Reporter.Error.WriteLine($"Project '{projectPath}' does not support framework: {dotnetTestParams.UnparsedFramework}");
|
||||
return 1;
|
||||
}
|
||||
projectContext = workspace.GetRuntimeContext(projectContext, runtimeIdentifiers);
|
||||
|
||||
exitCode = RunTest(projectContext, dotnetTestParams, workspace);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -66,7 +73,7 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
// Execute for all TFMs the project targets.
|
||||
foreach (var projectContext in projectContexts)
|
||||
{
|
||||
var result = RunTest(projectContext, dotnetTestParams);
|
||||
var result = RunTest(projectContext, dotnetTestParams, workspace);
|
||||
if (result == 0)
|
||||
{
|
||||
summary.Passed++;
|
||||
|
@ -134,11 +141,11 @@ namespace Microsoft.DotNet.Tools.Test
|
|||
}
|
||||
}
|
||||
|
||||
private int RunTest(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
|
||||
private int RunTest(ProjectContext projectContext, DotnetTestParams dotnetTestParams, BuildWorkspace workspace)
|
||||
{
|
||||
var testRunner = projectContext.ProjectFile.TestRunner;
|
||||
var dotnetTestRunner = _dotnetTestRunnerFactory.Create(dotnetTestParams.Port);
|
||||
return dotnetTestRunner.RunTests(projectContext, dotnetTestParams);
|
||||
return dotnetTestRunner.RunTests(projectContext, dotnetTestParams, workspace);
|
||||
}
|
||||
|
||||
private static string GetProjectPath(string projectPath)
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.ProjectModel.Graph;
|
||||
using NuGet.Frameworks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectModel.Tests
|
||||
{
|
||||
public class ProjectContextBuilderTests
|
||||
{
|
||||
private static readonly HashSet<string> KnownProperties = new HashSet<string>(StringComparer.Ordinal) {
|
||||
"Project",
|
||||
"LockFile",
|
||||
"TargetFramework",
|
||||
"RuntimeIdentifiers",
|
||||
"RootDirectory",
|
||||
"ProjectDirectory",
|
||||
"PackagesDirectory",
|
||||
"ReferenceAssembliesPath",
|
||||
"IsDesignTime",
|
||||
"ProjectResolver",
|
||||
"LockFileResolver",
|
||||
"ProjectReaderSettings"
|
||||
};
|
||||
|
||||
// This test ensures that Clone is always kept up-to-date to avoid hard-to-debug errors
|
||||
// because someone added a property but didn't put it in Clone.
|
||||
[Fact]
|
||||
public void CloneTest()
|
||||
{
|
||||
// Initialize a test instance that we're going to clone. Make sure all properties are initialized here.
|
||||
var initialBuilder = new ProjectContextBuilder()
|
||||
.WithProject(new Project())
|
||||
.WithLockFile(new LockFile("abc"))
|
||||
.WithTargetFramework(FrameworkConstants.CommonFrameworks.NetStandard10)
|
||||
.WithRuntimeIdentifiers(new[] { "win7-x64", "osx.10.10-x64" })
|
||||
.WithRootDirectory("C:\\The\\Root")
|
||||
.WithProjectDirectory("/where/the/project/at")
|
||||
.WithPackagesDirectory("D:\\My\\Awesome\\NuGet\\Packages")
|
||||
.WithReferenceAssembliesPath("/these/are/the/reference/assemblies")
|
||||
.WithProjectResolver(_ => new Project())
|
||||
.WithLockFileResolver(_ => new LockFile("def"))
|
||||
.WithProjectReaderSettings(new ProjectReaderSettings());
|
||||
|
||||
// Clone the builder
|
||||
var clonedBuilder = initialBuilder.Clone();
|
||||
|
||||
// Compare all the properties. This is a shallow clone, so they should all be exactly ReferenceEqual
|
||||
foreach(var prop in typeof(ProjectContextBuilder).GetTypeInfo().DeclaredProperties)
|
||||
{
|
||||
KnownProperties.Remove(prop.Name).Should().BeTrue(because: $"{prop.Name} should be listed in the known properties to ensure it is properly tested.");
|
||||
|
||||
if (prop.PropertyType.GetTypeInfo().IsValueType)
|
||||
{
|
||||
// Can't use reference equality on value types
|
||||
prop.GetValue(clonedBuilder).Should().Be(prop.GetValue(initialBuilder), because: $"clone should have duplicated the {prop.Name} property");
|
||||
}
|
||||
else
|
||||
{
|
||||
prop.GetValue(clonedBuilder).Should().BeSameAs(prop.GetValue(initialBuilder), because: $"clone should have duplicated the {prop.Name} property");
|
||||
}
|
||||
}
|
||||
|
||||
KnownProperties.Should().BeEmpty(because: "all properties should have been checked by the CloneTest");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ using Microsoft.DotNet.Cli.Utils;
|
|||
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||
using Xunit;
|
||||
using Microsoft.DotNet.TestFramework;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Builder.Tests
|
||||
{
|
||||
|
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
using Moq;
|
||||
using NuGet.Frameworks;
|
||||
using Xunit;
|
||||
|
@ -20,7 +19,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
|
|||
private Mock<ICompiler> _nativeCompilerMock;
|
||||
private List<ProjectContext> _contexts;
|
||||
private BuildCommandApp _args;
|
||||
private readonly WorkspaceContext _workspace;
|
||||
private readonly BuildWorkspace _workspace;
|
||||
|
||||
public GivenACompilationDriverController()
|
||||
{
|
||||
|
@ -35,13 +34,13 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
|
|||
.Compile(It.IsAny<ProjectContext>(), It.IsAny<BuildCommandApp>()))
|
||||
.Returns(true);
|
||||
|
||||
_workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
|
||||
_workspace = new BuildWorkspace(ProjectReaderSettings.ReadFromEnvironment());
|
||||
_contexts = new List<ProjectContext>
|
||||
{
|
||||
_workspace.GetProjectContext(_projectJson, NuGetFramework.Parse("netcoreapp1.0"))
|
||||
};
|
||||
|
||||
_args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform", WorkspaceContext.Create(designTime: false));
|
||||
_args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform", _workspace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -187,7 +187,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
|
|||
It.IsAny<string>()))
|
||||
.Returns(command.Object);
|
||||
|
||||
var _args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform", WorkspaceContext.Create(designTime: false));
|
||||
var _args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform", new BuildWorkspace(new ProjectReaderSettings()));
|
||||
_args.ConfigValue = ConfigValue;
|
||||
|
||||
PreCompileScriptVariables = new Dictionary<string, string>();
|
||||
|
@ -218,7 +218,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
|
|||
rids.Add(rid);
|
||||
}
|
||||
|
||||
var workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
|
||||
var workspace = new BuildWorkspace(new ProjectReaderSettings());
|
||||
var context = workspace.GetRuntimeContext(workspace.GetProjectContext(projectJson, TestAssetFramework), rids);
|
||||
context = workspace.GetRuntimeContext(context, rids);
|
||||
managedCompiler.Compile(context, _args);
|
||||
|
|
|
@ -53,12 +53,12 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
Task.Run(() => ReadMessage(_readCancellationToken.Token), _readCancellationToken.Token);
|
||||
}
|
||||
|
||||
public void SendPayLoad(Project project, string messageType)
|
||||
public void SendPayload(Project project, string messageType)
|
||||
{
|
||||
SendPayLoad(project.ProjectDirectory, messageType);
|
||||
SendPayload(project.ProjectDirectory, messageType);
|
||||
}
|
||||
|
||||
public void SendPayLoad(string projectPath, string messageType)
|
||||
public void SendPayload(string projectPath, string messageType)
|
||||
{
|
||||
int contextId;
|
||||
if (!_projectContexts.TryGetValue(projectPath, out contextId))
|
||||
|
@ -66,15 +66,15 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
Assert.True(false, $"Unable to resolve context for {projectPath}");
|
||||
}
|
||||
|
||||
SendPayLoad(contextId, messageType);
|
||||
SendPayload(contextId, messageType);
|
||||
}
|
||||
|
||||
public void SendPayLoad(int contextId, string messageType)
|
||||
public void SendPayload(int contextId, string messageType)
|
||||
{
|
||||
SendPayLoad(contextId, messageType, new { });
|
||||
SendPayload(contextId, messageType, new { });
|
||||
}
|
||||
|
||||
public void SendPayLoad(int contextId, string messageType, object payload)
|
||||
public void SendPayload(int contextId, string messageType, object payload)
|
||||
{
|
||||
lock (_writer)
|
||||
{
|
||||
|
@ -94,7 +94,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
var contextId = _nextContextId++;
|
||||
|
||||
_projectContexts[projectPath] = contextId;
|
||||
SendPayLoad(contextId, MessageTypes.Initialize, new { ProjectFolder = projectPath });
|
||||
SendPayload(contextId, MessageTypes.Initialize, new { ProjectFolder = projectPath });
|
||||
|
||||
return contextId;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
var contextId = _nextContextId++;
|
||||
|
||||
_projectContexts[projectPath] = contextId;
|
||||
SendPayLoad(contextId, MessageTypes.Initialize, new { ProjectFolder = projectPath, Version = protocolVersion });
|
||||
SendPayload(contextId, MessageTypes.Initialize, new { ProjectFolder = projectPath, Version = protocolVersion });
|
||||
|
||||
return contextId;
|
||||
}
|
||||
|
@ -114,14 +114,14 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
var contextId = _nextContextId++;
|
||||
|
||||
_projectContexts[projectPath] = contextId;
|
||||
SendPayLoad(contextId, MessageTypes.Initialize, new { ProjectFolder = projectPath, Version = protocolVersion, Configuration = configuration });
|
||||
SendPayload(contextId, MessageTypes.Initialize, new { ProjectFolder = projectPath, Version = protocolVersion, Configuration = configuration });
|
||||
|
||||
return contextId;
|
||||
}
|
||||
|
||||
public void SetProtocolVersion(int version)
|
||||
{
|
||||
SendPayLoad(0, MessageTypes.ProtocolVersion, new { Version = version });
|
||||
SendPayload(0, MessageTypes.ProtocolVersion, new { Version = version });
|
||||
}
|
||||
|
||||
public List<DthMessage> DrainMessage(int count)
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel.Graph;
|
||||
using Microsoft.DotNet.TestFramework;
|
||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||
|
@ -260,7 +261,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
Path.Combine(projectPath, "home", GlobalSettings.FileName),
|
||||
JsonConvert.SerializeObject(new { project = new string[] { "src" } }));
|
||||
|
||||
client.SendPayLoad(testProject, "RefreshDependencies");
|
||||
client.SendPayload(testProject, "RefreshDependencies");
|
||||
|
||||
client.DrainTillFirst("ProjectInformation")
|
||||
.RetrievePayloadAs<JObject>()
|
||||
|
@ -379,7 +380,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
messages.ContainsMessage(MessageTypes.Error);
|
||||
|
||||
File.WriteAllText(projectFile, content);
|
||||
client.SendPayLoad(testProject, MessageTypes.FilesChanged);
|
||||
client.SendPayload(testProject, MessageTypes.FilesChanged);
|
||||
var clearError = client.DrainTillFirst(MessageTypes.Error);
|
||||
clearError.Payload.AsJObject().AssertProperty("Message", null as string);
|
||||
}
|
||||
|
@ -548,7 +549,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
|
||||
File.WriteAllText(projectFilePath, JsonConvert.SerializeObject(projectJson));
|
||||
|
||||
client.SendPayLoad(projectPath, MessageTypes.RefreshDependencies);
|
||||
client.SendPayload(projectPath, MessageTypes.RefreshDependencies);
|
||||
|
||||
var afterDependencies = client.DrainTillFirst(MessageTypes.Dependencies);
|
||||
afterDependencies.RetrieveDependency(appName)
|
||||
|
@ -597,7 +598,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
|
||||
File.WriteAllText(projectFilePath, JsonConvert.SerializeObject(projectJson));
|
||||
|
||||
client.SendPayLoad(projectPath, MessageTypes.RefreshDependencies);
|
||||
client.SendPayload(projectPath, MessageTypes.RefreshDependencies);
|
||||
|
||||
var afterDependencies = client.DrainTillFirst(MessageTypes.Dependencies);
|
||||
afterDependencies.RetrieveDependency("MainApp")
|
||||
|
@ -625,6 +626,82 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestTargetFrameworkChange()
|
||||
{
|
||||
using (var server = new DthTestServer())
|
||||
using (var client = new DthTestClient(server))
|
||||
{
|
||||
var testProject = _testAssetsManager.CreateTestInstance("EmptyLibrary")
|
||||
.WithLockFiles()
|
||||
.TestRoot;
|
||||
|
||||
// initialize the project and drain all messages (7 message for project with one framework)
|
||||
client.Initialize(testProject);
|
||||
client.DrainAllMessages();
|
||||
|
||||
// update the target framework from netstandard1.3 to netstandard 1.5 so as to invalidate all
|
||||
// dependencies
|
||||
var projectJsonPath = Path.Combine(testProject, "project.json");
|
||||
File.WriteAllText(projectJsonPath,
|
||||
File.ReadAllText(projectJsonPath).Replace("netstandard1.3", "netstandard1.5"));
|
||||
|
||||
// send files change request to server to prompt update
|
||||
client.SendPayload(testProject, MessageTypes.FilesChanged);
|
||||
|
||||
// assert project information is updated
|
||||
client.DrainTillFirst(MessageTypes.ProjectInformation)
|
||||
.RetrievePayloadAs<JObject>()
|
||||
.RetrievePropertyAs<JArray>("Frameworks")
|
||||
.AssertJArrayCount(1)
|
||||
.RetrieveArraryElementAs<JObject>(0)
|
||||
.AssertProperty("ShortName", "netstandard1.5");
|
||||
|
||||
// the NETStandard.Library dependency should turn unresolved
|
||||
var dependencies = client.DrainTillFirst(MessageTypes.Dependencies);
|
||||
|
||||
dependencies.RetrievePayloadAs<JObject>()
|
||||
.RetrievePropertyAs<JObject>("Framework")
|
||||
.AssertProperty("ShortName", "netstandard1.5");
|
||||
|
||||
dependencies.RetrieveDependency("NETStandard.Library")
|
||||
.RetrievePropertyAs<JArray>("Errors")
|
||||
.AssertJArrayCount(1)
|
||||
.RetrieveArraryElementAs<JObject>(0)
|
||||
.AssertProperty("ErrorCode", "NU1001");
|
||||
|
||||
// warning for project.json and project.lock.json out of sync
|
||||
var diagnostics = client.DrainTillFirst(MessageTypes.DependencyDiagnostics);
|
||||
|
||||
diagnostics.RetrievePayloadAs<JObject>()
|
||||
.RetrievePropertyAs<JObject>("Framework")
|
||||
.AssertProperty("ShortName", "netstandard1.5");
|
||||
|
||||
diagnostics.RetrievePayloadAs<JObject>()
|
||||
.RetrievePropertyAs<JArray>("Warnings")
|
||||
.AssertJArrayCount(1)
|
||||
.RetrieveArraryElementAs<JObject>(0)
|
||||
.AssertProperty("ErrorCode", "NU1006");
|
||||
|
||||
// restore again
|
||||
var restoreCommand = new RestoreCommand();
|
||||
restoreCommand.WorkingDirectory = testProject;
|
||||
restoreCommand.Execute().Should().Pass();
|
||||
|
||||
client.SendPayload(testProject, MessageTypes.RefreshDependencies);
|
||||
|
||||
client.DrainTillFirst(MessageTypes.Dependencies)
|
||||
.RetrieveDependency("NETStandard.Library")
|
||||
.RetrievePropertyAs<JArray>("Errors")
|
||||
.AssertJArrayCount(0);
|
||||
|
||||
client.DrainTillFirst(MessageTypes.DependencyDiagnostics)
|
||||
.RetrievePayloadAs<JObject>()
|
||||
.RetrievePropertyAs<JArray>("Warnings")
|
||||
.AssertJArrayCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizePathString(string original)
|
||||
{
|
||||
return original.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Microsoft.Dotnet.Tools.Test.Tests
|
|||
{
|
||||
_dotnetTestRunnerMock = new Mock<IDotnetTestRunner>();
|
||||
_dotnetTestRunnerMock
|
||||
.Setup(d => d.RunTests(It.IsAny<ProjectContext>(), It.IsAny<DotnetTestParams>()))
|
||||
.Setup(d => d.RunTests(It.IsAny<ProjectContext>(), It.IsAny<DotnetTestParams>(), It.IsAny<BuildWorkspace>()))
|
||||
.Returns(0);
|
||||
|
||||
_dotnetTestRunnerFactoryMock = new Mock<IDotnetTestRunnerFactory>();
|
||||
|
@ -62,7 +62,7 @@ namespace Microsoft.Dotnet.Tools.Test.Tests
|
|||
var result = _testCommand.DoRun(new[] { ProjectJsonPath, "-f", "netcoreapp1.0" });
|
||||
|
||||
_dotnetTestRunnerMock.Verify(
|
||||
d => d.RunTests(It.IsAny<ProjectContext>(), It.IsAny<DotnetTestParams>()),
|
||||
d => d.RunTests(It.IsAny<ProjectContext>(), It.IsAny<DotnetTestParams>(), It.IsAny<BuildWorkspace>()),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue