Skip to content

Commit

Permalink
Add support for numerous NuGet, handling inheritance (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
glennawatson authored Jun 19, 2019
1 parent fba8713 commit 047c03f
Show file tree
Hide file tree
Showing 90 changed files with 193,191 additions and 50,860 deletions.
50 changes: 50 additions & 0 deletions src/Pharmacist.Benchmarks/NuGetHelperBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using BenchmarkDotNet.Attributes;

using NuGet.Frameworks;
using NuGet.Packaging.Core;
using NuGet.Versioning;

using Pharmacist.Core.NuGet;

namespace Pharmacist.Benchmarks
{
[ClrJob]
////[CoreJob]
[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class NuGetTaskGeneratorBenchmarks
{
private static readonly string _packageDirectory = Path.Combine(Path.GetTempPath(), "Pharmacist.Benchamarks");

[IterationSetup]
public void IterationSetup()
{
try
{
Directory.Delete(_packageDirectory);
}
catch
{
}
}

[Benchmark]
public async Task MultipleDirectoryCase()
{
// NetCore contains multiple directories that match.
var package = new[] { new PackageIdentity("Microsoft.NETCore.App", new NuGetVersion("2.0.0")) };
var frameworks = new[] { FrameworkConstants.CommonFrameworks.NetCoreApp20 };

var result = (await NuGetPackageHelper
.DownloadPackageFilesAndFolder(package, frameworks, packageOutputDirectory: _packageDirectory)
.ConfigureAwait(false)).ToList();
}
}
}
2 changes: 1 addition & 1 deletion src/Pharmacist.Benchmarks/Pharmacist.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
5 changes: 2 additions & 3 deletions src/Pharmacist.Benchmarks/PlatformGeneratorBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Pharmacist.Benchmarks
/// Benchmarks for the NavigationStack and the RoutingState objects.
/// </summary>
[ClrJob]
[CoreJob]
////[CoreJob]
[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class NavigationStackBenchmark
Expand All @@ -33,8 +33,7 @@ public class NavigationStackBenchmark
[Benchmark]
public Task Navigate()
{
var platforms = Enum.GetValues(typeof(AutoPlatform)).OfType<AutoPlatform>();
return ObservablesForEventGenerator.ExtractEventsFromPlatforms(Path.GetTempPath(), Guid.NewGuid().ToString(), _referenceAssembliesLocation, platforms);
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@ public class NugetCommandLineOptions : CommandLineOptionsBase
/// </summary>
[Option('t', "target-framework", Required = true, HelpText = "Specify the Target framework to extract for.")]
public string TargetFramework { get; set; }

/// <summary>
/// Gets or sets the package folder. If not set a random folder will be used.
/// </summary>
[Option("package-folder", HelpText = "Optional folder where to place NuGet packages for processing, otherwise random temp folder will be used.")]
public string PackageFolder { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/Pharmacist.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public static async Task<int> Main(string[] args)
var packageIdentity = new PackageIdentity(options.NugetPackageName, new NuGetVersion(options.NugetVersion));
var nugetFramework = options.TargetFramework.ToFrameworks();
await ObservablesForEventGenerator.WriteHeader(stream).ConfigureAwait(false);
await ObservablesForEventGenerator.ExtractEventsFromNuGetPackages(stream, new[] { packageIdentity }, nugetFramework).ConfigureAwait(false);
await ObservablesForEventGenerator.ExtractEventsFromNuGetPackages(stream, new[] { packageIdentity }, nugetFramework, options.PackageFolder).ConfigureAwait(false);
}

return ExitCode.Success;
Expand Down
21 changes: 14 additions & 7 deletions src/Pharmacist.Core/Extractors/NuGetExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,34 @@ public class NuGetExtractor : IExtractor
/// </summary>
/// <param name="targetFrameworks">The target framework to extract in order of priority.</param>
/// <param name="packages">The packages to extract the information from.</param>
/// <param name="packageOutputDirectory">Directory for the packages, if null a random path in the temp folder will be used.</param>
/// <returns>A task to monitor the progress.</returns>
public async Task Extract(IReadOnlyCollection<NuGetFramework> targetFrameworks, IReadOnlyCollection<PackageIdentity> packages)
public async Task Extract(IReadOnlyCollection<NuGetFramework> targetFrameworks, IReadOnlyCollection<PackageIdentity> packages, string packageOutputDirectory)
{
var results = await NuGetPackageHelper.DownloadPackageFilesAndFolder(packages, targetFrameworks).ConfigureAwait(false);
var results = await NuGetPackageHelper.DownloadPackageFilesAndFolder(packages, targetFrameworks, packageOutputDirectory: packageOutputDirectory).ConfigureAwait(false);

Assemblies = new List<string>(results.SelectMany(x => x.files).Where(x => x.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)));
SearchDirectories = new List<string>(results.Select(x => x.folder));
await Extract(targetFrameworks, results).ConfigureAwait(false);
}

/// <summary>
/// Extracts the data using the specified target framework.
/// </summary>
/// <param name="targetFrameworks">The target framework to extract in order of priority.</param>
/// <param name="packages">The packages to extract the information from.</param>
/// <param name="packageOutputDirectory">Directory for the packages, if null a random path in the temp folder will be used.</param>
/// <returns>A task to monitor the progress.</returns>
public async Task Extract(IReadOnlyCollection<NuGetFramework> targetFrameworks, IReadOnlyCollection<LibraryRange> packages)
public async Task Extract(IReadOnlyCollection<NuGetFramework> targetFrameworks, IReadOnlyCollection<LibraryRange> packages, string packageOutputDirectory)
{
var results = await NuGetPackageHelper.DownloadPackageFilesAndFolder(packages, targetFrameworks).ConfigureAwait(false);
var results = await NuGetPackageHelper.DownloadPackageFilesAndFolder(packages, targetFrameworks, packageOutputDirectory: packageOutputDirectory).ConfigureAwait(false);

await Extract(targetFrameworks, results).ConfigureAwait(false);
}

private async Task Extract(IReadOnlyCollection<NuGetFramework> targetFrameworks, IReadOnlyCollection<(string folder, IReadOnlyCollection<string> files)> results)
{
var extraSearchFolders = (await Task.WhenAll(targetFrameworks.Select(x => x.GetNuGetFrameworkFolders())).ConfigureAwait(false)).SelectMany(x => x);
Assemblies = new List<string>(results.SelectMany(x => x.files).Where(x => x.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)));
SearchDirectories = new List<string>(results.Select(x => x.folder));
SearchDirectories = new List<string>(results.Select(x => x.folder).Concat(extraSearchFolders));
}
}
}
41 changes: 23 additions & 18 deletions src/Pharmacist.Core/Generation/Compilation/EventBuilderCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;

