Create a single Microsoft.Extensions.ApiDescription.Client package

- #8428
- add signing-related and PackageVerifier configuration for new package
- remove packaging configuration from dotnet-getdocument project
- adjust `dotnet-getdocument` invocation to its new location
- remove use of nonexistent (ignored) `dotnet-getdocument --no-build` option

Remove `--uri` feature from `dotnet-getdocument`
- reduce dependencies from Microsoft.AspNetCore.TestHost to Microsoft.AspNetCore.Hosting.Abstractions
  - assume web site depends on that
- merge `DownloadFileCore` into `DownloadFile`
- remove other unecessary code e.g. `WrappedException` was never `throw`n

Correct issues in `DownloadFile`
- e.g. dispose of `responseStream`, use `await` more, support FIPS-compliant machines

nits:
- clean up `Project` and the metadata it fetches
- remove unnecessary `.props` and `.targets` files
This commit is contained in:
Doug Bunting 2018-09-11 16:20:07 -07:00
parent 5a58f81d8d
commit d3442f3590
No known key found for this signature in database
GPG Key ID: 888B4EB7822B32E9
29 changed files with 261 additions and 809 deletions

View File

@ -1,7 +1,16 @@
{
"Default": {
"rules": [
"DefaultCompositeRule"
]
"Default": {
"rules": [
"DefaultCompositeRule"
],
"packages": {
"Microsoft.Extensions.ApiDescription.Client": {
"Exclusions": {
"BUILD_ITEMS_FRAMEWORK": {
"*": "Package includes tool with different target frameworks."
}
}
}
}
}
}
}

View File

@ -7,7 +7,7 @@
is not otherwise referenced. They avoid unnecessary changes to the Universe build graph or to product
dependencies. Do not use these properties elsewhere.
-->
<AngleSharpPackageVersion>0.9.9</AngleSharpPackageVersion>
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>2.1.1</BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>
@ -32,6 +32,7 @@
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreDiagnosticsPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingAbstractions20PackageVersion>2.0.0</MicrosoftAspNetCoreHostingAbstractions20PackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
@ -54,7 +55,6 @@
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreSessionPackageVersion>
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreStaticFilesPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestHost20PackageVersion>2.0.0</MicrosoftAspNetCoreTestHost20PackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>

View File

@ -17,13 +17,11 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
internal const string FallbackDocumentName = "v1";
internal const string FallbackMethod = "Generate";
internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider";
private const string WorkerType = "Microsoft.Extensions.ApiDescription.Client.Commands.GetDocumentCommandWorker";
private CommandOption _documentName;
private CommandOption _method;
private CommandOption _output;
private CommandOption _service;
private CommandOption _uri;
public override void Configure(CommandLineApplication command)
{
@ -35,7 +33,6 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
_method = command.Option("--method <Name>", Resources.FormatMethodDescription(FallbackMethod));
_output = command.Option("--output <Path>", Resources.OutputDescription);
_service = command.Option("--service <QualifiedName>", Resources.FormatServiceDescription(FallbackService));
_uri = command.Option("--uri <URI>", Resources.UriDescription);
}
protected override void Validate()
@ -131,12 +128,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
#error target frameworks need to be updated.
#endif
// Now safe to reference TestHost type.
// Now safe to reference the application's code.
try
{
var workerType = thisAssembly.GetType(WorkerType, throwOnError: true);
var methodInfo = workerType.GetMethod("Process", BindingFlags.Public | BindingFlags.Static);
var assemblyPath = AssemblyPath.Value();
var context = new GetDocumentCommandContext
{
@ -147,10 +141,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
Method = _method.Value(),
Output = _output.Value(),
Service = _service.Value(),
Uri = _uri.Value(),
};
return (int)methodInfo.Invoke(obj: null, parameters: new[] { context });
return GetDocumentCommandWorker.Process(context);
}
catch (Exception ex)
{

View File

@ -21,7 +21,5 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
public string Output { get; set; }
public string Service { get; set; }
public string Uri { get; set; }
}
}

View File

@ -2,15 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.ApiDescription.Client.Commands
{
@ -34,25 +29,10 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
}
var success = TryProcess(context, services);
if (!success && string.IsNullOrEmpty(context.Uri))
if (!success)
{
return 4;
}
var builder = GetBuilder(entryPointType, context.AssemblyPath, context.AssemblyName);
if (builder == null)
{
return 5;
}
// Mute the HttpsRedirectionMiddleware warning about HTTPS configuration.
builder.ConfigureLogging(loggingBuilder => loggingBuilder.AddFilter(
"Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware",
LogLevel.Error));
using (var server = new TestServer(builder))
{
ProcessAsync(context, server).Wait();
// As part of the aspnet/Mvc#8425 fix, return 4 here.
return 0;
}
return 0;
@ -95,15 +75,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
if (!success)
{
// As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
var message = Resources.FormatMethodInvocationFailed(methodName, serviceName, documentName);
if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output))
{
Reporter.WriteError(message);
}
else
{
Reporter.WriteWarning(message);
}
Reporter.WriteWarning(message);
}
return success;
@ -111,35 +85,14 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
catch (Exception ex)
{
var message = FormatException(ex);
if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output))
{
Reporter.WriteError(message);
}
else
{
Reporter.WriteWarning(message);
}
// As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
Reporter.WriteWarning(message);
return false;
}
}
public static async Task ProcessAsync(GetDocumentCommandContext context, TestServer server)
{
Debug.Assert(!string.IsNullOrEmpty(context.Uri));
Reporter.WriteInformation(Resources.FormatUsingUri(context.Uri));
var httpClient = server.CreateClient();
await DownloadFileCore.DownloadAsync(
context.Uri,
context.Output,
httpClient,
new LogWrapper(),
CancellationToken.None,
timeoutSeconds: 60);
}
// TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available.
private static IServiceProvider GetServices(Type entryPointType, string assemblyPath, string assemblyName)
{
@ -203,54 +156,7 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
}
}
// Startup
return new WebHostBuilder().UseStartup(assemblyName).Build().Services;
}
// TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available.
private static IWebHostBuilder GetBuilder(Type entryPointType, string assemblyPath, string assemblyName)
{
var methodInfo = entryPointType.GetMethod("BuildWebHost");
if (methodInfo != null)
{
// BuildWebHost cannot be used. Fall through, most likely to Startup fallback.
Reporter.WriteWarning(
"BuildWebHost method cannot be used. Falling back to minimal Startup configuration.");
}
methodInfo = entryPointType.GetMethod("CreateWebHostBuilder");
if (methodInfo != null)
{
// CreateWebHostBuilder
var parameters = methodInfo.GetParameters();
if (!methodInfo.IsStatic ||
parameters.Length != 1 ||
typeof(string[]) != parameters[0].ParameterType ||
typeof(IWebHostBuilder) != methodInfo.ReturnType)
{
Reporter.WriteError(
"CreateWebHostBuilder method found in {assemblyPath} does not have expected signature.");
return null;
}
try
{
var args = new[] { Array.Empty<string>() };
var builder = (IWebHostBuilder)methodInfo.Invoke(obj: null, parameters: args);
return builder;
}
catch (Exception ex)
{
Reporter.WriteError($"CreateWebHostBuilder method threw: {FormatException(ex)}");
return null;
}
}
// Startup
return new WebHostBuilder().UseStartup(assemblyName);
return null;
}
private static string FormatException(Exception exception)

