Map solution configurations to existing project configurations on add.

This commit implements solution configuration to project configuration mapping.

Previously, when a project was added to the solution with the `sln add`
command, solution configurations would be mapped to a project configuration and
platform of the same name, regardless of whether or not the project had a
configuration or platform of that name.  This caused the solution to appear
dirty when opened in Visual Studio if the configuration or platform did not
exist at the project level because Visual Studio would attempt to correct the
mapping.

The fix is to check what configurations and platforms are supported by the
project and only map to what is present.  If a solution configuration can't be
mapped, the first configuration/platform supported by the project is chosen;
this is consistent with how Visual Studio does the fallback mapping.

Fixes #6221.
This commit is contained in:
Peter Huene 2017-12-15 17:01:37 -08:00
parent b456668193
commit f7009106d8
No known key found for this signature in database
GPG key ID: E1D265D820213D6A
10 changed files with 438 additions and 63 deletions

View file

@ -0,0 +1,21 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26006.2
MinimumVisualStudioVersion = 10.0.40219.1
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
Foo Bar|Any CPU = Foo Bar|Any CPU
Foo Bar|x64 = Foo Bar|x64
Foo Bar|x86 = Foo Bar|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,15 @@
// 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;
namespace ProjectWithAdditionalConfigs
{
public static class Library
{
public static string GetMessage()
{
return "Hello World!";
}
}
}

View file

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProjectGuid>{A302325B-D680-4C0E-8680-7AE283981624}</ProjectGuid>
<Platforms>AnyCPU;x64;x86;AdditionalPlatform</Platforms>
<Configurations>Debug;Release;FooBar;AdditionalConfiguration</Configurations>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,15 @@
// 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;
namespace ProjectWithMatchingConfigs
{
public static class Library
{
public static string GetMessage()
{
return "Hello World!";
}
}
}

View file

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProjectGuid>{C9601CA2-DB64-4FB6-B463-368C7764BF0D}</ProjectGuid>
<Platforms>AnyCPU;x64;x86</Platforms>
<Configurations>Debug;Release;FooBar</Configurations>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,15 @@
// 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;
namespace ProjectWithoutMatchingConfigs
{
public static class Library
{
public static string GetMessage()
{
return "Hello World!";
}
}
}

View file

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProjectGuid>{C49B64DE-4401-4825-8A88-10DCB5950E57}</ProjectGuid>
</PropertyGroup>
</Project>

View file

@ -4,6 +4,7 @@
using Microsoft.Build.Execution;
using Microsoft.DotNet.Cli.Sln.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.DotNet.Tools.Common
@ -45,5 +46,25 @@ namespace Microsoft.DotNet.Tools.Common
return projectTypeGuid;
}
public static IEnumerable<string> GetPlatforms(this ProjectInstance projectInstance)
{
return (projectInstance.GetPropertyValue("Platforms") ?? "")
.Split(
new char[] { ';' },
StringSplitOptions.RemoveEmptyEntries)
.Where(p => !string.IsNullOrWhiteSpace(p))
.DefaultIfEmpty("AnyCPU");
}
public static IEnumerable<string> GetConfigurations(this ProjectInstance projectInstance)
{
return (projectInstance.GetPropertyValue("Configurations") ?? "Debug;Release")
.Split(
new char[] { ';' },
StringSplitOptions.RemoveEmptyEntries)
.Where(c => !string.IsNullOrWhiteSpace(c))
.DefaultIfEmpty("Debug");
}
}
}

View file

