diff --git a/src/extensions/try-convert/MSBuild.Abstractions/MSBuildConversionWorkspace.cs b/src/extensions/try-convert/MSBuild.Abstractions/MSBuildConversionWorkspace.cs index 0cb39601f..c976e6a5b 100644 --- a/src/extensions/try-convert/MSBuild.Abstractions/MSBuildConversionWorkspace.cs +++ b/src/extensions/try-convert/MSBuild.Abstractions/MSBuildConversionWorkspace.cs @@ -249,6 +249,7 @@ private bool IsSupportedOutputType(ProjectOutputType type) => ProjectOutputType.Library => true, ProjectOutputType.WinExe => true, ProjectOutputType.AppContainerExe => true, + ProjectOutputType.WinMdObj => true, _ => false }; @@ -295,6 +296,10 @@ private ProjectOutputType GetProjectOutputType(IProjectRootElement root, Project { return ProjectOutputType.AppContainerExe; } + else if (ProjectPropertyHelpers.IsWinMdObjProjectType(outputTypeNode)) + { + return ProjectOutputType.WinMdObj; + } else { return ProjectOutputType.Other; diff --git a/src/extensions/try-convert/MSBuild.Abstractions/ProjectOutputType.cs b/src/extensions/try-convert/MSBuild.Abstractions/ProjectOutputType.cs index 795d1db67..2d72d2bbe 100644 --- a/src/extensions/try-convert/MSBuild.Abstractions/ProjectOutputType.cs +++ b/src/extensions/try-convert/MSBuild.Abstractions/ProjectOutputType.cs @@ -16,6 +16,7 @@ public enum ProjectOutputType Exe, WinExe, AppContainerExe, + WinMdObj, Other, None } diff --git a/src/extensions/try-convert/MSBuild.Abstractions/ProjectPropertyHelpers.cs b/src/extensions/try-convert/MSBuild.Abstractions/ProjectPropertyHelpers.cs index 5c049641d..600ed3853 100644 --- a/src/extensions/try-convert/MSBuild.Abstractions/ProjectPropertyHelpers.cs +++ b/src/extensions/try-convert/MSBuild.Abstractions/ProjectPropertyHelpers.cs @@ -188,6 +188,13 @@ public static bool IsWinExeOutputType(ProjectPropertyElement prop) => prop.ElementName.Equals(MSBuildFacts.OutputTypeNodeName, StringComparison.OrdinalIgnoreCase) && prop.Value.Equals(MSBuildFacts.WinExeOutputType, StringComparison.OrdinalIgnoreCase); + /// + /// Checks if an OutputType node is WinMdObj. + /// + public static bool IsWinMdObjProjectType(ProjectPropertyElement prop) => + prop.ElementName.Equals(MSBuildFacts.OutputTypeNodeName, StringComparison.OrdinalIgnoreCase) + && prop.Value.Equals(MSBuildFacts.WinMdObjOutputType, StringComparison.OrdinalIgnoreCase); + public static bool IsVisualBasicProject(ProjectPropertyElement prop) => IsProjectTypeGuidsNode(prop) && prop.Value.Split(';').Any(guidString => Guid.Parse(guidString) == MSBuildFacts.LanguageProjectTypeVisualBasic); diff --git a/src/extensions/try-convert/MSBuild.Conversion.Facts/MSBuildFacts.cs b/src/extensions/try-convert/MSBuild.Conversion.Facts/MSBuildFacts.cs index ffd10c0a2..22042b255 100644 --- a/src/extensions/try-convert/MSBuild.Conversion.Facts/MSBuildFacts.cs +++ b/src/extensions/try-convert/MSBuild.Conversion.Facts/MSBuildFacts.cs @@ -288,6 +288,7 @@ public static class MSBuildFacts public const string ExeOutputType = "Exe"; public const string WinExeOutputType = "WinExe"; public const string AppContainerExeOutputType = "AppContainerExe"; + public const string WinMdObjOutputType = "winmdobj"; public const string NuGetPackageImportStampNodeName = "NuGetPackageImportStamp"; public const string ReferencePathNodeName = "ReferencePath"; public const string LegacyTargetFrameworkPropertyNodeName = "TargetFrameworkIdentifier"; @@ -311,5 +312,7 @@ public static class MSBuildFacts public const string TargetPlatformIdentifierNodeName = "TargetPlatformIdentifier"; public const string UapValue = "UAP"; public const string TargetPlatformVersionNodeName = "TargetPlatformVersion"; + public const string CsWinRTComponentName = "CsWinRTComponent"; + public static readonly (string Name, string Version) CsWinRTPackageReference = (Name: "Microsoft.Windows.CsWinRT", Version: "1.6.4"); } } diff --git a/src/extensions/try-convert/MSBuild.Conversion.Project/Converter.cs b/src/extensions/try-convert/MSBuild.Conversion.Project/Converter.cs index c6b18b00d..b98dae548 100644 --- a/src/extensions/try-convert/MSBuild.Conversion.Project/Converter.cs +++ b/src/extensions/try-convert/MSBuild.Conversion.Project/Converter.cs @@ -46,6 +46,7 @@ public void Convert(string outputPath) // Now we can convert the project over .ChangeImportsAndAddSdkAttribute(_sdkBaselineProject, _forceRemoveCustomImports) + .AddCsWinRTReferenceAndComponentProperty(_sdkBaselineProject) .UpdateOutputTypeProperty(_sdkBaselineProject) .RemoveDefaultedProperties(_sdkBaselineProject, _differs) .RemoveUnnecessaryPropertiesNotInSDKByDefault(_sdkBaselineProject.ProjectStyle) diff --git a/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs b/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs index 56987f280..5b04ea63f 100644 --- a/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs +++ b/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs @@ -68,6 +68,21 @@ public static IProjectRootElement ChangeImportsAndAddSdkAttribute(this IProjectR return projectRootElement; } + public static IProjectRootElement AddCsWinRTReferenceAndComponentProperty(this IProjectRootElement projectRootElement, BaselineProject baselineProject) + { + if (baselineProject.OutputType == ProjectOutputType.WinMdObj) + { + var topLevelPropGroup = MSBuildHelpers.GetOrCreateTopLevelPropertyGroup(baselineProject, projectRootElement); + topLevelPropGroup.AddProperty(MSBuildFacts.CsWinRTComponentName, "true"); + + var packageReferenceItemGroup = projectRootElement.ItemGroups.Where(ig => ig.Items.Any(i => i.ItemType == MSBuildFacts.MSBuildPackageReferenceName)) + .FirstOrDefault() ?? projectRootElement.AddItemGroup(); + AddPackageReferenceElement(packageReferenceItemGroup, MSBuildFacts.CsWinRTPackageReference.Name, MSBuildFacts.CsWinRTPackageReference.Version); + } + + return projectRootElement; + } + public static IProjectRootElement UpdateOutputTypeProperty(this IProjectRootElement projectRootElement, BaselineProject baselineProject) { var outputTypeNode = projectRootElement.GetOutputTypeNode(); @@ -79,6 +94,7 @@ public static IProjectRootElement UpdateOutputTypeProperty(this IProjectRootElem ProjectOutputType.Library => MSBuildFacts.LibraryOutputType, ProjectOutputType.WinExe => MSBuildFacts.WinExeOutputType, ProjectOutputType.AppContainerExe => MSBuildFacts.WinExeOutputType, + ProjectOutputType.WinMdObj => MSBuildFacts.LibraryOutputType, _ => throw new InvalidOperationException("Unsupported output type: " + baselineProject.OutputType) }; } diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/Utils/IProjectExtensions.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/Utils/IProjectExtensions.cs new file mode 100644 index 000000000..c9e0a6259 --- /dev/null +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/Utils/IProjectExtensions.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils +{ + public static class IProjectExtensions + { + public static IEnumerable AllProjectReferences(this IProject project) => project.GetRoslynProject().AllProjectReferences.Select(projRef => projRef.ProjectId.ToString()); + } +} diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/UWPToWinUIHelpers.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/Utils/SyntaxNodeExtensions.cs similarity index 92% rename from src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/UWPToWinUIHelpers.cs rename to src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/Utils/SyntaxNodeExtensions.cs index 7bdf103d6..f7bfa1c16 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/UWPToWinUIHelpers.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/Utils/SyntaxNodeExtensions.cs @@ -9,9 +9,9 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils { - internal static class UWPToWinUIHelpers + internal static class SyntaxNodeExtensions { public static IEnumerable GetAllImportedNamespaces(this SyntaxNode node) { diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIAppWIndowAnalyzer.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIAppWIndowAnalyzer.cs index ae4409be9..72ca770ca 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIAppWIndowAnalyzer.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIAppWIndowAnalyzer.cs @@ -10,8 +10,8 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade; using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Abstractions; +using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils; namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows { diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIInteropAnalyzer.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIInteropAnalyzer.cs index 4d1924067..3ddcc4eb9 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIInteropAnalyzer.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIInteropAnalyzer.cs @@ -10,7 +10,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade; +using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils; namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows { diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIMRTResourceManagerAnalyzer.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIMRTResourceManagerAnalyzer.cs index ed93572f2..a07b58dc0 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIMRTResourceManagerAnalyzer.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIMRTResourceManagerAnalyzer.cs @@ -10,7 +10,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade; +using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils; namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows { diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIPropertiesUpdater.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIPropertiesUpdater.cs index 693e4bb67..9852d6c18 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIPropertiesUpdater.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIPropertiesUpdater.cs @@ -4,10 +4,13 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Globalization; using System.IO; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -18,6 +21,12 @@ public class WinUIPropertiesUpdater : IUpdater { public const string RuleID = "UA302"; + private const string CsWinRTLogMessageFormat = "A CsWinRTIncludes property with value {0} has been added to specify the namespace of the referenced vcxproj component to project..\n" + + "If your project assembly name differs from {0}, update this value with the assembly name.\n" + + "Read more about C#/WinRT here: https://docs.microsoft.com/en-us/windows/apps/develop/platform/csharp-winrt/"; + + private const string CsWinRTIncludesProperty = "CsWinRTIncludes"; + public string Id => typeof(WinUIPropertiesUpdater).FullName; public string Title => "Update WinUI Project Properties"; @@ -69,6 +78,19 @@ public async Task ApplyAsync(IUpgradeContext context, ImmutableA } } + foreach (var projRef in project.AllProjectReferences()) + { + if (projRef.Contains(".vcxproj")) + { + var projectName = ParseProjectNameWithExtension(projRef, ".vcxproj"); + var csWinRTIncludesValue = projectFile.GetPropertyValue(CsWinRTIncludesProperty) ?? string.Empty; + var delimiter = csWinRTIncludesValue.Trim().Length == 0 || csWinRTIncludesValue.EndsWith(";") ? string.Empty : ";"; + projectFile.SetPropertyValue(CsWinRTIncludesProperty, $"{csWinRTIncludesValue}{delimiter}{projectName}"); + + _logger.LogInformation(string.Format(CsWinRTLogMessageFormat, projectName)); + } + } + projectFile.AddItem(new ProjectItemDescriptor(ProjectItemType.Compile) { Remove = "App.xaml.old.cs" }); projectFile.AddItem(new ProjectItemDescriptor(ProjectItemType.None) { Include = "App.xaml.old.cs" }); projectFile.RemoveItem(new ProjectItemDescriptor(ProjectItemType.Content) { Include = "Properties\\Default.rd.xml" }); @@ -85,6 +107,25 @@ public async Task ApplyAsync(IUpgradeContext context, ImmutableA new List()); } + /* + This function parses the project name from projectReference string which includes the file path, project id and more text. + It does so by finding the position of ".vcxproj" in the string and then reading the name backwards character by character + until the first non-alphanumeric character. + */ + private string ParseProjectNameWithExtension(string projectReference, string extension) + { + var index = projectReference.IndexOf(".vcxproj"); + index--; + StringBuilder sb = new StringBuilder(); + while (index > -1 && char.IsLetterOrDigit(projectReference[index])) + { + sb.Append(projectReference[index]); + index--; + } + + return new string(sb.ToString().Reverse().ToArray()); + } + public async Task IsApplicableAsync(IUpgradeContext context, ImmutableArray inputs, CancellationToken token) { await Task.Yield(); diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIReferenceAnalyzer.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIReferenceAnalyzer.cs index dcbe761c1..ae96d0a46 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIReferenceAnalyzer.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/UWPtoWinAppSDKUpgrade/WinUIReferenceAnalyzer.cs @@ -6,9 +6,11 @@ using System.Collections.Immutable; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.DotNet.UpgradeAssistant.Dependencies; +using Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade.Utils; using Microsoft.Extensions.Logging; namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.UWPtoWinAppSDKUpgrade @@ -18,6 +20,9 @@ internal class WinUIReferenceAnalyzer : IDependencyAnalyzer { public string Name => "Windows App SDK package analysis"; + private const string CsWinRTPackageName = "Microsoft.Windows.CsWinRT"; + private const string CsWinRTVersion = "1.6.4"; + private readonly IPackageLoader _packageLoader; private readonly ILogger _logger; @@ -34,6 +39,13 @@ public async Task AnalyzeAsync(IProject project, IDependencyAnalysisState state, return; } + if (project.AllProjectReferences().Any(id => id.Contains(".vcxproj"))) + { + var newPackage = new NuGetReference(CsWinRTPackageName, CsWinRTVersion); + state.Packages.Add(newPackage, + new OperationDetails() { Risk = BuildBreakRisk.Medium, Details = ImmutableList.Create(newPackage.Name) }); + } + foreach (var package in state.Packages) { if (package.Name.StartsWith("Microsoft.Toolkit", StringComparison.Ordinal))