diff --git a/.gitignore b/.gitignore
index b06878e45..926f18be7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -358,4 +358,6 @@ MigrationBackup/
**/*/launchSettings.json
**/*/FolderProfile.pubxml
-.tools/*
\ No newline at end of file
+.tools/*
+
+.vscode/
\ No newline at end of file
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index c188561ac..894909b4e 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -10,7 +10,7 @@
- 8.0
+ preview
enable
true
diff --git a/src/MSBuild.Abstractions/BaselineProject.cs b/src/MSBuild.Abstractions/BaselineProject.cs
index 1c7609ea5..87ddd0334 100644
--- a/src/MSBuild.Abstractions/BaselineProject.cs
+++ b/src/MSBuild.Abstractions/BaselineProject.cs
@@ -27,12 +27,12 @@ public BaselineProject(UnconfiguredProject project, ImmutableArray globa
private static string AdjustTargetTFM(ProjectStyle projectStyle, ProjectOutputType outputType, string candidateTargetTFM)
{
- if (candidateTargetTFM.ContainsIgnoreCase(MSBuildFacts.Net5) && projectStyle == ProjectStyle.WindowsDesktop)
+ if (candidateTargetTFM.ContainsIgnoreCase(MSBuildFacts.Net5) && projectStyle is ProjectStyle.WindowsDesktop)
{
return MSBuildFacts.Net5Windows;
}
- if (outputType == ProjectOutputType.Library)
+ if (projectStyle is not ProjectStyle.MSTest && outputType is ProjectOutputType.Library)
{
return MSBuildFacts.NetStandard20;
}
diff --git a/src/MSBuild.Abstractions/MSBuildConversionWorkspace.cs b/src/MSBuild.Abstractions/MSBuildConversionWorkspace.cs
index c379a6129..b6828baf5 100644
--- a/src/MSBuild.Abstractions/MSBuildConversionWorkspace.cs
+++ b/src/MSBuild.Abstractions/MSBuildConversionWorkspace.cs
@@ -3,6 +3,7 @@
using System.Collections.Immutable;
using System.IO;
using System.Linq;
+using System.Diagnostics.CodeAnalysis;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
@@ -36,24 +37,27 @@ public MSBuildConversionWorkspace(ImmutableArray paths, bool noBackup, s
var root = new MSBuildProjectRootElement(ProjectRootElement.Open(path, collection, preserveFormatting: true));
if (IsSupportedProjectType(root))
{
- if (!noBackup)
- {
- // Since git doesn't track the new '.old' addition in your changeset,
- // failing to overwrite will crash the tool if you have one in your directory.
- // This can be common if you're using the tool a few times and forget to delete the backup.
- File.Copy(path, path + ".old", overwrite: true);
- }
var configurations = DetermineConfigurations(root);
var unconfiguredProject = new UnconfiguredProject(configurations);
unconfiguredProject.LoadProjects(collection, globalProperties, path);
- var baseline = CreateSdkBaselineProject(path, unconfiguredProject.FirstConfiguredProject, root, configurations, tfm, keepCurrentTFMs);
- root.Reload(throwIfUnsavedChanges: false, preserveFormatting: true);
- var item = new MSBuildConversionWorkspaceItem(root, unconfiguredProject, baseline);
- items.Add(item);
+ if (TryCreateSdkBaselineProject(path, unconfiguredProject.FirstConfiguredProject, root, configurations, tfm, keepCurrentTFMs, out var baseline))
+ {
+ if (!noBackup)
+ {
+ // Since git doesn't track the new '.old' addition in your changeset,
+ // failing to overwrite will crash the tool if you have one in your directory.
+ // This can be common if you're using the tool a few times and forget to delete the backup.
+ File.Copy(path, path + ".old", overwrite: true);
+ }
+
+ root.Reload(throwIfUnsavedChanges: false, preserveFormatting: true);
+ var item = new MSBuildConversionWorkspaceItem(root, unconfiguredProject, baseline.Value);
+ items.Add(item);
+ }
}
}
@@ -79,7 +83,7 @@ public ImmutableDictionary> Determin
builder.Add(dimensionValuePair.Value, dimensionValueDictionary.ToImmutableDictionary());
}
}
- }
+ }
}
}
return builder.ToImmutable();
@@ -90,7 +94,7 @@ public ImmutableDictionary> Determin
/// We need to use the same name as the original csproj and same path so that all the default that derive
/// from name\path get the right values (there are a lot of them).
///
- private BaselineProject CreateSdkBaselineProject(string projectFilePath, IProject project, IProjectRootElement root, ImmutableDictionary> configurations, string tfm, bool keepCurrentTFMs)
+ private bool TryCreateSdkBaselineProject(string projectFilePath, IProject project, IProjectRootElement root, ImmutableDictionary> configurations, string tfm, bool keepCurrentTFMs, [NotNullWhen(true)] out BaselineProject? baselineProject)
{
var projectStyle = GetProjectStyle(root);
var outputType = GetProjectOutputType(root);
@@ -111,7 +115,8 @@ private BaselineProject CreateSdkBaselineProject(string projectFilePath, IProjec
: DesktopFacts.WinSDKAttribute; // pre-.NET 5 apps need a special SDK attribute.
break;
default:
- throw new NotSupportedException($"This project has custom imports in a manner that's not supported. '{projectFilePath}'");
+ baselineProject = null;
+ return false;
}
var propGroup = rootElement.AddPropertyGroup();
@@ -166,7 +171,8 @@ private BaselineProject CreateSdkBaselineProject(string projectFilePath, IProjec
? MSBuildFacts.Net5Windows
: tfm;
- return new BaselineProject(newProject, propertiesInTheBaseline, projectStyle, outputType, tfm, keepCurrentTFMs);
+ baselineProject = new BaselineProject(newProject, propertiesInTheBaseline, projectStyle, outputType, tfm, keepCurrentTFMs);
+ return true;
}
private bool IsSupportedOutputType(ProjectOutputType type) =>
@@ -231,21 +237,13 @@ private ProjectStyle GetProjectStyle(IProjectRootElement projectRootElement)
}
else
{
- var lastImport = imports.Last();
- var lastImportFileName = Path.GetFileName(lastImport.Project);
-
- if (firstImportFileName == FSharpFacts.FSharpTargetsPathVariableName)
- {
- firstImportFileName = Path.GetFileName(FSharpFacts.FSharpTargetsPath);
- }
-
- if (lastImportFileName == FSharpFacts.FSharpTargetsPathVariableName)
- {
- lastImportFileName = Path.GetFileName(FSharpFacts.FSharpTargetsPath);
- }
+ var cleansedImports = imports.Select(import => Path.GetFileName(import.Project));
+ var allImportsConvertibleToSdk =
+ cleansedImports.All(import =>
+ MSBuildFacts.PropsConvertibleToSDK.Contains(import, StringComparer.OrdinalIgnoreCase) ||
+ MSBuildFacts.TargetsConvertibleToSDK.Contains(import, StringComparer.OrdinalIgnoreCase));
- if (MSBuildFacts.PropsConvertibleToSDK.Contains(firstImportFileName, StringComparer.OrdinalIgnoreCase) &&
- MSBuildFacts.TargetsConvertibleToSDK.Contains(lastImportFileName, StringComparer.OrdinalIgnoreCase))
+ if (allImportsConvertibleToSdk)
{
if (MSBuildHelpers.IsNETFrameworkMSTestProject(projectRootElement))
{
@@ -262,6 +260,26 @@ private ProjectStyle GetProjectStyle(IProjectRootElement projectRootElement)
}
else
{
+ Console.WriteLine("This project has custom imports that are not accepted by try-convert.");
+ Console.WriteLine("Unexpected custom imports were found:");
+
+ var customImports =
+ cleansedImports.Where(import =>
+ !(MSBuildFacts.PropsConvertibleToSDK.Contains(import, StringComparer.OrdinalIgnoreCase) ||
+ MSBuildFacts.TargetsConvertibleToSDK.Contains(import, StringComparer.OrdinalIgnoreCase)));
+
+ foreach (var import in customImports)
+ {
+ Console.WriteLine($"\t{import}");
+ }
+
+ Console.WriteLine("The following imports are considered valid for conversion:");
+
+ foreach (var import in MSBuildFacts.TargetsConvertibleToSDK.Union(MSBuildFacts.PropsConvertibleToSDK))
+ {
+ Console.WriteLine($"\t{import}");
+ }
+
// It's something else, no idea what though
return ProjectStyle.Custom;
}
@@ -327,7 +345,7 @@ private bool IsSupportedProjectType(IProjectRootElement root)
{
return true;
}
- }
+ }
static void PrintGuidMessage(IEnumerable allSupportedProjectTypeGuids, IEnumerable allReadProjectTypeGuids)
{
diff --git a/src/MSBuild.Conversion.Facts/MSBuildFacts.cs b/src/MSBuild.Conversion.Facts/MSBuildFacts.cs
index 00428ad21..42d76244c 100644
--- a/src/MSBuild.Conversion.Facts/MSBuildFacts.cs
+++ b/src/MSBuild.Conversion.Facts/MSBuildFacts.cs
@@ -26,7 +26,8 @@ public static class MSBuildFacts
"Microsoft.Portable.CSharp.targets",
"Microsoft.Portable.VisualBasic.targets",
"Microsoft.FSharp.Targets",
- "MSTest.TestAdapter.targets"
+ "MSTest.TestAdapter.targets",
+ "Microsoft.TestTools.targets"
);
///
diff --git a/src/try-convert/Program.cs b/src/try-convert/Program.cs
index 129e94ccd..e66ea8e5c 100644
--- a/src/try-convert/Program.cs
+++ b/src/try-convert/Program.cs
@@ -101,6 +101,12 @@ public static int Run(string? project, string? workspace, string? msbuildPath, s
noBackup = noBackup || diffOnly;
var msbuildWorkspace = workspaceLoader.LoadWorkspace(workspacePath, noBackup, tfm, keepCurrentTfms);
+ if (msbuildWorkspace.WorkspaceItems.Length is 0)
+ {
+ Console.WriteLine("No projects converted.");
+ return 0;
+ }
+
foreach (var item in msbuildWorkspace.WorkspaceItems)
{
if (diffOnly)
diff --git a/tests/TestData/SmokeTests.LegacyMSTest/Properties/AssemblyInfo.cs b/tests/TestData/SmokeTests.LegacyMSTest/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..2a884ca4f
--- /dev/null
+++ b/tests/TestData/SmokeTests.LegacyMSTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("UnitTestProject1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("UnitTestProject1")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("a2de09a2-5c2a-4b5e-b700-9c14be5af757")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/TestData/SmokeTests.LegacyMSTest/SmokeTests.LegacyMSTest.csproj b/tests/TestData/SmokeTests.LegacyMSTest/SmokeTests.LegacyMSTest.csproj
new file mode 100644
index 000000000..14296b34e
--- /dev/null
+++ b/tests/TestData/SmokeTests.LegacyMSTest/SmokeTests.LegacyMSTest.csproj
@@ -0,0 +1,68 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {A2DE09A2-5C2A-4B5E-B700-9C14BE5AF757}
+ Library
+ Properties
+ UnitTestProject1
+ UnitTestProject1
+ v4.7.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ ..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/TestData/SmokeTests.LegacyMSTest/UnitTest1.cs b/tests/TestData/SmokeTests.LegacyMSTest/UnitTest1.cs
new file mode 100644
index 000000000..dbc289d34
--- /dev/null
+++ b/tests/TestData/SmokeTests.LegacyMSTest/UnitTest1.cs
@@ -0,0 +1,14 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTestProject1
+{
+ [TestClass]
+ public class UnitTest1
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ }
+ }
+}
diff --git a/tests/TestData/SmokeTests.LegacyMSTest/packages.config b/tests/TestData/SmokeTests.LegacyMSTest/packages.config
new file mode 100644
index 000000000..7079f8161
--- /dev/null
+++ b/tests/TestData/SmokeTests.LegacyMSTest/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/TestData/SmokeTests.MStestCoreBaseline/Properties/AssemblyInfo.cs b/tests/TestData/SmokeTests.MStestCoreBaseline/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..2a884ca4f
--- /dev/null
+++ b/tests/TestData/SmokeTests.MStestCoreBaseline/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("UnitTestProject1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("UnitTestProject1")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("a2de09a2-5c2a-4b5e-b700-9c14be5af757")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/TestData/SmokeTests.MStestCoreBaseline/SmokeTests.MSTestCoreBaseline.csproj b/tests/TestData/SmokeTests.MStestCoreBaseline/SmokeTests.MSTestCoreBaseline.csproj
new file mode 100644
index 000000000..79bdb6311
--- /dev/null
+++ b/tests/TestData/SmokeTests.MStestCoreBaseline/SmokeTests.MSTestCoreBaseline.csproj
@@ -0,0 +1,15 @@
+
+
+ UnitTestProject1
+ UnitTestProject1
+
+
+ netcoreapp3.1
+ false
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/TestData/SmokeTests.MStestCoreBaseline/UnitTest1.cs b/tests/TestData/SmokeTests.MStestCoreBaseline/UnitTest1.cs
new file mode 100644
index 000000000..dbc289d34
--- /dev/null
+++ b/tests/TestData/SmokeTests.MStestCoreBaseline/UnitTest1.cs
@@ -0,0 +1,14 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTestProject1
+{
+ [TestClass]
+ public class UnitTest1
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ }
+ }
+}
diff --git a/tests/TestData/SmokeTests.MStestCoreBaseline/packages.config b/tests/TestData/SmokeTests.MStestCoreBaseline/packages.config
new file mode 100644
index 000000000..7079f8161
--- /dev/null
+++ b/tests/TestData/SmokeTests.MStestCoreBaseline/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/end-to-end/Smoke.Tests/BasicConversions.cs b/tests/end-to-end/Smoke.Tests/BasicConversions.cs
index 4319d94a6..880278461 100644
--- a/tests/end-to-end/Smoke.Tests/BasicConversions.cs
+++ b/tests/end-to-end/Smoke.Tests/BasicConversions.cs
@@ -74,6 +74,14 @@ public void ConvertsWinformsFrameworkTemplateForNet50()
AssertConversionWorks(projectToConvertPath, projectBaselinePath, "net5.0-windows");
}
+ [Fact]
+ public void ConvertsLegacyMSTest()
+ {
+ var projectToConvertPath = GetCSharpProjectPath("SmokeTests.LegacyMSTest");
+ var projectBaselinePath = GetCSharpProjectPath("SmokeTests.MSTestCoreBaseline");
+ AssertConversionWorks(projectToConvertPath, projectBaselinePath, "netcoreapp3.1");
+ }
+
private void AssertConversionWorks(string projectToConvertPath, string projectBaselinePath, string targetTFM)
{
var (baselineRootElement, convertedRootElement) = GetRootElementsForComparison(projectToConvertPath, projectBaselinePath, targetTFM);