Add first cut of Microsoft.Extensions.ApiDescription.Client package / project

- WIP in a number of ways
This commit is contained in:
Doug Bunting 2018-09-03 20:08:00 -07:00
parent 9daf5ff7a4
commit 95b4dc8ca0
No known key found for this signature in database
GPG Key ID: 888B4EB7822B32E9
57 changed files with 4794 additions and 0 deletions

View File

@ -117,6 +117,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Ap
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Api.Analyzers.Test", "test\Mvc.Api.Analyzers.Test\Mvc.Api.Analyzers.Test.csproj", "{71C626FC-6408-494B-A127-38CB64F71324}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-getdocument", "src\dotnet-getdocument\dotnet-getdocument.csproj", "{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GetDocumentInsider", "src\GetDocumentInsider\GetDocumentInsider.csproj", "{2F683CF8-B055-46AE-BF83-9D1307F8D45F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.ApiDescription.Client", "src\Microsoft.Extensions.ApiDescription.Client\Microsoft.Extensions.ApiDescription.Client.csproj", "{34E3C302-B767-40C8-B538-3EE2BD4000C4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -575,6 +581,42 @@ Global
{71C626FC-6408-494B-A127-38CB64F71324}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{71C626FC-6408-494B-A127-38CB64F71324}.Release|x86.ActiveCfg = Release|Any CPU
{71C626FC-6408-494B-A127-38CB64F71324}.Release|x86.Build.0 = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|x86.ActiveCfg = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|x86.Build.0 = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Any CPU.Build.0 = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|x86.ActiveCfg = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|x86.Build.0 = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|x86.ActiveCfg = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|x86.Build.0 = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Any CPU.Build.0 = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|x86.ActiveCfg = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|x86.Build.0 = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|x86.ActiveCfg = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|x86.Build.0 = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Any CPU.Build.0 = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|x86.ActiveCfg = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -619,6 +661,9 @@ Global
{92D959F2-66B8-490A-BA33-DA4421EBC948} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{1B398182-9EAE-400B-A2BD-EFFAC0168A36} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{71C626FC-6408-494B-A127-38CB64F71324} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{2F683CF8-B055-46AE-BF83-9D1307F8D45F} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{34E3C302-B767-40C8-B538-3EE2BD4000C4} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D003597F-372F-4068-A2F0-353BE3C3B39A}

45
Mvc.sln
View File

@ -178,6 +178,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Ap
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorRendering", "benchmarkapps\RazorRendering\RazorRendering.csproj", "{D7C6A696-F232-4288-BCCD-367407E4A934}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-getdocument", "src\dotnet-getdocument\dotnet-getdocument.csproj", "{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GetDocumentInsider", "src\GetDocumentInsider\GetDocumentInsider.csproj", "{2F683CF8-B055-46AE-BF83-9D1307F8D45F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.ApiDescription.Client", "src\Microsoft.Extensions.ApiDescription.Client\Microsoft.Extensions.ApiDescription.Client.csproj", "{34E3C302-B767-40C8-B538-3EE2BD4000C4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -938,6 +944,42 @@ Global
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|x86.ActiveCfg = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|x86.Build.0 = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|x86.ActiveCfg = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Debug|x86.Build.0 = Debug|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Any CPU.Build.0 = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|x86.ActiveCfg = Release|Any CPU
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6}.Release|x86.Build.0 = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|x86.ActiveCfg = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Debug|x86.Build.0 = Debug|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Any CPU.Build.0 = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|x86.ActiveCfg = Release|Any CPU
{2F683CF8-B055-46AE-BF83-9D1307F8D45F}.Release|x86.Build.0 = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|x86.ActiveCfg = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Debug|x86.Build.0 = Debug|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Any CPU.Build.0 = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|x86.ActiveCfg = Release|Any CPU
{34E3C302-B767-40C8-B538-3EE2BD4000C4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1010,6 +1052,9 @@ Global
{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{D7C6A696-F232-4288-BCCD-367407E4A934} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
{4EDC489F-3EC5-4AE3-9841-A285F40F5FF6} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{2F683CF8-B055-46AE-BF83-9D1307F8D45F} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{34E3C302-B767-40C8-B538-3EE2BD4000C4} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {63D344F6-F86D-40E6-85B9-0AABBE338C4A}

View File

@ -54,9 +54,11 @@
<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>
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisRazorPackageVersion>2.2.0-preview3-35359</MicrosoftCodeAnalysisRazorPackageVersion>
@ -100,8 +102,10 @@
<MoqPackageVersion>4.7.49</MoqPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
<NewtonsoftJsonBsonPackageVersion>1.0.1</NewtonsoftJsonBsonPackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<SystemComponentModelAnnotationsPackageVersion>4.5.0</SystemComponentModelAnnotationsPackageVersion>
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0</SystemDiagnosticsDiagnosticSourcePackageVersion>
<SystemNetHttpPackageVersion>4.3.2</SystemNetHttpPackageVersion>
<SystemThreadingTasksExtensionsPackageVersion>4.5.1</SystemThreadingTasksExtensionsPackageVersion>
<XunitAnalyzersPackageVersion>0.10.0</XunitAnalyzersPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion>

View File

@ -0,0 +1,15 @@
// 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 GetDocument
{
internal class AnsiConsole
{
public static readonly AnsiTextWriter _out = new AnsiTextWriter(Console.Out);
public static void WriteLine(string text)
=> _out.WriteLine(text);
}
}

View File

@ -0,0 +1,20 @@
// 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.
namespace GetDocument
{
internal static class AnsiConstants
{
public const string Reset = "\x1b[22m\x1b[39m";
public const string Bold = "\x1b[1m";
public const string Dark = "\x1b[22m";
public const string Black = "\x1b[30m";
public const string Red = "\x1b[31m";
public const string Green = "\x1b[32m";
public const string Yellow = "\x1b[33m";
public const string Blue = "\x1b[34m";
public const string Magenta = "\x1b[35m";
public const string Cyan = "\x1b[36m";
public const string Gray = "\x1b[37m";
}
}

View File

@ -0,0 +1,131 @@
// 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.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
namespace GetDocument
{
internal class AnsiTextWriter
{
private readonly TextWriter _writer;
public AnsiTextWriter(TextWriter writer) => _writer = writer;
public void WriteLine(string text)
{
Interpret(text);
_writer.Write(Environment.NewLine);
}
private void Interpret(string value)
{
var matches = Regex.Matches(value, "\x1b\\[([0-9]+)?m");
var start = 0;
foreach (Match match in matches)
{
var length = match.Index - start;
if (length != 0)
{
_writer.Write(value.Substring(start, length));
}
Apply(match.Groups[1].Value);
start = match.Index + match.Length;
}
if (start != value.Length)
{
_writer.Write(value.Substring(start));
}
}
private static void Apply(string parameter)
{
switch (parameter)
{
case "1":
ApplyBold();
break;
case "22":
ResetBold();
break;
case "30":
ApplyColor(ConsoleColor.Black);
break;
case "31":
ApplyColor(ConsoleColor.DarkRed);
break;
case "32":
ApplyColor(ConsoleColor.DarkGreen);
break;
case "33":
ApplyColor(ConsoleColor.DarkYellow);
break;
case "34":
ApplyColor(ConsoleColor.DarkBlue);
break;
case "35":
ApplyColor(ConsoleColor.DarkMagenta);
break;
case "36":
ApplyColor(ConsoleColor.DarkCyan);
break;
case "37":
ApplyColor(ConsoleColor.Gray);
break;
case "39":
ResetColor();
break;
default:
Debug.Fail("Unsupported parameter: " + parameter);
break;
}
}
private static void ApplyBold()
=> Console.ForegroundColor = (ConsoleColor)((int)Console.ForegroundColor | 8);
private static void ResetBold()
=> Console.ForegroundColor = (ConsoleColor)((int)Console.ForegroundColor & 7);
private static void ApplyColor(ConsoleColor color)
{
var wasBold = ((int)Console.ForegroundColor & 8) != 0;
Console.ForegroundColor = color;
if (wasBold)
{
ApplyBold();
}
}
private static void ResetColor()
{
var wasBold = ((int)Console.ForegroundColor & 8) != 0;
Console.ResetColor();
if (wasBold)
{
ApplyBold();
}
}
}
}

View File

@ -0,0 +1,23 @@
// 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 JetBrains.Annotations
{
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field)]
internal sealed class NotNullAttribute : Attribute
{
}
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field)]
internal sealed class CanBeNullAttribute : Attribute
{
}
}

View File

@ -0,0 +1,20 @@
// 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 GetDocument
{
internal class CommandException : Exception
{
public CommandException(string message)
: base(message)
{
}
public CommandException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,19 @@
// 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.Collections.Generic;
using System.Linq;
namespace Microsoft.DotNet.Cli.CommandLine
{
internal class CommandArgument
{
public CommandArgument() => Values = new List<string>();
public string Name { get; set; }
public string Description { get; set; }
public List<string> Values { get; private set; }
public bool MultipleValues { get; set; }
public string Value => Values.FirstOrDefault();
}
}

View File

@ -0,0 +1,604 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli.CommandLine
{
internal class CommandLineApplication
{
private enum ParseOptionResult
{
Succeeded,
ShowHelp,
ShowVersion,
UnexpectedArgs,
}
// Indicates whether the parser should throw an exception when it runs into an unexpected argument.
// If this field is set to false, the parser will stop parsing when it sees an unexpected argument, and all
// remaining arguments, including the first unexpected argument, will be stored in RemainingArguments property.
private readonly bool _throwOnUnexpectedArg;
public CommandLineApplication(bool throwOnUnexpectedArg = true)
{
_throwOnUnexpectedArg = throwOnUnexpectedArg;
Options = new List<CommandOption>();
Arguments = new List<CommandArgument>();
Commands = new List<CommandLineApplication>();
RemainingArguments = new List<string>();
Invoke = () => 0;
}
public CommandLineApplication Parent { get; set; }
public string Name { get; set; }
public string FullName { get; set; }
public string Syntax { get; set; }
public string Description { get; set; }
public List<CommandOption> Options { get; private set; }
public CommandOption OptionHelp { get; private set; }
public CommandOption OptionVersion { get; private set; }
public List<CommandArgument> Arguments { get; private set; }
public List<string> RemainingArguments { get; private set; }
public bool IsShowingInformation { get; protected set; } // Is showing help or version?
public Func<int> Invoke { get; set; }
public Func<string> LongVersionGetter { get; set; }
public Func<string> ShortVersionGetter { get; set; }
public List<CommandLineApplication> Commands { get; private set; }
public bool HandleResponseFiles { get; set; }
public bool AllowArgumentSeparator { get; set; }
public bool HandleRemainingArguments { get; set; }
public string ArgumentSeparatorHelpText { get; set; }
public CommandLineApplication Command(string name, bool throwOnUnexpectedArg = true)
=> Command(name, _ => { }, throwOnUnexpectedArg);
public CommandLineApplication Command(string name, Action<CommandLineApplication> configuration,
bool throwOnUnexpectedArg = true)
{
var command = new CommandLineApplication(throwOnUnexpectedArg) { Name = name, Parent = this };
Commands.Add(command);
configuration(command);
return command;
}
public CommandOption Option(string template, string description, CommandOptionType optionType)
=> Option(template, description, optionType, _ => { });
public CommandOption Option(string template, string description, CommandOptionType optionType, Action<CommandOption> configuration)
{
var option = new CommandOption(template, optionType) { Description = description };
Options.Add(option);
configuration(option);
return option;
}
public CommandArgument Argument(string name, string description, bool multipleValues = false)
=> Argument(name, description, _ => { }, multipleValues);
public CommandArgument Argument(string name, string description, Action<CommandArgument> configuration, bool multipleValues = false)
{
var lastArg = Arguments.LastOrDefault();
if (lastArg != null && lastArg.MultipleValues)
{
var message = string.Format("The last argument '{0}' accepts multiple values. No more argument can be added.",
lastArg.Name);
throw new InvalidOperationException(message);
}
var argument = new CommandArgument { Name = name, Description = description, MultipleValues = multipleValues };
Arguments.Add(argument);
configuration(argument);
return argument;
}
public void OnExecute(Func<int> invoke) => Invoke = invoke;
public void OnExecute(Func<Task<int>> invoke) => Invoke = () => invoke().Result;
public int Execute(params string[] args)
{
var command = this;
IEnumerator<CommandArgument> arguments = null;
if (HandleResponseFiles)
{
args = ExpandResponseFiles(args).ToArray();
}
for (var index = 0; index < args.Length; index++)
{
var arg = args[index];
var isLongOption = arg.StartsWith("--");
if (isLongOption || arg.StartsWith("-"))
{
var result = ParseOption(isLongOption, command, args, ref index, out var option);
if (result == ParseOptionResult.ShowHelp)
{
command.ShowHelp();
return 0;
}
else if (result == ParseOptionResult.ShowVersion)
{
command.ShowVersion();
return 0;
}
}
else
{
var subcommand = ParseSubCommand(arg, command);
if (subcommand != null)
{
command = subcommand;
}
else
{
if (arguments == null)
{
arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator());
}
if (arguments.MoveNext())
{
arguments.Current.Values.Add(arg);
}
else
{
HandleUnexpectedArg(command, args, index, argTypeName: "command or argument");
}
}
}
}
return command.Invoke();
}
private ParseOptionResult ParseOption(
bool isLongOption,
CommandLineApplication command,
string[] args,
ref int index,
out CommandOption option)
{
option = null;
var result = ParseOptionResult.Succeeded;
var arg = args[index];
var optionPrefixLength = isLongOption ? 2 : 1;
var optionComponents = arg.Substring(optionPrefixLength).Split(new[] { ':', '=' }, 2);
var optionName = optionComponents[0];
if (isLongOption)
{
option = command.Options.SingleOrDefault(
opt => string.Equals(opt.LongName, optionName, StringComparison.Ordinal));
}
else
{
option = command.Options.SingleOrDefault(
opt => string.Equals(opt.ShortName, optionName, StringComparison.Ordinal));
if (option == null)
{
option = command.Options.SingleOrDefault(
opt => string.Equals(opt.SymbolName, optionName, StringComparison.Ordinal));
}
}
if (option == null)
{
if (isLongOption && string.IsNullOrEmpty(optionName) &&
!command._throwOnUnexpectedArg && AllowArgumentSeparator)
{
// a stand-alone "--" is the argument separator, so skip it and
// handle the rest of the args as unexpected args
index++;
}
HandleUnexpectedArg(command, args, index, argTypeName: "option");
result = ParseOptionResult.UnexpectedArgs;
}
else if (command.OptionHelp == option)
{
result = ParseOptionResult.ShowHelp;
}
else if (command.OptionVersion == option)
{
result = ParseOptionResult.ShowVersion;
}
else
{
if (optionComponents.Length == 2)
{
if (!option.TryParse(optionComponents[1]))
{
command.ShowHint();
throw new CommandParsingException(command,
$"Unexpected value '{optionComponents[1]}' for option '{optionName}'");
}
}
else
{
if (option.OptionType == CommandOptionType.NoValue ||
option.OptionType == CommandOptionType.BoolValue)
{
// No value is needed for this option
option.TryParse(null);
}
else
{
index++;
arg = args[index];
if (!option.TryParse(arg))
{
command.ShowHint();
throw new CommandParsingException(command, $"Unexpected value '{arg}' for option '{optionName}'");
}
}
}
}
return result;
}
private static CommandLineApplication ParseSubCommand(string arg, CommandLineApplication command)
{
foreach (var subcommand in command.Commands)
{
if (string.Equals(subcommand.Name, arg, StringComparison.OrdinalIgnoreCase))
{
return subcommand;
}
}
return null;
}
// Helper method that adds a help option
public CommandOption HelpOption(string template)
{
// Help option is special because we stop parsing once we see it
// So we store it separately for further use
OptionHelp = Option(template, "Show help information", CommandOptionType.NoValue);
return OptionHelp;
}
public CommandOption VersionOption(string template,
string shortFormVersion,
string longFormVersion = null)
{
if (longFormVersion == null)
{
return VersionOption(template, () => shortFormVersion);
}
else
{
return VersionOption(template, () => shortFormVersion, () => longFormVersion);
}
}
// Helper method that adds a version option
public CommandOption VersionOption(string template,
Func<string> shortFormVersionGetter,
Func<string> longFormVersionGetter = null)
{
// Version option is special because we stop parsing once we see it
// So we store it separately for further use
OptionVersion = Option(template, "Show version information", CommandOptionType.NoValue);
ShortVersionGetter = shortFormVersionGetter;
LongVersionGetter = longFormVersionGetter ?? shortFormVersionGetter;
return OptionVersion;
}
// Show short hint that reminds users to use help option
public void ShowHint()
{
if (OptionHelp != null)
{
Console.WriteLine(string.Format("Specify --{0} for a list of available options and commands.", OptionHelp.LongName));
}
}
// Show full help
public void ShowHelp(string commandName = null)
{
var headerBuilder = new StringBuilder("Usage:");
var usagePrefixLength = headerBuilder.Length;
for (var cmd = this; cmd != null; cmd = cmd.Parent)
{
cmd.IsShowingInformation = true;
if (cmd != this && cmd.Arguments.Any())
{
var args = string.Join(" ", cmd.Arguments.Select(arg => arg.Name));
headerBuilder.Insert(usagePrefixLength, string.Format(" {0} {1}", cmd.Name, args));
}
else
{
headerBuilder.Insert(usagePrefixLength, string.Format(" {0}", cmd.Name));
}
}
CommandLineApplication target;
if (commandName == null || string.Equals(Name, commandName, StringComparison.OrdinalIgnoreCase))
{
target = this;
}
else
{
target = Commands.SingleOrDefault(cmd => string.Equals(cmd.Name, commandName, StringComparison.OrdinalIgnoreCase));
if (target != null)
{
headerBuilder.AppendFormat(" {0}", commandName);
}
else
{
// The command name is invalid so don't try to show help for something that doesn't exist
target = this;
}
}
var optionsBuilder = new StringBuilder();
var commandsBuilder = new StringBuilder();
var argumentsBuilder = new StringBuilder();
var argumentSeparatorBuilder = new StringBuilder();
var maxArgLen = 0;
for (var cmd = target; cmd != null; cmd = cmd.Parent)
{
if (cmd.Arguments.Any())
{
if (cmd == target)
{
headerBuilder.Append(" [arguments]");
}
if (argumentsBuilder.Length == 0)
{
argumentsBuilder.AppendLine();
argumentsBuilder.AppendLine("Arguments:");
}
maxArgLen = Math.Max(maxArgLen, MaxArgumentLength(cmd.Arguments));
}
}
for (var cmd = target; cmd != null; cmd = cmd.Parent)
{
if (cmd.Arguments.Any())
{
var outputFormat = " {0}{1}";
foreach (var arg in cmd.Arguments)
{
argumentsBuilder.AppendFormat(
outputFormat,
arg.Name.PadRight(maxArgLen + 2),
arg.Description);
argumentsBuilder.AppendLine();
}
}
}
if (target.Options.Any())
{
headerBuilder.Append(" [options]");
optionsBuilder.AppendLine();
optionsBuilder.AppendLine("Options:");
var maxOptLen = MaxOptionTemplateLength(target.Options);
var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxOptLen + 2);
foreach (var opt in target.Options)
{
optionsBuilder.AppendFormat(outputFormat, opt.Template, opt.Description);
optionsBuilder.AppendLine();
}
}
if (target.Commands.Any())
{
headerBuilder.Append(" [command]");
commandsBuilder.AppendLine();
commandsBuilder.AppendLine("Commands:");
var maxCmdLen = MaxCommandLength(target.Commands);
var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxCmdLen + 2);
foreach (var cmd in target.Commands.OrderBy(c => c.Name))
{
commandsBuilder.AppendFormat(outputFormat, cmd.Name, cmd.Description);
commandsBuilder.AppendLine();
}
if (OptionHelp != null)
{
commandsBuilder.AppendLine();
commandsBuilder.AppendFormat("Use \"{0} [command] --help\" for more information about a command.", Name);
commandsBuilder.AppendLine();
}
}
if (target.AllowArgumentSeparator || target.HandleRemainingArguments)
{
if (target.AllowArgumentSeparator)
{
headerBuilder.Append(" [[--] <arg>...]]");
}
else
{
headerBuilder.Append(" [args]");
}
if (!string.IsNullOrEmpty(target.ArgumentSeparatorHelpText))
{
argumentSeparatorBuilder.AppendLine();
argumentSeparatorBuilder.AppendLine("Args:");
argumentSeparatorBuilder.AppendLine($" {target.ArgumentSeparatorHelpText}");
argumentSeparatorBuilder.AppendLine();
}
}
headerBuilder.AppendLine();
var nameAndVersion = new StringBuilder();
nameAndVersion.AppendLine(GetFullNameAndVersion());
nameAndVersion.AppendLine();
Console.Write("{0}{1}{2}{3}{4}{5}", nameAndVersion, headerBuilder, argumentsBuilder, optionsBuilder, commandsBuilder, argumentSeparatorBuilder);
}
public void ShowVersion()
{
for (var cmd = this; cmd != null; cmd = cmd.Parent)
{
cmd.IsShowingInformation = true;
}
Console.WriteLine(FullName);
Console.WriteLine(LongVersionGetter());
}
public string GetFullNameAndVersion()
=> ShortVersionGetter == null ? FullName : string.Format("{0} {1}", FullName, ShortVersionGetter());
public void ShowRootCommandFullNameAndVersion()
{
var rootCmd = this;
while (rootCmd.Parent != null)
{
rootCmd = rootCmd.Parent;
}
Console.WriteLine(rootCmd.GetFullNameAndVersion());
Console.WriteLine();
}
private static int MaxOptionTemplateLength(IEnumerable<CommandOption> options)
{
var maxLen = 0;
foreach (var opt in options)
{
maxLen = opt.Template.Length > maxLen ? opt.Template.Length : maxLen;
}
return maxLen;
}
private static int MaxCommandLength(IEnumerable<CommandLineApplication> commands)
{
var maxLen = 0;
foreach (var cmd in commands)
{
maxLen = cmd.Name.Length > maxLen ? cmd.Name.Length : maxLen;
}
return maxLen;
}
private static int MaxArgumentLength(IEnumerable<CommandArgument> arguments)
{
var maxLen = 0;
foreach (var arg in arguments)
{
maxLen = arg.Name.Length > maxLen ? arg.Name.Length : maxLen;
}
return maxLen;
}
private static void HandleUnexpectedArg(CommandLineApplication command, string[] args, int index, string argTypeName)
{
if (command._throwOnUnexpectedArg)
{
command.ShowHint();
throw new CommandParsingException(command, $"Unrecognized {argTypeName} '{args[index]}'");
}
else
{
command.RemainingArguments.Add(args[index]);
}
}
private IEnumerable<string> ExpandResponseFiles(IEnumerable<string> args)
{
foreach (var arg in args)
{
if (!arg.StartsWith("@", StringComparison.Ordinal))
{
yield return arg;
}
else
{
var fileName = arg.Substring(1);
var responseFileArguments = ParseResponseFile(fileName);
// ParseResponseFile can suppress expanding this response file by
// returning null. In that case, we'll treat the response
// file token as a regular argument.
if (responseFileArguments == null)
{
yield return arg;
}
else
{
foreach (var responseFileArgument in responseFileArguments)
{
yield return responseFileArgument.Trim();
}
}
}
}
}
private IEnumerable<string> ParseResponseFile(string fileName)
{
if (!HandleResponseFiles)
{
return null;
}
if (!File.Exists(fileName))
{
throw new InvalidOperationException($"Response file '{fileName}' doesn't exist.");
}
return File.ReadLines(fileName);
}
private class CommandArgumentEnumerator : IEnumerator<CommandArgument>
{
private readonly IEnumerator<CommandArgument> _enumerator;
public CommandArgumentEnumerator(IEnumerator<CommandArgument> enumerator) => _enumerator = enumerator;
public CommandArgument Current => _enumerator.Current;
object IEnumerator.Current => Current;
public void Dispose() => _enumerator.Dispose();
public bool MoveNext()
{
if (Current == null || !Current.MultipleValues)
{
return _enumerator.MoveNext();
}
// If current argument allows multiple values, we don't move forward and
// all later values will be added to current CommandArgument.Values
return true;
}
public void Reset() => _enumerator.Reset();
}
}
}

View File

@ -0,0 +1,18 @@
// 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.
namespace Microsoft.DotNet.Cli.CommandLine
{
internal static class CommandLineApplicationExtensions
{
public static CommandOption Option(this CommandLineApplication command, string template, string description)
=> command.Option(
template,
description,
template.IndexOf('<') != -1
? template.EndsWith(">...")
? CommandOptionType.MultipleValue
: CommandOptionType.SingleValue
: CommandOptionType.NoValue);
}
}

View File

@ -0,0 +1,125 @@
// 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.Linq;
namespace Microsoft.DotNet.Cli.CommandLine
{
internal class CommandOption
{
public CommandOption(string template, CommandOptionType optionType)
{
Template = template;
OptionType = optionType;
Values = new List<string>();
foreach (var part in Template.Split(new[] { ' ', '|' }, StringSplitOptions.RemoveEmptyEntries))
{
if (part.StartsWith("--"))
{
LongName = part.Substring(2);
}
else if (part.StartsWith("-"))
{
var optName = part.Substring(1);
// If there is only one char and it is not an English letter, it is a symbol option (e.g. "-?")
if (optName.Length == 1 && !IsEnglishLetter(optName[0]))
{
SymbolName = optName;
}
else
{
ShortName = optName;
}
}
else if (part.StartsWith("<") && part.EndsWith(">"))
{
ValueName = part.Substring(1, part.Length - 2);
}
else if (optionType == CommandOptionType.MultipleValue && part.StartsWith("<") && part.EndsWith(">..."))
{
ValueName = part.Substring(1, part.Length - 5);
}
else
{
throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template));
}
}
if (string.IsNullOrEmpty(LongName) && string.IsNullOrEmpty(ShortName) && string.IsNullOrEmpty(SymbolName))
{
throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template));
}
}
public string Template { get; set; }
public string ShortName { get; set; }
public string LongName { get; set; }
public string SymbolName { get; set; }
public string ValueName { get; set; }
public string Description { get; set; }
public List<string> Values { get; private set; }
public bool? BoolValue { get; private set; }
public CommandOptionType OptionType { get; private set; }
public bool TryParse(string value)
{
switch (OptionType)
{
case CommandOptionType.MultipleValue:
Values.Add(value);
break;
case CommandOptionType.SingleValue:
if (Values.Any())
{
return false;
}
Values.Add(value);
break;
case CommandOptionType.BoolValue:
if (Values.Any())
{
return false;
}
if (value == null)
{
// add null to indicate that the option was present, but had no value
Values.Add(null);
BoolValue = true;
}
else
{
if (!bool.TryParse(value, out var boolValue))
{
return false;
}
Values.Add(value);
BoolValue = boolValue;
}
break;
case CommandOptionType.NoValue:
if (value != null)
{
return false;
}
// Add a value to indicate that this option was specified
Values.Add("on");
break;
default:
break;
}
return true;
}
public bool HasValue() => Values.Any();
public string Value() => HasValue() ? Values[0] : null;
private static bool IsEnglishLetter(char c) => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
}

