diff --git a/data/options/SharpDevelop-tools.xml b/data/options/SharpDevelop-tools.xml index 2eb7879ed7..5f8dedf2bd 100644 --- a/data/options/SharpDevelop-tools.xml +++ b/data/options/SharpDevelop-tools.xml @@ -9,14 +9,14 @@ ${TargetDir} "${TargetPath}" - ${NetSdkDir}bin\ILDasm.exe + ${SdkToolPath:ILDasm.exe} IL Dasm False ${TargetDir} - ${NetSdkDir}bin\FUSLOGVW.exe + ${SdkToolPath:FUSLOGVW.exe} Assembly Binding Log Viewer False diff --git a/src/Main/Base/Project/Src/Commands/CustomStringTagProvider.cs b/src/Main/Base/Project/Src/Commands/CustomStringTagProvider.cs index 50bb62d6be..e8f49f3eed 100644 --- a/src/Main/Base/Project/Src/Commands/CustomStringTagProvider.cs +++ b/src/Main/Base/Project/Src/Commands/CustomStringTagProvider.cs @@ -15,6 +15,10 @@ using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.SharpDevelop.Commands { + /// + /// Provides tag to string mapping for SharpDevelop. Tags are mapped to strings by several methods + /// such as registry and resource files. + /// public class SharpDevelopStringTagProvider : IStringTagProvider { readonly static string[] tags = new string[] { @@ -26,8 +30,7 @@ namespace ICSharpCode.SharpDevelop.Commands "CombineDir", "CombineFilename", "SolutionDir", "SolutionFilename", "Startuppath", - "TaskService.Warnings", "TaskService.Errors", "TaskService.Messages", - "NetSdkDir" + "TaskService.Warnings", "TaskService.Errors", "TaskService.Messages" }; public string[] Tags { @@ -70,8 +73,6 @@ namespace ICSharpCode.SharpDevelop.Commands } switch (tag.ToUpperInvariant()) { - case "NETSDKDIR": - return FileUtility.NetSdkInstallRoot; case "ITEMPATH": try { return GetCurrentItemPath(); diff --git a/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs b/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs index dc699a8254..cec37b67fc 100644 --- a/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs +++ b/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs @@ -227,52 +227,69 @@ namespace ICSharpCode.SharpDevelop.Commands TaskService.BuildMessageViewCategory.AppendText(output + Environment.NewLine + "${res:XML.MainMenu.ToolMenu.ExternalTools.ExitedWithCode} " + p.ExitCode + Environment.NewLine); } + + /// + /// This handler gets called when a tool in the Tool menu is clicked on. + /// + /// The MenuCommand that sent the event. + /// Event arguments. void ToolEvt(object sender, EventArgs e) { MenuCommand item = (MenuCommand)sender; + // TODO: ToolLoader.Tool should get a string indexor. Overloading List or making it a Dictionary would work. for (int i = 0; i < ToolLoader.Tool.Count; ++i) { - if (item.Text == ToolLoader.Tool[i].ToString()) { - ExternalTool tool = (ExternalTool)ToolLoader.Tool[i]; + if (item.Text != ToolLoader.Tool[i].ToString()) { continue; } + ExternalTool tool = (ExternalTool)ToolLoader.Tool[i]; + + // Set these to somewhat useful values in case StingParser.Parse() passes when being called on one of them. + string command = tool.Command; + string args = tool.Arguments; + + // This needs it's own try/catch because if parsing these messages fail, the catch block after + // the second try would also throw because MessageService.ShowError() calls StringParser.Parse() + try { + command = StringParser.Parse(tool.Command); + args = StringParser.Parse(tool.Arguments); + } catch (Exception ex) { + MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + ex.Message); + return; + } - string command = StringParser.Parse(tool.Command); - string args = StringParser.Parse(tool.Arguments); + if (tool.PromptForArguments) { + InputBox box = new InputBox(); + box.Text = tool.MenuCommand; + box.Label.Text = ResourceService.GetString("XML.MainMenu.ToolMenu.ExternalTools.EnterArguments"); + box.TextBox.Text = args; + if (box.ShowDialog() != DialogResult.OK) + return; + args = box.TextBox.Text; + } - if (tool.PromptForArguments) { - InputBox box = new InputBox(); - box.Text = tool.MenuCommand; - box.Label.Text = ResourceService.GetString("XML.MainMenu.ToolMenu.ExternalTools.EnterArguments"); - box.TextBox.Text = args; - if (box.ShowDialog() != DialogResult.OK) - return; - args = box.TextBox.Text; + try { + ProcessStartInfo startinfo; + if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) { + startinfo = new ProcessStartInfo(command); + } else { + startinfo = new ProcessStartInfo(command, args); } - try { - ProcessStartInfo startinfo; - if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) { - startinfo = new ProcessStartInfo(command); - } else { - startinfo = new ProcessStartInfo(command, args); - } - - startinfo.WorkingDirectory = StringParser.Parse(tool.InitialDirectory); - if (tool.UseOutputPad) { - startinfo.UseShellExecute = false; - startinfo.RedirectStandardOutput = true; - } - Process process = new Process(); - process.EnableRaisingEvents = true; - process.StartInfo = startinfo; - if (tool.UseOutputPad) { - process.Exited += new EventHandler(ProcessExitEvent); - } - process.Start(); - } catch (Exception ex) { - MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + command + " " + args + "'\n" + ex.Message); + startinfo.WorkingDirectory = StringParser.Parse(tool.InitialDirectory); + if (tool.UseOutputPad) { + startinfo.UseShellExecute = false; + startinfo.RedirectStandardOutput = true; + } + Process process = new Process(); + process.EnableRaisingEvents = true; + process.StartInfo = startinfo; + if (tool.UseOutputPad) { + process.Exited += new EventHandler(ProcessExitEvent); } - break; + process.Start(); + } catch (Exception ex) { + MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + command + " " + args + "'\n" + ex.Message); } + return; } } } diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/CreateKey.cs b/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/CreateKey.cs index b25408a85e..b82aeac760 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/CreateKey.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/CreateKey.cs @@ -15,10 +15,18 @@ using ICSharpCode.SharpDevelop.Gui.XmlForms; namespace ICSharpCode.SharpDevelop.Gui.OptionPanels { + /// + /// A form that creates keys for assembly signing. + /// public class CreateKeyForm : BaseSharpDevelopForm { string baseDirectory; + /// + /// Initializes the CreateKeyFrom() dialog setting the b ase directory for adding keys to the + /// location specified. + /// + /// The folder for placing the key. public CreateKeyForm(string baseDirectory) { this.baseDirectory = baseDirectory; @@ -54,6 +62,11 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels } } + /// + /// Creates a key with the sn.exe utility. + /// + /// The path of the key to create. + /// True if the key was created correctly. public static bool CreateKey(string keyPath) { if (File.Exists(keyPath)) { @@ -97,9 +110,13 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels return true; } + /// + /// Gets the path of the "strong named" executable. This is used to create keys for strongly signing + /// .NET assemblies. + /// public static string StrongNameTool { get { - return Path.Combine(FileUtility.NetSdkInstallRoot, "bin\\sn.exe"); + return FileUtility.GetSdkPath("sn.exe"); } } } diff --git a/src/Main/Base/Project/Src/Project/Converter/PrjxToSolutionProject.cs b/src/Main/Base/Project/Src/Project/Converter/PrjxToSolutionProject.cs index 97466a6bf0..80463dda5c 100644 --- a/src/Main/Base/Project/Src/Project/Converter/PrjxToSolutionProject.cs +++ b/src/Main/Base/Project/Src/Project/Converter/PrjxToSolutionProject.cs @@ -82,7 +82,7 @@ namespace ICSharpCode.SharpDevelop.Project.Converter { if (hintPath == null || hintPath.Length == 0) return false; - return !FileUtility.IsBaseDirectory(FileUtility.NETFrameworkInstallRoot, hintPath); + return !FileUtility.IsBaseDirectory(FileUtility.NetFrameworkInstallRoot, hintPath); } string rootNamespace; diff --git a/src/Main/Base/Project/Src/Project/MSBuildEngine.cs b/src/Main/Base/Project/Src/Project/MSBuildEngine.cs index 23637281df..c93700ac3d 100644 --- a/src/Main/Base/Project/Src/Project/MSBuildEngine.cs +++ b/src/Main/Base/Project/Src/Project/MSBuildEngine.cs @@ -354,6 +354,7 @@ namespace ICSharpCode.SharpDevelop.Project AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, true); } + // TODO: Add XmlDocBloc to MSBuildError.AppendError() void AppendError(string file, int lineNumber, int columnNumber, string code, string message, bool isWarning) { if (string.Equals(file, activeTaskName, StringComparison.InvariantCultureIgnoreCase)) { @@ -365,7 +366,9 @@ namespace ICSharpCode.SharpDevelop.Project } if (isShortFileName && !File.Exists(file)) { file = ""; - } else if (FileUtility.IsBaseDirectory(FileUtility.NETFrameworkInstallRoot, file) + } + //TODO: Do we have to check for other SDKs here. + else if (FileUtility.IsBaseDirectory(FileUtility.NetFrameworkInstallRoot, file) || FileUtility.IsBaseDirectory(FileUtility.ApplicationRootPath, file)) { file = ""; diff --git a/src/Main/Core/Project/Src/Services/FileUtility/FileUtility.cs b/src/Main/Core/Project/Src/Services/FileUtility/FileUtility.cs index 3f93ed5549..1868e6c68f 100644 --- a/src/Main/Core/Project/Src/Services/FileUtility/FileUtility.cs +++ b/src/Main/Core/Project/Src/Services/FileUtility/FileUtility.cs @@ -64,28 +64,68 @@ namespace ICSharpCode.Core return null; } + #region InstallRoot Properties + + + + static string netFramework20InstallRoot = null; + /// + /// Location of the .NET Framework install root. + /// + public static string NetFrameworkInstallRoot { + get { + // Lazy load this. + if (netSdk20InstallRoot != null) { return netSdk20InstallRoot; } + netSdk20InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\.NETFramework", "InstallRoot") + ?? string.Empty; + return netSdk20InstallRoot; + } + } + + static string netSdk20InstallRoot = null; /// - /// Gets the installation root of the .NET Framework (@"C:\Windows\Microsoft.NET\Framework\") + /// Location of the .NET 2.0 SDK install root. /// - public static string NETFrameworkInstallRoot { + public static string NetSdk20InstallRoot { get { - return GetPathFromRegistry(@"SOFTWARE\Microsoft\.NETFramework", "InstallRoot") ?? string.Empty; + // Lazy load this. + if (netSdk20InstallRoot != null) { return netSdk20InstallRoot; } + netSdk20InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\.NETFramework", "sdkInstallRootv2.0") + ?? string.Empty; + return netSdk20InstallRoot; } } + static string netSdk30InstallRoot = null; /// - /// Gets the Windows Vista SDK installation root. If the Vista SDK is not installed, the - /// .NET 2.0 SDK installation root is returned. If both are not installed, an empty string is returned. + /// Location of the .NET 3.0 SDK (Windows SDK 6.0) install root. /// - public static string NetSdkInstallRoot { + public static string NetSdk30InstallRoot { get { - return GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0a", "InstallationFolder") - ?? GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0", "InstallationFolder") - ?? GetPathFromRegistry(@"SOFTWARE\Microsoft\.NETFramework", "sdkInstallRootv2.0") + // Lazy load this. + if (netSdk30InstallRoot != null) { return netSdk30InstallRoot; } + netSdk30InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0", "InstallationFolder") ?? string.Empty; + return netSdk30InstallRoot; } } + static string netSdk35InstallRoot = null; + /// + /// Location of the .NET 3.5 SDK (Windows SDK 6.0a) install root. + /// + public static string NetSdk35InstallRoot { + get { + // Lazy load this. + if (netSdk35InstallRoot != null) { return netSdk35InstallRoot; } + netSdk35InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0a", "InstallationFolder") + ?? string.Empty; + return netSdk35InstallRoot; + } + } + + #endregion + public static string Combine(params string[] paths) { if (paths == null || paths.Length == 0) { @@ -130,6 +170,32 @@ namespace ICSharpCode.Core return result.ToString(); } + /// + /// Searches all the .net sdk bin folders and return the path of the + /// exe from the latest sdk. + /// + /// The EXE to search for. + /// The path of the executable. + /// + /// Thrown if the exe is not found. + /// + public static string GetSdkPath(string exeName) { + string execPath; + if (Directory.Exists(NetSdk35InstallRoot)) { + execPath = Path.Combine(NetSdk35InstallRoot + "\\bin", exeName); + if (File.Exists(execPath)) { return execPath; } + } + if (Directory.Exists(NetSdk30InstallRoot)) { + execPath = Path.Combine(NetSdk30InstallRoot + "\\bin", exeName); + if (File.Exists(execPath)) { return execPath; } + } + if (Directory.Exists(NetSdk20InstallRoot)) { + execPath = Path.Combine(NetSdk20InstallRoot + "\\bin", exeName); + if (File.Exists(execPath)) { return execPath; } + } + throw new FileNotFoundException(StringParser.Parse("${res:Fileutility.CantFindExecutableError}", new string[,] { {"EXECUTABLE", exeName} })); + } + /// /// Converts a given absolute path and a given base path to a path that leads /// from the base path to the absoulte path. (as a relative path) diff --git a/src/Main/Core/Project/Src/Services/StringParser/StringParser.cs b/src/Main/Core/Project/Src/Services/StringParser/StringParser.cs index 9afb1d3d23..22c71c379d 100644 --- a/src/Main/Core/Project/Src/Services/StringParser/StringParser.cs +++ b/src/Main/Core/Project/Src/Services/StringParser/StringParser.cs @@ -14,7 +14,7 @@ using System.Text; namespace ICSharpCode.Core { /// - /// this class parses internal ${xyz} tags of sd. + /// This class parses internal ${xyz} tags of #Develop. /// All environment variables are avaible under the name env.[NAME] /// where [NAME] represents the string under which it is avaiable in /// the environment. @@ -94,31 +94,6 @@ namespace ICSharpCode.Core /// public static string Parse(string input, string[,] customTags) { - // Parse is a important method and should have good performance, - // so we don't use an expensive Regex here. - - /* old code using regex: - string output = input; - if (input != null) { - foreach (Match m in pattern.Matches(input)) { - if (m.Length > 0) { - string token = m.ToString(); - string propertyName = m.Groups[1].Captures[0].Value; - - string propertyValue = GetValue(propertyName, customTags); - - if (propertyValue != null) { - if (m.Length == input.Length) { - // safe a replace operation when input is a property on its own. - return propertyValue; - } - output = output.Replace(token, propertyValue); - } - } - } - } - return output; - */ if (input == null) return null; int pos = 0; @@ -170,9 +145,11 @@ namespace ICSharpCode.Core static string GetValue(string propertyName, string[,] customTags) { + + // most properties start with res: in lowercase, + // so we can save 2 string allocations here, in addition to all the jumps + // All other prefixed properties {prefix:Key} shoulg get handled in the switch below. if (propertyName.StartsWith("res:")) { - // most properties start with res: in lowercase, - // so we can safe 2 string allocations here try { return Parse(ResourceService.GetString(propertyName.Substring(4)), customTags); } catch (ResourceNotFoundException) { @@ -210,6 +187,8 @@ namespace ICSharpCode.Core string prefix = propertyName.Substring(0, k); propertyName = propertyName.Substring(k + 1); switch (prefix.ToUpperInvariant()) { + case "SDKTOOLPATH": + return FileUtility.GetSdkPath(propertyName); case "ADDINPATH": foreach (AddIn addIn in AddInTree.AddIns) { if (addIn.Manifest.Identities.ContainsKey(propertyName)) {