Skip to content

Commit 4e9f137

Browse files
authored
Skip writing functions.metadata when using source gen (#2974)
* Skip writing functions.metadata when using source gen * Update SDK tests * update release notes * Fix tests, allow for manual override of writing functions.metadata * Update tests for force write metadata * Fix copy condition, refactor tests * FIx IDisposable * Fix restore with rid * Fix bad merge * Restore separately for test
1 parent 7c348d1 commit 4e9f137

12 files changed

+286
-216
lines changed

sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
4848
<FunctionsEnablePlaceholder Condition="'$(FunctionsEnablePlaceholder)' == ''">false</FunctionsEnablePlaceholder>
4949
<FunctionsEnableWorkerIndexing Condition="'$(FunctionsEnableWorkerIndexing)' == ''">true</FunctionsEnableWorkerIndexing>
5050
<FunctionsEnableMetadataSourceGen>$(FunctionsEnableWorkerIndexing)</FunctionsEnableMetadataSourceGen>
51-
<FunctionsAutoRegisterGeneratedMetadataProvider>$(FunctionsEnableWorkerIndexing)</FunctionsAutoRegisterGeneratedMetadataProvider>
51+
<FunctionsAutoRegisterGeneratedMetadataProvider>$(FunctionsEnableWorkerIndexing)</FunctionsAutoRegisterGeneratedMetadataProvider>
52+
<FunctionsWriteMetadataJson Condition="'$(FunctionsWriteMetadataJson)' == '' AND '$(FunctionsEnableMetadataSourceGen)' == 'true'">false</FunctionsWriteMetadataJson>
53+
<FunctionsWriteMetadataJson Condition="'$(FunctionsWriteMetadataJson)' == ''">true</FunctionsWriteMetadataJson>
5254

5355
<FunctionsEnableExecutorSourceGen Condition="'$(FunctionsEnableExecutorSourceGen)' == ''">true</FunctionsEnableExecutorSourceGen>
5456
<FunctionsAutoRegisterGeneratedFunctionsExecutor Condition="'$(FunctionsAutoRegisterGeneratedFunctionsExecutor)' == ''">true</FunctionsAutoRegisterGeneratedFunctionsExecutor>
@@ -184,9 +186,10 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
184186
ReferencePaths="@(ReferencePath)"
185187
ExtensionsCsProjFilePath="$(ExtensionsCsProj)"
186188
AzureFunctionsVersion="$(AzureFunctionsVersion)"
189+
WriteMetadataFile="$(FunctionsWriteMetadataJson)"
187190
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
188191
TargetFrameworkVersion="$(TargetFrameworkVersion)"
189-
OutputPath="$(IntermediateOutputPath)"/>
192+
OutputPath="$(IntermediateOutputPath)" />
190193
</Target>
191194

192195
<!-- Generate worker.config.json. -->
@@ -240,7 +243,8 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
240243

241244
<!-- Hook all these files into CopyToOutputDirectory by appending to _NoneWithTargetPath. -->
242245
<ItemGroup>
243-
<_FunctionsAdditionalFile Include="$(_FunctionsMetadataPath);$(_FunctionsWorkerConfigPath);$(_FunctionsIntermediateExtensionUpdatedJsonPath)" />
246+
<_FunctionsAdditionalFile Include="$(_FunctionsMetadataPath)" Condition="'$(FunctionsWriteMetadataJson)' == 'true'" />
247+
<_FunctionsAdditionalFile Include="$(_FunctionsWorkerConfigPath);$(_FunctionsIntermediateExtensionUpdatedJsonPath)" />
244248
<_FunctionsAdditionalFile Include="$(_FunctionsMetadataLoaderExtensionFile)" SubPath="$(_FunctionsExtensionsDirectory)/" />
245249
<_NoneWithTargetPath Include="@(_FunctionsAdditionalFile)"
246250
TargetPath="%(_FunctionsAdditionalFile.SubPath)%(Filename)%(Extension)"

sdk/Sdk/Tasks/GenerateFunctionMetadata.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public class GenerateFunctionMetadata : Task
2626

2727
public string? ExtensionsCsProjFilePath { get; set; }
2828

29+
public bool WriteMetadataFile { get; set; } = true;
30+
2931
[Required]
3032
public ITaskItem[]? ReferencePaths { get; set; }
3133

@@ -67,11 +69,18 @@ public override bool Execute()
6769

6870
private void WriteMetadataWithRetry(IEnumerable<SdkFunctionMetadata> functions)
6971
{
72+
if (!WriteMetadataFile)
73+
{
74+
Log.LogMessage("Skipping writing function metadata file.");
75+
return;
76+
}
77+
7078
int attempt = 0;
7179
while (attempt < 10)
7280
{
7381
try
7482
{
83+
Log.LogMessage($"Writing function metadata to {OutputPath} directory.");
7584
FunctionMetadataJsonWriter.WriteMetadata(functions, OutputPath!);
7685
break;
7786
}

sdk/release_notes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
- My change description (#PR/#issue)
55
-->
66

7-
### Microsoft.Azure.Functions.Worker.Sdk 2.0.5
7+
### Microsoft.Azure.Functions.Worker.Sdk <version>
88

9-
- Address issue with design time build producing an error when project had not yet been built yet. (#3081)
9+
- Build no longer generates `functions.metadata` if source-generated metadata provider is enabled. (#2974)
1010

1111
### Microsoft.Azure.Functions.Worker.Sdk.Generators <version>
1212

test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ public async Task HttpTriggerFunctions_WithCancellationToken_BehaveAsExpected(st
3737
await TestUtility.RetryAsync(() =>
3838
{
3939
invocationStartLog = _fixture.TestLogs.CoreToolsLogs.Where(p => p.Contains($"Executing 'Functions.{functionName}'"));
40-
return Task.FromResult(invocationStartLog.Count() >= 1);
40+
return Task.FromResult(invocationStartLog.Any());
4141
});
4242

43-
// The task should be cancelled before it completes, mimicing a client closing the connection.
43+
// The task should be cancelled before it completes, mimicking a client closing the connection.
4444
// This should lead to the worker getting an InvocationCancel request from the functions host
4545
cts.Cancel();
4646
await Assert.ThrowsAsync<TaskCanceledException>(async () => await task);
@@ -49,13 +49,13 @@ await TestUtility.RetryAsync(() =>
4949
await TestUtility.RetryAsync(() =>
5050
{
5151
invocationEndLog = _fixture.TestLogs.CoreToolsLogs.Where(p => p.Contains($"Executed 'Functions.{functionName}'"));
52-
return Task.FromResult(invocationEndLog.Count() >= 1);
52+
return Task.FromResult(invocationEndLog.Any());
5353
});
5454

5555
Assert.Contains(_fixture.TestLogs.CoreToolsLogs, log => log.Contains(expectedMessage, StringComparison.OrdinalIgnoreCase));
5656

5757
// TODO: 2/3 of the test invocations will fail until the host with the ForwarderProxy fix is released - uncomment this line when the fix is released
58-
Assert.NotEqual(null, invocationResult); // just here to 'use' invocationResult to avoid a warning.
58+
Assert.NotNull(invocationResult); // just here to 'use' invocationResult to avoid a warning.
5959
// Assert.Contains(_fixture.TestLogs.CoreToolsLogs, log => log.Contains($"'Functions.{functionName}' ({invocationResult}", StringComparison.OrdinalIgnoreCase));
6060
}
6161

test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ namespace Microsoft.Azure.Functions.Tests.E2ETests
1717
{
1818
public class FunctionAppFixture : IAsyncLifetime
1919
{
20+
private readonly string _testApp = Constants.TestAppNames.E2EApp;
2021
private readonly ILogger _logger;
2122
private bool _disposed;
2223
private Process _funcProcess;
2324
private JobObjectRegistry _jobObjectRegistry;
24-
private string _testApp = Constants.TestAppNames.E2EApp;
2525

2626
public FunctionAppFixture(IMessageSink messageSink)
2727
{
@@ -32,7 +32,8 @@ public FunctionAppFixture(IMessageSink messageSink)
3232
_logger = loggerFactory.CreateLogger<FunctionAppFixture>();
3333
}
3434

35-
internal FunctionAppFixture(IMessageSink messageSink, string testApp) : this(messageSink)
35+
internal FunctionAppFixture(IMessageSink messageSink, string testApp)
36+
: this(messageSink)
3637
{
3738
_testApp = testApp;
3839
}

test/Sdk.E2ETests/InnerBuildTests.cs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4+
using System;
45
using System.IO;
56
using System.Threading.Tasks;
67
using Newtonsoft.Json.Linq;
@@ -9,25 +10,27 @@
910

1011
namespace Microsoft.Azure.Functions.Sdk.E2ETests
1112
{
12-
public class InnerBuildTests
13+
public sealed class InnerBuildTests(ITestOutputHelper testOutputHelper) : IDisposable
1314
{
14-
private readonly ITestOutputHelper _testOutputHelper;
15+
private readonly ProjectBuilder _builder = new(
16+
testOutputHelper,
17+
Path.Combine(TestUtility.TestResourcesProjectsRoot, "FunctionApp01", "FunctionApp01.csproj"));
1518

16-
public InnerBuildTests(ITestOutputHelper testOutputHelper)
19+
[Theory]
20+
[InlineData("", false)]
21+
[InlineData("-p:FunctionsEnableWorkerIndexing=true", false)]
22+
[InlineData("-p:FunctionsEnableWorkerIndexing=true -p:FunctionsWriteMetadataJson=false", false)]
23+
[InlineData("-p:FunctionsEnableWorkerIndexing=true -p:FunctionsWriteMetadataJson=true", true)]
24+
[InlineData("-p:FunctionsEnableWorkerIndexing=false", true)]
25+
[InlineData("-p:FunctionsEnableWorkerIndexing=false -p:FunctionsWriteMetadataJson=false", false)]
26+
[InlineData("-p:FunctionsEnableWorkerIndexing=false -p:FunctionsWriteMetadataJson=true", true)]
27+
public async Task Build_ScansReferences(string parameters, bool metadataGenerated)
1728
{
18-
_testOutputHelper = testOutputHelper;
19-
}
20-
21-
[Fact]
22-
public async Task Build_ScansReferences()
23-
{
24-
string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Build_ScansReferences));
25-
string projectFileDirectory = Path.Combine(TestUtility.TestResourcesProjectsRoot, "FunctionApp01", "FunctionApp01.csproj");
26-
27-
await TestUtility.RestoreAndBuildProjectAsync(projectFileDirectory, outputDir, null, _testOutputHelper);
29+
await _builder.RestoreAsync();
30+
await _builder.BuildAsync(parameters, restore: false);
2831

2932
// Verify extensions.json contents
30-
string extensionsJsonPath = Path.Combine(outputDir, "extensions.json");
33+
string extensionsJsonPath = Path.Combine(_builder.OutputPath, "extensions.json");
3134
Assert.True(File.Exists(extensionsJsonPath));
3235

3336
JToken extensionsJsonContents = JObject.Parse(File.ReadAllText(extensionsJsonPath));
@@ -54,8 +57,13 @@ public async Task Build_ScansReferences()
5457
Assert.True(JToken.DeepEquals(expectedExtensionsJson, extensionsJsonContents));
5558

5659
// Verify functions.metadata contents
57-
string functionsMetadataPath = Path.Combine(outputDir, "functions.metadata");
58-
Assert.True(File.Exists(functionsMetadataPath));
60+
string functionsMetadataPath = Path.Combine(_builder.OutputPath, "functions.metadata");
61+
Assert.Equal(metadataGenerated, File.Exists(functionsMetadataPath));
62+
63+
if (!metadataGenerated)
64+
{
65+
return;
66+
}
5967

6068
JToken functionsMetadataContents = JArray.Parse(File.ReadAllText(functionsMetadataPath));
6169
JToken expectedFunctionsMetadata = JArray.Parse(@"[
@@ -115,5 +123,7 @@ public async Task Build_ScansReferences()
115123

116124
Assert.True(JToken.DeepEquals(expectedFunctionsMetadata, functionsMetadataContents));
117125
}
126+
127+
public void Dispose() => _builder.Dispose();
118128
}
119129
}

test/Sdk.E2ETests/ProcessWrapper.cs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,27 @@
66
using System.Text;
77
using System.Threading;
88
using System.Threading.Tasks;
9-
using Xunit.Abstractions;
109

1110
namespace Microsoft.Azure.Functions.Sdk.E2ETests
1211
{
13-
public class ProcessWrapper
12+
public static class ProcessWrapper
1413
{
15-
16-
public async Task<int?> RunProcess(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null)
14+
public static async Task<int?> RunProcessAsync(
15+
string fileName, string arguments, string workingDirectory = null, Action<string> log = null)
1716
{
18-
return await RunProcessInternal(fileName, arguments, workingDirectory, testOutputHelper);
17+
return await RunProcessInternalAsync(fileName, arguments, workingDirectory, log);
1918
}
2019

21-
public async Task<Tuple<int?, string>> RunProcessForOutput(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null)
20+
public static async Task<Tuple<int?, string>> RunProcessForOutputAsync(
21+
string fileName, string arguments, string workingDirectory = null, Action<string> log = null)
2222
{
2323
StringBuilder processOutputStringBuilder = new StringBuilder();
24-
var exitCode = await RunProcessInternal(fileName, arguments, workingDirectory, testOutputHelper, processOutputStringBuilder);
24+
var exitCode = await RunProcessInternalAsync(fileName, arguments, workingDirectory, log, processOutputStringBuilder);
2525
return new Tuple<int?,string>(exitCode, processOutputStringBuilder.ToString());
2626
}
2727

28-
private async Task<int?> RunProcessInternal(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null, StringBuilder processOutputBuilder = null)
28+
private static async Task<int?> RunProcessInternalAsync(
29+
string fileName, string arguments, string workingDirectory = null, Action<string> log = null, StringBuilder processOutputBuilder = null)
2930
{
3031

3132
SemaphoreSlim processExitSemaphore = new SemaphoreSlim(0, 1);
@@ -53,23 +54,17 @@ public class ProcessWrapper
5354
{
5455
if (o.Data != null)
5556
{
56-
testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Error: {o.Data}");
57-
if (processOutputBuilder != null)
58-
{
59-
processOutputBuilder.AppendLine(o.Data);
60-
}
57+
log?.Invoke($"[{DateTime.UtcNow:O}] Error: {o.Data}");
58+
processOutputBuilder?.AppendLine(o.Data);
6159
}
6260
};
6361

6462
testProcess.OutputDataReceived += (s, o) =>
6563
{
6664
if (o.Data != null)
6765
{
68-
testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] {o.Data}");
69-
if (processOutputBuilder != null)
70-
{
71-
processOutputBuilder.AppendLine(o.Data);
72-
}
66+
log?.Invoke($"[{DateTime.UtcNow:O}] {o.Data}");
67+
processOutputBuilder?.AppendLine(o.Data);
7368
}
7469
};
7570

@@ -81,7 +76,7 @@ public class ProcessWrapper
8176
int wait = 3 * 60 * 1000;
8277
if (!await processExitSemaphore.WaitAsync(wait))
8378
{
84-
testOutputHelper?.WriteLine($"Process '{testProcess.Id}' did not exit in {wait}ms.");
79+
log?.Invoke($"Process '{testProcess.Id}' did not exit in {wait}ms.");
8580
testProcess.Kill();
8681
}
8782

test/Sdk.E2ETests/ProjectBuilder.cs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.IO;
7+
using System.Runtime.CompilerServices;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using Xunit;
11+
using Xunit.Abstractions;
12+
13+
namespace Microsoft.Azure.Functions.Sdk.E2ETests
14+
{
15+
public sealed class ProjectBuilder(ITestOutputHelper logger, string project) : IDisposable
16+
{
17+
#if DEBUG
18+
public const string Configuration = "Debug";
19+
#elif RELEASE
20+
public const string Configuration = "Release";
21+
#endif
22+
public static readonly string LocalPackages = Path.Combine(TestUtility.PathToRepoRoot, "local");
23+
public static readonly string SrcRoot = Path.Combine(TestUtility.PathToRepoRoot, "src");
24+
public static readonly string SdkSolutionRoot = Path.Combine(TestUtility.PathToRepoRoot, "sdk");
25+
public static readonly string SdkProjectRoot = Path.Combine(SdkSolutionRoot, "Sdk");
26+
public static readonly string DotNetExecutable = "dotnet";
27+
public static readonly string SdkVersion = "99.99.99-test";
28+
public static readonly string SdkBuildProj = Path.Combine(TestUtility.PathToRepoRoot, "build", "Sdk.slnf");
29+
public static readonly string NuGetOrgPackages = "https://api.nuget.org/v3/index.json";
30+
31+
private static Task _initialization;
32+
private static object _sync;
33+
34+
private readonly TempDirectory _tempDirectory = new();
35+
36+
public string OutputPath => _tempDirectory.Path;
37+
38+
public async Task RestoreAsync()
39+
{
40+
await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync);
41+
logger.WriteLine("Restoring...");
42+
string dotnetArgs = $"restore {project} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}";
43+
Stopwatch stopwatch = Stopwatch.StartNew();
44+
int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine);
45+
Assert.True(exitCode.HasValue && exitCode.Value == 0);
46+
logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)");
47+
}
48+
49+
public async Task BuildAsync(string additionalParams = null, bool restore = true)
50+
{
51+
await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync);
52+
53+
Stopwatch stopwatch = Stopwatch.StartNew();
54+
logger.WriteLine("Building...");
55+
string dotnetArgs = $"build {project} -c {Configuration} -o {OutputPath} -p:SdkVersion={SdkVersion} {additionalParams}";
56+
57+
if (!restore)
58+
{
59+
dotnetArgs += " --no-restore";
60+
}
61+
62+
if (Debugger.IsAttached)
63+
{
64+
dotnetArgs += " -bl";
65+
}
66+
67+
int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine);
68+
Assert.True(exitCode.HasValue && exitCode.Value == 0);
69+
logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)");
70+
}
71+
72+
public async Task PublishAsync(string additionalParams = null, bool restore = true)
73+
{
74+
await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync);
75+
76+
Stopwatch stopwatch = Stopwatch.StartNew();
77+
logger.WriteLine($"Publishing...");
78+
string dotnetArgs = $"publish {project} -c {Configuration} -o {OutputPath} -p:SdkVersion={SdkVersion} {additionalParams}";
79+
80+
if (!restore)
81+
{
82+
dotnetArgs += " --no-restore";
83+
}
84+
85+
if (Debugger.IsAttached)
86+
{
87+
dotnetArgs += " -bl";
88+
}
89+
90+
int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine);
91+
Assert.True(exitCode.HasValue && exitCode.Value == 0);
92+
logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)");
93+
}
94+
95+
private async Task InitializeAsync()
96+
{
97+
logger.WriteLine($"Packing {SdkBuildProj} with version {SdkVersion}");
98+
string arguments = $"pack {SdkBuildProj} -c {Configuration} -o {LocalPackages} -p:Version={SdkVersion}";
99+
100+
int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, arguments, SrcRoot, logger.WriteLine);
101+
Assert.True(exitCode.HasValue && exitCode.Value == 0);
102+
}
103+
104+
public void Dispose() => _tempDirectory.Dispose();
105+
}
106+
}

0 commit comments

Comments
 (0)