File: AssemblyUtilities.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis;
 
namespace Roslyn.Utilities
{
    internal static partial class AssemblyUtilities
    {
        /// <summary>
        /// Given a path to an assembly, finds the paths to all of its satellite
        /// assemblies.
        /// </summary>
        /// <exception cref="IOException">If the file at <paramref name="filePath"/> does not exist or cannot be accessed.</exception>
        /// <exception cref="BadImageFormatException">If the file is not an assembly or is somehow corrupted.</exception>
        public static ImmutableArray<string> FindSatelliteAssemblies(string filePath)
        {
            Debug.Assert(PathUtilities.IsAbsolute(filePath));
 
            var builder = ImmutableArray.CreateBuilder<string>();
 
            string? directory = Path.GetDirectoryName(filePath);
            RoslynDebug.AssertNotNull(directory);
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
            string resourcesNameWithoutExtension = fileNameWithoutExtension + ".resources";
            string resourcesNameWithExtension = resourcesNameWithoutExtension + ".dll";
 
            foreach (var subDirectory in Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly))
            {
                string satelliteAssemblyPath = Path.Combine(subDirectory, resourcesNameWithExtension);
                if (File.Exists(satelliteAssemblyPath))
                {
                    builder.Add(satelliteAssemblyPath);
                }
 
                satelliteAssemblyPath = Path.Combine(subDirectory, resourcesNameWithoutExtension, resourcesNameWithExtension);
                if (File.Exists(satelliteAssemblyPath))
                {
                    builder.Add(satelliteAssemblyPath);
                }
            }
 
            return builder.ToImmutable();
        }
 
        /// <summary>
        /// Given a path to an assembly and a set of paths to possible dependencies,
        /// identifies which of the assembly's references are missing. May throw.
        /// </summary>
        /// <exception cref="IOException">If the files does not exist or cannot be accessed.</exception>
        /// <exception cref="BadImageFormatException">If one of the files is not an assembly or is somehow corrupted.</exception>
        public static ImmutableArray<AssemblyIdentity> IdentifyMissingDependencies(string assemblyPath, IEnumerable<string> dependencyFilePaths)
        {
            RoslynDebug.Assert(PathUtilities.IsAbsolute(assemblyPath));
            RoslynDebug.Assert(dependencyFilePaths != null);
 
            HashSet<AssemblyIdentity> assemblyDefinitions = new HashSet<AssemblyIdentity>();
            foreach (var potentialDependency in dependencyFilePaths)
            {
                using (var reader = new PEReader(FileUtilities.OpenRead(potentialDependency)))
                {
                    var metadataReader = reader.GetMetadataReader();
                    var assemblyDefinition = metadataReader.ReadAssemblyIdentityOrThrow();
 
                    assemblyDefinitions.Add(assemblyDefinition);
                }
            }
 
            HashSet<AssemblyIdentity> assemblyReferences = new HashSet<AssemblyIdentity>();
            using (var reader = new PEReader(FileUtilities.OpenRead(assemblyPath)))
            {
                var metadataReader = reader.GetMetadataReader();
 
                var references = metadataReader.GetReferencedAssembliesOrThrow();
 
                assemblyReferences.AddAll(references);
            }
 
            assemblyReferences.ExceptWith(assemblyDefinitions);
 
            return ImmutableArray.CreateRange(assemblyReferences);
        }
 
        /// <summary>
        /// Given a path to an assembly, returns the <see cref="AssemblyIdentity"/> for the assembly.
        ///  May throw.
        /// </summary>
        /// <exception cref="IOException">If the file at <paramref name="assemblyPath"/> does not exist or cannot be accessed.</exception>
        /// <exception cref="BadImageFormatException">If the file is not an assembly or is somehow corrupted.</exception>
        public static AssemblyIdentity GetAssemblyIdentity(string assemblyPath)
        {
            Debug.Assert(PathUtilities.IsAbsolute(assemblyPath));
 
            using (var reader = new PEReader(FileUtilities.OpenRead(assemblyPath)))
            {
                var metadataReader = reader.GetMetadataReader();
                var assemblyIdentity = metadataReader.ReadAssemblyIdentityOrThrow();
 
                return assemblyIdentity;
            }
        }
    }
}