public bool FSharpSpecificConversions()

in src/Deprecated/Conversion/ProjectFileConverter.cs [924:1231]


        public bool FSharpSpecificConversions(bool actuallyMakeChanges)
        {           
            // For FSharp projects, should import different location of FSharp targets
            const string fsharpFS10TargetsPath = @"$(MSBuildExtensionsPath)\FSharp\1.0\Microsoft.FSharp.Targets";
            const string fsharpFS10TargetsPath32 = @"$(MSBuildExtensionsPath32)\FSharp\1.0\Microsoft.FSharp.Targets";
            const string fsharpFS40TargetsPath = @"$(MSBuildExtensionsPath32)\..\Microsoft F#\v4.0\Microsoft.FSharp.Targets";
            const string fsharpFS45TargetsPath = @"$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets";
            const string fsharpPortableDev11TargetsPath = @"$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.Portable.FSharp.Targets";

            const string fsharpDev12PlusProperty = "FSharpTargetsPath";

            // Dev12+ projects import *.targets files using property
            const string fsharpDev12PlusImportsValue = @"$(" + fsharpDev12PlusProperty + ")";
            // Q: do we need to distinguish between different versions of F# for the same version of VS
            const string fsharpDev12PlusTargetsPath = @"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets";
            const string fsharpDev12PlusPortableTargetsPath = @"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.Portable.FSharp.Targets";

            bool isAtLeastDev10Project = false;

            ProjectImportElement fsharpTargetsFS10Import = null;
            ProjectImportElement fsharpTargetsFS40Import = null;
            ProjectImportElement fsharpTargetsFS45Import = null;
            ProjectImportElement fsharpTargetsDev12PlusImport = null;
            ProjectImportElement fsharpTargetsDev11PortableImport = null;

            if (actuallyMakeChanges == false && this.xmakeProject == null)
            {
                // when coming down the actuallyMakeChanges==false code path (from the F# project system's UpgradeProject_CheckOnly method), we may not have loaded the Xml yet, so do that now
                this.xmakeProject = ProjectRootElement.Open(oldProjectFile);
            }

            // local function: string equality check using OrdinalIgnoreCase comparison
            Func<string, string, bool> equals = (s1, s2) => String.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);
            
            // local function: wraps specified string value into Exists('value')
            Func<string, string> exists = s => string.Format(CultureInfo.InvariantCulture, "Exists('{0}')", s);
            
            // local function: 
            // Creates property group element containing one property fsharpDev12PlusProperty with value 'path'. 
            // If addCondition is true, property group will have Exists(path) condition
            Action<string, ProjectElementContainer> appendPropertyGroupForDev12PlusTargetsPath =
                (path, parent) =>
                {
                    var propGroup = xmakeProject.CreatePropertyGroupElement();
                    parent.AppendChild(propGroup);
                    var prop = xmakeProject.CreatePropertyElement(fsharpDev12PlusProperty);
                    prop.Value = path;
                    propGroup.AppendChild(prop);                    
                };

            foreach (ProjectImportElement importElement in xmakeProject.Imports)
            {
                if (equals(importElement.Project, fsharpFS10TargetsPath) || equals(importElement.Project, fsharpFS10TargetsPath32))
                {
                    fsharpTargetsFS10Import = importElement;
                    if (equals(@"!Exists('$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll')", fsharpTargetsFS10Import.Condition)
                        || equals(@"!Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')", fsharpTargetsFS10Import.Condition))
                    {
                        isAtLeastDev10Project = true;
                    }
                }
                else if (equals(importElement.Project, fsharpFS40TargetsPath))
                {
                    fsharpTargetsFS40Import = importElement;
                    isAtLeastDev10Project = true;
                }
                else if (equals(importElement.Project, fsharpFS45TargetsPath))
                {
                    fsharpTargetsFS45Import = importElement;
                    isAtLeastDev10Project = true;
                }
                else if (equals(importElement.Project, fsharpDev12PlusImportsValue))
                {
                    fsharpTargetsDev12PlusImport = importElement;
                    isAtLeastDev10Project = true;
                }
                else if (equals(importElement.Project, fsharpPortableDev11TargetsPath))
                {
                    fsharpTargetsDev11PortableImport = importElement;
                    isAtLeastDev10Project= true;
                }
            }

            if (fsharpTargetsDev12PlusImport != null)
            {
                // if project already contains version independent import - then assume it is already at least dev12 - do nothing
                return false;
            }

            // no other F# imports - do nothing
            if (fsharpTargetsFS10Import == null && fsharpTargetsFS40Import == null && fsharpTargetsFS45Import == null && fsharpTargetsDev11PortableImport == null)
                return false;

            if (!actuallyMakeChanges)
                return true;            

            // both branches adds this elements to the project
            var chooseElement = xmakeProject.CreateChooseElement(); // (1)
            
            if (fsharpTargetsDev11PortableImport != null)
            {
                // Dev11 portable library
                // Expected fragment of the project file after upgrade
                //<Choose>
                //  <When Condition="'$(VisualStudioVersion)' == '11.0'"> (2)
                //    <PropertyGroup>
                //      <FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.Portable.FSharp.Targets</FSharpTargetsPath>
                //    </PropertyGroup>
                //  </When>
                //  <Otherwise> (3)
                //    <PropertyGroup>
                //      <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.Portable.FSharp.Targets</FSharpTargetsPath>
                //    </PropertyGroup>
                //  </Otherwise>
                //</Choose>
                //<Import Project=""$(FSharpTargetsPath)"" Condition="Exists('$(FSharpTargetsPath)')"/>
                fsharpTargetsDev11PortableImport.Parent.InsertBeforeChild(chooseElement, fsharpTargetsDev11PortableImport);

                // portable libraries are supported since Dev11
                var whenVsVersionIsDev11 = xmakeProject.CreateWhenElement("'$(VisualStudioVersion)' == '11.0'"); // (2)
                chooseElement.AppendChild(whenVsVersionIsDev11);
                
                appendPropertyGroupForDev12PlusTargetsPath(fsharpPortableDev11TargetsPath, whenVsVersionIsDev11);

                var otherwiseIfVsVersionIsDev12Plus = xmakeProject.CreateOtherwiseElement(); // (3)
                chooseElement.AppendChild(otherwiseIfVsVersionIsDev12Plus);

                appendPropertyGroupForDev12PlusTargetsPath(fsharpDev12PlusPortableTargetsPath, otherwiseIfVsVersionIsDev12Plus);
            }
            else
            {
                // This is an FSharp project, and it does not already have a 4.5 import, and thus it needs repair.
                // one of these elements should be non-null, otherwise we'll exit based on the check above
                var someNonNullImportElement = fsharpTargetsFS10Import ?? fsharpTargetsFS40Import ?? fsharpTargetsFS45Import;

                someNonNullImportElement.Parent.InsertBeforeChild(chooseElement, someNonNullImportElement);

                // Expected fragment of the project file after upgrade 
                //<Choose>
                //  <When Condition="'$(VisualStudioVersion)' == '11.0'">
                //    <PropertyGroup>
                //      <FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
                //    </PropertyGroup>
                //  </When>
                //  <Otherwise>
                //    <PropertyGroup>
                //      <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
                //    </PropertyGroup>
                //  </Otherwise>
                //</Choose>
                //<Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />           


                var whenVsVersionIsDev11 = xmakeProject.CreateWhenElement("'$(VisualStudioVersion)' == '11.0'");
                chooseElement.AppendChild(whenVsVersionIsDev11);
                {
                    appendPropertyGroupForDev12PlusTargetsPath(fsharpFS45TargetsPath, whenVsVersionIsDev11);
                }

                var otherwiseIfVsVersionIsDev12Plus = xmakeProject.CreateOtherwiseElement();
                chooseElement.AppendChild(otherwiseIfVsVersionIsDev12Plus);
                {
                    // Dev12+ projects - import target file based on property 'fsharpDev12PlusProperty'
                    appendPropertyGroupForDev12PlusTargetsPath(fsharpDev12PlusTargetsPath, otherwiseIfVsVersionIsDev12Plus);
                }
            }
            // add Dev12 specific Imports element
            var dev12PlusImportElement = xmakeProject.CreateImportElement(fsharpDev12PlusImportsValue);
            dev12PlusImportElement.Condition = exists(fsharpDev12PlusImportsValue);
            chooseElement.Parent.InsertAfterChild(dev12PlusImportElement, chooseElement);

            if (fsharpTargetsFS10Import != null)
                xmakeProject.RemoveChild(fsharpTargetsFS10Import);
            if (fsharpTargetsFS40Import != null)
                xmakeProject.RemoveChild(fsharpTargetsFS40Import);
            if (fsharpTargetsFS45Import != null)
                xmakeProject.RemoveChild(fsharpTargetsFS45Import);
            if (fsharpTargetsDev11PortableImport != null)
                xmakeProject.RemoveChild(fsharpTargetsDev11PortableImport);

            const string ReferenceItemType = "Reference";

            // find ItemGroup for Reference items
            ProjectItemGroupElement referencesItemGroup = xmakeProject.Items
                .Where(projectItem => projectItem.ItemType == ReferenceItemType && projectItem.Parent is ProjectItemGroupElement)
                .Select(projectItem => (ProjectItemGroupElement)projectItem.Parent)
                .FirstOrDefault();

            if (referencesItemGroup == null)
            {
                referencesItemGroup = this.xmakeProject.AddItemGroup();
            }

            var targetFrameworkVersionProperty = xmakeProject.Properties.FirstOrDefault(p => equals(p.Name, "TargetFrameworkVersion"));

            // fix FSharp.Core reference
            const string TargetFSharpCoreVersionProperty = "TargetFSharpCoreVersion";

            // by default import with minimal possible version
            const string DefaultFSharpCoreVersionFor40 = "4.3.0.0";
            const string DefaultFSharpCoreVersionFor20 = "2.3.0.0";
            const string DefaultPortableFSharpCoreVersion = "2.3.5.0";
            const string FSharpCoreName = "FSharp.Core";

            if (!isAtLeastDev10Project)
            {
                bool hasMscorlibReference = xmakeProject.Items.Any(projectItem => projectItem.ItemType == ReferenceItemType && equals(projectItem.Include, "mscorlib"));
                // It appears pre-dev10, so add explicit references to mscorlib
                if (!hasMscorlibReference)
                {
                    referencesItemGroup.AddItem(ReferenceItemType, "mscorlib");
                }
            }            

            // try to find reference to FSharp.Core 
            ProjectItemElement fsharpCoreItem = null;
            foreach (var item in xmakeProject.Items.Where(x => x.ItemType == ReferenceItemType))
            {
                try
                {
                    var name = new AssemblyName(item.Include);
                    if (name.Name == FSharpCoreName)
                    {
                        fsharpCoreItem = item;
                        break;
                    }
                }
                catch (FileLoadException)
                {
                    // Include contains not AssemblyName but rather something else - not the case for F# projects
                }
            }

            const string Dev11PortableFSharpCoreLocation = @"$(MSBuildExtensionsPath32)\..\Reference Assemblies\Microsoft\FSharp\3.0\Runtime\.NETPortable\FSharp.Core.dll";
            const string Dev12PortableFSharpCoreLocationForDev11Projects = @"$(MSBuildExtensionsPath32)\..\Reference Assemblies\Microsoft\FSharp\.NETPortable\$(" + TargetFSharpCoreVersionProperty + @")\FSharp.Core.dll";
            const string HintPath = "HintPath";

            ProjectItemElement newFSharpCoreItem = null;
            string targetFSharpCoreVersionValue = null;

            var hintPathValue = fsharpCoreItem != null ? fsharpCoreItem.Metadata.FirstOrDefault(metadata => metadata.Name == HintPath) : null;
            if (hintPathValue != null)
            {
                if (equals(hintPathValue.Value, Dev11PortableFSharpCoreLocation))
                {
                    // Reference to Dev11 portable library
                    newFSharpCoreItem = referencesItemGroup.AddItem(ReferenceItemType, FSharpCoreName);
                    newFSharpCoreItem.AddMetadata(HintPath, Dev12PortableFSharpCoreLocationForDev11Projects);

                    targetFSharpCoreVersionValue = DefaultPortableFSharpCoreVersion;
                }
            }
            else if (!isAtLeastDev10Project || fsharpCoreItem != null)
            {
                newFSharpCoreItem = referencesItemGroup.AddItem(ReferenceItemType, string.Format("FSharp.Core, Version=$({0}), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", TargetFSharpCoreVersionProperty));
                if (targetFrameworkVersionProperty == null || string.IsNullOrEmpty(targetFrameworkVersionProperty.Value) || !targetFrameworkVersionProperty.Value.StartsWith("v"))
                {
                    targetFSharpCoreVersionValue = DefaultFSharpCoreVersionFor40;
                }
                else
                {
                    var versionStr = targetFrameworkVersionProperty.Value.Substring(1); // strip 'v'
                    Version version;
                    targetFSharpCoreVersionValue =
                        Version.TryParse(versionStr, out version)
                        ? version.Major < 4 ? DefaultFSharpCoreVersionFor20 : DefaultFSharpCoreVersionFor40
                        : DefaultFSharpCoreVersionFor40;
                }
            }

            if (newFSharpCoreItem != null)
            {
                newFSharpCoreItem.AddMetadata("Private", "True");
            }

            
            const string MinimumVisualStudioVersionProperty = "MinimumVisualStudioVersion";
            var hasMinimumVSVersion = xmakeProject.Properties.Any(prop => prop.Name == MinimumVisualStudioVersionProperty);
            
            foreach(var group in xmakeProject.PropertyGroups)
            {
                // find first non-conditional property group to add TargetFSharpCoreVersion property
                if (string.IsNullOrEmpty(group.Condition))
                {
                    if (targetFSharpCoreVersionValue != null)
                    {
                        group.AddProperty(TargetFSharpCoreVersionProperty, targetFSharpCoreVersionValue);
                    }

                    if (!hasMinimumVSVersion)
                    {
                        var prop = group.AddProperty(MinimumVisualStudioVersionProperty, "11");
                        prop.Condition = "'$(" + MinimumVisualStudioVersionProperty + ")' == ''";
                    }

                    break;
                }
            }

            // new FSharp.Core was added - can delete the old reference
            if (newFSharpCoreItem != null && fsharpCoreItem != null)
            {
                fsharpCoreItem.Parent.RemoveChild(fsharpCoreItem);
            }


            return true;
        }