168 lines
5.9 KiB
C#
168 lines
5.9 KiB
C#
// 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.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
#if NETCOREAPP2_0
|
|
using System.Runtime.Loader;
|
|
#endif
|
|
using Microsoft.DotNet.Cli.CommandLine;
|
|
|
|
namespace Microsoft.Extensions.ApiDescription.Tool.Commands
|
|
{
|
|
internal class GetDocumentCommand : ProjectCommandBase
|
|
{
|
|
internal const string FallbackDocumentName = "v1";
|
|
internal const string FallbackMethod = "GenerateAsync";
|
|
internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider";
|
|
|
|
private CommandOption _documentName;
|
|
private CommandOption _method;
|
|
private CommandOption _output;
|
|
private CommandOption _service;
|
|
|
|
public override void Configure(CommandLineApplication command)
|
|
{
|
|
base.Configure(command);
|
|
|
|
_documentName = command.Option(
|
|
"--documentName <Name>",
|
|
Resources.FormatDocumentDescription(FallbackDocumentName));
|
|
_method = command.Option("--method <Name>", Resources.FormatMethodDescription(FallbackMethod));
|
|
_output = command.Option("--output <Path>", Resources.OutputDescription);
|
|
_service = command.Option("--service <QualifiedName>", Resources.FormatServiceDescription(FallbackService));
|
|
}
|
|
|
|
protected override void Validate()
|
|
{
|
|
base.Validate();
|
|
|
|
if (!_output.HasValue())
|
|
{
|
|
throw new CommandException(Resources.FormatMissingOption(_output.LongName));
|
|
}
|
|
|
|
if (_method.HasValue() && !_service.HasValue())
|
|
{
|
|
throw new CommandException(Resources.FormatMissingOption(_service.LongName));
|
|
}
|
|
|
|
if (_service.HasValue() && !_method.HasValue())
|
|
{
|
|
throw new CommandException(Resources.FormatMissingOption(_method.LongName));
|
|
}
|
|
}
|
|
|
|
protected override int Execute()
|
|
{
|
|
var thisAssembly = typeof(GetDocumentCommand).Assembly;
|
|
|
|
var toolsDirectory = ToolsDirectory.Value();
|
|
var packagedAssemblies = Directory
|
|
.EnumerateFiles(toolsDirectory, "*.dll")
|
|
.Except(new[] { Path.GetFullPath(thisAssembly.Location) })
|
|
.ToDictionary(path => Path.GetFileNameWithoutExtension(path), 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 NETCOREAPP2_0
|
|
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 NET461
|
|
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,
|
|
AssemblyDirectory = Path.GetDirectoryName(assemblyPath),
|
|
AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath),
|
|
DocumentName = _documentName.Value(),
|
|
Method = _method.Value(),
|
|
OutputPath = _output.Value(),
|
|
Service = _service.Value(),
|
|
};
|
|
|
|
return GetDocumentCommandWorker.Process(context);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.Error.WriteLine(ex.ToString());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
private class AssemblyInfo
|
|
{
|
|
public AssemblyInfo(string path)
|
|
{
|
|
Path = path;
|
|
}
|
|
|
|
public string Path { get; }
|
|
|
|
public Assembly Assembly { get; set; }
|
|
}
|
|
}
|
|
}
|