diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 672a90ce9392e087c0247ab6e1c9bb1b76d2b8f4..ae012a508e2b42363b9d3eadee6fc2ed1ac9ad1b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -529,34 +529,6 @@ private void InferTypeArgsFirstPhase(Binder binder, ref HashSet private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression argument, TypeSymbolWithAnnotations target, ExactOrBoundsKind kind, ref HashSet useSiteDiagnostics) { - // If the argument is a TYPEORNAMESPACEERROR and the pSource is an - // error type, then we want to set it to the generic error type - // that has no name text. This is because of the following scenario: - // - // void M(T t) { } - // void Goo() - // { - // UnknownType t; - // M(t); - // M(undefinedVariable); - // } - // - // In the first call to M, we'll have an EXPRLOCAL with an error type, - // which is correct - we want the parameter help to display that we've - // got an inferred type of UnknownType, which is an error type since - // its undefined. - // - // However, for the M in the second call, we DON'T want to display parameter - // help that gives undefinedVariable as the type parameter for T, because - // there is no parameter of that name, let alone that type. This appears - // as an EXPRTYPEORNAMESPACEERROR with an ErrorType. We create a new error sym - // without the type name. - - // UNDONE: if (pExpr->isTYPEORNAMESPACEERROR() && pSource->IsErrorType()) - // UNDONE:{ - // UNDONE: pSource = GetTypeManager().GetErrorSym(); - // UNDONE:} - // SPEC: * If Ei is an anonymous function, an explicit type parameter // SPEC: inference is made from Ei to Ti. diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs index 1da5d7921a219c977c66bb1f95e8ff03a4d7cdd0..f1d9e2e32586184e10055580d9a6fd150861b15b 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.Parsers.cs @@ -80,9 +80,16 @@ internal static bool DetermineIfNewLineOptionIsSet(string value, NewLineOption o } private static NewLineOption? ConvertToNewLineOption(string value) - => s_newLineOptionsEditorConfigMap.TryGetValue(value, out var option) - ? option - : (NewLineOption?)null; + { + if (s_newLineOptionsEditorConfigMap.TryGetValue(value, out var option)) { + return option; + } + if (s_legacyNewLineOptionsEditorConfigMap.TryGetValue(value, out var legacyOption)) + { + return legacyOption; + } + return null; + } private static string GetNewLineOptionEditorConfigString(OptionSet optionSet) { diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.cs index b1992b24fee2a5fe0028d4a02a00bf3e07d5164f..fab57e7a88544ae10f321c37e469471d3e95bbd2 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.cs @@ -40,6 +40,11 @@ public static partial class CSharpFormattingOptions KeyValuePairUtil.Create("no_change", LabelPositionOptions.NoIndent), KeyValuePairUtil.Create("one_less_than_current", LabelPositionOptions.OneLess), }); + private static readonly BidirectionalMap s_legacyNewLineOptionsEditorConfigMap = + new BidirectionalMap(new[] + { + KeyValuePairUtil.Create("object_collection_array_initalizers", NewLineOption.ObjectCollectionsArrayInitializers), + }); private static readonly BidirectionalMap s_newLineOptionsEditorConfigMap = new BidirectionalMap(new[] { @@ -52,7 +57,7 @@ public static partial class CSharpFormattingOptions KeyValuePairUtil.Create("anonymous_methods", NewLineOption.AnonymousMethods), KeyValuePairUtil.Create("control_blocks", NewLineOption.ControlBlocks), KeyValuePairUtil.Create("anonymous_types", NewLineOption.AnonymousTypes), - KeyValuePairUtil.Create("object_collection_array_initalizers", NewLineOption.ObjectCollectionsArrayInitializers), + KeyValuePairUtil.Create("object_collection_array_initializers", NewLineOption.ObjectCollectionsArrayInitializers), KeyValuePairUtil.Create("lambdas", NewLineOption.Lambdas), KeyValuePairUtil.Create("local_functions", NewLineOption.LocalFunction), }); diff --git a/src/Workspaces/CSharpTest/Formatting/EditorConfigOptionParserTests.cs b/src/Workspaces/CSharpTest/Formatting/EditorConfigOptionParserTests.cs index edfde43278d33fc91eac5b1a10964a5ee17ec54c..cbf06f05991152a6767f053433636c69b98fa82e 100644 --- a/src/Workspaces/CSharpTest/Formatting/EditorConfigOptionParserTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/EditorConfigOptionParserTests.cs @@ -92,6 +92,7 @@ static void TestParseEditorConfigLabelPositioningFalse(string value) InlineData("control_blocks", NewLineOption.ControlBlocks), InlineData("anonymous_types", NewLineOption.AnonymousTypes), InlineData("object_collection_array_initalizers", NewLineOption.ObjectCollectionsArrayInitializers), + InlineData("object_collection_array_initializers", NewLineOption.ObjectCollectionsArrayInitializers), InlineData("lambdas", NewLineOption.Lambdas), InlineData("local_functions", NewLineOption.LocalFunction)] static void TestParseNewLineOptionTrue(string value, NewLineOption option) diff --git a/src/Workspaces/Core/MSBuild/MSBuild/Build/ProjectBuildManager.cs b/src/Workspaces/Core/MSBuild/MSBuild/Build/ProjectBuildManager.cs index a8c4f847c64a9d8daf0c45632138eeb01ca9dec0..dff6fca43ee83f071afcf41ad4b7c1ce9bfab8fe 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/Build/ProjectBuildManager.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/Build/ProjectBuildManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -38,107 +39,66 @@ internal class ProjectBuildManager // don't actually run the compiler { PropertyNames.SkipCompilerExecution, bool.TrueString }, - { PropertyNames.ContinueOnError, PropertyValues.ErrorAndContinue } + { PropertyNames.ContinueOnError, PropertyValues.ErrorAndContinue }, + + // this ensures that the parent project's configuration and platform will be used for + // referenced projects. So, setting Configuration=Release will also cause any project + // references to also be built with Configuration=Release. This is necessary for getting + // a more-likely-to-be-correct output path from project references. + { PropertyNames.ShouldUnsetParentConfigurationAndPlatform, bool.FalseString } }.ToImmutableDictionary(); - private MSB.Evaluation.ProjectCollection _projectCollection; - private MSBuildDiagnosticLogger _logger; - private bool _started; + private readonly ImmutableDictionary _additionalGlobalProperties; + + private MSB.Evaluation.ProjectCollection _batchBuildProjectCollection; + private MSBuildDiagnosticLogger _batchBuildLogger; + private bool _batchBuildStarted; ~ProjectBuildManager() { - if (_started) + if (_batchBuildStarted) { new InvalidOperationException("ProjectBuilderManager.Stop() not called."); } } - private static MSB.Evaluation.Project FindProject( - string path, - IDictionary globalProperties, - MSB.Evaluation.ProjectCollection projectCollection, - CancellationToken cancellationToken) + public ProjectBuildManager(ImmutableDictionary additionalGlobalProperties) { - var loadedProjects = projectCollection.GetLoadedProjects(path); - if (loadedProjects == null || loadedProjects.Count == 0) - { - return null; - } - - // We need to walk through all of the projects that have been previously loaded from this path and - // find the one that has the given set of global properties, plus the default global properties that - // we load every project with. - - globalProperties = globalProperties ?? ImmutableDictionary.Empty; - var totalGlobalProperties = projectCollection.GlobalProperties.Count + globalProperties.Count; - - foreach (var loadedProject in loadedProjects) - { - cancellationToken.ThrowIfCancellationRequested(); - - // If this project has a different number of global properties than we expect, it's not the - // one we're looking for. - if (loadedProject.GlobalProperties.Count != totalGlobalProperties) - { - continue; - } - - // Since we loaded all of them, the projects in this collection should all have the default - // global properties (i.e. the ones in _projectCollection.GlobalProperties). So, we just need to - // check the extra global properties. - - var found = true; - foreach (var (key, value) in globalProperties) - { - // MSBuild escapes the values of a project's global properties, so we must too. - var escapedValue = MSB.Evaluation.ProjectCollection.Escape(value); - - if (!loadedProject.GlobalProperties.TryGetValue(key, out var actualValue) || - !string.Equals(actualValue, escapedValue, StringComparison.Ordinal)) - { - found = false; - break; - } - } - - if (found) - { - return loadedProject; - } - } - - // We couldn't find a project with this path and the set of global properties we expect. - return null; + _additionalGlobalProperties = additionalGlobalProperties ?? ImmutableDictionary.Empty; } - public async Task<(MSB.Evaluation.Project project, DiagnosticLog log)> LoadProjectAsync( - string path, IDictionary globalProperties, CancellationToken cancellationToken) + private ImmutableDictionary AllGlobalProperties + => s_defaultGlobalProperties.AddRange(_additionalGlobalProperties); + + private static async Task<(MSB.Evaluation.Project project, DiagnosticLog log)> LoadProjectAsync( + string path, MSB.Evaluation.ProjectCollection projectCollection, CancellationToken cancellationToken) { var log = new DiagnosticLog(); try { - var projectCollection = _projectCollection ?? new MSB.Evaluation.ProjectCollection(s_defaultGlobalProperties); + var loadedProjects = projectCollection.GetLoadedProjects(path); + if (loadedProjects != null && loadedProjects.Count > 0) + { + Debug.Assert(loadedProjects.Count == 1); - var project = FindProject(path, globalProperties, projectCollection, cancellationToken); + return (loadedProjects.First(), log); + } - if (project == null) + using (var stream = FileUtilities.OpenAsyncRead(path)) + using (var readStream = await SerializableBytes.CreateReadableStreamAsync(stream, cancellationToken).ConfigureAwait(false)) + using (var xmlReader = XmlReader.Create(readStream, s_xmlReaderSettings)) { - using (var stream = FileUtilities.OpenAsyncRead(path)) - using (var readStream = await SerializableBytes.CreateReadableStreamAsync(stream, cancellationToken).ConfigureAwait(false)) - using (var xmlReader = XmlReader.Create(readStream, s_xmlReaderSettings)) - { - var xml = MSB.Construction.ProjectRootElement.Create(xmlReader, projectCollection); + var xml = MSB.Construction.ProjectRootElement.Create(xmlReader, projectCollection); - // When constructing a project from an XmlReader, MSBuild cannot determine the project file path. Setting the - // path explicitly is necessary so that the reserved properties like $(MSBuildProjectDirectory) will work. - xml.FullPath = path; + // When constructing a project from an XmlReader, MSBuild cannot determine the project file path. Setting the + // path explicitly is necessary so that the reserved properties like $(MSBuildProjectDirectory) will work. + xml.FullPath = path; - project = new MSB.Evaluation.Project(xml, globalProperties, toolsVersion: null, projectCollection); - } - } + var project = new MSB.Evaluation.Project(xml, globalProperties: null, toolsVersion: null, projectCollection); - return (project, log); + return (project, log); + } } catch (Exception e) { @@ -147,42 +107,70 @@ internal class ProjectBuildManager } } + public Task<(MSB.Evaluation.Project project, DiagnosticLog log)> LoadProjectAsync( + string path, CancellationToken cancellationToken) + { + if (_batchBuildStarted) + { + return LoadProjectAsync(path, _batchBuildProjectCollection, cancellationToken); + } + else + { + var projectCollection = new MSB.Evaluation.ProjectCollection(AllGlobalProperties); + try + { + return LoadProjectAsync(path, projectCollection, cancellationToken); + } + finally + { + // unload project so collection will release global strings + projectCollection.UnloadAllProjects(); + } + } + } + public async Task TryGetOutputFilePathAsync( - string path, IDictionary globalProperties, CancellationToken cancellationToken) + string path, CancellationToken cancellationToken) { - // This tries to get the project output path and retrieving the $(TargetPath) property. + Debug.Assert(_batchBuildStarted); + + // This tries to get the project output path and retrieving the evaluated $(TargetPath) property. - var (project, _) = await LoadProjectAsync(path, globalProperties, cancellationToken).ConfigureAwait(false); + var (project, _) = await LoadProjectAsync(path, cancellationToken).ConfigureAwait(false); return project?.GetPropertyValue(PropertyNames.TargetPath); } - public void Start() + public bool BatchBuildStarted => _batchBuildStarted; + + public void StartBatchBuild(IDictionary globalProperties = null) { - if (_started) + if (_batchBuildStarted) { throw new InvalidOperationException(); } - _projectCollection = new MSB.Evaluation.ProjectCollection(s_defaultGlobalProperties); + globalProperties = globalProperties ?? ImmutableDictionary.Empty; + var allProperties = s_defaultGlobalProperties.AddRange(globalProperties); + _batchBuildProjectCollection = new MSB.Evaluation.ProjectCollection(allProperties); - _logger = new MSBuildDiagnosticLogger() + _batchBuildLogger = new MSBuildDiagnosticLogger() { Verbosity = MSB.Framework.LoggerVerbosity.Normal }; - var buildParameters = new MSB.Execution.BuildParameters(_projectCollection) + var buildParameters = new MSB.Execution.BuildParameters(_batchBuildProjectCollection) { - Loggers = new MSB.Framework.ILogger[] { _logger } + Loggers = new MSB.Framework.ILogger[] { _batchBuildLogger } }; MSB.Execution.BuildManager.DefaultBuildManager.BeginBuild(buildParameters); - _started = true; + _batchBuildStarted = true; } - public void Stop() + public void EndBatchBuild() { - if (!_started) + if (!_batchBuildStarted) { throw new InvalidOperationException(); } @@ -190,16 +178,16 @@ public void Stop() MSB.Execution.BuildManager.DefaultBuildManager.EndBuild(); // unload project so collection will release global strings - _projectCollection.UnloadAllProjects(); - _projectCollection = null; - _logger = null; - _started = false; + _batchBuildProjectCollection.UnloadAllProjects(); + _batchBuildProjectCollection = null; + _batchBuildLogger = null; + _batchBuildStarted = false; } public Task BuildProjectAsync( MSB.Evaluation.Project project, DiagnosticLog log, CancellationToken cancellationToken) { - Debug.Assert(_started); + Debug.Assert(_batchBuildStarted); var targets = new[] { TargetNames.Compile, TargetNames.CoreCompile }; @@ -223,7 +211,7 @@ public void Stop() } } - _logger.SetProjectAndLog(projectInstance.FullPath, log); + _batchBuildLogger.SetProjectAndLog(projectInstance.FullPath, log); var buildRequestData = new MSB.Execution.BuildRequestData(projectInstance, targets); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs b/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs index 0b6a173d8ebea432b7630430242aebb2f82b34f1..525efe24d69d0edb54d71f4236041ed4fea8bf1f 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs @@ -49,6 +49,7 @@ internal static class PropertyNames public const string RemoveIntegerChecks = nameof(RemoveIntegerChecks); public const string ResolvedCodeAnalysisRuleSet = nameof(ResolvedCodeAnalysisRuleSet); public const string RootNamespace = nameof(RootNamespace); + public const string ShouldUnsetParentConfigurationAndPlatform = nameof(ShouldUnsetParentConfigurationAndPlatform); public const string SignAssembly = nameof(SignAssembly); public const string SkipCompilerExecution = nameof(SkipCompilerExecution); public const string StartupObject = nameof(StartupObject); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 10c8c588b5e9488ddde8043ad7b744d8ea6a904c..4ecbb64d3a4cec80117892b0df9a99cfebd0d0e4 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -130,7 +130,7 @@ public async Task> LoadAsync(CancellationToken cance var results = ImmutableArray.CreateBuilder(); var processedPaths = new HashSet(PathUtilities.Comparer); - _buildManager.Start(); + _buildManager.StartBatchBuild(_globalProperties); try { foreach (var projectPath in _requestedProjectPaths) @@ -171,7 +171,7 @@ public async Task> LoadAsync(CancellationToken cance } finally { - _buildManager.Stop(); + _buildManager.EndBatchBuild(); } } @@ -186,7 +186,7 @@ private async Task> LoadProjectFileInfosAsync(st ProjectLoadOperation.Evaluate, projectPath, targetFramework: null, - () => loader.LoadProjectFileAsync(projectPath, _globalProperties, _buildManager, cancellationToken) + () => loader.LoadProjectFileAsync(projectPath, _buildManager, cancellationToken) ).ConfigureAwait(false); // If there were any failures during load, we won't be able to build the project. So, bail early with an empty project. diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker_ResolveReferences.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker_ResolveReferences.cs index e5c3371ea3c5e66bfd4c29e25136d4284ede4c36..42260d8d26b4561874d310bf34dedd34f5a2e155 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker_ResolveReferences.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker_ResolveReferences.cs @@ -314,7 +314,7 @@ private bool IsProjectLoadable(string projectPath) private async Task VerifyUnloadableProjectOutputExistsAsync(string projectPath, ResolvedReferencesBuilder builder, CancellationToken cancellationToken) { - var outputFilePath = await _buildManager.TryGetOutputFilePathAsync(projectPath, _globalProperties, cancellationToken).ConfigureAwait(false); + var outputFilePath = await _buildManager.TryGetOutputFilePathAsync(projectPath, cancellationToken).ConfigureAwait(false); return builder.Contains(outputFilePath) && File.Exists(outputFilePath); } diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.cs index 72b0655dd1acc4ee0404ba7a5b9cd2d93d522974..e263db71796c12ada572ebf81bc93a9eb602b305 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.cs @@ -27,8 +27,6 @@ public partial class MSBuildProjectLoader private readonly NonReentrantLock _dataGuard = new NonReentrantLock(); private ImmutableDictionary _properties; - internal readonly ProjectBuildManager BuildManager; - internal MSBuildProjectLoader( Workspace workspace, DiagnosticReporter diagnosticReporter, @@ -46,8 +44,6 @@ public partial class MSBuildProjectLoader { _properties = _properties.AddRange(properties); } - - BuildManager = new ProjectBuildManager(); } /// @@ -183,12 +179,14 @@ private DiagnosticReportingMode GetReportingModeForUnrecognizedProjects() } } + var buildManager = new ProjectBuildManager(_properties); + var worker = new Worker( _workspace, _diagnosticReporter, _pathResolver, _projectFileLoaderRegistry, - BuildManager, + buildManager, projectPaths.ToImmutable(), baseDirectory: Path.GetDirectoryName(absoluteSolutionPath), _properties, @@ -237,12 +235,14 @@ private DiagnosticReportingMode GetReportingModeForUnrecognizedProjects() onPathFailure: reportingMode, onLoaderFailure: reportingMode); + var buildManager = new ProjectBuildManager(_properties); + var worker = new Worker( _workspace, _diagnosticReporter, _pathResolver, _projectFileLoaderRegistry, - BuildManager, + buildManager, requestedProjectPaths: ImmutableArray.Create(projectFilePath), baseDirectory: Directory.GetCurrentDirectory(), globalProperties: _properties, diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs index 733732bd9d17dc9c9930988871a166e204ab9645..13ba11884f44b2010e4f750a11b1fcdf3a900ffc 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -281,11 +282,12 @@ protected override void ApplyProjectChanges(ProjectChanges projectChanges) if (this.HasProjectFileChanges(projectChanges)) { var projectPath = project.FilePath; - if (_projectFileLoaderRegistry.TryGetLoaderFromProjectPath(projectPath, out var loader)) + if (_projectFileLoaderRegistry.TryGetLoaderFromProjectPath(projectPath, out var fileLoader)) { try { - _applyChangesProjectFile = loader.LoadProjectFileAsync(projectPath, _loader.Properties, _loader.BuildManager, CancellationToken.None).Result; + var buildManager = new ProjectBuildManager(_loader.Properties); + _applyChangesProjectFile = fileLoader.LoadProjectFileAsync(projectPath, buildManager, CancellationToken.None).Result; } catch (IOException exception) { diff --git a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/IProjectFileLoader.cs b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/IProjectFileLoader.cs index 41e09be1967e6f61907468291454b3b1a9fcb641..778f768d60bf912c673c0c5023fbb7677290bf98 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/IProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/IProjectFileLoader.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -13,7 +12,6 @@ internal interface IProjectFileLoader : ILanguageService string Language { get; } Task LoadProjectFileAsync( string path, - IDictionary globalProperties, ProjectBuildManager buildManager, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileLoader.cs b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileLoader.cs index e0316977191db133c003dc59ad74e24f36d811a1..d3fd39f01de8d8c352a2e7304b8ac67bf88e783f 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileLoader.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,7 +18,7 @@ internal abstract class ProjectFileLoader : IProjectFileLoader protected abstract ProjectFile CreateProjectFile(MSB.Evaluation.Project project, ProjectBuildManager buildManager, DiagnosticLog log); - public async Task LoadProjectFileAsync(string path, IDictionary globalProperties, ProjectBuildManager buildManager, CancellationToken cancellationToken) + public async Task LoadProjectFileAsync(string path, ProjectBuildManager buildManager, CancellationToken cancellationToken) { if (path == null) { @@ -27,7 +26,7 @@ public async Task LoadProjectFileAsync(string path, IDictionary + + + + + + + netstandard2.0 + + + diff --git a/src/Workspaces/CoreTestUtilities/Resources/Issue30174/ReferencedLibrary/ReferencedLibrary.csproj b/src/Workspaces/CoreTestUtilities/Resources/Issue30174/ReferencedLibrary/ReferencedLibrary.csproj new file mode 100644 index 0000000000000000000000000000000000000000..9f5c4f4abb611103e8d62ab979dda9bfcb5a7fc2 --- /dev/null +++ b/src/Workspaces/CoreTestUtilities/Resources/Issue30174/ReferencedLibrary/ReferencedLibrary.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/Workspaces/CoreTestUtilities/Resources/Issue30174/ReferencedLibrary/SomeMetadataAttribute.cs b/src/Workspaces/CoreTestUtilities/Resources/Issue30174/ReferencedLibrary/SomeMetadataAttribute.cs new file mode 100644 index 0000000000000000000000000000000000000000..12ccd988aec5db87a7f0ee39fafc016f06e15377 --- /dev/null +++ b/src/Workspaces/CoreTestUtilities/Resources/Issue30174/ReferencedLibrary/SomeMetadataAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace ReferencedLibrary +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public sealed class SomeMetadataAttribute : Attribute + { + } +} diff --git a/src/Workspaces/CoreTestUtilities/Resources/Issue30174/Solution.sln b/src/Workspaces/CoreTestUtilities/Resources/Issue30174/Solution.sln new file mode 100644 index 0000000000000000000000000000000000000000..a8b1a1e657e419e231b3e89c4108e43e8189df0a --- /dev/null +++ b/src/Workspaces/CoreTestUtilities/Resources/Issue30174/Solution.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReferencedLibrary", "ReferencedLibrary\ReferencedLibrary.csproj", "{A5800480-7965-4BA3-95F5-4830B831163D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InspectedLibrary", "InspectedLibrary\InspectedLibrary.csproj", "{86B44D47-6E01-4385-AFDB-C82695AFD2DA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A5800480-7965-4BA3-95F5-4830B831163D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Debug|x64.Build.0 = Debug|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Debug|x86.Build.0 = Debug|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Release|Any CPU.Build.0 = Release|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Release|x64.ActiveCfg = Release|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Release|x64.Build.0 = Release|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Release|x86.ActiveCfg = Release|Any CPU + {A5800480-7965-4BA3-95F5-4830B831163D}.Release|x86.Build.0 = Release|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Debug|x64.ActiveCfg = Debug|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Debug|x64.Build.0 = Debug|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Debug|x86.ActiveCfg = Debug|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Debug|x86.Build.0 = Debug|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Release|Any CPU.Build.0 = Release|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Release|x64.ActiveCfg = Release|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Release|x64.Build.0 = Release|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Release|x86.ActiveCfg = Release|Any CPU + {86B44D47-6E01-4385-AFDB-C82695AFD2DA}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/Workspaces/CoreTestUtilities/TestFiles/Resources.cs b/src/Workspaces/CoreTestUtilities/TestFiles/Resources.cs index b61cbb987aa7e1020c9ed20ca3235627b57eeeac..d8cf7b908351aaf0ccdf7a21ed953a755a69ba3e 100644 --- a/src/Workspaces/CoreTestUtilities/TestFiles/Resources.cs +++ b/src/Workspaces/CoreTestUtilities/TestFiles/Resources.cs @@ -87,6 +87,7 @@ public static class SolutionFiles public static string DuplicatedGuidsBecomeCircularReferential => GetText("SolutionFiles.DuplicatedGuidsBecomeCircularReferential.sln"); public static string EmptyLineBetweenProjectBlock => GetText("SolutionFiles.EmptyLineBetweenProjectBlock.sln"); public static string Issue29122_Solution => GetText("Issue29122.TestVB2.sln"); + public static string Issue30174_Solution => GetText("Issue30174.Solution.sln"); public static string InvalidProjectPath => GetText("SolutionFiles.InvalidProjectPath.sln"); public static string MissingEndProject1 => GetText("SolutionFiles.MissingEndProject1.sln"); public static string MissingEndProject2 => GetText("SolutionFiles.MissingEndProject2.sln"); @@ -125,6 +126,8 @@ public static class CSharp public static string ExternAlias => GetText("ProjectFiles.CSharp.ExternAlias.csproj"); public static string ExternAlias2 => GetText("ProjectFiles.CSharp.ExternAlias2.csproj"); public static string ForEmittedOutput => GetText("ProjectFiles.CSharp.ForEmittedOutput.csproj"); + public static string Issue30174_InspectedLibrary => GetText("Issue30174.InspectedLibrary.InspectedLibrary.csproj"); + public static string Issue30174_ReferencedLibrary => GetText("Issue30174.ReferencedLibrary.ReferencedLibrary.csproj"); public static string MsbuildError => GetText("ProjectFiles.CSharp.MsbuildError.csproj"); public static string MallformedAdditionalFilePath => GetText("ProjectFiles.CSharp.MallformedAdditionalFilePath.csproj"); public static string NetCoreApp2_Project => GetText("NetCoreApp2.Project.csproj"); @@ -186,6 +189,8 @@ public static class CSharp public static string CSharpClass_WithConditionalAttributes => GetText("SourceFiles.CSharp.CSharpClass_WithConditionalAttributes.cs"); public static string CSharpConsole => GetText("SourceFiles.CSharp.CSharpConsole.cs"); public static string CSharpExternAlias => GetText("SourceFiles.CSharp.CSharpExternAlias.cs"); + public static string Issue30174_InspectedClass => GetText("Issue30174.InspectedLibrary.InspectedClass.cs"); + public static string Issue30174_SomeMetadataAttribute => GetText("Issue30174.ReferencedLibrary.SomeMetadataAttribute.cs"); public static string NetCoreApp2_Program => GetText("NetCoreApp2.Program.cs"); public static string NetCoreApp2AndLibrary_Class1 => GetText("NetCoreApp2AndLibrary.Class1.cs"); public static string NetCoreApp2AndLibrary_Program => GetText("NetCoreApp2AndLibrary.Program.cs"); diff --git a/src/Workspaces/CoreTestUtilities/WorkspaceTestBase.cs b/src/Workspaces/CoreTestUtilities/WorkspaceTestBase.cs index 7b3f185b192ed36d18aeecdc8aaf612c9dbb6ef0..c75583bd61aa5f31682d854e023c4df628de235c 100644 --- a/src/Workspaces/CoreTestUtilities/WorkspaceTestBase.cs +++ b/src/Workspaces/CoreTestUtilities/WorkspaceTestBase.cs @@ -90,6 +90,7 @@ protected void CreateCSharpFiles() protected FileSet GetBaseFiles() { return new FileSet( + (@"NuGet.Config", Resources.NuGet_Config), (@"Directory.Build.props", Resources.Directory_Build_props), (@"Directory.Build.targets", Resources.Directory_Build_targets)); } diff --git a/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs index 7a1433f11052df99ad1d39642f2def7909b102b0..0fecbde3919e47e916f900f06a07b4640a6f4a22 100644 --- a/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs @@ -3517,13 +3517,12 @@ public async Task TestOpenProject_CommandLineArgsHaveNoErrors() var projectFilePath = GetSolutionFileName(@"CSharpProject\CSharpProject.csproj"); - var properties = ImmutableDictionary.Empty; - var buildManager = new ProjectBuildManager(); - buildManager.Start(); + var buildManager = new ProjectBuildManager(ImmutableDictionary.Empty); + buildManager.StartBatchBuild(); - var projectFile = await loader.LoadProjectFileAsync(projectFilePath, properties, buildManager, CancellationToken.None); + var projectFile = await loader.LoadProjectFileAsync(projectFilePath, buildManager, CancellationToken.None); var projectFileInfo = (await projectFile.GetProjectFileInfosAsync(CancellationToken.None)).Single(); - buildManager.Stop(); + buildManager.EndBatchBuild(); var commandLineParser = workspace.Services .GetLanguageServices(loader.Language) diff --git a/src/Workspaces/MSBuildTest/NetCoreTests.cs b/src/Workspaces/MSBuildTest/NetCoreTests.cs index 62805e03af9ae95f9eaf8613edfcbf5859780c3f..a65018d7a1de5e821b6330edd2a69876733d7a89 100644 --- a/src/Workspaces/MSBuildTest/NetCoreTests.cs +++ b/src/Workspaces/MSBuildTest/NetCoreTests.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.CodeAnalysis.UnitTests.TestFiles; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -22,7 +23,7 @@ public NetCoreTests() _nugetCacheDir = SolutionDirectory.CreateDirectory(".packages"); } - private void DotNetRestore(string solutionOrProjectFileName) + private void RunDotNet(string arguments) { Assert.NotNull(DotNetCoreSdk.ExePath); @@ -32,12 +33,29 @@ private void DotNetRestore(string solutionOrProjectFileName) }; var restoreResult = ProcessUtilities.Run( - fileName: DotNetCoreSdk.ExePath, - arguments: $@"msbuild ""{solutionOrProjectFileName}"" /t:restore /bl:{Path.Combine(SolutionDirectory.Path, "restore.binlog")}", + DotNetCoreSdk.ExePath, arguments, workingDirectory: SolutionDirectory.Path, additionalEnvironmentVars: environmentVariables); - Assert.True(restoreResult.ExitCode == 0, $"Restore failed with exit code {restoreResult.ExitCode}: {restoreResult.Output}"); + Assert.True(restoreResult.ExitCode == 0, $"{DotNetCoreSdk.ExePath} failed with exit code {restoreResult.ExitCode}: {restoreResult.Output}"); + } + + private void DotNetRestore(string solutionOrProjectFileName) + { + var arguments = $@"msbuild ""{solutionOrProjectFileName}"" /t:restore /bl:{Path.Combine(SolutionDirectory.Path, "restore.binlog")}"; + RunDotNet(arguments); + } + + private void DotNetBuild(string solutionOrProjectFileName, string configuration = null) + { + var arguments = $@"msbuild ""{solutionOrProjectFileName}"" /bl:{Path.Combine(SolutionDirectory.Path, "build.binlog")}"; + + if (configuration != null) + { + arguments += $" /p:Configuration={configuration}"; + } + + RunDotNet(arguments); } [ConditionalFact(typeof(VisualStudioMSBuildInstalled), typeof(DotNetCoreSdk.IsAvailable))] @@ -347,5 +365,37 @@ public async Task TestOpenSolution_NetCoreMultiTFMWithProjectReferenceToFSharp() } } } + + [ConditionalFact(typeof(VisualStudioMSBuildInstalled), typeof(DotNetCoreSdk.IsAvailable))] + [Trait(Traits.Feature, Traits.Features.MSBuildWorkspace)] + [Trait(Traits.Feature, Traits.Features.NetCore)] + public async Task TestOpenProject_ReferenceConfigurationSpecificMetadata() + { + var files = GetBaseFiles() + .WithFile(@"Solution.sln", Resources.SolutionFiles.Issue30174_Solution) + .WithFile(@"InspectedLibrary\InspectedLibrary.csproj", Resources.ProjectFiles.CSharp.Issue30174_InspectedLibrary) + .WithFile(@"InspectedLibrary\InspectedClass.cs", Resources.SourceFiles.CSharp.Issue30174_InspectedClass) + .WithFile(@"ReferencedLibrary\ReferencedLibrary.csproj", Resources.ProjectFiles.CSharp.Issue30174_ReferencedLibrary) + .WithFile(@"ReferencedLibrary\SomeMetadataAttribute.cs", Resources.SourceFiles.CSharp.Issue30174_SomeMetadataAttribute); + + CreateFiles(files); + + DotNetRestore("Solution.sln"); + DotNetBuild("Solution.sln", configuration: "Release"); + + var projectFilePath = GetSolutionFileName(@"InspectedLibrary\InspectedLibrary.csproj"); + + using (var workspace = CreateMSBuildWorkspace(("Configuration", "Release"))) + { + workspace.LoadMetadataForReferencedProjects = true; + + var project = await workspace.OpenProjectAsync(projectFilePath); + + Assert.Empty(project.ProjectReferences); + Assert.Empty(workspace.Diagnostics); + + var compilation = await project.GetCompilationAsync(); + } + } } }