From da2fc3699abfff1ee2fbd900cde03e47fa182989 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Sat, 2 Dec 2023 12:34:25 -0800 Subject: [PATCH 1/5] Checking generators are running inside valid azure functions project --- sdk/Sdk.Generators/Constants.cs | 4 +- .../ExtensionStartupRunnerGenerator.cs | 5 ++ .../GeneratorExecutionContextExtensions.cs | 23 ++++++ .../FunctionExecutorGenerator.cs | 5 ++ .../FunctionMetadataProviderGenerator.cs | 5 ++ ...Microsoft.Azure.Functions.Worker.Sdk.props | 1 + .../ExtensionStartupRunnerGeneratorTests.cs | 2 +- .../ExtensionStartup/NotGeneratedTests.cs | 49 ++++++++++++ .../FunctionExecutor/NotGeneratedTests.cs | 74 +++++++++++++++++++ .../NotGeneratedTests.cs | 71 ++++++++++++++++++ test/Sdk.Generator.Tests/TestHelpers.cs | 9 ++- 11 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs rename test/Sdk.Generator.Tests/{ => ExtensionStartup}/ExtensionStartupRunnerGeneratorTests.cs (99%) create mode 100644 test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/NotGeneratedTests.cs create mode 100644 test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/NotGeneratedTests.cs diff --git a/sdk/Sdk.Generators/Constants.cs b/sdk/Sdk.Generators/Constants.cs index 68a25a127..0d5bfb61f 100644 --- a/sdk/Sdk.Generators/Constants.cs +++ b/sdk/Sdk.Generators/Constants.cs @@ -5,6 +5,8 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators { internal static class Constants { + internal const string Isolated = "isolated"; + internal static class Languages { internal const string DotnetIsolated = "dotnet-isolated"; @@ -12,8 +14,8 @@ internal static class Languages internal static class BuildProperties { + internal const string FunctionsExecutionModel = "build_property.FunctionsExecutionModel"; internal const string MSBuildTargetFrameworkIdentifier = "build_property.TargetFrameworkIdentifier"; - internal const string MSBuildRootNamespace = "build_property.RootNamespace"; internal const string GeneratedCodeNamespace = "build_property.FunctionsGeneratedCodeNamespace"; internal const string EnableSourceGen = "build_property.FunctionsEnableMetadataSourceGen"; internal const string EnablePlaceholder = "build_property.FunctionsEnableExecutorSourceGen"; diff --git a/sdk/Sdk.Generators/ExtensionStartupRunnerGenerator.cs b/sdk/Sdk.Generators/ExtensionStartupRunnerGenerator.cs index 163627c79..0cda0f75f 100644 --- a/sdk/Sdk.Generators/ExtensionStartupRunnerGenerator.cs +++ b/sdk/Sdk.Generators/ExtensionStartupRunnerGenerator.cs @@ -62,6 +62,11 @@ public class ExtensionStartupRunnerGenerator : ISourceGenerator public void Execute(GeneratorExecutionContext context) { + if (!context.IsRunningInAzureFunctionProject()) + { + return; + } + var extensionStartupTypeNames = GetExtensionStartupTypes(context); if (!extensionStartupTypeNames.Any()) diff --git a/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs b/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs new file mode 100644 index 000000000..8bd489f05 --- /dev/null +++ b/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; + +namespace Microsoft.Azure.Functions.Worker.Sdk.Generators +{ + internal static class GeneratorExecutionContextExtensions + { + /// + /// Returns true if the source generator is running in the context of an "Azure Function" project. + /// + internal static bool IsRunningInAzureFunctionProject(this GeneratorExecutionContext context) + { + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(Constants.BuildProperties.FunctionsExecutionModel, out var value)) + { + return string.Equals(value, Constants.Isolated); + } + + return false; + } + } +} diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs index 1f239e3b5..4cac87c9f 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs @@ -21,6 +21,11 @@ public void Initialize(GeneratorInitializationContext context) public void Execute(GeneratorExecutionContext context) { + if (!context.IsRunningInAzureFunctionProject()) + { + return; + } + if (!ShouldExecuteGeneration(context)) { return; diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.cs index 11736adf8..c21da214d 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.cs @@ -25,6 +25,11 @@ public partial class FunctionMetadataProviderGenerator : ISourceGenerator public void Execute(GeneratorExecutionContext context) { + if (!context.IsRunningInAzureFunctionProject()) + { + return; + } + if (context.SyntaxReceiver is not FunctionMethodSyntaxReceiver receiver || receiver.CandidateMethods.Count == 0) { return; diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props index d7e98339a..123cb7fe1 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props @@ -27,6 +27,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and + -### Microsoft.Azure.Functions.Worker.Sdk 1.16.3 (meta package) +### Microsoft.Azure.Functions.Worker.Sdk 1.16.4-preview1 (meta package) -- Update worker.config generation to accurate worker executable name (#1053) -- Default to optimized function executor. -- Update Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.5 +- Update Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.6-preview1 -### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.1.5 +### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.1.6-preview1 -- Adding support for executing functions from referenced assemblies in the optimized function executor (#2089) -- Update worker.config generation to accurate worker executable name (#1053) -- Fix incorrect value of `ScriptFile` property in function metadata for .Net Framework function apps (#2103) -- Generate valid namespace when root namespace contains `-` (#2097) -- Bug fix for scenarios with `$return` output binding and `HttpTrigger` breaking output-binding rules (#2098) -- Add `CompilerGeneratedAttribute`` to generated code (#2104) +- Avoid executing the source generator outside of an Azure Functions project. (#2119) diff --git a/test/Sdk.Generator.Tests/ExtensionStartupRunnerGeneratorTests.cs b/test/Sdk.Generator.Tests/ExtensionStartup/ExtensionStartupRunnerGeneratorTests.cs similarity index 99% rename from test/Sdk.Generator.Tests/ExtensionStartupRunnerGeneratorTests.cs rename to test/Sdk.Generator.Tests/ExtensionStartup/ExtensionStartupRunnerGeneratorTests.cs index b02cc8302..410fd7284 100644 --- a/test/Sdk.Generator.Tests/ExtensionStartupRunnerGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/ExtensionStartup/ExtensionStartupRunnerGeneratorTests.cs @@ -12,7 +12,7 @@ using Xunit; namespace Microsoft.Azure.Functions.SdkGeneratorTests { - public class ExtensionStartupRunnerGeneratorTests + public partial class ExtensionStartupRunnerGeneratorTests { const string InputCode = """ public class Foo diff --git a/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs b/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs new file mode 100644 index 000000000..adb04014c --- /dev/null +++ b/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Tests.WorkerExtensionsSample; +using Microsoft.Azure.Functions.Worker.Sdk.Generators; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +namespace Microsoft.Azure.Functions.SdkGeneratorTests +{ + public partial class ExtensionStartupRunnerGeneratorTests + { + public sealed class NotGeneratedTests + { + const string InputCode = """ + public class Foo + { + } + """; + + + [Theory] + [InlineData(LanguageVersion.CSharp7_3)] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp9)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.Latest)] + public async Task NotGeneratedWhenNotRunningInAnAzureFunctionsProject(LanguageVersion languageVersion) + { + var referencedExtensionAssemblies = new[] + { + typeof(SampleExtensionStartup).Assembly, + }; + + string? expectedGeneratedFileName = null; + string? expectedOutput = null; + + await TestHelpers.RunTestAsync( + referencedExtensionAssemblies, + InputCode, + expectedGeneratedFileName, + expectedOutput, + languageVersion: languageVersion, + runInsideAzureFunctionProject: false); + } + } + } +} diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/NotGeneratedTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/NotGeneratedTests.cs new file mode 100644 index 000000000..19f119944 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/NotGeneratedTests.cs @@ -0,0 +1,74 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Sdk.Generators; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.Azure.Functions.SdkGeneratorTests +{ + public partial class FunctionExecutorGeneratorTests + { + public sealed class NotGeneratedTests + { + private readonly Assembly[] _referencedAssemblies = new[] + { + typeof(HttpTriggerAttribute).Assembly, + typeof(FunctionAttribute).Assembly, + typeof(LoggingServiceCollectionExtensions).Assembly, + typeof(ServiceProviderServiceExtensions).Assembly, + typeof(ServiceCollection).Assembly, + typeof(ILogger).Assembly, + typeof(IConfiguration).Assembly, + typeof(HostBuilder).Assembly, + typeof(IHostBuilder).Assembly + }; + + [Theory] + [InlineData(LanguageVersion.CSharp7_3)] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp9)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.Latest)] + public async Task NotGeneratedWhenNotRunningInAnAzureFunctionsProject(LanguageVersion languageVersion) + { + string inputCode = """ + using System; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + + namespace MyCompany.MyApp.Functions + { + public static class HttpTriggerSimple + { + [Function(nameof(HttpTriggerSimple))] + public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req) + { + throw new NotImplementedException(); + } + } + } + """; + string? expectedGeneratedFileName = null; + string? expectedOutput = null; + + await TestHelpers.RunTestAsync( + _referencedAssemblies, + inputCode, + expectedGeneratedFileName, + expectedOutput, + languageVersion: languageVersion, + runInsideAzureFunctionProject: false); + } + + } + } +} diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/NotGeneratedTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/NotGeneratedTests.cs new file mode 100644 index 000000000..8c6c087ea --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/NotGeneratedTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Sdk.Generators; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.Azure.Functions.SdkGeneratorTests +{ + public partial class FunctionMetadataProviderGeneratorTests + { + public sealed class NotGeneratedTests + { + private readonly Assembly[] _referencedExtensionAssemblies = new[] + { + typeof(HttpTriggerAttribute).Assembly, typeof(FunctionAttribute).Assembly, + typeof(LoggingServiceCollectionExtensions).Assembly, + typeof(ServiceProviderServiceExtensions).Assembly, typeof(ServiceCollection).Assembly, + typeof(ILogger).Assembly, typeof(IConfiguration).Assembly, typeof(HostBuilder).Assembly, + typeof(IHostBuilder).Assembly + }; + + [Theory] + [InlineData(LanguageVersion.CSharp7_3)] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp9)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.Latest)] + public async Task NotGeneratedWhenNotRunningInAnAzureFunctionsProject(LanguageVersion languageVersion) + { + string inputCode = """ + using System; + using System.Collections.Generic; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + + namespace MyCompany.MyApp.Functions + { + public static class HttpTriggerSimple + { + [Function(nameof(HttpTriggerSimple))] + public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req) + { + throw new NotImplementedException(); + } + } + } + """; + + string? expectedGeneratedFileName = null; + string? expectedOutput = null; + + await TestHelpers.RunTestAsync( + _referencedExtensionAssemblies, + inputCode, + expectedGeneratedFileName, + expectedOutput, + languageVersion: languageVersion, + runInsideAzureFunctionProject: false); + } + } + } +} diff --git a/test/Sdk.Generator.Tests/TestHelpers.cs b/test/Sdk.Generator.Tests/TestHelpers.cs index 4b0aae6f6..296bb8932 100644 --- a/test/Sdk.Generator.Tests/TestHelpers.cs +++ b/test/Sdk.Generator.Tests/TestHelpers.cs @@ -34,7 +34,8 @@ public static Task RunTestAsync( List? expectedDiagnosticResults = null, IDictionary? buildPropertiesDictionary = null, string? generatedCodeNamespace = null, - LanguageVersion? languageVersion = null) where TSourceGenerator : ISourceGenerator, new() + LanguageVersion? languageVersion = null, + bool runInsideAzureFunctionProject = true) where TSourceGenerator : ISourceGenerator, new() { CSharpSourceGeneratorVerifier.Test test = new() { @@ -60,6 +61,12 @@ public static Task RunTestAsync( build_property.FunctionsEnableMetadataSourceGen = {true} build_property.FunctionsGeneratedCodeNamespace = {generatedCodeNamespace ?? "TestProject"}"; + if (runInsideAzureFunctionProject) + { + config += $@" + build_property.FunctionsExecutionModel = isolated"; + } + // Add test specific MSBuild properties. if (buildPropertiesDictionary is not null) { From 1a86b21f1e55cfc389d2d3f098a350eecbf41a31 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Sat, 2 Dec 2023 13:42:20 -0800 Subject: [PATCH 3/5] Minor cleanup --- sdk/Sdk.Generators/Constants.cs | 5 ++++- .../Extensions/GeneratorExecutionContextExtensions.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/Sdk.Generators/Constants.cs b/sdk/Sdk.Generators/Constants.cs index 0d5bfb61f..f7eb7b536 100644 --- a/sdk/Sdk.Generators/Constants.cs +++ b/sdk/Sdk.Generators/Constants.cs @@ -5,7 +5,10 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators { internal static class Constants { - internal const string Isolated = "isolated"; + internal static class ExecutionModel + { + internal const string Isolated = "isolated"; + } internal static class Languages { diff --git a/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs b/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs index 8bd489f05..0d86ba562 100644 --- a/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs +++ b/sdk/Sdk.Generators/Extensions/GeneratorExecutionContextExtensions.cs @@ -14,7 +14,7 @@ internal static bool IsRunningInAzureFunctionProject(this GeneratorExecutionCont { if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(Constants.BuildProperties.FunctionsExecutionModel, out var value)) { - return string.Equals(value, Constants.Isolated); + return string.Equals(value, Constants.ExecutionModel.Isolated); } return false; From a5f12c4a588253971b218e3318f0bee46fc7fed3 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Sat, 2 Dec 2023 13:44:11 -0800 Subject: [PATCH 4/5] Minor cleanup on release notes. --- sdk/release_notes.md | 2 +- test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/release_notes.md b/sdk/release_notes.md index f787588cc..327a8d756 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -10,4 +10,4 @@ ### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.1.6-preview1 -- Avoid executing the source generator outside of an Azure Functions project. (#2119) +- Avoid executing source generators outside of an Azure Functions project. (#2119) diff --git a/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs b/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs index adb04014c..5f1191a01 100644 --- a/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs +++ b/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs @@ -18,7 +18,6 @@ public class Foo } """; - [Theory] [InlineData(LanguageVersion.CSharp7_3)] [InlineData(LanguageVersion.CSharp8)] From 95e3e2410e50d696229700da0057576434f5c0de Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 4 Dec 2023 09:13:46 -0800 Subject: [PATCH 5/5] Added comment to props file about the usage of "FunctionsExecutionModel" property. --- sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props index 123cb7fe1..39145774b 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props @@ -11,6 +11,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and + isolated <_FunctionsSkipCleanOutput>true