View File

@ -0,0 +1,13 @@
// 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.
namespace Microsoft.DotNet.Cli.CommandLine
{
internal enum CommandOptionType
{
MultipleValue,
SingleValue,
BoolValue,
NoValue
}
}

View File

@ -0,0 +1,15 @@
// 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.DotNet.Cli.CommandLine
{
internal class CommandParsingException : Exception
{
public CommandParsingException(CommandLineApplication command, string message)
: base(message) => Command = command;
public CommandLineApplication Command { get; }
}
}

View File

@ -0,0 +1,39 @@
// 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 GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument.Commands
{
internal abstract class CommandBase
{
public virtual void Configure(CommandLineApplication command)
{
var verbose = command.Option("-v|--verbose", Resources.VerboseDescription);
var noColor = command.Option("--no-color", Resources.NoColorDescription);
var prefixOutput = command.Option("--prefix-output", Resources.PrefixDescription);
command.HandleResponseFiles = true;
command.OnExecute(
() =>
{
Reporter.IsVerbose = verbose.HasValue();
Reporter.NoColor = noColor.HasValue();
Reporter.PrefixOutput = prefixOutput.HasValue();
Validate();
return Execute();
});
}
protected virtual void Validate()
{
}
protected virtual int Execute()
=> 0;
}
}

