Example #1
0
        /// <summary>
        /// Clear out the project's construction model and add a simple SDK-based project to get a baseline.
        /// 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).
        /// </summary>
        private BaselineProject CreateSdkBaselineProject(string projectFilePath, IProject project, IProjectRootElement root, ImmutableDictionary <string, ImmutableDictionary <string, string> > configurations)
        {
            var projectStyle = GetProjectStyle(root);
            var outputType   = GetProjectOutputType(root);
            var rootElement  = ProjectRootElement.Open(projectFilePath);

            rootElement.RemoveAllChildren();
            switch (projectStyle)
            {
            case ProjectStyle.Default:
            case ProjectStyle.DefaultSubset:
            case ProjectStyle.MSTest:
                rootElement.Sdk = MSBuildFacts.DefaultSDKAttribute;
                break;

            case ProjectStyle.WindowsDesktop:
                rootElement.Sdk = DesktopFacts.WinSDKAttribute;
                break;

            default:
                throw new NotSupportedException($"This project has custom imports in a manner that's not supported. '{projectFilePath}'");
            }

            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}'"));

            if (projectStyle == ProjectStyle.WindowsDesktop)
            {
                if (MSBuildHelpers.IsWinForms(root))
                {
                    MSBuildHelpers.AddUseWinForms(propGroup);
                }

                if (MSBuildHelpers.IsWPF(root))
                {
                    MSBuildHelpers.AddUseWPF(propGroup);
                }

                // User is referencing WindowsBase only
                if (MSBuildHelpers.IsDesktop(root) && !MSBuildHelpers.HasWPFOrWinForms(propGroup))
                {
                    MSBuildHelpers.AddUseWinForms(propGroup);
                }
            }

            // Create a new collection because a project with this name has already been loaded into the global collection.
            using var pc = new ProjectCollection();
            var newProject = new UnconfiguredProject(configurations);

            newProject.LoadProjects(pc, rootElement);

            // If the original project had the TargetFramework property don't touch it during conversion.
            var propertiesInTheBaseline = ImmutableArray.Create(MSBuildFacts.OutputTypeNodeName);

            if (project.GetProperty(MSBuildFacts.TargetFrameworkNodeName) is { })
