From ecfd03f79ca809bae576441b3447706fab642678 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Jan 2026 18:49:47 +0100 Subject: [PATCH] Fix restoring `NuGet` packages for target with multiple projects Fix restoring `NuGet` packages to run before project build to ensure files are downloaded #3900 --- Source/Tools/Flax.Build/Build/Builder.cs | 61 +++++++++++++++++++ .../Flax.Build/Build/DotNet/Builder.DotNet.cs | 26 +------- .../Build/NativeCpp/BuildOptions.cs | 7 ++- .../Build/NativeCpp/Builder.NativeCpp.cs | 44 +------------ .../Tools/Flax.Build/Utilities/Utilities.cs | 17 ++---- 5 files changed, 77 insertions(+), 78 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Builder.cs b/Source/Tools/Flax.Build/Build/Builder.cs index 3a4286254..3b00daf72 100644 --- a/Source/Tools/Flax.Build/Build/Builder.cs +++ b/Source/Tools/Flax.Build/Build/Builder.cs @@ -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(""); + csProjectFileContent.AppendLine(" "); + csProjectFileContent.AppendLine($" net{dotnetSdk.Version.Major}.{dotnetSdk.Version.Minor}"); + csProjectFileContent.AppendLine(" false"); + csProjectFileContent.AppendLine(" false"); + csProjectFileContent.AppendLine(" false"); + csProjectFileContent.AppendLine(" false"); + csProjectFileContent.AppendLine(" false"); + csProjectFileContent.AppendLine(" false"); + csProjectFileContent.AppendLine($" {dotnetSdk.CSharpLanguageVersion}"); + csProjectFileContent.AppendLine(" 512"); + csProjectFileContent.AppendLine(" true"); + csProjectFileContent.AppendLine(" "); + csProjectFileContent.AppendLine(" "); + foreach (var reference in targetBuildOptions.NugetPackageReferences) + csProjectFileContent.AppendLine($" "); + csProjectFileContent.AppendLine(" "); + csProjectFileContent.AppendLine(""); + 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); + } } } diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 23646a3d7..54136b490 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -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)); } } diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs index 13ecd1982..ea1eb1ade 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs @@ -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"); + } } /// @@ -167,7 +172,7 @@ namespace Flax.Build.NativeCpp /// /// The nuget package references. /// - public List NugetPackageReferences = new List(); + public HashSet NugetPackageReferences = new HashSet(); /// /// The collection of defines with preprocessing symbol for a source files of this module. Inherited by the modules that include it. diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 4c9d521b4..322f1c3c1 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -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")) diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 917b8aa77..872b269ee 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -42,21 +42,14 @@ namespace Flax.Build } /// - /// Restores a targets nuget packages. + /// Gets the NuGet packages cache folder path. /// - /// The task graph. - /// The target. - /// The dotnet path. - public static void RestoreNugetPackages(Graph.TaskGraph graph, Target target) + /// The path. + public static string GetNugetPackagesPath() { - var dotNetPath = GetDotNetPath(); - var task = graph.Add(); - 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"); } - + /// /// Gets the hash code for the string (the same for all platforms). Matches Engine algorithm for string hashing. ///