View File

@ -0,0 +1,175 @@
// 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 GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument.Commands
{
internal class GetDocumentCommand : ProjectCommandBase
{
internal const string FallbackDocumentName = "v1";
internal const string FallbackMethod = "Generate";
internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider";
private const string WorkerType = "GetDocument.Commands.GetDocumentCommandWorker";
private CommandOption _documentName;
private CommandOption _method;
private CommandOption _output;
private CommandOption _service;
private CommandOption _uri;
public override void Configure(CommandLineApplication command)
{
base.Configure(command);
_documentName = command.Option(
"--documentName <Name>",
Resources.DocumentDescription(FallbackDocumentName));
_method = command.Option("--method <Name>", Resources.MethodDescription(FallbackMethod));
_output = command.Option("--output <Path>", Resources.OutputDescription);
_service = command.Option("--service <QualifiedName>", Resources.ServiceDescription(FallbackService));
_uri = command.Option("--uri <URI>", Resources.UriDescription);
}
protected override void Validate()
{
base.Validate();
if (!_output.HasValue())
{
throw new CommandException(Resources.MissingOption(_output.LongName));
}
if (_method.HasValue() && !_service.HasValue())
{
throw new CommandException(Resources.MissingOption(_service.LongName));
}
if (_service.HasValue() && !_method.HasValue())
{
throw new CommandException(Resources.MissingOption(_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 TestHost type.
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
{
AssemblyPath = assemblyPath,
AssemblyDirectory = Path.GetDirectoryName(assemblyPath),
AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath),
DocumentName = _documentName.Value(),
Method = _method.Value(),
Output = _output.Value(),
Service = _service.Value(),
Uri = _uri.Value(),
};
return (int)methodInfo.Invoke(obj: null, parameters: new[] { 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; }
}
}
}

View File

@ -0,0 +1,24 @@
using System;
namespace GetDocument.Commands
{
[Serializable]
public class GetDocumentCommandContext
{
public string AssemblyDirectory { get; set; }
public string AssemblyName { get; set; }
public string AssemblyPath { get; set; }
public string DocumentName { get; set; }
public string Method { get; set; }
public string Output { get; set; }
public string Service { get; set; }
public string Uri { get; set; }
}
}

View File

@ -0,0 +1,260 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using GenerationTasks;
using GetDocument.Properties;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace GetDocument.Commands
{
internal class GetDocumentCommandWorker
{
public static int Process(GetDocumentCommandContext context)
{
var assemblyName = new AssemblyName(context.AssemblyName);
var assembly = Assembly.Load(assemblyName);
var entryPointType = assembly.EntryPoint?.DeclaringType;
if (entryPointType == null)
{
Reporter.WriteError(Resources.MissingEntryPoint(context.AssemblyPath));
return 2;
}
var services = GetServices(entryPointType, context.AssemblyPath, context.AssemblyName);
if (services == null)
{
return 3;
}
var success = TryProcess(context, services);
if (!success && string.IsNullOrEmpty(context.Uri))
{
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();
}
return 0;
}
public static bool TryProcess(GetDocumentCommandContext context, IServiceProvider services)
{
var documentName = string.IsNullOrEmpty(context.DocumentName) ?
GetDocumentCommand.FallbackDocumentName :
context.DocumentName;
var methodName = string.IsNullOrEmpty(context.Method) ?
GetDocumentCommand.FallbackMethod :
context.Method;
var serviceName = string.IsNullOrEmpty(context.Service) ?
GetDocumentCommand.FallbackService :
context.Service;
Reporter.WriteInformation(Resources.UsingDocument(documentName));
Reporter.WriteInformation(Resources.UsingMethod(methodName));
Reporter.WriteInformation(Resources.UsingService(serviceName));
try
{
var serviceType = Type.GetType(serviceName, throwOnError: true);
var method = serviceType.GetMethod(methodName, new[] { typeof(TextWriter), typeof(string) });
var service = services.GetRequiredService(serviceType);
var success = true;
using (var writer = File.CreateText(context.Output))
{
if (method.ReturnType == typeof(bool))
{
success = (bool)method.Invoke(service, new object[] { writer, documentName });
}
else
{
method.Invoke(service, new object[] { writer, documentName });
}
}
if (!success)
{
var message = Resources.MethodInvocationFailed(methodName, serviceName, documentName);
if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output))
{
Reporter.WriteError(message);
}
else
{
Reporter.WriteWarning(message);
}
}
return success;
}
catch (Exception ex)
{
var message = FormatException(ex);
if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output))
{
Reporter.WriteError(message);
}
else
{
Reporter.WriteWarning(message);
}
return false;
}
}
public static async Task ProcessAsync(GetDocumentCommandContext context, TestServer server)
{
Debug.Assert(!string.IsNullOrEmpty(context.Uri));
Reporter.WriteInformation(Resources.UsingUri(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)
{
var args = new[] { Array.Empty<string>() };
var methodInfo = entryPointType.GetMethod("BuildWebHost");
if (methodInfo != null)
{
// BuildWebHost (old style has highest priority)
var parameters = methodInfo.GetParameters();
if (!methodInfo.IsStatic ||
parameters.Length != 1 ||
typeof(string[]) != parameters[0].ParameterType ||
typeof(IWebHost) != methodInfo.ReturnType)
{
Reporter.WriteError(
"BuildWebHost method found in {assemblyPath} does not have expected signature.");
return null;
}
try
{
var webHost = (IWebHost)methodInfo.Invoke(obj: null, parameters: args);
return webHost.Services;
}
catch (Exception ex)
{
Reporter.WriteError($"BuildWebHost method threw: {FormatException(ex)}");
return null;
}
}
if ((methodInfo = entryPointType.GetMethod("CreateWebHostBuilder")) != 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 builder = (IWebHostBuilder)methodInfo.Invoke(obj: null, parameters: args);
return builder.Build().Services;
}
catch (Exception ex)
{
Reporter.WriteError($"CreateWebHostBuilder method threw: {FormatException(ex)}");
return null;
}
}
// 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);
}
private static string FormatException(Exception exception)
{
return $"{exception.GetType().FullName}: {exception.Message}";
}
}
}

View File

@ -0,0 +1,17 @@
// 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 GetDocument.Commands
{
internal class HelpCommandBase : CommandBase
{
public override void Configure(CommandLineApplication command)
{
base.Configure(command);
command.HelpOption("-h|--help");
}
}
}

View File

@ -0,0 +1,38 @@
// 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 GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument.Commands
{
internal abstract class ProjectCommandBase : HelpCommandBase
{
public CommandOption AssemblyPath { get; private set; }
public CommandOption ToolsDirectory { get; private set; }
public override void Configure(CommandLineApplication command)
{
base.Configure(command);
AssemblyPath = command.Option("-a|--assembly <PATH>", Resources.AssemblyDescription);
ToolsDirectory = command.Option("--tools-directory <PATH>", Resources.ToolsDirectoryDescription);
}
protected override void Validate()
{
base.Validate();
if (!AssemblyPath.HasValue())
{
throw new CommandException(Resources.MissingOption(AssemblyPath.LongName));
}
if (!ToolsDirectory.HasValue())
{
throw new CommandException(Resources.MissingOption(ToolsDirectory.LongName));
}
}
}
}

View File

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>GetDocument.Insider</AssemblyName>
<Description>GetDocument Command-line Tool inside man</Description>
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
<RootNamespace>GetDocument</RootNamespace>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="../Microsoft.Extensions.ApiDescription.Client/DownloadFileCore.cs" />
<Compile Include="../Microsoft.Extensions.ApiDescription.Client/ILogWrapper.cs" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHost20PackageVersion)" />
</ItemGroup>
<ItemGroup>
<None Update="Properties/Resources.Designer.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties/Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.Designer.tt</DependentUpon>
</Compile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
// 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 GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument
{
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

@ -0,0 +1,29 @@
using System;
using GenerationTasks;
namespace GetDocument
{
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

@ -0,0 +1,24 @@
// 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.Reflection;
namespace GetDocument
{
/// <summary>
/// This API supports the GetDocument infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static class ProductInfo
{
/// <summary>
/// This API supports the GetDocument infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string GetVersion()
=> typeof(ProductInfo)
.Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
.InformationalVersion;
}
}

View File

@ -0,0 +1,51 @@
// 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.Text;
using GetDocument.Commands;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument
{
internal static class Program
{
private static int Main(string[] args)
{
if (Console.IsOutputRedirected)
{
Console.OutputEncoding = Encoding.UTF8;
}
var app = new CommandLineApplication(throwOnUnexpectedArg: false)
{
Name = "GetDocument.Insider"
};
new GetDocumentCommand().Configure(app);
try
{
return app.Execute(args);
}
catch (Exception ex)
{
if (ex is CommandException
|| ex is CommandParsingException
|| (ex is WrappedException wrappedException
&& wrappedException.Type == "GetDocument.Design.OperationException"))
{
Reporter.WriteVerbose(ex.ToString());
}
else
{
Reporter.WriteInformation(ex.ToString());
}
Reporter.WriteError(ex.Message);
return 1;
}
}
}
}

View File

@ -0,0 +1,207 @@
// <auto-generated />
using System;
using System.Reflection;
using System.Resources;
using JetBrains.Annotations;
namespace GetDocument.Properties
{
/// <summary>
/// This API supports the GetDocument infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("GetDocument.Properties.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The assembly to use.
/// </summary>
public static string AssemblyDescription
=> GetString("AssemblyDescription");
/// <summary>
/// Show JSON output.
/// </summary>
public static string JsonDescription
=> GetString("JsonDescription");
/// <summary>
/// Missing required option '--{option}'.
/// </summary>
public static string MissingOption([CanBeNull] object option)
=> string.Format(
GetString("MissingOption", nameof(option)),
option);
/// <summary>
/// Do not colorize output.
/// </summary>
public static string NoColorDescription
=> GetString("NoColorDescription");
/// <summary>
/// The file to write the result to.
/// </summary>
public static string OutputDescription
=> GetString("OutputDescription");
/// <summary>
/// Prefix console output with logging level.
/// </summary>
public static string PrefixDescription
=> GetString("PrefixDescription");
/// <summary>
/// Using application base '{appBase}'.
/// </summary>
public static string UsingApplicationBase([CanBeNull] object appBase)
=> string.Format(
GetString("UsingApplicationBase", nameof(appBase)),
appBase);
/// <summary>
/// Using assembly '{assembly}'.
/// </summary>
public static string UsingAssembly([CanBeNull] object assembly)
=> string.Format(
GetString("UsingAssembly", nameof(assembly)),
assembly);
/// <summary>
/// Using configuration file '{config}'.
/// </summary>
public static string UsingConfigurationFile([CanBeNull] object config)
=> string.Format(
GetString("UsingConfigurationFile", nameof(config)),
config);
/// <summary>
/// Show verbose output.
/// </summary>
public static string VerboseDescription
=> GetString("VerboseDescription");
/// <summary>
/// Writing '{file}'...
/// </summary>
public static string WritingFile([CanBeNull] object file)
=> string.Format(
GetString("WritingFile", nameof(file)),
file);
/// <summary>
/// Using working directory '{workingDirectory}'.
/// </summary>
public static string UsingWorkingDirectory([CanBeNull] object workingDirectory)
=> string.Format(
GetString("UsingWorkingDirectory", nameof(workingDirectory)),
workingDirectory);
/// <summary>
/// Location from which inside man was copied (in the .NET Framework case) or loaded.
/// </summary>
public static string ToolsDirectoryDescription
=> GetString("ToolsDirectoryDescription");
/// <summary>
/// The URI to download the document from.
/// </summary>
public static string UriDescription
=> GetString("UriDescription");
/// <summary>
/// The name of the method to invoke on the '--service' instance. Default value '{defaultMethod}'.
/// </summary>
public static string MethodDescription([CanBeNull] object defaultMethod)
=> string.Format(
GetString("MethodDescription", nameof(defaultMethod)),
defaultMethod);
/// <summary>
/// The qualified name of the service type to retrieve from dependency injection. Default value '{defaultService}'.
/// </summary>
public static string ServiceDescription([CanBeNull] object defaultService)
=> string.Format(
GetString("ServiceDescription", nameof(defaultService)),
defaultService);
/// <summary>
/// Missing required option '--{option1}' or '--{option2}'.
/// </summary>
public static string MissingOptions([CanBeNull] object option1, [CanBeNull] object option2)
=> string.Format(
GetString("MissingOptions", nameof(option1), nameof(option2)),
option1, option2);
/// <summary>
/// The name of the document to pass to the '--method' method. Default value '{defaultDocumentName}'.
/// </summary>
public static string DocumentDescription([CanBeNull] object defaultDocumentName)
=> string.Format(
GetString("DocumentDescription", nameof(defaultDocumentName)),
defaultDocumentName);
/// <summary>
/// Using document name '{documentName}'.
/// </summary>
public static string UsingDocument([CanBeNull] object documentName)
=> string.Format(
GetString("UsingDocument", nameof(documentName)),
documentName);
/// <summary>
/// Using method '{method}'.
/// </summary>
public static string UsingMethod([CanBeNull] object method)
=> string.Format(
GetString("UsingMethod", nameof(method)),
method);
/// <summary>
/// Using service '{service}'.
/// </summary>
public static string UsingService([CanBeNull] object service)
=> string.Format(
GetString("UsingService", nameof(service)),
service);
/// <summary>
/// Using URI '{uri}'.
/// </summary>
public static string UsingUri([CanBeNull] object uri)
=> string.Format(
GetString("UsingUri", nameof(uri)),
uri);
/// <summary>
/// Method '{method}' of service '{service}' failed to generate document '{documentName}'.
/// </summary>
public static string MethodInvocationFailed([CanBeNull] object method, [CanBeNull] object service, [CanBeNull] object documentName)
=> string.Format(
GetString("MethodInvocationFailed", nameof(method), nameof(service), nameof(documentName)),
method, service, documentName);
/// <summary>
/// Assembly '{assemblyPath}' does not contain an entry point.
/// </summary>
public static string MissingEntryPoint([CanBeNull] object assemblyPath)
=> string.Format(
GetString("MissingEntryPoint", nameof(assemblyPath)),
assemblyPath);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
return value;
}
}
}

