Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@
### Microsoft.Azure.Functions.Worker.Grpc <version>

- <entry>

### Microsoft.Azure.Functions.Worker.Sdk 1.13.0-preview1
Comment thread
jviau marked this conversation as resolved.
Outdated

- Improve incremental build support for worker extension project inner build [#1749](https://github.com/Azure/azure-functions-dotnet-worker/pull/1749)
- Now builds to intermediate output path
- Avoids disk writes for generated .csproj and file copies if nothing has changed
27 changes: 17 additions & 10 deletions sdk/Sdk/ExtensionsCsprojGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
{
internal class ExtensionsCsprojGenerator
{
private const string ExtensionsProjectName = "WorkerExtensions.csproj";
internal const string ExtensionsProjectName = "WorkerExtensions.csproj";

private readonly IDictionary<string, string> _extensions;
private readonly string _outputPath;
Expand All @@ -31,9 +31,23 @@ public void Generate()
{
var extensionsCsprojFilePath = Path.Combine(_outputPath, ExtensionsProjectName);

RecreateDirectory(_outputPath);
string csproj = GetCsProjContent();

// Incremental build support: write the new csproj only if contents have changed.
// By keeping one from a previous build around, our restore & build has a chance
// to follow incremental build and no-op if nothing needs to be done.
if (File.Exists(extensionsCsprojFilePath))
{
string existing = File.ReadAllText(extensionsCsprojFilePath);
if (string.Equals(csproj, existing, StringComparison.Ordinal))
{
// Up to date, nothing to do.
return;
}
}

WriteExtensionsCsProj(extensionsCsprojFilePath);
RecreateDirectory(_outputPath);
File.WriteAllText(extensionsCsprojFilePath, csproj);
}

private void RecreateDirectory(string directoryPath)
Expand All @@ -46,13 +60,6 @@ private void RecreateDirectory(string directoryPath)
Directory.CreateDirectory(directoryPath);
}

private void WriteExtensionsCsProj(string filePath)
{
string csprojContent = GetCsProjContent();

File.WriteAllText(filePath, csprojContent);
}

internal string GetCsProjContent()
{
string extensionReferences = GetExtensionReferences();
Expand Down
3 changes: 2 additions & 1 deletion sdk/Sdk/Sdk.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<MinorProductVersion>12</MinorProductVersion>
<MinorProductVersion>13</MinorProductVersion>
<PatchProductVersion>0</PatchProductVersion>
<VersionSuffix>-preview1</VersionSuffix>
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
<PackageId>Microsoft.Azure.Functions.Worker.Sdk</PackageId>
<Description>This package provides development time support for the Azure Functions .NET Worker.</Description>
Expand Down
65 changes: 33 additions & 32 deletions sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,17 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<Error Condition="'$(_IsFunctionsSdkBuild)' == 'true'" Text="Microsoft.NET.Sdk.Functions package is meant to be used with in-proc function apps. Please remove the reference to this package in isolated function apps."/>
</Target>

<Target Name="_FunctionsPostBuild" AfterTargets="AfterBuild" DependsOnTargets="_FunctionsPostBuildNetFx;_FunctionsPostBuildNetApp"/>
<!--.NET Framework post build-->
<Target Name="_FunctionsPostBuildNetFx" Condition="$(TargetFrameworkIdentifier) == '.NETFramework'">
<Target Name="_FunctionsPostBuild" AfterTargets="AfterBuild" DependsOnTargets="_FunctionsGetWorkerExtensionPath;_FunctionsPostBuildNetFx;_FunctionsPostBuildNetApp"/>

<Target Name="_FunctionsGetWorkerExtensionPath">
<PropertyGroup>
<OutputFile>$(TargetDir)\worker.config.json</OutputFile>
<ExtensionsCsProjFilePath>$([System.IO.Path]::Combine($([System.IO.Path]::GetTempPath()), $([System.IO.Path]::GetRandomFileName())))</ExtensionsCsProjFilePath>
<_FunctionsWorkerConfigOutputFile>$(TargetDir)\worker.config.json</_FunctionsWorkerConfigOutputFile>
<ExtensionsCsProjFilePath Condition="'$(ExtensionsCsProjFilePath)' == ''">$([System.IO.Path]::GetFullPath($(IntermediateOutputPath)WorkExtensions))</ExtensionsCsProjFilePath>
</PropertyGroup>
</Target>

<!--.NET Framework post build-->
<Target Name="_FunctionsPostBuildNetFx" Condition="$(TargetFrameworkIdentifier) == '.NETFramework'">
<GenerateFunctionMetadata
AssemblyPath="$(TargetDir)$(AssemblyName).exe"
ReferencePaths="@(ReferencePath)"
Expand All @@ -84,24 +87,20 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
Condition="!$(FunctionsEnablePlaceholder)"
SourceFiles="$(_FunctionsMetadataLoaderExtensionFile)"
DestinationFolder="$(ExtensionsCsProjFilePath)\buildout"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="true" />

<WriteLinesToFile
File="$(OutputFile)"
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile))
.Replace('$functionExe$', '{WorkerRoot}$(TargetName).exe')
.Replace('$functionWorker$', '$(TargetName).exe')
.Replace('$enableWorkerIndexing$', '$(FunctionsEnableWorkerIndexing)'))"
Overwrite="true" />
File="$(_FunctionsWorkerConfigOutputFile)"
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile))
.Replace('$functionExe$', '{WorkerRoot}$(TargetName).exe')
.Replace('$functionWorker$', '$(TargetName).exe')
.Replace('$enableWorkerIndexing$', '$(FunctionsEnableWorkerIndexing)'))"
Overwrite="true" />
</Target>

