Merge pull request #8585 from aspnet/feature/client.code.generation
Merge feature/client.code.generation branch into release/2.2
This commit is contained in:
commit
18acae77c8
|
|
@ -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.Design", "src\Microsoft.Extensions.ApiDescription.Design\Microsoft.Extensions.ApiDescription.Design.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
45
Mvc.sln
|
|
@ -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.Design", "src\Microsoft.Extensions.ApiDescription.Design\Microsoft.Extensions.ApiDescription.Design.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}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,31 @@
|
|||
{
|
||||
"Default": {
|
||||
"rules": [
|
||||
"DefaultCompositeRule"
|
||||
]
|
||||
"Default": {
|
||||
"rules": [
|
||||
"DefaultCompositeRule"
|
||||
],
|
||||
"packages": {
|
||||
"Microsoft.Extensions.ApiDescription.Design": {
|
||||
"Exclusions": {
|
||||
"BUILD_ITEMS_FRAMEWORK": {
|
||||
"*": "Package includes tool with different target frameworks."
|
||||
},
|
||||
"SERVICING_ATTRIBUTE": {
|
||||
"tools/Newtonsoft.Json.dll": "External assembly, not built as part of this process"
|
||||
},
|
||||
"WRONG_PUBLICKEYTOKEN": {
|
||||
"tools/Newtonsoft.Json.dll": "External assembly, not built as part of this process"
|
||||
},
|
||||
"ASSEMBLY_INFORMATIONAL_VERSION_MISMATCH": {
|
||||
"tools/Newtonsoft.Json.dll": "External assembly, not built as part of this process"
|
||||
},
|
||||
"ASSEMBLY_FILE_VERSION_MISMATCH": {
|
||||
"tools/Newtonsoft.Json.dll": "External assembly, not built as part of this process"
|
||||
},
|
||||
"ASSEMBLY_VERSION_MISMATCH": {
|
||||
"tools/Newtonsoft.Json.dll": "External assembly, not built as part of this process"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
is not otherwise referenced. They avoid unnecessary changes to the Universe build graph or to product
|
||||
dependencies. Do not use these properties elsewhere.
|
||||
-->
|
||||
|
||||
|
||||
<AngleSharpPackageVersion>0.9.9</AngleSharpPackageVersion>
|
||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||
<BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>2.1.1</BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreDiagnosticsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingAbstractions20PackageVersion>2.0.0</MicrosoftAspNetCoreHostingAbstractions20PackageVersion>
|
||||
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHostingPackageVersion>
|
||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
||||
|
|
@ -57,6 +58,7 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -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.Extensions.ApiDescription.Tool
|
||||
{
|
||||
internal class AnsiConsole
|
||||
{
|
||||
public static readonly AnsiTextWriter _out = new AnsiTextWriter(Console.Out);
|
||||
|
||||
public static void WriteLine(string text)
|
||||
=> _out.WriteLine(text);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
internal class CommandException : Exception
|
||||
{
|
||||
public CommandException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.DotNet.Cli.CommandLine;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
#if NETCOREAPP2_0
|
||||
using System.Runtime.Loader;
|
||||
#endif
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool.Commands
|
||||
{
|
||||
internal class GetDocumentCommand : ProjectCommandBase
|
||||
{
|
||||
internal const string FallbackDocumentName = "v1";
|
||||
internal const string FallbackMethod = "Generate";
|
||||
internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider";
|
||||
|
||||
private CommandOption _documentName;
|
||||
private CommandOption _method;
|
||||
private CommandOption _output;
|
||||
private CommandOption _service;
|
||||
|
||||
public override void Configure(CommandLineApplication command)
|
||||
{
|
||||
base.Configure(command);
|
||||
|
||||
_documentName = command.Option(
|
||||
"--documentName <Name>",
|
||||
Resources.FormatDocumentDescription(FallbackDocumentName));
|
||||
_method = command.Option("--method <Name>", Resources.FormatMethodDescription(FallbackMethod));
|
||||
_output = command.Option("--output <Path>", Resources.OutputDescription);
|
||||
_service = command.Option("--service <QualifiedName>", Resources.FormatServiceDescription(FallbackService));
|
||||
}
|
||||
|
||||
protected override void Validate()
|
||||
{
|
||||
base.Validate();
|
||||
|
||||
if (!_output.HasValue())
|
||||
{
|
||||
throw new CommandException(Resources.FormatMissingOption(_output.LongName));
|
||||
}
|
||||
|
||||
if (_method.HasValue() && !_service.HasValue())
|
||||
{
|
||||
throw new CommandException(Resources.FormatMissingOption(_service.LongName));
|
||||
}
|
||||
|
||||
if (_service.HasValue() && !_method.HasValue())
|
||||
{
|
||||
throw new CommandException(Resources.FormatMissingOption(_method.LongName));
|
||||
}
|
||||
}
|
||||
|
||||
protected override int Execute()
|
||||
{
|
||||
var thisAssembly = typeof(GetDocumentCommand).Assembly;
|
||||
|
||||
var toolsDirectory = ToolsDirectory.Value();
|
||||
var packagedAssemblies = Directory
|
||||
.EnumerateFiles(toolsDirectory, "*.dll")
|
||||
.Except(new[] { Path.GetFullPath(thisAssembly.Location) })
|
||||
.ToDictionary(path => Path.GetFileNameWithoutExtension(path), path => new AssemblyInfo(path));
|
||||
|
||||
// Explicitly load all assemblies we need first to preserve target project as much as possible. This
|
||||
// executable is always run in the target project's context (either through location or .deps.json file).
|
||||
foreach (var keyValuePair in packagedAssemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
keyValuePair.Value.Assembly = Assembly.Load(new AssemblyName(keyValuePair.Key));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore all failures because missing assemblies should be loadable from tools directory.
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_0
|
||||
AssemblyLoadContext.Default.Resolving += (loadContext, assemblyName) =>
|
||||
{
|
||||
var name = assemblyName.Name;
|
||||
if (!packagedAssemblies.TryGetValue(name, out var info))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var assemblyPath = info.Path;
|
||||
if (!File.Exists(assemblyPath))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Referenced assembly '{name}' was not found in '{toolsDirectory}'.");
|
||||
}
|
||||
|
||||
return loadContext.LoadFromAssemblyPath(assemblyPath);
|
||||
};
|
||||
|
||||
#elif NET461
|
||||
AppDomain.CurrentDomain.AssemblyResolve += (source, eventArgs) =>
|
||||
{
|
||||
var assemblyName = new AssemblyName(eventArgs.Name);
|
||||
var name = assemblyName.Name;
|
||||
if (!packagedAssemblies.TryGetValue(name, out var info))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var assembly = info.Assembly;
|
||||
if (assembly != null)
|
||||
{
|
||||
// Loaded already
|
||||
return assembly;
|
||||
}
|
||||
|
||||
var assemblyPath = info.Path;
|
||||
if (!File.Exists(assemblyPath))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Referenced assembly '{name}' was not found in '{toolsDirectory}'.");
|
||||
}
|
||||
|
||||
return Assembly.LoadFile(assemblyPath);
|
||||
};
|
||||
#else
|
||||
#error target frameworks need to be updated.
|
||||
#endif
|
||||
|
||||
// Now safe to reference the application's code.
|
||||
try
|
||||
{
|
||||
var assemblyPath = AssemblyPath.Value();
|
||||
var context = new GetDocumentCommandContext
|
||||
{
|
||||
AssemblyPath = assemblyPath,
|
||||
AssemblyDirectory = Path.GetDirectoryName(assemblyPath),
|
||||
AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath),
|
||||
DocumentName = _documentName.Value(),
|
||||
Method = _method.Value(),
|
||||
Output = _output.Value(),
|
||||
Service = _service.Value(),
|
||||
};
|
||||
|
||||
return GetDocumentCommandWorker.Process(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine(ex.ToString());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private class AssemblyInfo
|
||||
{
|
||||
public AssemblyInfo(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public Assembly Assembly { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool.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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool.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.FormatMissingEntryPoint(context.AssemblyPath));
|
||||
return 2;
|
||||
}
|
||||
|
||||
var services = GetServices(entryPointType, context.AssemblyPath, context.AssemblyName);
|
||||
if (services == null)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
var success = TryProcess(context, services);
|
||||
if (!success)
|
||||
{
|
||||
// As part of the aspnet/Mvc#8425 fix, return 4 here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.FormatUsingDocument(documentName));
|
||||
Reporter.WriteInformation(Resources.FormatUsingMethod(methodName));
|
||||
Reporter.WriteInformation(Resources.FormatUsingService(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)
|
||||
{
|
||||
// As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
|
||||
var message = Resources.FormatMethodInvocationFailed(methodName, serviceName, documentName);
|
||||
Reporter.WriteWarning(message);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = FormatException(ex);
|
||||
|
||||
// As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
|
||||
Reporter.WriteWarning(message);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string FormatException(Exception exception)
|
||||
{
|
||||
return $"{exception.GetType().FullName}: {exception.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.ApiDescription.Tool.Commands
|
||||
{
|
||||
internal class HelpCommandBase : CommandBase
|
||||
{
|
||||
public override void Configure(CommandLineApplication command)
|
||||
{
|
||||
base.Configure(command);
|
||||
|
||||
command.HelpOption("-h|--help");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool.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.FormatMissingOption(AssemblyPath.LongName));
|
||||
}
|
||||
|
||||
if (!ToolsDirectory.HasValue())
|
||||
{
|
||||
throw new CommandException(Resources.FormatMissingOption(ToolsDirectory.LongName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Internal.AspNetCore.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>GetDocument.Insider</AssemblyName>
|
||||
<Description>GetDocument Command-line Tool inside man</Description>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Microsoft.Extensions.ApiDescription.Tool</RootNamespace>
|
||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractions20PackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="BuildX86" AfterTargets="Build" Condition=" '$(TargetFramework)' == 'net461' And '$(Platform)' != 'x86' ">
|
||||
<MSBuild Projects="$(MSBuildProjectFullPath)" Properties="TargetFramework=$(TargetFramework);Platform=x86" Targets="Build" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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 Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
internal static class ProductInfo
|
||||
{
|
||||
public static string GetVersion()
|
||||
=> typeof(ProductInfo)
|
||||
.Assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||
.InformationalVersion;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// 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 Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.Extensions.ApiDescription.Tool.Commands;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
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)
|
||||
{
|
||||
Reporter.WriteVerbose(ex.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
Reporter.WriteInformation(ex.ToString());
|
||||
}
|
||||
|
||||
Reporter.WriteError(ex.Message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.Extensions.ApiDescription.Tool.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The assembly to use.
|
||||
/// </summary>
|
||||
internal static string AssemblyDescription
|
||||
{
|
||||
get => GetString("AssemblyDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assembly to use.
|
||||
/// </summary>
|
||||
internal static string FormatAssemblyDescription()
|
||||
=> GetString("AssemblyDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Missing required option '--{0}'.
|
||||
/// </summary>
|
||||
internal static string MissingOption
|
||||
{
|
||||
get => GetString("MissingOption");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing required option '--{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatMissingOption(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MissingOption"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Do not colorize output.
|
||||
/// </summary>
|
||||
internal static string NoColorDescription
|
||||
{
|
||||
get => GetString("NoColorDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do not colorize output.
|
||||
/// </summary>
|
||||
internal static string FormatNoColorDescription()
|
||||
=> GetString("NoColorDescription");
|
||||
|
||||
/// <summary>
|
||||
/// The file to write the result to.
|
||||
/// </summary>
|
||||
internal static string OutputDescription
|
||||
{
|
||||
get => GetString("OutputDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file to write the result to.
|
||||
/// </summary>
|
||||
internal static string FormatOutputDescription()
|
||||
=> GetString("OutputDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Prefix console output with logging level.
|
||||
/// </summary>
|
||||
internal static string PrefixDescription
|
||||
{
|
||||
get => GetString("PrefixDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix console output with logging level.
|
||||
/// </summary>
|
||||
internal static string FormatPrefixDescription()
|
||||
=> GetString("PrefixDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Show verbose output.
|
||||
/// </summary>
|
||||
internal static string VerboseDescription
|
||||
{
|
||||
get => GetString("VerboseDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show verbose output.
|
||||
/// </summary>
|
||||
internal static string FormatVerboseDescription()
|
||||
=> GetString("VerboseDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Location from which inside man was copied (in the .NET Framework case) or loaded.
|
||||
/// </summary>
|
||||
internal static string ToolsDirectoryDescription
|
||||
{
|
||||
get => GetString("ToolsDirectoryDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Location from which inside man was copied (in the .NET Framework case) or loaded.
|
||||
/// </summary>
|
||||
internal static string FormatToolsDirectoryDescription()
|
||||
=> GetString("ToolsDirectoryDescription");
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method to invoke on the '--service' instance. Default value '{0}'.
|
||||
/// </summary>
|
||||
internal static string MethodDescription
|
||||
{
|
||||
get => GetString("MethodDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method to invoke on the '--service' instance. Default value '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatMethodDescription(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MethodDescription"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// The qualified name of the service type to retrieve from dependency injection. Default value '{0}'.
|
||||
/// </summary>
|
||||
internal static string ServiceDescription
|
||||
{
|
||||
get => GetString("ServiceDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The qualified name of the service type to retrieve from dependency injection. Default value '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatServiceDescription(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ServiceDescription"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the document to pass to the '--method' method. Default value '{0}'.
|
||||
/// </summary>
|
||||
internal static string DocumentDescription
|
||||
{
|
||||
get => GetString("DocumentDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the document to pass to the '--method' method. Default value '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatDocumentDescription(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("DocumentDescription"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Using document name '{0}'.
|
||||
/// </summary>
|
||||
internal static string UsingDocument
|
||||
{
|
||||
get => GetString("UsingDocument");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using document name '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatUsingDocument(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingDocument"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Using method '{0}'.
|
||||
/// </summary>
|
||||
internal static string UsingMethod
|
||||
{
|
||||
get => GetString("UsingMethod");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using method '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatUsingMethod(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingMethod"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Using service '{0}'.
|
||||
/// </summary>
|
||||
internal static string UsingService
|
||||
{
|
||||
get => GetString("UsingService");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using service '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatUsingService(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingService"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of service '{1}' failed to generate document '{2}'.
|
||||
/// </summary>
|
||||
internal static string MethodInvocationFailed
|
||||
{
|
||||
get => GetString("MethodInvocationFailed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of service '{1}' failed to generate document '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatMethodInvocationFailed(object p0, object p1, object p2)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MethodInvocationFailed"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Assembly '{0}' does not contain an entry point.
|
||||
/// </summary>
|
||||
internal static string MissingEntryPoint
|
||||
{
|
||||
get => GetString("MissingEntryPoint");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assembly '{0}' does not contain an entry point.
|
||||
/// </summary>
|
||||
internal static string FormatMissingEntryPoint(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MissingEntryPoint"), p0);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.ApiDescription.Tool.AnsiConstants;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
<?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="MissingOption" xml:space="preserve">
|
||||
<value>Missing required option '--{0}'.</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="VerboseDescription" xml:space="preserve">
|
||||
<value>Show verbose output.</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="MethodDescription" xml:space="preserve">
|
||||
<value>The name of the method to invoke on the '--service' instance. Default value '{0}'.</value>
|
||||
</data>
|
||||
<data name="ServiceDescription" xml:space="preserve">
|
||||
<value>The qualified name of the service type to retrieve from dependency injection. Default value '{0}'.</value>
|
||||
</data>
|
||||
<data name="DocumentDescription" xml:space="preserve">
|
||||
<value>The name of the document to pass to the '--method' method. Default value '{0}'.</value>
|
||||
</data>
|
||||
<data name="UsingDocument" xml:space="preserve">
|
||||
<value>Using document name '{0}'.</value>
|
||||
</data>
|
||||
<data name="UsingMethod" xml:space="preserve">
|
||||
<value>Using method '{0}'.</value>
|
||||
</data>
|
||||
<data name="UsingService" xml:space="preserve">
|
||||
<value>Using service '{0}'.</value>
|
||||
</data>
|
||||
<data name="MethodInvocationFailed" xml:space="preserve">
|
||||
<value>Method '{0}' of service '{1}' failed to generate document '{2}'.</value>
|
||||
</data>
|
||||
<data name="MissingEntryPoint" xml:space="preserve">
|
||||
<value>Assembly '{0}' does not contain an entry point.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
// 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.AspNetCore.Mvc.Razor
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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.Net;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,227 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
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 Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
/// <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(MessageImportance.High, $"Downloading '{uri}' to '{destinationPath}'.");
|
||||
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
await DownloadAsync(uri, destinationPath, httpClient, cancellationToken, log, timeoutSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DownloadAsync(
|
||||
string uri,
|
||||
string destinationPath,
|
||||
HttpClient httpClient,
|
||||
CancellationToken cancellationToken,
|
||||
TaskLoggingHelper log,
|
||||
int timeoutSeconds)
|
||||
{
|
||||
// Timeout if the response has not begun within 1 minute
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(1);
|
||||
|
||||
var destinationExists = File.Exists(destinationPath);
|
||||
var reachedCopy = false;
|
||||
try
|
||||
{
|
||||
using (var response = await httpClient.GetAsync(uri, cancellationToken))
|
||||
{
|
||||
response.EnsureSuccessStatusCode();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
using (var responseStreamTask = response.Content.ReadAsStreamAsync())
|
||||
{
|
||||
var finished = await Task.WhenAny(
|
||||
responseStreamTask,
|
||||
Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
|
||||
|
||||
if (!ReferenceEquals(responseStreamTask, finished))
|
||||
{
|
||||
throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds.");
|
||||
}
|
||||
|
||||
using (var responseStream = await responseStreamTask)
|
||||
{
|
||||
if (destinationExists)
|
||||
{
|
||||
// Check hashes before using the downloaded information.
|
||||
var downloadHash = GetHash(responseStream);
|
||||
responseStream.Position = 0L;
|
||||
|
||||
byte[] destinationHash;
|
||||
using (var destinationStream = File.OpenRead(destinationPath))
|
||||
{
|
||||
destinationHash = GetHash(destinationStream);
|
||||
}
|
||||
|
||||
var sameHashes = downloadHash.Length == destinationHash.Length;
|
||||
for (var i = 0; sameHashes && i < downloadHash.Length; i++)
|
||||
{
|
||||
sameHashes = downloadHash[i] == destinationHash[i];
|
||||
}
|
||||
|
||||
if (sameHashes)
|
||||
{
|
||||
log.LogMessage($"Not overwriting existing and matching file '{destinationPath}'.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// May need to create directory to hold the file.
|
||||
var destinationDirectory = Path.GetDirectoryName(destinationPath);
|
||||
if (!string.IsNullOrEmpty(destinationDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(destinationDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// Create or overwrite the destination file.
|
||||
reachedCopy = true;
|
||||
using (var outStream = File.Create(destinationPath))
|
||||
{
|
||||
await responseStream.CopyToAsync(outStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex) when (destinationExists)
|
||||
{
|
||||
if (ex.InnerException is SocketException socketException)
|
||||
{
|
||||
log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.LogWarning($"Unable to download {uri}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError($"Downloading '{uri}' failed.");
|
||||
log.LogErrorFromException(ex, showStackTrace: true);
|
||||
if (reachedCopy)
|
||||
{
|
||||
File.Delete(destinationPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetHash(Stream stream)
|
||||
{
|
||||
SHA256 algorithm;
|
||||
try
|
||||
{
|
||||
algorithm = SHA256.Create();
|
||||
}
|
||||
catch (TargetInvocationException)
|
||||
{
|
||||
// SHA256.Create is documented to throw this exception on FIPS-compliant machines. See
|
||||
// https://msdn.microsoft.com/en-us/library/z08hz7ad Fall back to a FIPS-compliant SHA256 algorithm.
|
||||
algorithm = new SHA256CryptoServiceProvider();
|
||||
}
|
||||
|
||||
using (algorithm)
|
||||
{
|
||||
return algorithm.ComputeHash(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Restore <see cref="ITaskItem"/>s from given property value.
|
||||
/// </summary>
|
||||
public class GetCurrentItems : Task
|
||||
{
|
||||
/// <summary>
|
||||
/// The property value to deserialize.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The restored <see cref="ITaskItem"/>s. Will never contain more than one item.
|
||||
/// </summary>
|
||||
[Output]
|
||||
public ITaskItem[] Outputs { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Execute()
|
||||
{
|
||||
Outputs = new[] { MetadataSerializer.DeserializeMetadata(Input) };
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
// 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 Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds or corrects ClassName, Namespace and OutputPath metadata in ServiceFileReference items. Also stores final
|
||||
/// metadata as SerializedMetadata.
|
||||
/// </summary>
|
||||
public class GetFileReferenceMetadata : Task
|
||||
{
|
||||
private const string TypeScriptLanguageName = "TypeScript";
|
||||
|
||||
/// <summary>
|
||||
/// Extension to use in default OutputPath metadata value. Ignored when generating TypeScript.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Extension { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default Namespace metadata value.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Namespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default directory for OutputPath values.
|
||||
/// </summary>
|
||||
public string OutputDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ServiceFileReference items to update.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public ITaskItem[] Inputs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The updated ServiceFileReference items. Will include ClassName, Namespace and OutputPath metadata.
|
||||
/// </summary>
|
||||
[Output]
|
||||
public ITaskItem[] Outputs{ get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Execute()
|
||||
{
|
||||
var outputs = new List<ITaskItem>(Inputs.Length);
|
||||
var destinations = new HashSet<string>();
|
||||
|
||||
foreach (var item in Inputs)
|
||||
{
|
||||
var newItem = new TaskItem(item);
|
||||
outputs.Add(newItem);
|
||||
|
||||
var codeGenerator = item.GetMetadata("CodeGenerator");
|
||||
if (string.IsNullOrEmpty("CodeGenerator"))
|
||||
{
|
||||
// This case occurs when user forgets to specify the required metadata. We have no default here.
|
||||
string type;
|
||||
if (!string.IsNullOrEmpty(item.GetMetadata("SourceProject")))
|
||||
{
|
||||
type = "ServiceProjectReference";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(item.GetMetadata("SourceUri")))
|
||||
{
|
||||
type = "ServiceUriReference";
|
||||
}
|
||||
else
|
||||
{
|
||||
type = "ServiceFileReference";
|
||||
}
|
||||
|
||||
Log.LogError(Resources.FormatInvalidEmptyMetadataValue("CodeGenerator", type, item.ItemSpec));
|
||||
}
|
||||
|
||||
var className = item.GetMetadata("ClassName");
|
||||
if (string.IsNullOrEmpty(className))
|
||||
{
|
||||
var filename = item.GetMetadata("Filename");
|
||||
className = $"{filename}Client";
|
||||
if (char.IsLower(className[0]))
|
||||
{
|
||||
className = char.ToUpper(className[0]) + className.Substring(startIndex: 1);
|
||||
}
|
||||
|
||||
MetadataSerializer.SetMetadata(newItem, "ClassName", className);
|
||||
}
|
||||
|
||||
var @namespace = item.GetMetadata("Namespace");
|
||||
if (string.IsNullOrEmpty(@namespace))
|
||||
{
|
||||
MetadataSerializer.SetMetadata(newItem, "Namespace", Namespace);
|
||||
}
|
||||
|
||||
var outputPath = item.GetMetadata("OutputPath");
|
||||
if (string.IsNullOrEmpty(outputPath))
|
||||
{
|
||||
var isTypeScript = codeGenerator.EndsWith(TypeScriptLanguageName, StringComparison.OrdinalIgnoreCase);
|
||||
outputPath = $"{className}{(isTypeScript ? ".ts" : Extension)}";
|
||||
}
|
||||
|
||||
// Place output file in correct directory (relative to project directory).
|
||||
if (!Path.IsPathRooted(outputPath) && !string.IsNullOrEmpty(OutputDirectory))
|
||||
{
|
||||
outputPath = Path.Combine(OutputDirectory, outputPath);
|
||||
}
|
||||
|
||||
if (!destinations.Add(outputPath))
|
||||
{
|
||||
// This case may occur when user is experimenting e.g. with multiple code generators or options.
|
||||
// May also occur when user accidentally duplicates OutputPath metadata.
|
||||
Log.LogError(Resources.FormatDuplicateFileOutputPaths(outputPath));
|
||||
}
|
||||
|
||||
MetadataSerializer.SetMetadata(newItem, "OutputPath", outputPath);
|
||||
|
||||
// Add metadata which may be used as a property and passed to an inner build.
|
||||
newItem.RemoveMetadata("SerializedMetadata");
|
||||
newItem.SetMetadata("SerializedMetadata", MetadataSerializer.SerializeMetadata(newItem));
|
||||
}
|
||||
|
||||
Outputs = outputs.ToArray();
|
||||
|
||||
return !Log.HasLoggedErrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
// 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.IO;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds or corrects DocumentPath and project-related metadata in ServiceProjectReference items. Also stores final
|
||||
/// metadata as SerializedMetadata.
|
||||
/// </summary>
|
||||
public class GetProjectReferenceMetadata : Task
|
||||
{
|
||||
/// <summary>
|
||||
/// Default directory for DocumentPath values.
|
||||
/// </summary>
|
||||
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);
|
||||
var destinations = new HashSet<string>();
|
||||
|
||||
foreach (var item in Inputs)
|
||||
{
|
||||
var newItem = new TaskItem(item);
|
||||
outputs.Add(newItem);
|
||||
|
||||
var documentGenerator = item.GetMetadata("DocumentGenerator");
|
||||
if (string.IsNullOrEmpty(documentGenerator))
|
||||
{
|
||||
// This case occurs when user overrides the default metadata.
|
||||
Log.LogError(Resources.FormatInvalidEmptyMetadataValue(
|
||||
"DocumentGenerator",
|
||||
"ServiceProjectReference",
|
||||
item.ItemSpec));
|
||||
}
|
||||
|
||||
var documentPath = item.GetMetadata("DocumentPath");
|
||||
if (string.IsNullOrEmpty(documentPath))
|
||||
{
|
||||
var filename = item.GetMetadata("Filename");
|
||||
var documentName = item.GetMetadata("DocumentName");
|
||||
if (string.IsNullOrEmpty(documentName))
|
||||
{
|
||||
documentName = "v1";
|
||||
}
|
||||
|
||||
documentPath = $"{filename}.{documentName}.json";
|
||||
}
|
||||
|
||||
documentPath = GetFullPath(documentPath);
|
||||
MetadataSerializer.SetMetadata(newItem, "DocumentPath", documentPath);
|
||||
|
||||
if (!destinations.Add(documentPath))
|
||||
{
|
||||
// This case may occur when user is experimenting e.g. with multiple generators or options.
|
||||
// May also occur when user accidentally duplicates DocumentPath metadata.
|
||||
Log.LogError(Resources.FormatDuplicateProjectDocumentPaths(documentPath));
|
||||
}
|
||||
|
||||
// Add metadata which may be used as a property and passed to an inner build.
|
||||
newItem.SetMetadata("SerializedMetadata", MetadataSerializer.SerializeMetadata(newItem));
|
||||
}
|
||||
|
||||
Outputs = outputs.ToArray();
|
||||
|
||||
return !Log.HasLoggedErrors;
|
||||
}
|
||||
|
||||
private string GetFullPath(string path)
|
||||
{
|
||||
if (!Path.IsPathRooted(path))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(DocumentDirectory))
|
||||
{
|
||||
path = Path.Combine(DocumentDirectory, path);
|
||||
}
|
||||
|
||||
path = Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// 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 Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds or corrects DocumentPath metadata in ServiceUriReference items.
|
||||
/// </summary>
|
||||
public class GetUriReferenceMetadata : Task
|
||||
{
|
||||
/// <summary>
|
||||
/// Default directory for DocumentPath metadata values.
|
||||
/// </summary>
|
||||
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);
|
||||
var destinations = new HashSet<string>();
|
||||
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);
|
||||
MetadataSerializer.SetMetadata(newItem, "DocumentPath", documentPath);
|
||||
|
||||
if (!destinations.Add(documentPath))
|
||||
{
|
||||
// This case may occur when user is experimenting e.g. with multiple code generators or options.
|
||||
// May also occur when user accidentally duplicates DocumentPath metadata.
|
||||
Log.LogError(Resources.FormatDuplicateUriDocumentPaths(documentPath));
|
||||
}
|
||||
}
|
||||
|
||||
Outputs = outputs.ToArray();
|
||||
|
||||
return !Log.HasLoggedErrors;
|
||||
}
|
||||
|
||||
private string GetFullPath(string path)
|
||||
{
|
||||
if (!Path.IsPathRooted(path))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(DocumentDirectory))
|
||||
{
|
||||
path = Path.Combine(DocumentDirectory, path);
|
||||
}
|
||||
|
||||
path = Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
// 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.Text;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility methods to serialize and deserialize <see cref="ITaskItem"/> metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Based on and uses the same escaping as
|
||||
/// https://github.com/Microsoft/msbuild/blob/e70a3159d64f9ed6ec3b60253ef863fa883a99b1/src/Shared/EscapingUtilities.cs
|
||||
/// </remarks>
|
||||
public static class MetadataSerializer
|
||||
{
|
||||
private static readonly char[] CharsToEscape = { '%', '*', '?', '@', '$', '(', ')', ';', '\'' };
|
||||
private static readonly HashSet<char> CharsToEscapeHash = new HashSet<char>(CharsToEscape);
|
||||
|
||||
/// <summary>
|
||||
/// Add the given <paramref name="key"/> and <paramref name="value"/> to the <paramref name="item"/>. Or,
|
||||
/// modify existing value to be <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The <see cref="ITaskItem"/> to update.</param>
|
||||
/// <param name="key">The name of the new metadata.</param>
|
||||
/// <param name="value">The value of the new metadata. Assumed to be unescaped.</param>
|
||||
/// <remarks>Uses same hex-encoded format as MSBuild's EscapeUtilities.</remarks>
|
||||
public static void SetMetadata(ITaskItem item, string key, string value)
|
||||
{
|
||||
if (item is ITaskItem2 item2)
|
||||
{
|
||||
item2.SetMetadataValueLiteral(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.IndexOfAny(CharsToEscape) == -1)
|
||||
{
|
||||
item.SetMetadata(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
EscapeValue(value, builder);
|
||||
item.SetMetadata(key, builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize metadata for use as a property value passed into an inner build.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to serialize.</param>
|
||||
/// <returns>A <see cref="string"/> containing the serialized metadata.</returns>
|
||||
/// <remarks>Uses same hex-encoded format as MSBuild's EscapeUtilities.</remarks>
|
||||
public static string SerializeMetadata(ITaskItem item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
if (item is ITaskItem2 item2)
|
||||
{
|
||||
builder.Append($"Identity={item2.EvaluatedIncludeEscaped}");
|
||||
var metadata = item2.CloneCustomMetadataEscaped();
|
||||
foreach (var key in metadata.Keys)
|
||||
{
|
||||
var value = metadata[key];
|
||||
builder.Append($"|{key.ToString()}={value.ToString()}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append($"Identity=");
|
||||
EscapeValue(item.ItemSpec, builder);
|
||||
|
||||
var metadata = item.CloneCustomMetadata();
|
||||
foreach (var key in metadata.Keys)
|
||||
{
|
||||
builder.Append($"|{key.ToString()}=");
|
||||
|
||||
var value = metadata[key];
|
||||
EscapeValue(value.ToString(), builder);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recreate an <see cref="ITaskItem"/> with metadata encoded in given <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The serialized metadata.</param>
|
||||
/// <returns>The deserialized <see cref="ITaskItem"/>.</returns>
|
||||
public static ITaskItem DeserializeMetadata(string value)
|
||||
{
|
||||
var metadata = value.Split('|');
|
||||
var item = new TaskItem();
|
||||
|
||||
// TaskItem implements ITaskITem2 explicitly and ITaskItem implicitly.
|
||||
var item2 = (ITaskItem2)item;
|
||||
foreach (var segment in metadata)
|
||||
{
|
||||
var keyAndValue = segment.Split(new[] { '=' }, count: 2);
|
||||
if (string.Equals("Identity", keyAndValue[0]))
|
||||
{
|
||||
item2.EvaluatedIncludeEscaped = keyAndValue[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
item2.SetMetadata(keyAndValue[0], keyAndValue[1]);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static void EscapeValue(string value, StringBuilder builder)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
builder.Append(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.IndexOfAny(CharsToEscape) == -1)
|
||||
{
|
||||
builder.Append(value);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var @char in value)
|
||||
{
|
||||
if (CharsToEscapeHash.Contains(@char))
|
||||
{
|
||||
builder.Append('%');
|
||||
builder.Append(HexDigitChar(@char / 0x10));
|
||||
builder.Append(HexDigitChar(@char & 0x0F));
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Append(@char);
|
||||
}
|
||||
}
|
||||
|
||||
private static char HexDigitChar(int x)
|
||||
{
|
||||
return (char)(x + (x < 10 ? '0' : ('a' - 10)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<Project Sdk="Internal.AspNetCore.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Execute PopulateNuspec fairly late. -->
|
||||
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);PopulateNuspec</GenerateNuspecDependsOn>
|
||||
|
||||
<!-- Do not complain about lack of lib folder. -->
|
||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||
|
||||
<AssemblyName>Microsoft.Extensions.ApiDescription.Tasks</AssemblyName>
|
||||
<Description>MSBuild tasks and targets for code generation</Description>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<IncludeSource>false</IncludeSource>
|
||||
<IncludeSymbols>false</IncludeSymbols>
|
||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||
<PackageId>$(MSBuildProjectName)</PackageId>
|
||||
<PackageTags>Build Tasks;MSBuild;Swagger;Open API;code generation; Web API client</PackageTags>
|
||||
<RootNamespace>$(AssemblyName)</RootNamespace>
|
||||
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core"
|
||||
Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
|
||||
<PackageReference Include="System.Net.Http"
|
||||
Condition="'$(TargetFramework)' == 'net461'"
|
||||
Version="$(SystemNetHttpPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != ''">
|
||||
<SignedPackageFile Include="$(TargetPath)">
|
||||
<Certificate>$(AssemblySigningCertName)</Certificate>
|
||||
<PackagePath>tasks/$(TargetFramework)/$(TargetFileName)</PackagePath>
|
||||
<StrongName>$(AssemblySigningStrongName)</StrongName>
|
||||
</SignedPackageFile>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Add other signed files in a single inner build, avoiding duplications in this multi-TFM project. -->
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<SignedPackageFile Include="../dotnet-getdocument/bin/$(Configuration)/netcoreapp2.1/publish/dotnet-getdocument.dll">
|
||||
<Certificate>$(AssemblySigningCertName)</Certificate>
|
||||
<PackagePath>tools/dotnet-getdocument.dll</PackagePath>
|
||||
<StrongName>$(AssemblySigningStrongName)</StrongName>
|
||||
</SignedPackageFile>
|
||||
<SignedPackageFile Include="../GetDocumentInsider/bin/$(Configuration)/net461/GetDocument.Insider.exe">
|
||||
<Certificate>$(AssemblySigningCertName)</Certificate>
|
||||
<PackagePath>tools/net461/GetDocument.Insider.exe</PackagePath>
|
||||
<StrongName>$(AssemblySigningStrongName)</StrongName>
|
||||
</SignedPackageFile>
|
||||
<SignedPackageFile Include="../GetDocumentInsider/bin/x86/$(Configuration)/net461/GetDocument.Insider.exe">
|
||||
<Certificate>$(AssemblySigningCertName)</Certificate>
|
||||
<PackagePath>tools/net461-x86/GetDocument.Insider.exe</PackagePath>
|
||||
<StrongName>$(AssemblySigningStrongName)</StrongName>
|
||||
</SignedPackageFile>
|
||||
<SignedPackageFile Include="../GetDocumentInsider/bin/$(Configuration)/netcoreapp2.0/GetDocument.Insider.dll">
|
||||
<Certificate>$(AssemblySigningCertName)</Certificate>
|
||||
<PackagePath>tools/netcoreapp2.0/GetDocument.Insider.exe</PackagePath>
|
||||
<StrongName>$(AssemblySigningStrongName)</StrongName>
|
||||
</SignedPackageFile>
|
||||
|
||||
<SignedPackageFile Include="../dotnet-getdocument/bin/$(Configuration)/netcoreapp2.1/publish/Newtonsoft.Json.dll">
|
||||
<PackagePath>tools/Newtonsoft.Json.dll"</PackagePath>
|
||||
<Certificate>$(AssemblySigning3rdPartyCertName)"</Certificate>
|
||||
</SignedPackageFile>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PopulateNuspec">
|
||||
<MSBuild Projects="../dotnet-getdocument/dotnet-getdocument.csproj"
|
||||
BuildInParallel="$(BuildInParallel)"
|
||||
RemoveProperties="RuntimeIdentifier;TargetFrameworks;TargetFramework"
|
||||
Targets="Publish" />
|
||||
|
||||
<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>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?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>true</requireLicenseAcceptance>
|
||||
<tags>$tags$</tags>
|
||||
<version>$version$</version>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<file src="build\*" target="build" />
|
||||
<file src="buildMultiTargeting\*" target="buildMultiTargeting" />
|
||||
<file src="bin\$configuration$\net461\Microsoft.Extensions.ApiDescription.Tasks.*" target="tasks\net461" />
|
||||
<file src="bin\$configuration$\netstandard2.0\Microsoft.Extensions.ApiDescription.Tasks.*" target="tasks\netstandard2.0" />
|
||||
<file src="..\dotnet-getdocument\bin\$configuration$\netcoreapp2.1\publish\*.*" target="tools" />
|
||||
<file src="..\GetDocumentInsider\bin\$configuration$\net461\GetDocument.Insider.*" target="tools\net461" />
|
||||
<file src="..\GetDocumentInsider\bin\x86\$configuration$\net461\GetDocument.Insider.*" target="tools\net461-x86" />
|
||||
<file src="..\GetDocumentInsider\bin\$configuration$\netcoreapp2.0\GetDocument.Insider.*" target="tools\netcoreapp2.0" />
|
||||
</files>
|
||||
</package>
|
||||
86
src/Microsoft.Extensions.ApiDescription.Design/Properties/Resources.Designer.cs
generated
Normal file
86
src/Microsoft.Extensions.ApiDescription.Design/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.Extensions.ApiDescription.Tasks
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.Extensions.ApiDescription.Tasks.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Multiple items have OutputPath='{0}'. All ServiceFileReference, ServiceProjectReference and ServiceUriReference items must have unique OutputPath metadata.
|
||||
/// </summary>
|
||||
internal static string DuplicateFileOutputPaths
|
||||
{
|
||||
get => GetString("DuplicateFileOutputPaths");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiple items have OutputPath='{0}'. All ServiceFileReference, ServiceProjectReference and ServiceUriReference items must have unique OutputPath metadata.
|
||||
/// </summary>
|
||||
internal static string FormatDuplicateFileOutputPaths(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("DuplicateFileOutputPaths"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Mutliple ServiceProjectReference items have DocumentPath='{0}'. ServiceProjectReference items must have unique DocumentPath metadata.
|
||||
/// </summary>
|
||||
internal static string DuplicateProjectDocumentPaths
|
||||
{
|
||||
get => GetString("DuplicateProjectDocumentPaths");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mutliple ServiceProjectReference items have DocumentPath='{0}'. ServiceProjectReference items must have unique DocumentPath metadata.
|
||||
/// </summary>
|
||||
internal static string FormatDuplicateProjectDocumentPaths(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("DuplicateProjectDocumentPaths"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Mutliple ServiceUriReference items have DocumentPath='{0}'. ServiceUriReference items must have unique DocumentPath metadata.
|
||||
/// </summary>
|
||||
internal static string DuplicateUriDocumentPaths
|
||||
{
|
||||
get => GetString("DuplicateUriDocumentPaths");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mutliple ServiceUriReference items have DocumentPath='{0}'. ServiceUriReference items must have unique DocumentPath metadata.
|
||||
/// </summary>
|
||||
internal static string FormatDuplicateUriDocumentPaths(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUriDocumentPaths"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Invalid {0} metadata value for {1} item '{2}'. {0} metadata must not be set to the empty string.
|
||||
/// </summary>
|
||||
internal static string InvalidEmptyMetadataValue
|
||||
{
|
||||
get => GetString("InvalidEmptyMetadataValue");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid {0} metadata value for {1} item '{2}'. {0} metadata must not be set to the empty string.
|
||||
/// </summary>
|
||||
internal static string FormatInvalidEmptyMetadataValue(object p0, object p1, object p2)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidEmptyMetadataValue"), p0, p1, p2);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<?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="DuplicateFileOutputPaths" xml:space="preserve">
|
||||
<value>Multiple items have OutputPath='{0}'. All ServiceFileReference, ServiceProjectReference and ServiceUriReference items must have unique OutputPath metadata.</value>
|
||||
<comment>ServiceProjectReference and ServiceUriReference items become ServiceFileReference items and all ServiceFileReference items must have unique OutputPath metadata.</comment>
|
||||
</data>
|
||||
<data name="DuplicateProjectDocumentPaths" xml:space="preserve">
|
||||
<value>Mutliple ServiceProjectReference items have DocumentPath='{0}'. ServiceProjectReference items must have unique DocumentPath metadata.</value>
|
||||
</data>
|
||||
<data name="DuplicateUriDocumentPaths" xml:space="preserve">
|
||||
<value>Mutliple ServiceUriReference items have DocumentPath='{0}'. ServiceUriReference items must have unique DocumentPath metadata.</value>
|
||||
<comment>Ignore corner case of ServiceProjectReference and ServiceUriReference items having the same DocumentPath.</comment>
|
||||
</data>
|
||||
<data name="InvalidEmptyMetadataValue" xml:space="preserve">
|
||||
<value>Invalid {0} metadata value for {1} item '{2}'. {0} metadata must not be set to the empty string.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<_ApiDescriptionTasksAssemblyTarget
|
||||
Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0</_ApiDescriptionTasksAssemblyTarget>
|
||||
<_ApiDescriptionTasksAssemblyTarget
|
||||
Condition="'$(MSBuildRuntimeType)' != 'Core'">net461</_ApiDescriptionTasksAssemblyTarget>
|
||||
<_ApiDescriptionTasksAssemblyPath>$(MSBuildThisFileDirectory)/../tasks/$(_ApiDescriptionTasksAssemblyTarget)/Microsoft.Extensions.ApiDescription.Tasks.dll</_ApiDescriptionTasksAssemblyPath>
|
||||
<_ApiDescriptionTasksAssemblyTarget />
|
||||
</PropertyGroup>
|
||||
<UsingTask TaskName="GetCurrentItems" AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
|
||||
<UsingTask TaskName="GetFileReferenceMetadata" AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
|
||||
<UsingTask TaskName="GetProjectReferenceMetadata" AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
|
||||
<UsingTask TaskName="GetUriReferenceMetadata" AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
|
||||
<UsingTask TaskName="Microsoft.Extensions.ApiDescription.Tasks.DownloadFile"
|
||||
AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
|
||||
|
||||
<!--
|
||||
Settings users may update as they see fit. All $(...Directory) values are interpreted relative to the client
|
||||
project folder, unless already an absolute path.
|
||||
-->
|
||||
<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>
|
||||
|
||||
<GenerateDefaultDocumentDefaultOptions Condition="'$(GenerateDefaultDocumentDefaultOptions)' == ''" />
|
||||
</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. Builds will invoke a target named
|
||||
"Generate%(DocumentGenerator)Document" to do actual document retrieval / generation.
|
||||
-->
|
||||
<DocumentGenerator>Default</DocumentGenerator>
|
||||
|
||||
<!-- Server project metadata which is likely applicable to all document generators. -->
|
||||
|
||||
<!--
|
||||
Server project's chosen configuration. Corresponds to $(Configuration) which likely matches client project's
|
||||
$(Configuration).
|
||||
-->
|
||||
<Configuration />
|
||||
<!--
|
||||
Server project's extensions path. Corresponds to $(MSBuildProjectExtensionsPath). User must set this if
|
||||
server project's value is not 'obj/'.
|
||||
-->
|
||||
<ProjectExtensionsPath />
|
||||
<!-- Server project's target framework. Defaults to $(TargetFramework) or first of $(TargetFrameworks). -->
|
||||
<TargetFramework />
|
||||
<!--
|
||||
Full path of the server project's generated assembly. Corresponds to $(TargetPath). Because common code builds
|
||||
server projects, file exists prior to document generator invocation.
|
||||
-->
|
||||
<TargetPath />
|
||||
<!--
|
||||
Semicolon-separated list of targets in the server project that should be built. Default is empty, indicating
|
||||
the default targets of the server project. Does not honor $(ProjectReferenceBuildTargets) because that property
|
||||
is too general for these references and it's normally empty too.
|
||||
-->
|
||||
<Targets />
|
||||
|
||||
<!--
|
||||
Metadata specific to the Default document generator (though other document generators are free to use it).
|
||||
-->
|
||||
|
||||
<!--
|
||||
Options added to Default document generator tool's command line. Defaults to
|
||||
$(GenerateDefaultDocumentDefaultOptions) if that is set in the client project.
|
||||
-->
|
||||
<GenerateDefaultDocumentOptions />
|
||||
<!--
|
||||
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 />
|
||||
</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 $(ServiceUriReferenceDirectory).
|
||||
-->
|
||||
<DocumentPath />
|
||||
</ServiceUriReference>
|
||||
|
||||
<ServiceFileReference>
|
||||
<!-- Name of the class to generate. Defaults to %(Filename)Client but with an uppercase first letter. -->
|
||||
<ClassName />
|
||||
<!--
|
||||
Code generator to use. Required and must end with "CSharp" or "TypeScript" (the currently-supported target
|
||||
languages) unless %(OutputPath) is set. Builds will invoke a target named "Generate%(CodeGenerator)" to do
|
||||
actual code generation.
|
||||
-->
|
||||
<CodeGenerator />
|
||||
<!--
|
||||
Namespace to contain generated class. Default is $(RootNamespace).
|
||||
-->
|
||||
<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
|
||||
$(ServiceFileReferenceDirectory). Final value (depending on $(ServiceFileReferenceDirectory)) is likely to be
|
||||
a path relative to the client project.
|
||||
-->
|
||||
<OutputPath />
|
||||
</ServiceFileReference>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project>
|
||||
<!-- Internal settings. Not intended for customization. -->
|
||||
<PropertyGroup>
|
||||
<GenerateServiceProjectReferenceDocumentsDependsOn>
|
||||
_GetTargetFrameworkForServiceProjectReferences;
|
||||
_GetTargetPathForServiceProjectReferences;
|
||||
_GetMetadataForServiceProjectReferences;
|
||||
_BuildServiceProjectReferences;
|
||||
_GenerateServiceProjectReferenceDocuments;
|
||||
_CreateFileItemsForServiceProjectReferences
|
||||
</GenerateServiceProjectReferenceDocumentsDependsOn>
|
||||
<GenerateServiceUriReferenceDocumentsDependsOn>
|
||||
_GetMetadataForServiceUriReferences;
|
||||
_GenerateServiceUriReferenceDocuments
|
||||
</GenerateServiceUriReferenceDocumentsDependsOn>
|
||||
<GenerateServiceFileReferenceCodesDependsOn>
|
||||
GenerateServiceProjectReferenceDocuments;
|
||||
GenerateServiceUriReferenceDocuments;
|
||||
_GetMetadataForServiceFileReferences;
|
||||
_GenerateServiceFileReferenceCodes;
|
||||
_CreateCompileItemsForServiceFileReferences
|
||||
</GenerateServiceFileReferenceCodesDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- ServiceProjectReference support -->
|
||||
|
||||
<!--
|
||||
Metadata setup phase 1: Ensure items have TargetFramework metadata. Calls GetTargetFrameworks in the target
|
||||
project. Inputs and outputs cause MSBuild to run target unconditionally and to batch it (run once per project).
|
||||
-->
|
||||
<Target Name="_GetTargetFrameworkForServiceProjectReferences"
|
||||
Inputs="%(ServiceProjectReference.FullPath)"
|
||||
Outputs="<not-a-file !>">
|
||||
<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="'%(TargetFramework)' == ''">$(_TargetFramework)</TargetFramework>
|
||||
</ServiceProjectReference>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_FullPath />
|
||||
<_TargetFramework />
|
||||
<_TargetFrameworks />
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Metadata setup phase 2: Ensure items have TargetPath metadata. Calls 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="_GetTargetPathForServiceProjectReferences"
|
||||
Inputs="%(ServiceProjectReference.TargetFramework)%(FullPath)')"
|
||||
Outputs="<not-a-file !>">
|
||||
<PropertyGroup>
|
||||
<_FullPath>%(ServiceProjectReference.FullPath)</_FullPath>
|
||||
<_TargetFramework>%(ServiceProjectReference.TargetFramework)</_TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
|
||||
<MSBuild Projects="$(_FullPath)"
|
||||
Properties="TargetFramework=$(_TargetFramework)"
|
||||
RebaseOutputs="true"
|
||||
RemoveProperties="TargetFrameworks;RuntimeIdentifier"
|
||||
Targets="GetTargetPath"
|
||||
UseResultsCache="true">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="_Temporary" />
|
||||
</MSBuild>
|
||||
|
||||
<PropertyGroup>
|
||||
<_TargetPath>%(_Temporary.FullPath)</_TargetPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ServiceProjectReference Update="@(ServiceProjectReference)"
|
||||
Condition="'%(FullPath)' == '$(_FullPath)' AND '%(TargetFramework)' == '$(_TargetFramework)'">
|
||||
<TargetPath>$(_TargetPath)</TargetPath>
|
||||
</ServiceProjectReference>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_FullPath />
|
||||
<_TargetPath />
|
||||
<_TargetFramework />
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<!-- Metadata setup phase 3: Ensure items have DocumentPath metadata. -->
|
||||
<Target Name="_GetMetadataForServiceProjectReferences" Condition="'@(ServiceProjectReference)' != ''">
|
||||
<ItemGroup>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
|
||||
<GetProjectReferenceMetadata Inputs="@(ServiceProjectReference)"
|
||||
DocumentDirectory="$(ServiceProjectReferenceDirectory)">
|
||||
<Output TaskParameter="Outputs" ItemName="_Temporary" />
|
||||
</GetProjectReferenceMetadata>
|
||||
|
||||
<ItemGroup>
|
||||
<ServiceProjectReference Remove="@(ServiceProjectReference)" />
|
||||
<ServiceProjectReference Include="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_BuildServiceProjectReferences"
|
||||
Condition="'$(BuildProjectReferences)' == 'true'"
|
||||
Inputs="@(ServiceProjectReference)"
|
||||
Outputs="%(TargetPath)">
|
||||
<MSBuild Projects="@(ServiceProjectReference -> Distinct())"
|
||||
BuildInParallel="$(BuildInParallel)"
|
||||
RemoveProperties="TargetFramework;TargetFrameworks;RuntimeIdentifier"
|
||||
Targets="%(Targets)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_GetCurrentServiceProjectReference">
|
||||
<GetCurrentItems Input="$(GeneratorMetadata)">
|
||||
<Output TaskParameter="Outputs" ItemName="CurrentServiceProjectReference" />
|
||||
</GetCurrentItems>
|
||||
</Target>
|
||||
|
||||
<Target Name="_GenerateServiceProjectReferenceDocument"
|
||||
DependsOnTargets="_GetCurrentServiceProjectReference;$(GeneratorTarget)" />
|
||||
|
||||
<Target Name="_GenerateServiceProjectReferenceDocuments"
|
||||
Inputs="@(ServiceProjectReference)"
|
||||
Outputs="%(DocumentPath)">
|
||||
<MSBuild BuildInParallel="$(BuildInParallel)"
|
||||
Projects="$(MSBuildProjectFullPath)"
|
||||
Properties="GeneratorTargetPath=%(ServiceProjectReference.DocumentPath);GeneratorTarget=Generate%(DocumentGenerator)Document;GeneratorMetadata=%(SerializedMetadata)"
|
||||
RemoveProperties="TargetFrameworks"
|
||||
Targets="_GenerateServiceProjectReferenceDocument" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_CreateFileItemsForServiceProjectReferences" Condition="'@(ServiceProjectReference)' != ''">
|
||||
<!-- GetProjectReferenceMetadata task guarantees %(DocumentPath) values are unique. -->
|
||||
<ItemGroup>
|
||||
<ServiceFileReference Remove="@(ServiceProjectReference -> '%(DocumentPath)')" />
|
||||
<!-- Condition here is temporary. Useful while GenerateDefaultDocument fails. -->
|
||||
<ServiceFileReference Include="@(ServiceProjectReference -> '%(DocumentPath)')"
|
||||
Condition="Exists('%(ServiceProjectReference.DocumentPath)')">
|
||||
<SourceProject>%(ServiceProjectReference.FullPath)</SourceProject>
|
||||
</ServiceFileReference>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateServiceProjectReferenceDocuments"
|
||||
DependsOnTargets="$(GenerateServiceProjectReferenceDocumentsDependsOn)" />
|
||||
|
||||
<!-- GenerateDefaultDocument -->
|
||||
|
||||
<Target Name="GenerateDefaultDocument">
|
||||
<ItemGroup>
|
||||
<!-- @(CurrentServiceProjectReference) item group will never contain more than one item. -->
|
||||
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
|
||||
<Command>dotnet $(MSBuildThisFileDirectory)/../tools/dotnet-getdocument.dll --project %(FullPath)</Command>
|
||||
<Configuration Condition="'%(Configuration)' == ''">$(Configuration)</Configuration>
|
||||
<GenerateDefaultDocumentOptions
|
||||
Condition="'%(GenerateDefaultDocumentOptions)' == ''">$(GenerateDefaultDocumentDefaultOptions)</GenerateDefaultDocumentOptions>
|
||||
</CurrentServiceProjectReference>
|
||||
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
|
||||
<Command>%(Command) --framework %(TargetFramework) --output %(DocumentPath)</Command>
|
||||
</CurrentServiceProjectReference>
|
||||
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
|
||||
<Command Condition="'%(Method)' != ''">%(Command) --method %(Method)</Command>
|
||||
</CurrentServiceProjectReference>
|
||||
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
|
||||
<Command Condition="'%(Service)' != ''">%(Command) --service %(Service)</Command>
|
||||
</CurrentServiceProjectReference>
|
||||
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
|
||||
<Command
|
||||
Condition="'%(ProjectExtensionsPath)' != ''">%(Command) --projectExtensionsPath %(ProjectExtensionsPath)</Command>
|
||||
</CurrentServiceProjectReference>
|
||||
<CurrentServiceProjectReference Update="@(CurrentServiceProjectReference)">
|
||||
<Command>%(Command) --configuration %(Configuration) %(GenerateDefaultDocumentOptions)</Command>
|
||||
</CurrentServiceProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<Message Importance="high" Text="%0AGenerateDefaultDocument:" />
|
||||
<Message Importance="high" Text=" %(CurrentServiceProjectReference.Command)" />
|
||||
<Exec Command="%(CurrentServiceProjectReference.Command)"
|
||||
IgnoreExitCode="$([System.IO.File]::Exists('%(DocumentPath)'))" />
|
||||
</Target>
|
||||
|
||||
<!-- ServiceUriReference support -->
|
||||
|
||||
<Target Name="_GetMetadataForServiceUriReferences" Condition="'@(ServiceUriReference)' != ''">
|
||||
<ItemGroup>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
|
||||
<GetUriReferenceMetadata Inputs="@(ServiceUriReference)" DocumentDirectory="$(ServiceUriReferenceDirectory)">
|
||||
<Output TaskParameter="Outputs" ItemName="_Temporary" />
|
||||
</GetUriReferenceMetadata>
|
||||
|
||||
<ItemGroup>
|
||||
<ServiceUriReference Remove="@(ServiceUriReference)" />
|
||||
<ServiceUriReference Include="@(_Temporary)" />
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_GenerateServiceUriReferenceDocuments" Condition="'@(ServiceUriReference)' != ''">
|
||||
<Microsoft.Extensions.ApiDescription.Tasks.DownloadFile Uri="%(ServiceUriReference.Identity)"
|
||||
DestinationPath="%(DocumentPath)"
|
||||
Overwrite="$(ServiceUriReferenceCheckIfNewer)" />
|
||||
|
||||
<!-- GetUriReferenceMetadata task guarantees %(DocumentPath) values are unique. -->
|
||||
<ItemGroup>
|
||||
<ServiceFileReference Remove="@(ServiceUriReference -> '%(DocumentPath)')" />
|
||||
<ServiceFileReference Include="@(ServiceUriReference -> '%(DocumentPath)')">
|
||||
<SourceUri>%(ServiceUriReference.Identity)</SourceUri>
|
||||
</ServiceFileReference>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateServiceUriReferenceDocuments"
|
||||
DependsOnTargets="$(GenerateServiceUriReferenceDocumentsDependsOn)" />
|
||||
|
||||
<!-- ServiceFileReference support -->
|
||||
|
||||
<Target Name="_GetMetadataForServiceFileReferences" Condition="'@(ServiceFileReference)' != ''">
|
||||
<ItemGroup>
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
|
||||
<GetFileReferenceMetadata Inputs="@(ServiceFileReference)"
|
||||
Extension="$(DefaultLanguageSourceExtension)"
|
||||
Namespace="$(RootNamespace)"
|
||||
OutputDirectory="$(ServiceFileReferenceDirectory)">
|
||||
<Output TaskParameter="Outputs" ItemName="_Temporary" />
|
||||
</GetFileReferenceMetadata>
|
||||
|
||||
<ItemGroup>
|
||||
<ServiceFileReference Remove="@(ServiceFileReference)" />
|
||||
<ServiceFileReference Include="@(_Temporary)" />
|
||||
<_Temporary Remove="@(_Temporary)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_GetCurrentServiceFileReference">
|
||||
<GetCurrentItems Input="$(GeneratorMetadata)">
|
||||
<Output TaskParameter="Outputs" ItemName="CurrentServiceFileReference" />
|
||||
</GetCurrentItems>
|
||||
</Target>
|
||||
|
||||
<Target Name="_GenerateServiceFileReferenceCode"
|
||||
DependsOnTargets="_GetCurrentServiceFileReference;$(GeneratorTarget)" />
|
||||
|
||||
<Target Name="_GenerateServiceFileReferenceCodes" Inputs="@(ServiceFileReference)" Outputs="%(OutputPath)">
|
||||
<MSBuild BuildInParallel="$(BuildInParallel)"
|
||||
Projects="$(MSBuildProjectFullPath)"
|
||||
Properties="GeneratorTargetPath=%(ServiceFileReference.OutputPath);GeneratorTarget=Generate%(CodeGenerator);GeneratorMetadata=%(SerializedMetadata)"
|
||||
RemoveProperties="TargetFrameworks"
|
||||
Targets="_GenerateServiceFileReferenceCode" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_CreateCompileItemsForServiceFileReferences" Condition="'@(ServiceFileReference)' != ''">
|
||||
<!--
|
||||
While %(DocumentPath) metadata may include duplicates (due to overlaps between ServiceUriReference and
|
||||
ServiceProjectReference items), GetFileReferenceMetadata task guarantees %(OutputPath) values are unique.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<_Files Remove="@(_Files)" />
|
||||
<_Files Include="@(ServiceFileReference -> '%(OutputPath)')"
|
||||
Condition="$([System.IO.File]::Exists('%(ServiceFileReference.OutputPath)'))">
|
||||
<OutputPathExtension>$([System.IO.Path]::GetExtension('%(ServiceFileReference.OutputPath)'))</OutputPathExtension>
|
||||
</_Files>
|
||||
<_Directories Remove="@(_Directories)" />
|
||||
<_Directories Include="@(ServiceFileReference -> '%(OutputPath)')"
|
||||
Condition="Exists('%(ServiceFileReference.OutputPath)') AND ! $([System.IO.File]::Exists('%(ServiceFileReference.OutputPath)'))" />
|
||||
|
||||
<!-- If OutputPath is a file, add it directly to relevant items. -->
|
||||
<TypeScriptCompile Remove="@(_Files)" Condition="'%(_Files.OutputPathExtension)' == '.ts'" />
|
||||
<TypeScriptCompile Include="@(_Files)" Condition="'%(_Files.OutputPathExtension)' == '.ts'">
|
||||
<SourceDocument>%(_Files.FullPath)</SourceDocument>
|
||||
</TypeScriptCompile>
|
||||
|
||||
<Compile Remove="@(_Files)"
|
||||
Condition="'$(DefaultLanguageSourceExtension)' != '.ts' AND '%(_Files.OutputPathExtension)' == '$(DefaultLanguageSourceExtension)'" />
|
||||
<Compile Include="@(_Files)"
|
||||
Condition="'$(DefaultLanguageSourceExtension)' != '.ts' AND '%(_Files.OutputPathExtension)' == '$(DefaultLanguageSourceExtension)'">
|
||||
<SourceDocument>%(ServiceFileReference.FullPath)</SourceDocument>
|
||||
</Compile>
|
||||
|
||||
<!-- Otherwise, add all descendent files with the expected extension. -->
|
||||
<TypeScriptCompile Remove="@(_Directories -> '%(Identity)/**/*.ts')" />
|
||||
<TypeScriptCompile Include="@(_Directories -> '%(Identity)/**/*.ts')">
|
||||
<SourceDocument>%(_Directories.FullPath)</SourceDocument>
|
||||
</TypeScriptCompile>
|
||||
|
||||
<Compile Remove="@(_Directories -> '%(Identity)/**/*.$(DefaultLanguageSourceExtension)')"
|
||||
Condition="'$(DefaultLanguageSourceExtension)' != '.ts'" />
|
||||
<Compile Include="@(_Directories -> '%(Identity)/**/*.$(DefaultLanguageSourceExtension)')"
|
||||
Condition="'$(DefaultLanguageSourceExtension)' != '.ts'">
|
||||
<SourceDocument>%(_Directories.FullPath)</SourceDocument>
|
||||
</Compile>
|
||||
|
||||
<_Files Remove="@(_Files)" />
|
||||
<_Directories Remove="@(_Directories)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateServiceFileReferenceCodes"
|
||||
BeforeTargets="BeforeCompile"
|
||||
DependsOnTargets="$(GenerateServiceFileReferenceCodesDependsOn)" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project>
|
||||
<Target Name="GenerateServiceFileReferenceCodes" BeforeTargets="BeforeCompile">
|
||||
<MsBuild Projects="$(MSBuildProjectFile)"
|
||||
Targets="GenerateServiceFileReferenceCodes"
|
||||
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0])"
|
||||
RemoveProperties="TargetFrameworks;RuntimeIdentifier" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
// 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 Microsoft.DotNet.Cli.CommandLine;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool.Commands
|
||||
{
|
||||
internal class InvokeCommand : HelpCommandBase
|
||||
{
|
||||
private const string InsideManName = "GetDocument.Insider";
|
||||
|
||||
private IList<string> _args;
|
||||
private CommandOption _configuration;
|
||||
private CommandOption _output;
|
||||
private CommandOption _project;
|
||||
private CommandOption _projectExtensionsPath;
|
||||
private CommandOption _runtime;
|
||||
private CommandOption _targetFramework;
|
||||
|
||||
public override void Configure(CommandLineApplication command)
|
||||
{
|
||||
var options = new ProjectOptions();
|
||||
options.Configure(command);
|
||||
|
||||
_configuration = options.Configuration;
|
||||
_project = options.Project;
|
||||
_projectExtensionsPath = options.ProjectExtensionsPath;
|
||||
_runtime = options.Runtime;
|
||||
_targetFramework = options.TargetFramework;
|
||||
|
||||
_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.FormatUsingProject(projectFile));
|
||||
|
||||
var project = Project.FromFile(
|
||||
projectFile,
|
||||
_projectExtensionsPath.Value(),
|
||||
_targetFramework.Value(),
|
||||
_configuration.Value(),
|
||||
_runtime.Value());
|
||||
if (!File.Exists(project.TargetPath))
|
||||
{
|
||||
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.FormatNETCoreApp1Project(project.ProjectName, targetFramework.Version));
|
||||
}
|
||||
|
||||
args.Add("exec");
|
||||
args.Add("--depsFile");
|
||||
args.Add(project.ProjectDepsFilePath);
|
||||
|
||||
if (!string.IsNullOrEmpty(project.ProjectAssetsFile))
|
||||
{
|
||||
using (var reader = new JsonTextReader(File.OpenText(project.ProjectAssetsFile)))
|
||||
{
|
||||
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.ProjectRuntimeConfigFilePath))
|
||||
{
|
||||
args.Add("--runtimeConfig");
|
||||
args.Add(project.ProjectRuntimeConfigFilePath);
|
||||
}
|
||||
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.FormatNETStandardProject(project.ProjectName));
|
||||
|
||||
default:
|
||||
throw new CommandException(
|
||||
Resources.FormatUnsupportedFramework(project.ProjectName, targetFramework.Identifier));
|
||||
}
|
||||
|
||||
args.AddRange(_args);
|
||||
args.Add("--assembly");
|
||||
args.Add(project.TargetPath);
|
||||
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 (_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.ProjectDirectory);
|
||||
}
|
||||
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.FormatNoProjectInDirectory(path)
|
||||
: errorWhenNoProject);
|
||||
}
|
||||
if (projectFiles.Count != 1)
|
||||
{
|
||||
throw new CommandException(
|
||||
specified
|
||||
? Resources.FormatMultipleProjectsInDirectory(path)
|
||||
: errorWhenMultipleProjects);
|
||||
}
|
||||
|
||||
return projectFiles[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.Extensions.ApiDescription.Tool.Commands;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using IODirectory = System.IO.Directory;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
internal class Project
|
||||
{
|
||||
private const string ResourceFilename = "ServiceProjectReferenceMetadata.targets";
|
||||
private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Tool." + ResourceFilename;
|
||||
|
||||
private Project()
|
||||
{
|
||||
}
|
||||
|
||||
public string AssemblyName { get; private set; }
|
||||
|
||||
public string ConfigPath { get; private set; }
|
||||
|
||||
public string Configuration { get; private set; }
|
||||
|
||||
public string DefaultDocumentName { get; private set; }
|
||||
|
||||
public string DefaultMethod { get; private set; }
|
||||
|
||||
public string DefaultService { get; private set; }
|
||||
|
||||
public string OutputPath { get; private set; }
|
||||
|
||||
public string Platform { get; private set; }
|
||||
|
||||
public string PlatformTarget { get; private set; }
|
||||
|
||||
public string ProjectAssetsFile { get; private set; }
|
||||
|
||||
public string ProjectDepsFilePath { get; private set; }
|
||||
|
||||
public string ProjectDirectory { get; private set; }
|
||||
|
||||
public string ProjectExtensionsPath { get; private set; }
|
||||
|
||||
public string ProjectName { get; private set; }
|
||||
|
||||
public string ProjectRuntimeConfigFilePath { get; private set; }
|
||||
|
||||
public string RuntimeFrameworkVersion { get; private set; }
|
||||
|
||||
public string RuntimeIdentifier { get; private set; }
|
||||
|
||||
public string TargetFramework { get; private set; }
|
||||
|
||||
public string TargetFrameworkMoniker { get; private set; }
|
||||
|
||||
public string TargetPath { get; private set; }
|
||||
|
||||
public static Project FromFile(
|
||||
string projectFile,
|
||||
string buildExtensionsDirectory,
|
||||
string framework = null,
|
||||
string configuration = null,
|
||||
string runtime = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(projectFile))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFile));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(buildExtensionsDirectory))
|
||||
{
|
||||
buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(projectFile), "obj");
|
||||
}
|
||||
|
||||
IODirectory.CreateDirectory(buildExtensionsDirectory);
|
||||
|
||||
var assembly = typeof(Project).Assembly;
|
||||
var targetsPath = Path.Combine(
|
||||
buildExtensionsDirectory,
|
||||
$"{Path.GetFileName(projectFile)}.{ResourceFilename}");
|
||||
using (var input = assembly.GetManifestResourceStream(MSBuildResourceName))
|
||||
{
|
||||
using (var output = File.OpenWrite(targetsPath))
|
||||
{
|
||||
// NB: Copy always in case it changes
|
||||
Reporter.WriteVerbose(Resources.FormatWritingFile(targetsPath));
|
||||
input.CopyTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
IDictionary<string, string> metadata;
|
||||
var metadataPath = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
var args = new List<string>
|
||||
{
|
||||
"msbuild",
|
||||
"/target:WriteServiceProjectReferenceMetadata",
|
||||
"/verbosity:quiet",
|
||||
"/nologo",
|
||||
$"/property:ServiceProjectReferenceMetadataPath={metadataPath}",
|
||||
projectFile,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(framework))
|
||||
{
|
||||
args.Add($"/property:TargetFramework={framework}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(configuration))
|
||||
{
|
||||
args.Add($"/property:Configuration={configuration}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(runtime))
|
||||
{
|
||||
args.Add($"/property:RuntimeIdentifier={runtime}");
|
||||
}
|
||||
|
||||
var exitCode = Exe.Run("dotnet", args);
|
||||
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(metadataPath);
|
||||
File.Delete(targetsPath);
|
||||
}
|
||||
|
||||
var project = new Project
|
||||
{
|
||||
DefaultDocumentName = metadata[nameof(DefaultDocumentName)],
|
||||
DefaultMethod = metadata[nameof(DefaultMethod)],
|
||||
DefaultService = metadata[nameof(DefaultService)],
|
||||
|
||||
AssemblyName = metadata[nameof(AssemblyName)],
|
||||
Configuration = metadata[nameof(Configuration)],
|
||||
OutputPath = metadata[nameof(OutputPath)],
|
||||
Platform = metadata[nameof(Platform)],
|
||||
PlatformTarget = metadata[nameof(PlatformTarget)] ?? metadata[nameof(Platform)],
|
||||
ProjectAssetsFile = metadata[nameof(ProjectAssetsFile)],
|
||||
ProjectDepsFilePath = metadata[nameof(ProjectDepsFilePath)],
|
||||
ProjectDirectory = metadata[nameof(ProjectDirectory)],
|
||||
ProjectExtensionsPath = metadata[nameof(ProjectExtensionsPath)],
|
||||
ProjectName = metadata[nameof(ProjectName)],
|
||||
ProjectRuntimeConfigFilePath = metadata[nameof(ProjectRuntimeConfigFilePath)],
|
||||
RuntimeFrameworkVersion = metadata[nameof(RuntimeFrameworkVersion)],
|
||||
RuntimeIdentifier = metadata[nameof(RuntimeIdentifier)],
|
||||
TargetFramework = metadata[nameof(TargetFramework)],
|
||||
TargetFrameworkMoniker = metadata[nameof(TargetFrameworkMoniker)],
|
||||
TargetPath = metadata[nameof(TargetPath)],
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(project.OutputPath))
|
||||
{
|
||||
throw new CommandException(
|
||||
Resources.FormatGetMetadataValueFailed(nameof(OutputPath), nameof(OutputPath)));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(project.ProjectDirectory))
|
||||
{
|
||||
throw new CommandException(
|
||||
Resources.FormatGetMetadataValueFailed(nameof(ProjectDirectory), "MSBuildProjectDirectory"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(project.TargetPath))
|
||||
{
|
||||
throw new CommandException(
|
||||
Resources.FormatGetMetadataValueFailed(nameof(TargetPath), nameof(TargetPath)));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(project.ProjectDirectory))
|
||||
{
|
||||
project.OutputPath = Path.GetFullPath(
|
||||
Path.Combine(IODirectory.GetCurrentDirectory(), project.ProjectDirectory));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(project.OutputPath))
|
||||
{
|
||||
project.OutputPath = Path.GetFullPath(Path.Combine(project.ProjectDirectory, project.OutputPath));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(project.ProjectExtensionsPath))
|
||||
{
|
||||
project.ProjectExtensionsPath = Path.GetFullPath(
|
||||
Path.Combine(project.ProjectDirectory, project.ProjectExtensionsPath));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(project.TargetPath))
|
||||
{
|
||||
project.TargetPath = Path.GetFullPath(Path.Combine(project.OutputPath, project.TargetPath));
|
||||
}
|
||||
|
||||
// Some document generation tools support non-ASP.NET Core projects. Any of the remaining properties may
|
||||
// thus be null empty.
|
||||
var configPath = $"{project.TargetPath}.config";
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
project.ConfigPath = configPath;
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(project.ProjectAssetsFile) || Path.IsPathRooted(project.ProjectAssetsFile)))
|
||||
{
|
||||
project.ProjectAssetsFile = Path.GetFullPath(
|
||||
Path.Combine(project.ProjectDirectory, project.ProjectAssetsFile));
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(project.ProjectDepsFilePath) || Path.IsPathRooted(project.ProjectDepsFilePath)))
|
||||
{
|
||||
project.ProjectDepsFilePath = Path.GetFullPath(
|
||||
Path.Combine(project.ProjectDirectory, project.ProjectDepsFilePath));
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(project.ProjectRuntimeConfigFilePath) ||
|
||||
Path.IsPathRooted(project.ProjectRuntimeConfigFilePath)))
|
||||
{
|
||||
project.ProjectRuntimeConfigFilePath = Path.GetFullPath(
|
||||
Path.Combine(project.OutputPath, project.ProjectRuntimeConfigFilePath));
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
internal class ProjectOptions
|
||||
{
|
||||
public CommandOption Configuration { get; private set; }
|
||||
|
||||
public CommandOption Project { get; private set; }
|
||||
|
||||
public CommandOption ProjectExtensionsPath { get; private set; }
|
||||
|
||||
public CommandOption Runtime { get; private set; }
|
||||
|
||||
public CommandOption TargetFramework { get; private set; }
|
||||
|
||||
public void Configure(CommandLineApplication command)
|
||||
{
|
||||
Configuration = command.Option("--configuration <CONFIGURATION>", Resources.ConfigurationDescription);
|
||||
Project = command.Option("-p|--project <PROJECT>", Resources.ProjectDescription);
|
||||
ProjectExtensionsPath = command.Option(
|
||||
"--projectExtensionsPath <PATH>",
|
||||
Resources.ProjectExtensionsPathDescription);
|
||||
Runtime = command.Option("--runtime <RUNTIME_IDENTIFIER>", Resources.RuntimeDescription);
|
||||
TargetFramework = command.Option("--framework <FRAMEWORK>", Resources.TargetFrameworkDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.Extensions.ApiDescription.Tool
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.Extensions.ApiDescription.Tool.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The configuration to use.
|
||||
/// </summary>
|
||||
internal static string ConfigurationDescription
|
||||
{
|
||||
get => GetString("ConfigurationDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The configuration to use.
|
||||
/// </summary>
|
||||
internal static string FormatConfigurationDescription()
|
||||
=> GetString("ConfigurationDescription");
|
||||
|
||||
/// <summary>
|
||||
/// dotnet-getdocument
|
||||
/// </summary>
|
||||
internal static string CommandFullName
|
||||
{
|
||||
get => GetString("CommandFullName");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// dotnet-getdocument
|
||||
/// </summary>
|
||||
internal static string FormatCommandFullName()
|
||||
=> GetString("CommandFullName");
|
||||
|
||||
/// <summary>
|
||||
/// The target framework.
|
||||
/// </summary>
|
||||
internal static string TargetFrameworkDescription
|
||||
{
|
||||
get => GetString("TargetFrameworkDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The target framework.
|
||||
/// </summary>
|
||||
internal static string FormatTargetFrameworkDescription()
|
||||
=> GetString("TargetFrameworkDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Unable to retrieve project metadata. If you are using custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, use the --projectExtensionsPath option.
|
||||
/// </summary>
|
||||
internal static string GetMetadataFailed
|
||||
{
|
||||
get => GetString("GetMetadataFailed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to retrieve project metadata. If you are using custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, use the --projectExtensionsPath option.
|
||||
/// </summary>
|
||||
internal static string FormatGetMetadataFailed()
|
||||
=> GetString("GetMetadataFailed");
|
||||
|
||||
/// <summary>
|
||||
/// More than one project was found in the current working directory. Use the --project option.
|
||||
/// </summary>
|
||||
internal static string MultipleProjects
|
||||
{
|
||||
get => GetString("MultipleProjects");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More than one project was found in the current working directory. Use the --project option.
|
||||
/// </summary>
|
||||
internal static string FormatMultipleProjects()
|
||||
=> GetString("MultipleProjects");
|
||||
|
||||
/// <summary>
|
||||
/// More than one project was found in directory '{0}'. Specify one using its file name.
|
||||
/// </summary>
|
||||
internal static string MultipleProjectsInDirectory
|
||||
{
|
||||
get => GetString("MultipleProjectsInDirectory");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More than one project was found in directory '{0}'. Specify one using its file name.
|
||||
/// </summary>
|
||||
internal static string FormatMultipleProjectsInDirectory(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MultipleProjectsInDirectory"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Project '{0}' targets framework '.NETCoreApp' version '{1}'. This version of the dotnet-getdocument tool only supports version 2.0 or higher.
|
||||
/// </summary>
|
||||
internal static string NETCoreApp1Project
|
||||
{
|
||||
get => GetString("NETCoreApp1Project");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Project '{0}' targets framework '.NETCoreApp' version '{1}'. This version of the dotnet-getdocument tool only supports version 2.0 or higher.
|
||||
/// </summary>
|
||||
internal static string FormatNETCoreApp1Project(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("NETCoreApp1Project"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Project '{0}' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the dotnet-getdocument 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>
|
||||
internal static string NETStandardProject
|
||||
{
|
||||
get => GetString("NETStandardProject");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Project '{0}' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the dotnet-getdocument 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>
|
||||
internal static string FormatNETStandardProject(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("NETStandardProject"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Do not colorize output.
|
||||
/// </summary>
|
||||
internal static string NoColorDescription
|
||||
{
|
||||
get => GetString("NoColorDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do not colorize output.
|
||||
/// </summary>
|
||||
internal static string FormatNoColorDescription()
|
||||
=> GetString("NoColorDescription");
|
||||
|
||||
/// <summary>
|
||||
/// No project was found. Change the current working directory or use the --project option.
|
||||
/// </summary>
|
||||
internal static string NoProject
|
||||
{
|
||||
get => GetString("NoProject");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No project was found. Change the current working directory or use the --project option.
|
||||
/// </summary>
|
||||
internal static string FormatNoProject()
|
||||
=> GetString("NoProject");
|
||||
|
||||
/// <summary>
|
||||
/// No project was found in directory '{0}'.
|
||||
/// </summary>
|
||||
internal static string NoProjectInDirectory
|
||||
{
|
||||
get => GetString("NoProjectInDirectory");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No project was found in directory '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatNoProjectInDirectory(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("NoProjectInDirectory"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Prefix output with level.
|
||||
/// </summary>
|
||||
internal static string PrefixDescription
|
||||
{
|
||||
get => GetString("PrefixDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix output with level.
|
||||
/// </summary>
|
||||
internal static string FormatPrefixDescription()
|
||||
=> GetString("PrefixDescription");
|
||||
|
||||
/// <summary>
|
||||
/// The project to use.
|
||||
/// </summary>
|
||||
internal static string ProjectDescription
|
||||
{
|
||||
get => GetString("ProjectDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The project to use.
|
||||
/// </summary>
|
||||
internal static string FormatProjectDescription()
|
||||
=> GetString("ProjectDescription");
|
||||
|
||||
/// <summary>
|
||||
/// The MSBuild project extensions path. Defaults to "obj".
|
||||
/// </summary>
|
||||
internal static string ProjectExtensionsPathDescription
|
||||
{
|
||||
get => GetString("ProjectExtensionsPathDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The MSBuild project extensions path. Defaults to "obj".
|
||||
/// </summary>
|
||||
internal static string FormatProjectExtensionsPathDescription()
|
||||
=> GetString("ProjectExtensionsPathDescription");
|
||||
|
||||
/// <summary>
|
||||
/// The runtime identifier to use.
|
||||
/// </summary>
|
||||
internal static string RuntimeDescription
|
||||
{
|
||||
get => GetString("RuntimeDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The runtime identifier to use.
|
||||
/// </summary>
|
||||
internal static string FormatRuntimeDescription()
|
||||
=> GetString("RuntimeDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Project '{0}' targets framework '{1}'. The dotnet-getdocument tool does not support this framework.
|
||||
/// </summary>
|
||||
internal static string UnsupportedFramework
|
||||
{
|
||||
get => GetString("UnsupportedFramework");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Project '{0}' targets framework '{1}'. The dotnet-getdocument tool does not support this framework.
|
||||
/// </summary>
|
||||
internal static string FormatUnsupportedFramework(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UnsupportedFramework"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Using project '{0}'.
|
||||
/// </summary>
|
||||
internal static string UsingProject
|
||||
{
|
||||
get => GetString("UsingProject");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using project '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatUsingProject(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingProject"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Show verbose output.
|
||||
/// </summary>
|
||||
internal static string VerboseDescription
|
||||
{
|
||||
get => GetString("VerboseDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show verbose output.
|
||||
/// </summary>
|
||||
internal static string FormatVerboseDescription()
|
||||
=> GetString("VerboseDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Writing '{0}'...
|
||||
/// </summary>
|
||||
internal static string WritingFile
|
||||
{
|
||||
get => GetString("WritingFile");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writing '{0}'...
|
||||
/// </summary>
|
||||
internal static string FormatWritingFile(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Project output not found. Project must be up-to-date when using this tool.
|
||||
/// </summary>
|
||||
internal static string MustBuild
|
||||
{
|
||||
get => GetString("MustBuild");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Project output not found. Project must be up-to-date when using this tool.
|
||||
/// </summary>
|
||||
internal static string FormatMustBuild()
|
||||
=> GetString("MustBuild");
|
||||
|
||||
/// <summary>
|
||||
/// The file to write the result to.
|
||||
/// </summary>
|
||||
internal static string OutputDescription
|
||||
{
|
||||
get => GetString("OutputDescription");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file to write the result to.
|
||||
/// </summary>
|
||||
internal static string FormatOutputDescription()
|
||||
=> GetString("OutputDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Unable to retrieve '{0}' project metadata. Ensure '{1}' is set.
|
||||
/// </summary>
|
||||
internal static string GetMetadataValueFailed
|
||||
{
|
||||
get => GetString("GetMetadataValueFailed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to retrieve '{0}' project metadata. Ensure '{1}' is set.
|
||||
/// </summary>
|
||||
internal static string FormatGetMetadataValueFailed(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("GetMetadataValueFailed"), p0, p1);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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="TargetFrameworkDescription" 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 --projectExtensionsPath 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 '{0}'. Specify one using its file name.</value>
|
||||
</data>
|
||||
<data name="NETCoreApp1Project" xml:space="preserve">
|
||||
<value>Project '{0}' targets framework '.NETCoreApp' version '{1}'. This version of the dotnet-getdocument tool only supports version 2.0 or higher.</value>
|
||||
</data>
|
||||
<data name="NETStandardProject" xml:space="preserve">
|
||||
<value>Project '{0}' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the dotnet-getdocument 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 '{0}'.</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="ProjectExtensionsPathDescription" 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 '{0}' targets framework '{1}'. The dotnet-getdocument tool does not support this framework.</value>
|
||||
</data>
|
||||
<data name="UsingProject" xml:space="preserve">
|
||||
<value>Using project '{0}'.</value>
|
||||
</data>
|
||||
<data name="VerboseDescription" xml:space="preserve">
|
||||
<value>Show verbose output.</value>
|
||||
</data>
|
||||
<data name="WritingFile" xml:space="preserve">
|
||||
<value>Writing '{0}'...</value>
|
||||
</data>
|
||||
<data name="MustBuild" xml:space="preserve">
|
||||
<value>Project output not found. Project must be up-to-date when using this tool.</value>
|
||||
</data>
|
||||
<data name="OutputDescription" xml:space="preserve">
|
||||
<value>The file to write the result to.</value>
|
||||
</data>
|
||||
<data name="GetMetadataValueFailed" xml:space="preserve">
|
||||
<value>Unable to retrieve '{0}' project metadata. Ensure '$({1})' is set.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?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="WriteServiceProjectReferenceMetadata"
|
||||
Condition="'$(TargetFramework)' != ''"
|
||||
Returns="@(ServiceProjectReferenceMetadata)">
|
||||
<ItemGroup Condition="'$(TargetFramework)' != ''">
|
||||
<ServiceProjectReferenceMetadata Include="DefaultDocumentName: $(DefaultServiceProjectDocumentName)" />
|
||||
<ServiceProjectReferenceMetadata Include="DefaultMethod: $(DefaultServiceProjectMethod)" />
|
||||
<ServiceProjectReferenceMetadata Include="DefaultService: $(DefaultServiceProjectService)" />
|
||||
|
||||
<ServiceProjectReferenceMetadata Include="AssemblyName: $(AssemblyName)" />
|
||||
<ServiceProjectReferenceMetadata Include="Configuration: $(Configuration)" />
|
||||
<ServiceProjectReferenceMetadata Include="OutputPath: $(OutputPath)" />
|
||||
<ServiceProjectReferenceMetadata Include="Platform: $(Platform)" />
|
||||
<ServiceProjectReferenceMetadata Include="PlatformTarget: $(PlatformTarget)" />
|
||||
<ServiceProjectReferenceMetadata Include="ProjectAssetsFile: $(ProjectAssetsFile)" />
|
||||
<ServiceProjectReferenceMetadata Include="ProjectDepsFilePath: $(ProjectDepsFilePath)" />
|
||||
<ServiceProjectReferenceMetadata Include="ProjectDirectory: $(MSBuildProjectDirectory)" />
|
||||
<ServiceProjectReferenceMetadata Include="ProjectExtensionsPath: $(MSBuildProjectExtensionsPath)" />
|
||||
<ServiceProjectReferenceMetadata Include="ProjectName: $(MSBuildProjectName)" />
|
||||
<ServiceProjectReferenceMetadata Include="ProjectRuntimeConfigFilePath: $(ProjectRuntimeConfigFilePath)" />
|
||||
<ServiceProjectReferenceMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
|
||||
<ServiceProjectReferenceMetadata Include="RuntimeIdentifier: $(RuntimeIdentifier)" />
|
||||
<ServiceProjectReferenceMetadata Include="TargetFramework: $(TargetFramework)" />
|
||||
<ServiceProjectReferenceMetadata Include="TargetFrameworkMoniker: $(TargetFrameworkMoniker)" />
|
||||
<ServiceProjectReferenceMetadata Include="TargetPath: $(TargetPath)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!-- Write information only in inner build. -->
|
||||
<Target Name="WriteServiceProjectReferenceMetadata" Returns="@(ServiceProjectReferenceMetadata)">
|
||||
<MSBuild Condition="'$(TargetFramework)' == ''"
|
||||
Projects="$(MSBuildProjectFile)"
|
||||
Targets="WriteServiceProjectReferenceMetadata"
|
||||
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);ServiceProjectReferenceMetadataFile=$(ServiceProjectReferenceMetadataFile)" />
|
||||
|
||||
<WriteLinesToFile Condition="'$(TargetFramework)' != ''"
|
||||
File="$(ServiceProjectReferenceMetadataPath)"
|
||||
Lines="@(ServiceProjectReferenceMetadata)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Internal.AspNetCore.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>dotnet-getdocument</AssemblyName>
|
||||
<Description>GetDocument Command-line Tool outside man</Description>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Microsoft.Extensions.ApiDescription.Tool</RootNamespace>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="../GetDocumentInsider/Ansi*.cs" />
|
||||
<Compile Include="../GetDocumentInsider/CommandException.cs" />
|
||||
<Compile Include="../GetDocumentInsider/CommandLineUtils/*.cs" LinkBase="CommandLineUtils" />
|
||||
<Compile Include="../GetDocumentInsider/Commands/CommandBase.cs" Link="Commands/CommandBase.cs" />
|
||||
<Compile Include="../GetDocumentInsider/Commands/HelpCommandBase.cs" Link="Commands/HelpCommandBase.cs" />
|
||||
<Compile Include="../GetDocumentInsider/ProductInfo.cs" />
|
||||
<Compile Include="../GetDocumentInsider/Reporter.cs" />
|
||||
|
||||
<EmbeddedResource Include="ServiceProjectReferenceMetadata.targets" />
|
||||
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Loading…
Reference in New Issue