Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
36 changes: 36 additions & 0 deletions src/Azure.Functions.Sdk/FunctionsAssemblyResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Collections.Immutable;
using Mono.Cecil;

namespace Azure.Functions.Sdk;

/// <summary>
/// An assembly resolver that skips trusted platform assemblies.
/// </summary>
public class FunctionsAssemblyResolver : DefaultAssemblyResolver
{
private static readonly ImmutableHashSet<string> TrustedPlatformAssemblies
= GetTrustedPlatformAssemblies();

private static ImmutableHashSet<string> GetTrustedPlatformAssemblies()
{
var data = AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES");
return data.ToString().Split(Path.PathSeparator)
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase);
}

/// <summary>
/// Resolves the assembly, returning null if it's a trusted platform assembly.
/// </summary>
/// <param name="name">The assembly name reference.</param>
/// <returns>The assembly definition, if successfully resolved.</returns>
public override AssemblyDefinition? Resolve(AssemblyNameReference name)
{
// As soon as we get to a TPA we can stop. This helps prevent the odd circular reference
// with type forwarders as well.
AssemblyDefinition assemblyDef = base.Resolve(name);
return TrustedPlatformAssemblies.Contains(assemblyDef.MainModule.FileName) ? null : assemblyDef;
}
}
56 changes: 56 additions & 0 deletions src/Azure.Functions.Sdk/FunctionsAssemblyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
using Mono.Cecil;
using ILogger = NuGet.Common.ILogger;

namespace Azure.Functions.Sdk;

Expand All @@ -14,6 +17,46 @@ public sealed partial class FunctionsAssemblyScanner
@"^(System|Azure\.Core|Azure\.Identity|Microsoft\.Bcl|Microsoft\.Extensions|Microsoft\.Identity|Microsoft\.NETCore|Microsoft\.NETStandard|Microsoft\.Win32|Grpc)(\..*|$)",
RegexOptions.Compiled);

private readonly FunctionsAssemblyResolver _resolver;
private readonly ReaderParameters _readerParameters;

/// <summary>
/// Initializes a new instance of the <see cref="FunctionsAssemblyScanner"/> class.
/// </summary>
/// <param name="searchDirectories">The search directories to include, if any.</param>
public FunctionsAssemblyScanner(IEnumerable<string>? searchDirectories = null)
{
_resolver = new();
_readerParameters = new ReaderParameters
{
AssemblyResolver = _resolver,
};

if (searchDirectories != null)
{
foreach (string directory in searchDirectories)
{
_resolver.AddSearchDirectory(directory);
}
}
}

/// <summary>
/// Creates a <see cref="FunctionsAssemblyScanner"/> from the given task items. Uses
/// the directories of the task items as search directories.
/// </summary>
/// <param name="items">The items to use parent directories from.</param>
/// <returns>A <see cref="FunctionsAssemblyScanner" /> with search directories added.</returns>
public static FunctionsAssemblyScanner FromTaskItems(IEnumerable<ITaskItem> items)
{
IEnumerable<string> searchDirectories = items
.Select(item => Path.GetDirectoryName(item.ItemSpec))
.Where(dir => !string.IsNullOrEmpty(dir))
.Distinct();

return new(searchDirectories);
}

/// <summary>
/// Checks if the given package name should be scanned or not.
/// </summary>
Expand All @@ -23,4 +66,17 @@ public static bool ShouldScanPackage(string name)
{
return !string.IsNullOrEmpty(name) && !ExcludedPackagesRegex.IsMatch(name);
}

/// <summary>
/// Gets the WebJobs references, if any, from the given assembly path.
/// </summary>
/// <param name="assembly">The disk path of the assembly to scan.</param>
/// <param name="logger">The optional logger.</param>
/// <returns>The found WebJobs references, if any.</returns>
public IEnumerable<WebJobsReference> GetWebJobsReferences(string assembly, ILogger? logger = null)
{
Throw.IfNullOrEmpty(assembly);
AssemblyDefinition definition = AssemblyDefinition.ReadAssembly(assembly, _readerParameters);
return WebJobsReference.FromModule(definition, logger);
}
}
38 changes: 38 additions & 0 deletions src/Azure.Functions.Sdk/MonoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,42 @@ public void GetArguments<TFirst, TSecond>(out TFirst first, out TSecond second)
second = (TSecond)attribute.ConstructorArguments[1].Value;
}
}

