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
67 changes: 63 additions & 4 deletions src/Azure.Functions.Sdk/LogMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,63 @@

namespace Azure.Functions.Sdk;

/// <summary>
/// A helper struct representing a log message for MSBuild tasks.
/// </summary>
internal readonly struct LogMessage
{
/// <summary>
/// Log message for when the Func CLI cannot be run.
/// </summary>
public static readonly LogMessage Error_CannotRunFuncCli
= new(nameof(Strings.AZFW0100_Error_CannotRunFuncCli));

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

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

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

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

public static readonly LogMessage Error_UsingLegacyFunctionsSdk
= new(nameof(Strings.AZFW0105_Error_UsingLegacyFunctionsSdk));
/// <summary>
/// Log message for when an incompatible Functions SDK is used.
/// </summary>
public static readonly LogMessage Error_UsingIncompatibleSdk
= new(nameof(Strings.AZFW0105_Error_UsingIncompatibleSdk));

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

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

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

/// <summary>
/// Gets the level of the log message.
/// </summary>
public LogLevel Level { get; }

/// <summary>
/// Gets the identifier of the log message.
/// </summary>
/// <remarks>
/// This identifier must correspond to a resource string in the <see cref="Strings"/> resource file.
/// </remarks>
public string Id { get; }

/// <summary>
/// Gets the code of the log message, if available.
/// </summary>
public string? Code { get; }

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

/// <summary>
Expand All @@ -82,15 +130,26 @@ public static LogMessage FromId(string id)
nameof(Warning_ExtensionPackageDuplicate) => Warning_ExtensionPackageDuplicate,
nameof(Error_InvalidExtensionPackageVersion) => Error_InvalidExtensionPackageVersion,
nameof(Warning_EndOfLifeFunctionsVersion) => Warning_EndOfLifeFunctionsVersion,
nameof(Error_UsingLegacyFunctionsSdk) => Error_UsingLegacyFunctionsSdk,
nameof(Error_UsingIncompatibleSdk) => Error_UsingIncompatibleSdk,
nameof(Error_UnknownFunctionsVersion) => Error_UnknownFunctionsVersion,
nameof(Warning_UnsupportedTargetFramework) => Warning_UnsupportedTargetFramework,
_ => throw new ArgumentException($"Log message with id '{id}' not found.", nameof(id)),
};
}

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

/// <summary>
/// Formats the log message with the given culture and arguments.
/// </summary>
/// <param name="culture">The culture info to use.</param>
/// <param name="args">The arguments to use, if any.</param>
/// <returns>The formatted message.</returns>
public string Format(CultureInfo culture, params object[] args)
{
string resource = Strings.GetResourceString(Id)
Expand Down
4 changes: 2 additions & 2 deletions src/Azure.Functions.Sdk/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@
<data name="AZFW0104_Warning_EndOfLifeFunctionsVersion" xml:space="preserve">
<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>
</data>
<data name="AZFW0105_Error_UsingLegacyFunctionsSdk" xml:space="preserve">
<value>Microsoft.NET.Sdk.Functions package is meant to be used with in-proc function apps. Please remove the reference to this package.</value>
<data name="AZFW0105_Error_UsingIncompatibleSdk" xml:space="preserve">
<value>'{0}' package is incompatible with Azure.Functions.Sdk. Please remove the '{0}' package reference.</value>
</data>
<data name="AZFW0106_Error_UnknownFunctionsVersion" xml:space="preserve">
<value>The AzureFunctionsVersion '{0}' is unknown or not supported.</value>
Expand Down
9 changes: 7 additions & 2 deletions src/Azure.Functions.Sdk/Targets/Azure.Functions.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<Import Project="$(MSBuildFunctionsTargetsPath)Microsoft.Azure.Functions.Designtime.targets"
Condition="Exists('$(MSBuildFunctionsTargetsPath)Microsoft.Azure.Functions.Designtime.targets')" />

<Target Name="_FunctionAppValidation" DependsOnTargets="_ValidateFunctionsVersion;_CheckForConflictingFunctionSdk" BeforeTargets="CollectPackageReferences;Build" />

<Target Name="_ValidateFunctionsVersion">
<ItemGroup>
<_AllowedFunctionVersion Include="v4" InSupport="true" />
Expand All @@ -44,8 +46,11 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
Resource="Warning_UnsupportedTargetFramework" Arguments="$(TargetFramework)" />
</Target>

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

<Target Name="_FunctionsCheckForCoreTools">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ public void Restore_Incremental_NoOp()
hash.LastWriteTimeUtc.Should().Be(hashWrite);
}

[Theory]
[InlineData("Microsoft.NET.Sdk.Functions")]
[InlineData("Microsoft.Azure.Functions.Worker.Sdk")]
public void Restore_IncompatibleSdk_Fails(string package)
{
// Arrange
ProjectCreator project = ProjectCreator.Templates.AzureFunctionsProject(
GetTempCsproj())
.ItemPackageReference(package, "1.0.0");

// Act
BuildOutput output = project.Restore();

// Assert
output.Should().BeFailed()
.And.HaveNoWarnings()
.And.HaveSingleError()
.Which.Should().BeSdkMessage(
(LogMessage.Error_UsingIncompatibleSdk, package))
.And.HaveSender("FuncSdkLog");
}

private void ValidateProject(params NugetPackage[] packages)
{
ValidateProjectContents(packages);
Expand Down