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