Пример #1
0
        public static void MigrateFromOldConfigNames(MSBuildProject project)
        {
            var root = project.Root;

            bool hasGodotProjectGeneratorVersion = false;
            bool foundOldConfiguration           = false;

            foreach (var propertyGroup in root.PropertyGroups.Where(g => string.IsNullOrEmpty(g.Condition)))
            {
                if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
                {
                    hasGodotProjectGeneratorVersion = true;
                }

                foreach (var configItem in propertyGroup.Properties
                         .Where(p => p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Tools"))
                {
                    configItem.Value          = "Debug";
                    foundOldConfiguration     = true;
                    project.HasUnsavedChanges = true;
                }
            }

            if (!hasGodotProjectGeneratorVersion)
            {
                root.PropertyGroups.First(g => string.IsNullOrEmpty(g.Condition))?
                .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
                project.HasUnsavedChanges = true;
            }

            if (!foundOldConfiguration)
            {
                var toolsConditions = new[]
                {
                    "'$(Configuration)|$(Platform)' == 'Tools|AnyCPU'",
                    "'$(Configuration)|$(Platform)' != 'Tools|AnyCPU'",
                    "'$(Configuration)' == 'Tools'",
                    "'$(Configuration)' != 'Tools'"
                };

                foundOldConfiguration = root.PropertyGroups
                                        .Any(g => toolsConditions.Any(c => c == g.Condition.Trim()));
            }

            if (foundOldConfiguration)
            {
                void MigrateConfigurationConditions(string oldConfiguration, string newConfiguration)
                {
                    void MigrateConditions(string oldCondition, string newCondition)
                    {
                        foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
                        {
                            propertyGroup.Condition   = " " + newCondition + " ";
                            project.HasUnsavedChanges = true;
                        }

                        foreach (var propertyGroup in root.PropertyGroups)
                        {
                            foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
                            {
                                prop.Condition            = " " + newCondition + " ";
                                project.HasUnsavedChanges = true;
                            }
                        }

                        foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
                        {
                            itemGroup.Condition       = " " + newCondition + " ";
                            project.HasUnsavedChanges = true;
                        }

                        foreach (var itemGroup in root.ItemGroups)
                        {
                            foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
                            {
                                item.Condition            = " " + newCondition + " ";
                                project.HasUnsavedChanges = true;
                            }
                        }
                    }

                    foreach (var op in new[] { "==", "!=" })
                    {
                        MigrateConditions($"'$(Configuration)|$(Platform)' {op} '{oldConfiguration}|AnyCPU'", $"'$(Configuration)|$(Platform)' {op} '{newConfiguration}|AnyCPU'");
                        MigrateConditions($"'$(Configuration)' {op} '{oldConfiguration}'", $"'$(Configuration)' {op} '{newConfiguration}'");
                    }
                }

                MigrateConfigurationConditions("Debug", "ExportDebug");
                MigrateConfigurationConditions("Release", "ExportRelease");
                MigrateConfigurationConditions("Tools", "Debug"); // Must be last
            }
        }
Пример #2
0
        public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
        {
            var root = project.Root;

            if (!string.IsNullOrEmpty(root.Sdk))
            {
                return;
            }

            root.Sdk = ProjectGenerator.GodotSdkAttrValue;

            root.ToolsVersion   = null;
            root.DefaultTargets = null;

            root.AddProperty("TargetFramework", "net472");

            // Remove obsolete properties, items and elements. We're going to be conservative
            // here to minimize the chances of introducing breaking changes. As such we will
            // only remove elements that could potentially cause issues with the Godot.NET.Sdk.

            void RemoveElements(IEnumerable <ProjectElement> elements)
            {
                foreach (var element in elements)
                {
                    element.Parent.RemoveChild(element);
                }
            }

            // Default Configuration

            RemoveElements(root.PropertyGroups.SelectMany(g => g.Properties)
                           .Where(p => p.Name == "Configuration" && p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Debug"));

            // Default Platform

            RemoveElements(root.PropertyGroups.SelectMany(g => g.Properties)
                           .Where(p => p.Name == "Platform" && p.Condition.Trim() == "'$(Platform)' == ''" && p.Value == "AnyCPU"));

            // Simple properties

            var yabaiProperties = new[]
            {
                "OutputPath",
                "BaseIntermediateOutputPath",
                "IntermediateOutputPath",
                "TargetFrameworkVersion",
                "ProjectTypeGuids",
                "ApiConfiguration"
            };

            RemoveElements(root.PropertyGroups.SelectMany(g => g.Properties)
                           .Where(p => yabaiProperties.Contains(p.Name)));

            // Configuration dependent properties

            var yabaiPropertiesForConfigs = new[]
            {
                "DebugSymbols",
                "DebugType",
                "Optimize",
                "DefineConstants",
                "ErrorReport",
                "WarningLevel",
                "ConsolePause"
            };

            var configNames = new[]
            {
                "ExportDebug", "ExportRelease", "Debug",
                "Tools", "Release" // Include old config names as well in case it's upgrading from 3.2.1 or older
            };

            foreach (var config in configNames)
            {
                var group = root.PropertyGroups
                            .FirstOrDefault(g => g.Condition.Trim() == $"'$(Configuration)|$(Platform)' == '{config}|AnyCPU'");

                if (group == null)
                {
                    continue;
                }

                RemoveElements(group.Properties.Where(p => yabaiPropertiesForConfigs.Contains(p.Name)));

                if (group.Count == 0)
                {
                    // No more children, safe to delete the group
                    group.Parent.RemoveChild(group);
                }
            }

            // Godot API References

            var apiAssemblies = new[] { ApiAssemblyNames.Core, ApiAssemblyNames.Editor };

            RemoveElements(root.ItemGroups.SelectMany(g => g.Items)
                           .Where(i => i.ItemType == "Reference" && apiAssemblies.Contains(i.Include)));

            // Microsoft.NETFramework.ReferenceAssemblies PackageReference

            RemoveElements(root.ItemGroups.SelectMany(g => g.Items).Where(i =>
                                                                          i.ItemType == "PackageReference" &&
                                                                          i.Include.Equals("Microsoft.NETFramework.ReferenceAssemblies", StringComparison.OrdinalIgnoreCase)));

            // Imports

            var yabaiImports = new[]
            {
                "$(MSBuildBinPath)/Microsoft.CSharp.targets",
                "$(MSBuildBinPath)Microsoft.CSharp.targets"
            };

            RemoveElements(root.Imports.Where(import => yabaiImports.Contains(
                                                  import.Project.Replace("\\", "/").Replace("//", "/"))));

            // 'EnableDefaultCompileItems' and 'GenerateAssemblyInfo' are kept enabled by default
            // on new projects, but when migrating old projects we disable them to avoid errors.
            root.AddProperty("EnableDefaultCompileItems", "false");
            root.AddProperty("GenerateAssemblyInfo", "false");

            // Older AssemblyInfo.cs cause the following error:
            // 'Properties/AssemblyInfo.cs(19,28): error CS8357:
            // The specified version string contains wildcards, which are not compatible with determinism.
            // Either remove wildcards from the version string, or disable determinism for this compilation.'
            // We disable deterministic builds to prevent this. The user can then fix this manually when desired
            // by fixing 'AssemblyVersion("1.0.*")' to not use wildcards.
            root.AddProperty("Deterministic", "false");

            project.HasUnsavedChanges = true;

            var xDoc = XDocument.Parse(root.RawXml);

            if (xDoc.Root == null)
            {
                return; // Too bad, we will have to keep the xmlns/namespace and xml declaration
            }
            XElement GetElement(XDocument doc, string name, string value, string parentName)
            {
                foreach (var node in doc.DescendantNodes())
                {
                    if (!(node is XElement element))
                    {
                        continue;
                    }
                    if (element.Name.LocalName.Equals(name) && element.Value == value &&
                        element.Parent != null && element.Parent.Name.LocalName.Equals(parentName))
                    {
                        return(element);
                    }
                }

                return(null);
            }

            // Add comment about Microsoft.NET.Sdk properties disabled during migration

            GetElement(xDoc, name: "EnableDefaultCompileItems", value: "false", parentName: "PropertyGroup")
            .AddBeforeSelf(new XComment("The following properties were overriden during migration to prevent errors.\n" +
                                        "    Enabling them may require other manual changes to the project and its files."));

            void RemoveNamespace(XElement element)
            {
                element.Attributes().Where(x => x.IsNamespaceDeclaration).Remove();
                element.Name = element.Name.LocalName;

                foreach (var node in element.DescendantNodes())
                {
                    if (node is XElement xElement)
                    {
                        // Need to do the same for all children recursively as it adds it to them for some reason...
                        RemoveNamespace(xElement);
                    }
                }
            }

            // Remove xmlns/namespace
            RemoveNamespace(xDoc.Root);

            // Remove xml declaration
            xDoc.Nodes().FirstOrDefault(node => node.NodeType == XmlNodeType.XmlDeclaration)?.Remove();

            string projectFullPath = root.FullPath;

            root          = ProjectRootElement.Create(xDoc.CreateReader());
            root.FullPath = projectFullPath;

            project.Root = root;
        }
Пример #3
0
        ///  Simple function to make sure the Api assembly references are configured correctly
        public static void FixApiHintPath(MSBuildProject project)
        {
            var root = project.Root;

            void AddPropertyIfNotPresent(string name, string condition, string value)
            {
                if (root.PropertyGroups
                    .Any(g => (string.IsNullOrEmpty(g.Condition) || g.Condition.Trim() == condition) &&
                         g.Properties
                         .Any(p => p.Name == name &&
                              p.Value == value &&
                              (p.Condition.Trim() == condition || g.Condition.Trim() == condition))))
                {
                    return;
                }

                root.AddProperty(name, value).Condition = " " + condition + " ";
                project.HasUnsavedChanges = true;
            }

            AddPropertyIfNotPresent(name: "ApiConfiguration",
                                    condition: "'$(Configuration)' != 'ExportRelease'",
                                    value: "Debug");
            AddPropertyIfNotPresent(name: "ApiConfiguration",
                                    condition: "'$(Configuration)' == 'ExportRelease'",
                                    value: "Release");

            void SetReferenceHintPath(string referenceName, string condition, string hintPath)
            {
                foreach (var itemGroup in root.ItemGroups.Where(g =>
                                                                g.Condition.Trim() == string.Empty || g.Condition.Trim() == condition))
                {
                    var references = itemGroup.Items.Where(item =>
                                                           item.ItemType == "Reference" &&
                                                           item.Include == referenceName &&
                                                           (item.Condition.Trim() == condition || itemGroup.Condition.Trim() == condition));

                    var referencesWithHintPath = references.Where(reference =>
                                                                  reference.Metadata.Any(m => m.Name == "HintPath"));

                    if (referencesWithHintPath.Any(reference => reference.Metadata
                                                   .Any(m => m.Name == "HintPath" && m.Value == hintPath)))
                    {
                        // Found a Reference item with the right HintPath
                        return;
                    }

                    var referenceWithHintPath = referencesWithHintPath.FirstOrDefault();
                    if (referenceWithHintPath != null)
                    {
                        // Found a Reference item with a wrong HintPath
                        foreach (var metadata in referenceWithHintPath.Metadata.ToList()
                                 .Where(m => m.Name == "HintPath"))
                        {
                            // Safe to remove as we duplicate with ToList() to loop
                            referenceWithHintPath.RemoveChild(metadata);
                        }

                        referenceWithHintPath.AddMetadata("HintPath", hintPath);
                        project.HasUnsavedChanges = true;
                        return;
                    }

                    var referenceWithoutHintPath = references.FirstOrDefault();
                    if (referenceWithoutHintPath != null)
                    {
                        // Found a Reference item without a HintPath
                        referenceWithoutHintPath.AddMetadata("HintPath", hintPath);
                        project.HasUnsavedChanges = true;
                        return;
                    }
                }

                // Found no Reference item at all. Add it.
                root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
                project.HasUnsavedChanges = true;
            }

            const string coreProjectName   = "GodotSharp";
            const string editorProjectName = "GodotSharpEditor";

            const string coreCondition   = "";
            const string editorCondition = "'$(Configuration)' == 'Debug'";

            var coreHintPath   = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll";
            var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll";

            SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
            SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
        }