Skip to content

Commit de714dd

Browse files
authored
[MSBUILD SDK] Error out build if incompatible SDK is present (#3296)
* Error out build if incompatible SDK is present * Clarify error message * Update target * Remove extra quote * Add xmldoc to LogMessage * Fix missing xmldoc
1 parent 16981db commit de714dd

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

src/Azure.Functions.Sdk/LogMessage.cs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,63 @@
66

77
namespace Azure.Functions.Sdk;
88

9+
/// <summary>
10+
/// A helper struct representing a log message for MSBuild tasks.
11+
/// </summary>
912
internal readonly struct LogMessage
1013
{
14+
/// <summary>
15+
/// Log message for when the Func CLI cannot be run.
16+
/// </summary>
1117
public static readonly LogMessage Error_CannotRunFuncCli
1218
= new(nameof(Strings.AZFW0100_Error_CannotRunFuncCli));
1319

20+
/// <summary>
21+
/// Log message for when there is a conflict between extension packages.
22+
/// </summary>
1423
public static readonly LogMessage Error_ExtensionPackageConflict
1524
= new(nameof(Strings.AZFW0101_Error_ExtensionPackageConflict));
1625

26+
/// <summary>
27+
/// Log message for when there is a duplicate extension package.
28+
/// </summary>
1729
public static readonly LogMessage Warning_ExtensionPackageDuplicate
1830
= new(nameof(Strings.AZFW0102_Warning_ExtensionPackageDuplicate));
1931

32+
/// <summary>
33+
/// Log message for when an extension package version is invalid.
34+
/// </summary>
2035
public static readonly LogMessage Error_InvalidExtensionPackageVersion
2136
= new(nameof(Strings.AZFW0103_Error_InvalidExtensionPackageVersion));
2237

38+
/// <summary>
39+
/// Log message for when an end-of-life Functions version is used.
40+
/// </summary>
2341
public static readonly LogMessage Warning_EndOfLifeFunctionsVersion
2442
= new(nameof(Strings.AZFW0104_Warning_EndOfLifeFunctionsVersion));
2543

26-
public static readonly LogMessage Error_UsingLegacyFunctionsSdk
27-
= new(nameof(Strings.AZFW0105_Error_UsingLegacyFunctionsSdk));
44+
/// <summary>
45+
/// Log message for when an incompatible Functions SDK is used.
46+
/// </summary>
47+
public static readonly LogMessage Error_UsingIncompatibleSdk
48+
= new(nameof(Strings.AZFW0105_Error_UsingIncompatibleSdk));
2849

50+
/// <summary>
51+
/// Log message for when an unknown Functions version is specified.
52+
/// </summary>
2953
public static readonly LogMessage Error_UnknownFunctionsVersion
3054
= new(nameof(Strings.AZFW0106_Error_UnknownFunctionsVersion));
3155

56+
/// <summary>
57+
/// Log message for when an unsupported target framework is used.
58+
/// </summary>
3259
public static readonly LogMessage Warning_UnsupportedTargetFramework
3360
= new(nameof(Strings.AZFW0107_Warning_UnsupportedTargetFramework));
3461

3562
/// <summary>
3663
/// Initializes a new instance of the <see cref="LogMessage"/> struct.
3764
/// Parses the <see cref="Level"/> and <see cref="Code"/> properties from the given <paramref name="id"/>.
38-
/// LogCodes must:
65+
/// LogMessages must:
3966
/// - Be defined in the 'Strings.resx' file with the identifier <paramref name="id"/>.
4067
/// - Follow the format: (?:<LogCode>_)(?<LogLevel>_).*
4168
/// </summary>
@@ -59,12 +86,33 @@ public LogMessage(LogLevel level, string id, string? code = null)
5986
Code = code;
6087
}
6188

89+
/// <summary>
90+
/// Gets the level of the log message.
91+
/// </summary>
6292
public LogLevel Level { get; }
6393

94+
/// <summary>
95+
/// Gets the identifier of the log message.
96+
/// </summary>
97+
/// <remarks>
98+
/// This identifier must correspond to a resource string in the <see cref="Strings"/> resource file.
99+
/// </remarks>
64100
public string Id { get; }
65101

102+
/// <summary>
103+
/// Gets the code of the log message, if available.
104+
/// </summary>
66105
public string? Code { get; }
67106

107+
/// <summary>
108+
/// Gets the help keyword for the log message.
109+
/// </summary>
110+
/// <remarks>
111+
/// The help keyword is derived from the <see cref="Code"/> property.
112+
/// If the <see cref="Code"/> is null, the help keyword will also be null.
113+
/// A help keyword will emit a help link of "https://go.microsoft.com/fwlink/?LinkId=AzureFunctions.{Code}"
114+
/// as part of the msbuild log.
115+
/// </remarks>
68116
public string? HelpKeyword => Code is null ? null : $"AzureFunctions.{Code}";
69117

70118
/// <summary>
@@ -82,15 +130,26 @@ public static LogMessage FromId(string id)
82130
nameof(Warning_ExtensionPackageDuplicate) => Warning_ExtensionPackageDuplicate,
83131
nameof(Error_InvalidExtensionPackageVersion) => Error_InvalidExtensionPackageVersion,
84132
nameof(Warning_EndOfLifeFunctionsVersion) => Warning_EndOfLifeFunctionsVersion,
85-
nameof(Error_UsingLegacyFunctionsSdk) => Error_UsingLegacyFunctionsSdk,
133+
nameof(Error_UsingIncompatibleSdk) => Error_UsingIncompatibleSdk,
86134
nameof(Error_UnknownFunctionsVersion) => Error_UnknownFunctionsVersion,
87135
nameof(Warning_UnsupportedTargetFramework) => Warning_UnsupportedTargetFramework,
88136
_ => throw new ArgumentException($"Log message with id '{id}' not found.", nameof(id)),
89137
};
90138
}
91139

