diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index bf272a3d2..22e847230 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -127,18 +127,19 @@ namespace ICSharpCode.Decompiler.CSharp return WriteProjectFile(projectFileWriter, files, moduleDefinition); } - enum LanguageTargets - { - None, - Portable - } - #region WriteProjectFile ProjectId WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) { const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; string platformName = GetPlatformName(module); Guid guid = this.ProjectGuid ?? Guid.NewGuid(); + var targetFramework = DetectTargetFramework(module); + + List typeGuids = new List(); + if (targetFramework.IsPortableClassLibrary) + typeGuids.Add(ProjectTypeGuids.PortableLibrary); + typeGuids.Add(ProjectTypeGuids.CSharpWindows); + // TODO: .NET core support using (XmlTextWriter w = new XmlTextWriter(writer)) { w.Formatting = Formatting.Indented; @@ -149,6 +150,7 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteStartElement("PropertyGroup"); w.WriteElementString("ProjectGuid", guid.ToString("B").ToUpperInvariant()); + w.WriteElementString("ProjectTypeGuids", string.Join(";", typeGuids.Select(g => g.ToString("B").ToUpperInvariant()))); w.WriteStartElement("Configuration"); w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); @@ -179,53 +181,12 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteElementString("LangVersion", LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); w.WriteElementString("AssemblyName", module.Name); - bool useTargetFrameworkAttribute = false; - LanguageTargets languageTargets = LanguageTargets.None; - string targetFramework = module.Reader.DetectTargetFrameworkId(); - int frameworkVersionNumber = 0; - if (!string.IsNullOrEmpty(targetFramework)) { - string[] frameworkParts = targetFramework.Split(','); - 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; - } - } + if (targetFramework.TargetFrameworkIdentifier != null) + w.WriteElementString("TargetFrameworkIdentifier", targetFramework.TargetFrameworkIdentifier); + if (targetFramework.TargetFrameworkVersion != null) + w.WriteElementString("TargetFrameworkVersion", targetFramework.TargetFrameworkVersion); + if (targetFramework.TargetFrameworkProfile != null) + w.WriteElementString("TargetFrameworkProfile", targetFramework.TargetFrameworkProfile); w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); @@ -239,7 +200,7 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteStartElement("PropertyGroup"); // platform-specific w.WriteAttributeString("Condition", " '$(Platform)' == '" + 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.WriteEndElement(); // (platform-specific) @@ -286,17 +247,14 @@ namespace ICSharpCode.Decompiler.CSharp } w.WriteEndElement(); } - switch (languageTargets) { - case LanguageTargets.Portable: - w.WriteStartElement("Import"); - w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); - w.WriteEndElement(); - break; - default: - w.WriteStartElement("Import"); - w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); - w.WriteEndElement(); - break; + if (targetFramework.IsPortableClassLibrary) { + w.WriteStartElement("Import"); + w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); + w.WriteEndElement(); + } else { + w.WriteStartElement("Import"); + w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); + w.WriteEndElement(); } w.WriteEndDocument(); @@ -305,6 +263,56 @@ namespace ICSharpCode.Decompiler.CSharp 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) { return false;