View File

@ -0,0 +1,6 @@
<#
Session["ResourceFile"] = "Resources.resx";
Session["AccessModifier"] = "internal";
Session["NoDiagnostics"] = true;
#>
<#@ include file="..\..\tools\Resources.tt" #>

View File

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<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 '--{option}'.</value>
</data>
<data name="NoColorDescription" xml:space="preserve">
<value>Do not colorize output.</value>
</data>
<data name="OutputDescription" xml:space="preserve">
<value>The file to write the result to.</value>
</data>
<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 '{appBase}'.</value>
</data>
<data name="UsingAssembly" xml:space="preserve">
<value>Using assembly '{assembly}'.</value>
</data>
<data name="UsingConfigurationFile" xml:space="preserve">
<value>Using configuration file '{config}'.</value>
</data>
<data name="VerboseDescription" xml:space="preserve">
<value>Show verbose output.</value>
</data>
<data name="WritingFile" xml:space="preserve">
<value>Writing '{file}'...</value>
</data>
<data name="UsingWorkingDirectory" xml:space="preserve">
<value>Using working directory '{workingDirectory}'.</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 '{defaultMethod}'.</value>
</data>
<data name="ServiceDescription" xml:space="preserve">
<value>The qualified name of the service type to retrieve from dependency injection. Default value '{defaultService}'.</value>
</data>
<data name="MissingOptions" xml:space="preserve">
<value>Missing required option '--{option1}' or '--{option2}'.</value>
</data>
<data name="DocumentDescription" xml:space="preserve">
<value>The name of the document to pass to the '--method' method. Default value '{defaultDocumentName}'.</value>
</data>
<data name="UsingDocument" xml:space="preserve">
<value>Using document name '{documentName}'.</value>
</data>
<data name="UsingMethod" xml:space="preserve">
<value>Using method '{method}'.</value>
</data>
<data name="UsingService" xml:space="preserve">
<value>Using service '{service}'.</value>
</data>
<data name="UsingUri" xml:space="preserve">
<value>Using URI '{uri}'.</value>
</data>
<data name="MethodInvocationFailed" xml:space="preserve">
<value>Method '{method}' of service '{service}' failed to generate document '{documentName}'.</value>
</data>
<data name="MissingEntryPoint" xml:space="preserve">
<value>Assembly '{assemblyPath}' does not contain an entry point.</value>
</data>
</root>

View File

@ -0,0 +1,58 @@
// 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.Linq;
using static GetDocument.AnsiConstants;
namespace GetDocument
{
internal static class Reporter
{
public static bool IsVerbose { get; set; }
public static bool NoColor { get; set; }
public static bool PrefixOutput { get; set; }
public static string Colorize(string value, Func<string, string> colorizeFunc)
=> NoColor ? value : colorizeFunc(value);
public static void WriteError(string message)
=> WriteLine(Prefix("error: ", Colorize(message, x => Bold + Red + x + Reset)));
public static void WriteWarning(string message)
=> WriteLine(Prefix("warn: ", Colorize(message, x => Bold + Yellow + x + Reset)));
public static void WriteInformation(string message)
=> WriteLine(Prefix("info: ", message));
public static void WriteData(string message)
=> WriteLine(Prefix("data: ", Colorize(message, x => Bold + Gray + x + Reset)));
public static void WriteVerbose(string message)
{
if (IsVerbose)
{
WriteLine(Prefix("verbose: ", Colorize(message, x => Bold + Black + x + Reset)));
}
}
private static string Prefix(string prefix, string value)
=> PrefixOutput
? string.Join(
Environment.NewLine,
value.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Select(l => prefix + l))
: value;
private static void WriteLine(string value)
{
if (NoColor)
{
Console.WriteLine(value);
}
else
{
AnsiConsole.WriteLine(value);
}
}
}
}

View File

