summaryrefslogtreecommitdiff
path: root/Tools/ApiCompat.targets
blob: d6cd696e0d3ad5c425920f4e344d0ccd42b809ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="PrereleaseResolveNuGetPackageAssets" AssemblyFile="$(BuildToolsTaskDir)Microsoft.DotNet.Build.Tasks.dll"/>
  <UsingTask TaskName="LocatePreviousContract" AssemblyFile="$(BuildToolsTaskDir)Microsoft.DotNet.Build.Tasks.dll"/>

  <PropertyGroup>
    <RunApiCompat Condition="'$(RunApiCompat)'==''">false</RunApiCompat>
  </PropertyGroup>

  <PropertyGroup Condition="'$(RunApiCompat)' == 'true'">
    <ApiCompatBaseline Condition="!Exists('$(ApiCompatBaseline)')">$(MSBuildProjectDirectory)\ApiCompatBaseline.$(TargetGroup).txt</ApiCompatBaseline>
    <ApiCompatBaseline Condition="!Exists('$(ApiCompatBaseline)')">$(MSBuildProjectDirectory)\ApiCompatBaseline.txt</ApiCompatBaseline>

    <MatchingRefApiCompatBaseline Condition="!Exists('$(MatchingRefApiCompatBaseline)')">$(MSBuildProjectDirectory)\MatchingRefApiCompatBaseline.$(TargetGroup).txt</MatchingRefApiCompatBaseline>
    <MatchingRefApiCompatBaseline Condition="'$(BaselineAllMatchingRefApiCompatError)' != 'true' and !Exists('$(MatchingRefApiCompatBaseline)')">$(MSBuildProjectDirectory)\MatchingRefApiCompatBaseline.txt</MatchingRefApiCompatBaseline>

    <RunApiCompatForSrc Condition="$(MSBuildProjectDirectory.EndsWith('src'))">true</RunApiCompatForSrc>

    <RunMatchingRefApiCompat Condition="'$(RunMatchingRefApiCompat)' == ''">$(RunApiCompatForSrc)</RunMatchingRefApiCompat>

    <ResolveMatchingContract Condition="'$(RunApiCompatForSrc)'=='true'">true</ResolveMatchingContract>
    <TargetsTriggeredByCompilation Condition="'$(RunApiCompatForSrc)'=='true'">$(TargetsTriggeredByCompilation);ValidateApiCompatForSrc</TargetsTriggeredByCompilation>
    <TargetsTriggeredByCompilation Condition="'$(RunMatchingRefApiCompat)'=='true'">$(TargetsTriggeredByCompilation);RunMatchingRefApiCompat</TargetsTriggeredByCompilation>
  </PropertyGroup>

  <!-- ApiCompat for Implementation Assemblies  -->
  <Target Name="ValidateApiCompatForSrc"
          Condition="'$(RunApiCompatForSrc)' == 'true' AND '$(RunApiCompat)' == 'true' and '@(ResolvedMatchingContract)' != ''">

    <PropertyGroup>
      <ReferenceAssembly>@(ResolvedMatchingContract)</ReferenceAssembly>
    </PropertyGroup>

    <ItemGroup>
      <_DependencyDirectoriesTemp Include="@(ReferencePath->'%(RootDir)%(Directory)')" />
      <!-- Remove duplicate directories by batching over them -->
      <!-- Add project references first to give precedence to project-specific files -->
      <_DependencyDirectories Condition="'%(_DependencyDirectoriesTemp.ReferenceSourceTarget)'=='ProjectReference'" Include="%(_DependencyDirectoriesTemp.Identity)" />
      <_DependencyDirectories Condition="'%(_DependencyDirectoriesTemp.ReferenceSourceTarget)'!='ProjectReference'" Include="%(_DependencyDirectoriesTemp.Identity)" />
      <_ContractDependencyDirectories Include="@(ResolvedMatchingContract->'%(RootDir)%(Directory)')" />
      <_ContractDependencyDirectories Include="$(ContractOutputPath)" />
    </ItemGroup>

    <PropertyGroup>
      <ApiCompatArgs>$(ApiCompatArgs) "$(ReferenceAssembly)"</ApiCompatArgs>
      <ApiCompatArgs>$(ApiCompatArgs) -contractDepends:"@(_ContractDependencyDirectories, ','),"</ApiCompatArgs>
      <ApiCompatArgs>$(ApiCompatArgs) -implDirs:"$(IntermediateOutputPath),@(_DependencyDirectories, ','),"</ApiCompatArgs>
      <ApiCompatArgs Condition="'$(ApiCompatExcludeAttributeList)' != ''">$(ApiCompatArgs) -excludeAttributes:"$(ApiCompatExcludeAttributeList)"</ApiCompatArgs>
      <ApiCompatArgs Condition="'$(BaselineAllAPICompatError)'!='true' and Exists('$(ApiCompatBaseline)')">$(ApiCompatArgs) -baseline:"$(ApiCompatBaseline)"</ApiCompatArgs>
      <ApiCompatBaselineAll Condition="'$(BaselineAllAPICompatError)'=='true'">&gt; $(ApiCompatBaseline)</ApiCompatBaselineAll>
      <ApiCompatExitCode>0</ApiCompatExitCode>

      <ApiCompatResponseFile>$(IntermediateOutputPath)apicompat.rsp</ApiCompatResponseFile>
      <ApiCompatCmd>$(ToolHostCmd) "$(ToolsDir)ApiCompat.exe"</ApiCompatCmd>
    </PropertyGroup>

    <MakeDir Directories="$(IntermediateOutputPath)" />
    <WriteLinesToFile File="$(ApiCompatResponseFile)" Lines="$(ApiCompatArgs)" Overwrite="true" />

    <Exec Condition="Exists('$(ReferenceAssembly)')"
          Command="$(ApiCompatCmd) @&quot;$(ApiCompatResponseFile)&quot; $(ApiCompatBaselineAll)"
          CustomErrorRegularExpression="^[a-zA-Z]+ :"
          StandardOutputImportance="Low"
          IgnoreExitCode="true"
    >
      <Output TaskParameter="ExitCode" PropertyName="ApiCompatExitCode" />
    </Exec>

    <!--
      To force incremental builds to show failures again we are invalidating
       one compile input by touching the assembly info file
    -->
    <Touch Condition="'$(ApiCompatExitCode)'!='0'" Files="$(AssemblyInfoFile)" />
    <Error Condition="'$(ApiCompatExitCode)'!='0'" Text="ApiCompat failed for '$(TargetPath)'" />
  </Target>

  <!-- Reverse APICompat to verify that the reference assembly has all the APIs that are in the implementation -->
  <Target Name="RunMatchingRefApiCompat"
          Condition="'$(RunMatchingRefApiCompat)' == 'true' AND '$(RunApiCompat)' == 'true' AND '@(ReferenceFromRuntime)' == ''" >

    <PropertyGroup>
      <ImplemetnationAssemblyAsContract>@(IntermediateAssembly)</ImplemetnationAssemblyAsContract>
    </PropertyGroup>

    <ItemGroup>
      <_ContractDependencyDirectoriesTemp Include="@(ReferencePath->'%(RootDir)%(Directory)')" />
      <!-- Remove duplicate directories by batching over them -->
      <!-- Add project references first to give precedence to project-specific files -->
      <_ContractDependencyDirectories Condition="'%(_ContractDependencyDirectoriesTemp.ReferenceSourceTarget)'=='ProjectReference'" Include="%(_ContractDependencyDirectoriesTemp.Identity)" />
      <_ContractDependencyDirectories Condition="'%(_ContractDependencyDirectoriesTemp.ReferenceSourceTarget)'!='ProjectReference'" Include="%(_ContractDependencyDirectoriesTemp.Identity)" />
      <_ImplementationDependencyDirectories Include="@(ResolvedMatchingContract->'%(RootDir)%(Directory)')" />
      <_ImplementationDependencyDirectories Include="$(ContractOutputPath)" />
    </ItemGroup>

    <PropertyGroup>
      <MatchingRefApiCompatArgs>$(MatchingRefApiCompatArgs) "$(ImplemetnationAssemblyAsContract)"</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatArgs>$(MatchingRefApiCompatArgs) -contractDepends:"@(_ContractDependencyDirectories, ','),"</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatArgs>$(MatchingRefApiCompatArgs) -lhs:implementation</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatArgs>$(MatchingRefApiCompatArgs) -implDirs:"@(_ImplementationDependencyDirectories, ','),"</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatArgs>$(MatchingRefApiCompatArgs) -rhs:reference</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatArgs Condition="'$(ApiCompatExcludeAttributeList)' != ''">$(MatchingRefApiCompatArgs) -excludeAttributes:"$(ApiCompatExcludeAttributeList)"</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatArgs Condition="'$(BaselineAllMatchingRefApiCompatError)'!='true' and Exists('$(MatchingRefApiCompatBaseline)')">$(MatchingRefApiCompatArgs) -baseline:"$(MatchingRefApiCompatBaseline)"</MatchingRefApiCompatArgs>
      <MatchingRefApiCompatBaselineAll Condition="'$(BaselineAllMatchingRefApiCompatError)'=='true'">&gt; $(MatchingRefApiCompatBaseline)</MatchingRefApiCompatBaselineAll>

      <MatchingRefApiCompatExitCode>0</MatchingRefApiCompatExitCode>

      <MatchingRefApiCompatResponseFile>$(IntermediateOutputPath)MatchingRefApiCompat_verifyexactref.rsp</MatchingRefApiCompatResponseFile>
      <MatchingRefApiCompatCmd>$(ToolHostCmd) "$(ToolsDir)ApiCompat.exe"</MatchingRefApiCompatCmd>
    </PropertyGroup>

    <MakeDir Directories="$(IntermediateOutputPath)" />
    <WriteLinesToFile File="$(MatchingRefApiCompatResponseFile)" Lines="$(MatchingRefApiCompatArgs)" Overwrite="true" />

    <Exec Condition="Exists('$(ReferenceAssembly)')"
          Command="$(MatchingRefApiCompatCmd) @&quot;$(MatchingRefApiCompatResponseFile)&quot; $(MatchingRefApiCompatBaselineAll)"
          CustomErrorRegularExpression="^[a-zA-Z]+ :"
          StandardOutputImportance="Low"
          IgnoreExitCode="true"
    >
      <Output TaskParameter="ExitCode" PropertyName="MatchingRefApiCompatExitCode" />
    </Exec>

    <!--
      To force incremental builds to show failures again we are invalidating
       one compile input by touching the assembly info file
    -->
    <Touch Condition="'$(MatchingRefApiCompatExitCode)'!='0'" Files="$(AssemblyInfoFile)" />
    <Error Condition="'$(MatchingRefApiCompatExitCode)'!='0'" Text="MatchingRefApiCompat failed - The reference assembly doesn't match all the APIs in the implementation for '$(TargetPath)'. To address either fix errors in the reference assembly (referenced as implementation in compat errors for this reverse compat check), add the issues to the baseline file '$(MatchingRefApiCompatBaseline)' or disable this check by setting RunMatchingRefApiCompat=false in this project." />
  </Target>
</Project>