View File

@ -1,121 +0,0 @@
// 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.Net.Http;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.ApiDescription.Client
{
internal static class DownloadFileCore
{
public static async Task DownloadAsync(
string uri,
string destinationPath,
HttpClient httpClient,
ILogWrapper log,
CancellationToken cancellationToken,
int timeoutSeconds)
{
// Timeout if the response has not begun within 1 minute
httpClient.Timeout = TimeSpan.FromMinutes(1);
var destinationExists = File.Exists(destinationPath);
var reachedCopy = false;
try
{
using (var response = await httpClient.GetAsync(uri, cancellationToken))
{
response.EnsureSuccessStatusCode();
cancellationToken.ThrowIfCancellationRequested();
using (var responseStreamTask = response.Content.ReadAsStreamAsync())
{
var finished = await Task.WhenAny(
responseStreamTask,
Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
if (!ReferenceEquals(responseStreamTask, finished))
{
throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds.");
}
var responseStream = await responseStreamTask;
if (destinationExists)
{
// Check hashes before using the downloaded information.
var downloadHash = GetHash(responseStream);
responseStream.Position = 0L;
byte[] destinationHash;
using (var destinationStream = File.OpenRead(destinationPath))
{
destinationHash = GetHash(destinationStream);
}
var sameHashes = downloadHash.LongLength == destinationHash.LongLength;
for (var i = 0L; sameHashes && i < downloadHash.LongLength; i++)
{
sameHashes = downloadHash[i] == destinationHash[i];
}
if (sameHashes)
{
log.LogInformational($"Not overwriting existing and matching file '{destinationPath}'.");
return;
}
}
else
{
// May need to create directory to hold the file.
var destinationDirectory = Path.GetDirectoryName(destinationPath);
if (!(string.IsNullOrEmpty(destinationDirectory) || Directory.Exists(destinationDirectory)))
{
Directory.CreateDirectory(destinationDirectory);
}
}
// Create or overwrite the destination file.
reachedCopy = true;
using (var outStream = File.Create(destinationPath))
{
responseStream.CopyTo(outStream);
}
}
}
}
catch (HttpRequestException ex) when (destinationExists)
{
if (ex.InnerException is SocketException socketException)
{
log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'.");
}
else
{
log.LogWarning($"Unable to download {uri}: {ex.Message}");
}
}
catch (Exception ex)
{
log.LogError($"Downloading '{uri}' failed.");
log.LogError(ex, showStackTrace: true);
if (reachedCopy)
{
File.Delete(destinationPath);
}
}
}
private static byte[] GetHash(Stream stream)
{
using (var algorithm = SHA256.Create())
{
return algorithm.ComputeHash(stream);
}
}
}
}

View File

@ -13,6 +13,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHost20PackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractions20PackageVersion)" />
</ItemGroup>
<Target Name="BuildX86" AfterTargets="Build" Condition=" '$(TargetFramework)' == 'net461' And '$(Platform)' != 'x86' ">
<MSBuild Projects="$(MSBuildProjectFullPath)" Properties="TargetFramework=$(TargetFramework);Platform=x86" Targets="Build" />
</Target>
</Project>

View File

@ -1,53 +0,0 @@
// 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;
namespace Microsoft.Extensions.ApiDescription.Client
{
internal interface ILogWrapper
{
/// <summary>
/// Logs specified informational <paramref name="message"/>. Implementations should be thread safe.
/// </summary>
/// <param name="message">The message to log.</param>
/// <param name="messageArgs">Optional arguments for formatting the <paramref name="message"/> string.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="message"/> is <see langword="null"/>.
/// </exception>
void LogInformational(string message, params object[] messageArgs);
/// <summary>
/// Logs a warning with the specified <paramref name="message"/>. Implementations should be thread safe.
/// </summary>
/// <param name="message">The message to log.</param>
/// <param name="messageArgs">Optional arguments for formatting the <paramref name="message"/> string.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="message"/> is <see langword="null"/>.
/// </exception>
void LogWarning(string message, params object[] messageArgs);
/// <summary>
/// Logs an error with the specified <paramref name="message"/>. Implementations should be thread safe.
/// </summary>
/// <param name="message">The message to log.</param>
/// <param name="messageArgs">Optional arguments for formatting the <paramref name="message"/> string.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="message"/> is <see langword="null"/>.
/// </exception>
void LogError(string message, params object[] messageArgs);
/// <summary>
/// Logs an error with the message and (optionally) the stack trace of the given <paramref name="exception"/>.
/// Implementations should be thread safe.
/// </summary>
/// <param name="exception">The <see cref="Exception"/> to log.</param>
/// <param name="showStackTrace">
/// If <see langword="true"/>, append stack trace to <paramref name="exception"/>'s message.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="exception"/> is <see langword="null"/>.
/// </exception>
void LogError(Exception exception, bool showStackTrace);
}
}

View File

@ -1,18 +0,0 @@
// 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 Microsoft.DotNet.Cli.CommandLine;
namespace Microsoft.Extensions.ApiDescription.Client
{
internal static class Json
{
public static CommandOption ConfigureOption(CommandLineApplication command)
=> command.Option("--json", Resources.JsonDescription);
public static string Literal(string text)
=> text != null
? "\"" + text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""
: "null";
}
}

