| File: parent\Common\FileUtilities.MetadataReader.cs | Web Access |
| Project: Microsoft.NET.Build.Extensions.Tasks.csproj (Microsoft.NET.Build.Extensions.Tasks) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Use MetadataReader version of GetAssemblyVersion for: // - netcoreapp version of Microsoft.NET.Build.Extensions.Tasks // - All versions of Microsoft.NET.Build.Tasks // We don't use it for the .NET Framework version of Microsoft.NET.Build.Extensions in order to // avoid loading the System.Reflection.Metadata assembly in vanilla .NET Framework build scenarios // We do use the MetadataReader version for the SDK tasks in order to correctly read the assembly // versions of cross-gened assemblies. See https://github.com/dotnet/sdk/issues/1502 #if NETCOREAPP || !EXTENSIONS using System.Collections.Concurrent; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; namespace Microsoft.NET.Build.Tasks { static partial class FileUtilities { private static readonly ConcurrentDictionary<string, (DateTime LastKnownWriteTimeUtc, Version? Version)> s_versionCache = new(StringComparer.OrdinalIgnoreCase /* Not strictly correct on *nix. Fix? */); private static Version? GetAssemblyVersion(string sourcePath) { DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(sourcePath); if (s_versionCache.TryGetValue(sourcePath, out var cacheEntry) && lastWriteTimeUtc == cacheEntry.LastKnownWriteTimeUtc) { return cacheEntry.Version; } Version? version = GetAssemblyVersionFromFile(sourcePath); s_versionCache[sourcePath] = (lastWriteTimeUtc, version); // When introducing this cache, we decided that the cached // data was small and likely to be basically finite for // any given MSBuild process lifetime, so we did not implement // a cache lifetime. If you're here because those assumptions // don't hold, there's no reason the cache must be static // and monotonically growing. return version; static Version? GetAssemblyVersionFromFile(string sourcePath) { using (var assemblyStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) { Version? result = null; try { using (PEReader peReader = new(assemblyStream, PEStreamOptions.LeaveOpen)) { if (peReader.HasMetadata) { MetadataReader reader = peReader.GetMetadataReader(); if (reader.IsAssembly) { result = reader.GetAssemblyDefinition().Version; } } } } catch (BadImageFormatException) { // not a PE } return result; } } } } } #endif