-
Notifications
You must be signed in to change notification settings - Fork 317
Merge Project | Build the Common Project #3837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Remove runtime identifier/target framework from output path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR establishes the foundation for building a unified "common" Microsoft.Data.SqlClient project that consolidates code from the platform-specific netfx and netcore projects. The changes enable conditional target framework selection based on the target OS (net462 only on Windows), introduce OS-aware artifact output paths, and provide new build targets via build2.proj. Key modifications include adding #if NET preprocessor directives to platform-specific files, refactoring UDT exception creation code, updating package references, and adjusting file references in the netcore project to point to renamed SqlDependency files with .netcore.cs extensions.
- Conditional target frameworks: net462 only included when building for Windows
- New artifact directory structure:
artifacts/(AssemblyName)/(Configuration)/(TargetOs)/(TargetFramework) - build2.proj provides BuildMds, BuildMdsUnix, and BuildMdsWindows targets for streamlined builds
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| SqlDependencyUtils.AssemblyLoadContext.netcore.cs | New file with #if NET wrapper for AssemblyLoadContext-based unloading support |
| SqlDependencyUtils.AppDomain.netcore.cs | Added #if NET wrapper to conditionally compile AppDomain unload logic |
| AdapterUtil.cs | Refactored UDT exception creation, improved formatting, moved TraceExceptionAsReturnValue call outside conditional blocks |
| Microsoft.Data.SqlClient.csproj | Added conditional target frameworks, new artifact paths, package reference reorganization for netfx vs netcore |
| netcore/src/Microsoft.Data.SqlClient.csproj | Updated file references from .cs to .netcore.cs for SqlDependencyUtils files |
| Microsoft.Data.SqlClient.sln | Added Build.0 configurations for common project to enable building in Visual Studio |
| build2.proj | New build orchestration file with targets for building common project on Unix and Windows |
build2.proj
Outdated
| <DotnetCommand> | ||
| $(DotnetPath)dotnet build $(MdsProjectPath) | ||
| -p:Configuration=$(Configuration) | ||
| -p:TargetOs=Unix |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BuildMdsWindows target is building for Unix instead of Windows. Line 47 should be -p:TargetOs=Windows_NT instead of -p:TargetOs=Unix.
| -p:TargetOs=Unix | |
| -p:TargetOs=Windows_NT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦♂️
| <!-- included into the build. --> | ||
| <EmbeddedResource Include="Resources/ILLink.Substitutions.xml" | ||
| Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT'" /> | ||
| Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT' AND '$(TargetFramework)' == 'net462'" /> |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition for including ILLink.Substitutions.xml appears incorrect. Based on the code in LocalAppContextSwitches.cs, the UseManagedNetworking switch is only relevant for .NET Core (NET) on Windows, not for .NET Framework (net462). The condition should check for .NET Core target frameworks on Windows (e.g., '$(TargetFramework)' != 'net462') instead of checking == 'net462'. The comment on line 46 correctly states this file "only applies to netcore on windows", but the condition contradicts this.
| Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT' AND '$(TargetFramework)' == 'net462'" /> | |
| Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT' AND '$(TargetFramework)' != 'net462'" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot is correct, although I'd go further: there's no need for conditional inclusion, this should always be included.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@edwardneal you'll probably have to explain that a bit more. The way I'm looking at it, this file replaces UseManagedNetworking in the LocalAppContextSwitches with a constant if the consumer of the library specifies UseManagedNetworkingOnWindows as true or false in their project's csproj. But it does no check for whether the project is actually on windows or not. So if we include the file in all builds, we'll end up with these scenarios:
- netcore on Windows - behaves as expected, if user specifies the switch it'll replace with the constant appropriately
- netfx on Windows - allows user to replace
UseManagedNetworkingconstantfalsewithtruewhich will break things - netcore on Unix - allows user to replace
UseManagedNetworkingconstanttruewithfalsewhich will break things
So as far as I can tell, we really do need to conditionally include the file to lead users into the "pit of success"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifying UseManagedNetworkingOnWindows in the csproj doesn't automatically rewrite the AppContext switch, or instruct the IL trimmer to trim away the unnecessary SNI. It just sets an MSBuild variable. In the future, once trimming is supportable, there'll be a custom .targets file within M.D.S which includes a <RuntimeHostConfigurationOption /> if that variable is set to a specific value - and it's RuntimeHostConfigurationOption which provides the instruction to the IL trimmer to perform the substitution you're describing.
With that in mind:
- netcore on Windows - completely valid.
- netfx on Windows - doesn't happen, netfx doesn't support RuntimeHostConfigurationOption or IL trimming.
- netcore on Unix - possible. It's the reason why I'd originally included an OS-specific ILLink.Substitutions.xml file.
At present, IL trimming isn't supported. At the point that it is, the custom .targets file can detect that a user is publishing to Unix with that configuration and raise a build error. We can also guard the check to use the managed SNI, such that a non-Windows OS will always reference the managed SNI irrespective of the value of the AppContext switch:
internal static class LocalAppContextSwitches
{
// After IL trimming - this would normally check the AppContext switch.
private bool UseManagedNetworkingOnWindows => true;
public bool UseManagedNetworking =>
!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || UseManagedNetworkingOnWindows;
}
internal static class TdsParserStateObjectFactory
{
internal TdsParserStateObject CreateSessionObject(...) =>
return LocalAppContextSwitches.UseManagedNetworking
? new TdsParserStateObjectManaged(...)
: new TdsParserStateObjectNative(...);
}
edwardneal
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No major comments, just a confirmation of Copilot and a quick naming nit.
The commentary about OS-specific handling comes at the same time as I've been looking at what kind of work would be needed to eliminate the need for it entirely. It looks like almost everything can be trivially lifted into runtime checks, with one or two benchmarks to be run for cases where Unix has a slightly different class structure. I'm hoping to start unwinding the need for this soon.
| #endif | ||
|
|
||
| #if NETFRAMEWORK | ||
| private static readonly MethodInfo s_udtConstructor = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this isn't really a constructor - it's a factory method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically true ....... I guess in my mind anything that creates the type it's defined on is a constructor. Probably a holdover from working on typescript for so long where constructors cannot be overloaded, so I write a bunch of static methods to do construction. Regardless, it's a better name that what it used to be.
| <!-- included into the build. --> | ||
| <EmbeddedResource Include="Resources/ILLink.Substitutions.xml" | ||
| Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT'" /> | ||
| Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT' AND '$(TargetFramework)' == 'net462'" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot is correct, although I'd go further: there's no need for conditional inclusion, this should always be included.
paulmedynski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking for some commentary on a few things, and some suggestions.
| </DotnetCommand> | ||
| <DotnetCommand>$([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s{2,}", " "))</DotnetCommand> | ||
| </PropertyGroup> | ||
| <Exec ConsoleToMsBuild="true" Command="$(DotNetCommand)" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we emit the command as a <Message> like build.proj sometimes does?
| <TargetFrameworks>net462;net8.0;net9.0</TargetFrameworks> | ||
| <!-- General Properties ============================================== --> | ||
| <PropertyGroup> | ||
| <Configurations>Debug;Release;</Configurations> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see this property documented - what do we think it's doing?
| <PropertyGroup> | ||
| <!-- @TODO: Move to directory.build.props? --> | ||
| <!-- If a target OS was not specified, use the current OS as the target OS. --> | ||
| <TargetOs Condition="'$(TargetOs)' == ''">$(OS)</TargetOs> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to just default to WINDOWS_NT so we get the most build coverage?
I think I would prefer that we build the same set of stuff by default regardless of where the build is occurring, and document that. Then, if you want to build a different set of stuff, you have to explicitly ask by setting TargetOs.
I find it surprising that the host OS dictates what to build by default.
| <!-- MSBuild will add the target framework to the end of this path by default. Telling it not --> | ||
| <!-- to is possible but requires also specifying the IntermediateOutputPath. So while it --> | ||
| <!-- would be nice to have a flat directory structure, it's more hassle than its worth. --> | ||
| <OutputPath>$(ArtifactPath)$(AssemblyName)/$(Configuration)/$(TargetOs.ToLower())/</OutputPath> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we choosing ToLower() here for the TargetOs, but ToUpper() for the above checks?
| <DefineConstants Condition="'$(TargetOs.ToUpper())' == 'UNIX'">$(DefineConstants),_UNIX</DefineConstants> | ||
| <DefineConstants Condition="'$(TargetOs.ToUpper())' == 'WINDOWS_NT'">$(DefineConstants),_WINDOWS</DefineConstants> | ||
| <!-- flags. Those only apply to OS-specific target frameworks, and would interfere here. --> | ||
| <DefineConstants Condition="'$(TargetOs.ToUpper())' == 'UNIX'">$(DefineConstants);_UNIX</DefineConstants> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of remembering to ToUpper() the TargetOs all the time, why not just make another property with the upper-case value and use that everywhere?
<TargetOs Condition="'$(TargetOs)' == ''">$(OS)</TargetOs>
<CanonicalTargetOs>$(TargetOs.ToUpper())</CanonicalTargetOs>
...
<DefineConstants Condition="'$(TargetOsCanonical' == UNIX'">$(DefineConstants);_UNIX</DefineConstants>
|
|
||
| <!-- References that only apply to netcore < 10 --> | ||
| <PackageReference Include="System.Buffers" | ||
| Condition="$([MSBuild]::VersionLessThan($([MSBuild]::GetTargetFrameworkVersion($(TargetFramework))), '10.0'))" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a mouthful. Maybe these should be in a separate <ItemGroup> to avoid duplicating this conditional 3 times.
Description
This PR kicks the tires on building the common project. The goal of this PR was simply to get the common project to build, generate artifacts, and cause no disturbances to existing projects or pipelines. Here's a rundown of the changes:
#if NETwrappers around SqlDependencyUtils.AppDomain.netcore.csartifacts/Microsoft.Data.SqlClient/(configuration)/(targetos)/(targetframework)BuildMds- builds common project for unix and windowsBuildMdsUnix- builds common project for unixBuildMdsWindows- builds common project for windowsdotnetto do the build instead.Testing