public static async Task <CrawlerResults> CrawlAsync(NuGetFeed feed, PackageIdentity packageId) { ArgumentNullException.ThrowIfNull(feed); ArgumentNullException.ThrowIfNull(packageId); var crawler = new AssemblyCrawler(); using var reader = await feed.GetPackageAsync(packageId).ConfigureAwait(false); foreach (var packagePath in reader.GetFiles()) { #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await using var assemblyStream = reader.GetStream(packagePath); await using var memoryStream = new MemoryStream(); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task await assemblyStream.CopyToAsync(memoryStream).ConfigureAwait(false); memoryStream.Position = 0; using var env = new HostEnvironment(); var assembly = env.LoadAssemblyFrom(packagePath, memoryStream); if (assembly is null) { continue; } crawler.Crawl(assembly); } return(crawler.CreateResults()); }
public static async Task <CrawlerResults> CrawlAsync(NuGetFeed feed, PackageIdentity packageId) { var crawler = new AssemblyCrawler(); var reader = await feed.GetPackageAsync(packageId); foreach (var packagePath in reader.GetFiles()) { await using var assemblyStream = reader.GetStream(packagePath); await using var memoryStream = new MemoryStream(); await assemblyStream.CopyToAsync(memoryStream); memoryStream.Position = 0; var env = new HostEnvironment(); var assembly = env.LoadAssemblyFrom(packagePath, memoryStream); if (assembly is null) { continue; } crawler.Crawl(assembly); } return(crawler.GetResults()); }
private static void Check(IAssembly assembly, IReadOnlyDictionary <string, int> expectedResultsText) { var expectedResults = expectedResultsText.ToDictionary(kv => new ApiKey(kv.Key), kv => kv.Value); var crawler = new AssemblyCrawler(); crawler.Crawl(assembly); var actualResults = crawler.GetResults().Data; var messageBuilder = new StringBuilder(); foreach (var key in expectedResults.Keys.Where(k => actualResults.ContainsKey(k) && expectedResults[k] != actualResults[k])) { if (_autoGeneratedKeys.Contains(key.DocumentationId)) { continue; } messageBuilder.AppendLine($"{key} was expected to have a value {expectedResults[key]} but was {actualResults[key]}."); } foreach (var key in expectedResults.Keys.Where(k => !actualResults.ContainsKey(k))) { if (_autoGeneratedKeys.Contains(key.DocumentationId)) { continue; } messageBuilder.AppendLine($"{key} was expected but is missing."); } foreach (var key in actualResults.Keys.Where(k => !expectedResults.ContainsKey(k))) { if (_autoGeneratedKeys.Contains(key.DocumentationId)) { continue; } messageBuilder.AppendLine($"{key} was not expected."); } if (messageBuilder.Length > 0) { throw new Exception(messageBuilder.ToString()); } }
public static void Run(ApiCatalogModel catalog, IReadOnlyCollection <string> filePaths, IReadOnlyList <NuGetFramework> frameworks, bool analyzeObsoletion, IReadOnlyList <string> platforms, Action <AssemblyResult> resultReceiver) { var apiByGuid = catalog.GetAllApis().ToDictionary(a => a.Guid); var availabilityContext = ApiAvailabilityContext.Create(catalog); var platformContexts = frameworks.Select(fx => PlatformAnnotationContext.Create(availabilityContext, fx.GetShortFolderName())) .ToDictionary(pc => pc.Framework); foreach (var api in catalog.GetAllApis()) { var forwardedApi = catalog.GetForwardedApi(api); if (forwardedApi is not null) { apiByGuid[api.Guid] = forwardedApi.Value; } } var apiAvailability = new ConcurrentDictionary <ApiModel, ApiAvailability>(); var resultSink = new BlockingCollection <AssemblyResult>(); var resultSinkTask = Task.Run(() => { foreach (var result in resultSink.GetConsumingEnumerable()) { resultReceiver(result); } }); Parallel.ForEach(filePaths, filePath => { using var env = new HostEnvironment(); var assembly = env.LoadAssemblyFrom(filePath); var assemblyName = assembly is not null ? assembly.Name.Value : Path.GetFileName(filePath); if (assembly is null) { var result = new AssemblyResult(assemblyName, "Not a valid .NET assembly", Array.Empty <ApiResult>()); resultSink.Add(result); } else { var assemblyTfm = assembly.GetTargetFrameworkMoniker(); var assemblyFramework = string.IsNullOrEmpty(assemblyTfm) ? null : NuGetFramework.Parse(assemblyTfm); var crawler = new AssemblyCrawler(); crawler.Crawl(assembly); var crawlerResults = crawler.GetResults(); var apiResults = new List <ApiResult>(); var frameworkResultBuilder = new List <FrameworkResult>(frameworks.Count); var platformResultBuilder = new List <PlatformResult?>(platforms.Count); foreach (var apiKey in crawlerResults.Data.Keys) { if (apiByGuid.TryGetValue(apiKey.Guid, out var api)) { var availability = apiAvailability.GetOrAdd(api, a => availabilityContext.GetAvailability(a)); frameworkResultBuilder.Clear(); foreach (var framework in frameworks) { // Analyze availability AvailabilityResult availabilityResult; var infos = availability.Frameworks.Where(fx => fx.Framework == framework).ToArray(); // NOTE: There are APIs that exist in multiple places in-box, e.g. Microsoft.Windows.Themes.ListBoxChrome. // It doesn't really matter for our purposes. Either way, we'll pick the first one. var info = infos.FirstOrDefault(i => i.IsInBox) ?? infos.FirstOrDefault(i => !i.IsInBox); if (info is null) { availabilityResult = AvailabilityResult.Unavailable; } else if (info.IsInBox) { availabilityResult = AvailabilityResult.AvailableInBox; } else { availabilityResult = AvailabilityResult.AvailableInPackage(info.Package.Value); } // Analyze obsoletion ObsoletionResult?obsoletionResult; if (!analyzeObsoletion || info?.Declaration.Obsoletion is null) { obsoletionResult = null; } else { var compiledAgainstObsoleteApi = false; if (assemblyFramework is not null) { var compiledAvailability = availabilityContext.GetAvailability(api, assemblyFramework); if (compiledAvailability?.Declaration.Obsoletion is not null) { compiledAgainstObsoleteApi = true; } } if (compiledAgainstObsoleteApi) { obsoletionResult = null; } else { var o = info.Declaration.Obsoletion.Value; obsoletionResult = new ObsoletionResult(o.Message, o.Url); } } // Analyze platform support platformResultBuilder.Clear(); if (info is null) { for (var i = 0; i < platforms.Count; i++) { platformResultBuilder.Add(null); } } else { var platformContext = platformContexts[framework]; foreach (var platform in platforms) { var annotation = platformContext.GetPlatformAnnotation(api); var isSupported = annotation.IsSupported(platform); var platformResult = isSupported ? PlatformResult.Supported : PlatformResult.Unsupported; platformResultBuilder.Add(platformResult); } } var frameworkResult = new FrameworkResult(availabilityResult, obsoletionResult, platformResultBuilder.ToArray()); frameworkResultBuilder.Add(frameworkResult); } var apiResult = new ApiResult(api, frameworkResultBuilder.ToArray()); apiResults.Add(apiResult); } } var results = new AssemblyResult(assemblyName, null, apiResults.ToArray()); resultSink.Add(results); } }); resultSink.CompleteAdding(); resultSinkTask.Wait(); }
public async Task RunAsync(Func <AnalyzeResult, Task> receiver) { var framework = NuGetFramework.ParseFolder(_tfmSelector.DetermineTargetTfmValue()); var platforms = _options.Platform.Select(p => p.ToString().ToLowerInvariant()).ToImmutableList(); async void Receiver(AssemblyResult result) { _logger.LogInformation(Resources.BinaryAnalysisProcessedAssemblyMessageFormat, result.AssemblyName); foreach (var r in CreateUaResults(result).ToList()) { await receiver(r).ConfigureAwait(false); } } var catalog = await CatalogService.LoadCatalogAsync().ConfigureAwait(false); var apiByGuid = catalog.GetAllApis().ToDictionary(a => a.UniqueId); var availabilityContext = ApiAvailabilityContext.Create(catalog); var platformContext = PlatformAnnotationContext.Create(availabilityContext, framework.GetShortFolderName()); foreach (var api in catalog.GetAllApis()) { var forwardedApi = catalog.GetForwardedApi(api); if (forwardedApi is not null) { apiByGuid[api.UniqueId] = forwardedApi.Value; } } var apiAvailability = new ConcurrentDictionary <ApiModel, ApiAvailability>(); using var resultSink = new BlockingCollection <AssemblyResult>(); var resultSinkTask = Task.Run(() => { foreach (var result in resultSink.GetConsumingEnumerable()) { Receiver(result); } }); var filePaths = AssemblyFileSet.Create(_options.Content.ToList()); Parallel.ForEach(filePaths, filePath => { using var env = new HostEnvironment(); var assembly = env.LoadAssemblyFrom(filePath); var assemblyName = assembly is not null ? assembly.Name.Value : Path.GetFileName(filePath); if (assembly is null) { var result = new AssemblyResult(assemblyName, Resources.BinaryAnalysisInvalidAssemblyMessage, Array.Empty <ApiResult>()); resultSink.Add(result); } else { var assemblyTfm = assembly.GetTargetFrameworkMoniker(); var assemblyFramework = string.IsNullOrEmpty(assemblyTfm) ? null : NuGetFramework.Parse(assemblyTfm); var crawler = new AssemblyCrawler(); crawler.Crawl(assembly); var crawlerResults = crawler.CreateResults(); var apiResults = new List <ApiResult>(); var frameworkResultBuilder = new List <FrameworkResult>(); var platformResultBuilder = new List <PlatformResult?>(platforms.Count); foreach (var apiKey in crawlerResults.Data.Keys) { if (apiByGuid.TryGetValue(apiKey.Id, out var api)) { var availability = apiAvailability.GetOrAdd(api, a => availabilityContext.GetAvailability(a)); frameworkResultBuilder.Clear(); AnalyzeAvailability(availability, framework, out AvailabilityResult availabilityResult, out ApiFrameworkAvailability? info); var obsoletionResult = AnalyzeObsoletion(availabilityContext, assemblyFramework, api, info); AnalyzePlatformSupport(platforms, platformContext, frameworkResultBuilder, platformResultBuilder !, api, framework !, availabilityResult, info, obsoletionResult); var apiResult = new ApiResult(api, frameworkResultBuilder.ToArray()); apiResults.Add(apiResult); }