Memory usage improvements in build (#2626)

* Use a WorkspaceContext in dotnet-build to cache project data across
multiple compilations in a single build action
* Dramatically reduce string and object duplication by introducing a
"Symbol Table" that shares instances of NuGetVersion, NuGetFramework,
VersionRange and string across multiple lock-file parses

Test Results:
* Testing was done by compiling Microsoft.AspNetCore.Mvc (and it's
dependencies) and taking memory snapshots after each compilation in
dotMemory
* We used to allocate ~3MB and deallocate ~2.5MB on EACH compilation in
a single build action. This has been reduced to ~120KB
allocated/deallocated
* After introducing WorkspaceContext, total memory usage spiked from 6MB
across the whole build action to about 13MB, introducing the symbol
table dropped it back to about 5-6MB.
This commit is contained in:
Andrew Stanton-Nurse 2016-04-22 15:01:56 -07:00
parent 80df3688b1
commit ef0ca39da1
42 changed files with 496 additions and 268 deletions

View file

@ -1,8 +1,5 @@
{
"version": "1.0.0-*",
"compilationOptions": {
"emitEntryPoint": true
},
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24022",
"System.Runtime.Analyzers": {

View file

@ -280,7 +280,7 @@ namespace Microsoft.DotNet.Cli.Build
return rid;
}
[Target]
[Target(nameof(PrepareTargets.Init))]
public static BuildTargetResult CompileStage1(BuildTargetContext c)
{
CleanBinObj(c, Path.Combine(c.BuildContext.BuildDirectory, "src"));
@ -305,7 +305,7 @@ namespace Microsoft.DotNet.Cli.Build
return result;
}
[Target]
[Target(nameof(PrepareTargets.Init))]
public static BuildTargetResult CompileStage2(BuildTargetContext c)
{
var configuration = c.BuildContext.Get<string>("Configuration");

View file

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli.Utils
{
@ -51,16 +52,15 @@ namespace Microsoft.DotNet.Cli.Utils
// Reset the Reporters to the new Console Out and Error.
Reporter.Reset();
Thread threadOut = _stdOut.BeginRead(new StreamReader(outStream));
Thread threadErr = _stdErr.BeginRead(new StreamReader(errorStream));
var taskOut = _stdOut.BeginRead(new StreamReader(outStream));
var taskErr = _stdErr.BeginRead(new StreamReader(errorStream));
int exitCode = _builtInCommand(_commandArgs.ToArray());
outStream.DoneWriting();
errorStream.DoneWriting();
threadOut.Join();
threadErr.Join();
Task.WaitAll(taskOut, taskErr);
// fake out a ProcessStartInfo using the Muxer command name, since this is a built-in command
ProcessStartInfo startInfo = new ProcessStartInfo(new Muxer().MuxerPath, $"{CommandName} {CommandArgs}");

View file

@ -7,8 +7,9 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using NuGet.Frameworks;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectModel;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Cli.Utils
{
@ -131,12 +132,11 @@ namespace Microsoft.DotNet.Cli.Utils
Reporter.Verbose.WriteLine($"Process ID: {_process.Id}");
var threadOut = _stdOut.BeginRead(_process.StandardOutput);
var threadErr = _stdErr.BeginRead(_process.StandardError);
var taskOut = _stdOut.BeginRead(_process.StandardOutput);
var taskErr = _stdErr.BeginRead(_process.StandardError);
_process.WaitForExit();
threadOut.Join();
threadErr.Join();
Task.WaitAll(taskOut, taskErr);
var exitCode = _process.ExitCode;

View file

@ -1,8 +1,11 @@
// 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.IO;
using System.Text;
using System.Threading;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli.Utils
{
@ -43,11 +46,9 @@ namespace Microsoft.DotNet.Cli.Utils
return this;
}
public Thread BeginRead(TextReader reader)
public Task BeginRead(TextReader reader)
{
var thread = new Thread(() => Read(reader)) { IsBackground = true };
thread.Start();
return thread;
return Task.Run(() => Read(reader));
}
public void Read(TextReader reader)

View file

@ -13,10 +13,12 @@ namespace Microsoft.DotNet.ProjectModel.Graph
{
private readonly LockFile _lockFile;
private Dictionary<string, IList<LockFileTargetLibrary>> _msbuildTargetLibraries;
private readonly LockFileReader _reader;
public LockFilePatcher(LockFile lockFile)
public LockFilePatcher(LockFile lockFile, LockFileReader reader)
{
_lockFile = lockFile;
_reader = reader;
var msbuildProjectLibraries = lockFile.ProjectLibraries.Where(MSBuildDependencyProvider.IsMSBuildProjectLibrary);
_msbuildTargetLibraries = msbuildProjectLibraries.ToDictionary(GetProjectLibraryKey, l => GetTargetsForLibrary(_lockFile, l));
@ -27,7 +29,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
var exportFilePath = GetExportFilePath(_lockFile.LockFilePath);
if (File.Exists(exportFilePath) && _msbuildTargetLibraries.Any())
{
var exportFile = LockFileReader.ReadExportFile(exportFilePath);
var exportFile = _reader.ReadExportFile(exportFilePath);
PatchLockWithExport(exportFile);
}
else

View file

@ -13,15 +13,24 @@ using NuGet.Versioning;
namespace Microsoft.DotNet.ProjectModel.Graph
{
public static class LockFileReader
public class LockFileReader
{
private readonly LockFileSymbolTable _symbols;
public LockFileReader() : this(new LockFileSymbolTable()) { }
public LockFileReader(LockFileSymbolTable symbols)
{
_symbols = symbols;
}
public static LockFile Read(string lockFilePath, bool designTime)
{
using (var stream = ResilientFileStreamOpener.OpenFile(lockFilePath))
{
try
{
return Read(lockFilePath, stream, designTime);
return new LockFileReader().ReadLockFile(lockFilePath, stream, designTime);
}
catch (FileFormatException ex)
{
@ -34,7 +43,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
public static LockFile Read(string lockFilePath, Stream stream, bool designTime)
public LockFile ReadLockFile(string lockFilePath, Stream stream, bool designTime)
{
try
{
@ -50,7 +59,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
if (!designTime)
{
var patcher = new LockFilePatcher(lockFile);
var patcher = new LockFilePatcher(lockFile, this);
patcher.Patch();
}
@ -70,7 +79,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
public static ExportFile ReadExportFile(string fragmentLockFilePath)
public ExportFile ReadExportFile(string fragmentLockFilePath)
{
using (var stream = ResilientFileStreamOpener.OpenFile(fragmentLockFilePath))
{
@ -100,7 +109,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
private static LockFile ReadLockFile(string lockFilePath, JsonObject cursor)
private LockFile ReadLockFile(string lockFilePath, JsonObject cursor)
{
var lockFile = new LockFile(lockFilePath);
lockFile.Version = ReadInt(cursor, "version", defaultValue: int.MinValue);
@ -111,7 +120,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
return lockFile;
}
private static void ReadLibrary(JsonObject json, LockFile lockFile)
private void ReadLibrary(JsonObject json, LockFile lockFile)
{
if (json == null)
{
@ -128,9 +137,9 @@ namespace Microsoft.DotNet.ProjectModel.Graph
var parts = key.Split(new[] { '/' }, 2);
var name = parts[0];
var version = parts.Length == 2 ? NuGetVersion.Parse(parts[1]) : null;
var version = parts.Length == 2 ? _symbols.GetVersion(parts[1]) : null;
var type = value.ValueAsString("type")?.Value;
var type = _symbols.GetString(value.ValueAsString("type")?.Value);
if (type == null || string.Equals(type, "package", StringComparison.OrdinalIgnoreCase))
{
@ -162,7 +171,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
private static LockFileTarget ReadTarget(string property, JsonValue json)
private LockFileTarget ReadTarget(string property, JsonValue json)
{
var jobject = json as JsonObject;
if (jobject == null)
@ -172,10 +181,10 @@ namespace Microsoft.DotNet.ProjectModel.Graph
var target = new LockFileTarget();
var parts = property.Split(new[] { '/' }, 2);
target.TargetFramework = NuGetFramework.Parse(parts[0]);
target.TargetFramework = _symbols.GetFramework(parts[0]);
if (parts.Length == 2)
{
target.RuntimeIdentifier = parts[1];
target.RuntimeIdentifier = _symbols.GetString(parts[1]);
}
target.Libraries = ReadObject(jobject, ReadTargetLibrary);
@ -183,7 +192,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
return target;
}
private static LockFileTargetLibrary ReadTargetLibrary(string property, JsonValue json)
private LockFileTargetLibrary ReadTargetLibrary(string property, JsonValue json)
{
var jobject = json as JsonObject;
if (jobject == null)
@ -194,17 +203,17 @@ namespace Microsoft.DotNet.ProjectModel.Graph
var library = new LockFileTargetLibrary();
var parts = property.Split(new[] { '/' }, 2);
library.Name = parts[0];
library.Name = _symbols.GetString(parts[0]);
if (parts.Length == 2)
{
library.Version = NuGetVersion.Parse(parts[1]);
library.Version = _symbols.GetVersion(parts[1]);
}
library.Type = jobject.ValueAsString("type");
library.Type = _symbols.GetString(jobject.ValueAsString("type"));
var framework = jobject.ValueAsString("framework");
if (framework != null)
{
library.TargetFramework = NuGetFramework.Parse(framework);
library.TargetFramework = _symbols.GetFramework(framework);
}
library.Dependencies = ReadObject(jobject.ValueAsJsonObject("dependencies"), ReadPackageDependency);
@ -215,10 +224,11 @@ namespace Microsoft.DotNet.ProjectModel.Graph
library.NativeLibraries = ReadObject(jobject.ValueAsJsonObject("native"), ReadFileItem);
library.ContentFiles = ReadObject(jobject.ValueAsJsonObject("contentFiles"), ReadContentFile);
library.RuntimeTargets = ReadObject(jobject.ValueAsJsonObject("runtimeTargets"), ReadRuntimeTarget);
return library;
}
private static LockFileRuntimeTarget ReadRuntimeTarget(string property, JsonValue json)
private LockFileRuntimeTarget ReadRuntimeTarget(string property, JsonValue json)
{
var jsonObject = json as JsonObject;
if (jsonObject == null)
@ -227,13 +237,13 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
return new LockFileRuntimeTarget(
path: property,
runtime: jsonObject.ValueAsString("rid"),
assetType: jsonObject.ValueAsString("assetType")
path: _symbols.GetString(property),
runtime: _symbols.GetString(jsonObject.ValueAsString("rid")),
assetType: _symbols.GetString(jsonObject.ValueAsString("assetType"))
);
}
private static LockFileContentFile ReadContentFile(string property, JsonValue json)
private LockFileContentFile ReadContentFile(string property, JsonValue json)
{
var contentFile = new LockFileContentFile()
{
@ -248,7 +258,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
BuildAction.TryParse(jsonObject.ValueAsString("buildAction"), out action);
contentFile.BuildAction = action;
var codeLanguage = jsonObject.ValueAsString("codeLanguage");
var codeLanguage = _symbols.GetString(jsonObject.ValueAsString("codeLanguage"));
if (codeLanguage == "any")
{
codeLanguage = null;
@ -262,37 +272,37 @@ namespace Microsoft.DotNet.ProjectModel.Graph
return contentFile;
}
private static ProjectFileDependencyGroup ReadProjectFileDependencyGroup(string property, JsonValue json)
private ProjectFileDependencyGroup ReadProjectFileDependencyGroup(string property, JsonValue json)
{
return new ProjectFileDependencyGroup(
string.IsNullOrEmpty(property) ? null : NuGetFramework.Parse(property),
ReadArray(json, ReadString));
}
private static PackageDependency ReadPackageDependency(string property, JsonValue json)
private PackageDependency ReadPackageDependency(string property, JsonValue json)
{
var versionStr = ReadString(json);
return new PackageDependency(
property,
versionStr == null ? null : VersionRange.Parse(versionStr));
_symbols.GetString(property),
versionStr == null ? null : _symbols.GetVersionRange(versionStr));
}
private static LockFileItem ReadFileItem(string property, JsonValue json)
private LockFileItem ReadFileItem(string property, JsonValue json)
{
var item = new LockFileItem { Path = PathUtility.GetPathWithDirectorySeparator(property) };
var item = new LockFileItem { Path = _symbols.GetString(PathUtility.GetPathWithDirectorySeparator(property)) };
var jobject = json as JsonObject;
if (jobject != null)
{
foreach (var subProperty in jobject.Keys)
{
item.Properties[subProperty] = jobject.ValueAsString(subProperty);
item.Properties[_symbols.GetString(subProperty)] = jobject.ValueAsString(subProperty);
}
}
return item;
}
private static string ReadFrameworkAssemblyReference(JsonValue json)
private string ReadFrameworkAssemblyReference(JsonValue json)
{
return ReadString(json);
}
@ -318,9 +328,9 @@ namespace Microsoft.DotNet.ProjectModel.Graph
return items;
}
private static IList<string> ReadPathArray(JsonValue json, Func<JsonValue, string> readItem)
private IList<string> ReadPathArray(JsonValue json, Func<JsonValue, string> readItem)
{
return ReadArray(json, readItem).Select(f => PathUtility.GetPathWithDirectorySeparator(f)).ToList();
return ReadArray(json, readItem).Select(f => _symbols.GetString(PathUtility.GetPathWithDirectorySeparator(f))).ToList();
}
private static IList<TItem> ReadObject<TItem>(JsonObject json, Func<string, JsonValue, TItem> readItem)
@ -368,11 +378,11 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
private static string ReadString(JsonValue json)
private string ReadString(JsonValue json)
{
if (json is JsonString)
{
return (json as JsonString).Value;
return _symbols.GetString((json as JsonString).Value);
}
else if (json is JsonNull)
{

View file

@ -0,0 +1,23 @@
// 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 NuGet.Frameworks;
using NuGet.Versioning;
namespace Microsoft.DotNet.ProjectModel.Graph
{
public class LockFileSymbolTable
{
private ConcurrentDictionary<string, NuGetVersion> _versionTable = new ConcurrentDictionary<string, NuGetVersion>(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal);
private ConcurrentDictionary<string, VersionRange> _versionRangeTable = new ConcurrentDictionary<string, VersionRange>(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal);
private ConcurrentDictionary<string, NuGetFramework> _frameworksTable = new ConcurrentDictionary<string, NuGetFramework>(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal);
private ConcurrentDictionary<string, string> _stringsTable = new ConcurrentDictionary<string, string>(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal);
public NuGetVersion GetVersion(string versionString) => _versionTable.GetOrAdd(versionString, (s) => NuGetVersion.Parse(s));
public VersionRange GetVersionRange(string versionRangeString) => _versionRangeTable.GetOrAdd(versionRangeString, (s) => VersionRange.Parse(s));
public NuGetFramework GetFramework(string frameworkString) => _frameworksTable.GetOrAdd(frameworkString, (s) => NuGetFramework.Parse(s));
public string GetString(string frameworkString) => _stringsTable.GetOrAdd(frameworkString, frameworkString);
}
}

View file

@ -154,39 +154,6 @@ namespace Microsoft.DotNet.ProjectModel
.BuildAllTargets();
}
/// <summary>
/// Creates a project context based on existing context but using runtime target
/// </summary>
/// <param name="context"></param>
/// <param name="runtimeIdentifiers"></param>
/// <returns></returns>
public ProjectContext CreateRuntimeContext(IEnumerable<string> runtimeIdentifiers)
{
// Temporary until we have removed RID inference from NuGet
if(TargetFramework.IsCompileOnly)
{
return this;
}
var context = CreateBuilder(ProjectFile.ProjectFilePath, TargetFramework)
.WithRuntimeIdentifiers(runtimeIdentifiers)
.WithLockFile(LockFile)
.Build();
if (!context.IsPortable && context.RuntimeIdentifier == null)
{
// We are standalone, but don't support this runtime
var rids = string.Join(", ", runtimeIdentifiers);
throw new InvalidOperationException($"Can not find runtime target for framework '{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 context;
}
public OutputPaths GetOutputPaths(string configuration, string buidBasePath = null, string outputPath = null)
{
return OutputPathsCalculator.GetOutputPaths(ProjectFile,

View file

@ -132,16 +132,42 @@ namespace Microsoft.DotNet.ProjectModel
EnsureProjectLoaded();
LockFile = LockFile ?? LockFileResolver(ProjectDirectory);
if (LockFile != null)
if (LockFile != null && LockFile.Targets.Any())
{
var deduper = new HashSet<string>();
foreach (var target in LockFile.Targets)
{
yield return new ProjectContextBuilder()
var id = $"{target.TargetFramework}/{target.RuntimeIdentifier}";
if (deduper.Add(id))
{
var builder = new ProjectContextBuilder()
.WithProject(Project)
.WithLockFile(LockFile)
.WithTargetFramework(target.TargetFramework)
.WithRuntimeIdentifiers(new[] { target.RuntimeIdentifier })
.Build();
.WithRuntimeIdentifiers(new[] { target.RuntimeIdentifier });
if (IsDesignTime)
{
builder.AsDesignTime();
}
yield return builder.Build();
}
}
}
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)
foreach (var framework in Project.GetTargetFrameworks())
{
var builder = new ProjectContextBuilder()
.WithProject(Project)
.WithTargetFramework(framework.FrameworkName);
if (IsDesignTime)
{
builder.AsDesignTime();
}
yield return builder.Build();
}
}
}
@ -165,8 +191,17 @@ namespace Microsoft.DotNet.ProjectModel
RootDirectory = GlobalSettings?.DirectoryPath ?? RootDirectory;
PackagesDirectory = PackagesDirectory ?? PackageDependencyProvider.ResolvePackagesPath(RootDirectory, GlobalSettings);
ReferenceAssembliesPath = ReferenceAssembliesPath ?? FrameworkReferenceResolver.GetDefaultReferenceAssembliesPath();
var frameworkReferenceResolver = new FrameworkReferenceResolver(ReferenceAssembliesPath);
FrameworkReferenceResolver frameworkReferenceResolver;
if (string.IsNullOrEmpty(ReferenceAssembliesPath))
{
// Use the default static resolver
frameworkReferenceResolver = FrameworkReferenceResolver.Default;
}
else
{
frameworkReferenceResolver = new FrameworkReferenceResolver(ReferenceAssembliesPath);
}
LockFileLookup lockFileLookup = null;
EnsureProjectLoaded();
@ -274,7 +309,7 @@ namespace Microsoft.DotNet.ProjectModel
{
var frameworkInfo = Project.GetTargetFramework(TargetFramework);
if (string.IsNullOrEmpty(ReferenceAssembliesPath))
if (frameworkReferenceResolver == null || string.IsNullOrEmpty(frameworkReferenceResolver.ReferenceAssembliesPath))
{
// If there was an attempt to use reference assemblies but they were not installed
// report an error

View file

@ -4,6 +4,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectModel
{
@ -13,6 +15,8 @@ namespace Microsoft.DotNet.ProjectModel
public List<ProjectContext> ProjectContexts { get; } = new List<ProjectContext>();
public IEnumerable<ProjectContext> FrameworkOnlyContexts => ProjectContexts.Where(c => string.IsNullOrEmpty(c.RuntimeIdentifier));
public List<DiagnosticMessage> ProjectDiagnostics { get; } = new List<DiagnosticMessage>();
public string LockFilePath { get; set; }
@ -51,6 +55,16 @@ namespace Microsoft.DotNet.ProjectModel
}
}
public ProjectContext GetTarget(NuGetFramework targetFramework) => GetTarget(targetFramework, string.Empty);
public ProjectContext GetTarget(NuGetFramework targetFramework, string runtimeIdentifier)
{
return ProjectContexts
.FirstOrDefault(c =>
Equals(c.TargetFramework, targetFramework) &&
string.Equals(c.RuntimeIdentifier ?? string.Empty, runtimeIdentifier ?? string.Empty));
}
public void Reset()
{
Project = null;

View file

@ -85,8 +85,16 @@ namespace Microsoft.DotNet.ProjectModel
var project = new Project();
var reader = new StreamReader(stream);
JObject rawProject;
using (var jsonReader = new JsonTextReader(reader))
{
rawProject = JObject.Load(jsonReader);
// Try to read another token to ensure we're at the end of the document.
// This will no-op if we are, and throw a JsonReaderException if there is additional content (which is what we want)
jsonReader.Read();
}
var rawProject = JObject.Parse(reader.ReadToEnd());
if (rawProject == null)
{
throw FileFormatException.Create(

View file

@ -2,10 +2,12 @@
// 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 System.Runtime.Versioning;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.Extensions.DependencyModel.Resolution;
@ -23,7 +25,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution
private static FrameworkReferenceResolver _default;
private readonly IDictionary<NuGetFramework, FrameworkInformation> _cache = new Dictionary<NuGetFramework, FrameworkInformation>();
private readonly ConcurrentDictionary<NuGetFramework, FrameworkInformation> _cache = new ConcurrentDictionary<NuGetFramework, FrameworkInformation>();
private static readonly IDictionary<NuGetFramework, NuGetFramework[]> _aliases = new Dictionary<NuGetFramework, NuGetFramework[]>
{

View file

@ -83,7 +83,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution
LibraryDependencyType.Default));
}
if (!targetFramework.IsPackageBased)
if (!targetFramework.IsPackageBased())
{
// Only add framework assemblies for non-package based frameworks.
foreach (var frameworkAssembly in targetLibrary.FrameworkAssemblies)

View file

@ -138,7 +138,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution
LibraryDependencyType.Default));
}
if (!targetFramework.IsPackageBased)
if (!targetFramework.IsPackageBased())
{
// Only add framework assemblies for non-package based frameworks.
foreach (var frameworkAssembly in targetLibrary.FrameworkAssemblies)

View file

@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Utilities;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectModel
{
@ -28,9 +29,16 @@ namespace Microsoft.DotNet.ProjectModel
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)
private WorkspaceContext(IEnumerable<string> projectPaths, ProjectReaderSettings settings, bool designTime)
{
_settings = settings;
_designTime = designTime;
_lockFileReader = new LockFileReader();
foreach (var path in projectPaths)
{
AddProject(path);
@ -42,30 +50,59 @@ namespace Microsoft.DotNet.ProjectModel
/// <summary>
/// Create a WorkspaceContext from a given path.
///
/// There must be either a global.json or project.json at under the given path. Otherwise
/// null is returned.
///
/// 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)
public static WorkspaceContext CreateFrom(string projectPath, bool designTime)
{
var projectPaths = ResolveProjectPath(projectPath);
if (projectPaths == null || !projectPaths.Any())
{
return null;
return new WorkspaceContext(Enumerable.Empty<string>(), ProjectReaderSettings.ReadFromEnvironment(), designTime);
}
var context = new WorkspaceContext(projectPaths);
var context = new WorkspaceContext(projectPaths, ProjectReaderSettings.ReadFromEnvironment(), designTime);
return context;
}
public static WorkspaceContext Create()
/// <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)
{
return new WorkspaceContext(Enumerable.Empty<string>());
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)
@ -104,7 +141,7 @@ namespace Microsoft.DotNet.ProjectModel
foreach (var projectDirectory in basePaths)
{
var project = GetProject(projectDirectory).Model;
var project = GetProjectCore(projectDirectory).Model;
if (project == null)
{
continue;
@ -116,7 +153,7 @@ namespace Microsoft.DotNet.ProjectModel
{
foreach (var reference in GetProjectReferences(projectContext))
{
var referencedProject = GetProject(reference.Path).Model;
var referencedProject = GetProjectCore(reference.Path).Model;
if (referencedProject != null)
{
_projects.Add(referencedProject.ProjectDirectory);
@ -130,29 +167,100 @@ namespace Microsoft.DotNet.ProjectModel
public IReadOnlyList<ProjectContext> GetProjectContexts(string projectPath)
{
return GetProjectContextCollection(projectPath).ProjectContexts;
return (IReadOnlyList<ProjectContext>)GetProjectContextCollection(projectPath)?.ProjectContexts.AsReadOnly() ??
EmptyArray<ProjectContext>.Value;
}
public ProjectContext GetProjectContext(string projectPath, NuGetFramework framework) => GetProjectContext(projectPath, framework, EmptyArray<string>.Value);
public ProjectContext GetProjectContext(string projectPath, NuGetFramework framework, IEnumerable<string> runtimeIdentifier)
{
var contexts = GetProjectContextCollection(projectPath);
if (contexts == null)
{
return null;
}
return contexts
.ProjectContexts
.FirstOrDefault(c => Equals(c.TargetFramework, framework) && RidsMatch(c.RuntimeIdentifier, runtimeIdentifier));
}
public ProjectContext GetRuntimeContext(ProjectContext context, IEnumerable<string> runtimeIdentifiers)
{
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 = NormalizeProjectPath(projectPath);
if (normalizedPath == null)
{
return null;
}
return _projectContextsCache.AddOrUpdate(
projectPath,
normalizedPath,
key => AddProjectContextEntry(key, null),
(key, oldEntry) => AddProjectContextEntry(key, oldEntry));
}
private FileModelEntry<Project> GetProject(string projectDirectory)
private FileModelEntry<Project> GetProjectCore(string projectDirectory)
{
var normalizedPath = NormalizeProjectPath(projectDirectory);
if (normalizedPath == null)
{
return null;
}
return _projectsCache.AddOrUpdate(
projectDirectory,
normalizedPath,
key => AddProjectEntry(key, null),
(key, oldEntry) => AddProjectEntry(key, oldEntry));
}
private LockFile GetLockFile(string projectDirectory)
{
var normalizedPath = NormalizeProjectPath(projectDirectory);
if (normalizedPath == null)
{
return null;
}
return _lockFileCache.AddOrUpdate(
projectDirectory,
normalizedPath,
key => AddLockFileEntry(key, null),
(key, oldEntry) => AddLockFileEntry(key, oldEntry)).Model;
}
@ -173,7 +281,7 @@ namespace Microsoft.DotNet.ProjectModel
if (currentEntry.IsInvalid)
{
Project project;
if (!ProjectReader.TryGetProject(projectDirectory, out project, currentEntry.Diagnostics))
if (!ProjectReader.TryGetProject(projectDirectory, out project, currentEntry.Diagnostics, _settings))
{
currentEntry.Reset();
}
@ -211,7 +319,7 @@ namespace Microsoft.DotNet.ProjectModel
{
try
{
currentEntry.Model = LockFileReader.Read(currentEntry.FilePath, fs, designTime: true);
currentEntry.Model = _lockFileReader.ReadLockFile(currentEntry.FilePath, fs, designTime: true);
currentEntry.UpdateLastWriteTimeUtc();
}
catch (FileFormatException ex)
@ -238,8 +346,9 @@ namespace Microsoft.DotNet.ProjectModel
currentEntry = new ProjectContextCollection();
}
var projectEntry = GetProject(projectDirectory);
if (projectEntry.Model == null)
var projectEntry = GetProjectCore(projectDirectory);
if (projectEntry?.Model == null)
{
// project doesn't exist anymore
currentEntry.Reset();
@ -251,17 +360,9 @@ namespace Microsoft.DotNet.ProjectModel
{
currentEntry.Reset();
foreach (var framework in project.GetTargetFrameworks())
{
var builder = new ProjectContextBuilder()
.WithProjectResolver(path => GetProject(path).Model)
.WithLockFileResolver(path => GetLockFile(path))
.WithProject(project)
.WithTargetFramework(framework.FrameworkName)
.AsDesignTime();
var builder = InitializeProjectContextBuilder(project);
currentEntry.ProjectContexts.Add(builder.Build());
}
currentEntry.ProjectContexts.AddRange(builder.BuildAllTargets());
currentEntry.Project = project;
currentEntry.ProjectFilePath = project.ProjectFilePath;
@ -280,6 +381,20 @@ namespace Microsoft.DotNet.ProjectModel
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;
@ -400,5 +515,11 @@ namespace Microsoft.DotNet.ProjectModel
yield return description;
}
}
private static bool RidsMatch(string rid, IEnumerable<string> compatibleRids)
{
return (string.IsNullOrEmpty(rid) && !compatibleRids.Any()) ||
(compatibleRids.Contains(rid));
}
}
}

View file

@ -52,12 +52,17 @@ namespace Microsoft.DotNet.Tools.Compiler
yield return filePatternMatch.Path;
}
}
else if (value.Contains("*"))
{
throw new InvalidOperationException($"Globbing pattern '{value}' did not match any files");
}
else if (value.EndsWith(Project.FileName))
{
throw new InvalidOperationException($"Could not find project file '{value}'");
}
else
{
throw new InvalidOperationException($"Could not resolve project path from '{value}':" +
"1. It's not project file" +
"2. It's not directory containing project.json file" +
"3. Globbing returned no mathces");
throw new InvalidOperationException($"Couldn't find '{Project.FileName}' in '{value}'");
}
}
}

View file

@ -4,6 +4,7 @@
using System.Collections.Generic;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
@ -44,11 +45,16 @@ namespace Microsoft.DotNet.Tools.Compiler
public bool ShouldNotUseIncrementality { get; set; }
public bool ShouldSkipDependencies { get; set; }
public WorkspaceContext 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)
public BuildCommandApp(string name, string fullName, string description) : this(name, fullName, description, workspace: null) { }
public BuildCommandApp(string name, string fullName, string description, WorkspaceContext workspace)
{
Workspace = workspace;
_app = new CommandLineApplication
{
Name = name,
@ -99,6 +105,18 @@ namespace Microsoft.DotNet.Tools.Compiler
ShouldNotUseIncrementality = _shouldNotUseIncrementalityArgument.HasValue();
ShouldSkipDependencies = _shouldSkipDependenciesArgument.HasValue();
// 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);
}
var files = new ProjectGlobbingResolver().Resolve(_projectArgument.Values);
IEnumerable<NuGetFramework> frameworks = null;
if (_frameworkOption.HasValue())

View file

@ -18,16 +18,19 @@ namespace Microsoft.DotNet.Tools.Build
private readonly string _outputPath;
private readonly string _buildBasePath;
private readonly IList<string> _runtimes;
private readonly WorkspaceContext _workspace;
public CompilerIOManager(string configuration,
string outputPath,
string buildBasePath,
IEnumerable<string> runtimes)
IEnumerable<string> runtimes,
WorkspaceContext workspace)
{
_configuration = configuration;
_outputPath = outputPath;
_buildBasePath = buildBasePath;
_runtimes = runtimes.ToList();
_workspace = workspace;
}
public bool AnyMissingIO(ProjectContext project, IEnumerable<string> items, string itemsType)
@ -75,7 +78,7 @@ namespace Microsoft.DotNet.Tools.Build
var allOutputPath = new HashSet<string>(calculator.CompilationFiles.All());
if (isRootProject && project.ProjectFile.HasRuntimeOutput(_configuration))
{
var runtimeContext = project.CreateRuntimeContext(_runtimes);
var runtimeContext = _workspace.GetRuntimeContext(project, _runtimes);
foreach (var path in runtimeContext.GetOutputPaths(_configuration, _buildBasePath, _outputPath).RuntimeFiles.All())
{
allOutputPath.Add(path);

View file

@ -33,7 +33,8 @@ namespace Microsoft.DotNet.Tools.Build
args.ConfigValue,
args.OutputValue,
args.BuildBasePathValue,
args.GetRuntimes()
args.GetRuntimes(),
args.Workspace
);
_scriptRunner = new ScriptRunner();
_commandFactory = new DotNetCommandFactory();
@ -120,7 +121,7 @@ namespace Microsoft.DotNet.Tools.Build
private void MakeRunnable(ProjectGraphNode graphNode)
{
var runtimeContext = graphNode.ProjectContext.ProjectFile.HasRuntimeOutput(_args.ConfigValue) ?
graphNode.ProjectContext.CreateRuntimeContext(_args.GetRuntimes()) :
_args.Workspace.GetRuntimeContext(graphNode.ProjectContext, _args.GetRuntimes()) :
graphNode.ProjectContext;
var outputPaths = runtimeContext.GetOutputPaths(_args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue);

View file

@ -15,13 +15,19 @@ namespace Microsoft.DotNet.Tools.Build
{
public class BuildCommand
{
public static int Run(string[] args)
public static int Run(string[] args) => Run(args, null);
public static int Run(string[] args, WorkspaceContext workspace)
{
DebugHelper.HandleDebugSwitch(ref args);
try
{
var app = new BuildCommandApp("dotnet build", ".NET Builder", "Builder for the .NET Platform. It performs incremental compilation if it's safe to do so. Otherwise it delegates to dotnet-compile which performs non-incremental compilation");
var app = new BuildCommandApp(
"dotnet build",
".NET Builder",
"Builder for the .NET Platform. It performs incremental compilation if it's safe to do so. Otherwise it delegates to dotnet-compile which performs non-incremental compilation",
workspace);
return app.Execute(OnExecute, args);
}
catch (Exception ex)
@ -40,7 +46,7 @@ namespace Microsoft.DotNet.Tools.Build
var builderCommandApp = args;
var graphCollector = new ProjectGraphCollector(
!builderCommandApp.ShouldSkipDependencies,
(project, target) => ProjectContext.Create(project, target));
(project, target) => args.Workspace.GetProjectContext(project, target));
var contexts = ResolveRootContexts(files, frameworks, args);
var graph = graphCollector.Collect(contexts).ToArray();
@ -53,19 +59,11 @@ namespace Microsoft.DotNet.Tools.Build
IEnumerable<NuGetFramework> frameworks,
BuildCommandApp args)
{
List<Task<ProjectContext>> tasks = new List<Task<ProjectContext>>();
// Set defaults based on the environment
var settings = ProjectReaderSettings.ReadFromEnvironment();
if (!string.IsNullOrEmpty(args.VersionSuffixValue))
{
settings.VersionSuffix = args.VersionSuffixValue;
}
foreach (var file in files)
{
var project = ProjectReader.GetProject(file);
var project = args.Workspace.GetProject(file);
var projectFrameworks = project.GetTargetFrameworks().Select(f => f.FrameworkName);
if (!projectFrameworks.Any())
{
@ -91,11 +89,7 @@ namespace Microsoft.DotNet.Tools.Build
foreach (var framework in selectedFrameworks)
{
tasks.Add(Task.Run(() => new ProjectContextBuilder()
.WithProjectDirectory(Path.GetDirectoryName(file))
.WithTargetFramework(framework)
.WithReaderSettings(settings)
.Build()));
tasks.Add(Task.Run(() => args.Workspace.GetProjectContext(file, framework)));
}
}
return Task.WhenAll(tasks).GetAwaiter().GetResult();

View file

@ -26,7 +26,7 @@ namespace Microsoft.DotNet.Tools.Compiler
success &= _managedCompiler.Compile(context, args);
if (args.IsNativeValue && success)
{
var runtimeContext = context.CreateRuntimeContext(args.GetRuntimes());
var runtimeContext = args.Workspace.GetRuntimeContext(context, args.GetRuntimes());
success &= _nativeCompiler.Compile(runtimeContext, args);
}
}

View file

@ -170,7 +170,7 @@ namespace Microsoft.DotNet.Tools.Compiler
if (context.ProjectFile.HasRuntimeOutput(args.ConfigValue))
{
var runtimeContext = context.CreateRuntimeContext(args.GetRuntimes());
var runtimeContext = args.Workspace.GetRuntimeContext(context, args.GetRuntimes());
var runtimeOutputPath = runtimeContext.GetOutputPaths(args.ConfigValue, args.BuildBasePathValue, args.OutputValue);
contextVariables.Add(

View file

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Server.Helpers;
using Microsoft.DotNet.ProjectModel.Server.Models;
@ -35,7 +36,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
snapshot.ProjectSearchPaths = currentSearchPaths.ToList();
snapshot.GlobalJsonPath = globalSettings?.FilePath;
foreach (var projectContext in projectContextsCollection.ProjectContexts)
foreach (var projectContext in projectContextsCollection.FrameworkOnlyContexts)
{
snapshot.ProjectContexts[projectContext.TargetFramework] =
ProjectContextSnapshot.Create(projectContext, configuration, currentSearchPaths);

View file

@ -1,6 +1,7 @@
// 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.Diagnostics;
using System.Net;
@ -26,7 +27,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
_hostName = hostName;
_loggerFactory = loggerFactory;
_protocolManager = new ProtocolManager(maxVersion: 4, loggerFactory: _loggerFactory);
_workspaceContext = WorkspaceContext.Create();
_workspaceContext = WorkspaceContext.Create(designTime: true);
_projects = new Dictionary<int, ProjectManager>();
}
@ -51,6 +52,8 @@ namespace Microsoft.DotNet.ProjectModel.Server
var logger = loggerFactory.CreateLogger<ProjectModelServerCommand>();
try
{
if (!MonitorHostProcess(hostpid, logger))
{
return 1;
@ -70,6 +73,12 @@ namespace Microsoft.DotNet.ProjectModel.Server
var program = new ProjectModelServerCommand(intPort, hostname.Value(), loggerFactory);
program.OpenChannel();
}
catch (Exception ex)
{
logger.LogCritical($"Unhandled exception in server main: {ex}");
throw;
}
return 0;
});

View file

@ -5,6 +5,7 @@ using System;
using System.IO;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
namespace Microsoft.DotNet.Tools.Publish
{
@ -44,6 +45,8 @@ namespace Microsoft.DotNet.Tools.Publish
publish.VersionSuffix = versionSuffix.Value();
publish.ShouldBuild = !noBuild.HasValue();
publish.Workspace = WorkspaceContext.Create(versionSuffix.Value(), designTime: false);
if (string.IsNullOrEmpty(publish.ProjectPath))
{
publish.ProjectPath = Directory.GetCurrentDirectory();

View file

@ -30,6 +30,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 IList<ProjectContext> ProjectContexts { get; set; }
public string VersionSuffix { get; set; }
public int NumberOfProjects { get; private set; }
@ -432,14 +433,17 @@ namespace Microsoft.DotNet.Tools.Publish
throw new InvalidProjectException($"'{projectPath}' does not contain a project.json file");
}
var allContexts = framework == null ?
ProjectContext.CreateContextForEachFramework(projectPath) :
new[] { ProjectContext.Create(projectPath, framework) };
var contexts = Workspace.GetProjectContextCollection(projectPath).FrameworkOnlyContexts;
var runtimes = !string.IsNullOrEmpty(runtime) ?
new [] {runtime} :
PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers();
return allContexts.Select(c => c.CreateRuntimeContext(runtimes));
contexts = framework == null ?
contexts :
contexts.Where(c => Equals(c.TargetFramework, framework));
var rids = string.IsNullOrEmpty(runtime) ?
PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers() :
new[] { runtime };
return contexts.Select(c => Workspace.GetRuntimeContext(c, rids));
}
private static void CopyContents(ProjectContext context, string outputPath)

View file

@ -22,6 +22,7 @@ namespace Microsoft.DotNet.Tools.Run
ProjectContext _context;
List<string> _args;
private WorkspaceContext _workspace;
public int Start()
{
@ -60,6 +61,8 @@ namespace Microsoft.DotNet.Tools.Run
Configuration = Constants.DefaultConfiguration;
}
var frameworkContexts = _workspace.GetProjectContexts(Project).Where(c => string.IsNullOrEmpty(c.RuntimeIdentifier));
var rids = PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers();
if (Framework == null)
@ -70,16 +73,14 @@ namespace Microsoft.DotNet.Tools.Run
FrameworkConstants.FrameworkIdentifiers.NetStandardApp,
};
var contexts = ProjectContext.CreateContextForEachFramework(Project, null);
ProjectContext context;
if (contexts.Count() == 1)
if (frameworkContexts.Count() == 1)
{
context = contexts.Single();
context = frameworkContexts.Single();
}
else
{
context = contexts.FirstOrDefault(c => defaultFrameworks.Contains(c.TargetFramework.Framework));
context = frameworkContexts.FirstOrDefault(c => defaultFrameworks.Contains(c.TargetFramework.Framework));
if (context == null)
{
throw new InvalidOperationException($"Couldn't find target to run. Possible causes:" + Environment.NewLine +
@ -88,11 +89,11 @@ namespace Microsoft.DotNet.Tools.Run
}
}
_context = context.CreateRuntimeContext(rids);
_context = _workspace.GetRuntimeContext(context, rids);
}
else
{
_context = ProjectContext.Create(Project, NuGetFramework.Parse(Framework), rids);
_context = _workspace.GetProjectContext(Project, NuGetFramework.Parse(Framework), rids);
}
if (Args == null)
@ -107,6 +108,9 @@ namespace Microsoft.DotNet.Tools.Run
private int RunExecutable()
{
// Set up the workspace
_workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
CalculateDefaultsForNonAssigned();
// Compile to that directory

View file

@ -42,7 +42,10 @@ namespace Microsoft.DotNet.Tools.Test
RegisterForParentProcessExit(dotnetTestParams.ParentProcessId.Value);
}
var projectContexts = CreateProjectContexts(dotnetTestParams.ProjectPath, dotnetTestParams.Runtime);
// Create a workspace
var workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
var projectContexts = CreateProjectContexts(workspace, dotnetTestParams.ProjectPath, dotnetTestParams.Runtime);
var projectContext = projectContexts.First();
@ -98,7 +101,7 @@ namespace Microsoft.DotNet.Tools.Test
}
}
private static IEnumerable<ProjectContext> CreateProjectContexts(string projectPath, string runtime)
private static IEnumerable<ProjectContext> CreateProjectContexts(WorkspaceContext workspace, string projectPath, string runtime)
{
projectPath = projectPath ?? Directory.GetCurrentDirectory();
@ -116,7 +119,8 @@ namespace Microsoft.DotNet.Tools.Test
new[] { runtime } :
PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers();
return ProjectContext.CreateContextForEachFramework(projectPath).Select(context => context.CreateRuntimeContext(runtimeIdentifiers));
var contexts = workspace.GetProjectContextCollection(projectPath).FrameworkOnlyContexts;
return contexts.Select(c => workspace.GetRuntimeContext(c, runtimeIdentifiers));
}
}
}

View file

@ -43,14 +43,14 @@ namespace Microsoft.DotNet.ProjectModel.Tests
}
[Theory]
[InlineDataAttribute("TestMscorlibReference", true)]
[InlineDataAttribute("TestMscorlibReference", false)]
[InlineDataAttribute("TestMicrosoftCSharpReference", true)]
[InlineDataAttribute("TestMicrosoftCSharpReference", false)]
[InlineDataAttribute("TestSystemReference", true)]
[InlineDataAttribute("TestSystemReference", false)]
[InlineDataAttribute("TestSystemCoreReference", true)]
[InlineDataAttribute("TestSystemCoreReference", false)]
[InlineData("TestMscorlibReference", true)]
[InlineData("TestMscorlibReference", false)]
[InlineData("TestMicrosoftCSharpReference", true)]
[InlineData("TestMicrosoftCSharpReference", false)]
[InlineData("TestSystemReference", true)]
[InlineData("TestSystemReference", false)]
[InlineData("TestSystemCoreReference", true)]
[InlineData("TestSystemCoreReference", false)]
public void TestDuplicateDefaultDesktopReferences(string sampleName, bool withLockFile)
{
var instance = TestAssetsManager.CreateTestInstance(sampleName);

View file

@ -109,12 +109,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
private CommandResult RunProcess(string executable, string args, StreamForwarder stdOut, StreamForwarder stdErr)
{
CurrentProcess = StartProcess(executable, args);
var threadOut = stdOut.BeginRead(CurrentProcess.StandardOutput);
var threadErr = stdErr.BeginRead(CurrentProcess.StandardError);
var taskOut = stdOut.BeginRead(CurrentProcess.StandardOutput);
var taskErr = stdErr.BeginRead(CurrentProcess.StandardError);
CurrentProcess.WaitForExit();
threadOut.Join();
threadErr.Join();
Task.WaitAll(taskOut, taskErr);
var result = new CommandResult(
CurrentProcess.StartInfo,
@ -128,14 +127,13 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
private Task<CommandResult> RunProcessAsync(string executable, string args, StreamForwarder stdOut, StreamForwarder stdErr)
{
CurrentProcess = StartProcess(executable, args);
var threadOut = stdOut.BeginRead(CurrentProcess.StandardOutput);
var threadErr = stdErr.BeginRead(CurrentProcess.StandardError);
var taskOut = stdOut.BeginRead(CurrentProcess.StandardOutput);
var taskErr = stdErr.BeginRead(CurrentProcess.StandardError);
var tcs = new TaskCompletionSource<CommandResult>();
CurrentProcess.Exited += (sender, arg) =>
{
threadOut.Join();
threadErr.Join();
Task.WaitAll(taskOut, taskErr);
var result = new CommandResult(
CurrentProcess.StartInfo,
CurrentProcess.ExitCode,

View file

@ -20,6 +20,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
private Mock<ICompiler> _nativeCompilerMock;
private List<ProjectContext> _contexts;
private BuildCommandApp _args;
private readonly WorkspaceContext _workspace;
public GivenACompilationDriverController()
{
@ -34,12 +35,13 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
.Compile(It.IsAny<ProjectContext>(), It.IsAny<BuildCommandApp>()))
.Returns(true);
_workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
_contexts = new List<ProjectContext>
{
ProjectContext.Create(_projectJson, NuGetFramework.Parse("netcoreapp1.0"))
_workspace.GetProjectContext(_projectJson, NuGetFramework.Parse("netcoreapp1.0"))
};
_args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform");
_args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform", WorkspaceContext.Create(designTime: false));
}
[Fact]

View file

@ -188,7 +188,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");
var _args = new BuildCommandApp("dotnet compile", ".NET Compiler", "Compiler for the .NET Platform", WorkspaceContext.Create(designTime: false));
_args.ConfigValue = ConfigValue;
PreCompileScriptVariables = new Dictionary<string, string>();
@ -219,7 +219,8 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
rids.Add(rid);
}
var context = ProjectContext.Create(projectJson, TestAssetFramework, rids);
var workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false);
var context = workspace.GetProjectContext(projectJson, TestAssetFramework, rids);
managedCompiler.Compile(context, _args);
RuntimeOutputDir = Path.Combine(OutputPath, rid);

View file

@ -506,13 +506,13 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
var testProjectRoot = Path.Combine(RepoRoot, "TestAssets", "ProjectModelServer", "MSBuildReferencesProjects", "ValidCase01");
foreach (var classLibrary in classLibraries)
{
dependencies.RetrieveDependency(classLibrary)
.AssertProperty("Type", LibraryType.MSBuildProject.ToString())
.AssertProperty("Path", NormalizePathString(Path.Combine(testProjectRoot, classLibrary, $"{classLibrary}.csproj")))
.AssertProperty<bool>("Resolved", true)
.AssertProperty("Name", classLibrary)
.AssertProperty<JArray>("Errors", array => array.Count == 0)
.AssertProperty<JArray>("Warnings", array => array.Count == 0);
var dependency = dependencies.RetrieveDependency(classLibrary);
dependency.AssertProperty("Type", LibraryType.MSBuildProject.ToString());
dependency.AssertProperty("Path", NormalizePathString(Path.Combine(testProjectRoot, classLibrary, $"{classLibrary}.csproj")));
dependency.AssertProperty<bool>("Resolved", true);
dependency.AssertProperty("Name", classLibrary);
dependency.AssertProperty<JArray>("Errors", array => array.Count == 0);
dependency.AssertProperty<JArray>("Warnings", array => array.Count == 0);
}
var references = messages.RetrieveSingleMessage(MessageTypes.References)

View file

@ -28,7 +28,8 @@
}
},
"content": [
"../../TestAssets/TestProjects/ProjectWithTests/project.json"
"../../TestAssets/TestProjects/ProjectWithTests/project.json",
"../../TestAssets/TestProjects/ProjectWithTests/project.lock.json"
],
"testRunner": "xunit"
}