Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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
1 change: 1 addition & 0 deletions sdk/Sdk/Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<MinorProductVersion>14</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 @@ -66,14 +66,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 @@ -88,24 +91,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 @@ -114,31 +113,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 @@ -157,8 +158,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
7 changes: 5 additions & 2 deletions sdk/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker.Sdk <version> (meta package)

- <entry>
### Microsoft.Azure.Functions.Worker.Sdk 1.14.0-preview1

- 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

### Microsoft.Azure.Functions.Worker.Sdk.Analyzers <version> (delete if not updated)

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>
";
}
";
}
}
}