@ -0,0 +1,24 @@
// 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 GetDocument
{
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

@ -0,0 +1,113 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Task = System.Threading.Tasks.Task;
using Utilities = Microsoft.Build.Utilities;
namespace GenerationTasks
{
/// <summary>
/// Downloads a file.
/// </summary>
public class DownloadFile : Utilities.Task, ICancelableTask
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
/// <summary>
/// The URI to download.
/// </summary>
[Required]
public string Uri { get; set; }
/// <summary>
/// Destination for the downloaded file. If the file already exists, it is not re-downloaded unless
/// <see cref="Overwrite"/> is true.
/// </summary>
[Required]
public string DestinationPath { get; set; }
/// <summary>
/// Should <see cref="DestinationPath"/> be overwritten. When <c>true</c>, the file is downloaded and its hash
/// compared to the existing file. If those hashes do not match (or <see cref="DestinationPath"/> does not
/// exist), <see cref="DestinationPath"/> is overwritten.
/// </summary>
public bool Overwrite { get; set; }
/// <summary>
/// The maximum amount of time in seconds to allow for downloading the file. Defaults to 2 minutes.
/// </summary>
public int TimeoutSeconds { get; set; } = 60 * 2;
/// <inheritdoc/>
public void Cancel() => _cts.Cancel();
/// <inheritdoc/>
public override bool Execute() => ExecuteAsync().Result;
public async Task<bool> ExecuteAsync()
{
if (string.IsNullOrEmpty(Uri))
{
Log.LogError("Uri parameter must not be null or empty.");
return false;
}
if (string.IsNullOrEmpty(Uri))
{
Log.LogError("DestinationPath parameter must not be null or empty.");
return false;
}
var builder = new UriBuilder(Uri);
if (!string.Equals(System.Uri.UriSchemeHttp, builder.Scheme, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(System.Uri.UriSchemeHttps, builder.Scheme, StringComparison.OrdinalIgnoreCase))
{
Log.LogError($"{nameof(Uri)} parameter does not have scheme {System.Uri.UriSchemeHttp} or " +
$"{System.Uri.UriSchemeHttps}.");
return false;
}
await DownloadFileAsync(Uri, DestinationPath, Overwrite, _cts.Token, TimeoutSeconds, Log);
return !Log.HasLoggedErrors;
}
private static async Task DownloadFileAsync(
string uri,
string destinationPath,
bool overwrite,
CancellationToken cancellationToken,
int timeoutSeconds,
TaskLoggingHelper log)
{
var destinationExists = File.Exists(destinationPath);
if (destinationExists && !overwrite)
{
log.LogMessage($"Not downloading '{uri}' to overwrite existing file '{destinationPath}'.");
return;
}
log.LogMessage($"Downloading '{uri}' to '{destinationPath}'.");
using (var httpClient = new HttpClient
{
})
{
await DownloadFileCore.DownloadAsync(
uri,
destinationPath,
httpClient,
new LogWrapper(log),
cancellationToken,
timeoutSeconds);
}
}
}
}

View File

@ -0,0 +1,118 @@
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 GenerationTasks
{
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

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace GenerationTasks
{
/// <summary>
/// Adds or corrects Namespace and OutputPath metadata in ServiceFileReference items.
/// </summary>
public class GetFileReferenceMetadata : Task
{
/// <summary>
/// Default Namespace metadata value for C# output.
/// </summary>
[Required]
public string CSharpNamespace { get; set; }
/// <summary>
/// Default directory for OutputPath values.
/// </summary>
[Required]
public string OutputDirectory { get; set; }
/// <summary>
/// Default Namespace metadata value for TypeScript output.
/// </summary>
[Required]
public string TypeScriptNamespace { get; set; }
/// <summary>
/// The ServiceFileReference items to update.
/// </summary>
[Required]
public ITaskItem[] Inputs { get; set; }
/// <summary>
/// The updated ServiceFileReference items. Will include Namespace and OutputPath metadata. OutputPath metadata
/// will contain full paths.
/// </summary>
[Output]
public ITaskItem[] Outputs{ get; set; }
/// <inheritdoc />
public override bool Execute()
{
var outputs = new List<ITaskItem>(Inputs.Length);
foreach (var item in Inputs)
{
var newItem = new TaskItem(item);
outputs.Add(newItem);
var codeGenerator = item.GetMetadata("CodeGenerator");
var isTypeScript = codeGenerator.EndsWith("TypeScript", StringComparison.OrdinalIgnoreCase);
var @namespace = item.GetMetadata("Namespace");
if (string.IsNullOrEmpty(@namespace))
{
@namespace = isTypeScript ? CSharpNamespace : TypeScriptNamespace;
newItem.SetMetadata("Namespace", @namespace);
}
var outputPath = item.GetMetadata("OutputPath");
if (string.IsNullOrEmpty(outputPath))
{
var className = item.GetMetadata("ClassName");
outputPath = className + (isTypeScript ? ".ts" : ".cs");
}
outputPath = GetFullPath(outputPath);
newItem.SetMetadata("OutputPath", outputPath);
}
Outputs = outputs.ToArray();
return true;
}
private string GetFullPath(string path)
{
if (!Path.IsPathRooted(path))
{
if (!string.IsNullOrEmpty(OutputDirectory))
{
path = Path.Combine(OutputDirectory, path);
}
path = Path.GetFullPath(path);
}
return path;
}
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace GenerationTasks
{
/// <summary>
/// Adds or corrects DocumentPath and project-related metadata in ServiceProjectReference items.
/// </summary>
public class GetProjectReferenceMetadata : Task
{
/// <summary>
/// Default directory for DocumentPath values.
/// </summary>
[Required]
public string DocumentDirectory { get; set; }
/// <summary>
/// The ServiceFileReference items to update.
/// </summary>
[Required]
public ITaskItem[] Inputs { get; set; }
/// <summary>
/// The updated ServiceFileReference items. Will include Namespace and OutputPath metadata. OutputPath metadata
/// will contain full paths.
/// </summary>
[Output]
public ITaskItem[] Outputs{ get; set; }
/// <inheritdoc />
public override bool Execute()
{
var outputs = new List<ITaskItem>(Inputs.Length);
foreach (var item in Inputs)
{
var newItem = new TaskItem(item);
outputs.Add(newItem);
var codeGenerator = item.GetMetadata("CodeGenerator");
var isTypeScript = codeGenerator.EndsWith("TypeScript", StringComparison.OrdinalIgnoreCase);
var outputPath = item.GetMetadata("OutputPath");
if (string.IsNullOrEmpty(outputPath))
{
var className = item.GetMetadata("ClassName");
outputPath = className + (isTypeScript ? ".ts" : ".cs");
}
outputPath = GetFullPath(outputPath);
newItem.SetMetadata("OutputPath", outputPath);
}
Outputs = outputs.ToArray();
return true;
}
private string GetFullPath(string path)
{
if (!Path.IsPathRooted(path))
{
if (!string.IsNullOrEmpty(DocumentDirectory))
{
path = Path.Combine(DocumentDirectory, path);
}
path = Path.GetFullPath(path);
}
return path;
}
}
}

View File

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace GenerationTasks
{
/// <summary>
/// Adds or corrects DocumentPath metadata in ServiceUriReference items.
/// </summary>
public class GetUriReferenceMetadata : Task
{
/// <summary>
/// Default directory for DocumentPath metadata values.
/// </summary>
[Required]
public string DocumentDirectory { get; set; }
/// <summary>
/// The ServiceUriReference items to update.
/// </summary>
[Required]
public ITaskItem[] Inputs { get; set; }
/// <summary>
/// The updated ServiceUriReference items. Will include DocumentPath metadata with full paths.
/// </summary>
[Output]
public ITaskItem[] Outputs{ get; set; }
/// <inheritdoc />
public override bool Execute()
{
var outputs = new List<ITaskItem>(Inputs.Length);
foreach (var item in Inputs)
{
var newItem = new TaskItem(item);
outputs.Add(newItem);
var documentPath = item.GetMetadata("DocumentPath");
if (string.IsNullOrEmpty(documentPath))
{
var uri = item.ItemSpec;
var builder = new UriBuilder(uri);
if (!builder.Uri.IsAbsoluteUri)
{
Log.LogError($"{nameof(Inputs)} item '{uri}' is not an absolute URI.");
return false;
}
if (!string.Equals(Uri.UriSchemeHttp, builder.Scheme, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(Uri.UriSchemeHttps, builder.Scheme, StringComparison.OrdinalIgnoreCase))
{
Log.LogError($"{nameof(Inputs)} item '{uri}' does not have scheme {Uri.UriSchemeHttp} or " +
$"{Uri.UriSchemeHttps}.");
return false;
}
var host = builder.Host
.Replace("/", string.Empty)
.Replace("[", string.Empty)
.Replace("]", string.Empty)
.Replace(':', '_');
var path = builder.Path
.Replace("!", string.Empty)
.Replace("'", string.Empty)
.Replace("$", string.Empty)
.Replace("%", string.Empty)
.Replace("&", string.Empty)
.Replace("(", string.Empty)
.Replace(")", string.Empty)
.Replace("*", string.Empty)
.Replace("@", string.Empty)
.Replace("~", string.Empty)
.Replace('/', '_')
.Replace(':', '_')
.Replace(';', '_')
.Replace('+', '_')
.Replace('=', '_');
documentPath = host + path;
if (char.IsLower(documentPath[0]))
{
documentPath = char.ToUpper(documentPath[0]) + documentPath.Substring(startIndex: 1);
}
if (!documentPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
{
documentPath = $"{documentPath}.json";
}
}
documentPath = GetFullPath(documentPath);
newItem.SetMetadata("DocumentPath", documentPath);
}
Outputs = outputs.ToArray();
return true;
}
private string GetFullPath(string path)
{
if (!Path.IsPathRooted(path))
{
if (!string.IsNullOrEmpty(DocumentDirectory))
{
path = Path.Combine(DocumentDirectory, path);
}
path = Path.GetFullPath(path);
}
return path;
}
}
}

View File

@ -0,0 +1,50 @@
using System;
namespace GenerationTasks
{
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

@ -0,0 +1,35 @@
using System;
using Microsoft.Build.Utilities;
namespace GenerationTasks
{
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

@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Execute PopulateNuspec fairly late. -->
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);PopulateNuspec</GenerateNuspecDependsOn>
<!-- Do not complain about lack of lib folder. -->
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>MSBuild tasks and targets for code generation</Description>
<EnableApiCheck>false</EnableApiCheck>
<IsPackable>false</IsPackable>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<PackageTags>Build Tasks;msbuild;DownloadFile;GetFilenameFromUri;code generation</PackageTags>
<RootNamespace>GenerationTasks</RootNamespace>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="ServiceProjectReferenceMetadata.targets" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
<PackageReference Condition="'$(TargetFramework)' == 'net461'" Include="System.Net.Http" Version="$(SystemNetHttpPackageVersion)" />
</ItemGroup>
<Target Name="PopulateNuspec">
<PropertyGroup>
<NuspecProperties>
id=$(PackageId);
authors=$(Authors);
configuration=$(Configuration);
copyright=$(Copyright);
description=$(PackageDescription);
iconUrl=$(PackageIconUrl);
licenseUrl=$(PackageLicenseUrl);
owners=$(Company);
projectUrl=$(PackageProjectUrl);
repositoryCommit=$(RepositoryCommit);
repositoryUrl=$(RepositoryUrl);
tags=$(PackageTags.Replace(';', ' '));
version=$(PackageVersion);
</NuspecProperties>
</PropertyGroup>
</Target>
</Project>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>$id$</id>
<authors>$authors$</authors>
<copyright>$copyright$</copyright>
<description>$description$</description>
<developmentDependency>true</developmentDependency>
<iconUrl>$iconUrl$</iconUrl>
<licenseUrl>$licenseUrl$</licenseUrl>
<minClientVersion>2.8</minClientVersion>
<owners>$owners$</owners>
<projectUrl>$projectUrl$</projectUrl>
<repository type="git" url="$repositoryUrl$" commit="$repositoryCommit$" />
<requireLicenseAcceptance>false</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" />
</files>
</package>

View File

@ -0,0 +1,32 @@
<?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

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<Import Project="NSwagServiceReference.props" />
<PropertyGroup>
<_GenerationTasksAssemblyTarget Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0</_GenerationTasksAssemblyTarget>
<_GenerationTasksAssemblyTarget Condition="'$(MSBuildRuntimeType)' != 'Core'">net461</_GenerationTasksAssemblyTarget>
<_GenerationTasksAssemblyPath>$(MSBuildThisFileDirectory)/../tasks/$(_GenerationTasksAssemblyTarget)/GenerationTasks.dll</_GenerationTasksAssemblyPath>
<_GenerationTasksAssemblyTarget />
</PropertyGroup>
<UsingTask TaskName="GenerationTasks.GetFileReferenceMetadata" AssemblyFile="$(_GenerationTasksAssemblyPath)" />
<UsingTask TaskName="GenerationTasks.GetProjectReferenceMetadata" AssemblyFile="$(_GenerationTasksAssemblyPath)" />
<UsingTask TaskName="GenerationTasks.GetUriReferenceMetadata" AssemblyFile="$(_GenerationTasksAssemblyPath)" />
<UsingTask TaskName="GenerationTasks.DownloadFile" AssemblyFile="$(_GenerationTasksAssemblyPath)" />
<PropertyGroup>
<ServiceProjectReferenceCheckIfNewer Condition="'$(ServiceProjectReferenceCheckIfNewer)' == ''">true</ServiceProjectReferenceCheckIfNewer>
<ServiceProjectReferenceDirectory
Condition="'$(ServiceProjectReferenceDirectory)' != ''">$([MSBuild]::EnsureTrailingSlash('$(ServiceProjectReferenceDirectory)'))</ServiceProjectReferenceDirectory>
<ServiceUriReferenceCheckIfNewer Condition="'$(ServiceUriReferenceCheckIfNewer)' == ''">true</ServiceUriReferenceCheckIfNewer>
<ServiceUriReferenceDirectory
Condition="'$(ServiceUriReferenceDirectory)' != ''">$([MSBuild]::EnsureTrailingSlash('$(ServiceUriReferenceDirectory)'))</ServiceUriReferenceDirectory>
<ServiceFileReferenceCheckIfNewer Condition="'$(ServiceFileReferenceCheckIfNewer)' == ''">true</ServiceFileReferenceCheckIfNewer>
<ServiceFileReferenceDirectory
Condition="'$(ServiceFileReferenceDirectory)' != ''">$([MSBuild]::EnsureTrailingSlash('$(ServiceFileReferenceDirectory)'))</ServiceFileReferenceDirectory>
<ServiceFileReferenceCSharpNamespace Condition="'$(ServiceFileReferenceCSharpNamespace)' == ''">$(RootNamespace)</ServiceFileReferenceCSharpNamespace>
<ServiceFileReferenceTypeScriptNamespace Condition="'$(ServiceFileReferenceTypeScriptNamespace)' == ''">$(RootNamespace)</ServiceFileReferenceTypeScriptNamespace>
<DefaultDocumentGeneratorDependsOn>
_DefaultDocumentGenerator_GetMetadata;
_DefaultDocumentGenerator_Core;
_DefaultDocumentGenerator_SetMetadata
</DefaultDocumentGeneratorDependsOn>
<ServiceProjectReferenceGeneratorDependsOn>
_ServiceProjectReferenceGenerator_GetTargetFramework;
_ServiceProjectReferenceGenerator_GetProjectTargetPath;
_ServiceProjectReferenceGenerator_Restore;
_ServiceProjectReferenceGenerator_Build;
_ServiceProjectReferenceGenerator_Core
</ServiceProjectReferenceGeneratorDependsOn>
<ServiceUriReferenceGeneratorDependsOn>
_ServiceUriReferenceGenerator_GetMetadata;
_ServiceUriReferenceGenerator_Core
</ServiceUriReferenceGeneratorDependsOn>
<ServiceFileReferenceGeneratorDependsOn>
_CheckServiceReferences;
ServiceProjectReferenceGenerator;
ServiceUriReferenceGenerator;
_ServiceFileReferenceGenerator_GetMetadata;
_ServiceFileReferenceGenerator_Core
</ServiceFileReferenceGeneratorDependsOn>
</PropertyGroup>
<!--
Well-known metadata of the code and document generator item groups. ServiceProjectReference and ServiceUriReference
items may also include ServiceFileReference metadata.
-->
<ItemDefinitionGroup>
<ServiceProjectReference>
<!-- Name of the API description document generator. -->
<DocumentGenerator>Default</DocumentGenerator>
<!--
Name of the document to generate. Passed to the %(Method) when using Default document generator. Default is set
in server project, falling back to "v1".
-->
<DocumentName />
<!--
Full path where the API description document is placed. Default filename is %(Filename).%(DocumentName).json.
Filenames and relative paths (if explicitly set) are combined with $(ServiceProjectReferenceDirectory).
-->
<DocumentPath />
<!--
Method Default document generator should invoke on the %(Service) to generate document.
Default is set in server project, falling back to "Generate".
-->
<Method />
<!--
Service Default document generator should retrieve from DI to generate document.
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 />
<!--
Full path of the project's generated assembly. Corresponds to $(TargetPath). Because common code builds server
projects, file exists prior to document generator invocation.
-->
<ProjectAssemblyPath />
<!-- Server project's chosen configuration. Likely matches client project's configuration. -->
<ProjectConfiguration />
<!--
Server project's extensions path. Corresponds to $(MSBuildProjectExtensionsPath). Must set this if project's
value is not 'obj/'.
-->
<ProjectExtensionsPath />
<!-- Runtime identifier to use when building the server project. -->
<ProjectRuntimeIdentifier />
<!-- Server project's target framework. Defaults to $(TargetFramewok) or first of $(TargetFrameworks). -->
<ProjectTargetFramework />
</ServiceProjectReference>
<ServiceUriReference>
<!--
Full path where the API description document is placed. Default filename is based on %(Identity).
Filenames and relative paths (if explicitly set) are combined with $(ServiceProjectReferenceDirectory).
-->
<DocumentPath />
</ServiceUriReference>
<ServiceFileReference>
<!-- Name of the class to generate. -->
<ClassName>%(Filename)Client</ClassName>
<!-- Code generator to use. Required. -->
<CodeGenerator />
<!--
Namespace to contain generated class. Default is either $(ServiceFileReferenceCSharpNamespace) or
$(ServiceFileReferenceTypeScriptNamespace), depending on target language.
-->
<Namespace />
<!--
Path to place generated code. Code generator may interpret path as a filename or directory. Default filename or
folder name is %(ClassName).[cs|ts]. Filenames and relative paths (if explicitly set) are combined with
$(ServiceProjectReferenceDirectory).
-->
<OutputPath />
</ServiceFileReference>
</ItemDefinitionGroup>
</Project>

View File

@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<Import Project="NSwagServiceReference.targets" />
<Target Name="_CheckServiceReferences">
<Error Condition="'@(ServiceProjectReference)' != '' AND '%(ServiceProjectReference.CodeGenerator)' == ''"
Text="ServiceProjectReference items '@(ServiceProjectReference)' lack CodeGenerator metadata." />
<Error Condition="'@(ServiceUriReference)' != '' AND '%(ServiceUriReference.CodeGenerator)' == ''"
Text="ServiceUriReference items '@(ServiceUriReference)' lack CodeGenerator metadata." />
<Error Condition="'@(ServiceFileReference)' != '' AND '%(ServiceFileReference.CodeGenerator)' == ''"
Text="ServiceFileReference items '@(ServiceFileReference)' lack CodeGenerator metadata." />
</Target>
<!-- ServiceProjectReference support -->
<!-- Metadata setup phase 1: Ensure items have TargetFramework metadata. Call GetTargetFrameworks in the target project. -->
<!-- Inputs and outputs cause MSBuild to run target unconditionally and to batch it (run once per project). -->
<Target Name="_ServiceProjectReferenceGenerator_GetTargetFramework"
Inputs="%(ServiceProjectReference.FullPath)"
Outputs="&lt;not-a-file !&gt;">
<PropertyGroup>
<_FullPath>%(ServiceProjectReference.FullPath)</_FullPath>
</PropertyGroup>
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<MSBuild Projects="$(_FullPath)"
RebaseOutputs="true"
RemoveProperties="TargetFramework;TargetFrameworks;RuntimeIdentifier"
Targets="GetTargetFrameworks"
UseResultsCache="true">
<Output TaskParameter="TargetOutputs" ItemName="_Temporary" />
</MSBuild>
<!-- Please excuse the mess necessary to extract information from _Temporary and use it in ServiceProjectReference. -->
<PropertyGroup>
<_TargetFrameworks>%(_Temporary.TargetFrameworks)</_TargetFrameworks>
<_TargetFramework>$(_TargetFrameworks.Split(';')[0])</_TargetFramework>
</PropertyGroup>
<ItemGroup>
<ServiceProjectReference Update="@(ServiceProjectReference)" Condition="'%(FullPath)' == '$(_FullPath)'">
<TargetFramework Condition="'%(ServiceProjectReference.TargetFramework)' == ''">$(_TargetFramework)</TargetFramework>
</ServiceProjectReference>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<PropertyGroup>
<_FullPath />
<_TargetFramework />
<_TargetFrameworks />
</PropertyGroup>
</Target>
<!-- Metadata setup phase 2: Ensure items have ProjectTargetPath metadata. Call GetTargetPath in the target project. -->
<!-- Inputs and outputs cause MSBuild to run target unconditionally and batch it (run once per TargetFramework x project combination). -->
<Target Name="_ServiceProjectReferenceGenerator_GetProjectTargetPath"
Inputs="%(ServiceProjectReference.TargetFramework)%(FullPath)"
Outputs="&lt;not-a-file !&gt;">
<PropertyGroup>
<_FullPath>%(ServiceProjectReference.FullPath)</_FullPath>
<_TargetFramework>%(ServiceProjectReference.TargetFramework)</_TargetFramework>
</PropertyGroup>
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<Message Importance="high" Text="%0A_ServiceProjectReferenceGenerator_GetProjectTargetPath: '$(_FullPath)' '$(_TargetFramework)'" />
<MSBuild Projects="$(_FullPath)"
Properties="TargetFramework=$(_TargetFramework)"
RebaseOutputs="true"
RemoveProperties="TargetFrameworks;RuntimeIdentifier"
Targets="GetTargetPath"
UseResultsCache="true">
<Output TaskParameter="TargetOutputs" ItemName="_Temporary" />
</MSBuild>
<PropertyGroup>
<_ProjectTargetPath>%(_Temporary.FullPath)</_ProjectTargetPath>
</PropertyGroup>
<ItemGroup>
<ServiceProjectReference Update="@(ServiceProjectReference)"
Condition="'%(FullPath)' == '$(_FullPath)' AND '%(ServiceProjectReference.TargetFramework)' == '$(_TargetFramework)'">
<ProjectTargetPath>$(_ProjectTargetPath)</ProjectTargetPath>
</ServiceProjectReference>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<PropertyGroup>
<_FullPath />
<_ProjectTargetPath />
<_TargetFramework />
</PropertyGroup>
</Target>
<Target Name="_ServiceProjectReferenceGenerator_Restore"
Condition="'$(BuildProjectReferences)' == 'true'"
Inputs="@(ServiceProjectReference)"
Outputs="@(ServiceProjectReference -> '%(Directory)obj\project.assets.json')">
<MSBuild Projects="@(ServiceProjectReference -> '%(FullPath)')"
BuildInParallel="$(BuildInParallel)"
RemoveProperties="TargetFramework;TargetFrameworks;RuntimeIdentifier"
Targets="Restore" />
</Target>
<Target Name="_ServiceProjectReferenceGenerator_Build"
Condition="'$(BuildProjectReferences)' == 'true'"
Inputs="@(ServiceProjectReference)"
Outputs="@(ServiceProjectReference -> '%(ProjectTargetPath)')">
<MSBuild Projects="@(ServiceProjectReference)"
BuildInParallel="$(BuildInParallel)"
RemoveProperties="TargetFrameworks;RuntimeIdentifier"
Targets="Build" />
</Target>
<Target Name="_ServiceProjectReferenceGenerator_Core"
Condition="'@(ServiceProjectReference)' != ''"
DependsOnTargets="@(ServiceProjectReference -> '%(DocumentGenerator)DocumentGenerator')" />
<Target Name="ServiceProjectReferenceGenerator"
Condition="'@(ServiceProjectReference)' != ''"
DependsOnTargets="$(ServiceProjectReferenceGeneratorDependsOn)" />
<!-- DefaultDocumentGenerator -->
<Target Name="_DefaultDocumentGenerator_GetMetadata">
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
<_Temporary Include="@(ServiceProjectReference -> WithMetadataValue('DocumentGenerator', 'Default'))" />
</ItemGroup>
<Error Condition="'@(_Temporary)' != '' AND '%(_Temporary.Method)' != '' AND '%(_Temporary.Uri)' != ''"
Text="ServiceProjectReference items '@(_Temporary)' have both Method and Uri metadata." />
<Error Condition="'@(_Temporary)' != '' AND '%(_Temporary.Service)' != '' AND '%(_Temporary.Uri)' != ''"
Text="ServiceProjectReference items '@(_Temporary)' have both Service and Uri metadata." />
</Target>
<Target Name="_DefaultDocumentGenerator_Core" Inputs="%(_Temporary.ProjectTargetPath)" Outputs="%(_Temporary.DocumentPath)">
<!-- aspnetcore2swagger -->
<PropertyGroup>
<_Command>dotnet getdocument --configuration $(Configuration) --no-build</_Command>
</PropertyGroup>
<ItemGroup>
<_Temporary Update="@(_Temporary)">
<Options
Condition="'%(_Temporary.Options)' == '' AND '$(DefaultDocumentGeneratorDefaultOptions)' != ''">$(DefaultDocumentGeneratorDefaultOptions)</Options>
<Command>$(_Command) --project %(FullPath) --output %(DocumentPath) --framework %(TargetFramework)</Command>
</_Temporary>
<_Temporary Update="@(_Temporary)">
<Command Condition="'%(_Temporary.Uri)' != ''">%(Command) --uri %(_Temporary.Uri)</Command>
</_Temporary>
<_Temporary Update="@(_Temporary)">
<Command Condition="'%(_Temporary.Service)' != ''">%(Command) --service %(_Temporary.Service) --method %(_Temporary.Method)</Command>
</_Temporary>
<_Temporary Update="@(_Temporary)">
<Command Condition="'%(_Temporary.Options)' != ''">%(Command) %(_Temporary.Options)</Command>
</_Temporary>
</ItemGroup>
<Message Importance="high" Text="%0A%(_Temporary.Command)" />
<Exec IgnoreExitCode="$([System.IO.File]::Exists('%(DocumentPath)'))" Command="%(_Temporary.Command)" />
</Target>
<!--
Separate from _DefaultDocumentGenerator_Core to ensure ServiceFileReference items are complete even if
ServiceProjectReference items are not built in any batch.
-->
<Target Name="_DefaultDocumentGenerator_SetMetadata" Condition="'@(_Temporary)' != ''">
<ItemGroup>
<ServiceFileReference Remove="@(_Temporary -> '%(DocumentPath)')" />
<!-- Condition here is temporary. Useful while DefaultDocumentGenerator fails. -->
<ServiceFileReference Include="@(_Temporary -> '%(DocumentPath)')"
Condition="Exists('%(_Temporary.DocumentPath)')"
SourceProject="%(_Temporary.FullPath)" />
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
</Target>
<Target Name="DefaultDocumentGenerator" DependsOnTargets="$(DefaultDocumentGeneratorDependsOn)" />
<!-- ServiceUriReference support -->
<Target Name="_ServiceUriReferenceGenerator_GetMetadata">
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<GenerationTasks.GetUriReferenceMetadata DocumentDirectory="$(ServiceUriReferenceDirectory)" Inputs="@(ServiceUriReference)">
<Output TaskParameter="Outputs" ItemName="_Temporary" />
</GenerationTasks.GetUriReferenceMetadata>
<ItemGroup>
<ServiceUriReference Remove="@(ServiceUriReference)" />
<ServiceUriReference Include="@(_Temporary)" />
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
</Target>
<Target Name="_ServiceUriReferenceGenerator_Core" Condition="'@(ServiceUriReference)' != ''">
<GenerationTasks.DownloadFile Uri="%(ServiceUriReference.Identity)"
DestinationPath="%(DocumentPath)"
Overwrite="$(ServiceUriReferenceCheckIfNewer)" />
<ItemGroup>
<ServiceFileReference Remove="@(ServiceUriReference -> '%(DocumentPath)')" />
<ServiceFileReference Include="@(ServiceUriReference -> '%(DocumentPath)')" SourceUri="%(ServiceUriReference.Identity)" />
</ItemGroup>
</Target>
<Target Name="ServiceUriReferenceGenerator" DependsOnTargets="$(ServiceUriReferenceGeneratorDependsOn)" />
<!-- ServiceFileReference support -->
<Target Name="_ServiceFileReferenceGenerator_GetMetadata">
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<GenerationTasks.GetFileReferenceMetadata DocumentDirectory="$(ServiceFileReferenceDirectory)" Inputs="@(ServiceFileReference)">
<Output TaskParameter="Outputs" ItemName="_Temporary" />
</GenerationTasks.GetFileReferenceMetadata>
<ItemGroup>
<ServiceFileReference Remove="@(ServiceFileReference)" />
<ServiceFileReference Include="@(_Temporary)" />
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
</Target>
<Target Name="_ServiceFileReferenceGenerator_Core"
Condition="'@(ServiceFileReference)' != ''"
DependsOnTargets="@(ServiceFileReference -> '%(CodeGenerator)CodeGenerator')" />
<Target Name="ServiceFileReferenceGenerator" BeforeTargets="BeforeCompile" DependsOnTargets="$(ServiceFileReferenceGeneratorDependsOn)" />
</Project>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<PropertyGroup>
<NSwagDocumentGeneratorDependsOn>
_NSwagDocumentGenerator_GetMetadata;
_NSwagDocumentGenerator_Core;
_NSwagDocumentGenerator_SetMetadata
</NSwagDocumentGeneratorDependsOn>
<NSwagCSharpCodeGeneratorDependsOn>
_NSwagCSharpCodeGenerator_GetMetadata;
_NSwagCSharpCodeGenerator_Core;
_NSwagCSharpCodeGenerator_SetMetadata
</NSwagCSharpCodeGeneratorDependsOn>
<NSwagTypeScriptCodeGeneratorDependsOn>
_NSwagTypeScriptCodeGenerator_GetMetadata;
_NSwagTypeScriptCodeGenerator_Core;
_NSwagTypeScriptCodeGenerator_SetMetadata
</NSwagTypeScriptCodeGeneratorDependsOn>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<!-- ServiceProjectReference support -->
<Target Name="_NSwagDocumentGenerator_GetMetadata">
<ItemGroup>
<_NSwagTemporary Remove="@(_NSwagTemporary)" />
<_NSwagTemporary Include="@(ServiceProjectReference -> WithMetadataValue('DocumentGenerator', 'NSwag'))">
<Options
Condition="'%(ServiceFileReference.Options)' == '' AND '$(NSwagDocumentGeneratorDefaultOptions)' != ''">$(NSwagDocumentGeneratorDefaultOptions)</Options>
</_NSwagTemporary>
</ItemGroup>
</Target>
<Target Name="_NSwagDocumentGenerator_Core" Inputs="%(_NSwagTemporary.ProjectTargetPath)" Outputs="%(_NSwagTemporary.DocumentPath)">
<!-- aspnetcore2swagger -->
<PropertyGroup>
<_Command>$(NSwagExe_Core21) aspnetcore2swagger /Configuration:$(Configuration) /NoBuild:true</_Command>
</PropertyGroup>
<Message Importance="high" Text="%0A$(_Command) /Project:%(FullPath) /output:%(DocumentPath) /TargetFramework:%(TargetFramework) %(_NSwagTemporary.Options)" />
<Exec IgnoreExitCode="$([System.IO.File]::Exists('%(DocumentPath)'))"
Command="$(_Command) /Project:%(FullPath) /output:%(DocumentPath) /TargetFramework:%(TargetFramework) %(_NSwagTemporary.Options)" />
<PropertyGroup>
<_Command />
</PropertyGroup>
</Target>
<!--
Separate from _NSwagDocumentGenerator_Core to ensure ServiceFileReference items are complete even if
ServiceProjectReference items are not built in any batch.
-->
<Target Name="_NSwagDocumentGenerator_SetMetadata" Condition="'@(_NSwagTemporary)' != ''">
<ItemGroup>
<ServiceFileReference Remove="@(_NSwagTemporary -> '%(DocumentPath)')" />
<ServiceFileReference Include="@(_NSwagTemporary -> '%(DocumentPath)')" SourceProject="%(_NSwagTemporary.FullPath)" />
<_NSwagTemporary Remove="@(_NSwagTemporary)" />
</ItemGroup>
</Target>
<Target Name="NSwagDocumentGenerator" DependsOnTargets="$(NSwagDocumentGeneratorDependsOn)" />
<!-- ServiceFileReference support for C# -->
<Target Name="_NSwagCSharpCodeGenerator_GetMetadata">
<ItemGroup>
<_NSwagTemporary Remove="@(_NSwagTemporary)" />
<_NSwagTemporary Include="@(ServiceFileReference -> WithMetadataValue('CodeGenerator', 'NSwagCSharp'))">
<Options
Condition="'%(ServiceFileReference.Options)' == '' AND '$(NSwagCSharpCodeGeneratorDefaultOptions)' != ''">$(NSwagCSharpCodeGeneratorDefaultOptions)</Options>
</_NSwagTemporary>
<_NSwagTemporary Update="@(_NSwagTemporary)">
<OutputPath Condition="'%(_NSwagTemporary.OutputPath)' == ''">$(ServiceFileReferenceDirectory)%(ClassName).cs</OutputPath>
</_NSwagTemporary>
</ItemGroup>
</Target>
<Target Name="_NSwagCSharpCodeGenerator_Core" Inputs="%(_NSwagTemporary.FullPath)" Outputs="%(OutputPath)">
<PropertyGroup>
<_Command>$(NSwagExe_Core21) swagger2csclient /namespace:$(NSwagCSharpCodeGeneratorNamespace)</_Command>
</PropertyGroup>
<Message Importance="high" Text="%0A$(_Command) /className:%(ClassName) /input:%(FullPath) /output:%(OutputPath) %(_NSwagTemporary.Options)" />
<Exec IgnoreExitCode="$([System.IO.File]::Exists('%(OutputPath)'))"
Command="$(_Command) /className:%(ClassName) /input:%(FullPath) /output:%(OutputPath) %(_NSwagTemporary.Options)" />
<PropertyGroup>
<_Command />
</PropertyGroup>
</Target>
<!--
Separate from _NSwagCSharpCodeGenerator_Core to ensure Compile items are complete even if ServiceFileReference
items are both written outside the project and not included in a batch.
-->
<Target Name="_NSwagCSharpCodeGenerator_SetMetadata" Condition="'@(_NSwagTemporary)' != ''">
<Message Importance="high" Text="%0A_NSwagCSharpCodeGenerator_SetMetadata: @(_NSwagTemporary -> '%(OutputPath)')" />
<Message Importance="high" Text="_NSwagCSharpCodeGenerator_SetMetadata: '%(OutputPath)' '%(FullPath)' '%(_NSwagTemporary.SourceProject)' '%(_NSwagTemporary.SourceUrl)'" />
<ItemGroup>
<Compile Remove="@(_NSwagTemporary -> '%(OutputPath)')" />
<Compile Include="@(_NSwagTemporary -> '%(OutputPath)')" />
<_NSwagTemporary Remove="@(_NSwagTemporary)" />
</ItemGroup>
</Target>
<Target Name="NSwagCSharpCodeGenerator" DependsOnTargets="$(NSwagCSharpCodeGeneratorDependsOn)" />
<!-- ServiceFileReference support for TypeScript -->
<Target Name="_NSwagTypeScriptCodeGenerator_GetMetadata">
<ItemGroup>
<_NSwagTemporary Remove="@(_NSwagTemporary)" />
<_NSwagTemporary Include="@(ServiceFileReference -> WithMetadataValue('CodeGenerator', 'NSwagTypeScript'))">
<Options
Condition="'%(ServiceFileReference.Options)' == '' AND '$(NSwagTypeScriptCodeGeneratorDefaultOptions)' != ''">$(NSwagTypeScriptCodeGeneratorDefaultOptions)</Options>
</_NSwagTemporary>
<_NSwagTemporary Update="@(_NSwagTemporary)">
<OutputPath Condition="'%(_NSwagTemporary.OutputPath)' == ''">$(ServiceFileReferenceDirectory)%(ClassName).ts</OutputPath>
</_NSwagTemporary>
</ItemGroup>
</Target>
<Target Name="_NSwagTypeScriptCodeGenerator_Core" Inputs="%(_NSwagTemporary.FullPath)" Outputs="%(OutputPath)">
<PropertyGroup>
<_Command>$(NSwagExe_Core21) swagger2tsclient /namespace:$(NSwagTypeScriptCodeGeneratorNamespace)</_Command>
</PropertyGroup>
<Message Importance="high" Text="%0A$(_Command) /className:%(ClassName) /input:%(FullPath) /output:%(OutputPath) %(_NSwagTemporary.Options)" />
<Exec IgnoreExitCode="$([System.IO.File]::Exists('%(OutputPath)'))"
Command="$(_Command) /className:%(ClassName) /input:%(FullPath) /output:%(OutputPath) %(_NSwagTemporary.Options)" />
<PropertyGroup>
<_Command />
</PropertyGroup>
</Target>
<!--
Separate from _NSwagTypeScriptCodeGenerator_Core to ensure Compile items are complete even if ServiceFileReference
items are both written outside the project and not included in a batch.
-->
<Target Name="_NSwagTypeScriptCodeGenerator_SetMetadata" Condition="'@(_NSwagTemporary)' != ''">
<ItemGroup>
<TypeScriptCompile Remove="@(_NSwagTemporary -> '%(OutputPath)')" />
<TypeScriptCompile Include="@(_NSwagTemporary -> '%(OutputPath)')" />
<_NSwagTemporary Remove="@(_NSwagTemporary)" />
</ItemGroup>
</Target>
<Target Name="NSwagTypeScriptCodeGenerator" DependsOnTargets="$(NSwagTypeScriptCodeGeneratorDependsOn)" />
</Project>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<!-- ServiceProjectReference support -->
<Target Name="ServiceProjectReferenceGenerator">
<MsBuild Projects="$(MSBuildProjectFile)"
Targets="ServiceProjectReferenceGenerator"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0])"
RemoveProperties="TargetFrameworks;RuntimeIdentifier" />
</Target>
<!-- ServiceUriReference support -->
<Target Name="ServiceUriReferenceGenerator">
<MsBuild Projects="$(MSBuildProjectFile)"
Targets="ServiceUriReferenceGenerator"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0])"
RemoveProperties="TargetFrameworks;RuntimeIdentifier" />
</Target>
<!-- ServiceFileReference support -->
<Target Name="ServiceFileReferenceGenerator" BeforeTargets="BeforeCompile">
<MsBuild Projects="$(MSBuildProjectFile)"
Targets="ServiceFileReferenceGenerator"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0])"
RemoveProperties="TargetFrameworks;RuntimeIdentifier" />
</Target>
</Project>

View File

@ -0,0 +1,241 @@
// 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.IO;
using System.Linq;
using System.Runtime.Versioning;
using GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GetDocument.Commands
{
internal class InvokeCommand : HelpCommandBase
{
private const string InsideManName = "GetDocument.Insider";
private CommandOption _configuration;
private CommandOption _framework;
private CommandOption _msbuildprojectextensionspath;
private CommandOption _output;
private CommandOption _project;
private CommandOption _runtime;
private IList<string> _args;
public override void Configure(CommandLineApplication command)
{
var options = new ProjectOptions();
options.Configure(command);
_project = options.Project;
_framework = options.Framework;
_configuration = options.Configuration;
_runtime = options.Runtime;
_msbuildprojectextensionspath = options.MSBuildProjectExtensionsPath;
_output = command.Option("--output <Path>", Resources.OutputDescription);
command.VersionOption("--version", ProductInfo.GetVersion);
_args = command.RemainingArguments;
base.Configure(command);
}
protected override int Execute()
{
var projectFile = FindProjects(
_project.Value(),
Resources.NoProject,
Resources.MultipleProjects);
Reporter.WriteVerbose(Resources.UsingProject(projectFile));
var project = Project.FromFile(
projectFile,
_msbuildprojectextensionspath.Value(),
_framework.Value(),
_configuration.Value(),
_runtime.Value());
if (!File.Exists(project.AssemblyPath))
{
throw new CommandException(Resources.MustBuild);
}
var thisPath = Path.GetFullPath(Path.GetDirectoryName(typeof(InvokeCommand).Assembly.Location));
string executable = null;
var cleanupExecutable = false;
try
{
string toolsDirectory;
var args = new List<string>();
var targetFramework = new FrameworkName(project.TargetFrameworkMoniker);
switch (targetFramework.Identifier)
{
case ".NETFramework":
cleanupExecutable = true;
executable = Path.Combine(project.OutputPath, InsideManName + ".exe");
toolsDirectory = Path.Combine(
thisPath,
project.PlatformTarget == "x86" ? "net461-x86" : "net461");
var executableSource = Path.Combine(toolsDirectory, InsideManName + ".exe");
File.Copy(executableSource, executable, overwrite: true);
if (!string.IsNullOrEmpty(project.ConfigPath))
{
File.Copy(project.ConfigPath, executable + ".config", overwrite: true);
}
break;
case ".NETCoreApp":
executable = "dotnet";
toolsDirectory = Path.Combine(thisPath, "netcoreapp2.0");
if (targetFramework.Version < new Version(2, 0))
{
throw new CommandException(
Resources.NETCoreApp1Project(project.Name, targetFramework.Version));
}
args.Add("exec");
args.Add("--depsFile");
args.Add(project.DepsPath);
if (!string.IsNullOrEmpty(project.AssetsPath))
{
using (var reader = new JsonTextReader(File.OpenText(project.AssetsPath)))
{
var projectAssets = JToken.ReadFrom(reader);
var packageFolders = projectAssets["packageFolders"]
.Children<JProperty>()
.Select(p => p.Name);
foreach (var packageFolder in packageFolders)
{
args.Add("--additionalProbingPath");
args.Add(packageFolder.TrimEnd(Path.DirectorySeparatorChar));
}
}
}
if (File.Exists(project.RuntimeConfigPath))
{
args.Add("--runtimeConfig");
args.Add(project.RuntimeConfigPath);
}
else if (!string.IsNullOrEmpty(project.RuntimeFrameworkVersion))
{
args.Add("--fx-version");
args.Add(project.RuntimeFrameworkVersion);
}
args.Add(Path.Combine(toolsDirectory, InsideManName + ".dll"));
break;
case ".NETStandard":
throw new CommandException(Resources.NETStandardProject(project.Name));
default:
throw new CommandException(
Resources.UnsupportedFramework(project.Name, targetFramework.Identifier));
}
args.AddRange(_args);
args.Add("--assembly");
args.Add(project.AssemblyPath);
args.Add("--tools-directory");
args.Add(toolsDirectory);
if (!(args.Contains("--method") || string.IsNullOrEmpty(project.DefaultMethod)))
{
args.Add("--method");
args.Add(project.DefaultMethod);
}
if (!(args.Contains("--service") || string.IsNullOrEmpty(project.DefaultService)))
{
args.Add("--service");
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");
args.Add(Path.GetFullPath(_output.Value()));
}
if (Reporter.IsVerbose)
{
args.Add("--verbose");
}
if (Reporter.NoColor)
{
args.Add("--no-color");
}
if (Reporter.PrefixOutput)
{
args.Add("--prefix-output");
}
return Exe.Run(executable, args, project.Directory);
}
finally
{
if (cleanupExecutable && !string.IsNullOrEmpty(executable))
{
File.Delete(executable);
File.Delete(executable + ".config");
}
}
}
private static string FindProjects(
string path,
string errorWhenNoProject,
string errorWhenMultipleProjects)
{
var specified = true;
if (path == null)
{
specified = false;
path = Directory.GetCurrentDirectory();
}
else if (!Directory.Exists(path)) // It's not a directory
{
return path;
}
var projectFiles = Directory
.EnumerateFiles(path, "*.*proj", SearchOption.TopDirectoryOnly)
.Where(f => !string.Equals(Path.GetExtension(f), ".xproj", StringComparison.OrdinalIgnoreCase))
.Take(2)
.ToList();
if (projectFiles.Count == 0)
{
throw new CommandException(
specified
? Resources.NoProjectInDirectory(path)
: errorWhenNoProject);
}
if (projectFiles.Count != 1)
{
throw new CommandException(
specified
? Resources.MultipleProjectsInDirectory(path)
: errorWhenMultipleProjects);
}
return projectFiles[0];
}
}
}

View File

@ -0,0 +1,118 @@
// 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace GetDocument
{
internal static class Exe
{
public static int Run(
string executable,
IReadOnlyList<string> args,
string workingDirectory = null,
bool interceptOutput = false)
{
var arguments = ToArguments(args);
Reporter.WriteVerbose(executable + " " + arguments);
var startInfo = new ProcessStartInfo
{
FileName = executable,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = interceptOutput
};
if (workingDirectory != null)
{
startInfo.WorkingDirectory = workingDirectory;
}
var process = Process.Start(startInfo);
if (interceptOutput)
{
string line;
while ((line = process.StandardOutput.ReadLine()) != null)
{
Reporter.WriteVerbose(line);
}
}
process.WaitForExit();
return process.ExitCode;
}
private static string ToArguments(IReadOnlyList<string> args)
{
var builder = new StringBuilder();
for (var i = 0; i < args.Count; i++)
{
if (i != 0)
{
builder.Append(" ");
}
if (args[i].IndexOf(' ') == -1)
{
builder.Append(args[i]);
continue;
}
builder.Append("\"");
var pendingBackslashs = 0;
for (var j = 0; j < args[i].Length; j++)
{
switch (args[i][j])
{
case '\"':
if (pendingBackslashs != 0)
{
builder.Append('\\', pendingBackslashs * 2);
pendingBackslashs = 0;
}
builder.Append("\\\"");
break;
case '\\':
pendingBackslashs++;
break;
default:
if (pendingBackslashs != 0)
{
if (pendingBackslashs == 1)
{
builder.Append("\\");
}
else
{
builder.Append('\\', pendingBackslashs * 2);
}
pendingBackslashs = 0;
}
builder.Append(args[i][j]);
break;
}
}
if (pendingBackslashs != 0)
{
builder.Append('\\', pendingBackslashs * 2);
}
builder.Append("\"");
}
return builder.ToString();
}
}
}

View File

@ -0,0 +1,43 @@
// 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 GetDocument.Commands;
using GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument
{
internal static class Program
{
private static int Main(string[] args)
{
var app = new CommandLineApplication(throwOnUnexpectedArg: false)
{
FullName = Resources.CommandFullName,
};
new InvokeCommand().Configure(app);
try
{
return app.Execute(args);
}
catch (Exception ex)
{
if (ex is CommandException || ex is CommandParsingException)
{
Reporter.WriteVerbose(ex.ToString());
}
else
{
Reporter.WriteInformation(ex.ToString());
}
Reporter.WriteError(ex.Message);
return 1;
}
}
}
}

View File

@ -0,0 +1,228 @@
// 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using GetDocument.Properties;
using IODirectory = System.IO.Directory;
namespace GetDocument
{
internal class Project
{
private const string MSBuildResourceName = "GetDocument.ServiceProjectReferenceMetadata";
private Project()
{
}
public string AssemblyName { get; private set; }
public string AssemblyPath { get; private set; }
public string AssetsPath { get; private set; }
public string Configuration { get; private set; }
public string ConfigPath { get; private set; }
public string DefaultDocumentName { get; private set; }
public string DefaultMethod { get; private set; }
public string DefaultService { get; private set; }
public string DefaultUri { get; private set; }
public string DepsPath { get; private set; }
public string Directory { get; private set; }
public string ExtensionsPath { get; private set; }
public string Name { get; private set; }
public string OutputPath { get; private set; }
public string Platform { get; private set; }
public string PlatformTarget { get; private set; }
public string RuntimeConfigPath { get; private set; }
public string RuntimeFrameworkVersion { get; private set; }
public string TargetFramework { get; private set; }
public string TargetFrameworkMoniker { get; private set; }
public static Project FromFile(
string file,
string buildExtensionsDirectory,
string framework = null,
string configuration = null,
string runtime = null)
{
Debug.Assert(!string.IsNullOrEmpty(file), "file is null or empty.");
if (string.IsNullOrEmpty(buildExtensionsDirectory))
{
buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(file), "obj");
}
IODirectory.CreateDirectory(buildExtensionsDirectory);
var assembly = typeof(Project).Assembly;
var propsPath = Path.Combine(
buildExtensionsDirectory,
Path.GetFileName(file) + ".ServiceProjectReferenceMetadata.props");
using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.props"))
{
using (var output = File.OpenWrite(propsPath))
{
Reporter.WriteVerbose(Resources.WritingFile(propsPath));
input.CopyTo(output);
}
}
var targetsPath = Path.ChangeExtension(propsPath, ".targets");
using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.targets"))
{
using (var output = File.OpenWrite(targetsPath))
{
// NB: Copy always in case it changes
Reporter.WriteVerbose(Resources.WritingFile(targetsPath));
input.CopyTo(output);
}
}
IDictionary<string, string> metadata;
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"
};
if (!string.IsNullOrEmpty(file))
{
args.Add(file);
}
var exitCode = Exe.Run("dotnet", args);
if (exitCode != 0)
{
throw new CommandException(Resources.GetMetadataFailed);
}
metadata = File.ReadLines(metadataPath).Select(l => l.Split(new[] { ':' }, 2))
.ToDictionary(s => s[0], s => s[1].TrimStart());
}
finally
{
File.Delete(propsPath);
File.Delete(metadataPath);
File.Delete(targetsPath);
}
var project = new Project
{
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)],
Name = metadata[nameof(Name)],
OutputPath = metadata[nameof(OutputPath)],
Platform = metadata[nameof(Platform)],
PlatformTarget = metadata[nameof(PlatformTarget)] ?? metadata[nameof(Platform)],
RuntimeConfigPath = metadata[nameof(RuntimeConfigPath)],
RuntimeFrameworkVersion = metadata[nameof(RuntimeFrameworkVersion)],
TargetFramework = metadata[nameof(TargetFramework)],
TargetFrameworkMoniker = metadata[nameof(TargetFrameworkMoniker)],
};
if (string.IsNullOrEmpty(project.AssemblyPath))
{
throw new CommandException(Resources.GetMetadataValueFailed(nameof(AssemblyPath), "TargetPath"));
}
if (string.IsNullOrEmpty(project.Directory))
{
throw new CommandException(Resources.GetMetadataValueFailed(nameof(Directory), "ProjectDir"));
}
if (string.IsNullOrEmpty(project.OutputPath))
{
throw new CommandException(Resources.GetMetadataValueFailed(nameof(OutputPath), "OutDir"));
}
if (!Path.IsPathRooted(project.Directory))
{
project.Directory = Path.GetFullPath(Path.Combine(IODirectory.GetCurrentDirectory(), project.Directory));
}
if (!Path.IsPathRooted(project.AssemblyPath))
{
project.AssemblyPath = Path.GetFullPath(Path.Combine(project.Directory, project.AssemblyPath));
}
if (!Path.IsPathRooted(project.OutputPath))
{
project.OutputPath = Path.GetFullPath(Path.Combine(project.Directory, project.OutputPath));
}
// Some document generation tools support non-ASP.NET Core projects.
// Thus any of the remaining properties may be empty.
if (!(string.IsNullOrEmpty(project.AssetsPath) || Path.IsPathRooted(project.AssetsPath)))
{
project.AssetsPath = Path.GetFullPath(Path.Combine(project.Directory, project.AssetsPath));
}
var configPath = $"{project.AssemblyPath}.config";
if (File.Exists(configPath))
{
project.ConfigPath = configPath;
}
if (!(string.IsNullOrEmpty(project.DepsPath) || Path.IsPathRooted(project.DepsPath)))
{
project.DepsPath = Path.GetFullPath(Path.Combine(project.Directory, project.DepsPath));
}
if (!(string.IsNullOrEmpty(project.RuntimeConfigPath) || Path.IsPathRooted(project.RuntimeConfigPath)))
{
project.RuntimeConfigPath = Path.GetFullPath(Path.Combine(project.Directory, project.RuntimeConfigPath));
}
return project;
}
}
}