@ -59,7 +59,16 @@ namespace Microsoft.DotNet.Tools.Common
FilePath = relativeProjectPath
};
slnFile.AddDefaultBuildConfigurations(slnProject);
// NOTE: The order you create the sections determines the order they are written to the sln
// file. In the case of an empty sln file, in order to make sure the solution configurations
// section comes first we need to add it first. This doesn't affect correctness but does
// stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level
// it shouldn't care about the VS implementation details. That's why we handle this here.
slnFile.AddDefaultBuildConfigurations();
slnFile.MapSolutionConfigurationsToProject(
projectInstance,
slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id));
slnFile.AddSolutionFolders(slnProject);
@ -70,11 +79,13 @@ namespace Microsoft.DotNet.Tools.Common
}
}
public static void AddDefaultBuildConfigurations(this SlnFile slnFile, SlnProject slnProject)
private static void AddDefaultBuildConfigurations(this SlnFile slnFile)
{
if (slnProject == null)
var configurationsSection = slnFile.SolutionConfigurationsSection;
if (!configurationsSection.IsEmpty)
{
throw new ArgumentException();
return;
}
var defaultConfigurations = new List<string>()
@ -87,57 +98,108 @@ namespace Microsoft.DotNet.Tools.Common
"Release|x86",
};
// NOTE: The order you create the sections determines the order they are written to the sln
// file. In the case of an empty sln file, in order to make sure the solution configurations
// section comes first we need to add it first. This doesn't affect correctness but does
// stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level
// it shouldn't care about the VS implementation details. That's why we handle this here.
AddDefaultSolutionConfigurations(defaultConfigurations, slnFile.SolutionConfigurationsSection);
AddDefaultProjectConfigurations(
defaultConfigurations,
slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id));
}
private static void AddDefaultSolutionConfigurations(
List<string> defaultConfigurations,
SlnPropertySet solutionConfigs)
{
foreach (var config in defaultConfigurations)
{
if (!solutionConfigs.ContainsKey(config))
configurationsSection[config] = config;
}
}
private static void MapSolutionConfigurationsToProject(
this SlnFile slnFile,
ProjectInstance projectInstance,
SlnPropertySet solutionProjectConfigs)
{
var (projectConfigurations, defaultProjectConfiguration) = GetKeysDictionary(projectInstance.GetConfigurations());
var (projectPlatforms, defaultProjectPlatform) = GetKeysDictionary(projectInstance.GetPlatforms());
foreach (var solutionConfigKey in slnFile.SolutionConfigurationsSection.Keys)
{
var projectConfigKey = MapSolutionConfigKeyToProjectConfigKey(
solutionConfigKey,
projectConfigurations,
defaultProjectConfiguration,
projectPlatforms,
defaultProjectPlatform);
if (projectConfigKey == null)
{
solutionConfigs[config] = config;
continue;
}
var activeConfigKey = $"{solutionConfigKey}.ActiveCfg";
if (!solutionProjectConfigs.ContainsKey(activeConfigKey))
{
solutionProjectConfigs[activeConfigKey] = projectConfigKey;
}
var buildKey = $"{solutionConfigKey}.Build.0";
if (!solutionProjectConfigs.ContainsKey(buildKey))
{
solutionProjectConfigs[buildKey] = projectConfigKey;
}
}
}
private static void AddDefaultProjectConfigurations(
List<string> defaultConfigurations,
SlnPropertySet projectConfigs)
private static (Dictionary<string, string> Keys, string DefaultKey) GetKeysDictionary(IEnumerable<string> keys)
{
foreach (var config in defaultConfigurations)
{
var activeCfgKey = $"{config}.ActiveCfg";
if (!projectConfigs.ContainsKey(activeCfgKey))
{
projectConfigs[activeCfgKey] = config;
}
// A dictionary mapping key -> key is used instead of a HashSet so the original case of the key can be retrieved from the set
var dictionary = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
var build0Key = $"{config}.Build.0";
if (!projectConfigs.ContainsKey(build0Key))
{
projectConfigs[build0Key] = config;
}
foreach (var key in keys)
{
dictionary[key] = key;
}
return (dictionary, keys.FirstOrDefault());
}
public static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProject)
private static string GetMatchingProjectKey(IDictionary<string, string> projectKeys, string solutionKey)
{
if (slnProject == null)
string projectKey;
if (projectKeys.TryGetValue(solutionKey, out projectKey))
{
throw new ArgumentException();
return projectKey;
}
var keyWithoutWhitespace = String.Concat(solutionKey.Where(c => !Char.IsWhiteSpace(c)));
if (projectKeys.TryGetValue(keyWithoutWhitespace, out projectKey))
{
return projectKey;
}
return null;
}
private static string MapSolutionConfigKeyToProjectConfigKey(
string solutionConfigKey,
Dictionary<string, string> projectConfigurations,
string defaultProjectConfiguration,
Dictionary<string, string> projectPlatforms,
string defaultProjectPlatform)
{
var pair = solutionConfigKey.Split(new char[] {'|'}, 2);
if (pair.Length != 2)
{
return null;
}
var projectConfiguration = GetMatchingProjectKey(projectConfigurations, pair[0]) ?? defaultProjectConfiguration;
if (projectConfiguration == null)
{
return null;
}
var projectPlatform = GetMatchingProjectKey(projectPlatforms, pair[1]) ?? defaultProjectPlatform;
if (projectPlatform == null)
{
return null;
}
// VS stores "Any CPU" platform in the solution regardless of how it is named at the project level
return $"{projectConfiguration}|{(projectPlatform == "AnyCPU" ? "Any CPU" : projectPlatform)}";
}
private static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProject)
{
var solutionFolders = slnProject.GetSolutionFoldersFromProject();
if (solutionFolders.Any())

View file

@ -81,16 +81,16 @@ Global
{7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86
__LIB_PROJECT_GUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|Any CPU.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|x64
__LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|x64
__LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|x86
__LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|x86
__LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Release|Any CPU.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|Any CPU.Build.0 = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|x64
__LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|x64
__LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|x86
__LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|x86
__LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -117,16 +117,16 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
__LIB_PROJECT_GUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|Any CPU.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|x64
__LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|x64
__LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|x86
__LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|x86
__LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Release|Any CPU.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|Any CPU.Build.0 = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|x64
__LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|x64
__LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|x86
__LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|x86
__LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
";
@ -166,16 +166,16 @@ Global
{7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86
__LIB_PROJECT_GUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|Any CPU.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|x64
__LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|x64
__LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|x86
__LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|x86
__LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|Any CPU
__LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|Any CPU
__LIB_PROJECT_GUID__.Release|Any CPU.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|Any CPU.Build.0 = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|x64
__LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|x64
__LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|x86
__LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|x86
__LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|Any CPU
__LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -184,6 +184,141 @@ Global
__LIB_PROJECT_GUID__ = __SRC_FOLDER_GUID__
EndGlobalSection
EndGlobal
";
private const string ExpectedSlnFileAfterAddingProjectWithoutMatchingConfigs = @"
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26006.2
MinimumVisualStudioVersion = 10.0.40219.1
Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ProjectWithoutMatchingConfigs"", ""ProjectWithoutMatchingConfigs\ProjectWithoutMatchingConfigs.csproj"", ""{C49B64DE-4401-4825-8A88-10DCB5950E57}""
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
Foo Bar|Any CPU = Foo Bar|Any CPU
Foo Bar|x64 = Foo Bar|x64
Foo Bar|x86 = Foo Bar|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Debug|x64.ActiveCfg = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Debug|x64.Build.0 = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Debug|x86.ActiveCfg = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Debug|x86.Build.0 = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Release|Any CPU.Build.0 = Release|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Release|x64.ActiveCfg = Release|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Release|x64.Build.0 = Release|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Release|x86.ActiveCfg = Release|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Release|x86.Build.0 = Release|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Foo Bar|Any CPU.ActiveCfg = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Foo Bar|Any CPU.Build.0 = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Foo Bar|x64.ActiveCfg = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Foo Bar|x64.Build.0 = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Foo Bar|x86.ActiveCfg = Debug|Any CPU
{C49B64DE-4401-4825-8A88-10DCB5950E57}.Foo Bar|x86.Build.0 = Debug|Any CPU
EndGlobalSection
EndGlobal
";
private const string ExpectedSlnFileAfterAddingProjectWithMatchingConfigs = @"
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26006.2
MinimumVisualStudioVersion = 10.0.40219.1
Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ProjectWithMatchingConfigs"", ""ProjectWithMatchingConfigs\ProjectWithMatchingConfigs.csproj"", ""{C9601CA2-DB64-4FB6-B463-368C7764BF0D}""
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
Foo Bar|Any CPU = Foo Bar|Any CPU
Foo Bar|x64 = Foo Bar|x64
Foo Bar|x86 = Foo Bar|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Debug|x64.ActiveCfg = Debug|x64
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Debug|x64.Build.0 = Debug|x64
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Debug|x86.ActiveCfg = Debug|x86
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Debug|x86.Build.0 = Debug|x86
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Release|Any CPU.Build.0 = Release|Any CPU
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Release|x64.ActiveCfg = Release|x64
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Release|x64.Build.0 = Release|x64
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Release|x86.ActiveCfg = Release|x86
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Release|x86.Build.0 = Release|x86
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Foo Bar|Any CPU.ActiveCfg = FooBar|Any CPU
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Foo Bar|Any CPU.Build.0 = FooBar|Any CPU
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Foo Bar|x64.ActiveCfg = FooBar|x64
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Foo Bar|x64.Build.0 = FooBar|x64
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Foo Bar|x86.ActiveCfg = FooBar|x86
{C9601CA2-DB64-4FB6-B463-368C7764BF0D}.Foo Bar|x86.Build.0 = FooBar|x86
EndGlobalSection
EndGlobal
";
private const string ExpectedSlnFileAfterAddingProjectWithAdditionalConfigs = @"
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26006.2
MinimumVisualStudioVersion = 10.0.40219.1
Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ProjectWithAdditionalConfigs"", ""ProjectWithAdditionalConfigs\ProjectWithAdditionalConfigs.csproj"", ""{A302325B-D680-4C0E-8680-7AE283981624}""
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
Foo Bar|Any CPU = Foo Bar|Any CPU
Foo Bar|x64 = Foo Bar|x64
Foo Bar|x86 = Foo Bar|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A302325B-D680-4C0E-8680-7AE283981624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A302325B-D680-4C0E-8680-7AE283981624}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A302325B-D680-4C0E-8680-7AE283981624}.Debug|x64.ActiveCfg = Debug|x64
{A302325B-D680-4C0E-8680-7AE283981624}.Debug|x64.Build.0 = Debug|x64
{A302325B-D680-4C0E-8680-7AE283981624}.Debug|x86.ActiveCfg = Debug|x86
{A302325B-D680-4C0E-8680-7AE283981624}.Debug|x86.Build.0 = Debug|x86
{A302325B-D680-4C0E-8680-7AE283981624}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A302325B-D680-4C0E-8680-7AE283981624}.Release|Any CPU.Build.0 = Release|Any CPU
{A302325B-D680-4C0E-8680-7AE283981624}.Release|x64.ActiveCfg = Release|x64
{A302325B-D680-4C0E-8680-7AE283981624}.Release|x64.Build.0 = Release|x64
{A302325B-D680-4C0E-8680-7AE283981624}.Release|x86.ActiveCfg = Release|x86
{A302325B-D680-4C0E-8680-7AE283981624}.Release|x86.Build.0 = Release|x86
{A302325B-D680-4C0E-8680-7AE283981624}.Foo Bar|Any CPU.ActiveCfg = FooBar|Any CPU
{A302325B-D680-4C0E-8680-7AE283981624}.Foo Bar|Any CPU.Build.0 = FooBar|Any CPU
{A302325B-D680-4C0E-8680-7AE283981624}.Foo Bar|x64.ActiveCfg = FooBar|x64
{A302325B-D680-4C0E-8680-7AE283981624}.Foo Bar|x64.Build.0 = FooBar|x64
{A302325B-D680-4C0E-8680-7AE283981624}.Foo Bar|x86.ActiveCfg = FooBar|x86
{A302325B-D680-4C0E-8680-7AE283981624}.Foo Bar|x86.Build.0 = FooBar|x86
EndGlobalSection
EndGlobal
";
[Theory]
@ -788,6 +923,69 @@ EndGlobal
solutionFolderProjects.Count().Should().Be(1);
}
[Fact]
public void WhenProjectWithoutMatchingConfigurationsIsAddedSolutionMapsToFirstAvailable()
{
var slnDirectory = TestAssets
.Get("TestAppWithSlnAndProjectConfigs")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var slnFullPath = Path.Combine(slnDirectory, "App.sln");
var result = new DotnetCommand()
.WithWorkingDirectory(slnDirectory)
.ExecuteWithCapturedOutput($"sln add ProjectWithoutMatchingConfigs");
result.Should().Pass();
File.ReadAllText(slnFullPath)
.Should().BeVisuallyEquivalentTo(ExpectedSlnFileAfterAddingProjectWithoutMatchingConfigs);
}
[Fact]
public void WhenProjectWithMatchingConfigurationsIsAddedSolutionMapsAll()
{
var slnDirectory = TestAssets
.Get("TestAppWithSlnAndProjectConfigs")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var slnFullPath = Path.Combine(slnDirectory, "App.sln");
var result = new DotnetCommand()
.WithWorkingDirectory(slnDirectory)
.ExecuteWithCapturedOutput($"sln add ProjectWithMatchingConfigs");
result.Should().Pass();
File.ReadAllText(slnFullPath)
.Should().BeVisuallyEquivalentTo(ExpectedSlnFileAfterAddingProjectWithMatchingConfigs);
}
[Fact]
public void WhenProjectWithAdditionalConfigurationsIsAddedSolutionDoesNotMapThem()
{
var slnDirectory = TestAssets
.Get("TestAppWithSlnAndProjectConfigs")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var slnFullPath = Path.Combine(slnDirectory, "App.sln");
var result = new DotnetCommand()
.WithWorkingDirectory(slnDirectory)
.ExecuteWithCapturedOutput($"sln add ProjectWithAdditionalConfigs");
result.Should().Pass();
File.ReadAllText(slnFullPath)
.Should().BeVisuallyEquivalentTo(ExpectedSlnFileAfterAddingProjectWithAdditionalConfigs);
}
private string GetExpectedSlnContents(
string slnPath,
string slnTemplate,