From 0f35909d56ea0055457a89ba3879649d6d0d632c Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Tue, 15 Oct 2024 14:56:09 -0400 Subject: [PATCH] [RGen] First inclusion of a roslyn generator for the bindings. (#21389) We are doing the following: 1. Adding the rgen directory with the following solutions: * Analyzer: Analyzer that will catch errors in the bindings. At the moment it provides a single error when the BindingTypeAttribute is used in a nont partial type. * Analyzer Tests: Allows tests for the analyzer. * Analyzer Sample: Sample project to test the analyzer. * Code Generator: A code generator that adds the BindingTypeAttribute to the compilcation. * Code Generator Tests: Allows tests for the generator. * Code Sample: Sample project for the code generator. 2. Make rule to build the roslyn code generator. 3. Makefile changes to add the code generator as part as the second compilation of the bindings. This changes add the starting gounds to move to roslyn. --------- Co-authored-by: GitHub Actions Autoformatter Co-authored-by: Rolf Bjarne Kvinge Co-authored-by: Alex Soto --- docs/preview-apis.md | 7 + src/Makefile | 6 +- src/Makefile.rgenerator | 8 + src/ObjCBindings/BindingTypeAttribute.cs | 25 +++ src/frameworks.sources | 1 + .../Examples.cs | 12 ++ ...oft.Macios.Bindings.Analyzer.Sample.csproj | 19 ++ .../AnalyzerReleases.Shipped.md | 7 + .../AnalyzerReleases.Unshipped.md | 4 + .../BindingTypeCodeFixProvider.cs | 59 ++++++ .../BindingTypeSemanticAnalyzer.cs | 72 ++++++++ .../Microsoft.Macios.Bindings.Analyzer.csproj | 46 +++++ .../Properties/launchSettings.json | 9 + .../Readme.md | 26 +++ .../Resources.Designer.cs | 96 ++++++++++ .../Resources.resx | 37 ++++ .../Microsoft.Macios.Generator.Sample.csproj | 19 ++ .../SampleBinding.cs | 9 + .../BindingSourceGeneratorGenerator.cs | 24 +++ .../ExtraSources.cs | 5 + .../Microsoft.Macios.Generator.csproj | 26 +++ .../Properties/launchSettings.json | 9 + src/rgen/Microsoft.Macios.Generator/Readme.md | 21 +++ src/rgen/rgen.sln | 46 +++++ tests/bgen/bgen-tests.csproj | 3 + .../Documentation.KnownFailures.txt | 1 + tests/cecil-tests/cecil-tests.csproj | 3 + tests/common/Configuration.cs | 151 +-------------- tests/common/ConfigurationNUnit.cs | 172 ++++++++++++++++++ tests/common/ConfigurationXUnit.cs | 57 ++++++ tests/common/ExecutionHelper.cs | 4 - tests/common/mac/project_building.mk | 1 + tests/dotnet/UnitTests/DotNetUnitTests.csproj | 3 + tests/generator/generator-tests.csproj | 3 + tests/introspection/ApiFrameworkTest.cs | 3 + tests/mmptest/mmptest.csproj | 3 + .../dotnet/macOS/monotouch-test.csproj | 3 + .../Xamarin.MacDev.Tasks.Tests.csproj | 3 + .../Xamarin.MacDev.Tests.csproj | 3 + tests/mtouch/mtouchtests.csproj | 3 + .../BaseGeneratorWithAnalyzerTestClass.cs | 35 ++++ .../BindingTypeSemanticAnalyzerTests.cs | 36 ++++ ...soft.Macios.Bindings.Analyzer.Tests.csproj | 27 +++ .../BaseGeneratorTestClass.cs | 70 +++++++ .../BaseTestDataGenerator.cs | 13 ++ .../BindingSourceGeneratorGeneratorTests.cs | 44 +++++ .../Microsoft.Macios.Generator.Tests.csproj | 59 ++++++ tests/xammac_tests/xammac_tests.csproj | 3 + tests/xharness/Jenkins/Jenkins.cs | 36 ++++ 49 files changed, 1176 insertions(+), 156 deletions(-) create mode 100644 src/Makefile.rgenerator create mode 100644 src/ObjCBindings/BindingTypeAttribute.cs create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Examples.cs create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/Properties/launchSettings.json create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/Readme.md create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs create mode 100644 src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx create mode 100644 src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj create mode 100644 src/rgen/Microsoft.Macios.Generator.Sample/SampleBinding.cs create mode 100644 src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs create mode 100644 src/rgen/Microsoft.Macios.Generator/ExtraSources.cs create mode 100644 src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj create mode 100644 src/rgen/Microsoft.Macios.Generator/Properties/launchSettings.json create mode 100644 src/rgen/Microsoft.Macios.Generator/Readme.md create mode 100644 src/rgen/rgen.sln create mode 100644 tests/common/ConfigurationNUnit.cs create mode 100644 tests/common/ConfigurationXUnit.cs create mode 100644 tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BaseGeneratorWithAnalyzerTestClass.cs create mode 100644 tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs create mode 100644 tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Microsoft.Macios.Bindings.Analyzer.Tests.csproj create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/BaseTestDataGenerator.cs create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj diff --git a/docs/preview-apis.md b/docs/preview-apis.md index e056d55a8bac..99f5b1d61049 100644 --- a/docs/preview-apis.md +++ b/docs/preview-apis.md @@ -97,3 +97,10 @@ We've tentatively set .NET 11 as the release when we'll stop marking FSKit as pr The diagnostic id for FSKit is APL0002. [1]: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.experimentalattribute?view=net-8.0 + +## Rgen (APL0003) + +Rgen is the new Roslyn codegenerator based binding tool. The tool is underdevelopment and its API is open to change until +a stable release is announced. + +The diagnostic id for Rgen is APL0003. diff --git a/src/Makefile b/src/Makefile index ea2a4162dcb4..bb088101958c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -103,6 +103,7 @@ DOTNET_WARNINGS_TO_FIX = -nowarn:$(CSC_WARNINGS_TO_FIX) DOTNET_CORE_WARNINGS_TO_FIX = -nowarn:$(CSC_WARNINGS_TO_FIX),$(BGEN_WARNINGS_TO_FIX) include ./Makefile.generator +include ./Makefile.rgenerator include ./generator-diff.mk SHARED_RESX = $(TOP)/tools/mtouch/Errors.resx @@ -355,7 +356,7 @@ $($(2)_DOTNET_BUILD_DIR)/core-$(3).dll: $($(2)_DOTNET_CORE_SOURCES) frameworks.s $($(2)_DOTNET_BUILD_DIR)/$(3)-generated-sources: $(DOTNET_GENERATOR) $($(2)_DOTNET_APIS) $($(2)_DOTNET_BUILD_DIR)/core-$(3).dll $(DOTNET_BINDING_ATTRIBUTES) $($(2)_DOTNET_BUILD_DIR)/$(3).rsp | $($(2)_DOTNET_BUILD_DIR)/generated-sources $$(Q_DOTNET_GEN) $$< @$($(2)_DOTNET_BUILD_DIR)/$(3).rsp -$($(2)_DOTNET_BUILD_DIR)/$(3).rsp: Makefile Makefile.generator frameworks.sources $(DOTNET_COMPILER) | $($(2)_DOTNET_BUILD_DIR) +$($(2)_DOTNET_BUILD_DIR)/$(3).rsp: Makefile Makefile.generator Makefile.rgenerator frameworks.sources $(ROSLYN_GENERATOR) $(DOTNET_COMPILER) | $($(2)_DOTNET_BUILD_DIR) $(Q) echo \ $($(2)_GENERATOR_FLAGS) \ $(DOTNET_GENERATOR_FLAGS) \ @@ -450,10 +451,11 @@ $(2)_DOTNET_PLATFORM_ASSEMBLY_DIR_DEPENDENCIES = \ $($(2)_DOTNET_BUILD_DIR)/$(4) \ $($(2)_DOTNET_BUILD_DIR)/ref \ -$($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%dll $($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%pdb $$($(2)_$(4)_REF_TARGET) $$($(2)_$(4)_DOC_TARGET): $$($(2)_DOTNET_PLATFORM_ASSEMBLY_DEPENDENCIES) | $$($(2)_DOTNET_PLATFORM_ASSEMBLY_DIR_DEPENDENCIES) +$($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%dll $($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%pdb $$($(2)_$(4)_REF_TARGET) $$($(2)_$(4)_DOC_TARGET): $$($(2)_DOTNET_PLATFORM_ASSEMBLY_DEPENDENCIES) $$(ROSLYN_GENERATOR) | $$($(2)_DOTNET_PLATFORM_ASSEMBLY_DIR_DEPENDENCIES) $$(call Q_PROF_CSC,dotnet/$(4)-bit) \ $(DOTNET_CSC) \ $(DOTNET_FLAGS) \ + /analyzer:$(ROSLYN_GENERATOR) \ -unsafe \ -optimize \ $$(ARGS_$(1)) \ diff --git a/src/Makefile.rgenerator b/src/Makefile.rgenerator new file mode 100644 index 000000000000..e3bd9f2b997a --- /dev/null +++ b/src/Makefile.rgenerator @@ -0,0 +1,8 @@ +# Roslyn code generator +ROSLYN_GENERATOR=$(DOTNET_BUILD_DIR)/common/rgen/Microsoft.Macios.Generator.dll +ROSLYN_GENERATOR_FILES := $(wildcard rgen/Microsoft.Macios.Generator/*.cs) + +$(ROSLYN_GENERATOR): Makefile.rgenerator $(ROSLYN_GENERATOR_FILES) + $(Q_DOTNET_BUILD) $(DOTNET) publish rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj $(DOTNET_BUILD_VERBOSITY) /p:Configuration=Debug /p:IntermediateOutputPath=$(abspath $(DOTNET_BUILD_DIR)/IDE/obj/common/rgen)/ /p:OutputPath=$(abspath $(DOTNET_BUILD_DIR)/IDE/bin/common/rgen/)/ + @mkdir -p $(dir $@) + $(Q) $(CP) -r $(DOTNET_BUILD_DIR)/IDE/bin/common/rgen/publish/* $(dir $@) diff --git a/src/ObjCBindings/BindingTypeAttribute.cs b/src/ObjCBindings/BindingTypeAttribute.cs new file mode 100644 index 000000000000..6f936d77c69a --- /dev/null +++ b/src/ObjCBindings/BindingTypeAttribute.cs @@ -0,0 +1,25 @@ +using System; +using System.Reflection; +using System.Diagnostics.CodeAnalysis; + +namespace ObjCBindings { + + /// + /// Attribute that indicates that a class or enum is a binding type. This attribute is used by the binding generator + /// to generate all the necessary code for the binding. The attribute can be used in a class or enum and it is required. + /// + /// If the attribute is used in a class, the class must be partial otherwise the generator will fail. + /// + [Experimental ("APL0003")] + [AttributeUsage (AttributeTargets.Class | System.AttributeTargets.Enum, AllowMultiple = false)] + public class BindingTypeAttribute : Attribute { + + /// + /// Indicates the name of the binding type. This is the name that will be used by the registrar to make the + /// class available in the ObjC runtime. The default value is string.Empty, in that case the generator + /// will use the name of the C# class. + /// + public string Name { get; set; } = string.Empty; + } + +} diff --git a/src/frameworks.sources b/src/frameworks.sources index e129ca7cd91b..57094027d6fa 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -2012,6 +2012,7 @@ SHARED_CORE_SOURCES = \ DotNetGlobals.cs \ MinimumVersions.cs \ MonoPInvokeCallbackAttribute.cs \ + ObjCBindings/BindingTypeAttribute.cs \ ObjCRuntime/ArgumentSemantic.cs \ ObjCRuntime/BindAsAttribute.cs \ ObjCRuntime/Blocks.cs \ diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Examples.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Examples.cs new file mode 100644 index 000000000000..fe4905a77980 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Examples.cs @@ -0,0 +1,12 @@ +namespace Microsoft.Macios.Bindings.Analyzer.Sample; + +// If you don't see warnings, build the Analyzers Project. + +[BindingType] +public class Examples { +} + +[BindingType] +public class Foo { + +} diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj b/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj new file mode 100644 index 000000000000..bc9520bfbacf --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj @@ -0,0 +1,19 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + enable + + + + + + + + + + external\Attributes.cs + + + + diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md new file mode 100644 index 000000000000..5a165075fbca --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md @@ -0,0 +1,7 @@ +## Release 1.0 + +### New Rules + +| Rule ID | Category | Severity | Notes | +|---------|----------|----------|------------------------------------------------------| +| RBI0001 | Usage | Error | Binding types should be declared as partial classes. | \ No newline at end of file diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md new file mode 100644 index 000000000000..44f7c8f4ef7c --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md @@ -0,0 +1,4 @@ +### New Rules + +| Rule ID | Category | Severity | Notes | +|---------|----------|----------|-------| \ No newline at end of file diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs new file mode 100644 index 000000000000..17b8a2983607 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs @@ -0,0 +1,59 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using SyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; + +namespace Microsoft.Macios.Bindings.Analyzer; + +/// +/// Code fix provider that adds the 'partial' modifier to the class decorated with BindingTypeAttribute. +/// +[ExportCodeFixProvider (LanguageNames.CSharp, Name = nameof (BindingTypeCodeFixProvider)), Shared] +public class BindingTypeCodeFixProvider : CodeFixProvider { + // Specify the diagnostic IDs of analyzers that are expected to be linked. + public sealed override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create (BindingTypeSemanticAnalyzer.DiagnosticId); + + // If you don't need the 'fix all' behaviour, return null. + public override FixAllProvider? GetFixAllProvider () => null; + + public sealed override async Task RegisterCodeFixesAsync (CodeFixContext context) + { + var diagnostic = context.Diagnostics.Single (); + var diagnosticSpan = diagnostic.Location.SourceSpan; + var root = await context.Document.GetSyntaxRootAsync (context.CancellationToken).ConfigureAwait (false); + var diagnosticNode = root?.FindNode (diagnosticSpan); + + // To get the required metadata, we should match the Node to the specific type: 'ClassDeclarationSyntax'. + if (diagnosticNode is not ClassDeclarationSyntax declaration) + return; + + // Register a code action that will invoke the fix. + context.RegisterCodeFix ( + CodeAction.Create ( + title: Resources.RBI0001CodeFixTitle, + createChangedDocument: c => MakePartialClassAsync (context.Document, declaration, c), + equivalenceKey: nameof (Resources.RBI0001CodeFixTitle)), + diagnostic); + } + + async Task MakePartialClassAsync (Document document, + ClassDeclarationSyntax classDeclarationSyntax, CancellationToken cancellationToken) + { + var partialClass = classDeclarationSyntax.AddModifiers (SyntaxFactory.Token (SyntaxKind.PartialKeyword)); + var oldRoot = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); + if (oldRoot is null) + return document; + + var newRoot = oldRoot.ReplaceNode (classDeclarationSyntax, partialClass); + return document.WithSyntaxRoot (newRoot); + } +} diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs new file mode 100644 index 000000000000..fd5df71a0e8b --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs @@ -0,0 +1,72 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.Macios.Bindings.Analyzer; + +/// +/// Analyzer that ensures that the types that have been declared as binding types are partial and follow the correct +/// pattern. +/// +[DiagnosticAnalyzer (LanguageNames.CSharp)] +public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer { + internal const string DiagnosticId = "RBI0001"; + static readonly LocalizableString Title = new LocalizableResourceString (nameof (Resources.RBI0001Title), + Resources.ResourceManager, typeof (Resources)); + static readonly LocalizableString MessageFormat = + new LocalizableResourceString (nameof (Resources.RBI0001MessageFormat), Resources.ResourceManager, + typeof (Resources)); + static readonly LocalizableString Description = + new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager, + typeof (Resources)); + const string Category = "Usage"; + + static readonly DiagnosticDescriptor RBI0001 = new (DiagnosticId, Title, MessageFormat, Category, + DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create (RBI0001); + + public override void Initialize (AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution (); + context.RegisterSyntaxNodeAction (AnalysisContext, SyntaxKind.ClassDeclaration); + } + + void AnalysisContext (SyntaxNodeAnalysisContext context) + { + // only care about classes + if (context.Node is not ClassDeclarationSyntax classDeclarationNode) + return; + + var classSymbol = context.SemanticModel.GetDeclaredSymbol (classDeclarationNode); + if (classSymbol is null) + return; + + var boundAttributes = classSymbol.GetAttributes (); + if (boundAttributes.Length == 0) { + return; + } + + // the c# syntax is a a list of lists of attributes. That is why we need to iterate through the list of lists + foreach (var attributeData in boundAttributes) { + // based on the type use the correct parser to retrieve the data + var attributeType = attributeData.AttributeClass?.ToDisplayString (); + switch (attributeType) { + case "ObjCBindings.BindingTypeAttribute": + // validate that the class is partial, else we need to report an error + if (!classDeclarationNode.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword))) { + var diagnostic = Diagnostic.Create (RBI0001, + classDeclarationNode.Identifier.GetLocation (), // point to where the 'class' keyword is used + classSymbol.ToDisplayString ()); + context.ReportDiagnostic (diagnostic); + } + break; + } + } + } +} diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj new file mode 100644 index 000000000000..6f9e0902e438 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj @@ -0,0 +1,46 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + false + enable + latest + + true + true + + Microsoft.Macios.Bindings.Analyzer + Microsoft.Macios.Bindings.Analyzer + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + <_Parameter1>Microsoft.Macios.Bindings.Analyzer.Tests + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + True + True + Resources.resx + + + + diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Properties/launchSettings.json b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Properties/launchSettings.json new file mode 100644 index 000000000000..874ffd77b7e5 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "DebugRoslynAnalyzers": { + "commandName": "DebugRoslynComponent", + "targetProject": "../Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj" + } + } +} \ No newline at end of file diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Readme.md b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Readme.md new file mode 100644 index 000000000000..d3a566c3c1cd --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Readme.md @@ -0,0 +1,26 @@ +# Roslyn Analyzers Sample + +Roslyn analyzer to be installed along side the Roslyn Conde generator that will help developers work on Microsoft.Macios bindings. + +## Content +### Microsoft.Macios.Bindings.Analyzer + +A .NET Standard project with implementations of sample analyzers and code fix providers. +**You must build this project to see the results (warnings) in the IDE.** + +### Microsoft.Macios.Bindings.Analyzer.Sample +A project that references the sample analyzers. Note the parameters of `ProjectReference` in [Microsoft.Macios.Bindings.Analyzer.Sample.csproj](../Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj), they make sure that the project is referenced as a set of analyzers. + +### Microsoft.Macios.Bindings.Analyzer.Tests +Unit tests for the sample analyzers and code fix provider. The easiest way to develop language-related features is to start with unit tests. + +## How To? +### How to debug? +- Use the [launchSettings.json](Properties/launchSettings.json) profile. +- Debug tests (in VSCode). + +### How can I determine which syntax nodes I should expect? +Consider installing the Roslyn syntax tree viewer plugin [Rossynt](https://plugins.jetbrains.com/plugin/16902-rossynt/). + +### Learn more about wiring analyzers +The complete set of information is available at [roslyn github repo wiki](https://github.com/dotnet/roslyn/blob/main/docs/wiki/README.md). \ No newline at end of file diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs new file mode 100644 index 000000000000..bfe0d3152b0f --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Macios.Bindings.Analyzer { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Microsoft.Macios.Bindings.Analyzer.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string AB0001Description { + get { + return ResourceManager.GetString("AB0001Description", resourceCulture); + } + } + + internal static string AB0001MessageFormat { + get { + return ResourceManager.GetString("AB0001MessageFormat", resourceCulture); + } + } + + internal static string AB0001Title { + get { + return ResourceManager.GetString("AB0001Title", resourceCulture); + } + } + + internal static string RBI0001CodeFixTitle { + get { + return ResourceManager.GetString("RBI0001CodeFixTitle", resourceCulture); + } + } + + internal static string RBI0001Description { + get { + return ResourceManager.GetString("RBI0001Description", resourceCulture); + } + } + + internal static string RBI0001MessageFormat { + get { + return ResourceManager.GetString("RBI0001MessageFormat", resourceCulture); + } + } + + internal static string RBI0001Title { + get { + return ResourceManager.GetString("RBI0001Title", resourceCulture); + } + } + + internal static string AB0002Description { + get { + return ResourceManager.GetString("AB0002Description", resourceCulture); + } + } + } +} diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx new file mode 100644 index 000000000000..2f228a310c3f --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx @@ -0,0 +1,37 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Make Binding Type class partial + The title of the code fix. + + + In order for the code to be generated all binding types have to be declared as partial classes. + An optional longer localizable description of the diagnostic. + + + The binding type '{0}' must declared as a partial class + The format-able message the diagnostic displays. + + + Binding type declaration must be partial + The title of the diagnostic. + + diff --git a/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj b/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj new file mode 100644 index 000000000000..f21144e77c4c --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj @@ -0,0 +1,19 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + enable + Microsoft.Macios.Generator.Sample + + + + + + + + + external\Attributes.cs + + + + diff --git a/src/rgen/Microsoft.Macios.Generator.Sample/SampleBinding.cs b/src/rgen/Microsoft.Macios.Generator.Sample/SampleBinding.cs new file mode 100644 index 000000000000..28d28329ae21 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator.Sample/SampleBinding.cs @@ -0,0 +1,9 @@ +namespace Microsoft.Macios.Generator.Sample; + +// This code will not compile until you build the project with the Source Generators + +[BindingType] +public partial class SampleBinding { + public int Id { get; } = 42; + public string? Name { get; } = "Sample"; +} diff --git a/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs b/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs new file mode 100644 index 000000000000..fcc630f160ce --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs @@ -0,0 +1,24 @@ +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.Macios.Generator; + +/// +/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute. +/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead. +/// +[Generator] +public class BindingSourceGeneratorGenerator : IIncrementalGenerator { + + public void Initialize (IncrementalGeneratorInitializationContext context) + { + // Add the binding generator attributes to the compilation. This are only available when the + // generator is used, similar to how bgen works. + foreach ((string fileName, string content) in ExtraSources.Sources) { + context.RegisterPostInitializationOutput (ctx => ctx.AddSource ( + fileName, SourceText.From (content, Encoding.UTF8))); + } + } + +} diff --git a/src/rgen/Microsoft.Macios.Generator/ExtraSources.cs b/src/rgen/Microsoft.Macios.Generator/ExtraSources.cs new file mode 100644 index 000000000000..916949db8768 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator/ExtraSources.cs @@ -0,0 +1,5 @@ +namespace Microsoft.Macios.Generator; + +public static class ExtraSources { + public static readonly (string FileName, string Content) [] Sources = new (string, string) [0]; +} diff --git a/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj b/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj new file mode 100644 index 000000000000..f9c73ed0396a --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj @@ -0,0 +1,26 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + false + enable + latest + + true + true + + Microsoft.Macios.Generator + Microsoft.Macios.Generator + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/src/rgen/Microsoft.Macios.Generator/Properties/launchSettings.json b/src/rgen/Microsoft.Macios.Generator/Properties/launchSettings.json new file mode 100644 index 000000000000..5687e6130138 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "DebugRoslynSourceGenerator": { + "commandName": "DebugRoslynComponent", + "targetProject": "../Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj" + } + } +} diff --git a/src/rgen/Microsoft.Macios.Generator/Readme.md b/src/rgen/Microsoft.Macios.Generator/Readme.md new file mode 100644 index 000000000000..8151de594361 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Generator/Readme.md @@ -0,0 +1,21 @@ +# Roslyn binding code generator + +This directory contains the code generator for binding code. The generator is the implementation of [RFC: Migrate bgen to use roslyn instead of the reflection API](https://github.com/xamarin/xamarin-macios/issues/21308) + +## Content + +### Microsoft.Macios.Generator +A .NET Standard project with implementations of sample source generators. + +**You must build this project to see the result (generated code) in the IDE.** + +### Microsoft.Macios.Generator.Sample +A project that references source generators. Note the parameters of `ProjectReference` in [Microsoft.Macios.Generator.Sample.csproj](../Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj), they make sure that the project is referenced as a set of source generators. + +### Microsoft.Macios.Generator.Tests +Unit tests for source generators. The easiest way to develop language-related features is to start with unit tests. + +## How To? +### How to debug? +- Use the [launchSettings.json](Properties/launchSettings.json) profile. +- Debug tests. \ No newline at end of file diff --git a/src/rgen/rgen.sln b/src/rgen/rgen.sln new file mode 100644 index 000000000000..684992039540 --- /dev/null +++ b/src/rgen/rgen.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Generator", "Microsoft.Macios.Generator\Microsoft.Macios.Generator.csproj", "{8E9CF45D-E836-447E-9290-03A9CACE2704}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Generator.Sample", "Microsoft.Macios.Generator.Sample\Microsoft.Macios.Generator.Sample.csproj", "{AD0A1FDC-350F-47E2-AA9D-A6F32793C130}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Bindings.Analyzer", "Microsoft.Macios.Bindings.Analyzer\Microsoft.Macios.Bindings.Analyzer.csproj", "{27A7CBB0-A30D-4A08-A475-6D2DFD94C634}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Bindings.Analyzer.Sample", "Microsoft.Macios.Bindings.Analyzer.Sample\Microsoft.Macios.Bindings.Analyzer.Sample.csproj", "{65649B5A-9C23-4AA8-A687-82319EF4FA7E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Generator.Tests", "..\..\tests\rgen\Microsoft.Macios.Generator.Tests\Microsoft.Macios.Generator.Tests.csproj", "{CD222ACD-A54F-49D9-81CA-6D795CC31195}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Bindings.Analyzer.Tests", "..\..\tests\rgen\Microsoft.Macios.Bindings.Analyzer.Tests\Microsoft.Macios.Bindings.Analyzer.Tests.csproj", "{1AC4A248-CC98-4392-8690-4E2CAF6E194B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8E9CF45D-E836-447E-9290-03A9CACE2704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E9CF45D-E836-447E-9290-03A9CACE2704}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E9CF45D-E836-447E-9290-03A9CACE2704}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E9CF45D-E836-447E-9290-03A9CACE2704}.Release|Any CPU.Build.0 = Release|Any CPU + {AD0A1FDC-350F-47E2-AA9D-A6F32793C130}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD0A1FDC-350F-47E2-AA9D-A6F32793C130}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD0A1FDC-350F-47E2-AA9D-A6F32793C130}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD0A1FDC-350F-47E2-AA9D-A6F32793C130}.Release|Any CPU.Build.0 = Release|Any CPU + {27A7CBB0-A30D-4A08-A475-6D2DFD94C634}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27A7CBB0-A30D-4A08-A475-6D2DFD94C634}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27A7CBB0-A30D-4A08-A475-6D2DFD94C634}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27A7CBB0-A30D-4A08-A475-6D2DFD94C634}.Release|Any CPU.Build.0 = Release|Any CPU + {65649B5A-9C23-4AA8-A687-82319EF4FA7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65649B5A-9C23-4AA8-A687-82319EF4FA7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65649B5A-9C23-4AA8-A687-82319EF4FA7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65649B5A-9C23-4AA8-A687-82319EF4FA7E}.Release|Any CPU.Build.0 = Release|Any CPU + {CD222ACD-A54F-49D9-81CA-6D795CC31195}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD222ACD-A54F-49D9-81CA-6D795CC31195}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD222ACD-A54F-49D9-81CA-6D795CC31195}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD222ACD-A54F-49D9-81CA-6D795CC31195}.Release|Any CPU.Build.0 = Release|Any CPU + {1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/tests/bgen/bgen-tests.csproj b/tests/bgen/bgen-tests.csproj index 6ee37f30a0a8..cef7c5856f16 100644 --- a/tests/bgen/bgen-tests.csproj +++ b/tests/bgen/bgen-tests.csproj @@ -48,6 +48,9 @@ Configuration.cs + + ConfigurationNUnit.cs + ExecutionHelper.cs diff --git a/tests/cecil-tests/Documentation.KnownFailures.txt b/tests/cecil-tests/Documentation.KnownFailures.txt index 3e025923b416..4389dfbe71ce 100644 --- a/tests/cecil-tests/Documentation.KnownFailures.txt +++ b/tests/cecil-tests/Documentation.KnownFailures.txt @@ -44896,6 +44896,7 @@ M:NotificationCenter.NSExtensionContext_NCWidgetAdditions.GetWidgetMaximumSize(F M:NotificationCenter.NSExtensionContext_NCWidgetAdditions.SetWidgetLargestAvailableDisplayMode(Foundation.NSExtensionContext,NotificationCenter.NCWidgetDisplayMode) M:NotificationCenter.NSWidgetSearchForTermEventArgs.#ctor(System.String,System.UIntPtr) M:NotificationCenter.NSWidgetSearchResultSelectedEventArgs.#ctor(Foundation.NSObject) +M:ObjCBindings.BindingTypeAttribute.#ctor M:ObjCRuntime.AdoptsAttribute.#ctor(System.String) M:ObjCRuntime.AssemblyRegistrationEventArgs.#ctor M:ObjCRuntime.BaseWrapper.#ctor(ObjCRuntime.NativeHandle,System.Boolean) diff --git a/tests/cecil-tests/cecil-tests.csproj b/tests/cecil-tests/cecil-tests.csproj index 50d9f62c8f53..eac1c64fe7d1 100644 --- a/tests/cecil-tests/cecil-tests.csproj +++ b/tests/cecil-tests/cecil-tests.csproj @@ -19,6 +19,9 @@ Configuration.cs + + ConfigurationNUnit.cs + Profile.cs diff --git a/tests/common/Configuration.cs b/tests/common/Configuration.cs index 5fdbd955485d..2d2935ef367d 100644 --- a/tests/common/Configuration.cs +++ b/tests/common/Configuration.cs @@ -7,8 +7,6 @@ using System.Text; using System.Threading; -using NUnit.Framework; - using Xamarin.Utils; #nullable disable // until we get around to fixing this file @@ -384,12 +382,6 @@ public static bool TryGetRootPath (out string rootPath) } } - static string TestAssemblyDirectory { - get { - return TestContext.CurrentContext.WorkDirectory; - } - } - public static string SourceRoot { get { if (mt_src_root is null) @@ -682,25 +674,6 @@ public static string MlaunchPath { } #if !XAMMAC_TESTS - public static void AssertRuntimeIdentifierAvailable (ApplePlatform platform, string runtimeIdentifier) - { - if (string.IsNullOrEmpty (runtimeIdentifier)) - return; - - if (GetRuntimeIdentifiers (platform).Contains (runtimeIdentifier)) - return; - - Assert.Ignore ($"The runtime identifier {runtimeIdentifier} is not available on {platform}"); - } - - public static void AssertRuntimeIdentifiersAvailable (ApplePlatform platform, string runtimeIdentifiers) - { - if (string.IsNullOrEmpty (runtimeIdentifiers)) - return; - - foreach (var rid in runtimeIdentifiers.Split (new char [] { ';' }, StringSplitOptions.RemoveEmptyEntries)) - AssertRuntimeIdentifierAvailable (platform, rid); - } public static string GetBaseLibrary (Profile profile) { @@ -913,12 +886,6 @@ public static string GetCompiler (Profile profile, IList args) return "/Library/Frameworks/Mono.framework/Commands/csc"; } - public static void AssertiOS32BitAvailable () - { - if (iOSSupports32BitArchitectures) - return; - Assert.Ignore ($"32-bit iOS support is not available in the current build."); - } #endif // !XAMMAC_TESTS public static IEnumerable GetIncludedPlatforms (bool dotnet) @@ -959,27 +926,6 @@ public static string XIBuildPath { get { return Path.GetFullPath (Path.Combine (RootPath, "tools", "xibuild", "xibuild")); } } - public static void AssertDeviceAvailable () - { - if (include_device) - return; - Assert.Ignore ("This build does not include device support."); - } - - public static void AssertDotNetAvailable () - { - if (include_dotnet) - return; - Assert.Ignore (".NET tests not enabled"); - } - - public static void AssertLegacyXamarinAvailable () - { - if (include_legacy_xamarin) - return; - Assert.Ignore ("Legacy xamarin build not enabled"); - } - public static string CloneTestDirectory (string directory) { // Copy the test projects to a temporary directory so that we can run the tests from there without affecting the working directory. @@ -1072,102 +1018,6 @@ public static void SetBuildVariables (ApplePlatform platform, ref Dictionary notIncluded) - { - var allPlatforms = GetAllPlatforms (dotnet); - var includedPlatforms = GetIncludedPlatforms (dotnet); - notIncluded = allPlatforms.Where (v => !includedPlatforms.Contains (v)).ToArray (); - return notIncluded.Any (); - } - - public static void IgnoreIfAnyIgnoredPlatforms (bool dotnet = true) - { - if (AnyIgnoredPlatforms (dotnet, out var notIncluded)) - Assert.Ignore ($"This test requires all platforms to be included, but the following platforms aren't included: {string.Join (", ", notIncluded.Select (v => v.AsString ()))}"); - } - - public static void IgnoreIfNotOnMacOS () - { - IgnoreIfNotOn (System.Runtime.InteropServices.OSPlatform.OSX); - } - - public static void IgnoreIfNotOnWindows () - { - IgnoreIfNotOn (System.Runtime.InteropServices.OSPlatform.Windows); - } - - public static void IgnoreIfNotOn (System.Runtime.InteropServices.OSPlatform platform) - { - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform (platform)) - return; - Assert.Ignore ($"This test is only applicable on {platform}"); - } - - public static void IgnoreIfNotXamarinEnabled () - { - if (EnableXamarin) - return; - Assert.Ignore ($"This test is only applicable if Xamarin-specific bits are enabled."); - } - public static string GetTestLibraryDirectory (ApplePlatform platform, bool? simulator = null) { string dir; @@ -1278,3 +1128,4 @@ public static IEnumerable GetUndefinedNativeSymbols (string file, string } } } + diff --git a/tests/common/ConfigurationNUnit.cs b/tests/common/ConfigurationNUnit.cs new file mode 100644 index 000000000000..9e1531be0c84 --- /dev/null +++ b/tests/common/ConfigurationNUnit.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +using NUnit.Framework; + +using Xamarin.Utils; + +#nullable disable // until we get around to fixing this file + +namespace Xamarin.Tests { + static partial class Configuration { + + static string TestAssemblyDirectory { + get { + return TestContext.CurrentContext.WorkDirectory; + } + } + +#if !XAMMAC_TESTS + public static void AssertRuntimeIdentifierAvailable (ApplePlatform platform, string runtimeIdentifier) + { + if (string.IsNullOrEmpty (runtimeIdentifier)) + return; + + if (GetRuntimeIdentifiers (platform).Contains (runtimeIdentifier)) + return; + + Assert.Ignore ($"The runtime identifier {runtimeIdentifier} is not available on {platform}"); + } + + public static void AssertRuntimeIdentifiersAvailable (ApplePlatform platform, string runtimeIdentifiers) + { + if (string.IsNullOrEmpty (runtimeIdentifiers)) + return; + + foreach (var rid in runtimeIdentifiers.Split (new char [] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + AssertRuntimeIdentifierAvailable (platform, rid); + } + + public static void AssertiOS32BitAvailable () + { + if (iOSSupports32BitArchitectures) + return; + Assert.Ignore ($"32-bit iOS support is not available in the current build."); + } +#endif // !XAMMAC_TESTS + + public static void AssertDeviceAvailable () + { + if (include_device) + return; + Assert.Ignore ("This build does not include device support."); + } + + public static void AssertDotNetAvailable () + { + if (include_dotnet) + return; + Assert.Ignore (".NET tests not enabled"); + } + + public static void AssertLegacyXamarinAvailable () + { + if (include_legacy_xamarin) + return; + Assert.Ignore ("Legacy xamarin build not enabled"); + } + + // Calls Assert.Ignore if the given platform isn't included in the current build. + public static void IgnoreIfIgnoredPlatform (ApplePlatform platform) + { + switch (platform) { + case ApplePlatform.iOS: + if (!include_ios) + Assert.Ignore ("iOS is not included in this build"); + break; + case ApplePlatform.TVOS: + if (!include_tvos) + Assert.Ignore ("tvOS is not included in this build"); + break; + case ApplePlatform.WatchOS: + if (!include_watchos) + Assert.Ignore ("watchOS is not included in this build"); +#if NET + if (!include_dotnet_watchos) + Assert.Ignore ("watchOS is not included in this build"); +#endif + + break; + case ApplePlatform.MacOSX: + if (!include_mac) + Assert.Ignore ("macOS is not included in this build"); + break; + case ApplePlatform.MacCatalyst: + if (!include_maccatalyst) + Assert.Ignore ("Mac Catalyst is not included in this build"); + break; + default: + throw new ArgumentOutOfRangeException ($"Unknown platform: {platform}"); + } + } + + // Calls Assert.Ignore if the given platform isn't included in the current build. + public static void IgnoreIfIgnoredPlatform (string platform) + { + switch (platform.ToLower ()) { + case "ios": + case "tvos": + case "watchos": + case "macosx": + case "maccatalyst": + IgnoreIfIgnoredPlatform ((ApplePlatform) Enum.Parse (typeof (ApplePlatform), platform, true)); + break; + case "macos": + IgnoreIfIgnoredPlatform (ApplePlatform.MacOSX); + break; + default: + throw new ArgumentOutOfRangeException ($"Unknown platform: {platform}"); + } + } + + public static bool AnyIgnoredPlatforms (bool dotnet = true) + { + return AnyIgnoredPlatforms (dotnet, out var _); + } + + public static bool AnyIgnoredPlatforms (bool dotnet, out IEnumerable notIncluded) + { + var allPlatforms = GetAllPlatforms (dotnet); + var includedPlatforms = GetIncludedPlatforms (dotnet); + notIncluded = allPlatforms.Where (v => !includedPlatforms.Contains (v)).ToArray (); + return notIncluded.Any (); + } + + public static void IgnoreIfAnyIgnoredPlatforms (bool dotnet = true) + { + if (AnyIgnoredPlatforms (dotnet, out var notIncluded)) + Assert.Ignore ($"This test requires all platforms to be included, but the following platforms aren't included: {string.Join (", ", notIncluded.Select (v => v.AsString ()))}"); + } + + public static void IgnoreIfNotOnMacOS () + { + IgnoreIfNotOn (System.Runtime.InteropServices.OSPlatform.OSX); + } + + public static void IgnoreIfNotOnWindows () + { + IgnoreIfNotOn (System.Runtime.InteropServices.OSPlatform.Windows); + } + + public static void IgnoreIfNotOn (System.Runtime.InteropServices.OSPlatform platform) + { + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform (platform)) + return; + Assert.Ignore ($"This test is only applicable on {platform}"); + } + + public static void IgnoreIfNotXamarinEnabled () + { + if (EnableXamarin) + return; + Assert.Ignore ($"This test is only applicable if Xamarin-specific bits are enabled."); + } + + } +} diff --git a/tests/common/ConfigurationXUnit.cs b/tests/common/ConfigurationXUnit.cs new file mode 100644 index 000000000000..d1555af04e69 --- /dev/null +++ b/tests/common/ConfigurationXUnit.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xamarin.Utils; +using Xunit.Sdk; + +namespace Xamarin.Tests { + + [AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public sealed class PlatformInlineDataAttribute : DataAttribute { + readonly object [] dataValues; + public PlatformInlineDataAttribute (ApplePlatform platform, params object [] parameters) + { + // data values are the join of the platform and all other values passed to the attr + dataValues = parameters.Prepend (platform).ToArray (); + // based on the passed platform and the configuration, decide if we skip the test + switch (platform) { + case ApplePlatform.iOS: + if (!Configuration.include_ios) + Skip = "iOS is not included in this build"; + break; + case ApplePlatform.TVOS: + if (!Configuration.include_tvos) + Skip = "tvOS is not included in this build"; + break; + case ApplePlatform.MacOSX: + if (!Configuration.include_mac) + Skip = "macOS is not included in this build"; + break; + case ApplePlatform.MacCatalyst: + if (!Configuration.include_maccatalyst) + Skip = "Mac Catalyst is not included in this build"; + break; + default: + throw new ArgumentOutOfRangeException ($"Unknown platform: {platform}"); + } + } + + public object [] DataValues { + get { return dataValues; } + } + + public override IEnumerable GetData (MethodInfo testMethod) + { + yield return dataValues; + } + } + + public partial class Configuration { + static string TestAssemblyDirectory { + get { + return Assembly.GetExecutingAssembly ().Location; + } + } + } +} diff --git a/tests/common/ExecutionHelper.cs b/tests/common/ExecutionHelper.cs index 00ee9a318b8f..d0315a82d777 100644 --- a/tests/common/ExecutionHelper.cs +++ b/tests/common/ExecutionHelper.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; -using System.Text.RegularExpressions; - -using NUnit.Framework; using Xamarin.Utils; #nullable disable // until we get around to fixing this file diff --git a/tests/common/mac/project_building.mk b/tests/common/mac/project_building.mk index 628abba9e3d8..da612f0741d3 100644 --- a/tests/common/mac/project_building.mk +++ b/tests/common/mac/project_building.mk @@ -1,5 +1,6 @@ SOURCES = $(TEST_SRC) \ $(TOP)/tests/common/Configuration.cs \ + $(TOP)/tests/common/ConfigurationNUnit.cs \ $(TOP)/tests/common/mac/ProjectTestHelpers.cs \ $(TOP)/tools/common/Driver.cs \ $(TOP)/tools/common/TargetFramework.cs \ diff --git a/tests/dotnet/UnitTests/DotNetUnitTests.csproj b/tests/dotnet/UnitTests/DotNetUnitTests.csproj index 6115afb46443..e56cc641adaa 100644 --- a/tests/dotnet/UnitTests/DotNetUnitTests.csproj +++ b/tests/dotnet/UnitTests/DotNetUnitTests.csproj @@ -20,6 +20,9 @@ external\Configuration.cs + + external\ConfigurationNUnit.cs + external\DotNet.cs diff --git a/tests/generator/generator-tests.csproj b/tests/generator/generator-tests.csproj index 4b177794bf2b..0a39b4fab9c5 100644 --- a/tests/generator/generator-tests.csproj +++ b/tests/generator/generator-tests.csproj @@ -55,6 +55,9 @@ Configuration.cs + + ConfigurationNUnit.cs + Cache.cs diff --git a/tests/introspection/ApiFrameworkTest.cs b/tests/introspection/ApiFrameworkTest.cs index f95e01192736..96e0ef866bf7 100644 --- a/tests/introspection/ApiFrameworkTest.cs +++ b/tests/introspection/ApiFrameworkTest.cs @@ -43,6 +43,9 @@ public bool Skip (string @namespace) // not a framework, largely p/invokes to /usr/lib/libobjc.dylib case "ObjCRuntime": return true; + // no a framework, namespace for the binding attrs for rgen + case "ObjCBindings": + return true; // pinvokes into OpenGL[ES] case "OpenTK": return true; diff --git a/tests/mmptest/mmptest.csproj b/tests/mmptest/mmptest.csproj index a8ae33a8f6ae..addb3187d5b4 100644 --- a/tests/mmptest/mmptest.csproj +++ b/tests/mmptest/mmptest.csproj @@ -57,6 +57,9 @@ Configuration.cs + + ConfigurationNUnit.cs + ErrorHelper.cs diff --git a/tests/monotouch-test/dotnet/macOS/monotouch-test.csproj b/tests/monotouch-test/dotnet/macOS/monotouch-test.csproj index 2636ce5187f9..71ad519b8fb2 100644 --- a/tests/monotouch-test/dotnet/macOS/monotouch-test.csproj +++ b/tests/monotouch-test/dotnet/macOS/monotouch-test.csproj @@ -12,6 +12,9 @@ Configuration.cs + + ConfigurationNUnit.cs + ExecutionHelper.cs diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/Xamarin.MacDev.Tasks.Tests.csproj b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/Xamarin.MacDev.Tasks.Tests.csproj index 23074d2e2d40..33fb783ef82f 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/Xamarin.MacDev.Tasks.Tests.csproj +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/Xamarin.MacDev.Tasks.Tests.csproj @@ -52,6 +52,9 @@ external\Configuration.cs + + external\ConfigurationNUnit.cs + external\DotNet.cs diff --git a/tests/msbuild/Xamarin.MacDev.Tests/Xamarin.MacDev.Tests.csproj b/tests/msbuild/Xamarin.MacDev.Tests/Xamarin.MacDev.Tests.csproj index 771e95f8bcb2..2e130dd64d2a 100644 --- a/tests/msbuild/Xamarin.MacDev.Tests/Xamarin.MacDev.Tests.csproj +++ b/tests/msbuild/Xamarin.MacDev.Tests/Xamarin.MacDev.Tests.csproj @@ -26,6 +26,9 @@ external\Configuration.cs + + external\ConfigurationNUnit.cs + external\ApplePlatform.cs diff --git a/tests/mtouch/mtouchtests.csproj b/tests/mtouch/mtouchtests.csproj index c207827fbe0b..be319512d85d 100644 --- a/tests/mtouch/mtouchtests.csproj +++ b/tests/mtouch/mtouchtests.csproj @@ -44,6 +44,9 @@ Configuration.cs + + ConfigurationNUnit.cs + diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BaseGeneratorWithAnalyzerTestClass.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BaseGeneratorWithAnalyzerTestClass.cs new file mode 100644 index 000000000000..e3df3d973dda --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BaseGeneratorWithAnalyzerTestClass.cs @@ -0,0 +1,35 @@ +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.Macios.Generator.Tests; +using Xunit; + +namespace Microsoft.Macios.Bindings.Analyzer.Tests; + +public class BaseGeneratorWithAnalyzerTestClass : BaseGeneratorTestClass { + + protected Task> RunAnalyzer (T analyzer, Compilation compilation) + where T : DiagnosticAnalyzer + { + var compilationWithAnalyzers = + // run generators on the compilation + RunGeneratorsAndUpdateCompilation (compilation, out _) + // attach analyzers + .WithAnalyzers (ImmutableArray.Create (analyzer)); + return compilationWithAnalyzers.GetAllDiagnosticsAsync (); + } + + protected static void VerifyDiagnosticMessage (Diagnostic diagnostic, string diagnosticId, + DiagnosticSeverity severity, string message) + { + Assert.Equal (diagnosticId, diagnostic.Id); + Assert.Equal (severity, diagnostic.Severity); + Assert.Equal (message, diagnostic.GetMessage ()); + } + protected static void VerifyDiagnosticMessage (Diagnostic diagnostic, string diagnosticId, string message) + { + Assert.Equal (diagnosticId, diagnostic.Id); + Assert.Equal (message, diagnostic.GetMessage ()); + } +} diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs new file mode 100644 index 000000000000..48cba5be3fbd --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Xamarin.Tests; +using Xamarin.Utils; +using Xunit; + +namespace Microsoft.Macios.Bindings.Analyzer.Tests; + +public class BindingTypeSemanticAnalyzerTests : BaseGeneratorWithAnalyzerTestClass { + + [Theory] + [PlatformInlineData (ApplePlatform.iOS)] + [PlatformInlineData (ApplePlatform.TVOS)] + [PlatformInlineData (ApplePlatform.MacOSX)] + [PlatformInlineData (ApplePlatform.MacCatalyst)] + public async Task BindingTypeMustBePartial (ApplePlatform platform) + { + const string inputText = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public class Examples { + } +} +"; + + var compilation = CreateCompilation (nameof (CompareGeneratedCode), platform, inputText); + var diagnostics = await RunAnalyzer (new BindingTypeSemanticAnalyzer (), compilation); + Assert.Single (diagnostics); + // verify the diagnostic message + var location = diagnostics [0].Location; + VerifyDiagnosticMessage (diagnostics [0], BindingTypeSemanticAnalyzer.DiagnosticId, + DiagnosticSeverity.Error, "The binding type 'Test.Examples' must declared as a partial class"); + } +} diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Microsoft.Macios.Bindings.Analyzer.Tests.csproj b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Microsoft.Macios.Bindings.Analyzer.Tests.csproj new file mode 100644 index 000000000000..f508fcca942a --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/Microsoft.Macios.Bindings.Analyzer.Tests.csproj @@ -0,0 +1,27 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + enable + + false + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs new file mode 100644 index 000000000000..1ed459e25ccc --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Xamarin.Tests; +using Xamarin.Utils; +using Xunit; + +namespace Microsoft.Macios.Generator.Tests; + +/// +/// Base class that allows to test the generator. +/// +public class BaseGeneratorTestClass { + protected BindingSourceGeneratorGenerator GeneratorGenerator; + protected CSharpGeneratorDriver _driver; + + public BaseGeneratorTestClass () + { + GeneratorGenerator = new BindingSourceGeneratorGenerator (); + _driver = CSharpGeneratorDriver.Create (GeneratorGenerator); + } + + protected Compilation RunGeneratorsAndUpdateCompilation (Compilation compilation, out ImmutableArray diagnostics) + { + _driver.RunGeneratorsAndUpdateCompilation (compilation, out var updatedCompilation, out diagnostics); + return updatedCompilation; + } + + protected GeneratorDriverRunResult RunGenerators (Compilation compilation) + => _driver.RunGenerators (compilation).GetRunResult (); + + protected Compilation CreateCompilation (string name, ApplePlatform platform, params string [] sources) + { + // get the dotnet bcl and fully load it for the test. + var references = Directory.GetFiles (Configuration.DotNetBclDir, "*.dll") + .Select (assembly => MetadataReference.CreateFromFile (assembly)).ToList (); + // get the dll for the current platform + var targetFramework = TargetFramework.GetTargetFramework (platform, isDotNet: true); + var platformDll = Configuration.GetBaseLibrary (targetFramework); + if (!string.IsNullOrEmpty (platformDll)) { + references.Add (MetadataReference.CreateFromFile (platformDll)); + } else { + throw new InvalidOperationException ($"Could not find platform dll for {platform}"); + } + var trees = sources.Select (s => CSharpSyntaxTree.ParseText (s)); + var options = new CSharpCompilationOptions (OutputKind.NetModule); + return CSharpCompilation.Create (name, trees, references, options); + } + + protected void CompareGeneratedCode (ApplePlatform platform, string className, string inputFileName, string inputText, string outputFileName, string expectedOutputText) + { + // We need to create a compilation with the required source code. + var compilation = CreateCompilation (nameof (CompareGeneratedCode), platform, inputText); + + // Run generators and retrieve all results. + var runResult = RunGenerators (compilation); + + // All generated files can be found in 'RunResults.GeneratedTrees'. + var generatedFileSyntax = runResult.GeneratedTrees.Single (t => t.FilePath.EndsWith ($"{className}.g.cs")); + + // Complex generators should be tested using text comparison. + Assert.Equal (expectedOutputText, generatedFileSyntax.GetText ().ToString (), + ignoreLineEndingDifferences: true); + + } +} diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/BaseTestDataGenerator.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseTestDataGenerator.cs new file mode 100644 index 000000000000..b587edeceac1 --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseTestDataGenerator.cs @@ -0,0 +1,13 @@ +using System.IO; +using System.Runtime.CompilerServices; + +namespace Microsoft.Macios.Generator.Tests; + +public class BaseTestDataGenerator { + public static string ReadFileAsString (string file, [CallerFilePath] string filePath = "") + { + var directoryPath = Path.GetDirectoryName (filePath); + var fullPath = Path.Join (directoryPath, "Data", file); + return File.ReadAllText (fullPath); + } +} diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs new file mode 100644 index 000000000000..a101ccbbd214 --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs @@ -0,0 +1,44 @@ +using System.Linq; +using Microsoft.CodeAnalysis.CSharp; +using Xamarin.Tests; +using Xamarin.Utils; +using Xunit; + +namespace Microsoft.Macios.Generator.Tests; + +// Unit test that ensures that all the generator attributes are correctly added in the compilation initialization +public class BindingSourceGeneratorGeneratorTests : BaseGeneratorTestClass { + + const string SampleBindingType = @" +namespace TestNamespace; + +[BindingType (Name = ""AVAudioPCMBuffer"")] +interface AVAudioPcmBuffer : AVAudioBuffer { +} +"; + + [Theory] + [PlatformInlineData (ApplePlatform.iOS)] + [PlatformInlineData (ApplePlatform.TVOS)] + [PlatformInlineData (ApplePlatform.MacOSX)] + [PlatformInlineData (ApplePlatform.MacCatalyst)] + public void AttributesAreNotPresent (ApplePlatform platform) + { + // We need to create a compilation with the required source code. + var compilation = CreateCompilation (nameof (AttributesAreNotPresent), + platform, SampleBindingType); + + // Run generators and retrieve all results. + var runResult = _driver.RunGenerators (compilation).GetRunResult (); + + // ensure that we do have all the needed attributes present + var expectedGeneratedAttributes = new [] { + "BindingTypeAttribute.g.cs", + }; + + foreach (string generatedAttribute in expectedGeneratedAttributes) { + var generatedFile = runResult.GeneratedTrees.SingleOrDefault (t => t.FilePath.EndsWith (generatedAttribute)); + Assert.Null (generatedFile); + } + } +} diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj b/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj new file mode 100644 index 000000000000..417b2c63d198 --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj @@ -0,0 +1,59 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + enable + + false + + Microsoft.Macios.Generator.Tests + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + external\Configuration.cs + + + external\ConfigurationXUnit.cs + + + external\Profile.cs + + + external\ExecutionHelper.cs + + + external\ApplePlatform.cs + + + external\TargetFramework.cs + + + external\StringUtils.cs + + + external\Execution.cs + + + external\SdkVersions.cs + + + external\Cache.cs + + + + diff --git a/tests/xammac_tests/xammac_tests.csproj b/tests/xammac_tests/xammac_tests.csproj index 76d9f3409811..73d0756eef6c 100644 --- a/tests/xammac_tests/xammac_tests.csproj +++ b/tests/xammac_tests/xammac_tests.csproj @@ -75,6 +75,9 @@ Configuration.cs + + ConfigurationNUnit.cs + PlatformInfo.cs diff --git a/tests/xharness/Jenkins/Jenkins.cs b/tests/xharness/Jenkins/Jenkins.cs index 1cbb0b369728..ae7da1490ad9 100644 --- a/tests/xharness/Jenkins/Jenkins.cs +++ b/tests/xharness/Jenkins/Jenkins.cs @@ -193,6 +193,42 @@ Task PopulateTasksAsync () }; Tasks.Add (runDotNetGenerator); + var buildDotNetRoslynGeneratorProject = new TestProject (TestLabel.Generator, Path.GetFullPath (Path.Combine (HarnessConfiguration.RootDirectory, "rgen", "Microsoft.Macios.Generator.Tests", "Microsoft.Macios.Generator.Tests.csproj"))) { + IsDotNetProject = true, + }; + var buildDotNetRoslynGenerator = new MSBuildTask (jenkins: this, testProject: buildDotNetRoslynGeneratorProject, processManager: processManager) { + TestProject = buildDotNetRoslynGeneratorProject, + SpecifyPlatform = false, + SpecifyConfiguration = false, + Platform = TestPlatform.iOS, + }; + var runDotNetRoslynGenerator = new DotNetTestTask (this, buildDotNetRoslynGenerator, processManager) { + TestProject = buildDotNetRoslynGeneratorProject, + Platform = TestPlatform.iOS, + TestName = "Roslyn Generator tests", + Mode = ".NET", + Ignored = !TestSelection.IsEnabled (TestLabel.Generator) || !TestSelection.IsEnabled (PlatformLabel.Dotnet), + }; + Tasks.Add (runDotNetRoslynGenerator); + + var buildDotNetRoslynAnalyzerProject = new TestProject (TestLabel.Generator, Path.GetFullPath (Path.Combine (HarnessConfiguration.RootDirectory, "rgen", "Microsoft.Macios.Bindings.Analyzer.Tests", "Microsoft.Macios.Bindings.Analyzer.Tests.csproj"))) { + IsDotNetProject = true, + }; + var buildDotNetRoslynAnalyzer = new MSBuildTask (jenkins: this, testProject: buildDotNetRoslynAnalyzerProject, processManager: processManager) { + TestProject = buildDotNetRoslynAnalyzerProject, + SpecifyPlatform = false, + SpecifyConfiguration = false, + Platform = TestPlatform.iOS, + }; + var runDotNetRoslynAnalyzer = new DotNetTestTask (this, buildDotNetRoslynAnalyzer, processManager) { + TestProject = buildDotNetRoslynAnalyzerProject, + Platform = TestPlatform.iOS, + TestName = "Roslyn Analyzer tests", + Mode = ".NET", + Ignored = !TestSelection.IsEnabled (TestLabel.Generator) || !TestSelection.IsEnabled (PlatformLabel.Dotnet), + }; + Tasks.Add (runDotNetRoslynAnalyzer); + var buildDotNetTestsProject = new TestProject (TestLabel.DotnetTest, Path.GetFullPath (Path.Combine (HarnessConfiguration.RootDirectory, "dotnet", "UnitTests", "DotNetUnitTests.csproj"))) { IsDotNetProject = true, };