Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

Commit

Permalink
Merge pull request #335 from mjrousos/EnableWeb
Browse files Browse the repository at this point in the history
  • Loading branch information
cartermp authored Nov 26, 2020
2 parents 95b51e0 + 7975e2b commit ca9f5ae
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 18 deletions.
37 changes: 31 additions & 6 deletions src/MSBuild.Abstractions/MSBuildConversionWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class MSBuildConversionWorkspace
{
public ImmutableArray<MSBuildConversionWorkspaceItem> WorkspaceItems { get; }

public MSBuildConversionWorkspace(ImmutableArray<string> paths, bool noBackup, string tfm, bool keepCurrentTFMs)
public MSBuildConversionWorkspace(ImmutableArray<string> paths, bool noBackup, string tfm, bool keepCurrentTFMs, bool forceWeb)
{
var items = ImmutableArray.CreateBuilder<MSBuildConversionWorkspaceItem>();

Expand All @@ -35,7 +35,7 @@ public MSBuildConversionWorkspace(ImmutableArray<string> paths, bool noBackup, s
}

var root = new MSBuildProjectRootElement(ProjectRootElement.Open(path, collection, preserveFormatting: true));
if (IsSupportedProjectType(root))
if (IsSupportedProjectType(root, forceWeb))
{

var configurations = DetermineConfigurations(root);
Expand Down Expand Up @@ -114,15 +114,25 @@ private bool TryCreateSdkBaselineProject(string projectFilePath, IProject projec
? MSBuildFacts.DefaultSDKAttribute
: DesktopFacts.WinSDKAttribute; // pre-.NET 5 apps need a special SDK attribute.
break;
case ProjectStyle.Web:
rootElement.Sdk = WebFacts.WebSDKAttribute;
break;
default:
baselineProject = null;
return false;
}

var propGroup = rootElement.AddPropertyGroup();
propGroup.AddProperty(MSBuildFacts.TargetFrameworkNodeName, project.GetTargetFramework());
propGroup.AddProperty(MSBuildFacts.OutputTypeNodeName,
project.GetPropertyValue(MSBuildFacts.OutputTypeNodeName) ?? throw new InvalidOperationException($"OutputType is not set! '{projectFilePath}'"));

var outputTypeValue = outputType switch
{
ProjectOutputType.Exe => MSBuildFacts.ExeOutputType,
ProjectOutputType.Library => MSBuildFacts.LibraryOutputType,
ProjectOutputType.WinExe => MSBuildFacts.WinExeOutputType,
_ => project.GetPropertyValue(MSBuildFacts.OutputTypeNodeName)
};
propGroup.AddProperty(MSBuildFacts.OutputTypeNodeName, outputTypeValue ?? throw new InvalidOperationException($"OutputType is not set! '{projectFilePath}'"));

if (projectStyle == ProjectStyle.WindowsDesktop)
{
Expand Down Expand Up @@ -186,6 +196,15 @@ private bool IsSupportedOutputType(ProjectOutputType type) =>

private ProjectOutputType GetProjectOutputType(IProjectRootElement root)
{
if (root.Imports.Any(i => i.Project.Contains(WebFacts.WebApplicationTargets, StringComparison.OrdinalIgnoreCase)))
{
// ASP.NET Core apps use an EXE output type even though ASP.NET apps use Library
// Note that this specifically looks for WebApplicationTargets (rather than a System.Web reference) since
// ASP.NET libraries may reference System.Web and should still use a Library output types. Only ASP.NET
// apps should convert with Exe output type.
return ProjectOutputType.Exe;
}

var outputTypeNode = root.GetOutputTypeNode();
if (outputTypeNode is null)
{
Expand Down Expand Up @@ -253,6 +272,10 @@ private ProjectStyle GetProjectStyle(IProjectRootElement projectRootElement)
{
return ProjectStyle.WindowsDesktop;
}
else if (MSBuildHelpers.IsWeb(projectRootElement))
{
return ProjectStyle.Web;
}
else
{
return ProjectStyle.Default;
Expand Down Expand Up @@ -287,7 +310,7 @@ private ProjectStyle GetProjectStyle(IProjectRootElement projectRootElement)
}
}

private bool IsSupportedProjectType(IProjectRootElement root)
private bool IsSupportedProjectType(IProjectRootElement root, bool forceWeb)
{
if (root.Sdk.ContainsIgnoreCase(MSBuildFacts.DefaultSDKAttribute))
{
Expand Down Expand Up @@ -318,7 +341,9 @@ private bool IsSupportedProjectType(IProjectRootElement root)
{
case ProjectSupportType.LegacyWeb:
Console.WriteLine($"'{root.FullPath}' is a legacy web project and/or reference System.Web. Legacy Web projects and System.Web are unsupported on .NET Core. You will need to rewrite your application or find a way to not depend on System.Web to convert this project.");
return false;

// Proceed only if migrating web scenarios is explicitly enabled
return forceWeb;
case ProjectSupportType.CodedUITest:
Console.WriteLine($"'{root.FullPath}' is a coded UI test. Coded UI tests are deprecated and not convertable to .NET Core.");
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/MSBuild.Abstractions/MSBuildConversionWorkspaceLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public MSBuildConversionWorkspaceLoader(string workspacePath, MSBuildConversionW
_workspaceType = workspaceType;
}

public MSBuildConversionWorkspace LoadWorkspace(string path, bool noBackup, string tfm, bool keepCurrentTFMs)
public MSBuildConversionWorkspace LoadWorkspace(string path, bool noBackup, string tfm, bool keepCurrentTFMs, bool forceWeb)
{
var projectPaths =
_workspaceType switch
Expand All @@ -42,7 +42,7 @@ public MSBuildConversionWorkspace LoadWorkspace(string path, bool noBackup, stri
_ => throw new InvalidOperationException("Somehow, an enum that isn't possible was passed in here.")
};

return new MSBuildConversionWorkspace(projectPaths, noBackup, tfm, keepCurrentTFMs);
return new MSBuildConversionWorkspace(projectPaths, noBackup, tfm, keepCurrentTFMs, forceWeb);

static bool IsSupportedSolutionItemType(ProjectInSolution project)
{
Expand Down
16 changes: 16 additions & 0 deletions src/MSBuild.Abstractions/MSBuildHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,22 @@ public static bool IsDesktop(IProjectRootElement projectRoot)
}
}

/// <summary>
/// Determines if a given project references ASP.NET assemblies.
/// </summary>
public static bool IsWeb(IProjectRootElement projectRoot)
{
var references = projectRoot.ItemGroups.SelectMany(GetReferences)?.Select(elem => elem.Include.Split(',').First());
if (references is null)
{
return false;
}
else
{
return WebFacts.KnownWebReferences.All(reference => references.Contains(reference, StringComparer.OrdinalIgnoreCase));
}
}

/// <summary>
/// Determines if a project is a .NET Framework MSTest project by looking at its references.
/// </summary>
Expand Down
8 changes: 7 additions & 1 deletion src/MSBuild.Abstractions/MSBuildProjectRootElement.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

using Microsoft.Build.Construction;
Expand All @@ -19,6 +21,7 @@ public interface IProjectRootElement
ICollection<ProjectPropertyGroupElement> PropertyGroups { get; }
ICollection<ProjectItemGroupElement> ItemGroups { get; }
ICollection<ProjectTargetElement> Targets { get; }
ProjectExtensionsElement? ProjectExtensions { get; }

ProjectPropertyElement CreatePropertyElement(string propertyName);
ProjectPropertyGroupElement AddPropertyGroup();
Expand All @@ -31,6 +34,8 @@ public interface IProjectRootElement

public class MSBuildProjectRootElement : IProjectRootElement
{
private const string ProjectExtensionsElementName = "ProjectExtensions";

private readonly ProjectRootElement _rootElement;

public MSBuildProjectRootElement(ProjectRootElement rootElement)
Expand All @@ -50,6 +55,7 @@ public MSBuildProjectRootElement(ProjectRootElement rootElement)
public ICollection<ProjectPropertyGroupElement> PropertyGroups => _rootElement.PropertyGroups;
public ICollection<ProjectItemGroupElement> ItemGroups => _rootElement.ItemGroups;
public ICollection<ProjectTargetElement> Targets => _rootElement.Targets;
public ProjectExtensionsElement? ProjectExtensions => _rootElement.Children.FirstOrDefault(e => e.ElementName.Equals(ProjectExtensionsElementName, StringComparison.OrdinalIgnoreCase)) as ProjectExtensionsElement;

public ProjectItemGroupElement AddItemGroup() => _rootElement.AddItemGroup();

Expand Down
9 changes: 8 additions & 1 deletion src/MSBuild.Abstractions/ProjectStyle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public enum ProjectStyle
/// <summary>
/// The project is an MSTest project that pulls in a lot of unnecessary imports.
/// </summary>
MSTest
MSTest,

/// <summary>
/// The project is an ASP.NET project that will not be possible to completely convert automatically.
/// If the user has specified --force-web-conversion, a best-effort will be made using the
/// Microsoft.NET.Sdk.Web SDK.
/// </summary>
Web
}
}
3 changes: 1 addition & 2 deletions src/MSBuild.Conversion.Facts/DesktopFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public static class DesktopFacts
/// </remarks>
public static ImmutableArray<string> KnownWinFormsReferences => ImmutableArray.Create(
"System.Windows.Forms",
"System.Deployment",
"System.Drawing"
"System.Deployment"
);

public static ImmutableArray<string> KnownDesktopReferences => ImmutableArray.Create(
Expand Down
65 changes: 62 additions & 3 deletions src/MSBuild.Conversion.Facts/MSBuildFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public static class MSBuildFacts
"Microsoft.Portable.VisualBasic.targets",
"Microsoft.FSharp.Targets",
"MSTest.TestAdapter.targets",
"Microsoft.TestTools.targets"
"Microsoft.TestTools.targets",
"Microsoft.WebApplication.targets"
);

/// <summary>
Expand Down Expand Up @@ -58,6 +59,7 @@ public static class MSBuildFacts

public static ImmutableArray<string> UnnecessaryProperties => ImmutableArray.Create(
// The following are unecessary in CPS and/or are already in the .NET SDK
"OldToolsVersion",
"ProjectGuid",
"ProjectTypeGuids",
"TargetFrameworkIdentifier",
Expand All @@ -78,7 +80,22 @@ public static class MSBuildFacts
"VSToolsPath",

// This is dropped in by templates, but is unlikely to be valid given that the .NET SDK specifies a minimum VS version that will work
"VisualStudioVersion"
"VisualStudioVersion",

// ASP.NET Core always defaults to building views
"MvcBuildViews",

// ASP.NET Core does not configure IIS settings in the project file
"UseIISExpress",
"Use64BitIISExpress",
"IISExpressSSLPort",
"IISExpressAnonymousAuthentication",
"IISExpressWindowsAuthentication",
"IISExpressUseClassicPipelineMode",

// No longer used in ASP.NET Core
"MvcProjectUpgradeChecked",
"UseGlobalApplicationHostFile"
);

public static ImmutableArray<string> DefaultDefineConstants => ImmutableArray.Create(
Expand Down Expand Up @@ -114,8 +131,50 @@ public static class MSBuildFacts
// packages.config is now deprecated, user needs to move to PackageReference
"packages.config",

// Enterprise Services is no longer supported
"System.EnterpriseServices",

// System.Net.Http is a part of the .NET SDK now
"System.Net.Http"
"System.Net.Http",

// ASP.NET references are no longer used
"System.Web",
"System.Web.Abstractions",
"System.Web.ApplicationServices",
"System.Web.DataVisualization",
"System.Web.DynamicData",
"System.Web.Entity",
"System.Web.Extensions",
"System.Web.Extensions.Design",
"System.Web.Helpers",
"System.Web.Mobile",
"System.Web.Mvc",
"System.Web.Optimization",
"System.Web.Razor",
"System.Web.Routing",
"System.Web.Services",
"System.Web.WebPages",
"System.Web.WebPages.Deployment",
"System.Web.WebPages.Razor",
"System.Net.Http.WebRequest",
"Microsoft.AspNet.Mvc",
"Microsoft.AspNet.Razor",
"Microsoft.AspNet.Identity.Core",
"Microsoft.AspNet.Identity.EntityFramework",
"Microsoft.AspNet.Identity.Owin",
"Microsoft.AspNet.Web.Optimization",
"Microsoft.AspNet.WebApi.Core",
"Microsoft.AspNet.WebApi.WebHost",
"Microsoft.AspNet.WebPages",
"Microsoft.CodeDom.Providers.DotNetCompilerPlatform",
"Microsoft.Owin",
"Microsoft.Owin.Host.SystemWeb",
"Microsoft.Owin.Security",
"Microsoft.Owin.Security.Cookies",
"Microsoft.Owin.Security.OAuth",
"Microsoft.Owin.Security.OpenIdConnect",
"Microsoft.Web.Infrastructure",
"Owin"
);

public static ImmutableDictionary<string, string> DefaultItemsThatHavePackageEquivalents => ImmutableDictionary.CreateRange(new Dictionary<string, string>
Expand Down
19 changes: 19 additions & 0 deletions src/MSBuild.Conversion.Facts/WebFacts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Immutable;

namespace MSBuild.Conversion.Facts
{
public static class WebFacts
{
public const string WebSDKAttribute = "Microsoft.NET.Sdk.Web";
public const string MvcBuildViewsName = "MvcBuildViews";
public const string WebProjectPropertiesName = "WebProjectProperties";
public const string WebApplicationTargets = "Microsoft.WebApplication.targets";

/// <summary>
/// The core set of references all ASP.NET projects use.
/// </summary>
public static ImmutableArray<string> KnownWebReferences => ImmutableArray.Create(
"System.Web"
);
}
}
2 changes: 2 additions & 0 deletions src/MSBuild.Conversion.Project/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public void Convert(string outputPath)

// Now we can convert the project over
.ChangeImportsAndAddSdkAttribute(_sdkBaselineProject)
.UpdateOutputTypeProperty(_sdkBaselineProject)
.RemoveDefaultedProperties(_sdkBaselineProject, _differs)
.RemoveUnnecessaryPropertiesNotInSDKByDefault(_sdkBaselineProject.ProjectStyle)
.AddTargetFrameworkProperty(_sdkBaselineProject, _sdkBaselineProject.TargetTFM)
Expand All @@ -46,6 +47,7 @@ public void Convert(string outputPath)
.RemoveOrUpdateItems(_differs, _sdkBaselineProject, _sdkBaselineProject.TargetTFM)
.AddItemRemovesForIntroducedItems(_differs)
.RemoveUnnecessaryTargetsIfTheyExist()
.RemoveWebExtensions(_sdkBaselineProject.ProjectStyle)
.ModifyProjectElement();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public static IProjectRootElement ChangeImportsAndAddSdkAttribute(this IProjectR
{
projectRootElement.Sdk = DesktopFacts.WinSDKAttribute;
}
else if (MSBuildHelpers.IsWeb(projectRootElement))
{
projectRootElement.Sdk = WebFacts.WebSDKAttribute;
}
else
{
projectRootElement.Sdk = MSBuildFacts.DefaultSDKAttribute;
Expand All @@ -31,6 +35,22 @@ public static IProjectRootElement ChangeImportsAndAddSdkAttribute(this IProjectR
return projectRootElement;
}

public static IProjectRootElement UpdateOutputTypeProperty(this IProjectRootElement projectRootElement, BaselineProject baselineProject)
{
var outputTypeNode = projectRootElement.GetOutputTypeNode();
if (outputTypeNode != null)
{
outputTypeNode.Value = baselineProject.OutputType switch
{
ProjectOutputType.Exe => MSBuildFacts.ExeOutputType,
ProjectOutputType.Library => MSBuildFacts.LibraryOutputType,
ProjectOutputType.WinExe => MSBuildFacts.WinExeOutputType,
_ => throw new InvalidOperationException("Unsupported output type: " + baselineProject.OutputType)
};
}
return projectRootElement;
}

public static IProjectRootElement RemoveDefaultedProperties(this IProjectRootElement projectRootElement, BaselineProject baselineProject, ImmutableDictionary<string, Differ> differs)
{
foreach (var propGroup in projectRootElement.PropertyGroups)
Expand Down Expand Up @@ -279,6 +299,12 @@ public static IProjectRootElement RemoveUnnecessaryTargetsIfTheyExist(this IProj
{
projectRootElement.RemoveChild(target);
}

// ASP.NET target for building views which is no longer needed in ASP.NET Core.
if (target.Name.Equals(WebFacts.MvcBuildViewsName, StringComparison.OrdinalIgnoreCase))
{
projectRootElement.RemoveChild(target);
}
}

return projectRootElement;
Expand Down Expand Up @@ -458,5 +484,20 @@ public static IProjectRootElement AddTargetFrameworkProperty(this IProjectRootEl
propGroup.PrependChild(targetFrameworkElement);
return projectRootElement;
}

public static IProjectRootElement RemoveWebExtensions(this IProjectRootElement projectRootElement, ProjectStyle projectStyle)
{
// ASP.NET apps often contain project extensions that aren't used in ASP.NET Core
if (projectStyle == ProjectStyle.Web)
{
var extensions = projectRootElement.ProjectExtensions;
if (extensions != null)
{
projectRootElement.RemoveChild(extensions);
}
}

return projectRootElement;
}
}
}
Loading

0 comments on commit ca9f5ae

Please sign in to comment.