diff --git a/build.proj b/build.proj
index b8ac7d44ed..c7eaa524dd 100644
--- a/build.proj
+++ b/build.proj
@@ -461,6 +461,35 @@
+
+
+
+
+ "$(DotnetPath)dotnet" build "$(SqlClientNotSupportedProjectPath)"
+ -v:minimal
+ -p:Configuration=$(Configuration)
+ -p:NotSupportedSG=true
+ $(SigningKeyPathArgument)
+
+
+ $(BuildNumberArgument)
+ $(BuildSuffixArgument)
+ $(PackageVersionSqlClientArgument)
+
+
+ $(ReferenceTypeArgument)
+ $(PackageVersionAbstractionsArgument)
+ $(PackageVersionLoggingArgument)
+
+
+ $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " "))
+
+
+
+
+
+
PackLogging;PackAbstractions;PackSqlServer
diff --git a/src/Microsoft.Data.SqlClient.slnx b/src/Microsoft.Data.SqlClient.slnx
index d65c3edc90..b5991b09f1 100644
--- a/src/Microsoft.Data.SqlClient.slnx
+++ b/src/Microsoft.Data.SqlClient.slnx
@@ -160,6 +160,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj
index a9474613e6..86c80fdffc 100644
--- a/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj
@@ -1,4 +1,4 @@
-
+
$(NoWarn);CS0618
+
+ $(DefineConstants);NOTSUPPORTED
@@ -94,6 +96,7 @@
nice to have a flatter directory structure, it's more hassle than its worth.
-->
$(RepoRoot)artifacts/$(AssemblyName).notsupported/$(ReferenceType)-$(Configuration)/
+ $(RepoRoot)artifacts/$(AssemblyName).notsupported/$(ReferenceType)-$(Configuration)-SG/
@@ -185,10 +188,20 @@
- $(AssemblyName).$(TargetFramework).notsupported.cs
+ $(AssemblyName).$(TargetFramework).notsupported.cs
+ Microsoft.Data.SqlClient.SourceGenerator/Microsoft.Data.SqlClient.SourceGenerator.RefToNotSupportedGenerator/$(AssemblyName).$(TargetFramework).notsupported.g.cs
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj
index 43aca3f1c1..78ec702632 100644
--- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj
@@ -1,4 +1,4 @@
-
+
Microsoft.Data.SqlClient
@@ -8,6 +8,8 @@
true
+ true
+ ../notsupported
@@ -21,6 +23,10 @@
$(SqlClientPackageVersion)
+
+
+
+
@@ -91,6 +97,10 @@
+
+ false
+ Analyzer
+
diff --git a/tools/Directory.Packages.props b/tools/Directory.Packages.props
index e74445ea81..e0b23d25cb 100644
--- a/tools/Directory.Packages.props
+++ b/tools/Directory.Packages.props
@@ -1,18 +1,19 @@
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/Microsoft.Data.SqlClient.SourceGenerator.csproj b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/Microsoft.Data.SqlClient.SourceGenerator.csproj
new file mode 100644
index 0000000000..efab1b29a4
--- /dev/null
+++ b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/Microsoft.Data.SqlClient.SourceGenerator.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netstandard2.0
+ true
+ false
+ true
+
+
+
+ $(DefineConstants);GENAPI_COMPAT
+
+
+
+
+
+
+
diff --git a/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/Properties/launchSettings.json b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/Properties/launchSettings.json
new file mode 100644
index 0000000000..2292898349
--- /dev/null
+++ b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Debug SG": {
+ "commandName": "DebugRoslynComponent",
+ "targetProject": "..\\..\\..\\src\\Microsoft.Data.SqlClient\\ref\\Microsoft.Data.SqlClient.csproj"
+ }
+ }
+}
diff --git a/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/RefToNotSupportedGenerator.cs b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/RefToNotSupportedGenerator.cs
new file mode 100644
index 0000000000..7912738b2d
--- /dev/null
+++ b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/RefToNotSupportedGenerator.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Microsoft.Data.SqlClient.SourceGenerator
+{
+ [Generator]
+ public class RefToNotSupportedGenerator : IIncrementalGenerator
+ {
+ private const string DefaultFileHeader =
+ "//------------------------------------------------------------------------------\r\n" +
+ "// \r\n" +
+ "// This code was generated by a tool.\r\n" +
+ "// {0}\r\n" +
+ "//\r\n" +
+ "// Changes to this file may cause incorrect behavior and will be lost if\r\n" +
+ "// the code is regenerated.\r\n" +
+ "// \r\n" +
+ "//------------------------------------------------------------------------------\r\n";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ IncrementalValuesProvider modelsProvider = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: (s, _) => IsSyntaxTargetForGeneration(s),
+ transform: (ctx, _) => ctx.Node)
+ .Where(m => m != null); // Filter out errors that we don't care about
+
+ var modelProvider = modelsProvider
+ .Collect()
+ .Combine(context.AnalyzerConfigOptionsProvider);
+
+ context.RegisterSourceOutput(
+ modelProvider,
+ (ctx, compilation) =>
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat(DefaultFileHeader, nameof(RefToNotSupportedGenerator));
+ using (StringWriter writer = new StringWriter(sb))
+ {
+ if (!compilation.Right.GlobalOptions.TryGetValue("build_property.targetframework", out string framework))
+ {
+ throw new KeyNotFoundException("build_property.targetframework");
+ }
+
+ try
+ {
+ sb.AppendLine("using System;");
+ sb.AppendLine("#if NOTSUPPORTED");
+
+ RefToNotsupportedTypeRewriter visitor = new RefToNotsupportedTypeRewriter();
+ // Group all collected types by namespace. Expects source file name from ref library to match namespace of all incuded types
+ foreach (var group in compilation.Left
+ .GroupBy(c => c.GetLocation().SourceTree.FilePath)
+ .OrderBy(cg => cg.Key)
+ )
+ {
+
+ sb.Append("namespace ");
+ sb.AppendLine(Path.GetFileNameWithoutExtension(group.Key));
+ sb.AppendLine("{");
+ foreach (SyntaxNode model in group)
+ {
+ SyntaxNode result = visitor.Visit(model);
+ result.WriteTo(writer);
+ }
+ sb.AppendLine("}");
+ }
+
+ sb.AppendLine("#endif");
+
+ }
+ catch (Exception ex)
+ {
+ sb.AppendLine($"// Exception: {ex.Message}\r\n// StackTrace: {ex.StackTrace}");
+ }
+
+ ctx.AddSource($"Microsoft.Data.SqlClient.{framework}.notsupported.g.cs", sb.ToString());
+ }
+ });
+ }
+
+ private static bool IsSyntaxTargetForGeneration(SyntaxNode node)
+ {
+ // Public delegates
+ if (node is DelegateDeclarationSyntax membDecl && membDecl.Modifiers.Any(SyntaxKind.PublicKeyword))
+ {
+ return true;
+ }
+ // Public, or internal types
+ if (node is BaseTypeDeclarationSyntax typeDecl &&
+ (typeDecl.Modifiers.Any(SyntaxKind.PublicKeyword) || typeDecl.Modifiers.Any(SyntaxKind.InternalKeyword)))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/RefToNotsupportedTypeRewriter.cs b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/RefToNotsupportedTypeRewriter.cs
new file mode 100644
index 0000000000..63e74e044d
--- /dev/null
+++ b/tools/GenAPI/Microsoft.Data.SqlClient.SourceGenerator/RefToNotsupportedTypeRewriter.cs
@@ -0,0 +1,207 @@
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Data.SqlClient.SourceGenerator
+{
+ public class RefToNotsupportedTypeRewriter : CSharpSyntaxRewriter
+ {
+ private static readonly BlockSyntax s_throwBlock;
+ private static readonly ArrowExpressionClauseSyntax s_throwExpression;
+
+ static RefToNotsupportedTypeRewriter()
+ {
+ ThrowStatementSyntax throwStatement =
+ ThrowStatement( // throw new System.PlatformNotSupportedException("Microsoft.Data.SqlClient is not supported on this platform.")
+ ObjectCreationExpression( // new System.PlatformNotSupportedException("Microsoft.Data.SqlClient is not supported on this platform.")
+ IdentifierName("System.PlatformNotSupportedException"),
+ ArgumentList( // ("Microsoft.Data.SqlClient is not supported on this platform.")
+ SingletonSeparatedList(
+ Argument( // "Microsoft.Data.SqlClient is not supported on this platform."
+ LiteralExpression(
+ SyntaxKind.StringLiteralExpression,
+ Literal("Microsoft.Data.SqlClient is not supported on this platform.") // Microsoft.Data.SqlClient is not supported on this platform.
+ )
+ )
+ )
+ ),
+ null
+ )
+ )
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); // throw new System.PlatformNotSupportedException("Microsoft.Data.SqlClient is not supported on this platform.");
+
+ s_throwBlock = Block(throwStatement)
+ .NormalizeWhitespace(indentation: " ", eol: ""); // { throw new System.PlatformNotSupportedException("Microsoft.Data.SqlClient is not supported on this platform."); }
+ s_throwExpression = ArrowExpressionClause(ThrowExpression(throwStatement.Expression))
+ .NormalizeWhitespace(); // => throw new System.PlatformNotSupportedException("Microsoft.Data.SqlClient is not supported on this platform.")
+ }
+
+ public override SyntaxNode VisitAccessorList(AccessorListSyntax node)
+ {
+ if (node.Parent.IsKind(SyntaxKind.EventDeclaration))
+ {
+ return node;
+ }
+
+ AccessorListSyntax newNode = AccessorList();
+ foreach (AccessorDeclarationSyntax accessor in node.Accessors)
+ {
+ newNode = newNode.AddAccessors(AccessorDeclaration(accessor.Kind(), accessor.AttributeLists, accessor.Modifiers, s_throwBlock));
+ }
+
+ return newNode.NormalizeWhitespace(indentation: " ", eol: "").WithTrailingTrivia(CarriageReturnLineFeed);
+ }
+
+ public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
+ {
+ if (node.ExpressionBody != null)
+ {
+#if GENAPI_COMPAT
+ // Replacing property getter expression with accessor list with throw statement for compatibility with GenAPI generated source
+ node = PropertyDeclaration(node.AttributeLists, node.Modifiers, node.Type, node.ExplicitInterfaceSpecifier, node.Identifier,
+ AccessorList(SingletonList(AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, s_throwBlock))), null, null, MissingToken(SyntaxKind.SemicolonToken))
+ .WithTriviaFrom(node);
+#else
+ node = node.WithExpressionBody(ThrowExpression);
+#endif
+ }
+
+ return base.VisitPropertyDeclaration(node);
+ }
+
+ public override SyntaxNode VisitIndexerDeclaration(IndexerDeclarationSyntax node)
+ {
+ if (node.ExpressionBody != null)
+ {
+ node = node.WithExpressionBody(s_throwExpression);
+ }
+
+ return base.VisitIndexerDeclaration(node);
+ }
+
+ public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
+ {
+ if (node.Modifiers.Any(m => m.ValueText == "abstract"))
+ {
+ return base.VisitMethodDeclaration(node);
+ }
+
+#if GENAPI_COMPAT
+ MethodDeclarationSyntax newNode = node.WithBody(s_throwBlock)
+ .WithExpressionBody(null)
+ .WithSemicolonToken(MissingToken(SyntaxKind.SemicolonToken))
+ .WithTrailingTrivia(CarriageReturnLineFeed);
+#else
+ MethodDeclarationSyntax newNode = (node.ExpressionBody != null ? node.WithExpressionBody(s_throwExpression) : node.WithBody(s_throwBlock))
+ .WithTrailingTrivia(CarriageReturnLineFeed);
+#endif
+ return base.VisitMethodDeclaration(newNode);
+ }
+
+ public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
+ {
+ ConstructorDeclarationSyntax newNode = (node.ExpressionBody != null ? node.WithExpressionBody(s_throwExpression) : node.WithBody(s_throwBlock))
+ .WithTrailingTrivia(CarriageReturnLineFeed);
+ return base.VisitConstructorDeclaration(newNode);
+ }
+
+#if GENAPI_COMPAT
+ public override SyntaxNode VisitAttribute(AttributeSyntax node)
+ {
+ // Ref sources uses following form of the attribute: [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
+ // , while GenAPI sources it is generated as: [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
+ // Here for GenAPI compatibility we substitute former with a latter in source
+ if (node.Name.ToString().EndsWith("DesignerSerializationVisibilityAttribute") &&
+ node.ArgumentList.Arguments.Count > 0 && node.ArgumentList.ToFullString() == "(0)")
+ {
+ node = node.WithArgumentList(
+ AttributeArgumentList(
+ SingletonSeparatedList(
+ AttributeArgument(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("System.ComponentModel.DesignerSerializationVisibility"),
+ IdentifierName("Hidden"))))));
+ }
+
+ return base.VisitAttribute(node);
+ }
+#endif
+
+#if GENAPI_COMPAT
+ // Order enum elements by value (as GenAPI does), instead of by name (as source generator does)
+ public override SyntaxNode VisitEnumDeclaration(EnumDeclarationSyntax node)
+ {
+ node = node.WithMembers(SeparatedList(
+ node.Members.OrderBy(m => int.Parse(m.EqualsValue.Value.ToFullString())).ToArray(),
+ node.Members.GetSeparators()));
+ return base.VisitEnumDeclaration(node);
+ }
+#endif
+
+#if GENAPI_COMPAT
+ public override SyntaxNode Visit(SyntaxNode node)
+ {
+ // Strip away any documentation, or preprocessor directives
+ if (node is MemberDeclarationSyntax member)
+ {
+ if (!member.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword)))
+ {
+ if (member.Modifiers != null && member.Modifiers.Count > 0 && member.Modifiers[0].HasLeadingTrivia)
+ {
+ node = member.WithModifiers(member.Modifiers.Replace(member.Modifiers[0], member.Modifiers[0].WithLeadingTrivia(RemoveExcessTrivia(member.Modifiers[0].LeadingTrivia))));
+ }
+ if (node is MethodDeclarationSyntax method && method.HasLeadingTrivia)
+ {
+ node = method.WithReturnType(method.ReturnType.WithLeadingTrivia(RemoveExcessTrivia(method.ReturnType.GetLeadingTrivia())));
+ }
+ if (node is BasePropertyDeclarationSyntax property && property.HasLeadingTrivia)
+ {
+ node = property.WithType(property.Type.WithLeadingTrivia(RemoveExcessTrivia(property.Type.GetLeadingTrivia())));
+ }
+ if (node is BaseTypeDeclarationSyntax typeNode)
+ {
+ node = typeNode.WithOpenBraceToken(typeNode.OpenBraceToken.WithLeadingTrivia(RemoveExcessTrivia(typeNode.OpenBraceToken.LeadingTrivia)))
+ .WithCloseBraceToken(typeNode.CloseBraceToken.WithLeadingTrivia(RemoveExcessTrivia(typeNode.CloseBraceToken.LeadingTrivia)));
+ }
+ }
+
+ return base.Visit(node.WithLeadingTrivia(RemoveExcessTrivia(node.GetLeadingTrivia())));
+ }
+
+ return base.Visit(node);
+ }
+
+ private static SyntaxTriviaList RemoveExcessTrivia(SyntaxTriviaList trivias)
+ {
+ SyntaxTriviaList newTrivias = TriviaList();
+ for (int i = 0; i < trivias.Count; i++)
+ {
+ SyntaxTrivia trivia = trivias[i];
+ SyntaxTrivia? lastTrivia = newTrivias.LastOrDefault();
+ if (!trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) &&
+ !trivia.IsKind(SyntaxKind.DisabledTextTrivia) &&
+ !trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia) &&
+ !trivia.IsKind(SyntaxKind.IfDirectiveTrivia) &&
+ !trivia.IsKind(SyntaxKind.ElseDirectiveTrivia) &&
+ !trivia.IsKind(SyntaxKind.ElifDirectiveTrivia))
+ {
+ newTrivias = newTrivias.Add(trivia);
+ }
+ else if (lastTrivia != null &&
+ (
+ lastTrivia.Value.IsKind(SyntaxKind.WhitespaceTrivia) ||
+ lastTrivia.Value.IsKind(SyntaxKind.DisabledTextTrivia)
+ ))
+ {
+ newTrivias = newTrivias.Remove(lastTrivia.Value);
+ }
+ }
+
+ return newTrivias;
+ }
+#endif
+ }
+}
diff --git a/tools/targets/CompareMdsRefAssemblies.targets b/tools/targets/CompareMdsRefAssemblies.targets
index 47caaa7a56..90651636c0 100644
--- a/tools/targets/CompareMdsRefAssemblies.targets
+++ b/tools/targets/CompareMdsRefAssemblies.targets
@@ -147,6 +147,51 @@
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizePath('$(RepoRoot)', 'src', 'Microsoft.Data.SqlClient', 'notsupported', 'Microsoft.Data.SqlClient.csproj'))
+
+
+
+
+
+
+
+
+
+ $(BaselinePackageDir)results\
+
+
+
+
+
+
+
+
+
@@ -159,4 +204,16 @@
+
+
+
+
+
+ <_ApiCompatResultFiles Include="$(ApiCompatResultsDir)*.txt" />
+
+
+
+
+
+