Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,27 @@
using Microsoft.DotNet.Scaffolding.Core.Model;
using Microsoft.DotNet.Scaffolding.Internal.CliHelpers;

namespace Microsoft.DotNet.Tools.Scaffold.AspNet.Common;
namespace Microsoft.DotNet.Scaffolding.Core.Helpers;

public class TargetFrameworkHelpers
{
/// <summary>
/// Gets the target framework enum for the specified project file. Returns null if no compatible target
/// framework is found.
/// </summary>
/// <param name="projectPath">The full path to the project file to evaluate. Cannot be null or empty.</param>
/// <returns>The target framework enum representing the lowest compatible framework, or null if no compatible
/// framework is found.</returns>
public static TargetFramework? GetTargetFrameworkForProject(string projectPath)
{
string? lowestCompatibleTfm = GetLowestCompatibleTargetFramework(projectPath);
if (lowestCompatibleTfm is not null)
{
return GetTargetFrameworkEnum(lowestCompatibleTfm);
}
return null;
}

internal class TargetFrameworkHelpers
{
/// <summary>
/// Determines the lowest compatible target framework for the specified project file. Returns null if there are any incompatible target frameworks.
/// </summary>
Expand All @@ -17,7 +34,7 @@ internal class TargetFrameworkHelpers
/// <param name="projectPath">The full path to the project file to evaluate. Cannot be null or empty.</param>
/// <returns>The target framework moniker (TFM) string representing the lowest compatible framework, or null if no compatible
/// framework is found.</returns>
internal static string? GetLowestCompatibleTargetFramework(string projectPath)
private static string? GetLowestCompatibleTargetFramework(string projectPath)
{
MsBuildPropertiesOutput? msbuildOutput = MsBuildCliRunner.RunMSBuildCommandAndDeserialize<MsBuildPropertiesOutput>(["-getProperty:TargetFramework;TargetFrameworks"], projectPath);
if (msbuildOutput?.Properties is null)
Expand Down Expand Up @@ -136,6 +153,20 @@ private static bool IsCompatibleFramework(string tfm, string projectPath, out Ve
.FirstOrDefault();
}

/// <summary>
/// Maps a target framework moniker (TFM) string to its corresponding TargetFramework enum value.
/// </summary>
/// <param name="tfm">The target framework moniker string to map, such as "net8.0".</param>
/// <returns>The corresponding TargetFramework enum value if the mapping exists; otherwise, null.</returns>
private static TargetFramework? GetTargetFrameworkEnum(string tfm)
{
if (TargetFrameworkConstants.TargetFrameworkMapping.TryGetValue(tfm, out TargetFramework framework))
{
return framework;
}
return null;
}

/// <summary>
/// Represents the output from dotnet msbuild -getProperty command.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal static class PackageExtensions
/// <param name="logger">The logger to use for logging messages.</param>
/// <returns>A package instance with the version property set to the resolved version for the specified target framework, or
/// the original package if the version is already set or cannot be resolved.</returns>
public static async Task<Package> WithResolvedVersionAsync(this Package package, string? targetFramework, NuGetVersionService nugetVersionHelper, ILogger? logger = null)
public static async Task<Package> WithResolvedVersionAsync(this Package package, TargetFramework? targetFramework, NuGetVersionService nugetVersionHelper, ILogger? logger = null)
{
if (package.PackageVersion is not null)
{
Expand All @@ -59,7 +59,7 @@ public static async Task<Package> WithResolvedVersionAsync(this Package package,
/// <param name="logger">The logger to use for logging messages.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the corresponding NuGet package
/// version if available; otherwise, <see langword="null"/> if the package does not require a version or target framework is not supported.</returns>
private static Task<NuGetVersion?> GetVersionForTargetFrameworkAsync(this Package package, string? targetFramework, NuGetVersionService nugetVersionHelper, ILogger? logger = null)
private static Task<NuGetVersion?> GetVersionForTargetFrameworkAsync(this Package package, TargetFramework? targetFramework, NuGetVersionService nugetVersionHelper, ILogger? logger = null)
{
if (!package.IsVersionRequired)
{
Expand All @@ -72,19 +72,19 @@ public static async Task<Package> WithResolvedVersionAsync(this Package package,
return Task.FromResult<NuGetVersion?>(null);
}

if (targetFramework.Equals(TargetFrameworkConstants.Net8, StringComparison.OrdinalIgnoreCase))
if (targetFramework is TargetFramework.Net8)
{
return nugetVersionHelper.GetLatestPackageForNetVersionAsync(package.Name, 8);
}
else if (targetFramework.Equals(TargetFrameworkConstants.Net9, StringComparison.OrdinalIgnoreCase))
else if (targetFramework is TargetFramework.Net9)
{
return nugetVersionHelper.GetLatestPackageForNetVersionAsync(package.Name, 9);
}
else if (targetFramework.Equals(TargetFrameworkConstants.Net10, StringComparison.OrdinalIgnoreCase))
else if (targetFramework is TargetFramework.Net10)
{
return nugetVersionHelper.GetLatestPackageForNetVersionAsync(package.Name, 10);
}
else if (targetFramework.Equals(TargetFrameworkConstants.Net11, StringComparison.OrdinalIgnoreCase))
else if (targetFramework is TargetFramework.Net11)
{
return nugetVersionHelper.GetLatestPackageForNetVersionAsync(package.Name, 11);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,20 @@ internal static class TargetFrameworkConstants
public const string NetCoreApp = ".NETCoreApp";

public static readonly ImmutableArray<string> SupportedTargetFrameworks = [Net8, Net9, Net10, Net11];

public static readonly ImmutableDictionary<string, TargetFramework> TargetFrameworkMapping = new Dictionary<string, TargetFramework>(StringComparer.OrdinalIgnoreCase)
{
[Net8] = TargetFramework.Net8,
[Net9] = TargetFramework.Net9,
[Net10] = TargetFramework.Net10,
[Net11] = TargetFramework.Net11
}.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase);
}

public enum TargetFramework
{
Net8,
Net9,
Net10,
Net11
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ public static class ScaffolderContextExtensions
/// </summary>
/// <param name="context">The scaffolder context.</param>
/// <returns>The target framework name if present; otherwise, null.</returns>
public static string? GetSpecifiedTargetFramework(this ScaffolderContext context)
internal static TargetFramework? GetSpecifiedTargetFramework(this ScaffolderContext context)
{
string? targetFramework = null;
if (context.Properties.TryGetValue(TargetFrameworkConstants.TargetFrameworkPropertyName, out object? tfm))
TargetFramework? targetFramework = null;
if (context.Properties.TryGetValue(TargetFrameworkConstants.TargetFrameworkPropertyName, out object? tfm) && tfm is TargetFramework targetFrameworkValue)
{
targetFramework = tfm as string;
targetFramework = targetFrameworkValue;
}

return targetFramework;
}

public static string? SetSpecifiedTargetFramework(this ScaffolderContext context, string? targetFramework)
internal static TargetFramework? SetSpecifiedTargetFramework(this ScaffolderContext context, TargetFramework? targetFramework)
{
context.Properties[TargetFrameworkConstants.TargetFrameworkPropertyName] = targetFramework;
return targetFramework;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public AddPackagesStep(ILogger<AddPackagesStep> logger, NuGetVersionService nuge
/// <returns>True if the packages were added successfully; otherwise, false.</returns>
public override async Task<bool> ExecuteAsync(ScaffolderContext context, CancellationToken cancellationToken = default)
{
// Try to get the target framework from the contextcls
string? targetFramework = context.GetSpecifiedTargetFramework();
// Try to get the target framework from the context
TargetFramework? targetFramework = context.GetSpecifiedTargetFramework();

foreach (Package package in Packages)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
//src
[assembly: InternalsVisibleTo("dotnet-scaffold, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.DotNet.Scaffolding.Internal, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("dotnet-scaffold.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Reflection;
using Microsoft.DotNet.Scaffolding.Core.Helpers;
using Microsoft.DotNet.Scaffolding.Core.Model;

namespace Microsoft.DotNet.Scaffolding.TextTemplating;
internal class TemplateFoldersUtilities : ITemplateFolderService
Expand Down Expand Up @@ -53,6 +55,55 @@ public IEnumerable<string> GetAllFiles(string[] baseFolders, string? extension =
return allTemplates;
}

public IEnumerable<string> GetAllT4TemplatesForTargetFramework(string[] baseFolders, string projectPath)
{
string targetFrameworkTemplateFolder = GetTargetFrameworkTemplateFolder(projectPath);
return GetAllFiles(targetFrameworkTemplateFolder, baseFolders, ".tt");
}

public IEnumerable<string> GetAllFiles(string targetFrameworkTemplateFolder, string[] baseFolders, string? extension = null)
{
List<string> allTemplates = [];
var allTemplateFolders = GetTemplateFoldersWithFramework(targetFrameworkTemplateFolder, baseFolders);
var searchPattern = string.IsNullOrEmpty(extension) ? string.Empty : $"*{Path.GetExtension(extension)}";
if (allTemplateFolders != null && allTemplateFolders.Any())
{
foreach (var templateFolder in allTemplateFolders)
{
allTemplates.AddRange(Directory.EnumerateFiles(templateFolder, searchPattern, SearchOption.AllDirectories));
}
}

return allTemplates;
}

public IEnumerable<string> GetTemplateFoldersWithFramework(string frameworkTemplateFolder, string[] baseFolders)
{
ArgumentNullException.ThrowIfNull(baseFolders);
var rootFolders = new List<string>();
var templateFolders = new List<string>();
var basePath = FindFolderWithToolsFolder(Assembly.GetExecutingAssembly().Location);
if (Directory.Exists(basePath))
{
rootFolders.Add(basePath);
}

foreach (var rootFolder in rootFolders)
{
foreach (var baseFolderName in baseFolders)
{
string templatesFolderName = "Templates";
var candidateTemplateFolders = Path.Combine(rootFolder, templatesFolderName, frameworkTemplateFolder, baseFolderName);
if (Directory.Exists(candidateTemplateFolders))
{
templateFolders.Add(candidateTemplateFolders);
}
}
}

return templateFolders;
}

private string? FindFolderWithToolsFolder(string startPath)
{
DirectoryInfo? directory = new DirectoryInfo(startPath);
Expand All @@ -69,6 +120,23 @@ public IEnumerable<string> GetAllFiles(string[] baseFolders, string? extension =

return null;
}

private string GetTargetFrameworkTemplateFolder(string projectPath)
{
TargetFramework? targetFramework = TargetFrameworkHelpers.GetTargetFrameworkForProject(projectPath);

switch (targetFramework)
{
case TargetFramework.Net8:
return "net8.0";
case TargetFramework.Net9:
return "net9.0";
}

// there are not any .net 11 specific templates yet, so default to net10.0

return "net10.0";
}
}


Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.DotNet.Scaffolding.Core.Model;
using Microsoft.DotNet.Scaffolding.Roslyn.Services;
using Microsoft.DotNet.Scaffolding.Core.Helpers;

namespace Microsoft.DotNet.Tools.Scaffold.AspNet.Common;

Expand All @@ -12,7 +14,7 @@ internal class ProjectInfo
public ProjectInfo(string? projectPath)
{
ProjectPath = projectPath;
LowestSupportedTargetFramework = projectPath is not null ? TargetFrameworkHelpers.GetLowestCompatibleTargetFramework(projectPath) : null;
LowestSupportedTargetFramework = projectPath is not null ? TargetFrameworkHelpers.GetTargetFrameworkForProject(projectPath) : null;
}

/// <summary>
Expand All @@ -30,7 +32,7 @@ public ProjectInfo(string? projectPath)
/// <summary>
/// Null if the project contains an unsupported target framework; otherwise, the supported target framework moniker (TFM).
/// </summary>
public string? LowestSupportedTargetFramework { get; }
public TargetFramework? LowestSupportedTargetFramework { get; }
/// <summary>
/// Gets or sets the list of project capabilities.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.DotNet.Scaffolding.Core.ComponentModel;
using Microsoft.DotNet.Scaffolding.Core.Model;
using Microsoft.DotNet.Scaffolding.Core.Helpers;
using Microsoft.DotNet.Scaffolding.Internal.Services;
using Microsoft.DotNet.Scaffolding.Roslyn.Services;
using Microsoft.DotNet.Tools.Scaffold.AspNet.Commands;
Expand Down Expand Up @@ -189,8 +190,8 @@ private static bool ShouldSkipPrereleaseOption(IFlowContext context)
if (context.Properties.Get(projectParameterKey) is FlowProperty projectFileProperty &&
projectFileProperty.Value is string projectFilePath && !string.IsNullOrEmpty(projectFilePath))
{
string? targetFramework = TargetFrameworkHelpers.GetLowestCompatibleTargetFramework(projectFilePath);
return targetFramework is null || !targetFramework.Equals(TargetFrameworkConstants.Net11, StringComparison.OrdinalIgnoreCase);
TargetFramework? targetFramework = TargetFrameworkHelpers.GetTargetFrameworkForProject(projectFilePath);
return targetFramework is null || targetFramework != TargetFramework.Net11;
}
return false;
}
Expand Down
Loading