internal static string ChainReferenceAssemblyPath()

in src/Utilities/ToolLocationHelper.cs [3130:3280]


        internal static string ChainReferenceAssemblyPath(string targetFrameworkDirectory)
        {
            string path = Path.GetFullPath(targetFrameworkDirectory);

            lock (s_locker)
            {
                // Cache the results of the chain search so that we do not have to do an expensive read more than once per process per redist list.
                s_chainedReferenceAssemblyPath = s_chainedReferenceAssemblyPath ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                s_cachedTargetFrameworkDisplayNames = s_cachedTargetFrameworkDisplayNames ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

                string cachedPath = null;
                if (s_chainedReferenceAssemblyPath.TryGetValue(path, out cachedPath))
                {
                    return cachedPath;
                }
            }

            // Read in the redist list at the specified path, and return 
            // the display name and the "include framework" value for chaining.
            // If display name is not available, returns empty string.
            // If include framework is not available, returns null.
            // Caches the display name keyed by the path.

            // Make sure we have a directory with a redist list folder and a FrameworkList.xml file in there as this is what we will use for chaining.
            string redistListFolder = Path.Combine(path, "RedistList");
            string redistFile = Path.Combine(redistListFolder, "FrameworkList.xml");

            // If the redist list does not exist then the entire chain is incorrect.
            if (!File.Exists(redistFile))
            {
                // Under MONO a directory may chain to one that has no redist list
                var chainReference = NativeMethodsShared.IsMono ? string.Empty : null;
                lock (s_locker)
                {
                    s_chainedReferenceAssemblyPath[path] = chainReference;
                    s_cachedTargetFrameworkDisplayNames[path] = chainReference;
                }

                return chainReference;
            }

            string includeFramework = null;
            string displayName = null;
            string redirectPath = null;

            try
            {
                // Read in the xml file looking for the includeFramework inorder to chain.
                XmlReaderSettings readerSettings = new XmlReaderSettings();
                readerSettings.DtdProcessing = DtdProcessing.Ignore;

                using (XmlReader reader = XmlReader.Create(redistFile, readerSettings))
                {
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            if (string.Equals(reader.Name, "FileList", StringComparison.OrdinalIgnoreCase))
                            {
                                reader.MoveToFirstAttribute();
                                do
                                {
                                    if (String.Equals(reader.Name, "IncludeFramework", StringComparison.OrdinalIgnoreCase))
                                    {
                                        includeFramework = reader.Value;
                                        continue;
                                    }

                                    if (String.Equals(reader.Name, "Name", StringComparison.OrdinalIgnoreCase))
                                    {
                                        displayName = reader.Value;
                                        continue;
                                    }

                                    // Mono may redirect this to another place
                                    if (NativeMethodsShared.IsMono && String.Equals(reader.Name, "TargetFrameworkDirectory", StringComparison.OrdinalIgnoreCase))
                                    {
                                        // The new folder is relative to the place where the FrameworkList.
                                        redirectPath = Path.GetFullPath(Path.Combine(redistListFolder, FileUtilities.FixFilePath(reader.Value)));
                                    }
                                }
                                while (reader.MoveToNextAttribute());
                                reader.MoveToElement();
                                break;
                            }
                        }
                    }
                }
            }
            catch (XmlException ex)
            {
                ErrorUtilities.ThrowInvalidOperation("ToolsLocationHelper.InvalidRedistFile", redistFile, ex.Message);
            }
            catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex))
            {
                ErrorUtilities.ThrowInvalidOperation("ToolsLocationHelper.InvalidRedistFile", redistFile, ex.Message);
            }

            // Cache the display name if we have one
            if (displayName != null)
            {
                lock (s_locker)
                {
                    s_cachedTargetFrameworkDisplayNames[path] = displayName;
                }
            }

            string pathToReturn = String.Empty;

            try
            {
                // The IncludeFramework element could not be found so our chain is done.
                if (!String.IsNullOrEmpty(includeFramework))
                {
                    // Take the path which should point to something like  c:\ProgramFiles\ReferenceAssemblies\Framework\.NETFramework\v4.1
                    // We will take the path, to "up" a directory then append the name found in the redist. For example if the redist list had v4.0 
                    // the path which would be expected would be c:\ProgramFiles\ReferenceAssemblies\Framework\.NETFramework\v4.0
                    pathToReturn = path;
                    pathToReturn = Directory.GetParent(pathToReturn).FullName;
                    pathToReturn = Path.Combine(pathToReturn, includeFramework);
                    pathToReturn = Path.GetFullPath(pathToReturn);

                    // The directory which we are chaining to does not exist, return null indicating the chain is incorrect.
                    if (!Directory.Exists(pathToReturn))
                    {
                        pathToReturn = null;
                    }
                }
                // We may also have a redirect path
                else if (!string.IsNullOrEmpty(redirectPath) && Directory.Exists(redirectPath))
                {
                    pathToReturn = redirectPath;
                }

                lock (s_locker)
                {
                    s_chainedReferenceAssemblyPath[path] = pathToReturn;
                }

                return pathToReturn;
            }
            catch (Exception e)
            {
                if (ExceptionHandling.IsCriticalException(e))
                    throw;

                ErrorUtilities.ThrowInvalidOperation("ToolsLocationHelper.CouldNotCreateChain", path, pathToReturn, e.Message);
            }

            return null;
        }