using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
Expand All @@ -29,8 +30,9 @@ internal sealed class EventBuilderCompiler : ICompilation, IDisposable
private bool _initialized;
private INamespace _rootNamespace;

public EventBuilderCompiler(IEnumerable<IModuleReference> modules, IEnumerable<string> searchDirectories)
public EventBuilderCompiler(IEnumerable<string> targetAssemblies, IEnumerable<string> searchDirectories)
{
var modules = targetAssemblies.Select(x => new PEFile(x, PEStreamOptions.PrefetchMetadata));
_knownTypeCache = new KnownTypeCache(this);
Init(modules, searchDirectories.ToList());
}
Expand Down Expand Up @@ -178,29 +180,32 @@ private void Init(IEnumerable<IModuleReference> mainAssemblies, IReadOnlyCollect

assemblyReferencesVisited.Add(currentAssemblyReference.FullName);

IModule asm;
try
{
var currentModule = currentAssemblyReference.Resolve(searchDirectories);

if (currentModule == null)
{
continue;
}
var currentModules = currentAssemblyReference.Resolve(searchDirectories);

asm = ((IModuleReference)currentModule).Resolve(context);
}
catch (InvalidOperationException)
if (currentModules.Count == 0)
{
throw new InvalidOperationException("Tried to initialize compilation with an invalid assembly reference. (Forgot to load the assembly reference ? - see CecilLoader)");
continue;
}

if (asm != null)
foreach (var currentModule in currentModules)
{
referencedAssemblies.Add(asm);
foreach (var element in asm.PEFile.AssemblyReferences)
IModule asm;
try
{
asm = ((IModuleReference)currentModule).Resolve(context);
}
catch (InvalidOperationException)
{
throw new InvalidOperationException("Tried to initialize compilation with an invalid assembly reference. (Forgot to load the assembly reference ? - see CecilLoader)");
}

if (asm != null)
{
referenceModulesToProcess.Push(element);
referencedAssemblies.Add(asm);
foreach (var element in asm.PEFile.AssemblyReferences)
{
referenceModulesToProcess.Push(element);
}
}
}
}
Expand Down
26 changes: 6 additions & 20 deletions src/Pharmacist.Core/Generation/Compilation/PathSearchExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,19 @@ internal static class PathSearchExtensions
/// <param name="reference">A reference with details about the assembly.</param>
/// <param name="targetAssemblyDirectories">The directories potentially containing the assemblies.</param>
/// <param name="parameters">Parameters to provide to the reflection system..</param>
/// <returns>The assembly definition.</returns>
public static PEFile Resolve(this IAssemblyReference reference, IReadOnlyCollection<string> targetAssemblyDirectories, PEStreamOptions parameters = PEStreamOptions.PrefetchMetadata)
/// <returns>The assembly definitions.</returns>
public static IReadOnlyCollection<PEFile> Resolve(this IAssemblyReference reference, IReadOnlyCollection<string> targetAssemblyDirectories, PEStreamOptions parameters = PEStreamOptions.PrefetchMetadata)
{
var dllName = reference.Name + ".dll";

var fullPath = targetAssemblyDirectories.Select(x => Path.Combine(x, dllName)).FirstOrDefault(File.Exists);
if (fullPath == null)
var fullPaths = targetAssemblyDirectories.Select(x => Path.Combine(x, dllName)).Where(File.Exists).ToList();
if (fullPaths.Count == 0)
{
dllName = reference.Name + ".winmd";
fullPath = targetAssemblyDirectories.Select(x => Path.Combine(x, dllName)).FirstOrDefault(File.Exists);
fullPaths = targetAssemblyDirectories.Select(x => Path.Combine(x, dllName)).Where(File.Exists).ToList();
}

// NB: This hacks WinRT's weird mscorlib to just use the regular one
// We forget why this was needed, maybe it's not needed anymore?
if (reference.Name.IndexOf("mscorlib", StringComparison.InvariantCultureIgnoreCase) >= 0 && reference.Name.Contains("255"))
{
fullPath =
Environment.ExpandEnvironmentVariables(
@"%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll");
}

if (fullPath == null)
{
return null;
}

return new PEFile(fullPath, parameters);
return fullPaths.Select(fullPath => new PEFile(fullPath, parameters)).ToList();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal static class DelegateGenerator
/// <returns>An array of namespace declarations.</returns>
internal static IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, bool isAbstract, IEnumerable<IMethod> methods)> declarations)
{
foreach (var groupedDeclarations in declarations.GroupBy(x => x.typeDefinition.Namespace))
foreach (var groupedDeclarations in declarations.GroupBy(x => x.typeDefinition.Namespace).OrderBy(x => x.Key))
{
var namespaceName = groupedDeclarations.Key;
var members = new List<ClassDeclarationSyntax>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal abstract class EventGeneratorBase : IEventGenerator
/// </summary>
/// <param name="declarations">The declarations to add.</param>
/// <returns>An array of namespace declarations.</returns>
public abstract IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, IEnumerable<IEvent> events)> declarations);
public abstract IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, ITypeDefinition baseDefinition, IEnumerable<IEvent> events)> declarations);

