diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs index 985dbdd03..5b2a77dec 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/FrameworkReferenceResolver.cs @@ -231,7 +231,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution return null; } - return GetFrameworkInformation(version, targetFramework); + return GetFrameworkInformation(version, targetFramework, referenceAssembliesPath); } private static FrameworkInformation GetLegacyFrameworkInformation(NuGetFramework targetFramework, string referenceAssembliesPath) @@ -294,7 +294,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution var dir = new DirectoryInfo(searchPaths[i]); if (dir.Exists) { - PopulateFromRedistList(dir, frameworkInfo); + PopulateFromRedistList(dir, targetFramework, referenceAssembliesPath, frameworkInfo); } } @@ -323,7 +323,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution return targetFramework.ToString(); } - private static FrameworkInformation GetFrameworkInformation(DirectoryInfo directory, NuGetFramework targetFramework) + private static FrameworkInformation GetFrameworkInformation(DirectoryInfo directory, NuGetFramework targetFramework, string referenceAssembliesPath) { var frameworkInfo = new FrameworkInformation(); frameworkInfo.Exists = true; @@ -333,7 +333,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution Path.Combine(frameworkInfo.Path, "Facades") }; - PopulateFromRedistList(directory, frameworkInfo); + PopulateFromRedistList(directory, targetFramework, referenceAssembliesPath, frameworkInfo); if (string.IsNullOrEmpty(frameworkInfo.Name)) { frameworkInfo.Name = SynthesizeFrameworkFriendlyName(targetFramework); @@ -341,7 +341,7 @@ namespace Microsoft.DotNet.ProjectModel.Resolution return frameworkInfo; } - private static void PopulateFromRedistList(DirectoryInfo directory, FrameworkInformation frameworkInfo) + private static void PopulateFromRedistList(DirectoryInfo directory, NuGetFramework targetFramework, string referenceAssembliesPath, FrameworkInformation frameworkInfo) { // The redist list contains the list of assemblies for this target framework string redistList = Path.Combine(directory.FullName, "RedistList", "FrameworkList.xml"); @@ -354,11 +354,36 @@ namespace Microsoft.DotNet.ProjectModel.Resolution { var frameworkList = XDocument.Load(stream); + // Remember the original search paths, because we might need them later + var originalSearchPaths = frameworkInfo.SearchPaths; + + // There are some frameworks, that "inherit" from a base framework, like + // e.g. .NET 4.0.3, and MonoAndroid. + var includeFrameworkVersion = frameworkList.Root.Attribute("IncludeFramework")?.Value; + if (includeFrameworkVersion != null) + { + // Get the NuGetFramework identifier for the framework to include + var includeFramework = NuGetFramework.Parse($"{targetFramework.Framework}, Version={includeFrameworkVersion}"); + + // Recursively call the code to get the framework information + var includeFrameworkInfo = GetFrameworkInformation(includeFramework, referenceAssembliesPath); + + // Append the search paths of the included framework + frameworkInfo.SearchPaths = frameworkInfo.SearchPaths.Concat(includeFrameworkInfo.SearchPaths).ToArray(); + + // Add the assemblies of the included framework + foreach (var assemblyEntry in includeFrameworkInfo.Assemblies) + { + frameworkInfo.Assemblies[assemblyEntry.Key] = assemblyEntry.Value; + } + } + // On mono, the RedistList.xml has an entry pointing to the TargetFrameworkDirectory // It basically uses the GAC as the reference assemblies for all .NET framework // profiles var targetFrameworkDirectory = frameworkList.Root.Attribute("TargetFrameworkDirectory")?.Value; + IEnumerable populateFromPaths; if (!string.IsNullOrEmpty(targetFrameworkDirectory)) { // For some odd reason, the paths are actually listed as \ so normalize them here @@ -370,13 +395,20 @@ namespace Microsoft.DotNet.ProjectModel.Resolution // Update the path to the framework frameworkInfo.Path = resovledPath; - PopulateAssemblies(frameworkInfo.Assemblies, resovledPath); - PopulateAssemblies(frameworkInfo.Assemblies, Path.Combine(resovledPath, "Facades")); + populateFromPaths = new List + { + resovledPath, + Path.Combine(resovledPath, "Facades") + }; } else { + var emptyFileElements = true; foreach (var e in frameworkList.Root.Elements()) { + // Remember that we had at least one framework assembly + emptyFileElements = false; + var assemblyName = e.Attribute("AssemblyName").Value; var version = e.Attribute("Version")?.Value; @@ -384,6 +416,27 @@ namespace Microsoft.DotNet.ProjectModel.Resolution entry.Version = version != null ? Version.Parse(version) : null; frameworkInfo.Assemblies[assemblyName] = entry; } + + if (emptyFileElements) + { + // When we haven't found any file elements, we probably processed a + // Mono/Xamarin FrameworkList.xml. That means, that we have to + // populate the assembly list from the files. + populateFromPaths = originalSearchPaths; + } + else + { + populateFromPaths = null; + } + } + + // Do we need to populate from search paths? + if (populateFromPaths != null) + { + foreach (var populateFromPath in populateFromPaths) + { + PopulateAssemblies(frameworkInfo.Assemblies, populateFromPath); + } } var nameAttribute = frameworkList.Root.Attribute("Name");