<!--.NET/.NET Core post build-->
<Target Name="_FunctionsPostBuildNetApp" Condition="$(TargetFrameworkIdentifier) != '.NETFramework'">
<PropertyGroup>
<OutputFile>$(TargetDir)\worker.config.json</OutputFile>
<ExtensionsCsProjFilePath>$([System.IO.Path]::Combine($([System.IO.Path]::GetTempPath()), $([System.IO.Path]::GetRandomFileName())))</ExtensionsCsProjFilePath>
</PropertyGroup>

<GenerateFunctionMetadata
AssemblyPath="$(TargetDir)$(AssemblyName).dll"
ReferencePaths="@(ReferencePath)"
Expand All @@ -110,31 +109,33 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
TargetFrameworkVersion="$(TargetFrameworkVersion)"
OutputPath="$(TargetDir)"/>

<!-- Do not copy _FunctionsMetadataLoaderExtensionFile when in placeholder mode, as we don't want the FunctionMetadataLoader entry in extensions.json-->
<Copy
Condition="!$(FunctionsEnablePlaceholder)"
SourceFiles="$(_FunctionsMetadataLoaderExtensionFile)"
DestinationFolder="$(ExtensionsCsProjFilePath)\buildout"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="true" />

<WriteLinesToFile
Condition="$(SelfContained)"
File="$(OutputFile)"
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile))
.Replace('$functionExe$', '{WorkerRoot}$(TargetName)')
.Replace('$functionWorker$', '$(TargetName).dll')
.Replace('$enableWorkerIndexing$', '$(FunctionsEnableWorkerIndexing)'))"
Overwrite="true" />
Condition="$(SelfContained)"
File="$(_FunctionsWorkerConfigOutputFile)"
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile))
.Replace('$functionExe$', '{WorkerRoot}$(TargetName)')
.Replace('$functionWorker$', '$(TargetName).dll')
.Replace('$enableWorkerIndexing$', '$(FunctionsEnableWorkerIndexing)'))"
Overwrite="true" />


<WriteLinesToFile
Condition="!$(SelfContained)"
File="$(OutputFile)"
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile))
.Replace('$functionExe$', 'dotnet')
.Replace('$functionWorker$', '$(TargetName).dll')
.Replace('$enableWorkerIndexing$', '$(FunctionsEnableWorkerIndexing)'))"
Overwrite="true" />
Condition="!$(SelfContained)"
File="$(_FunctionsWorkerConfigOutputFile)"
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile))
.Replace('$functionExe$', 'dotnet')
.Replace('$functionWorker$', '$(TargetName).dll')
.Replace('$enableWorkerIndexing$', '$(FunctionsEnableWorkerIndexing)'))"
Overwrite="true" />

</Target>

Expand All @@ -153,8 +154,8 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<ExtensionRuntimeBinaries Include="$(ExtensionsCsProjFilePath)\buildout\runtimes\**\*.*" />
</ItemGroup>