140+
/// <summary>
141+
/// Formats the log message with the given arguments and <see cref="CultureInfo.CurrentUICulture" />.
142+
/// </summary>
143+
/// <param name="args">The arguments to use, if any.</param>
144+
/// <returns>The formatted message.</returns>
92145
public string Format(params object[] args) => Format(CultureInfo.CurrentUICulture, args);
93146

147+
/// <summary>
148+
/// Formats the log message with the given culture and arguments.
149+
/// </summary>
150+
/// <param name="culture">The culture info to use.</param>
151+
/// <param name="args">The arguments to use, if any.</param>
152+
/// <returns>The formatted message.</returns>
94153
public string Format(CultureInfo culture, params object[] args)
95154
{
96155
string resource = Strings.GetResourceString(Id)

src/Azure.Functions.Sdk/Strings.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@
132132
<data name="AZFW0104_Warning_EndOfLifeFunctionsVersion" xml:space="preserve">
133133
<value>Azure Functions '{0}' is out of support and will not receive security updates in the future. Please refer to https://aka.ms/azure-functions-retired-versions for more information on the support policy.</value>
134134
</data>
135-
<data name="AZFW0105_Error_UsingLegacyFunctionsSdk" xml:space="preserve">
136-
<value>Microsoft.NET.Sdk.Functions package is meant to be used with in-proc function apps. Please remove the reference to this package.</value>
135+
<data name="AZFW0105_Error_UsingIncompatibleSdk" xml:space="preserve">
136+
<value>'{0}' package is incompatible with Azure.Functions.Sdk. Please remove the '{0}' package reference.</value>
137137
</data>
138138
<data name="AZFW0106_Error_UnknownFunctionsVersion" xml:space="preserve">
139139
<value>The AzureFunctionsVersion '{0}' is unknown or not supported.</value>

src/Azure.Functions.Sdk/Targets/Azure.Functions.Sdk.targets

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
2828
<Import Project="$(MSBuildFunctionsTargetsPath)Microsoft.Azure.Functions.Designtime.targets"
2929
Condition="Exists('$(MSBuildFunctionsTargetsPath)Microsoft.Azure.Functions.Designtime.targets')" />
3030

31+
<Target Name="_FunctionAppValidation" DependsOnTargets="_ValidateFunctionsVersion;_CheckForConflictingFunctionSdk" BeforeTargets="CollectPackageReferences;Build" />
32+
3133
<Target Name="_ValidateFunctionsVersion">
3234
<ItemGroup>
3335
<_AllowedFunctionVersion Include="v4" InSupport="true" />
@@ -44,8 +46,11 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
4446
Resource="Warning_UnsupportedTargetFramework" Arguments="$(TargetFramework)" />
4547
</Target>
4648

47-
<Target Name="_FunctionAppValidation" DependsOnTargets="_ValidateFunctionsVersion" BeforeTargets="Restore;Build" >
48-
<FuncSdkLog Condition="'$(_IsFunctionsSdkBuild)' == 'true'" Resource="Error_UsingLegacyFunctionsSdk" />
49+
<Target Name="_CheckForConflictingFunctionSdk">
50+
<FuncSdkLog Condition="@(PackageReference->AnyHaveMetadataValue('Identity', 'Microsoft.Azure.Functions.Worker.Sdk'))"
51+
Resource="Error_UsingIncompatibleSdk" Arguments="Microsoft.Azure.Functions.Worker.Sdk" />
52+
<FuncSdkLog Condition="@(PackageReference->AnyHaveMetadataValue('Identity', 'Microsoft.NET.Sdk.Functions'))"
53+
Resource="Error_UsingIncompatibleSdk" Arguments="Microsoft.NET.Sdk.Functions" />
4954
</Target>
5055

5156
<Target Name="_FunctionsCheckForCoreTools">

test/Azure.Functions.Sdk.Tests/Integration/SdkEndToEndTests.Restore.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,28 @@ public void Restore_Incremental_NoOp()
111111
hash.LastWriteTimeUtc.Should().Be(hashWrite);
112112
}
113113

114+
[Theory]
115+
[InlineData("Microsoft.NET.Sdk.Functions")]
116+
[InlineData("Microsoft.Azure.Functions.Worker.Sdk")]
117+
public void Restore_IncompatibleSdk_Fails(string package)
118+
{
119+
// Arrange
120+
ProjectCreator project = ProjectCreator.Templates.AzureFunctionsProject(
121+
GetTempCsproj())
122+
.ItemPackageReference(package, "1.0.0");
123+
124+
// Act
125+
BuildOutput output = project.Restore();
126+
127+
// Assert
128+
output.Should().BeFailed()
129+
.And.HaveNoWarnings()
130+
.And.HaveSingleError()
131+
.Which.Should().BeSdkMessage(
132+
(LogMessage.Error_UsingIncompatibleSdk, package))
133+
.And.HaveSender("FuncSdkLog");
134+
}
135+
114136
private void ValidateProject(params NugetPackage[] packages)
115137
{
116138
ValidateProjectContents(packages);

0 commit comments

Comments
 (0)