diff --git a/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json b/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json index 7a6e4fe14..e00ef4fdc 100644 --- a/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json +++ b/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json @@ -1,8 +1,5 @@ { "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, "dependencies": { "NETStandard.Library": "1.5.0-rc2-24022", "System.Runtime.Analyzers": { diff --git a/global.json b/global.json index 14dc04189..4bf7ec8f2 100644 --- a/global.json +++ b/global.json @@ -1,3 +1,3 @@ { - "projects": [ "src", "test", "tools" ] + "projects": [ "src", "test", "tools" ] } diff --git a/scripts/dotnet-cli-build/CompileTargets.cs b/scripts/dotnet-cli-build/CompileTargets.cs index 079ce927c..74b13eb69 100644 --- a/scripts/dotnet-cli-build/CompileTargets.cs +++ b/scripts/dotnet-cli-build/CompileTargets.cs @@ -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("Configuration"); diff --git a/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs b/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs index abef5ceba..6fbabe352 100644 --- a/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs +++ b/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs @@ -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,17 +52,16 @@ 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}"); return new CommandResult(startInfo, exitCode, null, null); diff --git a/src/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Microsoft.DotNet.Cli.Utils/Command.cs index dfd608bcc..058109c8e 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Command.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Command.cs @@ -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; diff --git a/src/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs b/src/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs index a2cbc32af..aa16cb209 100644 --- a/src/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs +++ b/src/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs @@ -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 { @@ -17,7 +20,7 @@ namespace Microsoft.DotNet.Cli.Utils public string CapturedOutput { - get + get { return _capture?.GetStringBuilder()?.ToString(); } @@ -43,18 +46,16 @@ 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) { var bufferSize = 1; - int readCharacterCount; + int readCharacterCount; char currentCharacter; var buffer = new char[bufferSize]; @@ -93,7 +94,7 @@ namespace Microsoft.DotNet.Cli.Utils } private void WriteLine(string str) - { + { if (_capture != null) { _capture.WriteLine(str); diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFilePatcher.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFilePatcher.cs index c1e13ee63..ca27e42ef 100644 --- a/src/Microsoft.DotNet.ProjectModel/Graph/LockFilePatcher.cs +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFilePatcher.cs @@ -13,10 +13,12 @@ namespace Microsoft.DotNet.ProjectModel.Graph { private readonly LockFile _lockFile; private Dictionary> _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,8 +29,8 @@ namespace Microsoft.DotNet.ProjectModel.Graph var exportFilePath = GetExportFilePath(_lockFile.LockFilePath); if (File.Exists(exportFilePath) && _msbuildTargetLibraries.Any()) { - var exportFile = LockFileReader.ReadExportFile(exportFilePath); - PatchLockWithExport(exportFile); + var exportFile = _reader.ReadExportFile(exportFilePath); + PatchLockWithExport(exportFile); } else { diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs index 2fb514407..9275a5213 100644 --- a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs @@ -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 { @@ -47,10 +56,10 @@ namespace Microsoft.DotNet.ProjectModel.Graph } var lockFile = ReadLockFile(lockFilePath, jobject); - + 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 ReadPathArray(JsonValue json, Func readItem) + private IList ReadPathArray(JsonValue json, Func 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 ReadObject(JsonObject json, Func 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) { diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileSymbolTable.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileSymbolTable.cs new file mode 100644 index 000000000..a81da4530 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileSymbolTable.cs @@ -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 _versionTable = new ConcurrentDictionary(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal); + private ConcurrentDictionary _versionRangeTable = new ConcurrentDictionary(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal); + private ConcurrentDictionary _frameworksTable = new ConcurrentDictionary(Environment.ProcessorCount * 4, 1000, StringComparer.Ordinal); + private ConcurrentDictionary _stringsTable = new ConcurrentDictionary(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); + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs index f659ddb5f..cf7ba5a64 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs @@ -154,39 +154,6 @@ namespace Microsoft.DotNet.ProjectModel .BuildAllTargets(); } - - /// - /// Creates a project context based on existing context but using runtime target - /// - /// - /// - /// - - public ProjectContext CreateRuntimeContext(IEnumerable 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, diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs index 0c22afca3..bcf41c4d9 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs @@ -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(); foreach (var target in LockFile.Targets) { - yield return new ProjectContextBuilder() - .WithProject(Project) - .WithLockFile(LockFile) - .WithTargetFramework(target.TargetFramework) - .WithRuntimeIdentifiers(new[] { target.RuntimeIdentifier }) - .Build(); + 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 }); + 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(); @@ -202,7 +237,7 @@ namespace Microsoft.DotNet.ProjectModel if (mainProject != null) { platformDependency = mainProject.Dependencies - .Where(d => d.Type.Equals(LibraryDependencyType.Platform)) + .Where(d => d.Type.Equals(LibraryDependencyType.Platform)) .Cast() .FirstOrDefault(); } @@ -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 @@ -373,13 +408,13 @@ namespace Microsoft.DotNet.ProjectModel { var library = pair.Value; - // The System.* packages provide placeholders on any non netstandard platform + // The System.* packages provide placeholders on any non netstandard platform // To make them work seamlessly on those platforms, we fill the gap with a reference // assembly (if available) var package = library as PackageDescription; - if (package != null && - package.Resolved && - package.HasCompileTimePlaceholder && + if (package != null && + package.Resolved && + package.HasCompileTimePlaceholder && !TargetFramework.IsPackageBased()) { var newKey = new LibraryKey(library.Identity.Name, LibraryType.ReferenceAssembly); @@ -583,7 +618,7 @@ namespace Microsoft.DotNet.ProjectModel return combiner.CombinedHash; } - + public override string ToString() { return Name + " " + LibraryType; diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContextCollection.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContextCollection.cs index a8ad3f3ad..2467cb45d 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContextCollection.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContextCollection.cs @@ -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 ProjectContexts { get; } = new List(); + public IEnumerable FrameworkOnlyContexts => ProjectContexts.Where(c => string.IsNullOrEmpty(c.RuntimeIdentifier)); + public List ProjectDiagnostics { get; } = new List(); 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; diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs index 694944884..c82b58401 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs @@ -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( diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs index e5c605730..f522a000e 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs @@ -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 _cache = new Dictionary(); + private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); private static readonly IDictionary _aliases = new Dictionary { diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/MSBuildDependencyProvider.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/MSBuildDependencyProvider.cs index 216f56bb6..aef7508bc 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/MSBuildDependencyProvider.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/MSBuildDependencyProvider.cs @@ -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) diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs index df0bdf64f..9ef93928f 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs @@ -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) diff --git a/src/Microsoft.DotNet.ProjectModel/Utilities/FrameworksExtensions.cs b/src/Microsoft.DotNet.ProjectModel/Utilities/FrameworksExtensions.cs index 9500b0486..14341a1c4 100644 --- a/src/Microsoft.DotNet.ProjectModel/Utilities/FrameworksExtensions.cs +++ b/src/Microsoft.DotNet.ProjectModel/Utilities/FrameworksExtensions.cs @@ -56,7 +56,7 @@ namespace NuGet.Frameworks } return name + "-" + frameworkName.Profile; } - + internal static bool IsPackageBased(this NuGetFramework self) { return self.IsPackageBased || diff --git a/src/Microsoft.DotNet.ProjectModel/Utilities/ResilientFileStreamOpener.cs b/src/Microsoft.DotNet.ProjectModel/Utilities/ResilientFileStreamOpener.cs index 4a11904cb..dc824c1b2 100644 --- a/src/Microsoft.DotNet.ProjectModel/Utilities/ResilientFileStreamOpener.cs +++ b/src/Microsoft.DotNet.ProjectModel/Utilities/ResilientFileStreamOpener.cs @@ -13,18 +13,18 @@ namespace Microsoft.DotNet.ProjectModel.Utilities { return OpenFile(filepath, 0); } - + public static FileStream OpenFile(string filepath, int retry) { if (retry < 0) { throw new ArgumentException("Retry can't be fewer than 0", nameof(retry)); } - + var count = 0; while (true) { - try + try { return new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read); } diff --git a/src/Microsoft.DotNet.ProjectModel/WorkspaceContext.cs b/src/Microsoft.DotNet.ProjectModel/WorkspaceContext.cs index 66313b2a1..e9ce6d74a 100644 --- a/src/Microsoft.DotNet.ProjectModel/WorkspaceContext.cs +++ b/src/Microsoft.DotNet.ProjectModel/WorkspaceContext.cs @@ -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 _projects = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _needRefresh; + private readonly ProjectReaderSettings _settings; + private readonly bool _designTime; + private readonly LockFileReader _lockFileReader; - private WorkspaceContext(IEnumerable projectPaths) + private WorkspaceContext(IEnumerable projectPaths, ProjectReaderSettings settings, bool designTime) { + _settings = settings; + _designTime = designTime; + _lockFileReader = new LockFileReader(); + foreach (var path in projectPaths) { AddProject(path); @@ -41,31 +49,60 @@ namespace Microsoft.DotNet.ProjectModel /// /// 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 . /// - 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(), ProjectReaderSettings.ReadFromEnvironment(), designTime); } - var context = new WorkspaceContext(projectPaths); + var context = new WorkspaceContext(projectPaths, ProjectReaderSettings.ReadFromEnvironment(), designTime); return context; } - public static WorkspaceContext Create() + /// + /// Create an empty using the default + /// + /// A boolean indicating if the workspace should be created in Design-Time mode + /// + public static WorkspaceContext Create(bool designTime) => Create(ProjectReaderSettings.ReadFromEnvironment(), designTime); + + /// + /// Create an empty using the default , with the specified Version Suffix + /// + /// The suffix to use to replace any '-*' snapshot tokens in Project versions. + /// A boolean indicating if the workspace should be created in Design-Time mode + /// + public static WorkspaceContext Create(string versionSuffix, bool designTime) { - return new WorkspaceContext(Enumerable.Empty()); + var settings = ProjectReaderSettings.ReadFromEnvironment(); + if (!string.IsNullOrEmpty(versionSuffix)) + { + settings.VersionSuffix = versionSuffix; + } + return Create(settings, designTime); + } + + /// + /// Create an empty using the provided . + /// + /// The settings to use when reading projects + /// A boolean indicating if the workspace should be created in Design-Time mode + /// + public static WorkspaceContext Create(ProjectReaderSettings settings, bool designTime) + { + return new WorkspaceContext(Enumerable.Empty(), 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 GetProjectContexts(string projectPath) { - return GetProjectContextCollection(projectPath).ProjectContexts; + return (IReadOnlyList)GetProjectContextCollection(projectPath)?.ProjectContexts.AsReadOnly() ?? + EmptyArray.Value; } + public ProjectContext GetProjectContext(string projectPath, NuGetFramework framework) => GetProjectContext(projectPath, framework, EmptyArray.Value); + + public ProjectContext GetProjectContext(string projectPath, NuGetFramework framework, IEnumerable 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 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 GetProject(string projectDirectory) + private FileModelEntry 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 where TModel : class { private DateTime _lastWriteTimeUtc; @@ -400,5 +515,11 @@ namespace Microsoft.DotNet.ProjectModel yield return description; } } + + private static bool RidsMatch(string rid, IEnumerable compatibleRids) + { + return (string.IsNullOrEmpty(rid) && !compatibleRids.Any()) || + (compatibleRids.Contains(rid)); + } } } diff --git a/src/Microsoft.Extensions.DependencyModel/Resolution/DotNetReferenceAssembliesPathResolver.cs b/src/Microsoft.Extensions.DependencyModel/Resolution/DotNetReferenceAssembliesPathResolver.cs index b44668ede..ff214d8a7 100644 --- a/src/Microsoft.Extensions.DependencyModel/Resolution/DotNetReferenceAssembliesPathResolver.cs +++ b/src/Microsoft.Extensions.DependencyModel/Resolution/DotNetReferenceAssembliesPathResolver.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution public class DotNetReferenceAssembliesPathResolver { public static readonly string DotNetReferenceAssembliesPathEnv = "DOTNET_REFERENCE_ASSEMBLIES_PATH"; - + internal static string Resolve(IEnvironment envirnment, IFileSystem fileSystem, IRuntimeEnvironment runtimeEnvironment) { var path = envirnment.GetEnvironmentVariable(DotNetReferenceAssembliesPathEnv); @@ -19,40 +19,40 @@ namespace Microsoft.Extensions.DependencyModel.Resolution { return path; } - + return GetDefaultDotNetReferenceAssembliesPath(fileSystem, runtimeEnvironment); } - + public static string Resolve() { return Resolve(EnvironmentWrapper.Default, FileSystemWrapper.Default, PlatformServices.Default.Runtime); } - + private static string GetDefaultDotNetReferenceAssembliesPath(IFileSystem fileSystem, IRuntimeEnvironment runtimeEnvironment) - { + { var os = runtimeEnvironment.OperatingSystemPlatform; - + if (os == Platform.Windows) { return null; } - - if (os == Platform.Darwin && + + if (os == Platform.Darwin && fileSystem.Directory.Exists("/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/xbuild-frameworks")) { return "/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/xbuild-frameworks"; } - + if (fileSystem.Directory.Exists("/usr/local/lib/mono/xbuild-frameworks")) { return "/usr/local/lib/mono/xbuild-frameworks"; } - + if (fileSystem.Directory.Exists("/usr/lib/mono/xbuild-frameworks")) { return "/usr/lib/mono/xbuild-frameworks"; } - + return null; } } diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 0b360d313..2ecbed88b 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -111,7 +111,7 @@ namespace Microsoft.DotNet.Cli command = "help"; } - int exitCode; + int exitCode; Func builtIn; if (s_builtIns.TryGetValue(command, out builtIn)) { @@ -175,16 +175,16 @@ namespace Microsoft.DotNet.Cli { return (shortName != null && candidate.Equals("-" + shortName)) || (longName != null && candidate.Equals("--" + longName)); } - + private static string GetCommitSha() { var versionFile = DotnetFiles.VersionFile; - + if (File.Exists(versionFile)) { return File.ReadLines(versionFile).FirstOrDefault()?.Substring(0, 10); } - + return null; } } diff --git a/src/dotnet/ProjectGlobbingResolver.cs b/src/dotnet/ProjectGlobbingResolver.cs index 5284b1755..dbf20e64f 100644 --- a/src/dotnet/ProjectGlobbingResolver.cs +++ b/src/dotnet/ProjectGlobbingResolver.cs @@ -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}'"); } } } diff --git a/src/dotnet/commands/dotnet-build/BuildCommandApp.cs b/src/dotnet/commands/dotnet-build/BuildCommandApp.cs index 385c032e7..ce1b0626f 100644 --- a/src/dotnet/commands/dotnet-build/BuildCommandApp.cs +++ b/src/dotnet/commands/dotnet-build/BuildCommandApp.cs @@ -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 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 frameworks = null; if (_frameworkOption.HasValue()) diff --git a/src/dotnet/commands/dotnet-build/CompilerIOManager.cs b/src/dotnet/commands/dotnet-build/CompilerIOManager.cs index 8bb66f64e..2bac2204f 100644 --- a/src/dotnet/commands/dotnet-build/CompilerIOManager.cs +++ b/src/dotnet/commands/dotnet-build/CompilerIOManager.cs @@ -18,16 +18,19 @@ namespace Microsoft.DotNet.Tools.Build private readonly string _outputPath; private readonly string _buildBasePath; private readonly IList _runtimes; + private readonly WorkspaceContext _workspace; public CompilerIOManager(string configuration, string outputPath, string buildBasePath, - IEnumerable runtimes) + IEnumerable runtimes, + WorkspaceContext workspace) { _configuration = configuration; _outputPath = outputPath; _buildBasePath = buildBasePath; _runtimes = runtimes.ToList(); + _workspace = workspace; } public bool AnyMissingIO(ProjectContext project, IEnumerable items, string itemsType) @@ -75,7 +78,7 @@ namespace Microsoft.DotNet.Tools.Build var allOutputPath = new HashSet(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); diff --git a/src/dotnet/commands/dotnet-build/DotnetProjectBuilder.cs b/src/dotnet/commands/dotnet-build/DotnetProjectBuilder.cs index 19c9c5542..bcc40fcbd 100644 --- a/src/dotnet/commands/dotnet-build/DotnetProjectBuilder.cs +++ b/src/dotnet/commands/dotnet-build/DotnetProjectBuilder.cs @@ -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); diff --git a/src/dotnet/commands/dotnet-build/Program.cs b/src/dotnet/commands/dotnet-build/Program.cs index 1fe013e1d..a42320362 100644 --- a/src/dotnet/commands/dotnet-build/Program.cs +++ b/src/dotnet/commands/dotnet-build/Program.cs @@ -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 frameworks, BuildCommandApp args) { - List> tasks = new List>(); - // 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(); diff --git a/src/dotnet/commands/dotnet-compile/CompilationDriver.cs b/src/dotnet/commands/dotnet-compile/CompilationDriver.cs index a7dac3a27..5f679fc34 100644 --- a/src/dotnet/commands/dotnet-compile/CompilationDriver.cs +++ b/src/dotnet/commands/dotnet-compile/CompilationDriver.cs @@ -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); } } diff --git a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs index e9bd3ff69..70a2d4f9d 100644 --- a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs +++ b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs @@ -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( diff --git a/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectSnapshot.cs b/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectSnapshot.cs index caa0c7bf1..f376bd7a2 100644 --- a/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectSnapshot.cs +++ b/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectSnapshot.cs @@ -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,12 +36,12 @@ 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] = + snapshot.ProjectContexts[projectContext.TargetFramework] = ProjectContextSnapshot.Create(projectContext, configuration, currentSearchPaths); } - + return snapshot; } } diff --git a/src/dotnet/commands/dotnet-projectmodel-server/Program.cs b/src/dotnet/commands/dotnet-projectmodel-server/Program.cs index be3f55b21..076762ccd 100644 --- a/src/dotnet/commands/dotnet-projectmodel-server/Program.cs +++ b/src/dotnet/commands/dotnet-projectmodel-server/Program.cs @@ -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(); } @@ -51,26 +52,34 @@ namespace Microsoft.DotNet.ProjectModel.Server var logger = loggerFactory.CreateLogger(); - if (!MonitorHostProcess(hostpid, logger)) + try { - return 1; - } + if (!MonitorHostProcess(hostpid, logger)) + { + return 1; + } - var intPort = CheckPort(port, logger); - if (intPort == -1) + var intPort = CheckPort(port, logger); + if (intPort == -1) + { + return 1; + } + + if (!hostname.HasValue()) + { + logger.LogError($"Option \"{hostname.LongName}\" is missing."); + return 1; + } + + var program = new ProjectModelServerCommand(intPort, hostname.Value(), loggerFactory); + program.OpenChannel(); + } + catch (Exception ex) { - return 1; + logger.LogCritical($"Unhandled exception in server main: {ex}"); + throw; } - if (!hostname.HasValue()) - { - logger.LogError($"Option \"{hostname.LongName}\" is missing."); - return 1; - } - - var program = new ProjectModelServerCommand(intPort, hostname.Value(), loggerFactory); - program.OpenChannel(); - return 0; }); diff --git a/src/dotnet/commands/dotnet-projectmodel-server/ProjectManager.cs b/src/dotnet/commands/dotnet-projectmodel-server/ProjectManager.cs index 577dcbac7..ec4b18fd1 100644 --- a/src/dotnet/commands/dotnet-projectmodel-server/ProjectManager.cs +++ b/src/dotnet/commands/dotnet-projectmodel-server/ProjectManager.cs @@ -123,7 +123,7 @@ namespace Microsoft.DotNet.ProjectModel.Server catch (Exception ex) { _log.LogError("A unexpected exception occurred: {0}", ex.ToString()); - + var error = new ErrorMessage { Message = ex.Message diff --git a/src/dotnet/commands/dotnet-publish/Program.cs b/src/dotnet/commands/dotnet-publish/Program.cs index e68014b7c..7d0a07804 100644 --- a/src/dotnet/commands/dotnet-publish/Program.cs +++ b/src/dotnet/commands/dotnet-publish/Program.cs @@ -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(); diff --git a/src/dotnet/commands/dotnet-publish/PublishCommand.cs b/src/dotnet/commands/dotnet-publish/PublishCommand.cs index 1a5eafd10..ee1012341 100644 --- a/src/dotnet/commands/dotnet-publish/PublishCommand.cs +++ b/src/dotnet/commands/dotnet-publish/PublishCommand.cs @@ -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 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) diff --git a/src/dotnet/commands/dotnet-run/RunCommand.cs b/src/dotnet/commands/dotnet-run/RunCommand.cs index 5f5296130..4716b777a 100644 --- a/src/dotnet/commands/dotnet-run/RunCommand.cs +++ b/src/dotnet/commands/dotnet-run/RunCommand.cs @@ -22,6 +22,7 @@ namespace Microsoft.DotNet.Tools.Run ProjectContext _context; List _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 diff --git a/src/dotnet/commands/dotnet-test/Program.cs b/src/dotnet/commands/dotnet-test/Program.cs index 691a27f64..b1abfd5d9 100644 --- a/src/dotnet/commands/dotnet-test/Program.cs +++ b/src/dotnet/commands/dotnet-test/Program.cs @@ -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 CreateProjectContexts(string projectPath, string runtime) + private static IEnumerable 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)); } } } \ No newline at end of file diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/PackageDependencyProviderTests.cs b/test/Microsoft.DotNet.ProjectModel.Tests/PackageDependencyProviderTests.cs index 406a617b1..04b7cffba 100644 --- a/test/Microsoft.DotNet.ProjectModel.Tests/PackageDependencyProviderTests.cs +++ b/test/Microsoft.DotNet.ProjectModel.Tests/PackageDependencyProviderTests.cs @@ -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); @@ -65,7 +65,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests Assert.Equal(4, context.RootProject.Dependencies.Count()); } - + [Fact] public void NoDuplicateReferencesWhenFrameworkMissing() { @@ -79,7 +79,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests // Will fail with dupes if any context.LibraryManager.GetLibraries().ToDictionary(l => l.Identity.Name); } - + [Fact] public void NetCore50ShouldNotResolveFrameworkAssemblies() { diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs index 5a291f133..094c36a85 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs @@ -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 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(); CurrentProcess.Exited += (sender, arg) => { - threadOut.Join(); - threadErr.Join(); + Task.WaitAll(taskOut, taskErr); var result = new CommandResult( CurrentProcess.StartInfo, CurrentProcess.ExitCode, diff --git a/test/dotnet-compile.UnitTests/GivenACompilationDriver.cs b/test/dotnet-compile.UnitTests/GivenACompilationDriver.cs index a705a7beb..d9f1de1d0 100644 --- a/test/dotnet-compile.UnitTests/GivenACompilationDriver.cs +++ b/test/dotnet-compile.UnitTests/GivenACompilationDriver.cs @@ -20,6 +20,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests private Mock _nativeCompilerMock; private List _contexts; private BuildCommandApp _args; + private readonly WorkspaceContext _workspace; public GivenACompilationDriverController() { @@ -34,12 +35,13 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests .Compile(It.IsAny(), It.IsAny())) .Returns(true); + _workspace = WorkspaceContext.Create(ProjectReaderSettings.ReadFromEnvironment(), designTime: false); _contexts = new List { - 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] diff --git a/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs b/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs index 2ee384c16..efa1bec8d 100644 --- a/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs +++ b/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs @@ -188,7 +188,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests It.IsAny())) .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(); @@ -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); diff --git a/test/dotnet-projectmodel-server.Tests/DthTestClient.cs b/test/dotnet-projectmodel-server.Tests/DthTestClient.cs index 8c89377bc..b62dde787 100644 --- a/test/dotnet-projectmodel-server.Tests/DthTestClient.cs +++ b/test/dotnet-projectmodel-server.Tests/DthTestClient.cs @@ -228,7 +228,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests _writer.Dispose(); _networkStream.Dispose(); _readCancellationToken.Cancel(); - + try { _socket.Shutdown(SocketShutdown.Both); diff --git a/test/dotnet-projectmodel-server.Tests/DthTests.cs b/test/dotnet-projectmodel-server.Tests/DthTests.cs index 5e8e6e70c..2c22d38da 100644 --- a/test/dotnet-projectmodel-server.Tests/DthTests.cs +++ b/test/dotnet-projectmodel-server.Tests/DthTests.cs @@ -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("Resolved", true) - .AssertProperty("Name", classLibrary) - .AssertProperty("Errors", array => array.Count == 0) - .AssertProperty("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("Resolved", true); + dependency.AssertProperty("Name", classLibrary); + dependency.AssertProperty("Errors", array => array.Count == 0); + dependency.AssertProperty("Warnings", array => array.Count == 0); } var references = messages.RetrieveSingleMessage(MessageTypes.References) @@ -632,12 +632,12 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests afterDependencies.RetrieveDependency("ClassLibrary3"); } } - + [Fact] public void TestMscorlibLibraryDuplication() { var projectPath = Path.Combine(RepoRoot, "TestAssets", "ProjectModelServer", "MscorlibLibraryDuplication"); - + using (var server = new DthTestServer(_loggerFactory)) using (var client = new DthTestClient(server, _loggerFactory)) { diff --git a/test/dotnet-test.UnitTests/project.json b/test/dotnet-test.UnitTests/project.json index b01e1be2d..c85586274 100644 --- a/test/dotnet-test.UnitTests/project.json +++ b/test/dotnet-test.UnitTests/project.json @@ -28,7 +28,8 @@ } }, "content": [ - "../../TestAssets/TestProjects/ProjectWithTests/project.json" + "../../TestAssets/TestProjects/ProjectWithTests/project.json", + "../../TestAssets/TestProjects/ProjectWithTests/project.lock.json" ], "testRunner": "xunit" }