Browse Source

Fix #1958: Emit ProjectTypeGuids when generating .csproj

For portable class libraries, the type GUID is required so that Visual Studio for Mac can open the project.
pull/1968/head
Daniel Grunwald 6 years ago
parent
commit
ff40fbd984
  1. 138
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

138
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -127,18 +127,19 @@ namespace ICSharpCode.Decompiler.CSharp
return WriteProjectFile(projectFileWriter, files, moduleDefinition); return WriteProjectFile(projectFileWriter, files, moduleDefinition);
} }
enum LanguageTargets
{
None,
Portable
}
#region WriteProjectFile #region WriteProjectFile
ProjectId WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, Metadata.PEFile module) ProjectId WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, Metadata.PEFile module)
{ {
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
string platformName = GetPlatformName(module); string platformName = GetPlatformName(module);
Guid guid = this.ProjectGuid ?? Guid.NewGuid(); Guid guid = this.ProjectGuid ?? Guid.NewGuid();
var targetFramework = DetectTargetFramework(module);
List<Guid> typeGuids = new List<Guid>();
if (targetFramework.IsPortableClassLibrary)
typeGuids.Add(ProjectTypeGuids.PortableLibrary);
typeGuids.Add(ProjectTypeGuids.CSharpWindows);
// TODO: .NET core support
using (XmlTextWriter w = new XmlTextWriter(writer)) { using (XmlTextWriter w = new XmlTextWriter(writer)) {
w.Formatting = Formatting.Indented; w.Formatting = Formatting.Indented;
@ -149,6 +150,7 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteStartElement("PropertyGroup"); w.WriteStartElement("PropertyGroup");
w.WriteElementString("ProjectGuid", guid.ToString("B").ToUpperInvariant()); w.WriteElementString("ProjectGuid", guid.ToString("B").ToUpperInvariant());
w.WriteElementString("ProjectTypeGuids", string.Join(";", typeGuids.Select(g => g.ToString("B").ToUpperInvariant())));
w.WriteStartElement("Configuration"); w.WriteStartElement("Configuration");
w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); w.WriteAttributeString("Condition", " '$(Configuration)' == '' ");
@ -179,53 +181,12 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteElementString("LangVersion", LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); w.WriteElementString("LangVersion", LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.'));
w.WriteElementString("AssemblyName", module.Name); w.WriteElementString("AssemblyName", module.Name);
bool useTargetFrameworkAttribute = false; if (targetFramework.TargetFrameworkIdentifier != null)
LanguageTargets languageTargets = LanguageTargets.None; w.WriteElementString("TargetFrameworkIdentifier", targetFramework.TargetFrameworkIdentifier);
string targetFramework = module.Reader.DetectTargetFrameworkId(); if (targetFramework.TargetFrameworkVersion != null)
int frameworkVersionNumber = 0; w.WriteElementString("TargetFrameworkVersion", targetFramework.TargetFrameworkVersion);
if (!string.IsNullOrEmpty(targetFramework)) { if (targetFramework.TargetFrameworkProfile != null)
string[] frameworkParts = targetFramework.Split(','); w.WriteElementString("TargetFrameworkProfile", targetFramework.TargetFrameworkProfile);
string frameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase) && !a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase));
if (frameworkIdentifier != null) {
w.WriteElementString("TargetFrameworkIdentifier", frameworkIdentifier);
switch (frameworkIdentifier) {
case ".NETPortable":
languageTargets = LanguageTargets.Portable;
break;
}
}
string frameworkVersion = frameworkParts.FirstOrDefault(a => a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase));
if (frameworkVersion != null) {
w.WriteElementString("TargetFrameworkVersion", frameworkVersion.Substring("Version=".Length));
useTargetFrameworkAttribute = true;
frameworkVersionNumber = int.Parse(frameworkVersion.Substring("Version=v".Length).Replace(".", ""));
if (frameworkVersionNumber < 100) frameworkVersionNumber *= 10;
}
string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase));
if (frameworkProfile != null)
w.WriteElementString("TargetFrameworkProfile", frameworkProfile.Substring("Profile=".Length));
}
if (!useTargetFrameworkAttribute) {
switch (module.GetRuntime()) {
case Metadata.TargetRuntime.Net_1_0:
frameworkVersionNumber = 100;
w.WriteElementString("TargetFrameworkVersion", "v1.0");
break;
case Metadata.TargetRuntime.Net_1_1:
frameworkVersionNumber = 110;
w.WriteElementString("TargetFrameworkVersion", "v1.1");
break;
case Metadata.TargetRuntime.Net_2_0:
frameworkVersionNumber = 200;
w.WriteElementString("TargetFrameworkVersion", "v2.0");
// TODO: Detect when .NET 3.0/3.5 is required
break;
default:
frameworkVersionNumber = 400;
w.WriteElementString("TargetFrameworkVersion", "v4.0");
break;
}
}
w.WriteElementString("WarningLevel", "4"); w.WriteElementString("WarningLevel", "4");
w.WriteElementString("AllowUnsafeBlocks", "True"); w.WriteElementString("AllowUnsafeBlocks", "True");
@ -239,7 +200,7 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteStartElement("PropertyGroup"); // platform-specific w.WriteStartElement("PropertyGroup"); // platform-specific
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' ");
w.WriteElementString("PlatformTarget", platformName); w.WriteElementString("PlatformTarget", platformName);
if (frameworkVersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) { if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) {
w.WriteElementString("Prefer32Bit", "false"); w.WriteElementString("Prefer32Bit", "false");
} }
w.WriteEndElement(); // </PropertyGroup> (platform-specific) w.WriteEndElement(); // </PropertyGroup> (platform-specific)
@ -286,17 +247,14 @@ namespace ICSharpCode.Decompiler.CSharp
} }
w.WriteEndElement(); w.WriteEndElement();
} }
switch (languageTargets) { if (targetFramework.IsPortableClassLibrary) {
case LanguageTargets.Portable: w.WriteStartElement("Import");
w.WriteStartElement("Import"); w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets");
w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); w.WriteEndElement();
w.WriteEndElement(); } else {
break; w.WriteStartElement("Import");
default: w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets");
w.WriteStartElement("Import"); w.WriteEndElement();
w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets");
w.WriteEndElement();
break;
} }
w.WriteEndDocument(); w.WriteEndDocument();
@ -305,6 +263,56 @@ namespace ICSharpCode.Decompiler.CSharp
return new ProjectId(platformName, guid, ProjectTypeGuids.CSharpWindows); return new ProjectId(platformName, guid, ProjectTypeGuids.CSharpWindows);
} }
struct TargetFramework
{
public string TargetFrameworkIdentifier;
public string TargetFrameworkVersion;
public string TargetFrameworkProfile;
public int VersionNumber;
public bool IsPortableClassLibrary => TargetFrameworkIdentifier == ".NETPortable";
}
private TargetFramework DetectTargetFramework(PEFile module)
{
TargetFramework result = default;
switch (module.GetRuntime()) {
case Metadata.TargetRuntime.Net_1_0:
result.VersionNumber = 100;
result.TargetFrameworkVersion = "v1.0";
break;
case Metadata.TargetRuntime.Net_1_1:
result.VersionNumber = 110;
result.TargetFrameworkVersion = "v1.1";
break;
case Metadata.TargetRuntime.Net_2_0:
result.VersionNumber = 200;
result.TargetFrameworkVersion = "v2.0";
// TODO: Detect when .NET 3.0/3.5 is required
break;
default:
result.VersionNumber = 400;
result.TargetFrameworkVersion = "v4.0";
break;
}
string targetFramework = module.Reader.DetectTargetFrameworkId();
if (!string.IsNullOrEmpty(targetFramework)) {
string[] frameworkParts = targetFramework.Split(',');
result.TargetFrameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase) && !a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase));
string frameworkVersion = frameworkParts.FirstOrDefault(a => a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase));
if (frameworkVersion != null) {
result.TargetFrameworkVersion = frameworkVersion.Substring("Version=".Length);
result.VersionNumber = int.Parse(frameworkVersion.Substring("Version=v".Length).Replace(".", ""));
if (result.VersionNumber < 100) result.VersionNumber *= 10;
}
string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase));
if (frameworkProfile != null)
result.TargetFrameworkProfile = frameworkProfile.Substring("Profile=".Length);
}
return result;
}
protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm) protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm)
{ {
return false; return false;

Loading…
Cancel
Save