| File: Commands\GetDocumentCommand.cs | Web Access |
| Project: src\aspnetcore\src\Tools\GetDocumentInsider\src\GetDocument.Insider.csproj (GetDocument.Insider) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using System.Linq; using System.Reflection; #if NETCOREAPP using System.Runtime.Loader; #endif using Microsoft.Extensions.CommandLineUtils; using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands; internal sealed class GetDocumentCommand : ProjectCommandBase { private CommandOption _fileListPath; private CommandOption _output; private CommandOption _openApiVersion; private CommandOption _documentName; private CommandOption _fileName; public GetDocumentCommand(IConsole console) : base(console) { } public override void Configure(CommandLineApplication command) { base.Configure(command); _fileListPath = command.Option("--file-list <Path>", Resources.FileListDescription); _output = command.Option("--output <Directory>", Resources.OutputDescription); _openApiVersion = command.Option("--openapi-version <Version>", Resources.OpenApiVersionDescription); _documentName = command.Option("--document-name <Name>", Resources.DocumentNameDescription); _fileName = command.Option("--file-name <Name>", Resources.FileNameDescription); } protected override void Validate() { base.Validate(); if (!_fileListPath.HasValue()) { throw new CommandException(Resources.FormatMissingOption(_fileListPath.LongName)); } if (!_output.HasValue()) { throw new CommandException(Resources.FormatMissingOption(_output.LongName)); } // No need to validate --openapi-version, we'll fallback to whatever is configured by // the runtime in the event that none is provided. // No need to validate --document-name, we'll fallback to generating OpenAPI files for // documents registered in the application in the event that none is provided. } protected override int Execute() { var thisAssembly = typeof(GetDocumentCommand).Assembly; var toolsDirectory = ToolsDirectory.Value(); var packagedAssemblies = Directory .EnumerateFiles(toolsDirectory, "*.dll") .Except([Path.GetFullPath(thisAssembly.Location)]) .ToDictionary(Path.GetFileNameWithoutExtension, path => new AssemblyInfo(path)); // Explicitly load all assemblies we need first to preserve target project as much as possible. This // executable is always run in the target project's context (either through location or .deps.json file). foreach (var keyValuePair in packagedAssemblies) { try { keyValuePair.Value.Assembly = Assembly.Load(new AssemblyName(keyValuePair.Key)); } catch { // Ignore all failures because missing assemblies should be loadable from tools directory. } } #if NETCOREAPP AssemblyLoadContext.Default.Resolving += (loadContext, assemblyName) => { var name = assemblyName.Name; if (!packagedAssemblies.TryGetValue(name, out var info)) { return null; } var assemblyPath = info.Path; if (!File.Exists(assemblyPath)) { throw new InvalidOperationException( $"Referenced assembly '{name}' was not found in '{toolsDirectory}'."); } return loadContext.LoadFromAssemblyPath(assemblyPath); }; #elif NETFRAMEWORK AppDomain.CurrentDomain.AssemblyResolve += (source, eventArgs) => { var assemblyName = new AssemblyName(eventArgs.Name); var name = assemblyName.Name; if (!packagedAssemblies.TryGetValue(name, out var info)) { return null; } var assembly = info.Assembly; if (assembly != null) { // Loaded already return assembly; } var assemblyPath = info.Path; if (!File.Exists(assemblyPath)) { throw new InvalidOperationException( $"Referenced assembly '{name}' was not found in '{toolsDirectory}'."); } return Assembly.LoadFile(assemblyPath); }; #else #error Target frameworks need to be updated. #endif // Now safe to reference the application's code. try { var assemblyPath = AssemblyPath.Value(); var context = new GetDocumentCommandContext { AssemblyPath = assemblyPath, AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath), FileListPath = _fileListPath.Value(), OutputDirectory = _output.Value(), OpenApiVersion = _openApiVersion.Value(), DocumentName = _documentName.Value(), ProjectName = ProjectName.Value(), Reporter = Reporter, FileName = _fileName.Value() }; return new GetDocumentCommandWorker(context).Process(); } catch (Exception ex) { Reporter.WriteError(ex.ToString()); return 2; } } private sealed class AssemblyInfo { public AssemblyInfo(string path) { Path = path; } public string Path { get; } public Assembly Assembly { get; set; } } }