Example #2
0
        public MSBuildConversionWorkspace(ImmutableArray <string> paths, bool noBackup, string tfm, bool keepCurrentTFMs, bool forceWeb)
        {
            var items = ImmutableArray.CreateBuilder <MSBuildConversionWorkspaceItem>();

            var globalProperties = ImmutableDictionary <string, string> .Empty;

            using var collection = new ProjectCollection();

            foreach (var path in paths)
            {
                var fileExtension = Path.GetExtension(path);
                if ((StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".fsproj") != 0) &&
                    (StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".csproj") != 0) &&
                    (StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".vbproj") != 0))
                {
                    Console.WriteLine($"'{path}' is not a .NET project, skipping it.");
                    continue;
                }

                // This is a hack, but the only way to handle this is to re-architect try-convert along these lines:
                // 1. Launch a .NET Framework process using the VS-deployed MSBuild to evaluate a project
                // 2. Serialize the evaluation model
                // 3. Use the .NET Core process to load MSBuild, but this time from .NET SDK
                // 4. Deserialize the evaluation model
                // 5. Do conversions
                RemoveTargetsNotLoadableByNETSDKMSBuild(path);

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

                    var unconfiguredProject = new UnconfiguredProject(configurations);
                    unconfiguredProject.LoadProjects(collection, globalProperties, path);


                    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);
                    }
                }
            }

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

            var globalProperties = ImmutableDictionary <string, string> .Empty;

            using var collection = new ProjectCollection();

            foreach (var path in paths)
            {
                var fileExtension = Path.GetExtension(path);
                if ((StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".fsproj") != 0) &&
                    (StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".csproj") != 0) &&
                    (StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".vbproj") != 0))
                {
                    Console.WriteLine($"'{path}' is not a .NET project, skipping it.");
                    continue;
                }

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

                    var unconfiguredProject = new UnconfiguredProject(configurations);
                    unconfiguredProject.LoadProjects(collection, globalProperties, path);


                    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);
                    }
                }
            }

            WorkspaceItems = items.ToImmutable();
        }
        public MSBuildWorkspace(ImmutableArray <string> paths, bool noBackup)
        {
            var items = ImmutableArray.CreateBuilder <MSBuildWorkspaceItem>();

            var globalProperties = ImmutableDictionary <string, string> .Empty;

            using var collection = new ProjectCollection();

            foreach (var path in paths)
            {
                var fileExtension = Path.GetExtension(path);
                if ((StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".fsproj") != 0) &&
                    (StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".csproj") != 0) &&
                    (StringComparer.OrdinalIgnoreCase.Compare(fileExtension, ".vbproj") != 0))
                {
                    Console.WriteLine($"'{path}' is not a .NET project, skipping it.");
                    continue;
                }

                var root = new MSBuildProjectRootElement(ProjectRootElement.Open(path, collection, preserveFormatting: true));
                if (IsSupportedProjectType(root))
                {
                    if (!noBackup)
                    {
                        File.Copy(path, path + ".old");
                    }

                    var configurations = DetermineConfigurations(root);

                    var unconfiguredProject = new UnconfiguredProject(configurations);
                    unconfiguredProject.LoadProjects(collection, globalProperties, path);

                    var baseline = CreateSdkBaselineProject(path, unconfiguredProject.FirstConfiguredProject, root, configurations);
                    root.Reload(throwIfUnsavedChanges: false, preserveFormatting: true);

                    var item = new MSBuildWorkspaceItem(root, unconfiguredProject, baseline);
                    items.Add(item);
                }
            }

            WorkspaceItems = items.ToImmutable();
        }
Example #5
0
        /// <summary>
        /// Clear out the project's construction model and add a simple SDK-based project to get a baseline.
        /// 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).
        /// </summary>
        private bool TryCreateSdkBaselineProject(string projectFilePath, IProject project, IProjectRootElement root, ImmutableDictionary <string, ImmutableDictionary <string, string> > configurations, string tfm, bool keepCurrentTFMs, [NotNullWhen(true)] out BaselineProject?baselineProject)
        {
            var projectStyle = GetProjectStyle(root);
            var outputType   = GetProjectOutputType(root);
            var rootElement  = ProjectRootElement.Open(projectFilePath);

            rootElement.RemoveAllChildren();
            switch (projectStyle)
            {
            case ProjectStyle.Default:
            case ProjectStyle.DefaultSubset:
            case ProjectStyle.MSTest:
                rootElement.Sdk = MSBuildFacts.DefaultSDKAttribute;
                break;

            case ProjectStyle.WindowsDesktop:
                rootElement.Sdk =
                    tfm.ContainsIgnoreCase(MSBuildFacts.Net5)
                            ? 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());

            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)
            {
                if (MSBuildHelpers.IsWinForms(root))
                {
                    MSBuildHelpers.AddUseWinForms(propGroup);
                }

                if (MSBuildHelpers.IsWPF(root))
                {
                    MSBuildHelpers.AddUseWPF(propGroup);
                }

                // User is referencing WindowsBase only
                if (MSBuildHelpers.IsDesktop(root) && !MSBuildHelpers.HasWPFOrWinForms(propGroup))
                {
                    MSBuildHelpers.AddUseWinForms(propGroup);
                }
            }

            // Create a new collection because a project with this name has already been loaded into the global collection.
            using var pc = new ProjectCollection();
            var newProject = new UnconfiguredProject(configurations);

            newProject.LoadProjects(pc, rootElement);

            // If the original project had the TargetFramework property don't touch it during conversion.
            var propertiesInTheBaseline = ImmutableArray.Create(MSBuildFacts.OutputTypeNodeName);

            if (project.GetProperty(MSBuildFacts.TargetFrameworkNodeName) is { })