/// <summary>
/// Generates an observable declaration that wraps a event.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ internal interface IEventGenerator
/// </summary>
/// <param name="values">The values to generate for.</param>
/// <returns>The new compilation unit.</returns>
IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, IEnumerable<IEvent> events)> values);
IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, ITypeDefinition baseDefinition, IEnumerable<IEvent> events)> values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ internal class InstanceEventGenerator : EventGeneratorBase
{
private const string DataFieldName = "_data";

public override IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, IEnumerable<IEvent> events)> declarations)
public override IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, ITypeDefinition baseDefinition, IEnumerable<IEvent> events)> declarations)
{
foreach (var groupedDeclarations in declarations.GroupBy(x => x.typeDefinition.Namespace))
foreach (var groupedDeclarations in declarations.GroupBy(x => x.typeDefinition.Namespace).OrderBy(x => x.Key))
{
var namespaceName = groupedDeclarations.Key;
var members = new List<ClassDeclarationSyntax>();

var orderedTypeDeclarations = groupedDeclarations.OrderBy(x => x.typeDefinition.Name).ToList();

members.Add(GenerateStaticClass(namespaceName, orderedTypeDeclarations.Select(x => x.typeDefinition)));
members.AddRange(orderedTypeDeclarations.Select(x => GenerateEventWrapperClass(x.typeDefinition, x.events)).Where(x => x != null));
members.AddRange(orderedTypeDeclarations.Select(x => GenerateEventWrapperClass(x.typeDefinition, x.baseDefinition, x.events)).Where(x => x != null));

if (members.Count > 0)
{
Expand Down Expand Up @@ -93,16 +93,23 @@ private static FieldDeclarationSyntax GenerateEventWrapperField(ITypeDefinition
.WithModifiers(TokenList(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ReadOnlyKeyword)));
}

private static ClassDeclarationSyntax GenerateEventWrapperClass(ITypeDefinition typeDefinition, IEnumerable<IEvent> events)
private static ClassDeclarationSyntax GenerateEventWrapperClass(ITypeDefinition typeDefinition, ITypeDefinition baseTypeDefinition, IEnumerable<IEvent> events)
{
var members = new List<MemberDeclarationSyntax> { GenerateEventWrapperField(typeDefinition), GenerateEventWrapperClassConstructor(typeDefinition) };
members.AddRange(events.OrderBy(x => x.Name).Select(x => GenerateEventWrapperObservable(x, DataFieldName)).Where(x => x != null));

return ClassDeclaration(typeDefinition.Name + "Events")
var classDeclaration = ClassDeclaration(typeDefinition.Name + "Events")
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
.WithMembers(List(members))
.WithObsoleteAttribute(typeDefinition)
.WithLeadingTrivia(XmlSyntaxFactory.GenerateSummarySeeAlsoComment("A class which wraps the events contained within the {0} class as observables.", typeDefinition.GenerateFullGenericName()));

if (baseTypeDefinition != null)
{
classDeclaration = classDeclaration.WithBaseList(BaseList(SingletonSeparatedList<BaseTypeSyntax>(SimpleBaseType(IdentifierName($"global::{baseTypeDefinition.Namespace}.{baseTypeDefinition.Name}Events")))));
}

return classDeclaration;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ internal class StaticEventGenerator : EventGeneratorBase
/// </summary>
/// <param name="declarations">The declarations to add.</param>
/// <returns>An array of namespace declarations.</returns>
public override IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, IEnumerable<IEvent> events)> declarations)
public override IEnumerable<NamespaceDeclarationSyntax> Generate(IEnumerable<(ITypeDefinition typeDefinition, ITypeDefinition baseDefinition, IEnumerable<IEvent> events)> declarations)
{
foreach (var groupDeclaration in declarations.GroupBy(x => x.typeDefinition.Namespace))
foreach (var groupDeclaration in declarations.GroupBy(x => x.typeDefinition.Namespace).OrderBy(x => x.Key))
{
var namespaceName = groupDeclaration.Key;

Expand Down
Loading

0 comments on commit 047c03f

Please sign in to comment.