/// <summary>
/// Walks the inheritance chain of the type and checks if any type in the chain matches the provided check.
/// </summary>
/// <param name="type">The type reference to check.</param>
/// <param name="check">The predicate to check with.</param>
/// <returns>
/// <c>true</c> if <paramref name="check"/> succeeds on any type in the inheritance chain, <c>false</c> otherwise.
/// </returns>
public static bool CheckTypeInheritance(this TypeReference type, Func<TypeReference, bool> check)
{
Throw.IfNull(type);
Throw.IfNull(check);

TypeReference? currentType = type;
while (currentType != null)
{
if (check(currentType))
{
return true;
}

currentType = currentType.Resolve()?.BaseType;
}

return false;
}

/// <summary>
/// Gets the full name of the type reference suitable for reflection use.
/// </summary>
/// <param name="type">The type reference.</param>
/// <returns>The formatted name of the type.</returns>
public static string GetReflectionFullName(this TypeReference type)
{
Throw.IfNull(type);
return type.FullName.Replace("/", "+");
}
}
18 changes: 18 additions & 0 deletions src/Azure.Functions.Sdk/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,25 @@
<data name="ExtensionProject_HashUpToDate" xml:space="preserve">
<value>Existing hash '{0}' matches current hash. No changes detected, skipping project generation.</value>
</data>
<data name="ExtensionMetadata_DoesNotExist" xml:space="preserve">
<value>Hash file does not exist or extension metadata file does not exist. Regenerating extension metadata.</value>
</data>
<data name="ExtensionMetadata_FinishedGenerating" xml:space="preserve">
<value>Finished generating extension metadata. Updated cache file with hash '{0}'.</value>
</data>
<data name="ExtensionMetadata_HashOutOfDate" xml:space="preserve">
<value>Existing hash '{0}' does not match current hash '{1}'. Regenerating extension metadata.</value>
</data>
<data name="ExtensionMetadata_HashUpToDate" xml:space="preserve">
<value>Existing hash '{0}' matches current hash. No changes detected, skipping extension metadata generation.</value>
</data>
<data name="ExtensionMetadata_ScanningError" xml:space="preserve">
<value>Error scanning assembly '{0}'.</value>
</data>
<data name="MissingProjectAssetsFile" xml:space="preserve">
<value>Assets file '{0}' does not exist. Please ensure restore successfully ran.</value>
</data>
<data name="ExtensionMetadata_SkippingExcludedAssembly" xml:space="preserve">
<value>Skipping extension assembly '{0}' as it is explicitly excluded.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<_AzureFunctionsExtensionProjectDirectory>$(BaseIntermediateOutputPath)azure_functions/</_AzureFunctionsExtensionProjectDirectory>
<_AzureFunctionsExtensionProjectPath>$(_AzureFunctionsExtensionProjectDirectory)$(_AzureFunctionsExtensionProjectName)</_AzureFunctionsExtensionProjectPath>
<_AzureFunctionsExtensionRemoveProps>TargetFramework;RuntimeIdentifier;SelfContained;PublishSingleFile;PublishReadyToRun;UseCurrentRuntimeIdentifier;WebPublishMethod;PublishProfile;DeployOnBuild;PublishDir;OutDir;OutputPath;ManagePackageVersionsCentrally;</_AzureFunctionsExtensionRemoveProps>
<_FunctionsExtensionsJsonName>extensions.json</_FunctionsExtensionsJsonName>
</PropertyGroup>

<ItemDefinitionGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<UsingTask TaskName="$(AzureFunctionsTaskNamespace).Extensions.ResolveExtensionPackages" AssemblyFile="$(AzureFunctionsSdkTasksAssembly)" />
<UsingTask TaskName="$(AzureFunctionsTaskNamespace).Extensions.ValidateExtensionPackages" AssemblyFile="$(AzureFunctionsSdkTasksAssembly)" />
<UsingTask TaskName="$(AzureFunctionsTaskNamespace).Extensions.WriteExtensionProject" AssemblyFile="$(AzureFunctionsSdkTasksAssembly)" />
<UsingTask TaskName="$(AzureFunctionsTaskNamespace).Extensions.WriteExtensionMetadata" AssemblyFile="$(AzureFunctionsSdkTasksAssembly)" />

