Skip to content

Commit 9ba4ad9

Browse files
benrr101mdaigle
andauthored
Merge | Ref Projects (dotnet#3963)
* Add a common ref project, reorganize projects in solution a bit. * Add more junk to the common ref project file * Moving netcoreapp files back into netcore ref project. * Microsoft.Data namespace * Microsoft.Data.Sql * Microsoft.Data.SqlTypes * Add netfx package references * Batch 1 up to SqlCommand * SqlCommand * Up through SqlConnection * Up through SqlDataAdapter * Microsoft.Data.SqlClient * Microsoft.Data.SqlClient.Diagnostic * Microsoft.Data.SqlClient.Server * MDS.Batch files and MDS.DataClassification * Microsoft.Data.SqlClient.Manual ... whatever that means * Fix mistakes in xml file paths, etc. * Pointing separate ref projects at common files. * Adding reference to abstractions to common ref project * Comments from PR * Add ref assembly comparison target. * Clean up target. * Rename to make it clear this is specific to MDS. --------- Co-authored-by: Malcolm Daigle <mdaigle@microsoft.com>
1 parent 0208d81 commit 9ba4ad9

20 files changed

+3665
-5712
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# ApiCompat Ref Assembly Validation Target
2+
3+
## Goal
4+
5+
Add a build target that compares locally-built ref assemblies against those published in a specified NuGet package version of Microsoft.Data.SqlClient. This detects any unintended API surface changes introduced during the ref project consolidation (PR #3963).
6+
7+
The comparison uses `Microsoft.DotNet.ApiCompat.Tool` in **strict mode**, which flags both additions and removals.
8+
9+
## Usage
10+
11+
```
12+
dotnet msbuild build.proj /t:CompareRefAssemblies /p:BaselinePackageVersion=6.1.4
13+
```
14+
15+
- `BaselinePackageVersion` is **required** (no default). The user must specify which published package to compare against.
16+
- Both the **legacy** ref projects (`netcore/ref/`, `netfx/ref/`) and the **new consolidated** ref project (`ref/`) are built and compared independently, so you can distinguish whether a difference comes from source consolidation vs. project restructuring.
17+
- All comparisons run to completion (errors don't short-circuit), so you see all differences at once.
18+
19+
## Implementation Steps
20+
21+
### 1. Register `Microsoft.DotNet.ApiCompat.Tool` in `dotnet-tools.json`
22+
23+
Add an entry for version `9.0.200` alongside the existing `dotnet-coverage` entry. This enables `dotnet apicompat` after `dotnet tool restore`.
24+
25+
### 2. Create `tools/targets/CompareRefAssemblies.targets`
26+
27+
A single new file containing all properties, items, and targets (steps 3–11 below). Follows the naming convention of existing files like `GenerateMdsPackage.targets`.
28+
29+
### 3. Define properties
30+
31+
| Property | Value | Notes |
32+
|----------|-------|-------|
33+
| `BaselinePackageVersion` | *(none)* | Required; validated by guard target |
34+
| `BaselinePackageDir` | `$(Artifacts)apicompat\` | Working directory for downloads |
35+
| `BaselineNupkgPath` | `$(BaselinePackageDir)microsoft.data.sqlclient.$(BaselinePackageVersion).nupkg` | Downloaded nupkg path |
36+
| `BaselinePackageUrl` | `https://api.nuget.org/v3-flatcontainer/microsoft.data.sqlclient/$(BaselinePackageVersion)/microsoft.data.sqlclient.$(BaselinePackageVersion).nupkg` | NuGet flat container URL |
37+
| `BaselineExtractDir` | `$(BaselinePackageDir)extracted\$(BaselinePackageVersion)\` | Extraction destination |
38+
| `NewRefProjectPath` | `$(ManagedSourceCode)ref\Microsoft.Data.SqlClient.csproj` | New consolidated ref project |
39+
| `NewRefOutputDir` | `$(ManagedSourceCode)ref\bin\$(Configuration)\` | SDK default output for new ref project |
40+
| `LegacyNetFxRefDir` | `$(Artifacts)Project\bin\Windows_NT\$(Configuration)\Microsoft.Data.SqlClient\ref\` | Legacy netfx ref output |
41+
| `LegacyNetCoreRefDir` | `$(Artifacts)Project\bin\AnyOS\$(Configuration)\Microsoft.Data.SqlClient\ref\` | Legacy netcore ref output |
42+
43+
### 4. `_ValidateBaselineVersion` target
44+
45+
Emits `<Error>` if `$(BaselinePackageVersion)` is empty, with a message instructing the user to pass `/p:BaselinePackageVersion=X.Y.Z`.
46+
47+
### 5. `_DownloadBaselinePackage` target
48+
49+
- Depends on `_ValidateBaselineVersion`
50+
- Uses `<DownloadFile>` to fetch the nupkg from nuget.org
51+
- Uses `<Unzip>` to extract it into `$(BaselineExtractDir)`
52+
- Conditioned to skip if `$(BaselineExtractDir)` already exists
53+
54+
### 6. `_RestoreApiCompatTool` target
55+
56+
Runs `<Exec Command="dotnet tool restore" WorkingDirectory="$(RepoRoot)" />`.
57+
58+
### 7. `_BuildLegacyRefNetFx` target
59+
60+
- Depends on `RestoreNetFx`
61+
- Builds `$(NetFxSource)ref\Microsoft.Data.SqlClient.csproj` for `net462`
62+
- Conditioned on Windows (`$(IsEnabledWindows)`)
63+
- Output: `$(LegacyNetFxRefDir)net462\Microsoft.Data.SqlClient.dll`
64+
65+
### 8. `_BuildLegacyRefNetCore` target
66+
67+
- Depends on `RestoreNetCore`
68+
- Builds `$(NetCoreSource)ref\Microsoft.Data.SqlClient.csproj` with `/p:OSGroup=AnyOS`
69+
- Output: `$(LegacyNetCoreRefDir){net8.0,net9.0,netstandard2.0}\Microsoft.Data.SqlClient.dll`
70+
71+
### 9. `_BuildNewRefProject` target
72+
73+
- Runs `dotnet build` on `$(NewRefProjectPath)`
74+
- Builds all 4 TFMs (`net462`, `net8.0`, `net9.0`, `netstandard2.0`) in one shot
75+
- Output: `$(NewRefOutputDir){tfm}\Microsoft.Data.SqlClient.dll`
76+
77+
### 10. `_RunRefApiCompat` target
78+
79+
- Depends on `_DownloadBaselinePackage;_RestoreApiCompatTool;_BuildLegacyRefNetFx;_BuildLegacyRefNetCore;_BuildNewRefProject`
80+
- Iterates over 4 TFMs (`net462`, `net8.0`, `net9.0`, `netstandard2.0`) using item batching
81+
- For each TFM, runs two `<Exec>` calls with `ContinueOnError="ErrorAndContinue"`:
82+
- **Legacy vs baseline**: `dotnet apicompat -l {baseline-ref-dll} -r {legacy-ref-dll} --strict-mode`
83+
- **New vs baseline**: `dotnet apicompat -l {baseline-ref-dll} -r {new-ref-dll} --strict-mode`
84+
- Each `<Exec>` is conditioned on `Exists(...)` for both DLLs (gracefully skips missing TFMs)
85+
- Preceded by `<Message Importance="high">` labelling each comparison
86+
- Uses item metadata to map `net462` to `$(LegacyNetFxRefDir)` and others to `$(LegacyNetCoreRefDir)`
87+
88+
### 11. `CompareRefAssemblies` target (public entry point)
89+
90+
- Declared with `DependsOnTargets="_RunRefApiCompat"`
91+
- Emits a final `<Message>` summarizing completion
92+
93+
### 12. Import into `build.proj`
94+
95+
Add one line after the existing `.targets` imports (after line 7):
96+
97+
```xml
98+
<Import Project="$(ToolsDir)targets\CompareRefAssemblies.targets" />
99+
```
100+
101+
## Design Decisions
102+
103+
- **Single new file** at `tools/targets/CompareRefAssemblies.targets` — only one `<Import>` line added to `build.proj`.
104+
- **Internal targets prefixed with `_`** to signal they're not intended to be called directly.
105+
- **Strict mode** ensures API additions are also flagged — important for detecting accidental public surface changes during file reorganization.
106+
- **`ContinueOnError="ErrorAndContinue"`** on each apicompat `Exec` so all 8 comparisons run and all differences are reported together.
107+
- **`_BuildLegacyRefNetFx`** is conditioned on Windows (net462 can't build on Unix), matching the existing `BuildNetFx` pattern.
108+
- **No default baseline version** — the user must always explicitly specify which published package to compare against.
109+
- **Download caching** — re-runs skip the download if the extracted directory already exists.
110+
111+
## Verification
112+
113+
```
114+
dotnet msbuild build.proj /t:CompareRefAssemblies /p:BaselinePackageVersion=6.1.4
115+
```
116+
117+
- Downloads 6.1.4 nupkg, builds both ref project variants, runs 8 comparisons (4 TFMs × 2 variants).
118+
- If the ref consolidation introduced no API changes, all comparisons pass (exit code 0).
119+
- If differences are found, apicompat reports them (e.g., `CP0001: Member 'X' exists on left but not on right`).
120+
- To generate a suppression file for known/intended differences, run `dotnet apicompat ... --generate-suppression-file` manually.

build.proj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<Import Project="$(ToolsDir)targets\GenerateSqlServerPackage.targets" />
66
<Import Project="$(ToolsDir)targets\GenerateMdsPackage.targets" />
77
<Import Project="$(ToolsDir)targets\add-ons\GenerateAkvPackage.targets" />
8+
<Import Project="$(ToolsDir)targets\CompareMdsRefAssemblies.targets" />
89

910
<PropertyGroup>
1011
<!-- SourceLink variable-->

dotnet-tools.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
"dotnet-coverage"
99
],
1010
"rollForward": false
11+
},
12+
"microsoft.dotnet.apicompat.tool": {
13+
"version": "10.0.103",
14+
"commands": [
15+
"apicompat"
16+
],
17+
"rollForward": false
1118
}
1219
}
1320
}

src/Microsoft.Data.SqlClient.sln

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.SqlServer.Server"
212212
EndProject
213213
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestClr", "TestClr", "{CDE508A5-F5D0-4A59-A4EF-978833830727}"
214214
EndProject
215-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\src\Microsoft.Data.SqlClient.csproj", "{9A8996A8-6484-4AA7-B50F-F861430EDE2F}"
216-
EndProject
217215
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{4600328C-C134-499F-AAE2-964E8AD5472C}"
218216
ProjectSection(SolutionItems) = preProject
219217
..\build.proj = ..\build.proj
@@ -360,8 +358,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient",
360358
EndProject
361359
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7E0602AC-7F0A-362A-D734-0FDDFCC600B5}"
362360
EndProject
363-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{134A5E42-015B-3575-2B2B-722614F4C835}"
364-
EndProject
365361
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2B71F605-037E-5629-6E23-0FA3C297446D}"
366362
EndProject
367363
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{E9D12AEC-2F11-4871-89BB-343BF1CAEA4C}"
@@ -371,6 +367,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{E9D12A
371367
..\eng\pipelines\stages\stress-tests-ci-stage.yml = ..\eng\pipelines\stages\stress-tests-ci-stage.yml
372368
EndProjectSection
373369
EndProject
370+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{020A7E7B-04C9-4326-985F-045B42CC2200}"
371+
EndProject
372+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\ref\Microsoft.Data.SqlClient.csproj", "{D433ED2D-5E47-4A4B-B94A-EC71482715C7}"
373+
EndProject
374+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\src\Microsoft.Data.SqlClient.csproj", "{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}"
375+
EndProject
374376
Global
375377
GlobalSection(SolutionConfigurationPlatforms) = preSolution
376378
Debug|Any CPU = Debug|Any CPU
@@ -609,18 +611,6 @@ Global
609611
{A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x64.Build.0 = Release|Any CPU
610612
{A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x86.ActiveCfg = Release|Any CPU
611613
{A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x86.Build.0 = Release|Any CPU
612-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
613-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
614-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Debug|x64.ActiveCfg = Debug|Any CPU
615-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Debug|x64.Build.0 = Debug|Any CPU
616-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Debug|x86.ActiveCfg = Debug|Any CPU
617-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Debug|x86.Build.0 = Debug|Any CPU
618-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
619-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Release|Any CPU.Build.0 = Release|Any CPU
620-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Release|x64.ActiveCfg = Release|Any CPU
621-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Release|x64.Build.0 = Release|Any CPU
622-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Release|x86.ActiveCfg = Release|Any CPU
623-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F}.Release|x86.Build.0 = Release|Any CPU
624614
{4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
625615
{4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|Any CPU.Build.0 = Debug|Any CPU
626616
{4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|x64.ActiveCfg = Debug|x64
@@ -759,6 +749,30 @@ Global
759749
{4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x64.Build.0 = Release|Any CPU
760750
{4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x86.ActiveCfg = Release|Any CPU
761751
{4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x86.Build.0 = Release|Any CPU
752+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
753+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
754+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x64.ActiveCfg = Debug|Any CPU
755+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x64.Build.0 = Debug|Any CPU
756+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x86.ActiveCfg = Debug|Any CPU
757+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x86.Build.0 = Debug|Any CPU
758+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
759+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|Any CPU.Build.0 = Release|Any CPU
760+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x64.ActiveCfg = Release|Any CPU
761+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x64.Build.0 = Release|Any CPU
762+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x86.ActiveCfg = Release|Any CPU
763+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x86.Build.0 = Release|Any CPU
764+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
765+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|Any CPU.Build.0 = Debug|Any CPU
766+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x64.ActiveCfg = Debug|Any CPU
767+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x64.Build.0 = Debug|Any CPU
768+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x86.ActiveCfg = Debug|Any CPU
769+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x86.Build.0 = Debug|Any CPU
770+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|Any CPU.ActiveCfg = Release|Any CPU
771+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|Any CPU.Build.0 = Release|Any CPU
772+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x64.ActiveCfg = Release|Any CPU
773+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x64.Build.0 = Release|Any CPU
774+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x86.ActiveCfg = Release|Any CPU
775+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x86.Build.0 = Release|Any CPU
762776
EndGlobalSection
763777
GlobalSection(SolutionProperties) = preSolution
764778
HideSolutionNode = FALSE
@@ -796,7 +810,6 @@ Global
796810
{A314812A-7820-4565-A2A8-ABBE391C11E4} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45}
797811
{869A9BCC-D303-4365-9BF7-958CD6387916} = {71F356DC-DFA3-4163-8BFE-D268722CE189}
798812
{CDE508A5-F5D0-4A59-A4EF-978833830727} = {0CC4817A-12F3-4357-912C-09315FAAD008}
799-
{9A8996A8-6484-4AA7-B50F-F861430EDE2F} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45}
800813
{4CAE9195-4F1A-4D48-854C-1C9FBC512C66} = {4600328C-C134-499F-AAE2-964E8AD5472C}
801814
{FD4D7A96-79B1-4F89-B64D-29FACCC9232F} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66}
802815
{E76A4ED5-9137-4E4B-AE91-7AEDB2683823} = {FD4D7A96-79B1-4F89-B64D-29FACCC9232F}
@@ -827,9 +840,11 @@ Global
827840
{5AF52CDD-DF78-3712-7516-5B49F94F9491} = {A20114E1-82D8-903A-C389-726EB4FD943F}
828841
{4B953573-C3CD-4845-896B-EA0A0B7A7B27} = {5AF52CDD-DF78-3712-7516-5B49F94F9491}
829842
{7E0602AC-7F0A-362A-D734-0FDDFCC600B5} = {7289C27E-D7DF-2C71-84B4-151F3A162493}
830-
{134A5E42-015B-3575-2B2B-722614F4C835} = {7E0602AC-7F0A-362A-D734-0FDDFCC600B5}
831843
{2B71F605-037E-5629-6E23-0FA3C297446D} = {7289C27E-D7DF-2C71-84B4-151F3A162493}
832844
{E9D12AEC-2F11-4871-89BB-343BF1CAEA4C} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66}
845+
{020A7E7B-04C9-4326-985F-045B42CC2200} = {7289C27E-D7DF-2C71-84B4-151F3A162493}
846+
{D433ED2D-5E47-4A4B-B94A-EC71482715C7} = {020A7E7B-04C9-4326-985F-045B42CC2200}
847+
{AA77C107-9A78-4A99-98BB-21FF7A1E0B01} = {2B71F605-037E-5629-6E23-0FA3C297446D}
833848
EndGlobalSection
834849
GlobalSection(ExtensibilityGlobals) = postSolution
835850
SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431}

src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.Manual.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)