From 7378694f3b36f65981d449c8539c760c54010827 Mon Sep 17 00:00:00 2001 From: Alistair Chapman Date: Thu, 21 Mar 2019 11:05:52 -0700 Subject: [PATCH] Initial support for overriding the location of cake.exe the TRX uses Signed-off-by: Alistair Chapman --- src/Cake.VisualStudio.csproj | 1 + src/Configuration/ConfigurationParser.cs | 50 ++++++++++++++++++ src/Configuration/ToolLocator.cs | 67 ++++++++++++++++++++++++ src/TaskRunner/TaskRunner.cs | 35 ++++--------- src/TaskRunner/TaskRunnerConfig.cs | 31 +---------- 5 files changed, 131 insertions(+), 53 deletions(-) create mode 100644 src/Configuration/ToolLocator.cs diff --git a/src/Cake.VisualStudio.csproj b/src/Cake.VisualStudio.csproj index 2b6cf58..849d469 100644 --- a/src/Cake.VisualStudio.csproj +++ b/src/Cake.VisualStudio.csproj @@ -87,6 +87,7 @@ + diff --git a/src/Configuration/ConfigurationParser.cs b/src/Configuration/ConfigurationParser.cs index 846df5e..0e5c154 100644 --- a/src/Configuration/ConfigurationParser.cs +++ b/src/Configuration/ConfigurationParser.cs @@ -2,9 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Cake.VisualStudio.Helpers; using IniParser; using IniParser.Model.Configuration; using IniParser.Parser; +using System.IO; +using System.Linq; namespace Cake.VisualStudio.Configuration { @@ -12,6 +15,7 @@ internal sealed class ConfigurationParser { private static IniDataParser Parser => new IniDataParser(new IniParserConfiguration {AssigmentSpacer = ""}); internal string SectionName { get; set; } = "TaskRunnerBindings"; + public ConfigurationParser(string filePath) { FilePath = filePath; @@ -46,5 +50,51 @@ internal void RemoveBindings() var removeSection = data.Sections.RemoveSection(SectionName); parser.WriteFile(FilePath, data); } + + /// + /// Gets a key from the Paths section of cake.config + /// + /// The key to retrieve. + /// The config value + /// + /// This defaults the key to Tools since that's what we're using currently. + /// I've left this an argument in case we add a non-standard key to cake.config to specifically override VS. + /// + internal string GetToolsPath(string key = "Tools") + { + var parser = new FileIniDataParser(Parser); + var data = parser.ReadFile(FilePath); + if (data.Sections.ContainsSection("Paths")) + { + return data["Paths"].ReadValues(key).LastOrDefault(); + } + return null; + } + + internal static string GetConfigFilePath(string configPath, bool create = false) + { + string bindingPath; + var path = CakePackage.Dte?.Solution?.FindProjectItem(Constants.ConfigFileName); + if (path != null && path.FileCount == 1) + { + bindingPath = path.FileNames[1]; + } + else + { + var cpath = Path.Combine(Path.GetDirectoryName(configPath), Constants.ConfigFileName); + try + { + + if (!File.Exists(cpath) && create) File.Create(cpath).Close(); + if (File.Exists(cpath)) ProjectHelpers.GetSolutionItemsProject(CakePackage.Dte).AddFileToProject(cpath); + } + catch + { + // ignored + } + bindingPath = cpath; + } + return string.IsNullOrWhiteSpace(bindingPath) ? null : bindingPath; // remove the empty string scenario + } } } \ No newline at end of file diff --git a/src/Configuration/ToolLocator.cs b/src/Configuration/ToolLocator.cs new file mode 100644 index 0000000..711b096 --- /dev/null +++ b/src/Configuration/ToolLocator.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.VisualStudio.Helpers; +using Microsoft.VisualStudio.Shell; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Cake.VisualStudio.Configuration +{ + internal sealed class ToolLocator + { + private readonly string _executable; + private List KnownPaths { get; set; } = new List(); + + public ToolLocator(string executableName) + { + _executable = executableName; + } + + public ToolLocator AddEnvironmentVariables(string variable = "CAKE_PATHS_TOOLS") + { + var env = Environment.GetEnvironmentVariable(variable); + if (!string.IsNullOrWhiteSpace(env) && Directory.Exists(env)) + { + KnownPaths.Add(env); + } + return this; + } + + public ToolLocator AddKnownPaths(params string[] paths) + { + KnownPaths.AddRange(paths); + return this; + } + + public ToolLocator AddConfigPath(Func configFunc) + { + var path = configFunc?.Invoke()?.GetToolsPath(); + if (path != null) + { + KnownPaths.Add(path); + KnownPaths.Add(Path.Combine(path, "Cake")); + } + return this; + } + + public string Locate(string workingDirectory) + { + foreach (var path in KnownPaths.Select(p => p.TrimPrefix("./"))) + { + var fullPath = Path.Combine(workingDirectory, path, _executable); + if (File.Exists(fullPath)) return fullPath; + } + if (PathHelpers.ExistsOnPath(_executable)) + { + return _executable; // assume PATH + } + return null; + } + } +} diff --git a/src/TaskRunner/TaskRunner.cs b/src/TaskRunner/TaskRunner.cs index d816f61..10e4c87 100644 --- a/src/TaskRunner/TaskRunner.cs +++ b/src/TaskRunner/TaskRunner.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; +using Cake.VisualStudio.Configuration; using Cake.VisualStudio.Helpers; using Microsoft.VisualStudio.TaskRunnerExplorer; @@ -82,19 +83,6 @@ private ITaskRunnerNode NotFoundNode(string configPath) Description = message, Command = new TaskRunnerCommand(Path.GetDirectoryName(configPath), "echo", message), }; - /* - * return new TaskRunnerNode("Cake") - { - Children = - { - new TaskRunnerNode("Cake.exe not found", true) - { - Description = message, - Command = new TaskRunnerCommand(Path.GetDirectoryName(configPath), "echo", message), - } - } - }; - */ } private ITaskRunnerNode LoadHierarchy(string configPath) @@ -141,17 +129,16 @@ private ITaskRunnerCommand GetCommand(string cwd, string arguments) private static string GetCakePath(string cwd) { - var knownPaths = new[] {"tools/Cake/Cake.exe", "Cake/Cake.exe", "Cake.exe"}; - foreach (var path in knownPaths) - { - var fullPath = Path.Combine(cwd, path); - if (File.Exists(fullPath)) return fullPath; - } - if (PathHelpers.ExistsOnPath("cake.exe") || PathHelpers.ExistsOnPath("cake")) - { - return "cake"; // assume PATH - } - return null; + var locator = new ToolLocator("cake.exe") + .AddConfigPath(() => new ConfigurationParser(ConfigurationParser.GetConfigFilePath(cwd))) + .AddEnvironmentVariables() + .AddKnownPaths("tools/Cake", "Cake", "."); + var path = locator.Locate(cwd); + return string.IsNullOrWhiteSpace(path) + ? PathHelpers.ExistsOnPath("cake.exe") + ? "cake" + : null + : path; } private static string GetExecutableFolder() diff --git a/src/TaskRunner/TaskRunnerConfig.cs b/src/TaskRunner/TaskRunnerConfig.cs index 5444d97..6657e72 100644 --- a/src/TaskRunner/TaskRunnerConfig.cs +++ b/src/TaskRunner/TaskRunnerConfig.cs @@ -35,40 +35,13 @@ public void Dispose() public string LoadBindings(string configPath) { - string bindingPath = GetBindingPath(configPath) ?? configPath + ".bindings"; - + string bindingPath = ConfigurationParser.GetConfigFilePath(configPath) ?? configPath + ".bindings"; return File.Exists(bindingPath) ? new ConfigurationParser(bindingPath).LoadBinding().ToXml() : ""; } - private string GetBindingPath(string configPath, bool create = false) - { - string bindingPath; - var path = CakePackage.Dte.Solution?.FindProjectItem(Constants.ConfigFileName); - if (path != null && path.FileCount == 1) - { - bindingPath = path.FileNames[1]; - } - else - { - var cpath = Path.Combine(Path.GetDirectoryName(configPath), Constants.ConfigFileName); - try - { - - if (!File.Exists(cpath) && create) File.Create(cpath).Close(); - if (File.Exists(cpath)) ProjectHelpers.GetSolutionItemsProject(CakePackage.Dte).AddFileToProject(cpath); - } - catch - { - // ignored - } - bindingPath = cpath; - } - return string.IsNullOrWhiteSpace(bindingPath) ? null : bindingPath; // remove the empty string scenario - } - public bool SaveBindings(string configPath, string bindingsXml) { - string bindingPath = GetBindingPath(configPath, true) ?? configPath + ".bindings"; + string bindingPath = ConfigurationParser.GetConfigFilePath(configPath, true) ?? configPath + ".bindings"; var config = new ConfigurationParser(bindingPath); try {