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
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,23 @@ public async Task ExecuteAsync()
CodeModelGenerator.Instance.Emitter.Info("Starting code generation");
CodeModelGenerator.Instance.Stopwatch.Start();

GeneratedCodeWorkspace.Initialize();
var outputPath = CodeModelGenerator.Instance.Configuration.OutputDirectory;
var generatedSourceOutputPath = CodeModelGenerator.Instance.Configuration.ProjectGeneratedDirectory;

// Resolve PackageReference items from the .csproj so custom code referencing
// external NuGet types (e.g., Azure.Storage.Common) compiles correctly.
await GeneratedCodeWorkspace.AddPackageReferencesFromProject();

// Pre-walk the input library and resolve any external types that point at NuGet packages.
// This populates ExternalTypeReferenceResolver's cache and registers each resolved assembly
// as an additional metadata reference *before* the generated/custom code workspaces are
// constructed, so their cached Roslyn projects pick the references up.
await ExternalTypeReferenceResolver.ResolveAllAsync();

// Initialize the workspace project AFTER all metadata references have been added so the
// eagerly-cached project sees them.
GeneratedCodeWorkspace.Initialize();

GeneratedCodeWorkspace customCodeWorkspace = await GeneratedCodeWorkspace.Create(isCustomCodeProject: true);
// The generated attributes need to be added into the workspace before loading the custom code. Otherwise,
// Roslyn doesn't load the attributes completely and we are unable to get the attribute arguments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Utilities;
using NuGet.Configuration;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;

namespace Microsoft.TypeSpec.Generator
{
Expand Down Expand Up @@ -327,14 +325,14 @@ internal static async Task AddPackageReferencesFromProject()
}

// Search the NuGet global packages folder for any cached version of this package.
string? resolvedAssemblyPath = FindPackageAssembly(globalPackagesFolder, refPackageName);
string? resolvedAssemblyPath = NugetPackageResolver.FindPackageAssembly(globalPackagesFolder, refPackageName);

// If not found in cache, download the latest version from NuGet feeds
if (resolvedAssemblyPath == null)
{
try
{
var latestVersion = await ResolveLatestPackageVersion(refPackageName, nugetSettings);
var latestVersion = await NugetPackageResolver.ResolveLatestPackageVersion(refPackageName, nugetSettings);
if (latestVersion != null)
{
var downloader = new NugetPackageDownloader(refPackageName, latestVersion, null, nugetSettings);
Expand Down Expand Up @@ -363,67 +361,6 @@ internal static async Task AddPackageReferencesFromProject()
}
}

/// <summary>
/// Searches the NuGet global packages folder for a package assembly across all cached versions.
/// Returns the first matching assembly found, preferring newer versions.
/// </summary>
private static string? FindPackageAssembly(string globalPackagesFolder, string packageName)
{
var packageDir = Path.Combine(globalPackagesFolder, packageName.ToLowerInvariant());

if (!Directory.Exists(packageDir))
{
return null;
}

foreach (var versionDir in Directory.GetDirectories(packageDir).OrderDescending())
{
foreach (var tfm in NugetPackageDownloader.PreferredDotNetFrameworkVersions)
{
var assemblyPath = Path.Combine(versionDir, "lib", tfm, $"{packageName}.dll");
if (File.Exists(assemblyPath))
{
return assemblyPath;
}
}
}

return null;
}

/// <summary>
/// Queries configured NuGet feeds to resolve the latest stable version of a package.
/// </summary>
private static async Task<string?> ResolveLatestPackageVersion(string packageName, ISettings nugetSettings)
{
var sources = SettingsUtility.GetEnabledSources(nugetSettings);
using var cacheContext = new SourceCacheContext();
foreach (var source in sources)
{
try
{
var repository = Repository.Factory.GetCoreV3(source.Source);
var resource = await repository.GetResourceAsync<FindPackageByIdResource>();
var versions = await resource.GetAllVersionsAsync(
packageName, cacheContext, NuGet.Common.NullLogger.Instance, CancellationToken.None);
var latest = versions?
.Where(v => !v.IsPrerelease)
.OrderByDescending(v => v)
.FirstOrDefault();
if (latest != null)
{
return latest.ToString();
}
}
catch
{
// Skip sources that fail (auth, network, etc.)
}
}

return null;
}

internal static async Task<Compilation?> LoadBaselineContract()
{
var packageName = CodeModelGenerator.Instance.TypeFactory.PrimaryNamespace;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.TypeSpec.Generator.Input.Extensions;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Utilities;

namespace Microsoft.TypeSpec.Generator
{
Expand Down Expand Up @@ -272,18 +273,38 @@ protected internal TypeFactory()
/// <returns>A <see cref="CSharpType"/> representing the external type, or null if the type cannot be resolved.</returns>
private CSharpType? CreateExternalType(InputExternalTypeMetadata externalProperties)
{
// Try to create a framework type from the fully qualified name
// 1. Try to create a framework type from the fully qualified name. This stays as the
// first attempt because it's free (no I/O) and is the source of truth for BCL types.
var frameworkType = CreateFrameworkType(externalProperties.Identity);
if (frameworkType != null)
{
return new CSharpType(frameworkType);
}

// External types that cannot be resolved as framework types are not supported
// Report a diagnostic to inform the user
// 2. Fallback: dynamically resolve the type from the NuGet package named in the metadata.
// ExternalTypeReferenceResolver consults a process-wide cache populated by the eager
// pre-walk in CSharpGen.ExecuteAsync; on a miss it resolves on-demand.
if (!string.IsNullOrEmpty(externalProperties.Package))
{
var resolvedType = ExternalTypeReferenceResolver.TryResolve(externalProperties);
if (resolvedType != null)
{
return new CSharpType(resolvedType);
}
}

// 3. Neither path worked — emit a diagnostic that explains what was attempted.
// Each branch is a self-contained sentence so the final message reads naturally and
// doesn't repeat "could not be resolved".
var details = string.IsNullOrEmpty(externalProperties.Package)
? "no package metadata was provided"
: string.IsNullOrEmpty(externalProperties.MinVersion)
? $"package '{externalProperties.Package}' was not found in the NuGet cache or any configured feed"
: $"package '{externalProperties.Package}' (>= {externalProperties.MinVersion}) was not found in the NuGet cache or any configured feed";

CodeModelGenerator.Instance.Emitter.ReportDiagnostic(
"unsupported-external-type",
$"External type '{externalProperties.Identity}' is not currently supported.");
$"External type '{externalProperties.Identity}' could not be resolved: {details}.");

return null;
}
Expand Down
Loading
Loading