diff --git a/sdk/Sdk.Generators/Constants.cs b/sdk/Sdk.Generators/Constants.cs index 68a25a127..f7eb7b536 100644 --- a/sdk/Sdk.Generators/Constants.cs +++ b/sdk/Sdk.Generators/Constants.cs @@ -5,6 +5,11 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators { internal static class Constants { + internal static class ExecutionModel + { + internal const string Isolated = "isolated"; + } + internal static class Languages { internal const string DotnetIsolated = "dotnet-isolated"; @@ -12,8 +17,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..0d86ba562 --- /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.ExecutionModel.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.Generators/Sdk.Generators.csproj b/sdk/Sdk.Generators/Sdk.Generators.csproj index cbe6ac94e..eafd7104a 100644 --- a/sdk/Sdk.Generators/Sdk.Generators.csproj +++ b/sdk/Sdk.Generators/Sdk.Generators.csproj @@ -10,8 +10,8 @@ false true 1 - 5 - + 6 + -preview1 true diff --git a/sdk/Sdk/Sdk.csproj b/sdk/Sdk/Sdk.csproj index c6aee59b3..cc4333b22 100644 --- a/sdk/Sdk/Sdk.csproj +++ b/sdk/Sdk/Sdk.csproj @@ -2,7 +2,8 @@ 16 - 3 + 4 + -preview1 netstandard2.0;net472 Microsoft.Azure.Functions.Worker.Sdk This package provides development time support for the Azure Functions .NET Worker. diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.props index d7e98339a..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 @@ -27,6 +28,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 source generators 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..5f1191a01 --- /dev/null +++ b/test/Sdk.Generator.Tests/ExtensionStartup/NotGeneratedTests.cs @@ -0,0 +1,48 @@ +// 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) {