View File

@ -1,31 +0,0 @@
// 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;
namespace Microsoft.Extensions.ApiDescription.Client
{
public class LogWrapper : ILogWrapper
{
public void LogError(string message, params object[] messageArgs)
{
Reporter.WriteError(string.Format(message, messageArgs));
}
public void LogError(Exception exception, bool showStackTrace)
{
var message = showStackTrace ? exception.ToString() : exception.Message;
Reporter.WriteError(message);
}
public void LogInformational(string message, params object[] messageArgs)
{
Reporter.WriteInformation(string.Format(message, messageArgs));
}
public void LogWarning(string message, params object[] messageArgs)
{
Reporter.WriteWarning(string.Format(message, messageArgs));
}
}
}

View File

@ -30,10 +30,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
}
catch (Exception ex)
{
if (ex is CommandException
|| ex is CommandParsingException
|| (ex is WrappedException wrappedException
&& wrappedException.Type == "Microsoft.Extensions.ApiDescription.Client.Design.OperationException"))
if (ex is CommandException || ex is CommandParsingException)
{
Reporter.WriteVerbose(ex.ToString());
}

View File

@ -24,20 +24,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatAssemblyDescription()
=> GetString("AssemblyDescription");
/// <summary>
/// Show JSON output.
/// </summary>
internal static string JsonDescription
{
get => GetString("JsonDescription");
}
/// <summary>
/// Show JSON output.
/// </summary>
internal static string FormatJsonDescription()
=> GetString("JsonDescription");
/// <summary>
/// Missing required option '--{0}'.
/// </summary>
@ -94,48 +80,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatPrefixDescription()
=> GetString("PrefixDescription");
/// <summary>
/// Using application base '{0}'.
/// </summary>
internal static string UsingApplicationBase
{
get => GetString("UsingApplicationBase");
}
/// <summary>
/// Using application base '{0}'.
/// </summary>
internal static string FormatUsingApplicationBase(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingApplicationBase"), p0);
/// <summary>
/// Using assembly '{0}'.
/// </summary>
internal static string UsingAssembly
{
get => GetString("UsingAssembly");
}
/// <summary>
/// Using assembly '{0}'.
/// </summary>
internal static string FormatUsingAssembly(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingAssembly"), p0);
/// <summary>
/// Using configuration file '{0}'.
/// </summary>
internal static string UsingConfigurationFile
{
get => GetString("UsingConfigurationFile");
}
/// <summary>
/// Using configuration file '{0}'.
/// </summary>
internal static string FormatUsingConfigurationFile(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingConfigurationFile"), p0);
/// <summary>
/// Show verbose output.
/// </summary>
@ -150,34 +94,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatVerboseDescription()
=> GetString("VerboseDescription");
/// <summary>
/// Writing '{0}'...
/// </summary>
internal static string WritingFile
{
get => GetString("WritingFile");
}
/// <summary>
/// Writing '{0}'...
/// </summary>
internal static string FormatWritingFile(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0);
/// <summary>
/// Using working directory '{0}'.
/// </summary>
internal static string UsingWorkingDirectory
{
get => GetString("UsingWorkingDirectory");
}
/// <summary>
/// Using working directory '{0}'.
/// </summary>
internal static string FormatUsingWorkingDirectory(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingWorkingDirectory"), p0);
/// <summary>
/// Location from which inside man was copied (in the .NET Framework case) or loaded.
/// </summary>
@ -192,20 +108,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatToolsDirectoryDescription()
=> GetString("ToolsDirectoryDescription");
/// <summary>
/// The URI to download the document from.
/// </summary>
internal static string UriDescription
{
get => GetString("UriDescription");
}
/// <summary>
/// The URI to download the document from.
/// </summary>
internal static string FormatUriDescription()
=> GetString("UriDescription");
/// <summary>
/// The name of the method to invoke on the '--service' instance. Default value '{0}'.
/// </summary>
@ -234,20 +136,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatServiceDescription(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("ServiceDescription"), p0);
/// <summary>
/// Missing required option '--{0}' or '--{1}'.
/// </summary>
internal static string MissingOptions
{
get => GetString("MissingOptions");
}
/// <summary>
/// Missing required option '--{0}' or '--{1}'.
/// </summary>
internal static string FormatMissingOptions(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("MissingOptions"), p0, p1);
/// <summary>
/// The name of the document to pass to the '--method' method. Default value '{0}'.
/// </summary>
@ -304,20 +192,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatUsingService(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingService"), p0);
/// <summary>
/// Using URI '{0}'.
/// </summary>
internal static string UsingUri
{
get => GetString("UsingUri");
}
/// <summary>
/// Using URI '{0}'.
/// </summary>
internal static string FormatUsingUri(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingUri"), p0);
/// <summary>
/// Method '{0}' of service '{1}' failed to generate document '{2}'.
/// </summary>

View File

@ -120,9 +120,6 @@
<data name="AssemblyDescription" xml:space="preserve">
<value>The assembly to use.</value>
</data>
<data name="JsonDescription" xml:space="preserve">
<value>Show JSON output.</value>
</data>
<data name="MissingOption" xml:space="preserve">
<value>Missing required option '--{0}'.</value>
</data>
@ -135,39 +132,18 @@
<data name="PrefixDescription" xml:space="preserve">
<value>Prefix console output with logging level.</value>
</data>
<data name="UsingApplicationBase" xml:space="preserve">
<value>Using application base '{0}'.</value>
</data>
<data name="UsingAssembly" xml:space="preserve">
<value>Using assembly '{0}'.</value>
</data>
<data name="UsingConfigurationFile" xml:space="preserve">
<value>Using configuration file '{0}'.</value>
</data>
<data name="VerboseDescription" xml:space="preserve">
<value>Show verbose output.</value>
</data>
<data name="WritingFile" xml:space="preserve">
<value>Writing '{0}'...</value>
</data>
<data name="UsingWorkingDirectory" xml:space="preserve">
<value>Using working directory '{0}'.</value>
</data>
<data name="ToolsDirectoryDescription" xml:space="preserve">
<value>Location from which inside man was copied (in the .NET Framework case) or loaded.</value>
</data>
<data name="UriDescription" xml:space="preserve">
<value>The URI to download the document from.</value>
</data>
<data name="MethodDescription" xml:space="preserve">
<value>The name of the method to invoke on the '--service' instance. Default value '{0}'.</value>
</data>
<data name="ServiceDescription" xml:space="preserve">
<value>The qualified name of the service type to retrieve from dependency injection. Default value '{0}'.</value>
</data>
<data name="MissingOptions" xml:space="preserve">
<value>Missing required option '--{0}' or '--{1}'.</value>
</data>
<data name="DocumentDescription" xml:space="preserve">
<value>The name of the document to pass to the '--method' method. Default value '{0}'.</value>
</data>
@ -180,13 +156,10 @@
<data name="UsingService" xml:space="preserve">
<value>Using service '{0}'.</value>
</data>
<data name="UsingUri" xml:space="preserve">
<value>Using URI '{0}'.</value>
</data>
<data name="MethodInvocationFailed" xml:space="preserve">
<value>Method '{0}' of service '{1}' failed to generate document '{2}'.</value>
</data>
<data name="MissingEntryPoint" xml:space="preserve">
<value>Assembly '{0}' does not contain an entry point.</value>
</data>
</root>
</root>

View File

@ -1,24 +0,0 @@
// 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;
namespace Microsoft.Extensions.ApiDescription.Client
{
internal class WrappedException : Exception
{
private readonly string _stackTrace;
public WrappedException(string type, string message, string stackTrace)
: base(message)
{
Type = type;
_stackTrace = stackTrace;
}
public string Type { get; }
public override string ToString()
=> _stackTrace;
}
}

View File

@ -4,6 +4,9 @@
using System;
using System.IO;
using System.Net.Http;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
@ -94,19 +97,130 @@ namespace Microsoft.Extensions.ApiDescription.Client
return;
}
log.LogMessage($"Downloading '{uri}' to '{destinationPath}'.");
log.LogMessage(MessageImportance.High, $"Downloading '{uri}' to '{destinationPath}'.");
using (var httpClient = new HttpClient
using (var httpClient = new HttpClient())
{
})
await DownloadAsync(uri, destinationPath, httpClient, cancellationToken, log, timeoutSeconds);
}
}
public static async Task DownloadAsync(
string uri,
string destinationPath,
HttpClient httpClient,
CancellationToken cancellationToken,
TaskLoggingHelper log,
int timeoutSeconds)
{
// Timeout if the response has not begun within 1 minute
httpClient.Timeout = TimeSpan.FromMinutes(1);
var destinationExists = File.Exists(destinationPath);
var reachedCopy = false;
try
{
await DownloadFileCore.DownloadAsync(
uri,
destinationPath,
httpClient,
new LogWrapper(log),
cancellationToken,
timeoutSeconds);
using (var response = await httpClient.GetAsync(uri, cancellationToken))
{
response.EnsureSuccessStatusCode();
cancellationToken.ThrowIfCancellationRequested();
using (var responseStreamTask = response.Content.ReadAsStreamAsync())
{
var finished = await Task.WhenAny(
responseStreamTask,
Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
if (!ReferenceEquals(responseStreamTask, finished))
{
throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds.");
}
using (var responseStream = await responseStreamTask)
{
if (destinationExists)
{
// Check hashes before using the downloaded information.
var downloadHash = GetHash(responseStream);
responseStream.Position = 0L;
byte[] destinationHash;
using (var destinationStream = File.OpenRead(destinationPath))
{
destinationHash = GetHash(destinationStream);
}
var sameHashes = downloadHash.Length == destinationHash.Length;
for (var i = 0; sameHashes && i < downloadHash.Length; i++)
{
sameHashes = downloadHash[i] == destinationHash[i];
}
if (sameHashes)
{
log.LogMessage($"Not overwriting existing and matching file '{destinationPath}'.");
return;
}
}
else
{
// May need to create directory to hold the file.
var destinationDirectory = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}
}
// Create or overwrite the destination file.
reachedCopy = true;
using (var outStream = File.Create(destinationPath))
{
await responseStream.CopyToAsync(outStream);
}
}
}
}
}
catch (HttpRequestException ex) when (destinationExists)
{
if (ex.InnerException is SocketException socketException)
{
log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'.");
}
else
{
log.LogWarning($"Unable to download {uri}: {ex.Message}");
}
}
catch (Exception ex)
{
log.LogError($"Downloading '{uri}' failed.");
log.LogErrorFromException(ex, showStackTrace: true);
if (reachedCopy)
{
File.Delete(destinationPath);
}
}
}
private static byte[] GetHash(Stream stream)
{
SHA256 algorithm;
try
{
algorithm = SHA256.Create();
}
catch (TargetInvocationException)
{
// SHA256.Create is documented to throw this exception on FIPS-compliant machines. See
// https://msdn.microsoft.com/en-us/library/z08hz7ad Fall back to a FIPS-compliant SHA256 algorithm.
algorithm = new SHA256CryptoServiceProvider();
}
using (algorithm)
{
return algorithm.ComputeHash(stream);
}
}
}

View File

@ -1,38 +0,0 @@
// 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 Microsoft.Build.Utilities;
namespace Microsoft.Extensions.ApiDescription.Client
{
internal class LogWrapper : ILogWrapper
{
private readonly TaskLoggingHelper _log;
public LogWrapper(TaskLoggingHelper log)
{
_log = log;
}
public void LogError(string message, params object[] messageArgs)
{
_log.LogError(message, messageArgs);
}
public void LogError(Exception exception, bool showStackTrace)
{
_log.LogErrorFromException(exception, showStackTrace);
}
public void LogInformational(string message, params object[] messageArgs)
{
_log.LogMessage(message, messageArgs);
}
public void LogWarning(string message, params object[] messageArgs)
{
_log.LogWarning(message, messageArgs);
}
}
}

View File

@ -8,21 +8,52 @@
<Description>MSBuild tasks and targets for code generation</Description>
<EnableApiCheck>false</EnableApiCheck>
<IsPackable>false</IsPackable>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSource>false</IncludeSource>
<IncludeSymbols>false</IncludeSymbols>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<PackageTags>Build Tasks;msbuild;DownloadFile;GetFilenameFromUri;code generation</PackageTags>
<PackageTags>Build Tasks;MSBuild;Swagger;Open API;code generation; Web API client</PackageTags>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="../GetDocumentInsider/DownloadFileCore.cs" />
<Compile Include="../GetDocumentInsider/ILogWrapper.cs" />
<EmbeddedResource Include="ServiceProjectReferenceMetadata.targets" />
<PackageReference Include="Microsoft.Build.Utilities.Core"
Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
<PackageReference Include="System.Net.Http"
Condition="'$(TargetFramework)' == 'net461'"
Version="$(SystemNetHttpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
<PackageReference Condition="'$(TargetFramework)' == 'net461'" Include="System.Net.Http" Version="$(SystemNetHttpPackageVersion)" />
<ItemGroup Condition="'$(TargetFramework)' != ''">
<SignedPackageFile Include="$(TargetPath)">
<Certificate>$(AssemblySigningCertName)</Certificate>
<PackagePath>tasks/$(TargetFramework)/$(TargetFileName)</PackagePath>
<StrongName>$(AssemblySigningStrongName)</StrongName>
</SignedPackageFile>
</ItemGroup>
<!-- Add other signed files in a single inner build, avoiding duplications in this multi-TFM project. -->
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<SignedPackageFile Include="../dotnet-getdocument/bin/$(Configuration)/netcoreapp2.1/dotnet-getdocument.dll">
<Certificate>$(AssemblySigningCertName)</Certificate>
<PackagePath>tools/dotnet-getdocument.dll</PackagePath>
<StrongName>$(AssemblySigningStrongName)</StrongName>
</SignedPackageFile>
<SignedPackageFile Include="../GetDocumentInsider/bin/$(Configuration)/net461/GetDocument.Insider.exe">
<Certificate>$(AssemblySigningCertName)</Certificate>
<PackagePath>tools/net461/GetDocument.Insider.exe</PackagePath>
<StrongName>$(AssemblySigningStrongName)</StrongName>
</SignedPackageFile>
<SignedPackageFile Include="../GetDocumentInsider/bin/x86/$(Configuration)/net461/GetDocument.Insider.exe">
<Certificate>$(AssemblySigningCertName)</Certificate>
<PackagePath>tools/net461-x86/GetDocument.Insider.exe</PackagePath>
<StrongName>$(AssemblySigningStrongName)</StrongName>
</SignedPackageFile>
<SignedPackageFile Include="../GetDocumentInsider/bin/$(Configuration)/netcoreapp2.0/GetDocument.Insider.dll">
<Certificate>$(AssemblySigningCertName)</Certificate>
<PackagePath>tools/netcoreapp2.0/GetDocument.Insider.exe</PackagePath>
<StrongName>$(AssemblySigningStrongName)</StrongName>
</SignedPackageFile>
</ItemGroup>
<Target Name="PopulateNuspec">

View File

@ -12,17 +12,19 @@
<owners>$owners$</owners>
<projectUrl>$projectUrl$</projectUrl>
<repository type="git" url="$repositoryUrl$" commit="$repositoryCommit$" />
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<tags>$tags$</tags>
<version>$version$</version>
<dependencies>
<dependency id="NSwag.MSBuild" version="11.18.6" include="runtime,build,native,contentfiles,analyzers" />
</dependencies>
</metadata>
<files>
<file src="bin\$configuration$\**" target="tasks" exclude="**\Microsoft.Build.*.dll" />
<file src="build\*.*" target="build" />
<file src="buildMultiTargeting\*.*" target="buildMultiTargeting" />
<file src="build\*" target="build" />
<file src="buildMultiTargeting\*" target="buildMultiTargeting" />
<file src="bin\$configuration$\net461\Microsoft.Extensions.ApiDescription.Client.*" target="tasks\net461" />
<file src="bin\$configuration$\netstandard2.0\Microsoft.Extensions.ApiDescription.Client.*" target="tasks\netstandard2.0" />
<file src="..\dotnet-getdocument\bin\$configuration$\netcoreapp2.1\dotnet-getdocument.*" target="tools" />
<file src="..\GetDocumentInsider\bin\$configuration$\net461\GetDocument.Insider.*" target="tools\net461" />
<file src="..\GetDocumentInsider\bin\x86\$configuration$\net461\GetDocument.Insider.*" target="tools\net461-x86" />
<file src="..\GetDocumentInsider\bin\$configuration$\netcoreapp2.0\GetDocument.Insider.*" target="tools\netcoreapp2.0" />
</files>
</package>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Collect properties only in inner build. Execute unconditionally before WriteServiceProjectReferenceMetadata. -->
<Target Name="GetServiceProjectReferenceMetadata"
BeforeTargets="WriteServiceProjectReferenceMetadataCore"
Condition="'$(TargetFramework)' != ''"
Returns="@(ServiceProjectReferenceMetadata)">
<ItemGroup Condition="'$(TargetFramework)' != ''">
<ServiceProjectReferenceMetadata Include="DefaultDocumentName: $(DefaultServiceProjectDocumentName)" />
<ServiceProjectReferenceMetadata Include="DefaultMethod: $(DefaultServiceProjectMethod)" />
<ServiceProjectReferenceMetadata Include="DefaultService: $(DefaultServiceProjectService)" />
<ServiceProjectReferenceMetadata Include="DefaultUri: $(DefaultServiceProjectUri)" />
<ServiceProjectReferenceMetadata Include="AssemblyPath: $(TargetPath)" />
<ServiceProjectReferenceMetadata Include="Configuration: $(Configuration)" />
<ServiceProjectReferenceMetadata Include="ExtensionsPath: $(MSBuildProjectExtensionsPath)" />
<ServiceProjectReferenceMetadata Include="RuntimeIdentifier: $(RuntimeIdentifier)" />
<ServiceProjectReferenceMetadata Include="TargetFramework: $(TargetFramework)" />
</ItemGroup>
</Target>
<Target Name="WriteServiceProjectReferenceMetadata" Returns="@(ServiceProjectReferenceMetadata)">
<MSBuild Condition="'$(TargetFramework)' == ''"
Projects="$(MSBuildProjectFile)"
Targets="WriteServiceProjectReferenceMetadata"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);ServiceProjectReferenceMetadataFile=$(ServiceProjectReferenceMetadataFile)" />
<WriteLinesToFile Condition="'$(TargetFramework)' != ''"
File="$(ServiceProjectReferenceMetadataPath)"
Lines="@(ServiceProjectReferenceMetadata)" />
</Target>
</Project>

View File

@ -93,11 +93,6 @@
Default is set in server project, falling back to "Microsoft.Extensions.ApiDescription.IDocumentProvider".
-->
<Service />
<!--
URI from which Default document generator should download the document. Used only if invoking the %(Method) on
the %(Service) fails. Default is set in server project and metadata has no further fallback.
-->
<Uri />
</ServiceProjectReference>
<ServiceUriReference>

View File

@ -179,7 +179,7 @@
<ItemGroup>
<!-- @(CurrentServiceProjectReference) item group will never contain more than one item. -->
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
<Command>dotnet getdocument --no-build --project %(FullPath) --output %(DocumentPath)</Command>
<Command>dotnet $(MSBuildThisFileDirectory)/../tools/dotnet-getdocument.dll --project %(FullPath) --output %(DocumentPath)</Command>
<DefaultDocumentGeneratorOptions
Condition="'%(DefaultDocumentGeneratorOptions)' == ''">$(DefaultDocumentGeneratorDefaultOptions)</DefaultDocumentGeneratorOptions>
<ProjectConfiguration
@ -194,9 +194,6 @@
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
<Command Condition="'%(Service)' != ''">%(Command) --service %(Service)</Command>
</CurrentServiceProjectReference>
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
<Command Condition="'%(Uri)' != ''">%(Command) --uri %(Uri)</Command>
</CurrentServiceProjectReference>
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
<Command>%(Command) --configuration %(ProjectConfiguration) %(DefaultDocumentGeneratorOptions)</Command>
</CurrentServiceProjectReference>

View File

@ -159,12 +159,6 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
args.Add(project.DefaultService);
}
if (!(args.Contains("--uri") || string.IsNullOrEmpty(project.DefaultUri)))
{
args.Add("--uri");
args.Add(project.DefaultUri);
}
if (_output.HasValue())
{
args.Add("--output");

View File

@ -1,6 +1,7 @@
// 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.Diagnostics;
using System.IO;
@ -11,7 +12,8 @@ namespace Microsoft.Extensions.ApiDescription.Client
{
internal class Project
{
private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Client.ServiceProjectReferenceMetadata";
private const string ResourceFilename = "ServiceProjectReferenceMetadata.targets";
private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Client." + ResourceFilename;
private Project()
{
@ -33,8 +35,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
public string DefaultService { get; private set; }
public string DefaultUri { get; private set; }
public string DepsPath { get; private set; }
public string Directory { get; private set; }
@ -53,41 +53,36 @@ namespace Microsoft.Extensions.ApiDescription.Client
public string RuntimeFrameworkVersion { get; private set; }
public string RuntimeIdentifier { get; private set; }
public string TargetFramework { get; private set; }
public string TargetFrameworkMoniker { get; private set; }
public static Project FromFile(
string file,
string projectFile,
string buildExtensionsDirectory,
string framework = null,
string configuration = null,
string runtime = null)
{
Debug.Assert(!string.IsNullOrEmpty(file), "file is null or empty.");
if (string.IsNullOrEmpty(projectFile))
{
throw new ArgumentNullException(nameof(projectFile));
}
if (string.IsNullOrEmpty(buildExtensionsDirectory))
{
buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(file), "obj");
buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(projectFile), "obj");
}
IODirectory.CreateDirectory(buildExtensionsDirectory);
var assembly = typeof(Project).Assembly;
var propsPath = Path.Combine(
var targetsPath = Path.Combine(
buildExtensionsDirectory,
Path.GetFileName(file) + ".ServiceProjectReferenceMetadata.props");
using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.props"))
{
using (var output = File.OpenWrite(propsPath))
{
Reporter.WriteVerbose(Resources.FormatWritingFile(propsPath));
input.CopyTo(output);
}
}
var targetsPath = Path.ChangeExtension(propsPath, ".targets");
using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.targets"))
$"{Path.GetFileName(projectFile)}.{ResourceFilename}");
using (var input = assembly.GetManifestResourceStream(MSBuildResourceName))
{
using (var output = File.OpenWrite(targetsPath))
{
@ -101,32 +96,29 @@ namespace Microsoft.Extensions.ApiDescription.Client
var metadataPath = Path.GetTempFileName();
try
{
var propertyArg = "/property:ServiceProjectReferenceMetadataPath=" + metadataPath;
if (!string.IsNullOrEmpty(framework))
{
propertyArg += ";TargetFramework=" + framework;
}
if (!string.IsNullOrEmpty(configuration))
{
propertyArg += ";Configuration=" + configuration;
}
if (!string.IsNullOrEmpty(runtime))
{
propertyArg += ";RuntimeIdentifier=" + runtime;
}
var args = new List<string>
{
"msbuild",
"/target:WriteServiceProjectReferenceMetadata",
propertyArg,
"/verbosity:quiet",
"/nologo"
"/nologo",
$"/property:ServiceProjectReferenceMetadataPath={metadataPath}",
projectFile,
};
if (!string.IsNullOrEmpty(file))
if (!string.IsNullOrEmpty(framework))
{
args.Add(file);
args.Add($"/property:TargetFramework={framework}");
}
if (!string.IsNullOrEmpty(configuration))
{
args.Add($"/property:Configuration={configuration}");
}
if (!string.IsNullOrEmpty(runtime))
{
args.Add($"/property:RuntimeIdentifier={runtime}");
}
var exitCode = Exe.Run("dotnet", args);
@ -140,21 +132,20 @@ namespace Microsoft.Extensions.ApiDescription.Client
}
finally
{
File.Delete(propsPath);
File.Delete(metadataPath);
File.Delete(targetsPath);
}
var project = new Project
{
DefaultDocumentName = metadata[nameof(DefaultDocumentName)],
DefaultMethod = metadata[nameof(DefaultMethod)],
DefaultService = metadata[nameof(DefaultService)],
AssemblyName = metadata[nameof(AssemblyName)],
AssemblyPath = metadata[nameof(AssemblyPath)],
AssetsPath = metadata[nameof(AssetsPath)],
Configuration = metadata[nameof(Configuration)],
DefaultDocumentName = metadata[nameof(DefaultDocumentName)],
DefaultMethod = metadata[nameof(DefaultMethod)],
DefaultService = metadata[nameof(DefaultService)],
DefaultUri = metadata[nameof(DefaultUri)],
DepsPath = metadata[nameof(DepsPath)],
Directory = metadata[nameof(Directory)],
ExtensionsPath = metadata[nameof(ExtensionsPath)],
@ -164,6 +155,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
PlatformTarget = metadata[nameof(PlatformTarget)] ?? metadata[nameof(Platform)],
RuntimeConfigPath = metadata[nameof(RuntimeConfigPath)],
RuntimeFrameworkVersion = metadata[nameof(RuntimeFrameworkVersion)],
RuntimeIdentifier = metadata[nameof(RuntimeIdentifier)],
TargetFramework = metadata[nameof(TargetFramework)],
TargetFrameworkMoniker = metadata[nameof(TargetFrameworkMoniker)],
};
@ -193,6 +185,11 @@ namespace Microsoft.Extensions.ApiDescription.Client
project.AssemblyPath = Path.GetFullPath(Path.Combine(project.Directory, project.AssemblyPath));
}
if (!Path.IsPathRooted(project.ExtensionsPath))
{
project.ExtensionsPath = Path.GetFullPath(Path.Combine(project.Directory, project.ExtensionsPath));
}
if (!Path.IsPathRooted(project.OutputPath))
{
project.OutputPath = Path.GetFullPath(Path.Combine(project.Directory, project.OutputPath));

View File

@ -277,7 +277,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
=> string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0);
/// <summary>
/// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.
/// Project output not found. Project must be up-to-date when using this tool.
/// </summary>
internal static string MustBuild
{
@ -285,7 +285,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
}
/// <summary>
/// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.
/// Project output not found. Project must be up-to-date when using this tool.
/// </summary>
internal static string FormatMustBuild()
=> GetString("MustBuild");

View File

@ -175,7 +175,7 @@
<value>Writing '{0}'...</value>
</data>
<data name="MustBuild" xml:space="preserve">
<value>Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.</value>
<value>Project output not found. Project must be up-to-date when using this tool.</value>
</data>
<data name="OutputDescription" xml:space="preserve">
<value>The file to write the result to.</value>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- No need for any additional work in outer build. -->
<WriteServiceProjectReferenceMetadataDependsOn Condition="'$(TargetFramework)' != '' AND '$(WriteServiceProjectReferenceMetadataDependsOn)' == ''">
$(WriteServiceProjectReferenceMetadataDependsOn)
</WriteServiceProjectReferenceMetadataDependsOn>
<!-- Execute WriteServiceProjectReferenceMetadata support targets after WriteServiceProjectReferenceMetadataDependsOn targets by default. -->
<GetServiceProjectReferenceMetadataDependsOn Condition="'$(GetServiceProjectReferenceMetadataDependsOn)' == ''">
$(WriteServiceProjectReferenceMetadataDependsOn)
</GetServiceProjectReferenceMetadataDependsOn>
<WriteServiceProjectReferenceMetadataCoreDependsOn Condition="'$(WriteServiceProjectReferenceMetadataCoreDependsOn)' == ''">
$(WriteServiceProjectReferenceMetadataDependsOn)
</WriteServiceProjectReferenceMetadataCoreDependsOn>
</PropertyGroup>
</Project>

View File

@ -1,20 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="WriteServiceProjectReferenceMetadata"
DependsOnTargets="$(WriteServiceProjectReferenceMetadataDependsOn)"
Returns="@(ServiceProjectReferenceMetadata)" />
<!-- Collect properties only in inner build. Execute unconditionally before WriteServiceProjectReferenceMetadataCore. -->
<!-- Collect properties only in inner build. Execute unconditionally before WriteServiceProjectReferenceMetadata. -->
<Target Name="GetServiceProjectReferenceMetadata"
BeforeTargets="WriteServiceProjectReferenceMetadataCore"
BeforeTargets="WriteServiceProjectReferenceMetadata"
Condition="'$(TargetFramework)' != ''"
DependsOnTargets="$(GetServiceProjectReferenceMetadataDependsOn)"
Returns="@(ServiceProjectReferenceMetadata)">
<ItemGroup Condition="'$(TargetFramework)' != ''">
<ServiceProjectReferenceMetadata Include="DefaultDocumentName: $(DefaultServiceProjectDocumentName)" />
<ServiceProjectReferenceMetadata Include="DefaultMethod: $(DefaultServiceProjectMethod)"></ServiceProjectReferenceMetadata>
<ServiceProjectReferenceMetadata Include="DefaultService: $(DefaultServiceProjectService)"></ServiceProjectReferenceMetadata>
<ServiceProjectReferenceMetadata Include="DefaultUri: $(DefaultServiceProjectUri)"></ServiceProjectReferenceMetadata>
<ServiceProjectReferenceMetadata Include="DefaultMethod: $(DefaultServiceProjectMethod)" />
<ServiceProjectReferenceMetadata Include="DefaultService: $(DefaultServiceProjectService)" />
<ServiceProjectReferenceMetadata Include="AssemblyName: $(AssemblyName)" />
<ServiceProjectReferenceMetadata Include="AssemblyPath: $(TargetPath)" />
@ -29,21 +23,21 @@
<ServiceProjectReferenceMetadata Include="PlatformTarget: $(PlatformTarget)" />
<ServiceProjectReferenceMetadata Include="RuntimeConfigPath: $(ProjectRuntimeConfigFilePath)" />
<ServiceProjectReferenceMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
<ServiceProjectReferenceMetadata Include="RuntimeIdentifier: $(RuntimeIdentifier)" />
<ServiceProjectReferenceMetadata Include="TargetFramework: $(TargetFramework)" />
<ServiceProjectReferenceMetadata Include="TargetFrameworkMoniker: $(TargetFrameworkMoniker)" />
</ItemGroup>
</Target>
<!-- Write information only in inner build. Execute unconditionally before WriteServiceProjectReferenceMetadata. -->
<Target Name="WriteServiceProjectReferenceMetadataCore"
BeforeTargets="WriteServiceProjectReferenceMetadata"
DependsOnTargets="$(WriteServiceProjectReferenceMetadataCoreDependsOn)"
Returns="@(ServiceProjectReferenceMetadata)">
<!-- Write information only in inner build. -->
<Target Name="WriteServiceProjectReferenceMetadata" Returns="@(ServiceProjectReferenceMetadata)">
<MSBuild Condition="'$(TargetFramework)' == ''"
Projects="$(MSBuildProjectFile)"
Targets="WriteServiceProjectReferenceMetadata"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);ServiceProjectReferenceMetadataFile=$(ServiceProjectReferenceMetadataFile)" />
<WriteLinesToFile Condition="'$(TargetFramework)' != ''" File="$(ServiceProjectReferenceMetadataPath)" Lines="@(ServiceProjectReferenceMetadata)" />
<WriteLinesToFile Condition="'$(TargetFramework)' != ''"
File="$(ServiceProjectReferenceMetadataPath)"
Lines="@(ServiceProjectReferenceMetadata)" />
</Target>
</Project>

View File

@ -1,89 +1,25 @@
<Project Sdk="Internal.AspNetCore.Sdk">
<PropertyGroup>
<!-- Execute PopulateNuspec fairly late. -->
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);PopulateNuspec</GenerateNuspecDependsOn>
<AssemblyName>dotnet-getdocument</AssemblyName>
<Description>GetDocument Command-line Tool outside man</Description>
<EnableApiCheck>false</EnableApiCheck>
<GenerateToolShims>true</GenerateToolShims>
<IncludeSource>false</IncludeSource>
<IsPackable>false</IsPackable>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<OutputType>Exe</OutputType>
<PackAsTool>true</PackAsTool>
<PackageTags>GetDocument;command line;command-line;tool</PackageTags>
<RootNamespace>Microsoft.Extensions.ApiDescription.Client</RootNamespace>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="../GetDocumentInsider/AnsiConsole.cs" />
<Compile Include="../GetDocumentInsider/AnsiConstants.cs" />
<Compile Include="../GetDocumentInsider/AnsiTextWriter.cs" />
<Compile Include="../GetDocumentInsider/Ansi*.cs" />
<Compile Include="../GetDocumentInsider/CommandException.cs" />
<Compile Include="../GetDocumentInsider/CommandLineUtils/*.cs" LinkBase="CommandLineUtils" />
<Compile Include="../GetDocumentInsider/Commands/CommandBase.cs" Link="Commands/CommandBase.cs" />
<Compile Include="../GetDocumentInsider/Commands/HelpCommandBase.cs" Link="Commands/HelpCommandBase.cs" />
<Compile Include="../GetDocumentInsider/ProductInfo.cs" />
<Compile Include="../GetDocumentInsider/Reporter.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ServiceProjectReferenceMetadata.*" />
</ItemGroup>
<EmbeddedResource Include="ServiceProjectReferenceMetadata.targets" />
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
</ItemGroup>
<ItemGroup>
<!-- Additional files to be code signed -->
<SignedPackageFile Include="tools/netcoreapp2.1/any/tools/net461/any/GetDocument.Insider.exe" Certificate="$(AssemblySigningCertName)" />
<SignedPackageFile Include="tools/netcoreapp2.1/any/tools/net461/win-x86/GetDocument.Insider.exe" Certificate="$(AssemblySigningCertName)" />
<SignedPackageFile Include="tools/netcoreapp2.1/any/tools/netcoreapp2.0/any/GetDocument.Insider.dll" Certificate="$(AssemblySigningCertName)" />
<!-- Third-party assemblies should be signed with the 3PartyDual certificate -->
<SignedPackageFile Include="tools/netcoreapp2.1/any/Newtonsoft.Json.dll" Certificate="3PartyDual" />
</ItemGroup>
<Target Name="PopulateNuspec" DependsOnTargets="Build">
<ItemGroup>
<_Temporary Remove="@(Temporary)" />
<_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=net461" />
<_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=net461;Platform=x86" />
<_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=netcoreapp2.0" />
</ItemGroup>
<MSBuild Projects="../GetDocumentInsider/GetDocumentInsider.csproj" RemoveProperties="RuntimeIdentifier;TargetFramework" Targets="Restore" />
<MSBuild Projects="@(_Temporary)" BuildInParallel="$(BuildInParallel)" RemoveProperties="RuntimeIdentifier" Targets="Publish" />
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<PropertyGroup>
<NuspecProperties>
authors=$(Authors);
copyright=$(Copyright);
description=$(Description);
iconUrl=$(PackageIconUrl);
id=$(PackageId);
InsiderNet461Output=..\GetDocumentInsider\bin\$(Configuration)\net461\publish\*;
InsiderNet461X86Output=..\GetDocumentInsider\bin\x86\$(Configuration)\net461\publish\*;
InsiderNetCoreOutput=..\GetDocumentInsider\bin\$(Configuration)\netcoreapp2.0\publish\*;
licenseUrl=$(PackageLicenseUrl);
Output=$(PublishDir)**\*;
OutputShims=$(IntermediateOutputPath)shims\**\*;
packageType=$(PackageType);
projectUrl=$(PackageProjectUrl);
repositoryCommit=$(RepositoryCommit);
repositoryUrl=$(RepositoryUrl);
SettingsFile=$(_ToolsSettingsFilePath);
tags=$(PackageTags.Replace(';', ' '));
targetFramework=$(TargetFramework);
version=$(PackageVersion);
</NuspecProperties>
</PropertyGroup>
</Target>
</Project>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>$id$</id>
<version>$version$</version>
<authors>$authors$</authors>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<licenseUrl>$licenseUrl$</licenseUrl>
<projectUrl>$projectUrl$</projectUrl>
<iconUrl>$iconUrl$</iconUrl>
<description>$description$</description>
<copyright>$copyright$</copyright>
<tags>$tags$</tags>
<packageTypes>
<packageType name="$packageType$" />
</packageTypes>
<repository type="git" url="$repositoryUrl$" commit="$repositoryCommit$" />
</metadata>
<files>
<file src="$InsiderNet461Output$" target="tools\$targetFramework$\any\net461" />
<file src="$InsiderNet461X86Output$" target="tools\$targetFramework$\any\net461-x86" />
<file src="$InsiderNetCoreOutput$" target="tools\$targetFramework$\any\netcoreapp2.0" />
<file src="$Output$" target="tools\$targetFramework$\any" />
<!-- file src="$OutputShims$" target="tools\$targetFramework$\any\shims" / -->
<file src="$SettingsFile$" target="tools\$targetFramework$\any" />
</files>
</package>