Fix restoring NuGet packages for target with multiple projects

Fix restoring `NuGet` packages to run before project build to ensure files are downloaded

#3900
This commit is contained in:
Wojtek Figat
2026-01-22 18:49:47 +01:00
parent 3cfc5db54a
commit ecfd03f79c
5 changed files with 77 additions and 78 deletions

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Flax.Build.Graph;
using Flax.Build.NativeCpp;
@@ -424,5 +425,65 @@ namespace Flax.Build
return failed;
}
private static void DeployFiles(TaskGraph graph, Target target, BuildOptions targetBuildOptions, string outputPath)
{
using (new ProfileEventScope("DeployFiles"))
{
foreach (var srcFile in targetBuildOptions.OptionalDependencyFiles.Where(File.Exists).Union(targetBuildOptions.DependencyFiles))
{
var dstFile = Path.Combine(outputPath, Path.GetFileName(srcFile));
graph.AddCopyFile(dstFile, srcFile);
}
if (targetBuildOptions.NugetPackageReferences.Any())
{
var nugetPath = Utilities.GetNugetPackagesPath();
var restore = true;
foreach (var reference in targetBuildOptions.NugetPackageReferences)
{
var path = reference.GetLibPath(nugetPath);
if (!File.Exists(path) && restore)
{
RestoreNugetPackages(graph, target, targetBuildOptions);
restore = false;
}
var dstFile = Path.Combine(outputPath, Path.GetFileName(path));
graph.AddCopyFile(dstFile, path);
}
}
}
}
private static void RestoreNugetPackages(TaskGraph graph, Target target, BuildOptions targetBuildOptions)
{
// Generate a dummy csproj file to restore package from it
var csprojPath = Path.Combine(targetBuildOptions.IntermediateFolder, "nuget.restore.csproj");
var dotnetSdk = DotNetSdk.Instance;
var csProjectFileContent = new StringBuilder();
csProjectFileContent.AppendLine("<Project Sdk=\"Microsoft.NET.Sdk\">");
csProjectFileContent.AppendLine(" <PropertyGroup>");
csProjectFileContent.AppendLine($" <TargetFramework>net{dotnetSdk.Version.Major}.{dotnetSdk.Version.Minor}</TargetFramework>");
csProjectFileContent.AppendLine(" <IsPackable>false</IsPackable>");
csProjectFileContent.AppendLine(" <EnableDefaultItems>false</EnableDefaultItems>");
csProjectFileContent.AppendLine(" <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>");
csProjectFileContent.AppendLine(" <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>");
csProjectFileContent.AppendLine(" <EnableBaseIntermediateOutputPathMismatchWarning>false</EnableBaseIntermediateOutputPathMismatchWarning>");
csProjectFileContent.AppendLine(" <GenerateAssemblyInfo>false</GenerateAssemblyInfo>");
csProjectFileContent.AppendLine($" <LangVersion>{dotnetSdk.CSharpLanguageVersion}</LangVersion>");
csProjectFileContent.AppendLine(" <FileAlignment>512</FileAlignment>");
csProjectFileContent.AppendLine(" <RestorePackages>true</RestorePackages>");
csProjectFileContent.AppendLine(" </PropertyGroup>");
csProjectFileContent.AppendLine(" <ItemGroup>");
foreach (var reference in targetBuildOptions.NugetPackageReferences)
csProjectFileContent.AppendLine($" <PackageReference Include=\"{reference.Name}\" Version=\"{reference.Version}\" />");
csProjectFileContent.AppendLine(" </ItemGroup>");
csProjectFileContent.AppendLine("</Project>");
Utilities.WriteFileIfChanged(csprojPath, csProjectFileContent.ToString());
// Restore packages using dotnet CLI (synchronous to prevent task ordering issues on C# library building)
Log.Info($"Restoring NuGet packages for target {target.Name}");
Utilities.Run(Utilities.GetDotNetPath(), $"restore \"{csprojPath}\"", null, null, Utilities.RunOptions.DefaultTool);
}
}
}

View File