View File

@ -0,0 +1,30 @@
// 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 GetDocument.Properties;
using Microsoft.DotNet.Cli.CommandLine;
namespace GetDocument
{
internal class ProjectOptions
{
public CommandOption Project { get; private set; }
public CommandOption Framework { get; private set; }
public CommandOption Configuration { get; private set; }
public CommandOption Runtime { get; private set; }
public CommandOption MSBuildProjectExtensionsPath { get; private set; }
public void Configure(CommandLineApplication command)
{
Project = command.Option("-p|--project <PROJECT>", Resources.ProjectDescription);
Framework = command.Option("--framework <FRAMEWORK>", Resources.FrameworkDescription);
Configuration = command.Option("--configuration <CONFIGURATION>", Resources.ConfigurationDescription);
Runtime = command.Option("--runtime <RUNTIME_IDENTIFIER>", Resources.RuntimeDescription);
MSBuildProjectExtensionsPath = command.Option("--msbuildprojectextensionspath <PATH>", Resources.ProjectExtensionsDescription);
}
}
}

View File

@ -0,0 +1,179 @@
// <auto-generated />
using System;
using System.Reflection;
using System.Resources;
using JetBrains.Annotations;
namespace GetDocument.Properties
{
/// <summary>
/// This API supports the GetDocument infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("GetDocument.Properties.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The configuration to use.
/// </summary>
public static string ConfigurationDescription
=> GetString("ConfigurationDescription");
/// <summary>
/// dotnet getdocument
/// </summary>
public static string CommandFullName
=> GetString("CommandFullName");
/// <summary>
/// The target framework.
/// </summary>
public static string FrameworkDescription
=> GetString("FrameworkDescription");
/// <summary>
/// Unable to retrieve project metadata. If you are using custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, use the --msbuildprojectextensionspath option.
/// </summary>
public static string GetMetadataFailed
=> GetString("GetMetadataFailed");
/// <summary>
/// More than one project was found in the current working directory. Use the --project option.
/// </summary>
public static string MultipleProjects
=> GetString("MultipleProjects");
/// <summary>
/// More than one project was found in directory '{projectDirectory}'. Specify one using its file name.
/// </summary>
public static string MultipleProjectsInDirectory([CanBeNull] object projectDirectory)
=> string.Format(
GetString("MultipleProjectsInDirectory", nameof(projectDirectory)),
projectDirectory);
/// <summary>
/// Project '{Project}' targets framework '.NETCoreApp' version '{targetFrameworkVersion}'. This version of the GetDocument Command-line Tool only supports version 2.0 or higher.
/// </summary>
public static string NETCoreApp1Project([CanBeNull] object Project, [CanBeNull] object targetFrameworkVersion)
=> string.Format(
GetString("NETCoreApp1Project", nameof(Project), nameof(targetFrameworkVersion)),
Project, targetFrameworkVersion);
/// <summary>
/// Project '{Project}' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the GetDocument Command-line Tool with this project, add an executable project targeting .NET Core or .NET Framework that references this project and specify it using the --project option; or, update this project to target .NET Core and / or .NET Framework.
/// </summary>
public static string NETStandardProject([CanBeNull] object Project)
=> string.Format(
GetString("NETStandardProject", nameof(Project)),
Project);
/// <summary>
/// Do not colorize output.
/// </summary>
public static string NoColorDescription
=> GetString("NoColorDescription");
/// <summary>
/// No project was found. Change the current working directory or use the --project option.
/// </summary>
public static string NoProject
=> GetString("NoProject");
/// <summary>
/// No project was found in directory '{projectDirectory}'.
/// </summary>
public static string NoProjectInDirectory([CanBeNull] object projectDirectory)
=> string.Format(
GetString("NoProjectInDirectory", nameof(projectDirectory)),
projectDirectory);
/// <summary>
/// Prefix output with level.
/// </summary>
public static string PrefixDescription
=> GetString("PrefixDescription");
/// <summary>
/// The project to use.
/// </summary>
public static string ProjectDescription
=> GetString("ProjectDescription");
/// <summary>
/// The MSBuild project extensions path. Defaults to "obj".
/// </summary>
public static string ProjectExtensionsDescription
=> GetString("ProjectExtensionsDescription");
/// <summary>
/// The runtime identifier to use.
/// </summary>
public static string RuntimeDescription
=> GetString("RuntimeDescription");
/// <summary>
/// Project '{Project}' targets framework '{targetFramework}'. The GetDocument Command-line Tool does not support this framework.
/// </summary>
public static string UnsupportedFramework([CanBeNull] object Project, [CanBeNull] object targetFramework)
=> string.Format(
GetString("UnsupportedFramework", nameof(Project), nameof(targetFramework)),
Project, targetFramework);
/// <summary>
/// Using project '{project}'.
/// </summary>
public static string UsingProject([CanBeNull] object project)
=> string.Format(
GetString("UsingProject", nameof(project)),
project);
/// <summary>
/// Show verbose output.
/// </summary>
public static string VerboseDescription
=> GetString("VerboseDescription");
/// <summary>
/// Writing '{file}'...
/// </summary>
public static string WritingFile([CanBeNull] object file)
=> string.Format(
GetString("WritingFile", nameof(file)),
file);
/// <summary>
/// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.
/// </summary>
public static string MustBuild
=> GetString("MustBuild");
/// <summary>
/// The file to write the result to.
/// </summary>
public static string OutputDescription
=> GetString("OutputDescription");
/// <summary>
/// Unable to retrieve '{properrty}' project metadata. Ensure '{msbuildProperty}' is set.
/// </summary>
public static string GetMetadataValueFailed([CanBeNull] object properrty, [CanBeNull] object msbuildProperty)
=> string.Format(
GetString("GetMetadataValueFailed", nameof(properrty), nameof(msbuildProperty)),
properrty, msbuildProperty);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
return value;
}
}
}

