|
// 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.Reflection.Metadata;
using System.Reflection.PortableExecutable;
namespace Microsoft.NET.Build.Tasks
{
static partial class FileUtilities
{
private static Dictionary<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
|