public static IProjectRootElement RemoveDefaultedProperties(this IProjectRootElement projectRootElement, BaselineProject baselineProject, ImmutableDictionary <string, Differ> differs) { foreach (var propGroup in projectRootElement.PropertyGroups) { var configurationName = MSBuildHelpers.GetConfigurationName(propGroup.Condition); var propDiff = differs[configurationName].GetPropertiesDiff(); foreach (var prop in propGroup.Properties) { // These properties were added to the baseline - so don't treat them as defaulted properties. if (baselineProject.GlobalProperties.Contains(prop.Name, StringComparer.OrdinalIgnoreCase)) { continue; } if (propDiff.DefaultedProperties.Select(p => p.Name).Contains(prop.Name, StringComparer.OrdinalIgnoreCase)) { propGroup.RemoveChild(prop); } } if (propGroup.Properties.Count == 0) { projectRootElement.RemoveChild(propGroup); } } return(projectRootElement); }
public Converter(UnconfiguredProject project, BaselineProject sdkBaselineProject, IProjectRootElement projectRootElement) { _project = project ?? throw new ArgumentNullException(nameof(project)); _sdkBaselineProject = sdkBaselineProject; _projectRootElement = projectRootElement ?? throw new ArgumentNullException(nameof(projectRootElement)); _differs = _project.ConfiguredProjects.Select(p => (p.Key, new Differ(p.Value, _sdkBaselineProject.Project.ConfiguredProjects[p.Key]))).ToImmutableDictionary(kvp => kvp.Item1, kvp => kvp.Item2); }
private void AssertItemsEqual(IProjectRootElement baselineRootElement, IProjectRootElement convertedRootElement) { Assert.Equal(baselineRootElement.Sdk, convertedRootElement.Sdk); Assert.Equal(baselineRootElement.ItemGroups.Count, convertedRootElement.ItemGroups.Count); var baselineItemGroups = new List <ProjectItemGroupElement>(baselineRootElement.ItemGroups); var convertedItemGroups = new List <ProjectItemGroupElement>(convertedRootElement.ItemGroups); if (baselineItemGroups.Count > 0) { for (var i = 0; i < baselineItemGroups.Count; i++) { var baselineItems = new List <ProjectItemElement>(baselineItemGroups[i].Items); var convertedItems = new List <ProjectItemElement>(convertedItemGroups[i].Items); Assert.Equal(baselineItems.Count, convertedItems.Count); if (baselineItems.Count > 1) { for (var j = 0; j < baselineItems.Count; j++) { var baselineItem = baselineItems[j]; var convertedItem = convertedItems[j]; Assert.Equal(baselineItem.Include, convertedItem.Include); Assert.Equal(baselineItem.Update, convertedItem.Update); } } } } }
private void AssertPropsEqual(IProjectRootElement baselineRootElement, IProjectRootElement convertedRootElement) { Assert.Equal(baselineRootElement.Sdk, convertedRootElement.Sdk); Assert.Equal(baselineRootElement.PropertyGroups.Count, convertedRootElement.PropertyGroups.Count); var baselinePropGroups = new List <ProjectPropertyGroupElement>(baselineRootElement.PropertyGroups); var convertedPropGroups = new List <ProjectPropertyGroupElement>(convertedRootElement.PropertyGroups); if (baselinePropGroups.Count > 0) { for (var i = 0; i < baselinePropGroups.Count; i++) { var baselineProps = new List <ProjectPropertyElement>(baselinePropGroups[i].Properties); var convertedProps = new List <ProjectPropertyElement>(convertedPropGroups[i].Properties); Assert.Equal(baselineProps.Count, convertedProps.Count); if (baselineProps.Count > 0) { for (var j = 0; j < baselineProps.Count; j++) { var baselineProp = baselineProps[j]; var convertedProp = convertedProps[j]; Assert.Equal(baselineProp.Name, convertedProp.Name); Assert.Equal(baselineProp.Value, convertedProp.Value); } } } } }
public Converter(UnconfiguredProject project, BaselineProject sdkBaselineProject, IProjectRootElement projectRootElement) { _project = project ?? throw new ArgumentNullException(nameof(project)); _sdkBaselineProject = sdkBaselineProject; _projectRootElement = projectRootElement ?? throw new ArgumentNullException(nameof(projectRootElement)); _differs = GetDiffers(); }
public static ProjectStyle GetProjectStyle(IProjectRootElement project) { if (project.ImportGroups.Any()) { return(ProjectStyle.Custom); } // Exclude shared project references since they show up as imports. var imports = project.Imports.Where(i => i.Label != Facts.SharedProjectsImportLabel); if (imports.Count() == 2) { var firstImport = project.Imports.First(); var lastImport = project.Imports.Last(); var firstImportFileName = Path.GetFileName(firstImport.Project); var lastImportFileName = Path.GetFileName(lastImport.Project); if (Facts.PropsConvertibleToSDK.Contains(firstImportFileName, StringComparer.OrdinalIgnoreCase) && Facts.TargetsConvertibleToSDK.Contains(lastImportFileName, StringComparer.OrdinalIgnoreCase)) { return(ProjectStyle.Default); } } return(ProjectStyle.DefaultWithCustomTargets); }
public static IProjectRootElement ChangeImportsAndAddSdkAttribute(this IProjectRootElement projectRootElement, BaselineProject baselineProject) { foreach (var import in projectRootElement.Imports) { var fileName = Path.GetFileName(import.Project); if (MSBuildFacts.PropsToRemove.Contains(fileName, StringComparer.OrdinalIgnoreCase) || MSBuildFacts.TargetsToRemove.Contains(fileName, StringComparer.OrdinalIgnoreCase)) { projectRootElement.RemoveChild(import); } else if (!MSBuildFacts.ImportsToKeep.Contains(fileName, StringComparer.OrdinalIgnoreCase)) { Console.WriteLine($"This project has an unrecognized custom import which may need reviewed after conversion: {fileName}"); } } if (baselineProject.ProjectStyle is ProjectStyle.WindowsDesktop && baselineProject.TargetTFM is MSBuildFacts.NetCoreApp31) { projectRootElement.Sdk = DesktopFacts.WinSDKAttribute; } else if (MSBuildHelpers.IsAspNetCore(projectRootElement, baselineProject.TargetTFM)) { // Libraries targeting .NET Framework can use the default SDK and still be used by NetFx callers. // However, web apps (as opposed to libraries) or libraries that are targeting .NET Core/.NET should use the web SDK. projectRootElement.Sdk = WebFacts.WebSDKAttribute; } else { projectRootElement.Sdk = MSBuildFacts.DefaultSDKAttribute; } return(projectRootElement); }
public void LoadProjects(Options options) { string projectFilePath = Path.GetFullPath(options.ProjectFilePath); if (!File.Exists(projectFilePath)) { Console.Error.WriteLine($"The project file '{projectFilePath}' does not exist or is inaccessible."); return; } ImmutableDictionary <string, string> globalProperties = InitializeGlobalProperties(options); var collection = new ProjectCollection(globalProperties); ProjectRootElement = new MSBuildProjectRootElement(Microsoft.Build.Construction.ProjectRootElement.Open(projectFilePath, collection, preserveFormatting: true)); var configurations = DetermineConfigurations(ProjectRootElement); Project = new UnconfiguredProject(configurations); Project.LoadProjects(collection, globalProperties, projectFilePath); Console.WriteLine($"Successfully loaded project file '{projectFilePath}'."); var targetProjectProperties = options.TargetProjectProperties.ToImmutableDictionary(p => p.Split('=')[0], p => p.Split('=')[1]); SdkBaselineProject = CreateSdkBaselineProject(projectFilePath, Project.FirstConfiguredProject, globalProperties, configurations, targetProjectProperties); ProjectRootElement.Reload(throwIfUnsavedChanges: false, preserveFormatting: true); Console.WriteLine($"Successfully loaded sdk baseline of project."); }
private void AssertItemsEqual(IProjectRootElement baselineRootElement, IProjectRootElement convertedRootElement) { Assert.Equal(baselineRootElement.Sdk, convertedRootElement.Sdk); Assert.Equal(baselineRootElement.ItemGroups.Count, convertedRootElement.ItemGroups.Count); var baselineItemGroups = new List <ProjectItemGroupElement>(baselineRootElement.ItemGroups); var convertedItemGroups = new List <ProjectItemGroupElement>(convertedRootElement.ItemGroups); if (baselineItemGroups.Count > 0) { for (var i = 0; i < baselineItemGroups.Count; i++) { var baselineItems = new List <ProjectItemElement>(baselineItemGroups[i].Items); var convertedItems = new List <ProjectItemElement>(convertedItemGroups[i].Items); // TODO: this was regressed at some point // converted items will now have additional items // Assert.Equal(baselineItems.Count, convertedItems.Count); if (baselineItems.Count > 1) { for (var j = 0; j < baselineItems.Count; j++) { var baselineItem = baselineItems[j]; var convertedItem = convertedItems[j]; Assert.Equal(baselineItem.Include, convertedItem.Include); Assert.Equal(baselineItem.Update, convertedItem.Update); } } } } }
public static IProjectRootElement ChangeImportsAndAddSdkAttribute(this IProjectRootElement projectRootElement, BaselineProject baselineProject) { foreach (var import in projectRootElement.Imports) { var fileName = Path.GetFileName(import.Project); if (MSBuildFacts.PropsToRemove.Contains(fileName, StringComparer.OrdinalIgnoreCase) || MSBuildFacts.TargetsToRemove.Contains(fileName, StringComparer.OrdinalIgnoreCase)) { projectRootElement.RemoveChild(import); } else if (!MSBuildFacts.ImportsToKeep.Contains(fileName, StringComparer.OrdinalIgnoreCase)) { Console.WriteLine($"This project has an unrecognized custom import which may need reviewed after conversion: {fileName}"); } } if (baselineProject.ProjectStyle is ProjectStyle.WindowsDesktop && baselineProject.TargetTFM is MSBuildFacts.NetCoreApp31) { projectRootElement.Sdk = DesktopFacts.WinSDKAttribute; } else if (MSBuildHelpers.IsWeb(projectRootElement)) { projectRootElement.Sdk = WebFacts.WebSDKAttribute; } else { projectRootElement.Sdk = MSBuildFacts.DefaultSDKAttribute; } return(projectRootElement); }
public Converter(UnconfiguredProject project, BaselineProject sdkBaselineProject, IProjectRootElement projectRootElement, bool noBackup, bool forceRemoveCustomImports) { _project = project ?? throw new ArgumentNullException(nameof(project)); _sdkBaselineProject = sdkBaselineProject; _projectRootElement = projectRootElement ?? throw new ArgumentNullException(nameof(projectRootElement)); _noBackup = noBackup; _forceRemoveCustomImports = forceRemoveCustomImports; _differs = GetDiffers(); }
/// <summary> /// Determines if a given project references Desktop assemblies. /// </summary> public static bool IsDesktop(IProjectRootElement projectRoot) { var references = projectRoot.ItemGroups.SelectMany(GetReferences)?.Select(elem => elem.Include.Split(',').First()); if (references is null) { return false; } else { return DesktopFacts.KnownDesktopReferences.Any(reference => references.Contains(reference, StringComparer.OrdinalIgnoreCase)); } }
/// <summary> /// Determines if a project is a .NET Framework MSTest project by looking at its references. /// </summary> public static bool IsNETFrameworkMSTestProject(IProjectRootElement projectRoot) { var references = projectRoot.ItemGroups.SelectMany(GetReferences)?.Select(elem => elem.Include.Split(',').First()); if (references is null) { return false; } else { return MSTestFacts.MSTestReferences.All(reference => references.Contains(reference, StringComparer.OrdinalIgnoreCase)); } }
/// <summary> /// Determines if a given project is a WinForms project by looking at its references. /// </summary> public static bool IsWinForms(IProjectRootElement projectRoot) { var references = projectRoot.ItemGroups.SelectMany(GetReferences)?.Select(elem => elem.Include.Split(',').First()); if (references is null) { return(false); } else { return(DesktopFacts.KnownWinFormsReferences.Any(reference => references.Contains(reference, StringComparer.OrdinalIgnoreCase)) || references.Any(x => DevExpressFacts.IsDevExpressWpf(x))); } }
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(); 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); }
/// <summary> /// Determines if a given project is UWP. /// </summary> public static bool IsUwp(IProjectRootElement projectRoot) { if (projectRoot.PropertyGroups.Any(g => g.Properties.Any(p => p.Name == "TargetPlatformIdentifier" && p.Value == "UAP"))) { return true; } var packageReferences = projectRoot.ItemGroups.SelectMany(GetPackageReferences)?.Select(elem => elem.Include.Split(',').First()); if (packageReferences is null) { return false; } else { return DesktopFacts.KnownUwpReferences.Any(reference => packageReferences.Contains(reference, StringComparer.OrdinalIgnoreCase)); } }
public static IProjectRootElement ChangeImportsAndAddSdkAttribute(this IProjectRootElement projectRootElement, BaselineProject baselineProject) { foreach (var import in projectRootElement.Imports) { projectRootElement.RemoveChild(import); } if (baselineProject.ProjectStyle is ProjectStyle.WindowsDesktop && baselineProject.TargetTFM is MSBuildFacts.NetCoreApp31) { projectRootElement.Sdk = DesktopFacts.WinSDKAttribute; } else { projectRootElement.Sdk = MSBuildFacts.DefaultSDKAttribute; } return(projectRootElement); }
public static IProjectRootElement ChangeImports(this IProjectRootElement projectRootElement, BaselineProject baselineProject) { switch (baselineProject.ProjectStyle) { case ProjectStyle.Default: case ProjectStyle.DefaultSubset: case ProjectStyle.WindowsDesktop: case ProjectStyle.MSTest: foreach (var import in projectRootElement.Imports) { projectRootElement.RemoveChild(import); } projectRootElement.Sdk = MSBuildHelpers.IsWinForms(projectRootElement) || MSBuildHelpers.IsWPF(projectRootElement) || MSBuildHelpers.IsDesktop(projectRootElement) ? DesktopFacts.WinSDKAttribute : MSBuildFacts.DefaultSDKAttribute; break; } return(projectRootElement); }
public static IProjectRootElement ChangeImports(this IProjectRootElement projectRootElement, BaselineProject baselineProject) { var projectStyle = baselineProject.ProjectStyle; if (projectStyle == ProjectStyle.Default || projectStyle == ProjectStyle.DefaultSubset || projectStyle == ProjectStyle.WindowsDesktop) { foreach (var import in projectRootElement.Imports) { projectRootElement.RemoveChild(import); } if (MSBuildHelpers.IsWinForms(projectRootElement) || MSBuildHelpers.IsWPF(projectRootElement) || MSBuildHelpers.IsDesktop(projectRootElement)) { projectRootElement.Sdk = DesktopFacts.WinSDKAttribute; } else { projectRootElement.Sdk = MSBuildFacts.DefaultSDKAttribute; } } return(projectRootElement); }
public MSBuildConversionWorkspaceItem(IProjectRootElement root, UnconfiguredProject unconfiguredProject, BaselineProject baseline) { ProjectRootElement = root; UnconfiguredProject = unconfiguredProject; SdkBaselineProject = baseline; }
public ImmutableDictionary <string, ImmutableDictionary <string, string> > DetermineConfigurations(IProjectRootElement projectRootElement) { var builder = ImmutableDictionary.CreateBuilder <string, ImmutableDictionary <string, string> >(); foreach (var propertyGroup in projectRootElement.PropertyGroups) { if (MSBuildHelpers.ConditionToDimensionValues(propertyGroup.Condition, out var dimensionValues)) { var name = MSBuildHelpers.GetConfigurationName(dimensionValues); if (!builder.ContainsKey(name)) { builder.Add(name, dimensionValues.ToImmutableDictionary()); foreach (var dimensionValuePair in dimensionValues) { if (!builder.ContainsKey(dimensionValuePair.Value)) { var dimensionValueDictionary = new Dictionary <string, string> { { dimensionValuePair.Key, dimensionValuePair.Value } }; builder.Add(dimensionValuePair.Value, dimensionValueDictionary.ToImmutableDictionary()); } } } } } return(builder.ToImmutable()); }
/// <summary> /// Gets a flat list of all project type guids. /// </summary> public static IEnumerable <string> GetAllProjectTypeGuids(IProjectRootElement root) => root.PropertyGroups.SelectMany(pg => pg.Properties.Where(prop => prop.ElementName.Equals(MSBuildFacts.ProjectTypeGuidsNodeName)) .SelectMany(prop => prop.Value.Split(';')));
/// <summary> /// Determines if a given project is a WinForms project by looking at its references. /// </summary> public static bool IsWinForms(IProjectRootElement projectRoot) { var references = projectRoot.ItemGroups.SelectMany(GetReferences)?.Select(elem => elem.Include.Split(',').First()); return(DesktopFacts.KnownWinFormsReferences.Any(reference => references.Contains(reference, StringComparer.OrdinalIgnoreCase))); }
public static IProjectRootElement RemoveUnnecessaryPropertiesNotInSDKByDefault(this IProjectRootElement projectRootElement, ProjectStyle projectStyle) { foreach (var propGroup in projectRootElement.PropertyGroups) { foreach (var prop in propGroup.Properties) { if (MSBuildFacts.UnnecessaryProperties.Contains(prop.Name, StringComparer.OrdinalIgnoreCase)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsDefineConstantDefault(prop)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsDebugTypeDefault(prop)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsOutputPathDefault(prop)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsPlatformTargetDefault(prop)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsNameDefault(prop, GetProjectName(projectRootElement.FullPath))) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsDocumentationFileDefault(prop)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsUnnecessaryTestProperty(prop)) { propGroup.RemoveChild(prop); } else if (ProjectPropertyHelpers.IsEmptyNuGetPackageImportStamp(prop)) { propGroup.RemoveChild(prop); } else if (projectStyle == ProjectStyle.MSTest && ProjectPropertyHelpers.IsOutputTypeNode(prop)) { // Old MSTest projects specify library, but this is not valid since tests on .NET Core are netcoreapp projects. propGroup.RemoveChild(prop); } } if (propGroup.Properties.Count == 0) { projectRootElement.RemoveChild(propGroup); } } return(projectRootElement);
/// <summary> /// Finds the item group where a packages.config is included. Assumes only one. /// </summary> public static ProjectItemGroupElement GetPackagesConfigItemGroup(IProjectRootElement root) => root.ItemGroups.FirstOrDefault(pige => pige.Items.Any(pe => pe.Include.Equals(PackageFacts.PackagesConfigIncludeName, StringComparison.OrdinalIgnoreCase)));
/// <summary> /// Gets the OutputType node in a project. There will only reasonably be one. /// </summary> public static ProjectPropertyElement?GetOutputTypeNode(this IProjectRootElement root) => root.PropertyGroups.SelectMany(pg => pg.Properties.Where(ProjectPropertyHelpers.IsOutputTypeNode)).FirstOrDefault();
/// <summary> /// Gets the top-level property group, and if it doesn't exist, creates it. /// </summary> public static ProjectPropertyGroupElement GetOrCreateTopLevelPropertyGroup(BaselineProject baselineProject, IProjectRootElement projectRootElement) { bool IsAfterFirstImport(ProjectPropertyGroupElement propertyGroup) { if (baselineProject.ProjectStyle == ProjectStyle.Default || baselineProject.ProjectStyle == ProjectStyle.WindowsDesktop || baselineProject.ProjectStyle == ProjectStyle.DefaultSubset) { return(true); } var firstImport = projectRootElement.Imports.Where(i => i.Label != MSBuildFacts.SharedProjectsImportLabel).FirstOrDefault(); return(firstImport is { } && propertyGroup.Location.Line > firstImport.Location.Line); }
/// <summary> /// Finds the item group where PackageReferences are specified. Usually there is only one. /// </summary> public static ProjectItemGroupElement GetOrCreatePackageReferencesItemGroup(IProjectRootElement rootElement) => rootElement.ItemGroups.SingleOrDefault(ig => ig.Items.All(i => i.ElementName.Equals(PackageFacts.PackageReferencePackagesNodeName, StringComparison.OrdinalIgnoreCase))) ?? rootElement.AddItemGroup();
/// <summary> /// Finds the property group with the TFM specified, which is normally the top-level property group. /// </summary> public static ProjectPropertyGroupElement GetOrCreateTopLevelPropertyGroupWithTFM(IProjectRootElement rootElement) => rootElement.PropertyGroups.Single(pg => pg.Properties.Any(p => p.ElementName.Equals(MSBuildFacts.TargetFrameworkNodeName, StringComparison.OrdinalIgnoreCase))) ?? rootElement.AddPropertyGroup();