View File

@ -0,0 +1,6 @@
<#
Session["ResourceFile"] = "Resources.resx";
Session["AccessModifier"] = "internal";
Session["NoDiagnostics"] = true;
#>
<#@ include file="..\..\tools\Resources.tt" #>

View File

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ConfigurationDescription" xml:space="preserve">
<value>The configuration to use.</value>
</data>
<data name="CommandFullName" xml:space="preserve">
<value>dotnet getdocument</value>
</data>
<data name="FrameworkDescription" xml:space="preserve">
<value>The target framework.</value>
</data>
<data name="GetMetadataFailed" xml:space="preserve">
<value>Unable to retrieve project metadata. If you are using custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, use the --msbuildprojectextensionspath option.</value>
</data>
<data name="MultipleProjects" xml:space="preserve">
<value>More than one project was found in the current working directory. Use the --project option.</value>
</data>
<data name="MultipleProjectsInDirectory" xml:space="preserve">
<value>More than one project was found in directory '{projectDirectory}'. Specify one using its file name.</value>
</data>
<data name="NETCoreApp1Project" xml:space="preserve">
<value>Project '{Project}' targets framework '.NETCoreApp' version '{targetFrameworkVersion}'. This version of the GetDocument Command-line Tool only supports version 2.0 or higher.</value>
</data>
<data name="NETStandardProject" xml:space="preserve">
<value>Project '{Project}' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the GetDocument Command-line Tool with this project, add an executable project targeting .NET Core or .NET Framework that references this project and specify it using the --project option; or, update this project to target .NET Core and / or .NET Framework.</value>
</data>
<data name="NoColorDescription" xml:space="preserve">
<value>Do not colorize output.</value>
</data>
<data name="NoProject" xml:space="preserve">
<value>No project was found. Change the current working directory or use the --project option.</value>
</data>
<data name="NoProjectInDirectory" xml:space="preserve">
<value>No project was found in directory '{projectDirectory}'.</value>
</data>
<data name="PrefixDescription" xml:space="preserve">
<value>Prefix output with level.</value>
</data>
<data name="ProjectDescription" xml:space="preserve">
<value>The project to use.</value>
</data>
<data name="ProjectExtensionsDescription" xml:space="preserve">
<value>The MSBuild project extensions path. Defaults to "obj".</value>
</data>
<data name="RuntimeDescription" xml:space="preserve">
<value>The runtime identifier to use.</value>
</data>
<data name="UnsupportedFramework" xml:space="preserve">
<value>Project '{Project}' targets framework '{targetFramework}'. The GetDocument Command-line Tool does not support this framework.</value>
</data>
<data name="UsingProject" xml:space="preserve">
<value>Using project '{project}'.</value>
</data>
<data name="VerboseDescription" xml:space="preserve">
<value>Show verbose output.</value>
</data>
<data name="WritingFile" xml:space="preserve">
<value>Writing '{file}'...</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>
</data>
<data name="OutputDescription" xml:space="preserve">
<value>The file to write the result to.</value>
</data>
<data name="GetMetadataValueFailed" xml:space="preserve">
<value>Unable to retrieve '{properrty}' project metadata. Ensure '{msbuildProperty}' is set.</value>
</data>
</root>