@@ -135,27 +135,7 @@ namespace Flax.Build
// Deploy files
if (!target.IsPreBuilt)
{
using (new ProfileEventScope("DeployFiles"))
{
foreach (var srcFile in targetBuildOptions.OptionalDependencyFiles.Where(File.Exists).Union(targetBuildOptions.DependencyFiles))
{
var dstFile = Path.Combine(outputPath, Path.GetFileName(srcFile));
graph.AddCopyFile(dstFile, srcFile);
}
if (targetBuildOptions.NugetPackageReferences.Any())
{
var nugetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
foreach (var reference in targetBuildOptions.NugetPackageReferences)
{
var path = Path.Combine(nugetPath, reference.Name, reference.Version, "lib", reference.Framework, $"{reference.Name}.dll");
if (!File.Exists(path))
Utilities.RestoreNugetPackages(graph, target);
var dstFile = Path.Combine(outputPath, Path.GetFileName(path));
graph.AddCopyFile(dstFile, path);
}
}
}
DeployFiles(graph, target, targetBuildOptions, outputPath);
}
using (new ProfileEventScope("PostBuild"))
@@ -301,10 +281,10 @@ namespace Flax.Build
// Reference Nuget package
if (buildData.TargetOptions.NugetPackageReferences.Any())
{
var nugetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
var nugetPath = Utilities.GetNugetPackagesPath();
foreach (var reference in buildOptions.NugetPackageReferences)
{
var path = Path.Combine(nugetPath, reference.Name, reference.Version, "lib", reference.Framework, $"{reference.Name}.dll");
var path = reference.GetLibPath(nugetPath);
args.Add(string.Format("/reference:\"{0}\"", path));
}
}

View File

@@ -102,6 +102,11 @@ namespace Flax.Build.NativeCpp
Version = version;
Framework = framework;
}
internal string GetLibPath(string nugetPath)
{
return Path.Combine(nugetPath, Name, Version, "lib", Framework, $"{Name}.dll");
}
}
/// <summary>
@@ -167,7 +172,7 @@ namespace Flax.Build.NativeCpp
/// <summary>
/// The nuget package references.
/// </summary>
public List<NugetPackage> NugetPackageReferences = new List<NugetPackage>();
public HashSet<NugetPackage> NugetPackageReferences = new HashSet<NugetPackage>();
/// <summary>
/// The collection of defines with preprocessing symbol for a source files of this module. Inherited by the modules that include it.

View File

@@ -1057,27 +1057,7 @@ namespace Flax.Build
// Deploy files
if (!buildData.Target.IsPreBuilt)
{
using (new ProfileEventScope("DeployFiles"))
{
foreach (var srcFile in targetBuildOptions.OptionalDependencyFiles.Where(File.Exists).Union(targetBuildOptions.DependencyFiles))
{
var dstFile = Path.Combine(outputPath, Path.GetFileName(srcFile));
graph.AddCopyFile(dstFile, srcFile);
}
if (targetBuildOptions.NugetPackageReferences.Any())
{
var nugetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
foreach (var reference in targetBuildOptions.NugetPackageReferences)
{
var path = Path.Combine(nugetPath, reference.Name, reference.Version, "lib", reference.Framework, $"{reference.Name}.dll");
if (!File.Exists(path))
Utilities.RestoreNugetPackages(graph, target);
var dstFile = Path.Combine(outputPath, Path.GetFileName(path));
graph.AddCopyFile(dstFile, path);
}
}
}
DeployFiles(graph, target, targetBuildOptions, outputPath);
}
using (new ProfileEventScope("PostBuild"))
@@ -1270,27 +1250,7 @@ namespace Flax.Build
// Deploy files
if (!buildData.Target.IsPreBuilt)
{
using (new ProfileEventScope("DeployFiles"))
{
foreach (var srcFile in targetBuildOptions.OptionalDependencyFiles.Where(File.Exists).Union(targetBuildOptions.DependencyFiles))
{
var dstFile = Path.Combine(outputPath, Path.GetFileName(srcFile));
graph.AddCopyFile(dstFile, srcFile);
}
if (targetBuildOptions.NugetPackageReferences.Any())
{
var nugetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
foreach (var reference in targetBuildOptions.NugetPackageReferences)
{
var path = Path.Combine(nugetPath, reference.Name, reference.Version, "lib", reference.Framework, $"{reference.Name}.dll");
if (!File.Exists(path))
Utilities.RestoreNugetPackages(graph, target);
var dstFile = Path.Combine(outputPath, Path.GetFileName(path));
graph.AddCopyFile(dstFile, path);
}
}
}
DeployFiles(graph, target, targetBuildOptions, outputPath);
}
using (new ProfileEventScope("PostBuild"))

View File

@@ -42,21 +42,14 @@ namespace Flax.Build
}
/// <summary>
/// Restores a targets nuget packages.
/// Gets the NuGet packages cache folder path.
/// </summary>
/// <param name="graph">The task graph.</param>
/// <param name="target">The target.</param>
/// <param name="dotNetPath">The dotnet path.</param>
public static void RestoreNugetPackages(Graph.TaskGraph graph, Target target)
/// <returns>The path.</returns>
public static string GetNugetPackagesPath()
{
var dotNetPath = GetDotNetPath();
var task = graph.Add<Graph.Task>();
task.WorkingDirectory = target.FolderPath;
task.InfoMessage = $"Restoring Nuget Packages for {target.Name}";
task.CommandPath = dotNetPath;
task.CommandArguments = $"restore";
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
}
/// <summary>
/// Gets the hash code for the string (the same for all platforms). Matches Engine algorithm for string hashing.
/// </summary>