diff --git a/src/Internal.AspNetCore.BuildTools.Tasks/FindUnusedReferences.cs b/src/Internal.AspNetCore.BuildTools.Tasks/FindUnusedReferences.cs new file mode 100644 index 000000000..e027cedc4 --- /dev/null +++ b/src/Internal.AspNetCore.BuildTools.Tasks/FindUnusedReferences.cs @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.BuildTools +{ +#if SDK + public class Sdk_FindUnusedReferences : Task +#elif BuildTools + public class FindUnusedReferences : Task +#else +#error This must be built either for an SDK or for BuildTools +#endif + { + /// + /// IntermediateAssembly from CoreCompile + /// + [Required] + public string Assembly { get; set; } + + /// + /// ReferencePath from CoreCompile + /// + [Required] + public ITaskItem[] References { get; set; } + + /// + /// FileDefinitions from RunResolvePackageDependencies + /// + [Required] + public ITaskItem[] Packages { get; set; } + + /// + /// PackageDependencies from RunResolvePackageDependencies + /// + [Required] + public ITaskItem[] Files { get; set; } + + [Output] + public ITaskItem[] UnusedReferences { get; set; } + + public override bool Execute() + { + var references = new HashSet(References.Select(item => item.ItemSpec), StringComparer.OrdinalIgnoreCase); + var referenceFiles = Files.Where(file => references.Contains(file.GetMetadata("ResolvedPath"))) + .ToDictionary(item => Path.GetFileNameWithoutExtension(item.GetMetadata("ResolvedPath")), StringComparer.OrdinalIgnoreCase); + + var directReferences = new HashSet( + Packages.Where(p => string.IsNullOrEmpty(p.GetMetadata("ParentPackage"))).Select(i => i.ItemSpec), + StringComparer.OrdinalIgnoreCase); + + using (var fileStream = File.OpenRead(Assembly)) + using (var reader = new PEReader(fileStream)) + { + var metadataReader = reader.GetMetadataReader(); + foreach (AssemblyReferenceHandle assemblyReferenceHandle in metadataReader.AssemblyReferences) + { + var assemblyReference = metadataReader.GetAssemblyReference(assemblyReferenceHandle); + var name = metadataReader.GetString(assemblyReference.Name); + if (referenceFiles.TryGetValue(name, out var fileItem)) + { + var packageName = fileItem.GetMetadata("PackageName") + "/" + fileItem.GetMetadata("PackageVersion"); + directReferences.Remove(packageName); + referenceFiles.Remove(name); + } + + } + } + + UnusedReferences = referenceFiles.Values.Where(f => directReferences.Any(r => f.ItemSpec.StartsWith(r))).ToArray(); + return true; + } + } +} diff --git a/src/Internal.AspNetCore.BuildTools.Tasks/Internal.AspNetCore.BuildTools.Tasks.csproj b/src/Internal.AspNetCore.BuildTools.Tasks/Internal.AspNetCore.BuildTools.Tasks.csproj index b6e6545ec..d4404dd61 100644 --- a/src/Internal.AspNetCore.BuildTools.Tasks/Internal.AspNetCore.BuildTools.Tasks.csproj +++ b/src/Internal.AspNetCore.BuildTools.Tasks/Internal.AspNetCore.BuildTools.Tasks.csproj @@ -34,6 +34,8 @@ + + @@ -41,4 +43,17 @@ + + + + <_PackageFiles Include="bin\$(Configuration)\*\System.Reflection.Metadata.dll;bin\$(Configuration)\*\System.Collections.Immutable.dll"> + tools\%(RecursiveDir) + false + Content + + + diff --git a/src/Internal.AspNetCore.BuildTools.Tasks/build/Tasks.tasks b/src/Internal.AspNetCore.BuildTools.Tasks/build/Tasks.tasks index 1a3476b87..b92f4df66 100644 --- a/src/Internal.AspNetCore.BuildTools.Tasks/build/Tasks.tasks +++ b/src/Internal.AspNetCore.BuildTools.Tasks/build/Tasks.tasks @@ -1,4 +1,4 @@ - + <_BuildToolsAssemblyTfm Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard1.6 @@ -17,6 +17,7 @@ + diff --git a/src/Internal.AspNetCore.Sdk.Tasks/Internal.AspNetCore.Sdk.Tasks.csproj b/src/Internal.AspNetCore.Sdk.Tasks/Internal.AspNetCore.Sdk.Tasks.csproj index 43c404363..6ceaf38af 100644 --- a/src/Internal.AspNetCore.Sdk.Tasks/Internal.AspNetCore.Sdk.Tasks.csproj +++ b/src/Internal.AspNetCore.Sdk.Tasks/Internal.AspNetCore.Sdk.Tasks.csproj @@ -44,6 +44,9 @@ + + + @@ -51,4 +54,17 @@ + + + + <_PackageFiles Include="bin\$(Configuration)\*\System.Reflection.Metadata.dll;bin\$(Configuration)\*\System.Collections.Immutable.dll"> + tools\%(RecursiveDir) + false + Content + + + diff --git a/src/Internal.AspNetCore.Sdk/build/FindUnusedReferences.targets b/src/Internal.AspNetCore.Sdk/build/FindUnusedReferences.targets new file mode 100644 index 000000000..84b95d24a --- /dev/null +++ b/src/Internal.AspNetCore.Sdk/build/FindUnusedReferences.targets @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/src/Internal.AspNetCore.Sdk/build/Internal.AspNetCore.Sdk.targets b/src/Internal.AspNetCore.Sdk/build/Internal.AspNetCore.Sdk.targets index 30944f3ae..59809fc60 100644 --- a/src/Internal.AspNetCore.Sdk/build/Internal.AspNetCore.Sdk.targets +++ b/src/Internal.AspNetCore.Sdk/build/Internal.AspNetCore.Sdk.targets @@ -5,6 +5,7 @@ for use outside of Microsoft. +