View File

@ -0,0 +1,17 @@
<?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

@ -0,0 +1,49 @@
<?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. -->
<Target Name="GetServiceProjectReferenceMetadata"
BeforeTargets="WriteServiceProjectReferenceMetadataCore"
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="AssemblyName: $(AssemblyName)" />
<ServiceProjectReferenceMetadata Include="AssemblyPath: $(TargetPath)" />
<ServiceProjectReferenceMetadata Include="AssetsPath: $(ProjectAssetsFile)" />
<ServiceProjectReferenceMetadata Include="Configuration: $(Configuration)" />
<ServiceProjectReferenceMetadata Include="DepsPath: $(ProjectDepsFilePath)" />
<ServiceProjectReferenceMetadata Include="Directory: $(ProjectDir)" />
<ServiceProjectReferenceMetadata Include="ExtensionsPath: $(MSBuildProjectExtensionsPath)" />
<ServiceProjectReferenceMetadata Include="Name: $(MSBuildProjectName)" />
<ServiceProjectReferenceMetadata Include="OutputPath: $(OutDir)" />
<ServiceProjectReferenceMetadata Include="Platform: $(Platform)" />
<ServiceProjectReferenceMetadata Include="PlatformTarget: $(PlatformTarget)" />
<ServiceProjectReferenceMetadata Include="RuntimeConfigPath: $(ProjectRuntimeConfigFilePath)" />
<ServiceProjectReferenceMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
<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)">
<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

@ -0,0 +1,112 @@
<Project>
<Sdk Name="Microsoft.NET.Sdk" />
<!-- Sdk Name="Microsoft.DotNet.GlobalTools.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>GetDocument</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/CodeAnnotations.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>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
</ItemGroup>
<ItemGroup>
<None Update="Properties/Resources.Designer.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties/Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.Designer.tt</DependentUpon>
</Compile>
</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

@ -0,0 +1,28 @@
<?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>