<PropertyGroup>
<_FunctionsPackageHashPath>$(_AzureFunctionsExtensionProjectDirectory)azure_functions.package.hash</_FunctionsPackageHashPath>
<_FunctionsIntermediateExtensionJsonPath>$(IntermediateOutputPath)$(_FunctionsExtensionsJsonName)</_FunctionsIntermediateExtensionJsonPath>
<_FunctionsUnusedPackagesCachePath>$(_AzureFunctionsExtensionProjectDirectory)azure_functions.unused_packages.cache</_FunctionsUnusedPackagesCachePath>
</PropertyGroup>

<Target Name="GenerateFunctionsExtensionProject" Condition="'$(DesignTimeBuild)' != 'true'"
DependsOnTargets="CollectExtensionPackages;WriteExtensionProject;RestoreExtensionProject"
AfterTargets="Restore" />

<Target Name="PrepareFunctionsExtensionPayload" Condition="'$(DesignTimeBuild)' != 'true'"
DependsOnTargets="GetFunctionsExtensionFiles"
DependsOnTargets="GetFunctionsExtensionFiles;GenerateExtensionMetadata;AssignFunctionsTargetPaths"
AfterTargets="CoreCompile" BeforeTargets="GetCopyToOutputDirectoryItems" />

<Target Name="PublishFunctionsExtensionPayload" Condition="'$(DesignTimeBuild)' != 'true'"
Expand Down Expand Up @@ -66,4 +66,22 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
</ItemGroup>
</Target>

<Target Name="_CalculateFunctionsExtensionAssemblies">
<ItemGroup>
<_FunctionsExtensionAssemblies Include="@(_FunctionsExtensionsOutputItems->WithMetadataValue('AssetType', 'runtime'))" />
</ItemGroup>
</Target>

<Target Name="GenerateExtensionMetadata" DependsOnTargets="_CalculateFunctionsExtensionAssemblies">
<WriteExtensionMetadata OutputPath="$(_FunctionsIntermediateExtensionJsonPath)" ExtensionReferences="@(_FunctionsExtensionAssemblies)" />
</Target>

<Target Name="AssignFunctionsTargetPaths">
<ItemGroup>
<_NoneWithTargetPath Include="$(_FunctionsIntermediateExtensionJsonPath)" TargetPath="$(_FunctionsExtensionsJsonName)"
CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest"/>
<EmbedInBinlog Include="$(_FunctionsIntermediateExtensionJsonPath)" />
</ItemGroup>
</Target>

</Project>
20 changes: 20 additions & 0 deletions src/Azure.Functions.Sdk/TaskItemExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ public string NuGetPackageId
set => taskItem.SetMetadata("NuGetPackageId", value);
}

/// <summary>
/// Gets or sets the "NuGetPackageVersion" metadata on the task item.
/// </summary>
public string NuGetPackageVersion
{
get => taskItem.GetMetadata("NuGetPackageVersion") ?? string.Empty;
set => taskItem.SetMetadata("NuGetPackageVersion", value);
}

/// <summary>
/// Tries to get the NuGet package ID from the task item.
/// </summary>
Expand All @@ -60,5 +69,16 @@ public bool TryGetNuGetPackageId([NotNullWhen(true)] out string? packageId)
packageId = taskItem.NuGetPackageId;
return !string.IsNullOrEmpty(packageId);
}

/// <summary>
/// Tries to get the NuGet package version from the task item.
/// </summary>
/// <param name="packageVersion">The package version, if found.</param>
/// <returns><c>true</c> if nuget package version is found; <c>false</c> otherwise.</returns>
public bool TryGetNugetPackageVersion([NotNullWhen(true)] out string? packageVersion)
{
packageVersion = taskItem.NuGetPackageVersion;
return !string.IsNullOrEmpty(packageVersion);
}
}
}
Loading
Loading