From 1446c2af159af570be44f2f84a926302c07a8dd7 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 9 Oct 2024 11:57:16 +0100 Subject: [PATCH] Install dotnet tools locally in the Nuget Packages In commit 7a398b01 we added two new tools for compressing textures, `crunch` and `basisu`. The problem there is that the management of the `.config/dotnet-tool.json` file by the users was becoming an issue. We needed a more automatic way to install the required tooling. The problem is using the standard `dotnet tool install` calls requires a `.config/dotnet-tool.json` file to be present in either the current directory or a directory that is in the path ABOVE the current one. You would use the `--create-manifest-if-needed` flag to create the manifest, but that will still leave the users having to manage and upgrade the .json file every time we do a release. So lets get the pipeline to install the tooling itself. The `dotnet tool install` command has an additional argument`--tool-path` this allows us to say where we want the tool installed. Once that has happened we get a native binary launcher in `--tool-path` which allows us to launch the app directly without using the `dotnet` executable. So what this allows us to do is install the tooling locally in the directory that the content pipeline is installed. This will usually be the global `.nuget/package` directory. We need to keep an eye on the `DOTNET_ROOT` environment variable when installing the tooling, just in case a user (or CI) wants to use a custom dotnet install. Fix the calls to basisu and crunch to handle spaces in paths. Same for other area's which shell out to mgcb. Also added support for overriding which MGCB to use in the editor by looking for a `MGCBCommand` environment variable. This should be the full path to either the `mgcb` exe or the `mgcb.dll` that the user wants to use. This will allow users to change to a custom content compiler without having to change the editor. The one downside is users will no longer be able to run `dotnet mgcb` directly in their project directory unless they install the tooling manually. The Editor also needs to download the `mgcb` tool. We place this in the `ApplicationData` folder rather than in the local assembly folder because we cannot guarantee that folder is writable. So in this instance a location specific to the current user seemed like the best approach. The long term plan is to bundle all these tools into a single native library which can be called directly by the content pipeline. --- .github/workflows/main.yml | 10 +--- .vscode/launch.json | 2 +- .../ExternalTool.cs | 51 ++++++++++++++++- ...MonoGame.Framework.Content.Pipeline.csproj | 2 - .../Utilities/BasisUHelpers.cs | 6 +- .../Utilities/CrunchHelpers.cs | 4 +- .../.config/dotnet-tools.json | 6 -- .../.config/dotnet-tools.json | 6 -- .../.config/dotnet-tools.json | 6 -- .../.config/dotnet-tools.json | 6 -- .../.config/dotnet-tools.json | 6 -- .../Common/PipelineController.cs | 55 ++++++++++++------- .../Common/Util.cs | 38 +++++++++++++ .../MonoGame.Content.Builder.Task.props | 4 ++ .../MonoGame.Content.Builder.Task.targets | 18 ++++-- .../BuilderTargetsTest.cs | 6 +- .../BuildContentPipelineTask.cs | 6 -- build/BuildToolsTasks/BuildMGCBEditorTask.cs | 15 +++-- build/BuildToolsTasks/BuildMGCBTask.cs | 10 ++++ build/DeployTasks/UploadArtifactsTask.cs | 47 ++++++++++++++++ 20 files changed, 216 insertions(+), 88 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b61ea766dfe..b00d1bfb0fa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -105,7 +105,6 @@ jobs: - name: Test run: dotnet test Tools/MonoGame.Tools.Tests/MonoGame.Tools.Tests.csproj --blame-hang-timeout 5m -c Release --filter="TestCategory!=Audio" env: - DOTNET_ROOT: ${{github.workspace}}/dotnet64 MGFXC_WINE_PATH: /Users/runner/.winemonogame CI: true if: runner.os == 'macOS' @@ -216,7 +215,7 @@ jobs: uses: actions/download-artifact@v3 with: name: tests-tools-${{ matrix.platform }} - path: tests-tools + path: tests tools - name: Download tests-desktopgl-${{ matrix.platform }} uses: actions/download-artifact@v3 @@ -231,13 +230,8 @@ jobs: path: tests-windowsdx if: runner.os == 'Windows' - - name: Install Tools - run: | - dotnet tool install --create-manifest-if-needed mgcb-basisu - dotnet tool install --create-manifest-if-needed mgcb-crunch - - name: Run Tools Tests - run: dotnet test tests-tools/MonoGame.Tools.Tests.dll --blame-hang-timeout 1m --filter="TestCategory!=Effects" + run: dotnet test "tests tools/MonoGame.Tools.Tests.dll" --blame-hang-timeout 1m --filter="TestCategory!=Effects" env: CI: true diff --git a/.vscode/launch.json b/.vscode/launch.json index bf6c0271172..cd99a61f5a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,7 +20,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "mgcb-editor-mac", - "program": "${workspaceFolder}/Artifacts/MonoGame.Content.Builder.Editor/Mac/Debug/MGCB Editor.app/Contents/MacOS/mgcb-editor-mac", + "program": "${workspaceFolder}/Artifacts/MonoGame.Content.Builder.Editor/Mac/Debug/mgcb-editor-mac.app/Contents/MacOS/mgcb-editor-mac", "args": [], "cwd": "${workspaceFolder}/Artifacts/MonoGame.Content.Builder.Editor/Mac/Debug", "console": "internalConsole", diff --git a/MonoGame.Framework.Content.Pipeline/ExternalTool.cs b/MonoGame.Framework.Content.Pipeline/ExternalTool.cs index c1ceb7b4d2c..e4a8768d800 100644 --- a/MonoGame.Framework.Content.Pipeline/ExternalTool.cs +++ b/MonoGame.Framework.Content.Pipeline/ExternalTool.cs @@ -5,6 +5,8 @@ using System; using System.Diagnostics; using System.IO; +using System.Net; +using System.Reflection; using System.Threading; using MonoGame.Framework.Utilities; @@ -17,6 +19,11 @@ namespace Microsoft.Xna.Framework.Content.Pipeline /// internal class ExternalTool { + public static string Crunch = "mgcb-crunch"; + private static string CrunchVersion = "1.0.4.2"; + public static string BasisU = "mgcb-basisu"; + private static string BasisUVersion = "1.16.4.2"; + public static int Run(string command, string arguments) { string stdout, stderr; @@ -27,13 +34,47 @@ public static int Run(string command, string arguments) return result; } + public static void RestoreDotnetTool(string command, string toolName, string toolVersion, string path) + { + Directory.CreateDirectory(path); + var exe = CurrentPlatform.OS == OS.Windows ? "dotnet.exe" : "dotnet"; + var dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); + if (!string.IsNullOrEmpty(dotnetRoot)) + { + exe = Path.Combine(dotnetRoot, exe); + } + if (Run(exe, $"tool {command} {toolName} --version {toolVersion} --tool-path .", out string _, out string _, workingDirectory: path) != 0) + { + // install the latest + Run(exe, $"tool {command} {toolName} --tool-path .", out _, out _, workingDirectory: path); + } + } + + public static void RestoreDotnetTools() + { + var version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory); + if (CurrentPlatform.OS == OS.Linux) + path= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "linux"); + if (CurrentPlatform.OS == OS.MacOSX) + path= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "osx"); + var versionFile = Path.Combine(path, $"tools_version.txt"); + if (File.Exists(versionFile) && File.ReadAllText(versionFile) == version) + return; + RestoreDotnetTool("install", Crunch, CrunchVersion, path); + RestoreDotnetTool("install", BasisU, BasisUVersion, path); + File.WriteAllText(versionFile, version); + } + /// /// Run a dotnet tool. The tool should be installed in a .config/dotnet-tools.json file somewhere in the project lineage. /// public static int RunDotnetTool(string toolName, string args, out string stdOut, out string stdErr, string stdIn=null, string workingDirectory=null) { - var finalizedArgs = toolName + " " + args; - return ExternalTool.Run("dotnet", finalizedArgs, out stdOut, out stdErr, stdIn, workingDirectory); + RestoreDotnetTools(); + var exe = FindCommand(toolName); + var finalizedArgs = args; + return ExternalTool.Run(exe, finalizedArgs, out stdOut, out stdErr, stdIn, workingDirectory); } public static int Run(string command, string arguments, out string stdout, out string stderr, string stdin = null, string workingDirectory=null) @@ -68,6 +109,12 @@ public static int Run(string command, string arguments, out string stdout, out s if (!string.IsNullOrEmpty(workingDirectory)) processInfo.WorkingDirectory = workingDirectory; + var dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); + if (!string.IsNullOrEmpty(dotnetRoot)) + { + processInfo.EnvironmentVariables["DOTNET_ROOT"] = dotnetRoot; + } + EnsureExecutable(fullPath); using (var process = new Process()) diff --git a/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj b/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj index 9da881f5e69..7859d7e06c5 100644 --- a/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj +++ b/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj @@ -76,8 +76,6 @@ - - diff --git a/MonoGame.Framework.Content.Pipeline/Utilities/BasisUHelpers.cs b/MonoGame.Framework.Content.Pipeline/Utilities/BasisUHelpers.cs index f16fbb40388..def9f462779 100644 --- a/MonoGame.Framework.Content.Pipeline/Utilities/BasisUHelpers.cs +++ b/MonoGame.Framework.Content.Pipeline/Utilities/BasisUHelpers.cs @@ -206,7 +206,7 @@ internal static class BasisU /// The exit code for the basisu process. public static int Run(string args, out string stdOut, out string stdErr, string stdIn=null, string workingDirectory=null) { - return ExternalTool.RunDotnetTool("mgcb-basisu", args, out stdOut, out stdErr, stdIn, workingDirectory); + return ExternalTool.RunDotnetTool(ExternalTool.BasisU, args, out stdOut, out stdErr, stdIn, workingDirectory); } /// @@ -430,7 +430,7 @@ out string error // basisu -unpack foo.ktx2 -ktx_only -linear -format_only 2 var linearFlag = basisUFormat.isLinearColorSpace ? "-linear" : ""; // var linearFlag = ""; - var argStr = $"-unpack -file {basisFileName} -ktx_only -format_only {basisUFormat.code} {linearFlag}"; + var argStr = $"-unpack -file \"{basisFileName}\" -ktx_only -format_only {basisUFormat.code} {linearFlag}"; var exitCode = Run( args: argStr, stdOut: out var stdOut, @@ -489,7 +489,7 @@ private static bool TryBuildIntermediateFile( { var absImageFileName = Path.GetFullPath(imageFileName); var uastcFlag = format.nonUastcCompatible ? "": "-uastc"; - var argStr = $"-file {absImageFileName} {uastcFlag} -ktx2 -output_file {intermediateFileName}"; + var argStr = $"-file \"{absImageFileName}\" {uastcFlag} -ktx2 -output_file \"{intermediateFileName}\""; var exitCode = Run( args: argStr, stdOut: out var stdOut, diff --git a/MonoGame.Framework.Content.Pipeline/Utilities/CrunchHelpers.cs b/MonoGame.Framework.Content.Pipeline/Utilities/CrunchHelpers.cs index 8ef1ae3f97a..99b78a1cd85 100644 --- a/MonoGame.Framework.Content.Pipeline/Utilities/CrunchHelpers.cs +++ b/MonoGame.Framework.Content.Pipeline/Utilities/CrunchHelpers.cs @@ -137,7 +137,7 @@ internal static class Crunch /// The exit code for the basisu process. private static int Run(string args, out string stdOut, out string stdErr) { - return ExternalTool.RunDotnetTool("mgcb-crunch", args, out stdOut, out stdErr); + return ExternalTool.RunDotnetTool(ExternalTool.Crunch, args, out stdOut, out stdErr); } /// @@ -233,7 +233,7 @@ out string errorMessage ) { errorMessage = null; - var argStr = $"-file {pngFileName} -{format.formatString} -out {intermediateFileName} -fileformat ktx -forceprimaryencoding -noNormalDetection"; + var argStr = $"-file \"{pngFileName}\" -{format.formatString} -out \"{intermediateFileName}\" -fileformat ktx -forceprimaryencoding -noNormalDetection"; var exitCode = Run( args: argStr, stdOut: out var stdOut, diff --git a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.Android.CSharp/.config/dotnet-tools.json b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.Android.CSharp/.config/dotnet-tools.json index a966e81a617..099bf0db52e 100644 --- a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.Android.CSharp/.config/dotnet-tools.json +++ b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.Android.CSharp/.config/dotnet-tools.json @@ -2,12 +2,6 @@ "version": 1, "isRoot": true, "tools": { - "dotnet-mgcb": { - "version": "3.8.2.1-develop", - "commands": [ - "mgcb" - ] - }, "dotnet-mgcb-editor": { "version": "3.8.2.1-develop", "commands": [ diff --git a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.DesktopGL.CSharp/.config/dotnet-tools.json b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.DesktopGL.CSharp/.config/dotnet-tools.json index a966e81a617..099bf0db52e 100644 --- a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.DesktopGL.CSharp/.config/dotnet-tools.json +++ b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.DesktopGL.CSharp/.config/dotnet-tools.json @@ -2,12 +2,6 @@ "version": 1, "isRoot": true, "tools": { - "dotnet-mgcb": { - "version": "3.8.2.1-develop", - "commands": [ - "mgcb" - ] - }, "dotnet-mgcb-editor": { "version": "3.8.2.1-develop", "commands": [ diff --git a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.WindowsDX.CSharp/.config/dotnet-tools.json b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.WindowsDX.CSharp/.config/dotnet-tools.json index a966e81a617..099bf0db52e 100644 --- a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.WindowsDX.CSharp/.config/dotnet-tools.json +++ b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.WindowsDX.CSharp/.config/dotnet-tools.json @@ -2,12 +2,6 @@ "version": 1, "isRoot": true, "tools": { - "dotnet-mgcb": { - "version": "3.8.2.1-develop", - "commands": [ - "mgcb" - ] - }, "dotnet-mgcb-editor": { "version": "3.8.2.1-develop", "commands": [ diff --git a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.iOS.CSharp/.config/dotnet-tools.json b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.iOS.CSharp/.config/dotnet-tools.json index a966e81a617..099bf0db52e 100644 --- a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.iOS.CSharp/.config/dotnet-tools.json +++ b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Application.iOS.CSharp/.config/dotnet-tools.json @@ -2,12 +2,6 @@ "version": 1, "isRoot": true, "tools": { - "dotnet-mgcb": { - "version": "3.8.2.1-develop", - "commands": [ - "mgcb" - ] - }, "dotnet-mgcb-editor": { "version": "3.8.2.1-develop", "commands": [ diff --git a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Library.CSharp/.config/dotnet-tools.json b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Library.CSharp/.config/dotnet-tools.json index a966e81a617..099bf0db52e 100644 --- a/Templates/MonoGame.Templates.CSharp/content/MonoGame.Library.CSharp/.config/dotnet-tools.json +++ b/Templates/MonoGame.Templates.CSharp/content/MonoGame.Library.CSharp/.config/dotnet-tools.json @@ -2,12 +2,6 @@ "version": 1, "isRoot": true, "tools": { - "dotnet-mgcb": { - "version": "3.8.2.1-develop", - "commands": [ - "mgcb" - ] - }, "dotnet-mgcb-editor": { "version": "3.8.2.1-develop", "commands": [ diff --git a/Tools/MonoGame.Content.Builder.Editor/Common/PipelineController.cs b/Tools/MonoGame.Content.Builder.Editor/Common/PipelineController.cs index 8f5dbb2764e..e5e58858e7f 100644 --- a/Tools/MonoGame.Content.Builder.Editor/Common/PipelineController.cs +++ b/Tools/MonoGame.Content.Builder.Editor/Common/PipelineController.cs @@ -35,12 +35,17 @@ public partial class PipelineController : IController private static readonly string [] _mgcbSearchPaths = new [] { #if DEBUG +#if MAC + Path.Combine(Path.GetDirectoryName(System.AppContext.BaseDirectory) ?? "", "../../../MonoGame.Content.Builder/Debug/mgcb.dll"), + Path.Combine(Path.GetDirectoryName(System.AppContext.BaseDirectory) ?? "", "../../../../../MonoGame.Content.Builder/Debug/mgcb.dll"), +#else Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "../../../MonoGame.Content.Builder/Debug/mgcb.dll"), Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "../../../../../../MonoGame.Content.Builder/Debug/mgcb.dll"), +#endif #else #if MAC Path.Combine(Path.GetDirectoryName(System.AppContext.BaseDirectory) ?? "", "../../../MonoGame.Content.Builder/Release/mgcb.dll"), - Path.Combine(Path.GetDirectoryName(System.AppContext.BaseDirectory) ?? "", "../../../../../../MonoGame.Content.Builder/Release/mgcb.dll"), + Path.Combine(Path.GetDirectoryName(System.AppContext.BaseDirectory) ?? "", "../../../../../MonoGame.Content.Builder/Release/mgcb.dll"), #else Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "../../../MonoGame.Content.Builder/Release/mgcb.dll"), Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "../../../../../../MonoGame.Content.Builder/Release/mgcb.dll"), @@ -127,6 +132,7 @@ private PipelineController(IView view) LoadTemplates(templatesPath); #endif + RestoreMGCB(); UpdateMenu(); view.UpdateRecentList(PipelineSettings.Default.ProjectHistory); @@ -574,6 +580,22 @@ public void Clean() UpdateMenu(); } + private void RestoreMGCB() + { + var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + var workingDirectory = Path.Combine(appDataPath, "mgcb-dotnet-tool", version); + if (Directory.Exists(workingDirectory)) + return; + Directory.CreateDirectory(workingDirectory); + var dotnet = Global.Unix ? "dotnet" : "dotnet.exe"; + if (Util.Run(dotnet, $"tool install dotnet-mgcb --version {version} --tool-path .", workingDirectory) != 0) + { + // install the latest + Util.Run(dotnet, $"tool install dotnet-mgcb --tool-path .", workingDirectory); + } + } + private void DoBuild(string commands) { Encoding encoding; @@ -585,9 +607,11 @@ private void DoBuild(string commands) encoding = Encoding.UTF8; } - var mgcbCommand = "mgcb"; + + var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var version = Assembly.GetExecutingAssembly().GetName().Version.ToString (); + var mgcbCommand = Path.Combine(appDataPath, "mgcb-dotnet-tool", version, "mgcb"); var currentDir = Environment.CurrentDirectory; - foreach (var path in _mgcbSearchPaths) { var fullPath = Path.Combine(currentDir, path); @@ -598,26 +622,17 @@ private void DoBuild(string commands) break; } } + // allow the users to override the path with an environment variable + // the same as the MSBuild property in the .targets + var mgcbUserPath = Environment.GetEnvironmentVariable("MGCBCommand"); + if (!string.IsNullOrEmpty(mgcbUserPath) && File.Exists(mgcbUserPath)) + { + mgcbCommand = mgcbUserPath; + } try { - // Prepare the process. - _buildProcess = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Global.Unix ? "dotnet" : "dotnet.exe", - Arguments = $"{mgcbCommand} {commands}", - WorkingDirectory = Path.GetDirectoryName(_project.OriginalPath), - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = false, - RedirectStandardOutput = true, - StandardOutputEncoding = encoding - } - }; - _buildProcess.OutputDataReceived += (sender, args) => View.OutputAppend(args.Data); - + _buildProcess = Util.CreateProcess(mgcbCommand, commands, Path.GetDirectoryName (_project.OriginalPath), encoding, (s) => View.OutputAppend (s)); // Fire off the process. Console.WriteLine(_buildProcess.StartInfo.FileName + " " + _buildProcess.StartInfo.Arguments); Environment.CurrentDirectory = _buildProcess.StartInfo.WorkingDirectory; diff --git a/Tools/MonoGame.Content.Builder.Editor/Common/Util.cs b/Tools/MonoGame.Content.Builder.Editor/Common/Util.cs index 8684e46ad08..c791bd4eba2 100644 --- a/Tools/MonoGame.Content.Builder.Editor/Common/Util.cs +++ b/Tools/MonoGame.Content.Builder.Editor/Common/Util.cs @@ -1,6 +1,8 @@ using System; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text; namespace MonoGame.Tools.Pipeline { @@ -46,5 +48,41 @@ public static string GetRelativePath(string filespec, string folder) return result; } + + public static int Run(string command, string arguments, string workingDirectory) + { + var process = CreateProcess(command, arguments, workingDirectory, Encoding.UTF8, (s) => Console.WriteLine(s)); + process.Start(); + process.BeginOutputReadLine(); + process.WaitForExit(); + return process.ExitCode; + } + + public static Process CreateProcess(string command, string arguments, string workingDirectory, Encoding encoding, Action output) + { + var exe = command; + var args = arguments; + if (command.EndsWith (".dll")) { + // we are referencing the dll directly. We need to call dotnet to host. + exe = Global.Unix ? "dotnet" : "dotnet.exe"; + args = $"\"{command}\" {arguments}"; + } + var _buildProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = $"\"{exe}\"", + Arguments = args, + WorkingDirectory = workingDirectory, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = false, + RedirectStandardOutput = true, + StandardOutputEncoding = encoding + } + }; + _buildProcess.OutputDataReceived += (sender, args) => output(args.Data); + return _buildProcess; + } } } diff --git a/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.props b/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.props index d3e96526443..e2b66c6b931 100644 --- a/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.props +++ b/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.props @@ -3,7 +3,11 @@ dotnet true + + true + $(MSBuildThisFileDirectory)dotnet-tools/ mgcb + 3.8.1.1-develop diff --git a/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.targets b/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.targets index 4c2ab79c492..fb1cdc4766b 100644 --- a/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.targets +++ b/Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.targets @@ -84,6 +84,12 @@ + + + + + + - + - - - + + <_Command Condition="Exists ('$(MGCBToolDirectory)\$(MGCBCommand)')">"$(MGCBToolDirectory)\$(MGCBCommand)" + + <_Command Condition=" '$(_Command)' == '' ">"$(DotnetCommand)" "$(MGCBCommand)" + diff --git a/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs b/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs index 990d3c9d61a..349dad0f5ac 100644 --- a/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs +++ b/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs @@ -31,7 +31,7 @@ bool RunBuild(string buildTool, string projectFile, params string[] parameters) var tool = FindTool(buildTool); var psi = new ProcessStartInfo(tool) { - Arguments = $"build {projectFile} -t:IncludeContent {string.Join(" ", parameters)} -tl:off -bl -p:DotnetCommand=\"{tool}\"", + Arguments = $"build \"{projectFile}\" -t:IncludeContent {string.Join(" ", parameters)} -tl:off -bl -p:DotnetCommand=\"{tool}\"", WorkingDirectory = root, UseShellExecute = false, RedirectStandardOutput = true, @@ -72,11 +72,11 @@ public void BuildSimpleProject() Directory.Delete(outputPath, recursive: true); var result = RunBuild("dotnet", Path.Combine(root, "Assets", "Projects", "BuildSimpleProject.csproj"), new string[] { - "-p:MGCBCommand=" + Path.Combine(root, "mgcb.dll") + $"-p:MGCBCommand=\"{Path.Combine(root, "mgcb.dll")}\"" }); Assert.AreEqual(true, result, "Content Build should have succeeded."); var contentFont = Path.Combine(outputPath, "DesktopGL", "Content", "ContentFont.xnb"); - Assert.IsTrue(File.Exists(contentFont), "'" + contentFont + "' should exist."); + Assert.IsTrue(File.Exists(contentFont), $"'{contentFont}' should exist."); } } } diff --git a/build/BuildToolsTasks/BuildContentPipelineTask.cs b/build/BuildToolsTasks/BuildContentPipelineTask.cs index fed3f828928..2f6018bd76e 100644 --- a/build/BuildToolsTasks/BuildContentPipelineTask.cs +++ b/build/BuildToolsTasks/BuildContentPipelineTask.cs @@ -9,11 +9,5 @@ public override void Run(BuildContext context) { var builderPath = context.GetProjectPath(ProjectType.ContentPipeline); context.DotNetPack(builderPath, context.DotNetPackSettings); - - // ensure that the local development has the required dotnet tools. - // this won't actually include the tool manifest in a final build, - // but it will setup a local developer's project - context.DotNetTool(builderPath, "tool install --create-manifest-if-needed mgcb-basisu"); - context.DotNetTool(builderPath, "tool install --create-manifest-if-needed mgcb-crunch"); } } diff --git a/build/BuildToolsTasks/BuildMGCBEditorTask.cs b/build/BuildToolsTasks/BuildMGCBEditorTask.cs index c499dcb168f..8ded808d6d3 100644 --- a/build/BuildToolsTasks/BuildMGCBEditorTask.cs +++ b/build/BuildToolsTasks/BuildMGCBEditorTask.cs @@ -8,12 +8,15 @@ public sealed class BuildMGCBEditorTask : FrostingTask { public override void Run(BuildContext context) { - context.ReplaceRegexInFiles( - @"CFBundleShortVersionString<\/key>\s*([^\s]*)<\/string>", - "Tools/MonoGame.Content.Builder.Editor/Info.plist", - $"CFBundleShortVersionString\n\t{context.Version}", - RegexOptions.Singleline - ); + if (context.BuildSystem().IsRunningOnGitHubActions) + { + context.ReplaceRegexInFiles( + "Tools/MonoGame.Content.Builder.Editor/Info.plist", + @"CFBundleShortVersionString<\/key>\s*([^\s]*)<\/string>", + $"CFBundleShortVersionString\n\t{context.Version}", + RegexOptions.Singleline + ); + } var platform = context.Environment.Platform.Family switch { diff --git a/build/BuildToolsTasks/BuildMGCBTask.cs b/build/BuildToolsTasks/BuildMGCBTask.cs index c2276d601fc..a8f4cd1ac33 100644 --- a/build/BuildToolsTasks/BuildMGCBTask.cs +++ b/build/BuildToolsTasks/BuildMGCBTask.cs @@ -1,3 +1,4 @@ +using System.Text.RegularExpressions; namespace BuildScripts; @@ -7,6 +8,15 @@ public sealed class BuildMGCBTask : FrostingTask { public override void Run(BuildContext context) { + if (context.BuildSystem().IsRunningOnGitHubActions) + { + context.ReplaceRegexInFiles( + "Tools/MonoGame.Content.Builder.Task/MonoGame.Content.Builder.Task.props", + @"([^\s]*)<\/MonoGameVersion>", + $"{context.Version}", + RegexOptions.Singleline + ); + } context.DotNetPack(context.GetProjectPath(ProjectType.Tools, "MonoGame.Content.Builder"), context.DotNetPackSettings); context.DotNetPack(context.GetProjectPath(ProjectType.Tools, "MonoGame.Content.Builder.Task"), context.DotNetPackSettings); } diff --git a/build/DeployTasks/UploadArtifactsTask.cs b/build/DeployTasks/UploadArtifactsTask.cs index 61abf7eceef..c50723d0290 100644 --- a/build/DeployTasks/UploadArtifactsTask.cs +++ b/build/DeployTasks/UploadArtifactsTask.cs @@ -15,10 +15,57 @@ public override async Task RunAsync(BuildContext context) _ => "linux" }; + // Clean up build tools if installed + // otherwise we get permission issues after extraction + // because the zip removes all the permissions. + // Plus in windows hidden files (like the .store directory) + // are ignored. This causes `dotnet tool` to error. + var path = System.IO.Path.Combine(context.BuildOutput, "Tests", "Tools", "Release", "dotnet-tools"); + if (System.IO.Directory.Exists(path)) { + context.Log.Information ($"Deleting: {path}"); + System.IO.Directory.Delete (path, recursive: true); + } + if (context.IsRunningOnMacOs()) + { + path = System.IO.Path.Combine(context.BuildOutput, "Tests", "Tools", "Release", "osx"); + DeleteToolStore(context, path); + } + if (context.IsRunningOnLinux()) + { + path = System.IO.Path.Combine(context.BuildOutput, "Tests", "Tools", "Release", "linux"); + DeleteToolStore(context, path); + } + if (context.IsRunningOnWindows()) + { + path = System.IO.Path.Combine(context.BuildOutput, "Tests", "Tools", "Release"); + DeleteToolStore(context, path); + } + + await context.GitHubActions().Commands.UploadArtifact(new DirectoryPath(context.NuGetsDirectory.FullPath), $"nuget-{os}"); await context.GitHubActions().Commands.UploadArtifact(new DirectoryPath(System.IO.Path.Combine(context.BuildOutput, "Tests", "Tools", "Release")), $"tests-tools-{os}"); await context.GitHubActions().Commands.UploadArtifact(new DirectoryPath(System.IO.Path.Combine(context.BuildOutput, "Tests", "DesktopGL", "Release")), $"tests-desktopgl-{os}"); if (context.IsRunningOnWindows()) await context.GitHubActions().Commands.UploadArtifact(new DirectoryPath(System.IO.Path.Combine(context.BuildOutput, "Tests", "WindowsDX", "Release")), $"tests-windowsdx-{os}"); } + + void DeleteToolStore(BuildContext context, string path) + { + if (System.IO.Directory.Exists(path)) { + var store = System.IO.Path.Combine (path, ".store"); + if (System.IO.Directory.Exists(store)) { + context.Log.Information($"Deleting: {store}"); + System.IO.Directory.Delete(store, recursive: true); + foreach (var file in System.IO.Directory.GetFiles(path, "mgcb-*", System.IO.SearchOption.TopDirectoryOnly)) { + context.Log.Information($"Deleting: {file}"); + System.IO.File.Delete(file); + } + foreach (var file in System.IO.Directory.GetFiles(path, "tools_version.txt", System.IO.SearchOption.TopDirectoryOnly)) { + context.Log.Information($"Deleting: {file}"); + System.IO.File.Delete(file); + } + + } + } + } }