Add more properties controlling service reference code generation (#10641)

- `$(OpenApiGenerateCodeOnBuild)` controls if targets run before compile targets
  - #4924
  - also correct multiple invocations when project has multiple target frameworks
- `$(OpenApiBuildReferencedProjects)` controls whether `@(OpenApiProjectReference)` items build automatically
  - #6582
- rename a few other properties and targets

also:
- add symbols for Microsoft.Extensions.ApiDescription.Client task assembly
  - #10508
- unconditionally run `OpenApiGetDocuments` target in referenced projects
  - corrects compilation in design-time builds
  - no longer uses `@(ProjectReferenceWithConfiguration)`; referenced project chooses all property values

nits:
- consolidate into a single `$(GenerateOpenApiCodeDependsOn)` property
- rename task assembly and namespaces in Microsoft.Extensions.ApiDesription.Client to match the project
- allow `OpenApiGetDocuments` targets to run in parallel if `$(BuildInParallel)` is enabled
- remove `$(OpenApiCodeDirectory)` normalization; never concatenated with anything else
This commit is contained in:
Doug Bunting 2019-06-02 22:09:42 -07:00 committed by GitHub
parent 4c8ca0b080
commit 25672336f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 103 additions and 65 deletions

View File

@ -8,7 +8,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.Specification.Tests" ProjectPath="$(RepoRoot)src\Identity\Specification.Tests\src\Microsoft.AspNetCore.Identity.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.Web.Xdt.Extensions" ProjectPath="$(RepoRoot)src\SiteExtensions\Microsoft.Web.Xdt.Extensions\src\Microsoft.Web.Xdt.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepoRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Tasks" ProjectPath="$(RepoRoot)src\Mvc\Extensions.ApiDescription.Client\src\Microsoft.Extensions.ApiDescription.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Client" ProjectPath="$(RepoRoot)src\Mvc\Extensions.ApiDescription.Client\src\Microsoft.Extensions.ApiDescription.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Specification.Tests" ProjectPath="$(RepoRoot)src\SignalR\server\Specification.Tests\src\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Build" ProjectPath="$(RepoRoot)src\Components\Blazor\Build\src\Microsoft.AspNetCore.Blazor.Build.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" RefProjectPath="$(RepoRoot)src\DefaultBuilder\ref\Microsoft.AspNetCore.csproj" />

View File

@ -6,7 +6,7 @@ using System.Text;
// Copied from
// https://github.com/aspnet/AspNetCore-Tooling/blob/master/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CSharpIdentifier.cs
namespace Microsoft.Extensions.ApiDescription.Tasks
namespace Microsoft.Extensions.ApiDescription.Client
{
internal static class CSharpIdentifier
{

View File

@ -4,7 +4,7 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.Extensions.ApiDescription.Tasks
namespace Microsoft.Extensions.ApiDescription.Client
{
/// <summary>
/// Restore <see cref="ITaskItem"/>s from given property value.

View File

@ -7,7 +7,7 @@ using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.Extensions.ApiDescription.Tasks
namespace Microsoft.Extensions.ApiDescription.Client
{
/// <summary>
/// Adds or corrects ClassName, FirstForGenerator, Namespace, and OutputPath metadata in OpenApiReference items.

View File

@ -6,7 +6,7 @@ using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.Extensions.ApiDescription.Tasks
namespace Microsoft.Extensions.ApiDescription.Client
{
/// <summary>
/// Utility methods to serialize and deserialize <see cref="ITaskItem"/> metadata.

View File

@ -6,21 +6,19 @@
<!-- 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>
<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>
<IsShippingPackage>true</IsShippingPackage>
<PackageVersion>$(ExperimentalPackageVersion)</PackageVersion>
<RootNamespace>$(AssemblyName)</RootNamespace>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<VerifyVersion>false</VerifyVersion>
<VersionPrefix>$(ExperimentalVersionPrefix)</VersionPrefix>
<VersionSuffix>$(ExperimentalVersionSuffix)</VersionSuffix>
<HasReferenceAssembly>false</HasReferenceAssembly>
</PropertyGroup>

View File

@ -19,7 +19,7 @@
<files>
<file src="build\*" target="build" />
<file src="buildMultiTargeting\*" target="buildMultiTargeting" />
<file src="$baseOutputPath$\$configuration$\net461\Microsoft.Extensions.ApiDescription.Tasks.*" target="tasks\net461" />
<file src="$baseOutputPath$\$configuration$\netstandard2.0\Microsoft.Extensions.ApiDescription.Tasks.*" target="tasks\netstandard2.0" />
<file src="$baseOutputPath$\$configuration$\net461\Microsoft.Extensions.ApiDescription.Client.*" target="tasks\net461" />
<file src="$baseOutputPath$\$configuration$\netstandard2.0\Microsoft.Extensions.ApiDescription.Client.*" target="tasks\netstandard2.0" />
</files>
</package>

View File

@ -1,5 +1,5 @@
// <auto-generated />
namespace Microsoft.Extensions.ApiDescription.Tasks
namespace Microsoft.Extensions.ApiDescription.Client
{
using System.Globalization;
using System.Reflection;
@ -8,7 +8,7 @@ namespace Microsoft.Extensions.ApiDescription.Tasks
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.Extensions.ApiDescription.Tasks.Resources", typeof(Resources).GetTypeInfo().Assembly);
= new ResourceManager("Microsoft.Extensions.ApiDescription.Client.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// Multiple items have OutputPath='{0}'. All OpenApiReference items must have unique OutputPath metadata.

View File

@ -1,35 +1,60 @@
<?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 />
<_ApiDescriptionClientAssemblyTarget
Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0</_ApiDescriptionClientAssemblyTarget>
<_ApiDescriptionClientAssemblyTarget
Condition="'$(MSBuildRuntimeType)' != 'Core'">net461</_ApiDescriptionClientAssemblyTarget>
<_ApiDescriptionClientAssemblyPath>$(MSBuildThisFileDirectory)/../tasks/$(_ApiDescriptionClientAssemblyTarget)/Microsoft.Extensions.ApiDescription.Client.dll</_ApiDescriptionClientAssemblyPath>
<_ApiDescriptionClientAssemblyTarget />
</PropertyGroup>
<UsingTask TaskName="GetCurrentItems" AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
<UsingTask TaskName="GetFileReferenceMetadata" AssemblyFile="$(_ApiDescriptionTasksAssemblyPath)" />
<UsingTask TaskName="GetCurrentItems" AssemblyFile="$(_ApiDescriptionClientAssemblyPath)" />
<UsingTask TaskName="GetFileReferenceMetadata" AssemblyFile="$(_ApiDescriptionClientAssemblyPath)" />
<!--
Settings users may update as they see fit.
-->
<PropertyGroup>
<OpenApiDefaultGeneratorOptions Condition="'$(OpenApiDefaultGeneratorOptions)' == ''"></OpenApiDefaultGeneratorOptions>
<!--
Options added to the code generator command line by default. Provides the default %(Options) metadata of
@(OpenApiReference) and @(OpenApiProjectReference) items.
-->
<OpenApiGenerateCodeOptions Condition="'$(OpenApiGenerateCodeOptions)' == ''"></OpenApiGenerateCodeOptions>
<!--
If 'true', will generate code for OpenApiReference items during design-time builds. Otherwise, generate code only
for output files that do not yet exist during design-time builds.
If 'true' (the default), generate code for @(OpenApiReference) and @(OpenApiProjectReference) items before the
BeforeCompile target.
If 'false', the 'GenerateOpenApiCode' target is not part of the build (by default) but can run when explicitly
referenced. That is, the target may be invoked from the command line or tied in through another target.
-->
<OpenApiGenerateAtDesignTime Condition="'$(OpenApiGenerateAtDesignTime)' == ''">true</OpenApiGenerateAtDesignTime>
<OpenApiGenerateCodeOnBuild Condition="'$(OpenApiGenerateCodeOnBuild)' == ''">true</OpenApiGenerateCodeOnBuild>
<!--
$(OpenApiDefaultOutputDirectory) value is interpreted relative to the project folder, unless already an
absolute path.
If 'true' (the default), generate code for @(OpenApiReference) and @(OpenApiProjectReference) items during
design-time builds. Otherwise, generate code only during a full build.
-->
<OpenApiDefaultOutputDirectory
Condition="'$(OpenApiDefaultOutputDirectory)' == ''">$(BaseIntermediateOutputPath)</OpenApiDefaultOutputDirectory>
<OpenApiDefaultOutputDirectory>$([MSBuild]::EnsureTrailingSlash('$(OpenApiDefaultOutputDirectory)'))</OpenApiDefaultOutputDirectory>
<OpenApiGenerateCodeAtDesignTime
Condition="'$(OpenApiGenerateCodeAtDesignTime)' == ''">true</OpenApiGenerateCodeAtDesignTime>
<!--
If 'true' (the default), build projects referenced in @(OpenApiProjectReference) items before retrieving that
project's Open API documents list (or generating code). Recommend setting this to 'false' when the referenced
projects do not share target framework values (avoiding build errors and / or warnings).
If 'false', ensure the referenced projects build before this one in the solution or through other means. IDEs may
be confused about the project dependency graph in this case.
-->
<OpenApiBuildReferencedProjects
Condition="'$(OpenApiBuildReferencedProjects)' == ''">true</OpenApiBuildReferencedProjects>
<!--
Default folder to place code generated from Open API documents. Value is interpreted relative to the project
folder, unless already an absolute path. Part of the default %(OutputPath) metadata of @(OpenApiReference) and
@(OpenApiProjectReference) items.
-->
<OpenApiCodeDirectory
Condition="'$(OpenApiCodeDirectory)' == ''">$(BaseIntermediateOutputPath)</OpenApiCodeDirectory>
</PropertyGroup>
<!--
@ -42,24 +67,28 @@
<OpenApiReference>
<!-- Name of the class to generate. Defaults to match filename in %(OutputPath). -->
<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>NSwagCSharp</CodeGenerator>
<!-- Namespace to contain generated class. Default is $(RootNamespace). -->
<Namespace />
<!--
Options to pass to the code generator target then (likely) added to a tool's command line. Value is passed
along to the code generator but otherwise unused in this package.
-->
<Options>$(OpenApiDefaultGeneratorOptions)</Options>
<Options>$(OpenApiGenerateCodeOptions)</Options>
<!--
Path to place generated code. Code generator may interpret path as a filename or directory. Default filename or
folder name is %(Filename)Client.[cs|ts]. Filenames and relative paths (if explicitly set) are combined with
$(OpenApiDefaultOutputDirectory). Final value (depending on $(OpenApiDefaultOutputDirectory)) is likely to be
a path relative to the client project.
$(OpenApiCodeDirectory). Final value (depending on $(OpenApiCodeDirectory)) is likely to be a path relative to
the client project.
-->
<OutputPath />
</OpenApiReference>

View File

@ -2,21 +2,18 @@
<Project>
<!-- Internal settings. Not intended for customization. -->
<PropertyGroup>
<GenerateOpenApiReferenceCodeDependsOn>
_GetMetadataForOpenApiReferences;
_GenerateOpenApiReferenceCode;
_CreateCompileItemsForOpenApiReferences
</GenerateOpenApiReferenceCodeDependsOn>
<GenerateServiceFileReferenceCodeDependsOn>
<GenerateOpenApiCodeDependsOn>
_GenerateErrorsForOldItems;
_CreateOpenApiReferenceItemsForOpenApiProjectReferences;
GenerateOpenApiReferenceCode
</GenerateServiceFileReferenceCodeDependsOn>
_GetMetadataForOpenApiReferences;
_GenerateOpenApiCode;
_CreateCompileItemsForOpenApiReferences
</GenerateOpenApiCodeDependsOn>
</PropertyGroup>
<!-- OpenApiProjectReference support. -->
<ItemGroup>
<ItemGroup Condition=" '$(OpenApiBuildReferencedProjects)' == 'true' ">
<!--
Do not change %(ReferenceOutputAssembly) if project contains duplicate @(ProjectReference) and
@(OpenApiProjectReference) items.
@ -30,19 +27,16 @@
</ProjectReference>
</ItemGroup>
<Target Name="_CreateOpenApiReferenceItemsForOpenApiProjectReferences"
AfterTargets="ResolveProjectReferences"
Condition="'$(DesignTimeBuild)' != 'true' AND '$(BuildingProject)' == 'true'">
<Target Name="_CreateOpenApiReferenceItemsForOpenApiProjectReferences" DependsOnTargets="ResolveProjectReferences">
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<MSBuild Targets="OpenApiGetDocuments"
Projects="@(ProjectReferenceWithConfiguration)"
Condition="'%(ProjectReferenceWithConfiguration.OpenApiReference)' == 'true'"
Properties="%(ProjectReferenceWithConfiguration.SetConfiguration); %(ProjectReferenceWithConfiguration.SetPlatform); %(ProjectReferenceWithConfiguration.SetTargetFramework)"
BuildInParallel="$(BuildInParallel)"
Projects="@(OpenApiProjectReference)"
RebaseOutputs="true"
RemoveProperties="%(ProjectReferenceWithConfiguration.GlobalPropertiesToRemove);TargetFrameworks;RuntimeIdentifier">
RemoveProperties="Configuration;Platform;RuntimeIdentifier;TargetFramework;TargetFrameworks">
<Output TaskParameter="TargetOutputs" ItemName="_Temporary" />
</MSBuild>
@ -62,7 +56,7 @@
<GetFileReferenceMetadata Inputs="@(OpenApiReference)"
Extension="$(DefaultLanguageSourceExtension)"
Namespace="$(RootNamespace)"
OutputDirectory="$(OpenApiDefaultOutputDirectory)">
OutputDirectory="$(OpenApiCodeDirectory)">
<Output TaskParameter="Outputs" ItemName="_Temporary" />
</GetFileReferenceMetadata>
@ -79,17 +73,16 @@
</GetCurrentItems>
</Target>
<Target Name="_InnerGenerateOpenApiReferenceCode" DependsOnTargets="_GetCurrentOpenApiReference;$(GeneratorTarget)" />
<Target Name="_InnerGenerateOpenApiCode" DependsOnTargets="_GetCurrentOpenApiReference;$(GeneratorTarget)" />
<Target Name="_GenerateOpenApiReferenceCode"
Condition="$(OpenApiGenerateAtDesignTime) OR ('$(DesignTimeBuild)' != 'true' AND '$(BuildingProject)' == 'true')"
<Target Name="_GenerateOpenApiCode"
Condition="$(OpenApiGenerateCodeAtDesignTime) OR ('$(DesignTimeBuild)' != 'true' AND '$(BuildingProject)' == 'true')"
Inputs="@(OpenApiReference)"
Outputs="%(OutputPath)">
<MSBuild BuildInParallel="$(BuildInParallel)"
Projects="$(MSBuildProjectFullPath)"
<MSBuild Projects="$(MSBuildProjectFullPath)"
BuildInParallel="$(BuildInParallel)"
Properties="GeneratorTargetPath=%(OpenApiReference.OutputPath);GeneratorTarget=Generate%(CodeGenerator);GeneratorMetadata=%(SerializedMetadata)"
RemoveProperties="TargetFrameworks"
Targets="_InnerGenerateOpenApiReferenceCode" />
Targets="_InnerGenerateOpenApiCode" />
</Target>
<Target Name="_CreateCompileItemsForOpenApiReferences" Condition="'@(OpenApiReference)' != ''">
@ -133,9 +126,7 @@
</ItemGroup>
</Target>
<Target Name="GenerateOpenApiReferenceCode" DependsOnTargets="$(GenerateOpenApiReferenceCodeDependsOn)" />
<!-- Tie above code generation steps into the build. -->
<!-- Inform users of breaking changes in this file and Microsoft.Extensions.ApiDescription.Client.props. -->
<Target Name="_GenerateErrorsForOldItems">
<Error Condition="'@(ServiceProjectReference)' != ''" Text="ServiceProjectReference items are no longer supported." />
@ -143,7 +134,18 @@
<Error Condition="'@(ServiceFileReference)' != ''" Text="ServiceFileReference items are no longer supported." />
</Target>
<Target Name="GenerateServiceFileReferenceCode"
<!-- Main code generation entry point. -->
<Target Name="GenerateOpenApiCode" DependsOnTargets="$(GenerateOpenApiCodeDependsOn)" />
<!--
Unless this is an inner build or default timing is disabled, tie code generation into the build. Separate from
GenerateOpenApiCode and not invoked from the ../buildMultiTargeting targets to prevent repeated code generation
when multi-targeting.
-->
<Target Name="_TieInGenerateOpenApiCode"
BeforeTargets="BeforeCompile"
DependsOnTargets="$(GenerateServiceFileReferenceCodeDependsOn)" />
Condition=" '$(OpenApiGenerateCodeOnBuild)' == 'true' AND ('$(TargetFramework)' == '' OR '$(TargetFrameworks)' == '') "
DependsOnTargets="GenerateOpenApiCode" />
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<Import Project="../build/Microsoft.Extensions.ApiDescription.Client.props" />
</Project>

View File

@ -1,9 +1,14 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<Target Name="GenerateServiceFileReferenceCode" BeforeTargets="BeforeCompile">
<MsBuild Projects="$(MSBuildProjectFile)"
Targets="GenerateServiceFileReferenceCode"
<Target Name="GenerateOpenApiCode">
<MSBuild Projects="$(MSBuildProjectFile)"
Targets="GenerateOpenApiCode"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0])"
RemoveProperties="TargetFrameworks;RuntimeIdentifier" />
RemoveProperties="RuntimeIdentifier" />
</Target>
<Target Name="_GenerateOpenApiCode"
BeforeTargets="BeforeCompile"
Condition=" ''$(OpenApiGenerateCodeOnBuild)' == 'true' "
DependsOnTargets="GenerateOpenApiCode" />
</Project>