<Copy SourceFiles="@(ExtensionBinaries)" DestinationFolder="$(TargetDir)\.azurefunctions\%(RecursiveDir)" />
<Copy SourceFiles="@(ExtensionRuntimeBinaries)" DestinationFolder="$(TargetDir)\.azurefunctions\runtimes\%(RecursiveDir)" />
<Copy SourceFiles="@(ExtensionBinaries)" DestinationFolder="$(TargetDir)\.azurefunctions\%(RecursiveDir)" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(ExtensionRuntimeBinaries)" DestinationFolder="$(TargetDir)\.azurefunctions\runtimes\%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>

<Target Name ="_WorkerExtensionsBuild" AfterTargets="_WorkerExtensionsRestore">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,55 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Azure.Functions.Worker.Sdk;
using Xunit;

namespace Microsoft.Azure.Functions.SdkTests
{
public class ExtensionsCsProjGeneratorTests
{
[Fact]
public void GetCsProjContent_Succeeds_functions_v3()
public enum FuncVersion
{
V3,
V4,
}

[Theory]
[InlineData(FuncVersion.V3)]
[InlineData(FuncVersion.V4)]
public void GetCsProjContent_Succeeds(FuncVersion version)
{
var generator = GetGenerator(version);
string actual = generator.GetCsProjContent().Replace("\r\n", "\n");
string expected = ExpectedCsproj(version).Replace("\r\n", "\n");
Assert.Equal(expected, actual);
}

[Theory]
[InlineData(FuncVersion.V3)]
[InlineData(FuncVersion.V4)]
public void GetCsProjContent_IncrementalSupport(FuncVersion version)
{
DateTime RunGenerate(string subPath)
{
var generator = GetGenerator(version, subPath);
generator.Generate();

var csproj = new FileInfo(Path.Combine(subPath, ExtensionsCsprojGenerator.ExtensionsProjectName));
return csproj.LastWriteTimeUtc;
}

string subPath = Guid.NewGuid().ToString();
DateTime firstRun = RunGenerate(subPath);
DateTime secondRun = RunGenerate(subPath);

Assert.Equal(firstRun, secondRun);
}

static ExtensionsCsprojGenerator GetGenerator(FuncVersion version, string subPath = "")
{
IDictionary<string, string> extensions = new Dictionary<string, string>
{
Expand All @@ -19,13 +58,22 @@ public void GetCsProjContent_Succeeds_functions_v3()
{ "Microsoft.Azure.WebJobs.Extensions", "2.0.0" },
};

var generator = new ExtensionsCsprojGenerator(extensions, "", "v3", Constants.NetCoreApp, Constants.NetCoreVersion31);

string actualCsproj = generator.GetCsProjContent().Replace("\r\n", "\n");

Assert.Equal(ExpectedCsProjV3(), actualCsproj);
return version switch
{
FuncVersion.V3 => new ExtensionsCsprojGenerator(extensions, subPath, "v3", Constants.NetCoreApp, Constants.NetCoreVersion31),
FuncVersion.V4 => new ExtensionsCsprojGenerator(extensions, subPath, "v4", Constants.NetCoreApp, Constants.NetCoreVersion6),
_ => throw new ArgumentOutOfRangeException(nameof(version)),
};
}

private static string ExpectedCsproj(FuncVersion version)
=> version switch
{
FuncVersion.V3 => ExpectedCsProjV3(),
FuncVersion.V4 => ExpectedCsProjV4(),
_ => throw new ArgumentOutOfRangeException(nameof(version)),
};

private static string ExpectedCsProjV3()
{
return @"
Expand All @@ -52,27 +100,10 @@ private static string ExpectedCsProjV3()
</ItemGroup>
</Project>
";
}

[Fact]
public void GetCsProjContent_Succeeds_functions_v4()
{
IDictionary<string, string> extensions = new Dictionary<string, string>
{
{ "Microsoft.Azure.WebJobs.Extensions.Storage", "4.0.3" },
{ "Microsoft.Azure.WebJobs.Extensions.Http", "3.0.0" },
{ "Microsoft.Azure.WebJobs.Extensions", "2.0.0" },
};

var generator = new ExtensionsCsprojGenerator(extensions, "", "v4", Constants.NetCoreApp, Constants.NetCoreVersion6);

string actualCsproj = generator.GetCsProjContent().Replace("\r\n", "\n");

Assert.Equal(ExpectedCsProjV4(), actualCsproj);
}

private static string ExpectedCsProjV4()
{
}

private static string ExpectedCsProjV4()
{
return @"
<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
Expand All @@ -96,7 +127,7 @@ private static string ExpectedCsProjV4()

</ItemGroup>
</Project>
";
}
";
}
}
}